From dcfb29a90fd88e372e89dbab568cf1a6bc21fa6d Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Tue, 26 Jan 2021 22:53:49 +0100 Subject: [PATCH 1/5] Fix online state detection for Bongacams --- common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java b/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java index 6268abb3..32fa69fa 100644 --- a/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java +++ b/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java @@ -73,6 +73,8 @@ public class BongaCamsModel extends AbstractModel { online = false; onlineState = AWAY; } + } else { + online = false; } } catch (HtmlParserException e) { online = false; From 3e7d52a9882f31c8aaf22073ac3db9a3895f3932 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 13 Feb 2021 16:36:10 +0100 Subject: [PATCH 2/5] Delete the directory itself in deleteDirectory --- common/src/main/java/ctbrec/io/IoUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/src/main/java/ctbrec/io/IoUtils.java b/common/src/main/java/ctbrec/io/IoUtils.java index a76d6879..0497c70a 100644 --- a/common/src/main/java/ctbrec/io/IoUtils.java +++ b/common/src/main/java/ctbrec/io/IoUtils.java @@ -55,6 +55,8 @@ public class IoUtils { if (!deletedAllFiles) { throw new IOException("Couldn't delete all files in " + directory); } + + Files.delete(directory.toPath()); } public static long getDirectorySize(File dir) { From 1cf62f362ddc77e67607b12fd6bff499a02552d4 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 13 Feb 2021 16:52:58 +0100 Subject: [PATCH 3/5] Speed up creation of contact sheet Use FFmpegs select filter Also add a config option to add a timestamp to the thumbs --- .../CreateContactSheetPaneFactory.java | 21 ++-- .../postprocessing/CreateContactSheet.java | 99 +++++++++++++++---- 2 files changed, 96 insertions(+), 24 deletions(-) diff --git a/client/src/main/java/ctbrec/ui/settings/CreateContactSheetPaneFactory.java b/client/src/main/java/ctbrec/ui/settings/CreateContactSheetPaneFactory.java index 6f837260..28a58faf 100644 --- a/client/src/main/java/ctbrec/ui/settings/CreateContactSheetPaneFactory.java +++ b/client/src/main/java/ctbrec/ui/settings/CreateContactSheetPaneFactory.java @@ -1,10 +1,13 @@ package ctbrec.ui.settings; -import ctbrec.recorder.postprocessing.CreateContactSheet; +import static ctbrec.recorder.postprocessing.CreateContactSheet.*; +import static java.lang.Boolean.*; + import ctbrec.recorder.postprocessing.PostProcessor; import ctbrec.ui.settings.api.Category; import ctbrec.ui.settings.api.Preferences; import ctbrec.ui.settings.api.Setting; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleStringProperty; import javafx.scene.control.ColorPicker; import javafx.scene.paint.Color; @@ -15,18 +18,21 @@ public class CreateContactSheetPaneFactory extends AbstractPostProcessingPaneFac @Override public Preferences doCreatePostProcessorPane(PostProcessor pp) { - SimpleStringProperty totalSize = new SimpleStringProperty(null, CreateContactSheet.TOTAL_SIZE, pp.getConfig().getOrDefault(CreateContactSheet.TOTAL_SIZE, "1920")); - SimpleStringProperty padding = new SimpleStringProperty(null, CreateContactSheet.PADDING, pp.getConfig().getOrDefault(CreateContactSheet.PADDING, "4")); - SimpleStringProperty cols = new SimpleStringProperty(null, CreateContactSheet.COLS, pp.getConfig().getOrDefault(CreateContactSheet.COLS, "8")); - SimpleStringProperty rows = new SimpleStringProperty(null, CreateContactSheet.ROWS, pp.getConfig().getOrDefault(CreateContactSheet.ROWS, "7")); - SimpleStringProperty filename = new SimpleStringProperty(null, CreateContactSheet.FILENAME, pp.getConfig().getOrDefault(CreateContactSheet.FILENAME, "contactsheet.jpg")); - background = new SimpleStringProperty(null, CreateContactSheet.BACKGROUND, pp.getConfig().getOrDefault(CreateContactSheet.BACKGROUND, "0x333333")); + SimpleStringProperty totalSize = new SimpleStringProperty(null, TOTAL_SIZE, pp.getConfig().getOrDefault(TOTAL_SIZE, "1920")); + SimpleStringProperty padding = new SimpleStringProperty(null, PADDING, pp.getConfig().getOrDefault(PADDING, "4")); + SimpleStringProperty cols = new SimpleStringProperty(null, COLS, pp.getConfig().getOrDefault(COLS, "8")); + SimpleStringProperty rows = new SimpleStringProperty(null, ROWS, pp.getConfig().getOrDefault(ROWS, "7")); + SimpleStringProperty filename = new SimpleStringProperty(null, FILENAME, pp.getConfig().getOrDefault(FILENAME, "contactsheet.jpg")); + background = new SimpleStringProperty(null, BACKGROUND, pp.getConfig().getOrDefault(BACKGROUND, "0x333333")); + SimpleBooleanProperty burnTimestamp = new SimpleBooleanProperty(null, BURN_IN_TIMESTAMP, + Boolean.valueOf(pp.getConfig().getOrDefault(BURN_IN_TIMESTAMP, TRUE.toString()))); properties.add(totalSize); properties.add(padding); properties.add(cols); properties.add(rows); properties.add(filename); properties.add(background); + properties.add(burnTimestamp); Setting backgroundSetting = Setting.of("", background, "Hexadecimal value of the background color for the space between the thumbnails"); Preferences prefs = Preferences.of(new MapPreferencesStorage(), @@ -37,6 +43,7 @@ public class CreateContactSheetPaneFactory extends AbstractPostProcessingPaneFac Setting.of("Rows", rows), Setting.of("File Name", filename), Setting.of("Background", createColorPicker(background.get())), + Setting.of("Timestamp", burnTimestamp, "Burn in a timestamp on each thumb"), backgroundSetting ) ); diff --git a/common/src/main/java/ctbrec/recorder/postprocessing/CreateContactSheet.java b/common/src/main/java/ctbrec/recorder/postprocessing/CreateContactSheet.java index f8a8bace..dd2ad0b2 100644 --- a/common/src/main/java/ctbrec/recorder/postprocessing/CreateContactSheet.java +++ b/common/src/main/java/ctbrec/recorder/postprocessing/CreateContactSheet.java @@ -1,20 +1,31 @@ package ctbrec.recorder.postprocessing; +import static java.lang.Boolean.*; + import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.DecimalFormat; import java.text.MessageFormat; +import java.text.NumberFormat; import java.util.Arrays; import java.util.Locale; +import javax.annotation.concurrent.ThreadSafe; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Config; import ctbrec.OS; import ctbrec.Recording; +import ctbrec.io.IoUtils; import ctbrec.recorder.FFmpeg; import ctbrec.recorder.RecordingManager; +import ctbrec.recorder.download.ProcessExitedUncleanException; +@ThreadSafe public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor { private static final Logger LOG = LoggerFactory.getLogger(CreateContactSheet.class); @@ -25,6 +36,7 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor { public static final String ROWS = "rows"; public static final String BACKGROUND = "background"; public static final String FILENAME = "filename"; + public static final String BURN_IN_TIMESTAMP = "burn_in_timestamp"; @Override @@ -40,15 +52,12 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor { int rows = Integer.parseInt(getConfig().getOrDefault(ROWS, "7")); String color = getConfig().getOrDefault(BACKGROUND, "0x333333"); String filename = getConfig().getOrDefault(FILENAME, "contactsheet.jpg"); - int thumbWidth = (int) ((totalWidth - (cols + 1) * padding) / (double)cols); - int numberOfThumbs = rows * cols; - long lengthInSeconds = rec.getLength().getSeconds(); - double thumbnailInterval = lengthInSeconds / (double)numberOfThumbs; - MessageFormat mf = new MessageFormat("fps=1/{0},scale={4}:-1,tile={1}x{2}:color={6}:margin={3}:padding={3},scale={5}:-1", Locale.ENGLISH); + Path tempDir = createThumbnails(rec, config); + + MessageFormat mf = new MessageFormat("scale={3}:-1,tile={0}x{1}:color={5}:margin={2}:padding={2},scale={4}:-1", Locale.ENGLISH); String filterArg = mf.format(new Object[] { - thumbnailInterval, cols, rows, padding, Integer.toString(thumbWidth), @@ -58,22 +67,16 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor { File executionDir = rec.getPostProcessedFile().isDirectory() ? rec.getPostProcessedFile() : rec.getPostProcessedFile().getParentFile(); File output = new File(executionDir, fillInPlaceHolders(filename, rec, config)); - File input = getInputFile(rec); String[] args = { - "-skip_frame", - "nokey", + "-v", + "quiet", + "-y", "-i", - input.getCanonicalPath(), + tempDir.toAbsolutePath().toString() + "/image%03d.png", "-vf", filterArg, - "-an", - "-vsync", - "0", "-qscale:v", "3", - "-y", - "-frames:v", - "1", output.getCanonicalPath() }; String[] cmdline = OS.getFFmpegCommand(args); @@ -85,8 +88,70 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor { .build(); ffmpeg.exec(cmdline, OS.getEnvironment(), executionDir); int exitCode = ffmpeg.waitFor(); + if (exitCode != 0) { + throw new ProcessExitedUncleanException("FFmpeg exited unclean (" + exitCode + ") when stitching together the contactsheet"); + } rec.getAssociatedFiles().add(output.getCanonicalPath()); - return exitCode != 1; + IoUtils.deleteDirectory(tempDir.toFile()); + return true; + } + + private Path createThumbnails(Recording rec, Config config) throws IOException, InterruptedException { + Path tempDirectory = Files.createTempDirectory("ctbrec-contactsheet-" + rec.getModel().getSanitizedNamed()); + File executionDir = rec.getPostProcessedFile().isDirectory() ? rec.getPostProcessedFile() : rec.getPostProcessedFile().getParentFile(); + int cols = Integer.parseInt(getConfig().getOrDefault(COLS, "8")); + int rows = Integer.parseInt(getConfig().getOrDefault(ROWS, "7")); + int numberOfThumbs = rows * cols; + long lengthInSeconds = rec.getLength().getSeconds(); + double thumbnailInterval = lengthInSeconds / (double)numberOfThumbs; + + NumberFormat nf = new DecimalFormat("000"); + for (int i = 0; i < numberOfThumbs; i++) { + double startTimeInSeconds = thumbnailInterval * i; + String tile = "image" + nf.format(i) + ".png"; + String videoFilter = "scale=-1:720,select='eq(pict_type\\,I)'"; + boolean includeTimestamp = getConfig().getOrDefault(BURN_IN_TIMESTAMP, TRUE.toString()).equals(TRUE.toString()); + if (includeTimestamp) { + int hours = (int) (startTimeInSeconds / 3600); + int minutes = (int) (startTimeInSeconds % 3600 / 60); + int seconds = (int) (startTimeInSeconds % 60); + String timestamp = String.format("%02d\\:%02d\\:%02d", hours, minutes, seconds); + videoFilter += ",drawtext='text=" + timestamp + + ":fontcolor=white:fontsize=48:box=1:boxcolor=black@0.5:boxborderw=5:x=(w-text_w-20):y=(h-text_h-20)'"; + } + + File input = getInputFile(rec); + File output = new File(tempDirectory.toFile(), tile); + String[] args = { + "-v", + "quiet", + "-y", + "-skip_frame", + "nokey", + "-ss", + Double.toString(startTimeInSeconds), + "-i", + input.getCanonicalPath(), + "-vf", + videoFilter, + "-vframes", + "1", + output.getCanonicalPath() + }; + String[] cmdline = OS.getFFmpegCommand(args); + LOG.info("Executing {} in working directory {}", Arrays.toString(cmdline), executionDir); + + FFmpeg ffmpeg = new FFmpeg.Builder() + .logOutput(config.getSettings().logFFmpegOutput) + .logFile(new File(output.getParentFile(), output.getName() + ".log")) + .build(); + ffmpeg.exec(cmdline, OS.getEnvironment(), executionDir); + int exitCode = ffmpeg.waitFor(); + if (exitCode != 0) { + throw new ProcessExitedUncleanException("FFmpeg exited unclean (" + exitCode + ") for thumbnail " + i + ' ' + Arrays.toString(cmdline)); + } + } + return tempDirectory; } private File getInputFile(Recording rec) { From cf19ab9697641c06a98469312035dbd09162cac5 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 13 Feb 2021 18:03:27 +0100 Subject: [PATCH 4/5] Remove quiet start parameter for FFmpeg --- .../ctbrec/recorder/postprocessing/CreateContactSheet.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/common/src/main/java/ctbrec/recorder/postprocessing/CreateContactSheet.java b/common/src/main/java/ctbrec/recorder/postprocessing/CreateContactSheet.java index dd2ad0b2..7ec798d2 100644 --- a/common/src/main/java/ctbrec/recorder/postprocessing/CreateContactSheet.java +++ b/common/src/main/java/ctbrec/recorder/postprocessing/CreateContactSheet.java @@ -68,8 +68,6 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor { File output = new File(executionDir, fillInPlaceHolders(filename, rec, config)); String[] args = { - "-v", - "quiet", "-y", "-i", tempDir.toAbsolutePath().toString() + "/image%03d.png", @@ -80,7 +78,7 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor { output.getCanonicalPath() }; String[] cmdline = OS.getFFmpegCommand(args); - LOG.info("Executing {} in working directory {}", Arrays.toString(cmdline), executionDir); + LOG.debug("Executing {} in working directory {}", Arrays.toString(cmdline), executionDir); File ffmpegLog = new File(System.getProperty("java.io.tmpdir"), "create_contact_sheet_" + rec.getId() + ".log"); FFmpeg ffmpeg = new FFmpeg.Builder() .logOutput(config.getSettings().logFFmpegOutput) @@ -123,8 +121,6 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor { File input = getInputFile(rec); File output = new File(tempDirectory.toFile(), tile); String[] args = { - "-v", - "quiet", "-y", "-skip_frame", "nokey", From 1e3432d2d90e5a6e67eb0a6cad9b2aa0cce6bc11 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 13 Feb 2021 18:03:56 +0100 Subject: [PATCH 5/5] Change labels for contact sheet Timestamp option --- .../java/ctbrec/ui/settings/CreateContactSheetPaneFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/java/ctbrec/ui/settings/CreateContactSheetPaneFactory.java b/client/src/main/java/ctbrec/ui/settings/CreateContactSheetPaneFactory.java index 28a58faf..2d95b4a9 100644 --- a/client/src/main/java/ctbrec/ui/settings/CreateContactSheetPaneFactory.java +++ b/client/src/main/java/ctbrec/ui/settings/CreateContactSheetPaneFactory.java @@ -43,7 +43,7 @@ public class CreateContactSheetPaneFactory extends AbstractPostProcessingPaneFac Setting.of("Rows", rows), Setting.of("File Name", filename), Setting.of("Background", createColorPicker(background.get())), - Setting.of("Timestamp", burnTimestamp, "Burn in a timestamp on each thumb"), + Setting.of("Timestamp (experimental)", burnTimestamp, "Burn in a timestamp on each thumb. Can be very slow on some systems."), backgroundSetting ) );