Add readOnly config option, Imptove metadata inheritance
All checks were successful
Build and push container / Build-Docker-Container (push) Successful in 5m29s
All checks were successful
Build and push container / Build-Docker-Container (push) Successful in 5m29s
This commit is contained in:
parent
a6dd5bd6f7
commit
92b83d240d
@ -9,6 +9,9 @@ public class Config implements JSONConvertible {
|
||||
@JSONValue
|
||||
private String libraryPath;
|
||||
|
||||
@JSONValue
|
||||
private boolean readOnly;
|
||||
|
||||
@JSONValue
|
||||
private String cachePath;
|
||||
|
||||
@ -19,6 +22,10 @@ public class Config implements JSONConvertible {
|
||||
return libraryPath;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
public String getCachePath() {
|
||||
return cachePath;
|
||||
}
|
||||
@ -26,6 +33,7 @@ public class Config implements JSONConvertible {
|
||||
public static Config createDefault() {
|
||||
Config config = new Config();
|
||||
config.libraryPath = "library";
|
||||
config.readOnly = false;
|
||||
config.cachePath = "cache";
|
||||
return config;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public class VideoBase {
|
||||
}
|
||||
|
||||
private static void loadLibrary() {
|
||||
library = Library.load(Path.of(config.getLibraryPath()));
|
||||
library = Library.load(Path.of(config.getLibraryPath()), config.isReadOnly());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,13 +25,15 @@ public class Library {
|
||||
VIDEO_METADATA_SUFFIX = ".meta.json";
|
||||
|
||||
private Path path;
|
||||
private boolean readOnly;
|
||||
private List<Video> videos;
|
||||
|
||||
public Library(Path path, List<Video> videos) {
|
||||
public Library(Path path, boolean readOnly, List<Video> videos) {
|
||||
Objects.requireNonNull(path, "path");
|
||||
Objects.requireNonNull(videos, "videos");
|
||||
|
||||
this.path = path;
|
||||
this.readOnly = readOnly;
|
||||
this.videos = videos;
|
||||
}
|
||||
|
||||
@ -39,6 +41,10 @@ public class Library {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
public List<Video> getVideos() {
|
||||
return videos;
|
||||
}
|
||||
@ -78,8 +84,10 @@ public class Library {
|
||||
}
|
||||
|
||||
public boolean updateMetadata(Video video, VideoMetadata metadata) {
|
||||
if(readOnly) return false;
|
||||
|
||||
VideoMetadata oldMetadata = video.getMetadata();
|
||||
VideoMetadata newMetadata = VideoMetadata.inherit(oldMetadata, metadata);
|
||||
VideoMetadata newMetadata = VideoMetadata.inherit(oldMetadata.getParent(), metadata);
|
||||
|
||||
Path videoPath = video.getPath().toAbsolutePath();
|
||||
try {
|
||||
@ -94,28 +102,32 @@ public class Library {
|
||||
public boolean updateMetadata(String videoId, VideoMetadata metadata) {
|
||||
Video video = findVideoById(videoId);
|
||||
if(video == null) return false;
|
||||
updateMetadata(video, metadata);
|
||||
return true;
|
||||
return updateMetadata(video, metadata);
|
||||
}
|
||||
|
||||
public static Library load(Path path) {
|
||||
public static Library load(Path path, boolean readOnly) {
|
||||
List<Video> videos = new ArrayList<>();
|
||||
load(path, path, videos, VideoMetadata.DEFAULT_METADATA);
|
||||
return new Library(path, videos);
|
||||
return new Library(path, readOnly, videos);
|
||||
}
|
||||
|
||||
private static void load(Path rootPath, Path path, List<Video> videos, VideoMetadata parentDefaultMetadata) {
|
||||
if(!Files.isDirectory(path)) return;
|
||||
|
||||
LibraryMetadata libraryMeta = null;
|
||||
VideoMetadata defaultMetadata = parentDefaultMetadata;
|
||||
LibraryMetadata libraryMetadata = null;
|
||||
|
||||
VideoMetadata inferredMetadata = new VideoMetadata(new JSONObject(Map.of(
|
||||
VideoMetadata.FIELD_SERIES, path.getFileName().toString()
|
||||
)));
|
||||
|
||||
VideoMetadata defaultMetadata = VideoMetadata.inherit(parentDefaultMetadata, inferredMetadata);
|
||||
|
||||
Path metaPath = path.resolve("meta.json");
|
||||
if(Files.isRegularFile(metaPath)) {
|
||||
try {
|
||||
JSONObject obj = new JSONObject(Files.readString(metaPath));
|
||||
libraryMeta = JSONConverter.decodeObject(obj, LibraryMetadata.class);
|
||||
defaultMetadata = VideoMetadata.inherit(defaultMetadata, libraryMeta.getDefault());
|
||||
libraryMetadata = JSONConverter.decodeObject(obj, LibraryMetadata.class);
|
||||
defaultMetadata = VideoMetadata.inherit(defaultMetadata, libraryMetadata.getDefault());
|
||||
}catch(IOException | JSONParseException | ClassCastException e) {
|
||||
VideoBase.LOGGER.warn("Failed to parse metadata at " + path, e);
|
||||
}
|
||||
@ -141,27 +153,26 @@ public class Library {
|
||||
continue;
|
||||
}
|
||||
|
||||
VideoMetadata inferredMetadata = new VideoMetadata(new JSONObject(Map.of(
|
||||
VideoMetadata.FIELD_TITLE, fileNameNoExt,
|
||||
VideoMetadata.FIELD_SERIES, path.getFileName().toString()
|
||||
VideoMetadata inferredVideoMetadata = new VideoMetadata(new JSONObject(Map.of(
|
||||
VideoMetadata.FIELD_TITLE, fileNameNoExt
|
||||
)));
|
||||
|
||||
VideoMetadata videoMeta = VideoMetadata.inherit(defaultMetadata, inferredMetadata);
|
||||
VideoMetadata videoMetadata = VideoMetadata.inherit(defaultMetadata, inferredVideoMetadata);
|
||||
|
||||
Path videoMetaPath = path.resolve(subPath.getFileName().toString() + VIDEO_METADATA_SUFFIX);
|
||||
if(Files.isRegularFile(videoMetaPath)) {
|
||||
try {
|
||||
JSONObject obj = new JSONObject(Files.readString(videoMetaPath));
|
||||
videoMeta = VideoMetadata.inherit(videoMeta, JSONConverter.decodeObject(obj, VideoMetadata.class));
|
||||
videoMetadata = VideoMetadata.inherit(videoMetadata, JSONConverter.decodeObject(obj, VideoMetadata.class));
|
||||
}catch(IOException | JSONParseException | ClassCastException e) {
|
||||
VideoBase.LOGGER.warn("Failed to parse metadata at " + path, e);
|
||||
}
|
||||
}else if(libraryMeta != null && libraryMeta.getOverrides().containsKey(fileName)) {
|
||||
videoMeta = VideoMetadata.inherit(videoMeta, libraryMeta.getOverrides().get(fileName));
|
||||
}else if(libraryMetadata != null && libraryMetadata.getOverrides().containsKey(fileName)) {
|
||||
videoMetadata = VideoMetadata.inherit(videoMetadata, libraryMetadata.getOverrides().get(fileName));
|
||||
}
|
||||
|
||||
String id = Hash.hash(rootPath.relativize(subPath).toString());
|
||||
videos.add(new Video(subPath, id, videoMeta == null ? VideoMetadata.DEFAULT_METADATA : videoMeta));
|
||||
videos.add(new Video(subPath, id, videoMetadata == null ? VideoMetadata.DEFAULT_METADATA : videoMetadata));
|
||||
}
|
||||
}catch(IOException e) {
|
||||
VideoBase.LOGGER.warn("Failed to load folder at " + path, e);
|
||||
|
@ -1,7 +1,6 @@
|
||||
package me.mrletsplay.videobase.library;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import me.mrletsplay.mrcore.json.JSONObject;
|
||||
import me.mrletsplay.mrcore.json.JSONType;
|
||||
@ -38,36 +37,61 @@ public class VideoMetadata implements JSONConvertible {
|
||||
.optional(FIELD_AUTHOR, JSONType.STRING)
|
||||
.optional(FIELD_INDEX, JSONType.INTEGER);
|
||||
|
||||
private VideoMetadata parent;
|
||||
private JSONObject metadata;
|
||||
private JSONObject cachedEffectiveMetadata;
|
||||
|
||||
@JSONConstructor
|
||||
private VideoMetadata() {}
|
||||
|
||||
public VideoMetadata(JSONObject metadata) {
|
||||
Objects.requireNonNull(metadata, "metadata");
|
||||
this.metadata = metadata;
|
||||
this.metadata = metadata == null ? new JSONObject() : metadata;
|
||||
}
|
||||
|
||||
public String getSeries() {
|
||||
return metadata.optString(FIELD_SERIES).orElse(null);
|
||||
return getEffective().optString(FIELD_SERIES).orElse(null);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return metadata.optString(FIELD_TITLE).orElse(null);
|
||||
return getEffective().optString(FIELD_TITLE).orElse(null);
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return metadata.optString(FIELD_AUTHOR).orElse(null);
|
||||
return getEffective().optString(FIELD_AUTHOR).orElse(null);
|
||||
}
|
||||
|
||||
public Integer getIndex() {
|
||||
return metadata.optInt(FIELD_INDEX).orElse(null);
|
||||
return getEffective().optInt(FIELD_INDEX).orElse(null);
|
||||
}
|
||||
|
||||
public VideoMetadata getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public JSONObject getRaw() {
|
||||
return new JSONObject(metadata);
|
||||
}
|
||||
|
||||
public JSONObject getEffective() {
|
||||
if(cachedEffectiveMetadata != null) return cachedEffectiveMetadata;
|
||||
|
||||
JSONObject meta = new JSONObject(parent == null ? null : parent.getEffective());
|
||||
for(String key : metadata.keys()) {
|
||||
Object value = metadata.get(key);
|
||||
if(value == null) { // Setting to null resets to default value if available, otherwise removes attribute
|
||||
value = DEFAULT_METADATA.metadata.opt(key).orElse(null);
|
||||
if(value == null) {
|
||||
meta.remove(key);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
meta.set(key, value);
|
||||
}
|
||||
|
||||
return cachedEffectiveMetadata = meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDeserialize(JSONObject object) {
|
||||
this.metadata = object;
|
||||
@ -86,24 +110,9 @@ public class VideoMetadata implements JSONConvertible {
|
||||
}
|
||||
|
||||
public static VideoMetadata inherit(VideoMetadata parent, VideoMetadata child) {
|
||||
if(parent == null) return child;
|
||||
if(child == null) return parent;
|
||||
|
||||
JSONObject meta = new JSONObject(parent == null ? null : parent.metadata);
|
||||
for(String key : child.metadata.keys()) {
|
||||
Object value = child.metadata.get(key);
|
||||
if(value == null) { // Setting to null resets to default value if available, otherwise removes attribute
|
||||
value = DEFAULT_METADATA.metadata.opt(key).orElse(null);
|
||||
if(value == null) {
|
||||
meta.remove(key);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
meta.set(key, value);
|
||||
}
|
||||
|
||||
return new VideoMetadata(meta);
|
||||
VideoMetadata meta = new VideoMetadata(child == null ? null : child.getRaw());
|
||||
meta.parent = parent;
|
||||
return meta;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -98,19 +98,31 @@ public class LibraryAPI implements EndpointCollection {
|
||||
}
|
||||
|
||||
@Endpoint(method = HttpRequestMethod.GET, path = "/video/{video}/metadata", pathPattern = true)
|
||||
public void getMetadata(HttpRequestContext ctx, @RequestParameter("video") String videoId) {
|
||||
public void getVideoMetadata(HttpRequestContext ctx, @RequestParameter("video") String videoId) {
|
||||
Video video = VideoBase.getLibrary().findVideoById(videoId);
|
||||
if(video == null) {
|
||||
ctx.respond(HttpStatusCodes.NOT_FOUND_404, new TextResponse("Not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.respond(HttpStatusCodes.OK_200, new JsonResponse(video.getMetadata().getRaw()));
|
||||
VideoMetadata videoMetadata = video.getMetadata();
|
||||
|
||||
JSONObject meta = new JSONObject();
|
||||
meta.put("raw", videoMetadata.getRaw());
|
||||
meta.put("inherited", videoMetadata.getParent() == null ? new JSONObject() : videoMetadata.getParent().getEffective());
|
||||
meta.put("effective", videoMetadata.getEffective());
|
||||
|
||||
ctx.respond(HttpStatusCodes.OK_200, new JsonResponse(meta));
|
||||
}
|
||||
|
||||
@Endpoint(method = HttpRequestMethod.PUT, path = "/video/{video}/metadata", pathPattern = true)
|
||||
public void updateVideoMetadata(HttpRequestContext ctx, @RequestParameter("video") String videoId) {
|
||||
Library library = VideoBase.getLibrary();
|
||||
if(library.isReadOnly()) {
|
||||
ctx.respond(HttpStatusCodes.ACCESS_DENIED_403, new TextResponse("Library is read-only"));
|
||||
return;
|
||||
}
|
||||
|
||||
Video video = VideoBase.getLibrary().findVideoById(videoId);
|
||||
if(video == null) {
|
||||
ctx.respond(HttpStatusCodes.NOT_FOUND_404, new TextResponse("Not found"));
|
||||
@ -129,7 +141,11 @@ public class LibraryAPI implements EndpointCollection {
|
||||
return;
|
||||
}
|
||||
|
||||
library.updateMetadata(video, new VideoMetadata(metadata));
|
||||
if(!library.updateMetadata(video, new VideoMetadata(metadata))) {
|
||||
ctx.respond(HttpStatusCodes.INTERNAL_SERVER_ERROR_500, new TextResponse("Failed to update metadata"));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.respond(HttpStatusCodes.OK_200, new JsonResponse(video.getMetadata().getRaw()));
|
||||
}
|
||||
|
||||
@ -185,7 +201,7 @@ public class LibraryAPI implements EndpointCollection {
|
||||
private JSONObject videoToJSON(Video video) {
|
||||
JSONObject v = new JSONObject();
|
||||
v.put("id", video.getId());
|
||||
v.put("metadata", video.getMetadata().getRaw());
|
||||
v.put("metadata", video.getMetadata().getEffective());
|
||||
return v;
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,8 @@ public class ThumbnailCreator {
|
||||
}
|
||||
|
||||
BufferedImage thumbnail = ImageIO.read(new ByteArrayInputStream(bOut.toByteArray()));
|
||||
if(thumbnail == null) return null;
|
||||
|
||||
try(OutputStream out = Files.newOutputStream(thumbnailFile)) {
|
||||
ImageIO.write(thumbnail, "PNG", out);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user