Improve thumbnail creation
All checks were successful
Build and push container / Build-Docker-Container (push) Successful in 5m44s
All checks were successful
Build and push container / Build-Docker-Container (push) Successful in 5m44s
This commit is contained in:
parent
a45b809a99
commit
444e42c091
@ -85,6 +85,7 @@ public class VideoBase {
|
|||||||
private static void loadLibrary() {
|
private static void loadLibrary() {
|
||||||
library = Library.load(Path.of(config.getLibraryPath()), config.isReadOnly());
|
library = Library.load(Path.of(config.getLibraryPath()), config.isReadOnly());
|
||||||
ThumbnailCreator.clearCache();
|
ThumbnailCreator.clearCache();
|
||||||
|
ThumbnailCreator.createThumbnails();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ public class LibraryAPI implements EndpointCollection {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedImage thumbnail = ThumbnailCreator.createThumbnail(video);
|
BufferedImage thumbnail = ThumbnailCreator.getThumbnail(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;
|
||||||
|
@ -8,8 +8,10 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
@ -19,33 +21,34 @@ import me.mrletsplay.videobase.library.Video;
|
|||||||
|
|
||||||
public class ThumbnailCreator {
|
public class ThumbnailCreator {
|
||||||
|
|
||||||
private static Map<String, BufferedImage> cachedThumbnails = new HashMap<>();
|
private static Map<String, BufferedImage> cachedThumbnails = new ConcurrentHashMap<>();
|
||||||
|
private static ExecutorService executorService = Executors.newFixedThreadPool(10);
|
||||||
|
|
||||||
public static void clearCache() {
|
public static void clearCache() {
|
||||||
cachedThumbnails.clear();
|
cachedThumbnails.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void createThumbnails() {
|
||||||
|
for(Video v : VideoBase.getLibrary().getVideos()) {
|
||||||
|
executorService.submit(() -> createThumbnail(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static BufferedImage createThumbnail(Video video) {
|
public static BufferedImage createThumbnail(Video video) {
|
||||||
if(cachedThumbnails.containsKey(video.getId())) {
|
if(cachedThumbnails.containsKey(video.getId())) {
|
||||||
return cachedThumbnails.get(video.getId());
|
return cachedThumbnails.get(video.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
String cachePath = VideoBase.getConfig().getCachePath();
|
String cachePath = VideoBase.getConfig().getCachePath();
|
||||||
Path thumbnailFile = null;
|
Path thumbnailFile = cachePath == null ? null : Path.of(cachePath).resolve("thumbnails").resolve(video.getId() + ".png").toAbsolutePath();
|
||||||
|
|
||||||
if(cachePath != null) {
|
if(thumbnailFile != null && Files.isRegularFile(thumbnailFile)) {
|
||||||
thumbnailFile = Path.of(cachePath).resolve("thumbnails").resolve(video.getId() + ".png").toAbsolutePath();
|
try(InputStream in = Files.newInputStream(thumbnailFile)) {
|
||||||
|
BufferedImage thumbnail = ImageIO.read(in);
|
||||||
|
if(thumbnail == null) return null;
|
||||||
|
|
||||||
try {
|
cachedThumbnails.put(video.getId(), thumbnail);
|
||||||
if(Files.exists(thumbnailFile)) {
|
return thumbnail;
|
||||||
try(InputStream in = Files.newInputStream(thumbnailFile)) {
|
|
||||||
BufferedImage thumbnail = ImageIO.read(in);
|
|
||||||
if(thumbnail == null) return null;
|
|
||||||
|
|
||||||
cachedThumbnails.put(video.getId(), thumbnail);
|
|
||||||
return thumbnail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
VideoBase.LOGGER.warn("Failed to load thumbnail", e);
|
VideoBase.LOGGER.warn("Failed to load thumbnail", e);
|
||||||
return null;
|
return null;
|
||||||
@ -54,8 +57,8 @@ public class ThumbnailCreator {
|
|||||||
|
|
||||||
ProcessBuilder ffmpegBuilder = new ProcessBuilder("ffmpeg",
|
ProcessBuilder ffmpegBuilder = new ProcessBuilder("ffmpeg",
|
||||||
"-i", video.getPath().toAbsolutePath().toString(),
|
"-i", video.getPath().toAbsolutePath().toString(),
|
||||||
"-vf", "thumbnail",
|
"-r", "0.5",
|
||||||
"-vf", "scale=960:540:force_original_aspect_ratio=decrease,pad=960:540:-1:-1:color=black",
|
"-vf", "thumbnail,scale=960:540:force_original_aspect_ratio=decrease,pad=960:540:-1:-1:color=black",
|
||||||
"-frames:v", "1",
|
"-frames:v", "1",
|
||||||
"-f", "image2pipe",
|
"-f", "image2pipe",
|
||||||
"-vcodec", "png",
|
"-vcodec", "png",
|
||||||
@ -69,18 +72,18 @@ public class ThumbnailCreator {
|
|||||||
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 < 60000
|
||||||
&& (len = ffmpeg.getInputStream().read(buf)) > 0) {
|
&& (len = ffmpeg.getInputStream().read(buf)) != -1) {
|
||||||
bOut.write(buf, 0, len);
|
bOut.write(buf, 0, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
ffmpeg.waitFor(10, TimeUnit.SECONDS);
|
ffmpeg.waitFor(60, TimeUnit.SECONDS);
|
||||||
if(ffmpeg.isAlive()) {
|
if(ffmpeg.isAlive()) {
|
||||||
VideoBase.LOGGER.warn("ffmpeg didn't exit after 10 seconds, destroying");
|
VideoBase.LOGGER.warn("Video " + video.getId() + ": ffmpeg didn't exit after 60 seconds, destroying");
|
||||||
ffmpeg.destroy();
|
ffmpeg.destroy();
|
||||||
|
|
||||||
if(!ffmpeg.waitFor(10, TimeUnit.SECONDS)) {
|
if(!ffmpeg.waitFor(60, TimeUnit.SECONDS)) {
|
||||||
VideoBase.LOGGER.warn("ffmpeg didn't exit 10 seconds after destroying, destroying forcibly");
|
VideoBase.LOGGER.warn("Video " + video.getId() + ": ffmpeg didn't exit 60 seconds after destroying, destroying forcibly");
|
||||||
ffmpeg.destroyForcibly();
|
ffmpeg.destroyForcibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,14 +100,17 @@ public class ThumbnailCreator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VideoBase.LOGGER.debug("Created thumbnail for video at " + video.getPath());
|
||||||
cachedThumbnails.put(video.getId(), thumbnail);
|
cachedThumbnails.put(video.getId(), thumbnail);
|
||||||
return thumbnail;
|
return thumbnail;
|
||||||
} catch (InterruptedException | IOException e) {
|
} catch (InterruptedException | IOException e) {
|
||||||
VideoBase.LOGGER.warn("Failed to create thumbnail", e);
|
VideoBase.LOGGER.warn("Failed to create thumbnail", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BufferedImage getThumbnail(Video video) {
|
||||||
|
return cachedThumbnails.get(video.getId());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user