From a60df48774fd552008ced6131519a0f6f6c9e7bc Mon Sep 17 00:00:00 2001 From: RhiobeT Date: Sat, 25 Apr 2026 12:35:19 +0200 Subject: [PATCH] Implement default thumbnail generator --- .../advent/access/AdventAccessService.java | 16 +++- .../AdventThumbnailPathURIResolver.java} | 26 +++---- .../core/path/model/FileSystemPath.java | 12 --- .../rhiobet/lalafin/core/path/model/Path.java | 1 - .../lalafin/core/path/model/PathAccessor.java | 16 ++++ .../lalafin/core/path/model/ZipEntryPath.java | 9 --- .../lalafin/core/path/plugin/PathPlugin.java | 12 --- .../core/path/resolver/PathURIResolver.java | 9 +++ .../core/thumbnail/ThumbnailGenerator.java | 9 +++ .../core/thumbnail/ThumbnailPathAccessor.java | 41 ++++++++++ .../rhiobet/lalafin/core/util/MimeType.java | 42 +++++++++++ .../file/access/FileRoleAccessService.java | 19 ++++- .../file/model/FileMetadataService.java | 53 ++++++++++--- .../file/resolver/FilePathURIResolver.java | 34 +++++++++ .../FileThumbnailPathURIResolver.java | 42 +++++++++++ .../thumbnail/FileThumbnailGenerator.java | 68 +++++++++++++++++ .../thumbnail/FileThumbnailPathPlugin.java | 74 ------------------- 17 files changed, 342 insertions(+), 141 deletions(-) rename src/main/java/sh/rhiobet/lalafin/advent/{thumbnail/AdventThumbnailPathPlugin.java => resolver/AdventThumbnailPathURIResolver.java} (68%) delete mode 100644 src/main/java/sh/rhiobet/lalafin/core/path/plugin/PathPlugin.java create mode 100644 src/main/java/sh/rhiobet/lalafin/core/path/resolver/PathURIResolver.java create mode 100644 src/main/java/sh/rhiobet/lalafin/core/thumbnail/ThumbnailGenerator.java create mode 100644 src/main/java/sh/rhiobet/lalafin/core/thumbnail/ThumbnailPathAccessor.java create mode 100644 src/main/java/sh/rhiobet/lalafin/core/util/MimeType.java create mode 100644 src/main/java/sh/rhiobet/lalafin/file/resolver/FilePathURIResolver.java create mode 100644 src/main/java/sh/rhiobet/lalafin/file/resolver/FileThumbnailPathURIResolver.java create mode 100644 src/main/java/sh/rhiobet/lalafin/file/thumbnail/FileThumbnailGenerator.java delete mode 100644 src/main/java/sh/rhiobet/lalafin/file/thumbnail/FileThumbnailPathPlugin.java diff --git a/src/main/java/sh/rhiobet/lalafin/advent/access/AdventAccessService.java b/src/main/java/sh/rhiobet/lalafin/advent/access/AdventAccessService.java index 174f3bc..1b7b623 100644 --- a/src/main/java/sh/rhiobet/lalafin/advent/access/AdventAccessService.java +++ b/src/main/java/sh/rhiobet/lalafin/advent/access/AdventAccessService.java @@ -10,16 +10,28 @@ import jakarta.inject.Named; import sh.rhiobet.lalafin.advent.configuration.AdventConfiguration; import sh.rhiobet.lalafin.core.access.AccessService; import sh.rhiobet.lalafin.core.path.model.Path; +import sh.rhiobet.lalafin.core.path.resolver.PathURIResolver; @ApplicationScoped -@Named("advent") +@Named("advent/access") public class AdventAccessService implements AccessService { @Inject AdventConfiguration adventConfiguration; + @Inject + @Named("file/resolver") + PathURIResolver fileURIResolver; + @Override public boolean checkAccess(Path path) { - String pathUri = path.getURI(); + Optional pathUriOptional = this.fileURIResolver.resolve(path); + + String pathUri; + if (pathUriOptional.isEmpty()) { + return false; + } else { + pathUri = pathUriOptional.get().replaceAll("^.file", ""); + } Optional matchingAdvent = adventConfiguration.events().stream() .filter(e -> pathUri.startsWith(e.path())) diff --git a/src/main/java/sh/rhiobet/lalafin/advent/thumbnail/AdventThumbnailPathPlugin.java b/src/main/java/sh/rhiobet/lalafin/advent/resolver/AdventThumbnailPathURIResolver.java similarity index 68% rename from src/main/java/sh/rhiobet/lalafin/advent/thumbnail/AdventThumbnailPathPlugin.java rename to src/main/java/sh/rhiobet/lalafin/advent/resolver/AdventThumbnailPathURIResolver.java index a990fe9..faa99f2 100644 --- a/src/main/java/sh/rhiobet/lalafin/advent/thumbnail/AdventThumbnailPathPlugin.java +++ b/src/main/java/sh/rhiobet/lalafin/advent/resolver/AdventThumbnailPathURIResolver.java @@ -1,11 +1,11 @@ -package sh.rhiobet.lalafin.advent.thumbnail; +package sh.rhiobet.lalafin.advent.resolver; import java.io.IOException; -import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Optional; +import jakarta.annotation.Priority; import jakarta.decorator.Decorator; import jakarta.decorator.Delegate; import jakarta.inject.Inject; @@ -13,26 +13,27 @@ import jakarta.inject.Named; import sh.rhiobet.lalafin.advent.access.AdventAccessService; import sh.rhiobet.lalafin.core.path.model.Path; import sh.rhiobet.lalafin.core.path.model.PathAccessor; -import sh.rhiobet.lalafin.core.path.plugin.PathPlugin; +import sh.rhiobet.lalafin.core.path.resolver.PathURIResolver; import sh.rhiobet.lalafin.file.configuration.FileConfiguration; @Decorator -public class AdventThumbnailPathPlugin extends PathAccessor implements PathPlugin { +@Priority(0) +public class AdventThumbnailPathURIResolver extends PathAccessor implements PathURIResolver { @Inject FileConfiguration fileConfiguration; @Inject - @Named("file/thumbnail") + @Named("file/resolver/thumbnail") @Delegate - PathPlugin thumbnailPathPlugin; + PathURIResolver thumbnailURIResolver; @Inject AdventAccessService adventAccessService; @Override - public Optional resolveURI(Path path) { + public Optional resolve(Path path) { if (this.adventAccessService.checkAccess(path)) { - return this.thumbnailPathPlugin.resolveURI(path); + return this.thumbnailURIResolver.resolve(path); } else { Optional thumbnailAbsolutePath = getDefaultThumbnailPath(path); @@ -47,15 +48,6 @@ public class AdventThumbnailPathPlugin extends PathAccessor implements PathPlugi } } - @Override - public OutputStream getOutputStream(Path path) throws IOException { - if (this.adventAccessService.checkAccess(path)) { - return this.thumbnailPathPlugin.getOutputStream(path); - } else { - throw new IOException("You don't yet have access to this resource."); - } - } - private Optional getDefaultThumbnailPath(Path path) { try { return Files.list(getAbsolutePath(path).getParent().resolve(".thumbnails")) diff --git a/src/main/java/sh/rhiobet/lalafin/core/path/model/FileSystemPath.java b/src/main/java/sh/rhiobet/lalafin/core/path/model/FileSystemPath.java index 7b4f663..6d1d712 100644 --- a/src/main/java/sh/rhiobet/lalafin/core/path/model/FileSystemPath.java +++ b/src/main/java/sh/rhiobet/lalafin/core/path/model/FileSystemPath.java @@ -9,8 +9,6 @@ import java.util.List; import java.util.stream.Stream; import java.util.zip.ZipFile; -import org.jboss.resteasy.reactive.common.util.Encode; - public final class FileSystemPath implements Path { private java.nio.file.Path rootFolderPath; @@ -29,16 +27,6 @@ public final class FileSystemPath implements Path { this.absolutePath = getAbsolutePath(); } - @Override - public String getURI() { - if (this.segments.isEmpty()) { - return "/"; - } - return this.segments.stream() - .map(s -> "/" + Encode.encodePathSegment(s)) - .reduce("", String::concat); - } - @Override public String getFilename() { return this.segments.isEmpty() ? "/" : this.segments.getLast(); diff --git a/src/main/java/sh/rhiobet/lalafin/core/path/model/Path.java b/src/main/java/sh/rhiobet/lalafin/core/path/model/Path.java index a4384f3..536a714 100644 --- a/src/main/java/sh/rhiobet/lalafin/core/path/model/Path.java +++ b/src/main/java/sh/rhiobet/lalafin/core/path/model/Path.java @@ -5,7 +5,6 @@ import java.io.InputStream; import java.util.List; sealed public interface Path permits FileSystemPath, ZipEntryPath { - String getURI(); String getFilename(); long getSize(); boolean exists(); diff --git a/src/main/java/sh/rhiobet/lalafin/core/path/model/PathAccessor.java b/src/main/java/sh/rhiobet/lalafin/core/path/model/PathAccessor.java index 1b0110d..a39633b 100644 --- a/src/main/java/sh/rhiobet/lalafin/core/path/model/PathAccessor.java +++ b/src/main/java/sh/rhiobet/lalafin/core/path/model/PathAccessor.java @@ -1,5 +1,10 @@ package sh.rhiobet.lalafin.core.path.model; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + public abstract class PathAccessor { protected java.nio.file.Path getAbsolutePath(Path path) { switch (path) { @@ -10,4 +15,15 @@ public abstract class PathAccessor { return zep.zipFilePath.absolutePath; } } + + protected List getSegments(Path path) { + switch (path) { + case FileSystemPath fsp: + return Collections.unmodifiableList(fsp.segments); + + case ZipEntryPath zep: + return Stream.concat(zep.zipFilePath.segments.stream(), zep.segments.stream()) + .collect(Collectors.toUnmodifiableList()); + } + } } diff --git a/src/main/java/sh/rhiobet/lalafin/core/path/model/ZipEntryPath.java b/src/main/java/sh/rhiobet/lalafin/core/path/model/ZipEntryPath.java index 7602b09..a47dd53 100644 --- a/src/main/java/sh/rhiobet/lalafin/core/path/model/ZipEntryPath.java +++ b/src/main/java/sh/rhiobet/lalafin/core/path/model/ZipEntryPath.java @@ -10,8 +10,6 @@ import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import org.jboss.resteasy.reactive.common.util.Encode; - public final class ZipEntryPath implements Path { private long size = 0; private boolean exists = false; @@ -32,13 +30,6 @@ public final class ZipEntryPath implements Path { this.entryName = entryName; } - @Override - public String getURI() { - return this.zipFilePath.getURI() + this.segments.stream() - .map(s -> "/" + Encode.encodePathSegment(s)) - .reduce("", String::concat); - } - @Override public String getFilename() { return this.segments.getLast(); diff --git a/src/main/java/sh/rhiobet/lalafin/core/path/plugin/PathPlugin.java b/src/main/java/sh/rhiobet/lalafin/core/path/plugin/PathPlugin.java deleted file mode 100644 index 70b64e3..0000000 --- a/src/main/java/sh/rhiobet/lalafin/core/path/plugin/PathPlugin.java +++ /dev/null @@ -1,12 +0,0 @@ -package sh.rhiobet.lalafin.core.path.plugin; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Optional; - -import sh.rhiobet.lalafin.core.path.model.Path; - -public interface PathPlugin { - Optional resolveURI(Path path); - OutputStream getOutputStream(Path path) throws IOException; -} diff --git a/src/main/java/sh/rhiobet/lalafin/core/path/resolver/PathURIResolver.java b/src/main/java/sh/rhiobet/lalafin/core/path/resolver/PathURIResolver.java new file mode 100644 index 0000000..1498087 --- /dev/null +++ b/src/main/java/sh/rhiobet/lalafin/core/path/resolver/PathURIResolver.java @@ -0,0 +1,9 @@ +package sh.rhiobet.lalafin.core.path.resolver; + +import java.util.Optional; + +import sh.rhiobet.lalafin.core.path.model.Path; + +public interface PathURIResolver { + Optional resolve(Path path); +} diff --git a/src/main/java/sh/rhiobet/lalafin/core/thumbnail/ThumbnailGenerator.java b/src/main/java/sh/rhiobet/lalafin/core/thumbnail/ThumbnailGenerator.java new file mode 100644 index 0000000..f506a3e --- /dev/null +++ b/src/main/java/sh/rhiobet/lalafin/core/thumbnail/ThumbnailGenerator.java @@ -0,0 +1,9 @@ +package sh.rhiobet.lalafin.core.thumbnail; + +import java.io.IOException; + +import sh.rhiobet.lalafin.core.path.model.Path; + +public interface ThumbnailGenerator { + void generate(Path path) throws IOException; +} diff --git a/src/main/java/sh/rhiobet/lalafin/core/thumbnail/ThumbnailPathAccessor.java b/src/main/java/sh/rhiobet/lalafin/core/thumbnail/ThumbnailPathAccessor.java new file mode 100644 index 0000000..98f6731 --- /dev/null +++ b/src/main/java/sh/rhiobet/lalafin/core/thumbnail/ThumbnailPathAccessor.java @@ -0,0 +1,41 @@ +package sh.rhiobet.lalafin.core.thumbnail; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.Optional; +import java.util.regex.Pattern; + +import sh.rhiobet.lalafin.core.path.model.FileSystemPath; +import sh.rhiobet.lalafin.core.path.model.Path; +import sh.rhiobet.lalafin.core.path.model.PathAccessor; +import sh.rhiobet.lalafin.core.path.model.ZipEntryPath; + +public abstract class ThumbnailPathAccessor extends PathAccessor { + @Override + protected java.nio.file.Path getAbsolutePath(Path path) { + try { + switch (path) { + case FileSystemPath fsp: + java.nio.file.Path thumbnailsFolder = + super.getAbsolutePath(path).getParent().resolve(".thumbnails"); + + Optional foundPath = Optional.empty(); + if (Files.exists(thumbnailsFolder)) { + foundPath = Files.list(thumbnailsFolder) + .filter(f -> f.getFileName().toString() + .matches("^" + Pattern.quote(path.getFilename()) + "(\\.[^.]+)*$")) + .findFirst(); + } + + return foundPath.isPresent() ? + foundPath.get() : + thumbnailsFolder.resolve(path.getFilename() + ".png"); + + case ZipEntryPath zep: + return null; + } + } catch (IOException ignored) { + return null; + } + } +} diff --git a/src/main/java/sh/rhiobet/lalafin/core/util/MimeType.java b/src/main/java/sh/rhiobet/lalafin/core/util/MimeType.java new file mode 100644 index 0000000..03db655 --- /dev/null +++ b/src/main/java/sh/rhiobet/lalafin/core/util/MimeType.java @@ -0,0 +1,42 @@ +package sh.rhiobet.lalafin.core.util; + +public class MimeType { + public static String getMimeType(String filename) { + String extension = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase(); + + switch (extension) { + case "3gp": + return "video/3gpp"; + case "avi": + return "video/x-msvideo"; + case "flac": + return "audio/x-flac"; + case "flv": + return "video/x-flv"; + case "html": + return "text/html"; + case "jpg": + return "image/jpeg"; + case "mkv": + return "video/x-matroska"; + case "mp3": + return "audio/mp3"; + case "mp4": + return "video/mp4"; + case "png": + return "image/png"; + case "ts": + return "video/MP2T"; + case "wav": + return "audio/x-wav"; + case "webm": + return "video/webm"; + case "wmv": + return "video/x-ms-wmv"; + case "zip": + return "application/zip"; + default: + return "application/octet-stream"; + } + } +} diff --git a/src/main/java/sh/rhiobet/lalafin/file/access/FileRoleAccessService.java b/src/main/java/sh/rhiobet/lalafin/file/access/FileRoleAccessService.java index 682787f..1605e8c 100644 --- a/src/main/java/sh/rhiobet/lalafin/file/access/FileRoleAccessService.java +++ b/src/main/java/sh/rhiobet/lalafin/file/access/FileRoleAccessService.java @@ -1,6 +1,7 @@ package sh.rhiobet.lalafin.file.access; import java.util.List; +import java.util.Optional; import io.quarkus.security.identity.SecurityIdentity; import jakarta.enterprise.context.ApplicationScoped; @@ -8,10 +9,11 @@ import jakarta.inject.Inject; import jakarta.inject.Named; import sh.rhiobet.lalafin.core.access.AccessService; import sh.rhiobet.lalafin.core.path.model.Path; +import sh.rhiobet.lalafin.core.path.resolver.PathURIResolver; import sh.rhiobet.lalafin.file.configuration.FileConfiguration; @ApplicationScoped -@Named("file/role") +@Named("file/access/role") public class FileRoleAccessService implements AccessService { @Inject SecurityIdentity securityIdentity; @@ -19,13 +21,24 @@ public class FileRoleAccessService implements AccessService { @Inject FileConfiguration fileConfiguration; + @Inject + @Named("file/resolver") + PathURIResolver fileURIResolver; + public boolean checkAccess(Path path) { - String pathUri = path.getURI(); + Optional pathUriOptional = this.fileURIResolver.resolve(path); + + String pathUri; + if (pathUriOptional.isEmpty()) { + return false; + } else { + pathUri = pathUriOptional.get().replaceAll("^.file", ""); + } + List matchingRoutes = fileConfiguration.routes().stream() .filter(r -> pathUri.startsWith(r.path())) .toList(); - if (matchingRoutes.isEmpty()) { return false; } diff --git a/src/main/java/sh/rhiobet/lalafin/file/model/FileMetadataService.java b/src/main/java/sh/rhiobet/lalafin/file/model/FileMetadataService.java index ef5bdf7..2b6fa51 100644 --- a/src/main/java/sh/rhiobet/lalafin/file/model/FileMetadataService.java +++ b/src/main/java/sh/rhiobet/lalafin/file/model/FileMetadataService.java @@ -8,7 +8,8 @@ import jakarta.inject.Named; import sh.rhiobet.lalafin.LalafinConfiguration; import sh.rhiobet.lalafin.api.internal.redis.FileTokenProvider; import sh.rhiobet.lalafin.core.path.model.Path; -import sh.rhiobet.lalafin.core.path.plugin.PathPlugin; +import sh.rhiobet.lalafin.core.path.resolver.PathURIResolver; +import sh.rhiobet.lalafin.core.thumbnail.ThumbnailGenerator; import sh.rhiobet.lalafin.file.configuration.FileConfiguration; @ApplicationScoped @@ -19,19 +20,37 @@ public class FileMetadataService { @Inject FileConfiguration fileConfiguration; + @Inject + @Named("file/resolver") + PathURIResolver fileURIResolver; + + @Inject + @Named("file/resolver/thumbnail") + PathURIResolver fileThumbnailURIResolver; + @Inject @Named("file/thumbnail") - PathPlugin thumbnailPathPlugin; + ThumbnailGenerator fileThumbnailGenerator; public FileInfoBase getInfo(Path filePath, FileTokenProvider fileTokenProvider) { if (filePath.exists()) { - Optional thumbUrl = this.thumbnailPathPlugin.resolveURI(filePath); + Optional fileUrl = this.fileURIResolver.resolve(filePath); + Optional thumbUrl = this.fileThumbnailURIResolver.resolve(filePath); + + if (thumbUrl.isEmpty()) { + try { + this.fileThumbnailGenerator.generate(filePath); + thumbUrl = this.fileThumbnailURIResolver.resolve(filePath); + } catch (IOException e) { + e.printStackTrace(); + } + } if (filePath.canHaveChildren()) { FolderInfo folderInfo = new FolderInfo( filePath.getFilename(), - thumbUrl.isPresent() ? "/file" + thumbUrl.get() : "", - "/file" + filePath.getURI(), + thumbUrl.isPresent() ? thumbUrl.get() : "", + fileUrl.isPresent() ? fileUrl.get() : "", "" ); @@ -48,17 +67,29 @@ public class FileMetadataService { } } - Optional childThumbUrl = this.thumbnailPathPlugin.resolveURI(child); + Optional childFileUrl = this.fileURIResolver.resolve(child); + Optional childThumbUrl = this.fileThumbnailURIResolver.resolve(child); + + if (childThumbUrl.isEmpty()) { + try { + this.fileThumbnailGenerator.generate(child); + childThumbUrl = this.fileThumbnailURIResolver.resolve(child); + } catch (IOException e) { + e.printStackTrace(); + } + } FileInfoBase contentInfo; if (child.canHaveChildren()) { - contentInfo = new FolderInfo(child.getFilename(), "/file" + child.getURI()); + contentInfo = new FolderInfo(child.getFilename(), + childFileUrl.isPresent() ? childFileUrl.get() : ""); } else { - contentInfo = new FileInfo(child.getFilename(), "/file" + child.getURI()); + contentInfo = new FileInfo(child.getFilename(), + childFileUrl.isPresent() ? childFileUrl.get() : ""); } if (childThumbUrl.isPresent()) { - contentInfo.thumbnailUrl = "/file" + childThumbUrl.get(); + contentInfo.thumbnailUrl = childThumbUrl.get(); } folderInfo.content.add(contentInfo); @@ -71,8 +102,8 @@ public class FileMetadataService { } else { FileInfo fileInfo = new FileInfo( filePath.getFilename(), - thumbUrl.isPresent() ? "/file" + thumbUrl.get() : "", - "/file" + filePath.getURI(), + thumbUrl.isPresent() ? thumbUrl.get() : "", + fileUrl.isPresent() ? fileUrl.get() : "", "" ); diff --git a/src/main/java/sh/rhiobet/lalafin/file/resolver/FilePathURIResolver.java b/src/main/java/sh/rhiobet/lalafin/file/resolver/FilePathURIResolver.java new file mode 100644 index 0000000..988ba82 --- /dev/null +++ b/src/main/java/sh/rhiobet/lalafin/file/resolver/FilePathURIResolver.java @@ -0,0 +1,34 @@ +package sh.rhiobet.lalafin.file.resolver; + +import java.util.List; +import java.util.Optional; + +import org.jboss.resteasy.reactive.common.util.Encode; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import sh.rhiobet.lalafin.file.configuration.FileConfiguration; +import sh.rhiobet.lalafin.core.path.model.Path; +import sh.rhiobet.lalafin.core.path.model.PathAccessor; +import sh.rhiobet.lalafin.core.path.resolver.PathURIResolver; + +@ApplicationScoped +@Named("file/resolver") +public class FilePathURIResolver extends PathAccessor implements PathURIResolver { + @Inject + FileConfiguration fileConfiguration; + + @Override + public Optional resolve(Path path) { + List segments = getSegments(path); + + if (segments.isEmpty()) { + return Optional.of("/file/"); + } + + return Optional.of(segments.stream() + .map(s -> "/" + Encode.encodePathSegment(s)) + .reduce("/file", String::concat)); + } +} diff --git a/src/main/java/sh/rhiobet/lalafin/file/resolver/FileThumbnailPathURIResolver.java b/src/main/java/sh/rhiobet/lalafin/file/resolver/FileThumbnailPathURIResolver.java new file mode 100644 index 0000000..59f2c78 --- /dev/null +++ b/src/main/java/sh/rhiobet/lalafin/file/resolver/FileThumbnailPathURIResolver.java @@ -0,0 +1,42 @@ +package sh.rhiobet.lalafin.file.resolver; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Optional; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import sh.rhiobet.lalafin.file.configuration.FileConfiguration; +import sh.rhiobet.lalafin.core.path.model.FileSystemPath; +import sh.rhiobet.lalafin.core.path.model.Path; +import sh.rhiobet.lalafin.core.path.model.ZipEntryPath; +import sh.rhiobet.lalafin.core.path.resolver.PathURIResolver; +import sh.rhiobet.lalafin.core.thumbnail.ThumbnailPathAccessor; + +@ApplicationScoped +@Named("file/resolver/thumbnail") +public class FileThumbnailPathURIResolver extends ThumbnailPathAccessor implements PathURIResolver { + @Inject + FileConfiguration fileConfiguration; + + @Override + public Optional resolve(Path path) { + switch (path) { + case FileSystemPath fsp: + java.nio.file.Path thumbnailAbsolutePath = getAbsolutePath(path); + + if (thumbnailAbsolutePath != null && Files.exists(thumbnailAbsolutePath)) { + java.nio.file.Path rootFolderPath = Paths.get(this.fileConfiguration.directory()); + return Optional.of( + "/file/" + rootFolderPath.resolve("file").relativize(thumbnailAbsolutePath) + .toString()); + } else { + return Optional.empty(); + } + + case ZipEntryPath zep: + return Optional.empty(); + } + } +} diff --git a/src/main/java/sh/rhiobet/lalafin/file/thumbnail/FileThumbnailGenerator.java b/src/main/java/sh/rhiobet/lalafin/file/thumbnail/FileThumbnailGenerator.java new file mode 100644 index 0000000..d5c9d96 --- /dev/null +++ b/src/main/java/sh/rhiobet/lalafin/file/thumbnail/FileThumbnailGenerator.java @@ -0,0 +1,68 @@ +package sh.rhiobet.lalafin.file.thumbnail; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; + +import org.im4java.core.ConvertCmd; +import org.im4java.core.IM4JavaException; +import org.im4java.core.IMOperation; +import org.im4java.process.Pipe; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import sh.rhiobet.lalafin.file.configuration.FileConfiguration; +import sh.rhiobet.lalafin.core.path.model.Path; +import sh.rhiobet.lalafin.core.thumbnail.ThumbnailGenerator; +import sh.rhiobet.lalafin.core.thumbnail.ThumbnailPathAccessor; +import sh.rhiobet.lalafin.core.util.MimeType; + +@ApplicationScoped +@Named("file/thumbnail") +public class FileThumbnailGenerator extends ThumbnailPathAccessor implements ThumbnailGenerator { + @Inject + FileConfiguration fileConfiguration; + + @Override + public void generate(Path path) throws IOException { + java.nio.file.Path thumbnailAbsolutePath = getAbsolutePath(path); + + if (!path.canHaveChildren() || thumbnailAbsolutePath == null) { + return; + } + + if (Files.exists(thumbnailAbsolutePath)) { + Files.delete(thumbnailAbsolutePath); + } + + Files.createDirectories(thumbnailAbsolutePath.getParent()); + + for (Path childPath : path.getChildren()) { + String mimetype = MimeType.getMimeType(childPath.getFilename()); + + if (mimetype.startsWith("image/")) { + String extension = childPath.getFilename() + .substring(childPath.getFilename().lastIndexOf('.') + 1).toLowerCase(); + + ConvertCmd convert = new ConvertCmd(); + convert.getCommand().clear(); + convert.getCommand().push("magick"); + IMOperation op = new IMOperation(); + + try (InputStream pathInputStream = childPath.getInputStream()) { + Pipe inputPipe = new Pipe(pathInputStream, null); + op.addImage(extension + ":-"); + op.resize(200, null); + op.addImage(thumbnailAbsolutePath.toString()); + convert.setInputProvider(inputPipe); + convert.run(op); + + return; + } catch (InterruptedException | IM4JavaException e) { + throw new IOException("Error when generating thumbnail with IM4J.", e); + } + } + } + } +} diff --git a/src/main/java/sh/rhiobet/lalafin/file/thumbnail/FileThumbnailPathPlugin.java b/src/main/java/sh/rhiobet/lalafin/file/thumbnail/FileThumbnailPathPlugin.java deleted file mode 100644 index 738beb5..0000000 --- a/src/main/java/sh/rhiobet/lalafin/file/thumbnail/FileThumbnailPathPlugin.java +++ /dev/null @@ -1,74 +0,0 @@ -package sh.rhiobet.lalafin.file.thumbnail; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Optional; -import java.util.regex.Pattern; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import jakarta.inject.Named; -import sh.rhiobet.lalafin.file.configuration.FileConfiguration; -import sh.rhiobet.lalafin.core.path.model.FileSystemPath; -import sh.rhiobet.lalafin.core.path.model.Path; -import sh.rhiobet.lalafin.core.path.model.PathAccessor; -import sh.rhiobet.lalafin.core.path.model.ZipEntryPath; -import sh.rhiobet.lalafin.core.path.plugin.PathPlugin; - -@ApplicationScoped -@Named("file/thumbnail") -public class FileThumbnailPathPlugin extends PathAccessor implements PathPlugin { - @Inject - FileConfiguration fileConfiguration; - - @Override - public Optional resolveURI(Path path) { - switch (path) { - case FileSystemPath fsp: - Optional thumbnailAbsolutePath = getThumbnailPath(path); - - if (thumbnailAbsolutePath.isPresent()) { - java.nio.file.Path rootFolderPath = Paths.get(this.fileConfiguration.directory()); - return Optional.of( - "/" + rootFolderPath.resolve("file").relativize(thumbnailAbsolutePath.get()) - .toString()); - } else { - return Optional.empty(); - } - - case ZipEntryPath zep: - return Optional.empty(); - } - } - - @Override - public OutputStream getOutputStream(Path path) throws IOException { - switch (path) { - case FileSystemPath fsp: - Optional thumbnailAbsolutePath = getThumbnailPath(path); - if (thumbnailAbsolutePath.isPresent()) { - Files.delete(thumbnailAbsolutePath.get()); - } - java.nio.file.Path newThumbnailAbsolutePath = - getAbsolutePath(path).getParent().resolve(".thumbnails").resolve(path.getFilename()); - Files.createDirectories(newThumbnailAbsolutePath.getParent()); - return Files.newOutputStream(newThumbnailAbsolutePath); - - case ZipEntryPath zep: - throw new IOException("A zip file is not a valid location to store a thumbnail."); - } - } - - private Optional getThumbnailPath(Path path) { - try { - return Files.list(getAbsolutePath(path).getParent().resolve(".thumbnails")) - .filter(f -> f.getFileName().toString() - .matches("^" + Pattern.quote(path.getFilename()) + "(\\.[^.]+)*$")) - .findFirst(); - } catch (IOException ignored) { - return Optional.empty(); - } - } -}