From 436c0313eb1d7fe5338ef5f3b4adf2463e92c2f5 Mon Sep 17 00:00:00 2001 From: RhiobeT Date: Wed, 26 May 2021 12:00:48 +0200 Subject: [PATCH] Extract Jellyfin thumbnails as a module --- .../thumbnail/JellyfinThumbnailGenerator.java | 125 ++++++++++++++++++ .../api/thumbnail/ThumbnailGenerator.java | 12 ++ .../lalafin/file/ThumbnailService.java | 66 ++------- src/main/resources/application.yaml.example | 6 +- 4 files changed, 156 insertions(+), 53 deletions(-) create mode 100644 src/main/java/sh/rhiobet/lalafin/api/thumbnail/JellyfinThumbnailGenerator.java create mode 100644 src/main/java/sh/rhiobet/lalafin/api/thumbnail/ThumbnailGenerator.java diff --git a/src/main/java/sh/rhiobet/lalafin/api/thumbnail/JellyfinThumbnailGenerator.java b/src/main/java/sh/rhiobet/lalafin/api/thumbnail/JellyfinThumbnailGenerator.java new file mode 100644 index 0000000..ee2395d --- /dev/null +++ b/src/main/java/sh/rhiobet/lalafin/api/thumbnail/JellyfinThumbnailGenerator.java @@ -0,0 +1,125 @@ +package sh.rhiobet.lalafin.api.thumbnail; + +import java.io.IOException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Optional; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.im4java.core.ConvertCmd; +import org.im4java.core.IM4JavaException; +import org.im4java.core.IMOperation; +import sh.rhiobet.lalafin.api.configuration.FileApiConfiguration; +import sh.rhiobet.lalafin.api.model.FileInfo; +import sh.rhiobet.lalafin.api.model.FileInfoBase; +import sh.rhiobet.lalafin.api.model.FolderInfo; + +@ApplicationScoped +public class JellyfinThumbnailGenerator implements ThumbnailGenerator { + + @ConfigProperty(name = "api.thumbnail.jellyfin.paths") + Optional> paths; + + @Inject + FileApiConfiguration fileApiConfiguration; + + @Override + public boolean canHandle(FileInfoBase fileInfoBase) { + if (paths.isPresent()) { + String filePath = URLDecoder.decode(fileInfoBase.directUrl, StandardCharsets.UTF_8); + for (String path : paths.get()) { + if (filePath.toString().startsWith("/file" + path)) { + return true; + } + } + } + return false; + } + + @Override + public boolean generateThumbnail(FileInfoBase fileInfoBase, Path thumbPath) { + if (fileInfoBase instanceof FileInfo) { + return this.generateThumbnailForFile((FileInfo) fileInfoBase, thumbPath); + } else if (fileInfoBase instanceof FolderInfo) { + return this.generateThumbnailForFolder((FolderInfo) fileInfoBase, thumbPath); + } + return false; + } + + public boolean generateThumbnailForFile(FileInfo fileInfo, Path thumbPath) { + Path filePath = Paths.get(fileApiConfiguration.directory() + + URLDecoder.decode(fileInfo.directUrl, StandardCharsets.UTF_8)); + + Path imgPath = filePath.resolveSibling( + filePath.getFileName().toString().replaceAll("\\.[^.]*$", "-thumb.jpg")); + if (Files.exists(imgPath)) { + try { + thumbPath.getParent().toFile().mkdirs(); + + ConvertCmd convert = new ConvertCmd(); + IMOperation op = new IMOperation(); + op.addImage(imgPath.toString()); + op.resize(200, null); + op.addImage(thumbPath.toString()); + convert.run(op); + + return true; + } catch (IOException | InterruptedException | IM4JavaException e) { + e.printStackTrace(); + } + } + + return false; + } + + public boolean generateThumbnailForFolder(FolderInfo folderInfo, Path thumbPath) { + Path folderPath = Paths.get(fileApiConfiguration.directory() + + URLDecoder.decode(folderInfo.directUrl, StandardCharsets.UTF_8)); + + try { + Optional imgPath; + + Path posterPath = folderPath.resolve("poster.jpg"); + if (Files.exists(posterPath)) { + // Check for series poster + imgPath = Optional.of(posterPath); + } else if (folderPath.getFileName().toString().startsWith("Season ") + || (folderPath.getFileName().toString().equals("Specials"))) { + // Season folder + String folderName = folderPath.getFileName().toString(); + if (folderName.startsWith("Season ")) { + String seasonNumber = + String.format("%02d", Integer.parseInt(folderName.substring(7))); + imgPath = Optional + .of(folderPath.resolveSibling("season" + seasonNumber + "-poster.jpg")); + } else { + imgPath = Optional.of(folderPath.resolveSibling("season-specials-poster.jpg")); + } + } else { + imgPath = Optional.empty(); + } + if (imgPath.isPresent()) { + thumbPath.getParent().toFile().mkdirs(); + + ConvertCmd convert = new ConvertCmd(); + IMOperation op = new IMOperation(); + op.addImage(imgPath.get().toString()); + op.resize(200, null); + op.addImage(thumbPath.toString()); + convert.run(op); + + return true; + } + } catch (IOException | InterruptedException | IM4JavaException e) { + e.printStackTrace(); + } + + return false; + } + +} diff --git a/src/main/java/sh/rhiobet/lalafin/api/thumbnail/ThumbnailGenerator.java b/src/main/java/sh/rhiobet/lalafin/api/thumbnail/ThumbnailGenerator.java new file mode 100644 index 0000000..2034a34 --- /dev/null +++ b/src/main/java/sh/rhiobet/lalafin/api/thumbnail/ThumbnailGenerator.java @@ -0,0 +1,12 @@ +package sh.rhiobet.lalafin.api.thumbnail; + +import java.nio.file.Path; +import sh.rhiobet.lalafin.api.model.FileInfoBase; + +public interface ThumbnailGenerator { + + boolean canHandle(FileInfoBase fileInfoBase); + + boolean generateThumbnail(FileInfoBase fileInfoBase, Path thumbPath); + +} \ No newline at end of file diff --git a/src/main/java/sh/rhiobet/lalafin/file/ThumbnailService.java b/src/main/java/sh/rhiobet/lalafin/file/ThumbnailService.java index ba4de67..e61912c 100644 --- a/src/main/java/sh/rhiobet/lalafin/file/ThumbnailService.java +++ b/src/main/java/sh/rhiobet/lalafin/file/ThumbnailService.java @@ -11,6 +11,7 @@ import java.util.Optional; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Instance; import javax.inject.Inject; import org.im4java.core.ConvertCmd; import org.im4java.core.IM4JavaException; @@ -19,19 +20,26 @@ import sh.rhiobet.lalafin.api.configuration.FileApiConfiguration; import sh.rhiobet.lalafin.api.model.FileInfo; import sh.rhiobet.lalafin.api.model.FileInfoBase; import sh.rhiobet.lalafin.api.model.FolderInfo; +import sh.rhiobet.lalafin.api.thumbnail.ThumbnailGenerator; @ApplicationScoped public class ThumbnailService { @Inject FileApiConfiguration fileApiConfiguration; + @Inject + Instance generators; + public boolean generateThumbnail(FileInfoBase fileInfoBase, Path thumbPath) { + for (ThumbnailGenerator generator : generators) { + if (generator.canHandle(fileInfoBase)) { + return generator.generateThumbnail(fileInfoBase, thumbPath); + } + } if (fileInfoBase instanceof FileInfo) { FileInfo fileInfo = (FileInfo) fileInfoBase; if (fileInfo.filename.endsWith(".zip")) { return this.generateThumbnailFromZip(fileInfo, thumbPath); - } else { - return this.generateThumbnailForFile(fileInfo, thumbPath); } } else if (fileInfoBase instanceof FolderInfo) { return this.generateThumbnailForFolder((FolderInfo) fileInfoBase, thumbPath); @@ -39,32 +47,6 @@ public class ThumbnailService { return false; } - public boolean generateThumbnailForFile(FileInfo fileInfo, Path thumbPath) { - Path filePath = Paths.get(fileApiConfiguration.directory() - + URLDecoder.decode(fileInfo.directUrl, StandardCharsets.UTF_8)); - - Path imgPath = filePath.resolveSibling( - filePath.getFileName().toString().replaceAll("\\.[^.]*$", "-thumb.jpg")); - if (Files.exists(imgPath)) { - try { - thumbPath.getParent().toFile().mkdirs(); - - ConvertCmd convert = new ConvertCmd(); - IMOperation op = new IMOperation(); - op.addImage(imgPath.toString()); - op.resize(200, null); - op.addImage(thumbPath.toString()); - convert.run(op); - - return true; - } catch (IOException | InterruptedException | IM4JavaException e) { - e.printStackTrace(); - } - } - - return false; - } - public boolean generateThumbnailFromZip(FileInfo fileInfo, Path thumbPath) { Path zipPath = Paths.get(fileApiConfiguration.directory() + URLDecoder.decode(fileInfo.directUrl, StandardCharsets.UTF_8)); @@ -105,30 +87,10 @@ public class ThumbnailService { + URLDecoder.decode(folderInfo.directUrl, StandardCharsets.UTF_8)); try { - Optional imgPath; - - Path posterPath = folderPath.resolve("poster.jpg"); - if (Files.exists(posterPath)) { - // Check for series poster - imgPath = Optional.of(posterPath); - } else if (folderPath.getFileName().toString().startsWith("Season ") - || (folderPath.getFileName().toString().equals("Specials"))) { - // Season folder - String folderName = folderPath.getFileName().toString(); - if (folderName.startsWith("Season ")) { - String seasonNumber = - String.format("%02d", Integer.parseInt(folderName.substring(7))); - imgPath = Optional - .of(folderPath.resolveSibling("season" + seasonNumber + "-poster.jpg")); - } else { - imgPath = Optional.of(folderPath.resolveSibling("season-specials-poster.jpg")); - } - } else { - imgPath = Files.list(folderPath).sorted() - .filter(p -> !Files.isDirectory(p) - && (p.toString().endsWith(".png") || p.toString().endsWith(".jpg"))) - .findFirst(); - } + Optional imgPath = Files.list(folderPath).sorted() + .filter(p -> !Files.isDirectory(p) + && (p.toString().endsWith(".png") || p.toString().endsWith(".jpg"))) + .findFirst(); if (imgPath.isPresent()) { thumbPath.getParent().toFile().mkdirs(); diff --git a/src/main/resources/application.yaml.example b/src/main/resources/application.yaml.example index 786b057..ac0e32e 100644 --- a/src/main/resources/application.yaml.example +++ b/src/main/resources/application.yaml.example @@ -30,4 +30,8 @@ api: - path: / # Root corresonds to the endpoint /file/ roles: {} # Only users with these roles will have access to this route (empty = ALL) folder: - tokens: {} # List of tokens to make some routes available trhough the public folders API \ No newline at end of file + tokens: {} # List of tokens to make some routes available trhough the public folders API + + # thumbnail: + # jellyfin: + # paths: {} \ No newline at end of file