Improves comments for converters
This commit is contained in:
		| @@ -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 <p>Path/command to ffprobe.</p> | ||||
|      * @param ffmpegPath <p>Path/command to ffmpeg.</p> | ||||
|      * @param audioLang <p>List of wanted audio languages in descending order.</p> | ||||
|      * @param subtitleLang <p>List of wanted subtitle languages in descending order.</p> | ||||
|      * @param toStereo <p>Convert video with several audio channels to stereo.</p> | ||||
|      * @param preventSignsAndSongs <p>Prevent subtitles only converting signs and songs (not speech).</p> | ||||
|      */ | ||||
|     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 <p>The file to convert.</p> | ||||
|      * @throws IOException <p>If the file cannot be converted.</p> | ||||
|      */ | ||||
|     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 <p>The folder of the file to process.</p> | ||||
|      * @param file <p>The file to process.</p> | ||||
|      * @throws IOException <p>If the BufferedReader fails.</p> | ||||
|      */ | ||||
|     private void processFile(File folder, File file) throws IOException { | ||||
|         List<StreamObject> 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 <p>The executable file for ffmpeg.</p> | ||||
|      * @param fileName <p>The input file.</p> | ||||
|      * @param streams <p>A list of ffprobe streams.</p> | ||||
|      * @param outFile <p>The output file.</p> | ||||
|      * @return <p>A list of commands</p> | ||||
|      */ | ||||
|     private String[] builderCommand(String executable, String fileName, List<StreamObject> streams, String outFile) { | ||||
|         List<String> command = ffmpegWebVideo(executable, fileName); | ||||
|  | ||||
|         if (this.debug) { | ||||
|         if (this.DEBUG) { | ||||
|             addDebug(command, 50, 120); | ||||
|         } | ||||
|  | ||||
|         List<AudioStream> audioStreams = filterStreamsByType(streams, "audio"); | ||||
|         List<AudioStream> audioStreams = filterAudioStreams(filterStreamsByType(streams, "audio"), audioLang); | ||||
|         List<VideoStream> videoStreams = filterStreamsByType(streams, "video"); | ||||
|         List<SubtitleStream> subtitleStreams = filterStreamsByType(streams, "subtitle"); | ||||
|  | ||||
|         audioStreams = filterAudioStreams(audioStreams, audioLang); | ||||
|         subtitleStreams = filterSubtitleStreams(subtitleStreams, subtitleLang, preventSignsAndSongs); | ||||
|         List<SubtitleStream> 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 <p>The command to add audio to.</p> | ||||
|      * @param audioStream <p>The audio stream to be added.</p> | ||||
|      */ | ||||
|     private void addAudioStreams(List<String> 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 <p>The list containing the rest of the command.</p> | ||||
|      * @param subtitleStream <p>The subtitle stream to be used.</p> | ||||
|      * @param videoStream <p>The video stream to be used.</p> | ||||
|      * @param fileName <p>The name of the file which is converted.</p> | ||||
|      */ | ||||
|     private void addSubtitles(List<String> 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(), | ||||
| @@ -124,9 +148,6 @@ public class AnimeConverter extends Converter { | ||||
|             command.add("-map"); | ||||
|             command.add(String.format("0:%d", videoStream.getAbsoluteIndex())); | ||||
|         } | ||||
|  | ||||
|         command.add(outFile); | ||||
|         return command.toArray(new String[0]); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -10,6 +10,12 @@ import java.util.List; | ||||
| public class AudioConverter extends Converter { | ||||
|     private String newExt; | ||||
|  | ||||
|     /** | ||||
|      * Instantiates a new audio converter | ||||
|      * @param ffprobePath <p>Path/command to ffprobe.</p> | ||||
|      * @param ffmpegPath <p>Path/command to ffmpeg.</p> | ||||
|      * @param newExt <p>The extension of the new file.</p> | ||||
|      */ | ||||
|     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 <p>The work folder containing the file.</p> | ||||
|      * @param file <p>The file to convert.</p> | ||||
|      * @param newExt <p>The extension of the new file.</p> | ||||
|      * @throws IOException <p>If the file cannot be converted.</p> | ||||
|      */ | ||||
|     private void processFile(File folder, File file, String newExt) throws IOException { | ||||
|         List<StreamObject> 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 <p>The executable file for ffmpeg.</p> | ||||
|      * @param fileName <p>The input file.</p> | ||||
|      * @param streams <p>A list of ffprobe streams.</p> | ||||
|      * @param outFile <p>The output file.</p> | ||||
|      * @return <p>A list of commands.</p> | ||||
|      */ | ||||
|     private String[] builderCommand(String executable, String fileName, List<StreamObject> streams, String outFile) { | ||||
|         List<String> command = generalFile(executable, fileName); | ||||
|   | ||||
| @@ -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 <p>The path/command to ffprobe.</p> | ||||
|      * @param file <p>The file to probe.</p> | ||||
|      * @return <p>A list of StreamObjects.</p> | ||||
|      * @throws IOException <p>If the process can't be read.</p> | ||||
|      */ | ||||
|     static List<StreamObject> 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 <p>The path the file should ideally be saved at.</p> | ||||
|      * @param extension <p>The extension of the target file.</p> | ||||
|      * @return <p>A filename guaranteed not to collide with other files.</p> | ||||
|      */ | ||||
|     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 <p>The process to run.</p> | ||||
|      * @param folder <p>The folder the process should run in.</p> | ||||
|      * @throws IOException <p>If the process can't be read.</p> | ||||
|      */ | ||||
|     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 <p>The reader of a process.</p> | ||||
|      * @return <p>The output from the read.</p> | ||||
|      * @throws IOException <p>On reader failure.</p> | ||||
|      */ | ||||
|     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 <p>The executable to use (ffmpeg/ffprobe).</p> | ||||
|      * @param fileName <p>The name of the file to execute on.</p> | ||||
|      * @return <p>A base list of ffmpeg commands for converting a video for web</p> | ||||
|      */ | ||||
|     static List<String> ffmpegWebVideo(String executable, String fileName) { | ||||
|         List<String> 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 <p>The executable to use (ffmpeg/ffprobe).</p> | ||||
|      * @param fileName <p>The name of the file to execute on.</p> | ||||
|      * @return <p>A base list of ffmpeg commands for converting a file.</p> | ||||
|      */ | ||||
|     static List<String> generalFile(String executable, String fileName) { | ||||
|         List<String> 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 <p>The list containing the command to run.</p> | ||||
|      * @param start <p>The offset before converting.</p> | ||||
|      * @param length <p>The offset for stopping the conversion.</p> | ||||
|      */ | ||||
|     static void addDebug(List<String> 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 <p>A list of ffprobe indexes.</p> | ||||
|      * @return <p>An integer list containing just the wanted indexes</p> | ||||
|      */ | ||||
|     private static List<Integer> listIndexes(String[] list, Predicate<String> p) { | ||||
|         List<Integer> 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 <T> Any type | ||||
|      * @param list <p>A list.</p> | ||||
|      * @param p <p>A predicate</p> | ||||
|      * @param <T> <p>The type of the list and the predicate.</p> | ||||
|      * @return True if the list have an element for which the predicate is true | ||||
|      */ | ||||
|     private static <T> boolean testPredicate(T[] list, Predicate<T> 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 <p>The string containing the substrings.</p> | ||||
|      * @param start <p>The substring before the wanted substring.</p> | ||||
|      * @param end <p>The substring after the wanted substring.</p> | ||||
|      * @return <p>A list of all occurrences of the substring.</p> | ||||
|      */ | ||||
|     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 <p>The string containing the substrings.</p> | ||||
|      * @param start <p>The substring before the wanted substring.</p> | ||||
|      * @param end <p>The substring after the wanted substring.</p> | ||||
|      * @return <p>The wanted substring.</p> | ||||
|      */ | ||||
|     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 <p>A file object.</p> | ||||
|      * @return <p>A filename.</p> | ||||
|      */ | ||||
|     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 <p>A filename.</p> | ||||
|      * @return <p>A filename without its extension.</p> | ||||
|      */ | ||||
|     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 <T>   Any type | ||||
|      * @return      A new array containing all elements from the two arrays | ||||
|      * @param listA <p>The first array.</p> | ||||
|      * @param listB <p>The second array.</p> | ||||
|      * @param <T> <p>The type of the two lists.</p> | ||||
|      * @return <p>A new array containing all elements from the two arrays.</p> | ||||
|      */ | ||||
|     public static <T> T[] concatenate(T[] a, T[] b) { | ||||
|         int aLen = a.length; | ||||
|         int bLen = b.length; | ||||
|     public static <T> 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 <G> The correct object type for the streams with the selected codec type | ||||
|      * @return A potentially shorter list of streams | ||||
|      * @param streams <p>A list of stream objects.</p> | ||||
|      * @param codecType <p>The codec type of the streams to select.</p> | ||||
|      * @param <G> <p>The correct object type for the streams with the selected codec type.</p> | ||||
|      * @return <p>A potentially shorter list of streams.</p> | ||||
|      */ | ||||
|     static <G extends StreamObject> List<G> filterStreamsByType(List<StreamObject> streams, String codecType) { | ||||
|         Iterator<StreamObject> 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 <p>A list of audio streams.</p> | ||||
|      * @param audioLanguages <p>A list of languages.</p> | ||||
|      * @return <p>A list containing just audio tracks of chosen languages, sorted in order of languages.</p> | ||||
|      */ | ||||
|     static List<AudioStream> filterAudioStreams(List<AudioStream> audioStreams, String[] audioLanguages) { | ||||
|         List<AudioStream> 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 <p>A list of subtitle streams.</p> | ||||
|      * @param subtitleLanguages <p>A list of languages.</p> | ||||
|      * @param preventSignsAndSongs <p>Whether partial subtitles should be avoided.</p> | ||||
|      * @return <p>A list containing just subtitles of chosen languages, sorted in order of languages.</p> | ||||
|      */ | ||||
|     static List<SubtitleStream> filterSubtitleStreams(List<SubtitleStream> 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 <p>A list of all streams for the current file.</p> | ||||
|      * @return <p>A list of StreamObjects.</p> | ||||
|      */ | ||||
|     private static List<StreamObject> parseStreams(String[] streams) { | ||||
|         List<StreamObject> 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 <p>A list of parameters belonging to an video stream.</p> | ||||
|      * @param relativeIndex <p>The relative index of the video stream.</p> | ||||
|      * @return <p>A SubtitleStream object.</p> | ||||
|      * @throws NumberFormatException <p>If codec index contains a non-numeric value.</p> | ||||
|      */ | ||||
|     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 <p>A list of parameters belonging to an audio stream.</p> | ||||
|      * @param relativeIndex <p>The relative index of the audio stream.</p> | ||||
|      * @return <p>A SubtitleStream object.</p> | ||||
|      * @throws NumberFormatException <p>If codec index contains a non-numeric value.</p> | ||||
|      */ | ||||
|     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 <p>A list of parameters belonging to a subtitle stream.</p> | ||||
|      * @param relativeIndex <p>The relative index of the subtitle.</p> | ||||
|      * @return <p>A SubtitleStream object.</p> | ||||
|      * @throws NumberFormatException <p>If codec index contains a non-numeric value.</p> | ||||
|      */ | ||||
|     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 <p>The filename to escape.</p> | ||||
|      * @return <p>A filename with known special characters escaped.</p> | ||||
|      */ | ||||
|     static String escapeSpecialCharactersInFileName(String fileName) { | ||||
|         return fileName.replace("'", "\\\\\\'") | ||||
|                 .replace(",", "\\\\\\,") | ||||
| @@ -432,6 +447,11 @@ public abstract class Converter { | ||||
|                 .replace("[", "\\["); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Prints something to the commandline efficiently | ||||
|      * @param input <p>The text to print.</p> | ||||
|      * @throws IOException <p>If a write is not possible.</p> | ||||
|      */ | ||||
|     static void print(String input) throws IOException { | ||||
|         if (!input.equals("")) { | ||||
|             writer.write(input); | ||||
| @@ -439,10 +459,23 @@ public abstract class Converter { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Prints something and a newline to the commandline efficiently | ||||
|      * @param input <p>The text to print.</p> | ||||
|      * @throws IOException <p>If a write is not possible.</p> | ||||
|      */ | ||||
|     static void printl(String input) throws IOException { | ||||
|         if (!input.equals("")) { | ||||
|             writer.write(input); | ||||
|         } | ||||
|         printl(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Prints a newline | ||||
|      * @throws IOException <p>If a write is not possible.</p> | ||||
|      */ | ||||
|     static void printl() throws IOException { | ||||
|         writer.newLine(); | ||||
|         writer.flush(); | ||||
|     } | ||||
|   | ||||
| @@ -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 <p>Path/command to ffprobe.</p> | ||||
|      * @param ffmpegPath <p>Path/command to ffmpeg.</p> | ||||
|      * @param newExt <p>The extension of the new file.</p> | ||||
|      */ | ||||
|     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 <p>The folder of the file to process.</p> | ||||
|      * @param file <p>The file to process.</p> | ||||
|      * @throws IOException <p>If the BufferedReader fails.</p> | ||||
|      */ | ||||
|     private void processFile(File folder, File file, String newExt) throws IOException { | ||||
|         List<StreamObject> 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 <p>The executable file for ffmpeg.</p> | ||||
|      * @param fileName <p>The input file.</p> | ||||
|      * @param streams <p>A list of ffprobe streams.</p> | ||||
|      * @param outFile <p>The output file.</p> | ||||
|      * @return <p>A list of commands</p> | ||||
|      */ | ||||
|     private String[] builderCommand(String executable, String fileName, List<StreamObject> streams, String outFile, File folder) { | ||||
|         List<String> command = generalFile(executable, fileName); | ||||
|  | ||||
|         if (this.debug) { | ||||
|         if (this.DEBUG) { | ||||
|             addDebug(command, 50, 120); | ||||
|         } | ||||
|  | ||||
| @@ -62,26 +65,9 @@ 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 (ext2.equals("") || !ext.equals("")) { | ||||
|             if (videoStreams.size() > 0) { | ||||
|         if (!videoAdded && videoStreams.size() > 0) { | ||||
|             command.add("-map"); | ||||
|             command.add("0:" + videoStream); | ||||
|         } | ||||
| @@ -89,29 +75,80 @@ public class VideoConverter extends Converter { | ||||
|             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 <p>The command to add to.</p> | ||||
|      * @param folder <p>The folder containing the file to be converted.</p> | ||||
|      * @param fileName <p>The name of the file to be converted.</p> | ||||
|      * @param videoStream <p>The video stream to be added.</p> | ||||
|      * @return <p>True if a video stream was added.</p> | ||||
|      */ | ||||
|     private boolean addSubtitles(List<String> 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 <p>The command list to add to.</p> | ||||
|      */ | ||||
|     private void convertToStereo(List<String> 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 <p>The directory containing the file.</p> | ||||
|      * @param file <p>The file to be converted.</p> | ||||
|      * @return <p>The extension of the subtitle or empty if no subtitle was found.</p> | ||||
|      */ | ||||
|     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 <p>The directory containing the file.</p> | ||||
|      * @param file <p>The file to be converted.</p> | ||||
|      * @return <p>The extension of the subtitle or empty if no subtitle was found.</p> | ||||
|      */ | ||||
|     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 ""; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user