diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java index b800bf5..388550d 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java @@ -7,7 +7,6 @@ import net.knarcraft.ffmpegconverter.streams.VideoStream; import java.io.File; import java.io.IOException; -import java.util.Arrays; import java.util.List; public class AnimeConverter extends Converter { @@ -15,17 +14,18 @@ public class AnimeConverter extends Converter { private String[] subtitleLang; private boolean toStereo; private boolean preventSignsAndSongs; - private boolean debug = false; /** - * @param ffprobePath Path/command to ffprobe - * @param ffmpegPath Path/command to ffmpeg - * @param audioLang List of wanted audio languages in descending order - * @param subtitleLang List of wanted subtitle languages in descending order - * @param toStereo Convert video with several audio channels to stereo - * @param preventSignsAndSongs Prevent subtitles only converting signs and songs (not speech) + * Instantiates a new anime converter + * @param ffprobePath

Path/command to ffprobe.

+ * @param ffmpegPath

Path/command to ffmpeg.

+ * @param audioLang

List of wanted audio languages in descending order.

+ * @param subtitleLang

List of wanted subtitle languages in descending order.

+ * @param toStereo

Convert video with several audio channels to stereo.

+ * @param preventSignsAndSongs

Prevent subtitles only converting signs and songs (not speech).

*/ - public AnimeConverter(String ffprobePath, String ffmpegPath, String[] audioLang, String[] subtitleLang, boolean toStereo, boolean preventSignsAndSongs) { + public AnimeConverter(String ffprobePath, String ffmpegPath, String[] audioLang, String[] subtitleLang, + boolean toStereo, boolean preventSignsAndSongs) { this.ffprobePath = ffprobePath; this.ffmpegPath = ffmpegPath; this.audioLang = audioLang; @@ -34,21 +34,26 @@ public class AnimeConverter extends Converter { this.preventSignsAndSongs = preventSignsAndSongs; } + /** + * Converts the given file + * @param file

The file to convert.

+ * @throws IOException

If the file cannot be converted.

+ */ public void convert(File file) throws IOException { processFile(file.getParentFile(), file); } /** - * Reads streams from a file, and converts it to an mp4. - * - * @param folder The folder of the file to process - * @param file The file to process - * @throws IOException If the BufferedReader fails + * Reads streams from a file, and converts it to an mp4 + * @param folder

The folder of the file to process.

+ * @param file

The file to process.

+ * @throws IOException

If the BufferedReader fails.

*/ private void processFile(File folder, File file) throws IOException { List streams = probeFile(ffprobePath, file); if (streams.isEmpty()) { - throw new IllegalArgumentException("The file has no valid streams. Please make sure the file exists and is not corrupt."); + throw new IllegalArgumentException("The file has no valid streams. Please make sure the file exists and" + + " is not corrupt."); } String newPath = fileCollisionPrevention(folder.getAbsolutePath() + File.separator + stripExtension(file) + ".mp4", "mp4"); @@ -60,26 +65,23 @@ public class AnimeConverter extends Converter { /** * Generates a command for a ProcessBuilder. - * - * @param executable The executable file for ffmpeg - * @param fileName The input file - * @param streams A list of ffprobe streams - * @param outFile The output file - * @return A list of commands + * @param executable

The executable file for ffmpeg.

+ * @param fileName

The input file.

+ * @param streams

A list of ffprobe streams.

+ * @param outFile

The output file.

+ * @return

A list of commands

*/ private String[] builderCommand(String executable, String fileName, List streams, String outFile) { List command = ffmpegWebVideo(executable, fileName); - if (this.debug) { + if (this.DEBUG) { addDebug(command, 50, 120); } - List audioStreams = filterStreamsByType(streams, "audio"); + List audioStreams = filterAudioStreams(filterStreamsByType(streams, "audio"), audioLang); List videoStreams = filterStreamsByType(streams, "video"); - List subtitleStreams = filterStreamsByType(streams, "subtitle"); - - audioStreams = filterAudioStreams(audioStreams, audioLang); - subtitleStreams = filterSubtitleStreams(subtitleStreams, subtitleLang, preventSignsAndSongs); + List subtitleStreams = filterSubtitleStreams(filterStreamsByType(streams, + "subtitle"), subtitleLang, preventSignsAndSongs); VideoStream videoStream = null; AudioStream audioStream = null; @@ -87,6 +89,9 @@ public class AnimeConverter extends Converter { if (videoStreams.size() > 0) { videoStream = videoStreams.get(0); } + if (videoStream == null) { + throw new IllegalArgumentException("The file does not have any valid video streams."); + } if (audioStreams.size() > 0) { audioStream = audioStreams.get(0); } @@ -94,10 +99,19 @@ public class AnimeConverter extends Converter { subtitleStream = subtitleStreams.get(0); } - if (videoStream == null) { - throw new IllegalArgumentException("The file does not have any valid video streams."); - } + addAudioStreams(command, audioStream); + addSubtitles(command, subtitleStream, videoStream, fileName); + command.add(outFile); + return command.toArray(new String[0]); + } + + /** + * Adds audio to a command + * @param command

The command to add audio to.

+ * @param audioStream

The audio stream to be added.

+ */ + private void addAudioStreams(List command, AudioStream audioStream) { if (audioStream != null) { command.add("-map"); command.add("0:" + audioStream.getAbsoluteIndex()); @@ -106,7 +120,17 @@ public class AnimeConverter extends Converter { command.add("pan=stereo|FL=FC+0.30*FL+0.30*BL|FR=FC+0.30*FR+0.30*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 fileName

The name of the file which is converted.

+ */ + private void addSubtitles(List command, SubtitleStream subtitleStream, VideoStream videoStream, + String fileName) { if (subtitleStream != null && subtitleStream.getIsImageSubtitle()) { command.add("-filter_complex"); String filter = String.format("[0:v:%d][0:%d]overlay", videoStream.getAbsoluteIndex(), @@ -117,16 +141,13 @@ public class AnimeConverter extends Converter { command.add(String.format("0:%d", videoStream.getAbsoluteIndex())); command.add("-vf"); String safeFileName = escapeSpecialCharactersInFileName(fileName); - String subtitleCommand = String.format("subtitles='%s':si=%d", safeFileName, + String subtitleCommand = String.format("subtitles='%s':si=%d", safeFileName, subtitleStream.getRelativeIndex()); command.add(subtitleCommand); } else { command.add("-map"); command.add(String.format("0:%d", videoStream.getAbsoluteIndex())); } - - command.add(outFile); - return command.toArray(new String[0]); } @Override diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java index 204187b..a0c8224 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java @@ -10,6 +10,12 @@ import java.util.List; public class AudioConverter extends Converter { private String newExt; + /** + * Instantiates a new audio converter + * @param ffprobePath

Path/command to ffprobe.

+ * @param ffmpegPath

Path/command to ffmpeg.

+ * @param newExt

The extension of the new file.

+ */ public AudioConverter(String ffprobePath, String ffmpegPath, String newExt) { this.ffprobePath = ffprobePath; this.ffmpegPath = ffmpegPath; @@ -17,11 +23,11 @@ public class AudioConverter extends Converter { } /** - * Reads streams from a file, and converts it to an mp4. - * - * @param folder The folder of the file to process - * @param file The file to process - * @throws IOException If the BufferedReader fails + * Processes a file conversion + * @param folder

The work folder containing the file.

+ * @param file

The file to convert.

+ * @param newExt

The extension of the new file.

+ * @throws IOException

If the file cannot be converted.

*/ private void processFile(File folder, File file, String newExt) throws IOException { List streams = probeFile(ffprobePath, file); @@ -34,12 +40,11 @@ public class AudioConverter extends Converter { /** * Generates a command for a ProcessBuilder. - * - * @param executable The executable file for ffmpeg - * @param fileName The input file - * @param streams A list of ffprobe streams - * @param outFile The output file - * @return A list of commands + * @param executable

The executable file for ffmpeg.

+ * @param fileName

The input file.

+ * @param streams

A list of ffprobe streams.

+ * @param outFile

The output file.

+ * @return

A list of commands.

*/ private String[] builderCommand(String executable, String fileName, List streams, String outFile) { List command = generalFile(executable, fileName); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/Converter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/Converter.java index 455c126..caa2055 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/Converter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/Converter.java @@ -23,6 +23,7 @@ import java.util.function.Predicate; public abstract class Converter { String ffprobePath; String ffmpegPath; + boolean DEBUG = false; private static final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out)); @@ -41,10 +42,10 @@ public abstract class Converter { /** * Gets streams from a file - * @param ffprobePath The path/command to ffprobe - * @param file The file to probe - * @return A list of StreamObjects - * @throws IOException If the process can't be read + * @param ffprobePath

The path/command to ffprobe.

+ * @param file

The file to probe.

+ * @return

A list of StreamObjects.

+ * @throws IOException

If the process can't be read.

*/ static List probeFile(String ffprobePath, File file) throws IOException { ProcessBuilder builderProbe = new ProcessBuilder( @@ -71,20 +72,27 @@ public abstract class Converter { return parseStreams(stringBetween(output.toString(), "[STREAM]", "[/STREAM]")); } + /** + * Adds parentheses with an integer if the output file already exists + * @param targetPath

The path the file should ideally be saved at.

+ * @param extension

The extension of the target file.

+ * @return

A filename guaranteed not to collide with other files.

+ */ static String fileCollisionPrevention(String targetPath, String extension) { - File file = new File(targetPath); + File newFile = new File(targetPath); + String fileName = stripExtension(targetPath); int i = 1; - while (file.exists()) { - file = new File(stripExtension(file) + "(" + i + ")" + "." + extension); + while (newFile.exists()) { + newFile = new File(fileName + "(" + i++ + ")" + "." + extension); } - return file.toString(); + return newFile.toString(); } /** * Starts and prints output of a process - * @param process The process to run - * @param folder The folder the process should run in - * @throws IOException If the process can't be read + * @param process

The process to run.

+ * @param folder

The folder the process should run in.

+ * @throws IOException

If the process can't be read.

*/ static void convertProcess(ProcessBuilder process, File folder) throws IOException { print("Command to be run: "); @@ -99,15 +107,14 @@ public abstract class Converter { printl(read); } } - printl("FFMPEG is finished."); + printl("Process is finished."); } /** - * Reads from a process reader. - * - * @param reader The reader of a process - * @return The output from the read - * @throws IOException On reader failure + * Reads from a process reader + * @param reader

The reader of a process.

+ * @return

The output from the read.

+ * @throws IOException

On reader failure.

*/ private static String read(BufferedReader reader, String spacer) throws IOException { String line; @@ -119,7 +126,10 @@ public abstract class Converter { } /** - * @return A base list of ffmpeg commands for converting a video for web + * Creates a list containing all required arguments for converting a video to a web playable video + * @param executable

The executable to use (ffmpeg/ffprobe).

+ * @param fileName

The name of the file to execute on.

+ * @return

A base list of ffmpeg commands for converting a video for web

*/ static List ffmpegWebVideo(String executable, String fileName) { List command = generalFile(executable, fileName); @@ -135,7 +145,10 @@ public abstract class Converter { } /** - * @return A base list of ffmpeg commands for converting a file + * Creates a list containing command line arguments for a general file + * @param executable

The executable to use (ffmpeg/ffprobe).

+ * @param fileName

The name of the file to execute on.

+ * @return

A base list of ffmpeg commands for converting a file.

*/ static List generalFile(String executable, String fileName) { List command = new ArrayList<>(); @@ -148,9 +161,9 @@ public abstract class Converter { /** * Adds debugging parameters for only converting parts of a file - * @param command The list containing the command to run - * @param start The offset before converting - * @param length The offset for stopping the conversion + * @param command

The list containing the command to run.

+ * @param start

The offset before converting.

+ * @param length

The offset for stopping the conversion.

*/ static void addDebug(List command, int start, int length) { command.add("-ss"); @@ -160,10 +173,9 @@ public abstract class Converter { } /** - * Lists all indexes fulfilling a predicate. - * - * @param list A list of ffprobe indexes - * @return An integer list containing just the wanted indexes + * Lists all indexes fulfilling a predicate + * @param list

A list of ffprobe indexes.

+ * @return

An integer list containing just the wanted indexes

*/ private static List listIndexes(String[] list, Predicate p) { List indexes = new ArrayList<>(); @@ -177,9 +189,9 @@ public abstract class Converter { /** * Tests a predicate on a list - * @param list A list - * @param p A predicate - * @param Any type + * @param list

A list.

+ * @param p

A predicate

+ * @param

The type of the list and the predicate.

* @return True if the list have an element for which the predicate is true */ private static boolean testPredicate(T[] list, Predicate p) { @@ -192,12 +204,11 @@ public abstract class Converter { } /** - * Finds all substrings between two substrings in a string. - * - * @param string The string containing the substrings - * @param start The substring before the wanted substring - * @param end The substring after the wanted substring - * @return A list of all occurrences of the substring + * Finds all substrings between two substrings in a string + * @param string

The string containing the substrings.

+ * @param start

The substring before the wanted substring.

+ * @param end

The substring after the wanted substring.

+ * @return

A list of all occurrences of the substring.

*/ private static String[] stringBetween(String string, String start, String end) { int startPos = string.indexOf(start) + start.length(); @@ -211,12 +222,11 @@ public abstract class Converter { } /** - * Finds a substring between two substrings in a string. - * - * @param string The string containing the substrings - * @param start The substring before the wanted substring - * @param end The substring after the wanted substring - * @return The wanted substring. + * Finds a substring between two substrings in a string + * @param string

The string containing the substrings.

+ * @param start

The substring before the wanted substring.

+ * @param end

The substring after the wanted substring.

+ * @return

The wanted substring.

*/ private static String stringBetweenSingle(String string, String start, String end) { int startPos = string.indexOf(start) + start.length(); @@ -228,8 +238,8 @@ public abstract class Converter { /** * Gets filename without extension from File object - * @param file A file object - * @return A filename + * @param file

A file object.

+ * @return

A filename.

*/ static String stripExtension(File file) { return file.getName().substring(0, file.getName().lastIndexOf('.')); @@ -237,8 +247,8 @@ public abstract class Converter { /** * Removes the extension from a file name - * @param file A filename - * @return A filename without its extension + * @param file

A filename.

+ * @return

A filename without its extension.

*/ static String stripExtension(String file) { return file.substring(0, file.lastIndexOf('.')); @@ -246,28 +256,27 @@ public abstract class Converter { /** * Combines two arrays to one - * - * @param a The first array - * @param b The second array - * @param Any type - * @return A new array containing all elements from the two arrays + * @param listA

The first array.

+ * @param listB

The second array.

+ * @param

The type of the two lists.

+ * @return

A new array containing all elements from the two arrays.

*/ - public static T[] concatenate(T[] a, T[] b) { - int aLen = a.length; - int bLen = b.length; + public static T[] concatenate(T[] listA, T[] listB) { + int listALength = listA.length; + int listBLength = listB.length; @SuppressWarnings("unchecked") - T[] c = (T[]) Array.newInstance(a.getClass().getComponentType(), aLen + bLen); - System.arraycopy(a, 0, c, 0, aLen); - System.arraycopy(b, 0, c, aLen, bLen); - return c; + T[] resultList = (T[]) Array.newInstance(listA.getClass().getComponentType(), listALength + listBLength); + System.arraycopy(listA, 0, resultList, 0, listALength); + System.arraycopy(listB, 0, resultList, listALength, listBLength); + return resultList; } /** * Filters parsed streams into one of the stream types - * @param streams A list of stream objects - * @param codecType The codec type of the streams to select - * @param The correct object type for the streams with the selected codec type - * @return A potentially shorter list of streams + * @param streams

A list of stream objects.

+ * @param codecType

The codec type of the streams to select.

+ * @param

The correct object type for the streams with the selected codec type.

+ * @return

A potentially shorter list of streams.

*/ static List filterStreamsByType(List streams, String codecType) { Iterator i = streams.iterator(); @@ -283,9 +292,9 @@ public abstract class Converter { /** * Filters and sorts audio streams according to chosen languages - * @param audioStreams A list of audio streams - * @param audioLanguages A list of languages - * @return A list containing just audio tracks of chosen languages, sorted in order of languages + * @param audioStreams

A list of audio streams.

+ * @param audioLanguages

A list of languages.

+ * @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<>(); @@ -303,10 +312,10 @@ public abstract class Converter { /** * Filters and sorts subtitle streams according to chosen languages - * @param subtitleStreams A list of subtitle streams - * @param subtitleLanguages A list of languages - * @param preventSignsAndSongs Whether partial subtitles should be avoided - * @return A list containing just subtitles of chosen languages, sorted in order of languages + * @param subtitleStreams

A list of subtitle streams.

+ * @param subtitleLanguages

A list of languages.

+ * @param preventSignsAndSongs

Whether partial subtitles should be avoided.

+ * @return

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

*/ static List filterSubtitleStreams(List subtitleStreams, String[] subtitleLanguages, boolean preventSignsAndSongs) { @@ -328,8 +337,8 @@ public abstract class Converter { /** * Takes a list of all streams and parses each stream into one of three objects - * @param streams A list of all streams for the current file - * @return A list of StreamObjects + * @param streams

A list of all streams for the current file.

+ * @return

A list of StreamObjects.

*/ private static List parseStreams(String[] streams) { List parsedStreams = new ArrayList<>(); @@ -351,10 +360,10 @@ public abstract class Converter { /** * Parses a list of video stream parameters to a video stream object - * @param streamParts A list of parameters belonging to an video stream - * @param relativeIndex The relative index of the video stream - * @return A SubtitleStream object - * @throws NumberFormatException If codec index contains a non-numeric value + * @param streamParts

A list of parameters belonging to an video stream.

+ * @param relativeIndex

The relative index of the video stream.

+ * @return

A SubtitleStream object.

+ * @throws NumberFormatException

If codec index contains a non-numeric value.

*/ private static VideoStream parseVideoStream(String[] streamParts, int relativeIndex) throws NumberFormatException { String codec = null; @@ -371,10 +380,10 @@ public abstract class Converter { /** * Parses a list of audio stream parameters to an audio stream object - * @param streamParts A list of parameters belonging to an audio stream - * @param relativeIndex The relative index of the audio stream - * @return A SubtitleStream object - * @throws NumberFormatException If codec index contains a non-numeric value + * @param streamParts

A list of parameters belonging to an audio stream.

+ * @param relativeIndex

The relative index of the audio stream.

+ * @return

A SubtitleStream object.

+ * @throws NumberFormatException

If codec index contains a non-numeric value.

*/ private static AudioStream parseAudioStream(String[] streamParts, int relativeIndex) throws NumberFormatException { String codec = null; @@ -400,12 +409,13 @@ public abstract class Converter { /** * Parses a list of subtitle stream parameters to a subtitle stream object - * @param streamParts A list of parameters belonging to a subtitle stream - * @param relativeIndex The relative index of the subtitle - * @return A SubtitleStream object - * @throws NumberFormatException If codec index contains a non-numeric value + * @param streamParts

A list of parameters belonging to a subtitle stream.

+ * @param relativeIndex

The relative index of the subtitle.

+ * @return

A SubtitleStream object.

+ * @throws NumberFormatException

If codec index contains a non-numeric value.

*/ - private static SubtitleStream parseSubtitleStream(String[] streamParts, int relativeIndex) throws NumberFormatException { + private static SubtitleStream parseSubtitleStream(String[] streamParts, int relativeIndex) + throws NumberFormatException { String codecName = null; int absoluteIndex = -1; String language = null; @@ -424,6 +434,11 @@ public abstract class Converter { return new SubtitleStream(codecName, absoluteIndex, relativeIndex, language, title); } + /** + * Escapes special characters which can cause trouble for ffmpeg + * @param fileName

The filename to escape.

+ * @return

A filename with known special characters escaped.

+ */ static String escapeSpecialCharactersInFileName(String fileName) { return fileName.replace("'", "\\\\\\'") .replace(",", "\\\\\\,") @@ -431,18 +446,36 @@ public abstract class Converter { .replace("]", "\\]") .replace("[", "\\["); } - + + /** + * Prints something to the commandline efficiently + * @param input

The text to print.

+ * @throws IOException

If a write is not possible.

+ */ static void print(String input) throws IOException { if (!input.equals("")) { writer.write(input); writer.flush(); } } - + + /** + * Prints something and a newline to the commandline efficiently + * @param input

The text to print.

+ * @throws IOException

If a write is not possible.

+ */ static void printl(String input) throws IOException { if (!input.equals("")) { writer.write(input); } + printl(); + } + + /** + * Prints a newline + * @throws IOException

If a write is not possible.

+ */ + static void printl() throws IOException { writer.newLine(); writer.flush(); } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java index 39a70b4..a8a3874 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java @@ -10,8 +10,13 @@ import java.util.List; public class VideoConverter extends Converter { private String newExt; - private boolean debug = false; + /** + * Instantiates a new video converter + * @param ffprobePath

Path/command to ffprobe.

+ * @param ffmpegPath

Path/command to ffmpeg.

+ * @param newExt

The extension of the new file.

+ */ public VideoConverter(String ffprobePath, String ffmpegPath, String newExt) { this.ffprobePath = ffprobePath; this.ffmpegPath = ffmpegPath; @@ -19,11 +24,10 @@ public class VideoConverter extends Converter { } /** - * Reads streams from a file, and converts it to an mp4. - * - * @param folder The folder of the file to process - * @param file The file to process - * @throws IOException If the BufferedReader fails + * Reads streams from a file, and converts it to an mp4 + * @param folder

The folder of the file to process.

+ * @param file

The file to process.

+ * @throws IOException

If the BufferedReader fails.

*/ private void processFile(File folder, File file, String newExt) throws IOException { List streams = probeFile(ffprobePath, file); @@ -35,18 +39,17 @@ public class VideoConverter extends Converter { } /** - * Generates a command for a ProcessBuilder. - * - * @param executable The executable file for ffmpeg - * @param fileName The input file - * @param streams A list of ffprobe streams - * @param outFile The output file - * @return A list of commands + * Generates a command for a ProcessBuilder + * @param executable

The executable file for ffmpeg.

+ * @param fileName

The input file.

+ * @param streams

A list of ffprobe streams.

+ * @param outFile

The output file.

+ * @return

A list of commands

*/ private String[] builderCommand(String executable, String fileName, List streams, String outFile, File folder) { List command = generalFile(executable, fileName); - if (this.debug) { + if (this.DEBUG) { addDebug(command, 50, 120); } @@ -62,56 +65,90 @@ public class VideoConverter extends Converter { audioStream = audioStreams.get(0); } - String ext = hasExternalSubtitle(folder.getAbsolutePath(), fileName); - String ext2 = hasExternalImageSubtitle(folder.getAbsolutePath(), fileName); - if (!ext.equals("")) { - command.add("-vf"); - command.add("subtitles=" + stripExtension(fileName) + ext); - } else if (!ext2.equals("")) { - command.add("-i"); - command.add(stripExtension(fileName) + ext2); - if (this.debug) { - addDebug(command, 50, 120); - } - //TODO: Scale subtitles to video - command.add("-filter_complex"); - command.add("[1:s]scale=width=1920:height=800,crop=w=1920:h=800:x=0:y=out_h[sub];[" + videoStream + ":v][sub]overlay"); - command.add("-profile:v"); - command.add("baseline"); + boolean videoAdded = addSubtitles(command, folder, fileName, videoStream); + + if (!videoAdded && videoStreams.size() > 0) { + command.add("-map"); + command.add("0:" + videoStream); + } + if (audioStreams.size() > 0) { + command.add("-map"); + command.add("0:" + audioStream); } - if (ext2.equals("") || !ext.equals("")) { - if (videoStreams.size() > 0) { - command.add("-map"); - command.add("0:" + videoStream); - } - if (audioStreams.size() > 0) { - command.add("-map"); - command.add("0:" + audioStream); - } - } - command.add("-af"); - command.add("pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL|FR < 1.0*FR + 0.707*FC + 0.707*BR"); + convertToStereo(command); command.add(outFile); return command.toArray(new String[0]); } + /** + * Adds subtitles to a command + * @param command

The command to add to.

+ * @param folder

The folder containing the file to be converted.

+ * @param fileName

The name of the file to be converted.

+ * @param videoStream

The video stream to be added.

+ * @return

True if a video stream was added.

+ */ + private boolean addSubtitles(List command, File folder, String fileName, VideoStream videoStream) { + String externalSubtitle = hasExternalSubtitle(folder.getAbsolutePath(), fileName); + String externalImageSubtitle = hasExternalImageSubtitle(folder.getAbsolutePath(), fileName); + if (!externalSubtitle.equals("")) { + command.add("-vf"); + command.add("subtitles=" + stripExtension(fileName) + externalSubtitle); + } else if (!externalImageSubtitle.equals("")) { + command.add("-i"); + command.add(stripExtension(fileName) + externalImageSubtitle); + if (this.DEBUG) { + addDebug(command, 50, 120); + } + //TODO: Scale subtitles to video + command.add("-filter_complex"); + command.add("[1:s]scale=width=1920:height=800,crop=w=1920:h=800:x=0:y=out_h[sub];[" + videoStream + + ":v][sub]overlay"); + command.add("-profile:v"); + command.add("baseline"); + return true; + } + return false; + } + + /** + * Converts the audio of a video to stereo + * @param command

The command list to add to.

+ */ + private void convertToStereo(List command) { + command.add("-af"); + command.add("pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL|FR < 1.0*FR + 0.707*FC + 0.707*BR"); + } + + /** + * Checks whether there exists an external image subtitle with the same filename as the file + * @param directory

The directory containing the file.

+ * @param file

The file to be converted.

+ * @return

The extension of the subtitle or empty if no subtitle was found.

+ */ private String hasExternalImageSubtitle(String directory, String file) { String path = stripExtension(file); - for (String s : new String[] {".idx", ".sub"}) { - if (new File(directory + File.separator + path + s).exists()) { - return s; + for (String subtitleExtension : new String[] {".idx", ".sub"}) { + if (new File(directory + File.separator + path + subtitleExtension).exists()) { + return subtitleExtension; } } return ""; } + /** + * Checks whether there exists an external subtitle with the same filename as the file + * @param directory

The directory containing the file.

+ * @param file

The file to be converted.

+ * @return

The extension of the subtitle or empty if no subtitle was found.

+ */ private String hasExternalSubtitle(String directory, String file) { String path = stripExtension(file); - for (String s : new String[] {".srt", ".ass"}) { - if (new File(directory + File.separator + path + s).exists()) { - return s; + for (String subtitleExtension : new String[] {".srt", ".ass"}) { + if (new File(directory + File.separator + path + subtitleExtension).exists()) { + return subtitleExtension; } } return "";