Extract Jellyfin thumbnails as a module

This commit is contained in:
2021-05-26 12:00:48 +02:00
parent db5ceb7aea
commit 436c0313eb
4 changed files with 156 additions and 53 deletions

View File

@@ -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<List<String>> 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<Path> 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;
}
}

View File

@@ -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);
}

View File

@@ -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<ThumbnailGenerator> 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<Path> 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<Path> 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();

View File

@@ -31,3 +31,7 @@ api:
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
# thumbnail:
# jellyfin:
# paths: {}