Save metadata to library, Add thumbnail caching

This commit is contained in:
MrLetsplay 2025-02-12 22:40:35 +01:00
parent f3e321d8c7
commit 04b17abdee
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
6 changed files with 91 additions and 15 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
/target/
/dependency-reduced-pom.xml
/library
/cache
/config.json

View File

@ -1,5 +1,6 @@
package me.mrletsplay.videobase;
import me.mrletsplay.mrcore.json.converter.JSONConstructor;
import me.mrletsplay.mrcore.json.converter.JSONConvertible;
import me.mrletsplay.mrcore.json.converter.JSONValue;
@ -8,11 +9,24 @@ public class Config implements JSONConvertible {
@JSONValue
private String libraryPath;
@JSONValue
private String cachePath;
@JSONConstructor
private Config() {}
public String getLibraryPath() {
return libraryPath;
}
public String getCachePath() {
return cachePath;
}
public static Config createDefault() {
Config config = new Config();
config.libraryPath = "library";
config.cachePath = "cache";
return config;
}

View File

@ -1,5 +1,8 @@
package me.mrletsplay.videobase;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@ -7,25 +10,47 @@ import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.NOPLogger;
import me.mrletsplay.mrcore.json.JSONObject;
import me.mrletsplay.mrcore.json.converter.JSONConverter;
import me.mrletsplay.mrcore.json.converter.SerializationOption;
import me.mrletsplay.simplehttpserver.http.cors.CorsConfiguration;
import me.mrletsplay.simplehttpserver.http.server.HttpServer;
import me.mrletsplay.videobase.library.Library;
import me.mrletsplay.videobase.rest.LibraryAPI;
import me.mrletsplay.videobase.util.Hash;
public class VideoBase {
public static final Logger LOGGER = LoggerFactory.getLogger(VideoBase.class.getName());
private static ScheduledExecutorService executor;
private static Config config;
private static Library library;
public static void main(String[] args) {
executor = Executors.newScheduledThreadPool(0);
// Verify hash is working
Hash.hash("test");
Path configPath = Path.of("config.json");
if(args.length >= 1) {
configPath = Path.of(args[0]);
}
configPath = configPath.toAbsolutePath();
try {
if(!Files.exists(configPath)) {
Files.createDirectories(configPath.getParent());
Files.writeString(configPath, Config.createDefault().toJSON(SerializationOption.DONT_INCLUDE_CLASS).toFancyString(), StandardCharsets.UTF_8);
LOGGER.info("Default configuration created, please edit it and restart");
System.exit(0);
}
config = JSONConverter.decodeObject(new JSONObject(Files.readString(configPath, StandardCharsets.UTF_8)), Config.class);
} catch (IOException e) {
LOGGER.error("Failed to load config", e);
System.exit(1);
}
loadLibrary();
executor.scheduleWithFixedDelay(VideoBase::loadLibrary, 10, 10, TimeUnit.MINUTES);
@ -35,7 +60,7 @@ public class VideoBase {
.port(6969)
.poolSize(20)
.ioWorkers(3)
// .logger(NOPLogger.NOP_LOGGER)
.logger(NOPLogger.NOP_LOGGER)
.defaultCorsConfiguration(CorsConfiguration.createAllowAll())
.create());
@ -44,12 +69,16 @@ public class VideoBase {
server.start();
}
public static Config getConfig() {
return config;
}
public static Library getLibrary() {
return library;
}
private static void loadLibrary() {
library = Library.load(Path.of("/mnt/wd4tb/Files/ytdl/Aliensrock/"));
library = Library.load(Path.of(config.getLibraryPath()));
System.out.println(library.getVideos());
}

View File

@ -1,6 +1,7 @@
package me.mrletsplay.videobase.library;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@ -13,6 +14,7 @@ import java.util.stream.Collectors;
import me.mrletsplay.mrcore.json.JSONObject;
import me.mrletsplay.mrcore.json.JSONParseException;
import me.mrletsplay.mrcore.json.converter.JSONConverter;
import me.mrletsplay.mrcore.json.converter.SerializationOption;
import me.mrletsplay.videobase.VideoBase;
import me.mrletsplay.videobase.util.Hash;
@ -75,10 +77,18 @@ public class Library {
.collect(Collectors.toList());
}
public void updateMetadata(Video video, VideoMetadata metadata) {
public boolean updateMetadata(Video video, VideoMetadata metadata) {
VideoMetadata oldMetadata = video.getMetadata();
video.setMetadata(VideoMetadata.inherit(oldMetadata, metadata));
// TODO: save metadata
VideoMetadata newMetadata = VideoMetadata.inherit(oldMetadata, metadata);
Path videoPath = video.getPath().toAbsolutePath();
try {
Files.writeString(videoPath.getParent().resolve(videoPath.getFileName().toString() + VIDEO_METADATA_SUFFIX), newMetadata.toJSON(SerializationOption.DONT_INCLUDE_CLASS).toFancyString(), StandardCharsets.UTF_8);
video.setMetadata(newMetadata);
return true;
} catch (IOException e) {
return false;
}
}
public boolean updateMetadata(String videoId, VideoMetadata metadata) {

View File

@ -138,7 +138,7 @@ public class LibraryAPI implements EndpointCollection {
return;
}
BufferedImage thumbnail = ThumbnailCreator.createThumbnail(video.getPath());
BufferedImage thumbnail = ThumbnailCreator.createThumbnail(video);
if(thumbnail == null) {
ctx.respond(HttpStatusCodes.NOT_FOUND_404, new TextResponse("No thumbnail found"));
return;

View File

@ -4,18 +4,36 @@ import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import me.mrletsplay.videobase.VideoBase;
import me.mrletsplay.videobase.library.Video;
public class ThumbnailCreator {
public static BufferedImage createThumbnail(Path videoPath) {
public static BufferedImage createThumbnail(Video video) {
Path cacheDir = Path.of(VideoBase.getConfig().getCachePath()).resolve("thumbnails").toAbsolutePath();
Path thumbnailFile = cacheDir.resolve(video.getId() + ".png");
try {
Files.createDirectories(cacheDir);
if(Files.exists(thumbnailFile)) {
try(InputStream in = Files.newInputStream(thumbnailFile)) {
return ImageIO.read(in);
}
}
} catch (IOException e) {
VideoBase.LOGGER.warn("Failed to load thumbnail", e);
return null;
}
ProcessBuilder ffmpegBuilder = new ProcessBuilder("ffmpeg",
"-i", videoPath.toAbsolutePath().toString(),
"-i", video.getPath().toAbsolutePath().toString(),
"-vf", "thumbnail",
"-vf", "scale=960:540:force_original_aspect_ratio=decrease,pad=960:540:-1:-1:color=black",
"-frames:v", "1",
@ -26,11 +44,9 @@ public class ThumbnailCreator {
try {
Process ffmpeg = ffmpegBuilder.start();
long start = System.currentTimeMillis();
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len;
while(System.currentTimeMillis() - start < 1000
@ -51,9 +67,14 @@ public class ThumbnailCreator {
return null;
}
return ImageIO.read(new ByteArrayInputStream(bOut.toByteArray()));
BufferedImage thumbnail = ImageIO.read(new ByteArrayInputStream(bOut.toByteArray()));
try(OutputStream out = Files.newOutputStream(thumbnailFile)) {
ImageIO.write(thumbnail, "PNG", out);
}
return thumbnail;
} catch (InterruptedException | IOException e) {
e.printStackTrace();
VideoBase.LOGGER.warn("Failed to create thumbnail", e);
return null;
}