diff --git a/src/main/java/me/mrletsplay/videobase/VideoBase.java b/src/main/java/me/mrletsplay/videobase/VideoBase.java index a3ea175..9753933 100644 --- a/src/main/java/me/mrletsplay/videobase/VideoBase.java +++ b/src/main/java/me/mrletsplay/videobase/VideoBase.java @@ -8,6 +8,7 @@ import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +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; @@ -35,6 +36,7 @@ public class VideoBase { .poolSize(20) .ioWorkers(3) // .logger(NOPLogger.NOP_LOGGER) + .defaultCorsConfiguration(CorsConfiguration.createAllowAll()) .create()); new LibraryAPI().register(server.getDocumentProvider()); diff --git a/src/main/java/me/mrletsplay/videobase/rest/LibraryAPI.java b/src/main/java/me/mrletsplay/videobase/rest/LibraryAPI.java index 15ff79e..b69ee35 100644 --- a/src/main/java/me/mrletsplay/videobase/rest/LibraryAPI.java +++ b/src/main/java/me/mrletsplay/videobase/rest/LibraryAPI.java @@ -1,7 +1,12 @@ package me.mrletsplay.videobase.rest; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.List; +import javax.imageio.ImageIO; + import me.mrletsplay.mrcore.json.JSONArray; import me.mrletsplay.mrcore.json.JSONObject; import me.mrletsplay.simplehttpserver.http.HttpRequestMethod; @@ -13,11 +18,13 @@ import me.mrletsplay.simplehttpserver.http.header.DefaultClientContentTypes; import me.mrletsplay.simplehttpserver.http.request.HttpRequestContext; import me.mrletsplay.simplehttpserver.http.response.JsonResponse; import me.mrletsplay.simplehttpserver.http.response.TextResponse; +import me.mrletsplay.simplehttpserver.http.util.MimeType; import me.mrletsplay.simplehttpserver.http.validation.result.ValidationResult; import me.mrletsplay.videobase.VideoBase; import me.mrletsplay.videobase.library.Library; import me.mrletsplay.videobase.library.Video; import me.mrletsplay.videobase.library.VideoMetadata; +import me.mrletsplay.videobase.util.ThumbnailCreator; public class LibraryAPI implements EndpointCollection { @@ -71,10 +78,6 @@ public class LibraryAPI implements EndpointCollection { videosObj.put(group, videosArr); } - if(!"none".equals("groupBy")) { - - } - libraryObj.put("videos", videosObj); ctx.respond(HttpStatusCodes.OK_200, new JsonResponse(libraryObj)); @@ -127,6 +130,30 @@ public class LibraryAPI implements EndpointCollection { ctx.respond(HttpStatusCodes.OK_200, new JsonResponse(video.getMetadata().getRaw())); } + @Endpoint(method = HttpRequestMethod.GET, path = "/video/{video}/thumbnail", pathPattern = true) + public void getThumbnail(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; + } + + BufferedImage thumbnail = ThumbnailCreator.createThumbnail(video.getPath()); + if(thumbnail == null) { + ctx.respond(HttpStatusCodes.NOT_FOUND_404, new TextResponse("No thumbnail found")); + return; + } + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + try { + ImageIO.write(thumbnail, "PNG", bOut); + } catch (IOException e) { + ctx.respond(HttpStatusCodes.INTERNAL_SERVER_ERROR_500, new TextResponse("Failed to create thumbnail")); + } + + ctx.getServerHeader().setContent(MimeType.PNG, bOut.toByteArray()); + } + private JSONObject videoToJSON(Video video) { JSONObject v = new JSONObject(); v.put("id", video.getId()); diff --git a/src/main/java/me/mrletsplay/videobase/util/ThumbnailCreator.java b/src/main/java/me/mrletsplay/videobase/util/ThumbnailCreator.java new file mode 100644 index 0000000..20acaa1 --- /dev/null +++ b/src/main/java/me/mrletsplay/videobase/util/ThumbnailCreator.java @@ -0,0 +1,63 @@ +package me.mrletsplay.videobase.util; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +import javax.imageio.ImageIO; + +import me.mrletsplay.videobase.VideoBase; + +public class ThumbnailCreator { + + public static BufferedImage createThumbnail(Path videoPath) { + ProcessBuilder ffmpegBuilder = new ProcessBuilder("ffmpeg", + "-i", videoPath.toAbsolutePath().toString(), + "-vf", "thumbnail", + "-vf", "scale=960:540:force_original_aspect_ratio=decrease,pad=960:540:-1:-1:color=black", + "-frames:v", "1", + "-f", "image2pipe", + "-vcodec", "png", + "-" + ); + + 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 + && (len = ffmpeg.getInputStream().read(buf)) > 0) { + bOut.write(buf, 0, len); + } + + ffmpeg.waitFor(10, TimeUnit.SECONDS); + if(ffmpeg.isAlive()) { + VideoBase.LOGGER.warn("ffmpeg didn't exit after 10 seconds, destroying"); + ffmpeg.destroy(); + + if(!ffmpeg.waitFor(10, TimeUnit.SECONDS)) { + VideoBase.LOGGER.warn("ffmpeg didn't exit 10 seconds after destroying, destroying forcibly"); + ffmpeg.destroyForcibly(); + } + + return null; + } + + return ImageIO.read(new ByteArrayInputStream(bOut.toByteArray())); + } catch (InterruptedException | IOException e) { + e.printStackTrace(); + return null; + } + + } + + +}