From e1256f61c7a2b0c86da7ac88a0651f3b1dfdea72 Mon Sep 17 00:00:00 2001 From: EpicKnarvik97 Date: Tue, 12 May 2020 00:19:01 +0200 Subject: [PATCH] Simplifies and improves code. Removes some duplication --- .../net/knarcraft/ffmpegconverter/Main.java | 79 ++++---- .../converter/AbstractConverter.java | 162 +++------------- .../converter/AnimeConverter.java | 4 +- .../converter/AudioConverter.java | 2 +- .../converter/VideoConverter.java | 4 +- .../streams/AbstractStream.java | 7 + .../ffmpegconverter/streams/AudioStream.java | 10 - .../ffmpegconverter/streams/StreamObject.java | 7 + .../streams/SubtitleStream.java | 10 - .../ffmpegconverter/utility/FFMpegHelper.java | 173 +++++++++++++++--- 10 files changed, 237 insertions(+), 221 deletions(-) diff --git a/src/main/java/net/knarcraft/ffmpegconverter/Main.java b/src/main/java/net/knarcraft/ffmpegconverter/Main.java index b9fb6fe..cfd1e47 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/Main.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/Main.java @@ -16,7 +16,7 @@ import java.util.Scanner; import static net.knarcraft.ffmpegconverter.utility.Parser.tokenize; /** - * Converts a files or files in a folder to a web playable mp4. + * The main class for starting the software */ class Main { private static final String FFPROBE_PATH = "ffprobe"; //Can be just ffprobe if it's in the path @@ -25,27 +25,7 @@ class Main { private static AbstractConverter converter = null; public static void main(String[] args) throws IOException { - //System.out.println(tokenizer("AnimeConverter -audiolang jap,eng -sublang eng,nor,* \"C:\\Users\\Kristian\\Downloads\\Anime\\[Kametsu] ERASED (BD 1080p Hi10 FLAC)\"")); - //parser(tokenizer("AnimeConverter \"C:\\Users\\Kristian\\Downloads\\Anime\\[Kametsu] ERASED (BD 1080p Hi10 FLAC)\"")); - //System.exit(1); - - int choice = getChoice("Which converter do you want do use?\n1. Anime to web mp4\n2. Audio converter\n" + - "3. VideoStream converter", 1, 3); - - OutputUtil.println("Input for this converter:"); - switch (choice) { - case 1: - animeConverter(); - break; - case 2: - converter = new AudioConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("")); - break; - case 3: - converter = new VideoConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("")); - break; - default: - System.exit(1); - } + loadConverter(); int recursionSteps = 1; @@ -64,8 +44,46 @@ class Main { } } - if (folder.isDirectory()) { - File[] files = FileUtil.listFilesRecursive(folder, converter.getValidFormats(), recursionSteps); + convertAllFiles(folder, recursionSteps); + + OutputUtil.close(); + } + + /** + * Asks the user which converter they want, and assigns a converter instance to the converter variable + * + * @throws IOException

If there's a problem getting user input.

+ */ + private static void loadConverter() throws IOException { + int choice = getChoice("Which converter do you want do use?\n1. Anime to web mp4\n2. Audio converter\n" + + "3. VideoStream converter", 1, 3); + + OutputUtil.println("Input for this converter:"); + switch (choice) { + case 1: + animeConverter(); + break; + case 2: + converter = new AudioConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("")); + break; + case 3: + converter = new VideoConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("")); + break; + default: + System.exit(1); + } + } + + /** + * Converts the file(s) as specified + * + * @param fileOrFolder

A file or a folder.

+ * @param recursionSteps

The depth to recurse if a folder is given.

+ * @throws IOException

If conversion or writing fails.

+ */ + private static void convertAllFiles(File fileOrFolder, int recursionSteps) throws IOException { + if (fileOrFolder.isDirectory()) { + File[] files = FileUtil.listFilesRecursive(fileOrFolder, converter.getValidFormats(), recursionSteps); if (files != null && files.length > 0) { for (File file : files) { converter.convert(file); @@ -73,12 +91,11 @@ class Main { } else { OutputUtil.println("No valid files found in folder."); } - } else if (folder.exists()) { - converter.convert(folder); + } else if (fileOrFolder.exists()) { + converter.convert(fileOrFolder); } else { - OutputUtil.println("Path " + folder.getAbsolutePath() + " does not point to any file or folder."); + OutputUtil.println("Path " + fileOrFolder.getAbsolutePath() + " does not point to any file or folder."); } - OutputUtil.close(); } /** @@ -152,8 +169,8 @@ class Main { */ private static int getChoice(String prompt, int min, int max) throws IOException { OutputUtil.println(prompt); - int choice = 0; - while (choice < min || choice > max) { + int choice = Integer.MIN_VALUE; + do { OutputUtil.println("Your input: "); try { choice = Integer.parseInt(READER.next()); @@ -162,7 +179,7 @@ class Main { } finally { READER.nextLine(); } - } + } while (choice < min || choice > max); return choice; } } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java index 30fe0be..9d8a5a0 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java @@ -11,7 +11,6 @@ import net.knarcraft.ffmpegconverter.utility.OutputUtil; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; /** @@ -49,12 +48,10 @@ public abstract class AbstractConverter implements Converter { */ @SuppressWarnings("unchecked") static List filterStreamsByType(List streams, Class clazz) { - Iterator i = streams.iterator(); List newStreams = new ArrayList<>(); - while (i.hasNext()) { - StreamObject next = i.next(); - if (next.getClass() == clazz) { - newStreams.add((G) next); + for (StreamObject stream : streams) { + if (stream.getClass() == clazz) { + newStreams.add((G) stream); } } return newStreams; @@ -68,17 +65,7 @@ public abstract class AbstractConverter implements Converter { * @return

A list containing just audio tracks of chosen languages, sorted in order of languages.

*/ static List filterAudioStreams(List audioStreams, String[] audioLanguages) { - List filtered = new ArrayList<>(); - for (String language : audioLanguages) { - for (AudioStream stream : audioStreams) { - if ((stream.getLanguage() != null && stream.getLanguage().equals(language)) || language.equals("*")) { - filtered.add(stream); - } - } - //Tries to reduce execution time from n^2 - audioStreams.removeAll(filtered); - } - return filtered; + return sortStreamsByLanguage(audioStreams, audioLanguages); } /** @@ -91,35 +78,32 @@ public abstract class AbstractConverter implements Converter { */ static List filterSubtitleStreams(List subtitleStreams, String[] subtitleLanguages, boolean preventSignsAndSongs) { - List filtered = new ArrayList<>(); - //Go through languages. Select all subtitles of the language - for (String language : subtitleLanguages) { - for (SubtitleStream stream : subtitleStreams) { - String streamLanguage = stream.getLanguage(); - if (((streamLanguage != null && streamLanguage.equals(language)) || language.equals("*")) && - (!preventSignsAndSongs || stream.getIsFullSubtitle())) { - filtered.add(stream); - } - } - //Tries to reduce execution time from n^2 - subtitleStreams.removeAll(filtered); - } - return filtered; + List sorted = sortStreamsByLanguage(subtitleStreams, subtitleLanguages); + sorted.removeIf((stream) -> preventSignsAndSongs && !stream.getIsFullSubtitle()); + return sorted; } /** - * Escapes special characters which can cause trouble for ffmpeg + * Sorts subtitle streams according to chosen languages and removes non matching languages * - * @param fileName

The filename to escape.

- * @return

A filename with known special characters escaped.

+ * @param streams

A list of streams to sort.

+ * @param languages

The languages chosen by the user.

+ * @param

The type of streams to sort.

+ * @return

A sorted version of the list.

*/ - private static String escapeSpecialCharactersInFileName(String fileName) { - return fileName.replaceAll("\\\\", "\\\\\\\\\\\\\\\\") - .replaceAll("'", "'\\\\\\\\\\\\\''") - .replaceAll("%", "\\\\\\\\\\\\%") - .replaceAll(":", "\\\\\\\\\\\\:") - .replace("]", "\\]") - .replace("[", "\\["); + private static List sortStreamsByLanguage(List streams, String[] languages) { + List sorted = new ArrayList<>(); + for (String language : languages) { + for (G stream : streams) { + String streamLanguage = stream.getLanguage(); + if (language.equals("*") || (streamLanguage == null && language.equals("0")) || + (streamLanguage != null && streamLanguage.equals(language))) { + sorted.add(stream); + } + } + streams.removeAll(sorted); + } + return sorted; } /** @@ -129,100 +113,6 @@ public abstract class AbstractConverter implements Converter { */ public abstract String[] getValidFormats(); - /** - * Adds audio to a command - * - * @param command

The command to add audio to.

- * @param audioStream

The audio stream to be added.

- * @param toStereo

Whether to convert the audio stream to stereo.

- */ - void addAudioStreams(List command, AudioStream audioStream, boolean toStereo) { - if (audioStream != null) { - command.add("-map"); - command.add("0:" + audioStream.getAbsoluteIndex()); - if (toStereo && audioStream.getChannels() > 2) { - command.add("-af"); - command.add("pan=stereo|FL=FC+0.30*FL+0.30*BL|FR=FC+0.30*FR+0.30*BR"); - //command.add("pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL|FR < 1.0*FR + 0.707*FC + 0.707*BR"); - } - } - } - - /** - * Adds subtitles and video mapping to a command - * - * @param command

The list containing the rest of the command.

- * @param subtitleStream

The subtitle stream to be used.

- * @param videoStream

The video stream to be used.

- * @param file

The file to convert.

- */ - void addSubtitlesAndVideo(List command, SubtitleStream subtitleStream, VideoStream videoStream, File file) { - //No appropriate subtitle was found. Just add the video stream. - if (subtitleStream == null) { - command.add("-map"); - command.add(String.format("0:%d", videoStream.getAbsoluteIndex())); - return; - } - - if (!subtitleStream.getIsImageSubtitle()) { - addSubtitle(command, subtitleStream, videoStream); - } else if (file.getName().equals(subtitleStream.getFile())) { - addInternalImageSubtitle(command, subtitleStream, videoStream); - } else { - addExternalImageSubtitle(command, subtitleStream, videoStream); - } - } - - /** - * Adds subtitle commands to a command list - * - * @param command

The list containing the FFmpeg commands.

- * @param subtitleStream

The subtitle stream to add.

- * @param videoStream

The video stream to burn the subtitle into.

- */ - private void addSubtitle(List command, SubtitleStream subtitleStream, VideoStream videoStream) { - command.add("-map"); - command.add(String.format("0:%d", videoStream.getAbsoluteIndex())); - command.add("-vf"); - String safeFileName = escapeSpecialCharactersInFileName(subtitleStream.getFile()); - String subtitleCommand = String.format("subtitles='%s':si=%d", safeFileName, - subtitleStream.getRelativeIndex()); - command.add(subtitleCommand); - } - - /** - * Adds image subtitle commands to a command list - * - * @param command

The list containing the FFmpeg commands.

- * @param subtitleStream

The subtitle stream to add.

- * @param videoStream

The video stream to burn the subtitle into.

- */ - private void addInternalImageSubtitle(List command, SubtitleStream subtitleStream, VideoStream videoStream) { - command.add("-filter_complex"); - String filter = String.format("[0:v:%d][0:%d]overlay", videoStream.getAbsoluteIndex(), - subtitleStream.getAbsoluteIndex()); - command.add(filter); - } - - /** - * Adds external image subtitle commands to a command list - * - * @param command

The list containing the FFmpeg commands.

- * @param externalImageSubtitle

The external image subtitle stream to add.

- * @param videoStream

The video stream to burn the subtitle into.

- */ - private void addExternalImageSubtitle(List command, SubtitleStream externalImageSubtitle, - VideoStream videoStream) { - command.add("-i"); - command.add(externalImageSubtitle.getFile()); - command.add("-filter_complex"); - command.add(String.format("[1:s]scale=width=%d:height=%d,crop=w=%d:h=%d:x=0:y=out_h[sub];[%d:v]" + - "[sub]overlay", videoStream.getWidth(), videoStream.getHeight(), videoStream.getWidth(), - videoStream.getHeight(), videoStream.getAbsoluteIndex())); - command.add("-profile:v"); - command.add("baseline"); - } - /** * Reads streams from a file, and converts it to an mp4 * @@ -241,7 +131,7 @@ public abstract class AbstractConverter implements Converter { OutputUtil.println("Preparing to start process..."); OutputUtil.println("Converting " + file); ProcessBuilder processBuilder = new ProcessBuilder(builderCommand(ffmpegPath, file, streams, newPath)); - FFMpegHelper.convertProcess(processBuilder, folder); + FFMpegHelper.runProcess(processBuilder, folder, "\n", true); } /** diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java index f12f83f..68657cc 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java @@ -66,8 +66,8 @@ public class AnimeConverter extends AbstractConverter { VideoStream videoStream = getFirstVideoStream(streams); //Add streams to output file - addAudioStreams(command, audioStream, toStereo); - addSubtitlesAndVideo(command, subtitleStream, videoStream, file); + FFMpegHelper.addAudioStreams(command, audioStream, toStereo); + FFMpegHelper.addSubtitlesAndVideo(command, subtitleStream, videoStream, file); command.add(outFile); return command.toArray(new String[0]); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java index 04968a4..2d1fc08 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java @@ -35,7 +35,7 @@ public class AudioConverter extends AbstractConverter { //Gets the first audio stream from the file and adds it to the output file AudioStream audioStream = getFirstAudioSteam(streams); - addAudioStreams(command, audioStream, false); + FFMpegHelper.addAudioStreams(command, audioStream, false); return command.toArray(new String[0]); } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java index 18dc20f..61109dd 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java @@ -41,9 +41,9 @@ public class VideoConverter extends AbstractConverter { AudioStream audioStream = getFirstAudioSteam(streams); //Add streams to output - addSubtitlesAndVideo(command, subtitleStream, videoStream, file); + FFMpegHelper.addSubtitlesAndVideo(command, subtitleStream, videoStream, file); if (audioStream != null) { - addAudioStreams(command, audioStream, true); + FFMpegHelper.addAudioStreams(command, audioStream, true); } command.add(outFile); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/streams/AbstractStream.java b/src/main/java/net/knarcraft/ffmpegconverter/streams/AbstractStream.java index c1a0337..6543382 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/streams/AbstractStream.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/streams/AbstractStream.java @@ -7,6 +7,7 @@ public abstract class AbstractStream implements StreamObject { int absoluteIndex; int relativeIndex; String codecName; + String language; @Override public String getCodecName() { @@ -22,4 +23,10 @@ public abstract class AbstractStream implements StreamObject { public int getRelativeIndex() { return this.relativeIndex; } + + @Override + public String getLanguage() { + return this.language; + } + } \ No newline at end of file diff --git a/src/main/java/net/knarcraft/ffmpegconverter/streams/AudioStream.java b/src/main/java/net/knarcraft/ffmpegconverter/streams/AudioStream.java index 00235c7..0f89425 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/streams/AudioStream.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/streams/AudioStream.java @@ -4,7 +4,6 @@ package net.knarcraft.ffmpegconverter.streams; * This class represents an ffmpeg audio stream */ public class AudioStream extends AbstractStream implements StreamObject { - private final String language; private final int channels; private final String title; @@ -27,15 +26,6 @@ public class AudioStream extends AbstractStream implements StreamObject { this.channels = channels; } - /** - * Gets the language of the audio stream - * - * @return

The language of the audio stream.

- */ - public String getLanguage() { - return this.language; - } - /** * Gets the number of channels for the audio stream * diff --git a/src/main/java/net/knarcraft/ffmpegconverter/streams/StreamObject.java b/src/main/java/net/knarcraft/ffmpegconverter/streams/StreamObject.java index 069d4cc..f2430bd 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/streams/StreamObject.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/streams/StreamObject.java @@ -23,4 +23,11 @@ public interface StreamObject { */ int getRelativeIndex(); + /** + * Gets the language of the audio stream + * + * @return

The language of the audio stream.

+ */ + String getLanguage(); + } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/streams/SubtitleStream.java b/src/main/java/net/knarcraft/ffmpegconverter/streams/SubtitleStream.java index 9e5ce6d..fd8431a 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/streams/SubtitleStream.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/streams/SubtitleStream.java @@ -4,7 +4,6 @@ package net.knarcraft.ffmpegconverter.streams; * An object representation of a subtitle stream in a media file */ public class SubtitleStream extends AbstractStream implements StreamObject { - final private String language; final private String title; final private String file; final private boolean isFullSubtitle; @@ -32,15 +31,6 @@ public class SubtitleStream extends AbstractStream implements StreamObject { this.file = file; } - /** - * Gets the language of the subtitle stream - * - * @return

The language of the subtitle stream.

- */ - public String getLanguage() { - return this.language; - } - /** * Gets the title of the subtitle stream * diff --git a/src/main/java/net/knarcraft/ffmpegconverter/utility/FFMpegHelper.java b/src/main/java/net/knarcraft/ffmpegconverter/utility/FFMpegHelper.java index 872ec4d..e6c3eaf 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/utility/FFMpegHelper.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/utility/FFMpegHelper.java @@ -88,24 +88,149 @@ public final class FFMpegHelper { /** * Starts and prints output of a process * - * @param process

The process to run.

- * @param folder

The folder the process should run in.

+ * @param processBuilder

The process to run.

+ * @param folder

The folder the process should run in.

+ * @param spacer

The character(s) to use between each new line read.

+ * @param write

Whether to write the output directly instead of storing it.

* @throws IOException

If the process can't be readProcess.

*/ - public static void convertProcess(ProcessBuilder process, File folder) throws IOException { + public static String runProcess(ProcessBuilder processBuilder, File folder, String spacer, boolean write) + throws IOException { + //Give the user information about what's about to happen OutputUtil.print("Command to be run: "); - OutputUtil.println(process.command().toString()); - process.directory(folder); - process.redirectErrorStream(true); - Process processConvert = process.start(); - BufferedReader readerConvert = new BufferedReader(new InputStreamReader(processConvert.getInputStream())); - while (processConvert.isAlive()) { - String read = readProcess(readerConvert, "\n"); + OutputUtil.println(processBuilder.command().toString()); + + //Set directory and error stream + processBuilder.directory(folder); + processBuilder.redirectErrorStream(true); + + Process process = processBuilder.start(); + BufferedReader processReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + StringBuilder output = new StringBuilder(); + while (process.isAlive()) { + String read = readProcess(processReader, spacer); if (!read.equals("")) { - OutputUtil.println(read); + if (write) { + OutputUtil.print(read); + } else { + output.append(read); + } } } OutputUtil.println("Process finished."); + return output.toString(); + } + + /** + * Adds audio to a command + * + * @param command

The command to add audio to.

+ * @param audioStream

The audio stream to be added.

+ * @param toStereo

Whether to convert the audio stream to stereo.

+ */ + public static void addAudioStreams(List command, AudioStream audioStream, boolean toStereo) { + if (audioStream != null) { + command.add("-map"); + command.add("0:" + audioStream.getAbsoluteIndex()); + if (toStereo && audioStream.getChannels() > 2) { + command.add("-af"); + command.add("pan=stereo|FL=FC+0.30*FL+0.30*BL|FR=FC+0.30*FR+0.30*BR"); + //command.add("pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL|FR < 1.0*FR + 0.707*FC + 0.707*BR"); + } + } + } + + /** + * Adds subtitles and video mapping to a command + * + * @param command

The list containing the rest of the command.

+ * @param subtitleStream

The subtitle stream to be used.

+ * @param videoStream

The video stream to be used.

+ * @param file

The file to convert.

+ */ + public static void addSubtitlesAndVideo(List command, SubtitleStream subtitleStream, + VideoStream videoStream, File file) { + //No appropriate subtitle was found. Just add the video stream. + if (subtitleStream == null) { + command.add("-map"); + command.add(String.format("0:%d", videoStream.getAbsoluteIndex())); + return; + } + + //Add the correct command arguments depending on the subtitle type + if (!subtitleStream.getIsImageSubtitle()) { + addSubtitle(command, subtitleStream, videoStream); + } else if (file.getName().equals(subtitleStream.getFile())) { + addInternalImageSubtitle(command, subtitleStream, videoStream); + } else { + addExternalImageSubtitle(command, subtitleStream, videoStream); + } + } + + /** + * Adds subtitle commands to a command list + * + * @param command

The list containing the FFmpeg commands.

+ * @param subtitleStream

The subtitle stream to add.

+ * @param videoStream

The video stream to burn the subtitle into.

+ */ + private static void addSubtitle(List command, SubtitleStream subtitleStream, VideoStream videoStream) { + command.add("-map"); + command.add(String.format("0:%d", videoStream.getAbsoluteIndex())); + command.add("-vf"); + String safeFileName = escapeSpecialCharactersInFileName(subtitleStream.getFile()); + String subtitleCommand = String.format("subtitles='%s':si=%d", safeFileName, + subtitleStream.getRelativeIndex()); + command.add(subtitleCommand); + } + + /** + * Escapes special characters which can cause trouble for ffmpeg + * + * @param fileName

The filename to escape.

+ * @return

A filename with known special characters escaped.

+ */ + private static String escapeSpecialCharactersInFileName(String fileName) { + return fileName.replaceAll("\\\\", "\\\\\\\\\\\\\\\\") + .replaceAll("'", "'\\\\\\\\\\\\\''") + .replaceAll("%", "\\\\\\\\\\\\%") + .replaceAll(":", "\\\\\\\\\\\\:") + .replace("]", "\\]") + .replace("[", "\\["); + } + + /** + * Adds image subtitle commands to a command list + * + * @param command

The list containing the FFmpeg commands.

+ * @param subtitleStream

The subtitle stream to add.

+ * @param videoStream

The video stream to burn the subtitle into.

+ */ + private static void addInternalImageSubtitle(List command, SubtitleStream subtitleStream, + VideoStream videoStream) { + command.add("-filter_complex"); + String filter = String.format("[0:v:%d][0:%d]overlay", videoStream.getAbsoluteIndex(), + subtitleStream.getAbsoluteIndex()); + command.add(filter); + } + + /** + * Adds external image subtitle commands to a command list + * + * @param command

The list containing the FFmpeg commands.

+ * @param externalImageSubtitle

The external image subtitle stream to add.

+ * @param videoStream

The video stream to burn the subtitle into.

+ */ + private static void addExternalImageSubtitle(List command, SubtitleStream externalImageSubtitle, + VideoStream videoStream) { + command.add("-i"); + command.add(externalImageSubtitle.getFile()); + command.add("-filter_complex"); + command.add(String.format("[1:s]scale=width=%d:height=%d,crop=w=%d:h=%d:x=0:y=out_h[sub];[%d:v]" + + "[sub]overlay", videoStream.getWidth(), videoStream.getHeight(), videoStream.getWidth(), + videoStream.getHeight(), videoStream.getAbsoluteIndex())); + command.add("-profile:v"); + command.add("baseline"); } /** @@ -117,7 +242,7 @@ public final class FFMpegHelper { * @throws IOException

If something goes wrong while probing.

*/ private static String[] probeForStreams(String ffprobePath, File file) throws IOException { - ProcessBuilder builderProbe = new ProcessBuilder( + ProcessBuilder processBuilder = new ProcessBuilder( ffprobePath, "-v", "error", @@ -125,21 +250,8 @@ public final class FFMpegHelper { "stream_tags=language,title:stream=index,codec_name,codec_type,channels,codec_type,width,height", file.toString() ); - OutputUtil.println(); - OutputUtil.print("Probe command: "); - OutputUtil.println(builderProbe.command().toString()); - builderProbe.redirectErrorStream(true); - Process processProbe = builderProbe.start(); - BufferedReader readerProbe = new BufferedReader(new InputStreamReader(processProbe.getInputStream())); - StringBuilder output = new StringBuilder(); - while (processProbe.isAlive()) { - String read = readProcess(readerProbe, PROBE_SPLIT_CHARACTER); - if (!read.equals("")) { - OutputUtil.printDebug(read); - output.append(read); - } - } - return StringUtil.stringBetween(output.toString(), "[STREAM]", "[/STREAM]"); + String result = runProcess(processBuilder, file.getParentFile(), PROBE_SPLIT_CHARACTER, false); + return StringUtil.stringBetween(result, "[STREAM]", "[/STREAM]"); } /** @@ -180,19 +292,22 @@ public final class FFMpegHelper { private static List getExternalSubtitles(String ffprobePath, File directory, String convertingFile) throws IOException { List parsedStreams = new ArrayList<>(); + //Find all files in the same directory with external subtitle formats String[] subtitleFormats = FileUtil.readFileLines("subtitle_formats.txt"); File[] subtitleFiles = FileUtil.listFilesRecursive(directory, subtitleFormats, 1); + //Return early if no files were found if (subtitleFiles == null) { return parsedStreams; } String fileTitle = FileUtil.stripExtension(convertingFile); List subtitleFilesList = new ArrayList<>(Arrays.asList(subtitleFiles)); - //Finds the files which are subtitles belonging to the file + //Finds the files which are subtitles probably belonging to the file subtitleFilesList = ListUtil.getMatching(subtitleFilesList, (subtitleFile) -> subtitleFile.getName().contains(fileTitle)); for (File subtitleFile : subtitleFilesList) { + //Probe the files and add them to the result list String[] streams = probeForStreams(ffprobePath, subtitleFile); for (String stream : streams) { String[] streamParts = stream.split(PROBE_SPLIT_CHARACTER); @@ -291,7 +406,7 @@ public final class FFMpegHelper { String language = null; String title = ""; for (String streamPart : streamParts) { - if (streamPart.contains("codec_name=")) { + if (streamPart.startsWith("codec_name=")) { codecName = streamPart.replace("codec_name=", ""); } else if (streamPart.startsWith("index=")) { absoluteIndex = Integer.parseInt(streamPart.replace("index=", ""));