From 6c614b2f171172dec899ec77020b6fcfc4008490 Mon Sep 17 00:00:00 2001 From: EpicKnarvik97 Date: Fri, 15 Mar 2024 01:20:26 +0100 Subject: [PATCH] Adds a video downscaler --- .../net/knarcraft/ffmpegconverter/Main.java | 27 +++++++- .../converter/AbstractConverter.java | 14 +++- .../converter/DownScaleConverter.java | 69 +++++++++++++++++++ .../ffmpegconverter/utility/FileUtil.java | 38 +++++----- 4 files changed, 127 insertions(+), 21 deletions(-) create mode 100644 src/main/java/net/knarcraft/ffmpegconverter/converter/DownScaleConverter.java diff --git a/src/main/java/net/knarcraft/ffmpegconverter/Main.java b/src/main/java/net/knarcraft/ffmpegconverter/Main.java index 6561d8b..bc22a4e 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/Main.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/Main.java @@ -3,6 +3,7 @@ package net.knarcraft.ffmpegconverter; import net.knarcraft.ffmpegconverter.converter.AnimeConverter; import net.knarcraft.ffmpegconverter.converter.AudioConverter; import net.knarcraft.ffmpegconverter.converter.Converter; +import net.knarcraft.ffmpegconverter.converter.DownScaleConverter; import net.knarcraft.ffmpegconverter.converter.MKVToMP4Transcoder; import net.knarcraft.ffmpegconverter.converter.MkvH264Converter; import net.knarcraft.ffmpegconverter.converter.MkvH265ReducedConverter; @@ -65,7 +66,7 @@ class Main { private static Converter loadConverter() throws IOException { int choice = getChoice("Which converter do you want do use?\n1. Anime to web mp4\n2. Audio converter\n" + "3. Video converter\n4. Web video converter\n5. MKV to h264 converter\n6. MKV to h265 reduced " + - "converter\n7. MKV to MP4 transcoder", 1, 7); + "converter\n7. MKV to MP4 transcoder\n8. DownScaleConverter", 1, 8); switch (choice) { case 1: @@ -82,6 +83,8 @@ class Main { return new MkvH265ReducedConverter(FFPROBE_PATH, FFMPEG_PATH); case 7: return generateMKVToMP4Transcoder(); + case 8: + return generateDownScaleConverter(); } return null; } @@ -117,6 +120,28 @@ class Main { } } + /** + * Initializes and returns the downscale converter + * + * @return

The initialized downscale converter

+ * @throws IOException

If unable to print to output

+ */ + private static Converter generateDownScaleConverter() throws IOException { + OutputUtil.println("(New width e.x. 1920) (New height e.x. 1080)\nYour input: "); + List input = readInput(3); + int newWidth; + int newHeight; + + try { + newWidth = Integer.parseInt(input.get(0)); + newHeight = Integer.parseInt(input.get(1)); + return new DownScaleConverter(FFPROBE_PATH, FFMPEG_PATH, newWidth, newHeight); + } catch (NumberFormatException exception) { + OutputUtil.println("Width or height is not a number"); + return null; + } + } + /** * Initializes and returns the MKV to MP4 transcoder * diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java index c34d50c..d95c4dd 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java @@ -30,7 +30,7 @@ public abstract class AbstractConverter implements Converter { /** * Initializes variables used by the abstract converter */ - AbstractConverter(String newExtension) { + AbstractConverter(@Nullable String newExtension) { this.newExtension = newExtension; OutputUtil.setDebug(this.debug); try { @@ -128,11 +128,19 @@ public abstract class AbstractConverter implements Converter { throw new IllegalArgumentException("The file has no valid streams. Please make sure the file exists and" + " is not corrupt."); } - String newPath = FileUtil.getNonCollidingPath(folder, file, newExtension); + String outExtension = newExtension != null ? newExtension : FileUtil.getExtension(file.getName()); + String newPath = FileUtil.getNonCollidingPath(folder, file, outExtension); OutputUtil.println(); OutputUtil.println("Preparing to start process..."); OutputUtil.println("Converting " + file); - ProcessBuilder processBuilder = new ProcessBuilder(generateConversionCommand(ffmpegPath, file, streams, newPath)); + + String[] command = generateConversionCommand(ffmpegPath, file, streams, newPath); + // If no commands were given, no conversion is necessary + if (command.length == 0) { + return; + } + + ProcessBuilder processBuilder = new ProcessBuilder(command); FFMpegHelper.runProcess(processBuilder, folder, "\n", true); } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/DownScaleConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/DownScaleConverter.java new file mode 100644 index 0000000..cea1490 --- /dev/null +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/DownScaleConverter.java @@ -0,0 +1,69 @@ +package net.knarcraft.ffmpegconverter.converter; + +import net.knarcraft.ffmpegconverter.streams.StreamObject; +import net.knarcraft.ffmpegconverter.streams.VideoStream; +import net.knarcraft.ffmpegconverter.utility.FFMpegHelper; + +import java.io.File; +import java.util.List; + +/** + * A converter for converting video files + */ +public class DownScaleConverter extends AbstractConverter { + + private final int newWidth; + private final int newHeight; + + /** + * Instantiates a new video converter + * + * @param ffprobePath

Path/command to ffprobe.

+ * @param ffmpegPath

Path/command to ffmpeg.

+ * @param newWidth

The new width of the video

+ * @param newHeight

The new height of the video

+ */ + public DownScaleConverter(String ffprobePath, String ffmpegPath, int newWidth, int newHeight) { + super(null); + this.ffprobePath = ffprobePath; + this.ffmpegPath = ffmpegPath; + this.newHeight = newHeight; + this.newWidth = newWidth; + } + + @Override + public String[] generateConversionCommand(String executable, File file, List streams, String outFile) { + VideoStream videoStream = getNthVideoStream(streams, 0); + if (videoStream == null || (videoStream.getWidth() <= newWidth && videoStream.getHeight() <= newHeight)) { + return new String[0]; + } + + List command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, file.getName()); + if (this.debug) { + FFMpegHelper.addDebugArguments(command, 50, 120); + } + + //Add all streams without re-encoding + command.add("-map"); + command.add("0"); + command.add("-c:a"); + command.add("copy"); + command.add("-c:s"); + command.add("copy"); + command.add("-vf"); + command.add("scale=" + newWidth + ":" + newHeight); + command.add("-crf"); + command.add("20"); + command.add("-preset"); + command.add("slow"); + + command.add(outFile); + return command.toArray(new String[0]); + } + + @Override + public String[] getValidFormats() { + return videoFormats; + } + +} \ No newline at end of file diff --git a/src/main/java/net/knarcraft/ffmpegconverter/utility/FileUtil.java b/src/main/java/net/knarcraft/ffmpegconverter/utility/FileUtil.java index 99ce46a..f8d1c13 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/utility/FileUtil.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/utility/FileUtil.java @@ -25,17 +25,7 @@ public final class FileUtil { */ public static String getNonCollidingPath(File folder, File file, String outExtension) { return FileUtil.getNonCollidingFilename(folder.getAbsolutePath() + File.separator + - FileUtil.stripExtension(file) + "." + outExtension, outExtension); - } - - /** - * Removes the extension from a file name - * - * @param file

A filename.

- * @return

A filename without its extension.

- */ - static String stripExtension(String file) { - return file.substring(0, file.lastIndexOf('.')); + FileUtil.stripExtension(file.getName()) + "." + outExtension, outExtension); } /** @@ -117,7 +107,7 @@ public final class FileUtil { */ private static String getNonCollidingFilename(String targetPath, String extension) { File newFile = new File(targetPath); - String fileName = stripExtension(targetPath); + String fileName = stripExtension(targetPath).replaceAll("\\([0-9]+\\)$", ""); int i = 1; while (newFile.exists()) { newFile = new File(fileName + "(" + i++ + ")" + "." + extension); @@ -126,13 +116,27 @@ public final class FileUtil { } /** - * Gets filename without extension from File object + * Gets the extension of the given filename * - * @param file

A file object.

- * @return

A filename.

+ * @param file

The filename to check

+ * @return

The file's extension

*/ - private static String stripExtension(File file) { - return file.getName().substring(0, file.getName().lastIndexOf('.')); + public static String getExtension(String file) { + if (file.contains(".")) { + return file.substring(file.lastIndexOf('.') + 1); + } else { + return ""; + } + } + + /** + * Removes the extension from a file name + * + * @param file

A filename.

+ * @return

A filename without its extension.

+ */ + public static String stripExtension(String file) { + return file.substring(0, file.lastIndexOf('.')); } }