Save metadata to library, Add thumbnail caching
This commit is contained in:
parent
f3e321d8c7
commit
04b17abdee
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@
|
|||||||
/target/
|
/target/
|
||||||
/dependency-reduced-pom.xml
|
/dependency-reduced-pom.xml
|
||||||
/library
|
/library
|
||||||
|
/cache
|
||||||
|
/config.json
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package me.mrletsplay.videobase;
|
package me.mrletsplay.videobase;
|
||||||
|
|
||||||
|
import me.mrletsplay.mrcore.json.converter.JSONConstructor;
|
||||||
import me.mrletsplay.mrcore.json.converter.JSONConvertible;
|
import me.mrletsplay.mrcore.json.converter.JSONConvertible;
|
||||||
import me.mrletsplay.mrcore.json.converter.JSONValue;
|
import me.mrletsplay.mrcore.json.converter.JSONValue;
|
||||||
|
|
||||||
@ -8,11 +9,24 @@ public class Config implements JSONConvertible {
|
|||||||
@JSONValue
|
@JSONValue
|
||||||
private String libraryPath;
|
private String libraryPath;
|
||||||
|
|
||||||
|
@JSONValue
|
||||||
|
private String cachePath;
|
||||||
|
|
||||||
|
@JSONConstructor
|
||||||
private Config() {}
|
private Config() {}
|
||||||
|
|
||||||
|
public String getLibraryPath() {
|
||||||
|
return libraryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCachePath() {
|
||||||
|
return cachePath;
|
||||||
|
}
|
||||||
|
|
||||||
public static Config createDefault() {
|
public static Config createDefault() {
|
||||||
Config config = new Config();
|
Config config = new Config();
|
||||||
config.libraryPath = "library";
|
config.libraryPath = "library";
|
||||||
|
config.cachePath = "cache";
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package me.mrletsplay.videobase;
|
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.nio.file.Path;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
@ -7,25 +10,47 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.cors.CorsConfiguration;
|
||||||
import me.mrletsplay.simplehttpserver.http.server.HttpServer;
|
import me.mrletsplay.simplehttpserver.http.server.HttpServer;
|
||||||
import me.mrletsplay.videobase.library.Library;
|
import me.mrletsplay.videobase.library.Library;
|
||||||
import me.mrletsplay.videobase.rest.LibraryAPI;
|
import me.mrletsplay.videobase.rest.LibraryAPI;
|
||||||
import me.mrletsplay.videobase.util.Hash;
|
|
||||||
|
|
||||||
public class VideoBase {
|
public class VideoBase {
|
||||||
|
|
||||||
public static final Logger LOGGER = LoggerFactory.getLogger(VideoBase.class.getName());
|
public static final Logger LOGGER = LoggerFactory.getLogger(VideoBase.class.getName());
|
||||||
|
|
||||||
private static ScheduledExecutorService executor;
|
private static ScheduledExecutorService executor;
|
||||||
|
private static Config config;
|
||||||
private static Library library;
|
private static Library library;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
executor = Executors.newScheduledThreadPool(0);
|
executor = Executors.newScheduledThreadPool(0);
|
||||||
|
|
||||||
// Verify hash is working
|
Path configPath = Path.of("config.json");
|
||||||
Hash.hash("test");
|
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();
|
loadLibrary();
|
||||||
executor.scheduleWithFixedDelay(VideoBase::loadLibrary, 10, 10, TimeUnit.MINUTES);
|
executor.scheduleWithFixedDelay(VideoBase::loadLibrary, 10, 10, TimeUnit.MINUTES);
|
||||||
@ -35,7 +60,7 @@ public class VideoBase {
|
|||||||
.port(6969)
|
.port(6969)
|
||||||
.poolSize(20)
|
.poolSize(20)
|
||||||
.ioWorkers(3)
|
.ioWorkers(3)
|
||||||
// .logger(NOPLogger.NOP_LOGGER)
|
.logger(NOPLogger.NOP_LOGGER)
|
||||||
.defaultCorsConfiguration(CorsConfiguration.createAllowAll())
|
.defaultCorsConfiguration(CorsConfiguration.createAllowAll())
|
||||||
.create());
|
.create());
|
||||||
|
|
||||||
@ -44,12 +69,16 @@ public class VideoBase {
|
|||||||
server.start();
|
server.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Config getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
public static Library getLibrary() {
|
public static Library getLibrary() {
|
||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void loadLibrary() {
|
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());
|
System.out.println(library.getVideos());
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package me.mrletsplay.videobase.library;
|
package me.mrletsplay.videobase.library;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -13,6 +14,7 @@ import java.util.stream.Collectors;
|
|||||||
import me.mrletsplay.mrcore.json.JSONObject;
|
import me.mrletsplay.mrcore.json.JSONObject;
|
||||||
import me.mrletsplay.mrcore.json.JSONParseException;
|
import me.mrletsplay.mrcore.json.JSONParseException;
|
||||||
import me.mrletsplay.mrcore.json.converter.JSONConverter;
|
import me.mrletsplay.mrcore.json.converter.JSONConverter;
|
||||||
|
import me.mrletsplay.mrcore.json.converter.SerializationOption;
|
||||||
import me.mrletsplay.videobase.VideoBase;
|
import me.mrletsplay.videobase.VideoBase;
|
||||||
import me.mrletsplay.videobase.util.Hash;
|
import me.mrletsplay.videobase.util.Hash;
|
||||||
|
|
||||||
@ -75,10 +77,18 @@ public class Library {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateMetadata(Video video, VideoMetadata metadata) {
|
public boolean updateMetadata(Video video, VideoMetadata metadata) {
|
||||||
VideoMetadata oldMetadata = video.getMetadata();
|
VideoMetadata oldMetadata = video.getMetadata();
|
||||||
video.setMetadata(VideoMetadata.inherit(oldMetadata, metadata));
|
VideoMetadata newMetadata = VideoMetadata.inherit(oldMetadata, metadata);
|
||||||
// TODO: save 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) {
|
public boolean updateMetadata(String videoId, VideoMetadata metadata) {
|
||||||
|
@ -138,7 +138,7 @@ public class LibraryAPI implements EndpointCollection {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedImage thumbnail = ThumbnailCreator.createThumbnail(video.getPath());
|
BufferedImage thumbnail = ThumbnailCreator.createThumbnail(video);
|
||||||
if(thumbnail == null) {
|
if(thumbnail == null) {
|
||||||
ctx.respond(HttpStatusCodes.NOT_FOUND_404, new TextResponse("No thumbnail found"));
|
ctx.respond(HttpStatusCodes.NOT_FOUND_404, new TextResponse("No thumbnail found"));
|
||||||
return;
|
return;
|
||||||
|
@ -4,18 +4,36 @@ import java.awt.image.BufferedImage;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import me.mrletsplay.videobase.VideoBase;
|
import me.mrletsplay.videobase.VideoBase;
|
||||||
|
import me.mrletsplay.videobase.library.Video;
|
||||||
|
|
||||||
public class ThumbnailCreator {
|
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",
|
ProcessBuilder ffmpegBuilder = new ProcessBuilder("ffmpeg",
|
||||||
"-i", videoPath.toAbsolutePath().toString(),
|
"-i", video.getPath().toAbsolutePath().toString(),
|
||||||
"-vf", "thumbnail",
|
"-vf", "thumbnail",
|
||||||
"-vf", "scale=960:540:force_original_aspect_ratio=decrease,pad=960:540:-1:-1:color=black",
|
"-vf", "scale=960:540:force_original_aspect_ratio=decrease,pad=960:540:-1:-1:color=black",
|
||||||
"-frames:v", "1",
|
"-frames:v", "1",
|
||||||
@ -26,11 +44,9 @@ public class ThumbnailCreator {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Process ffmpeg = ffmpegBuilder.start();
|
Process ffmpeg = ffmpegBuilder.start();
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
|
|
||||||
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
|
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
|
||||||
|
|
||||||
byte[] buf = new byte[1024];
|
byte[] buf = new byte[1024];
|
||||||
int len;
|
int len;
|
||||||
while(System.currentTimeMillis() - start < 1000
|
while(System.currentTimeMillis() - start < 1000
|
||||||
@ -51,9 +67,14 @@ public class ThumbnailCreator {
|
|||||||
return null;
|
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) {
|
} catch (InterruptedException | IOException e) {
|
||||||
e.printStackTrace();
|
VideoBase.LOGGER.warn("Failed to create thumbnail", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user