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.File; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| public class AnimeConverter extends Converter { | public class AnimeConverter extends Converter { | ||||||
| @@ -15,17 +14,18 @@ public class AnimeConverter extends Converter { | |||||||
|     private String[] subtitleLang; |     private String[] subtitleLang; | ||||||
|     private boolean toStereo; |     private boolean toStereo; | ||||||
|     private boolean preventSignsAndSongs; |     private boolean preventSignsAndSongs; | ||||||
|     private boolean debug = false; |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @param ffprobePath           Path/command to ffprobe |      * Instantiates a new anime converter | ||||||
|      * @param ffmpegPath            Path/command to ffmpeg |      * @param ffprobePath <p>Path/command to ffprobe.</p> | ||||||
|      * @param audioLang             List of wanted audio languages in descending order |      * @param ffmpegPath <p>Path/command to ffmpeg.</p> | ||||||
|      * @param subtitleLang          List of wanted subtitle languages in descending order |      * @param audioLang <p>List of wanted audio languages in descending order.</p> | ||||||
|      * @param toStereo              Convert video with several audio channels to stereo |      * @param subtitleLang <p>List of wanted subtitle languages in descending order.</p> | ||||||
|      * @param preventSignsAndSongs  Prevent subtitles only converting signs and songs (not speech) |      * @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.ffprobePath = ffprobePath; | ||||||
|         this.ffmpegPath = ffmpegPath; |         this.ffmpegPath = ffmpegPath; | ||||||
|         this.audioLang = audioLang; |         this.audioLang = audioLang; | ||||||
| @@ -34,21 +34,26 @@ public class AnimeConverter extends Converter { | |||||||
|         this.preventSignsAndSongs = preventSignsAndSongs; |         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 { |     public void convert(File file) throws IOException { | ||||||
|         processFile(file.getParentFile(), file); |         processFile(file.getParentFile(), file); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Reads streams from a file, and converts it to an mp4. |      * Reads streams from a file, and converts it to an mp4 | ||||||
|      * |      * @param folder <p>The folder of the file to process.</p> | ||||||
|      * @param folder        The folder of the file to process |      * @param file <p>The file to process.</p> | ||||||
|      * @param file          The file to process |      * @throws IOException <p>If the BufferedReader fails.</p> | ||||||
|      * @throws IOException  If the BufferedReader fails |  | ||||||
|      */ |      */ | ||||||
|     private void processFile(File folder, File file) throws IOException { |     private void processFile(File folder, File file) throws IOException { | ||||||
|         List<StreamObject> streams = probeFile(ffprobePath, file); |         List<StreamObject> streams = probeFile(ffprobePath, file); | ||||||
|         if (streams.isEmpty()) { |         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 + |         String newPath = fileCollisionPrevention(folder.getAbsolutePath() + File.separator + | ||||||
|                 stripExtension(file) + ".mp4", "mp4"); |                 stripExtension(file) + ".mp4", "mp4"); | ||||||
| @@ -60,26 +65,23 @@ public class AnimeConverter extends Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Generates a command for a ProcessBuilder. |      * Generates a command for a ProcessBuilder. | ||||||
|      * |      * @param executable <p>The executable file for ffmpeg.</p> | ||||||
|      * @param executable    The executable file for ffmpeg |      * @param fileName <p>The input file.</p> | ||||||
|      * @param fileName      The input file |      * @param streams <p>A list of ffprobe streams.</p> | ||||||
|      * @param streams       A list of ffprobe streams |      * @param outFile <p>The output file.</p> | ||||||
|      * @param outFile       The output file |      * @return <p>A list of commands</p> | ||||||
|      * @return              A list of commands |  | ||||||
|      */ |      */ | ||||||
|     private String[] builderCommand(String executable, String fileName, List<StreamObject> streams, String outFile) { |     private String[] builderCommand(String executable, String fileName, List<StreamObject> streams, String outFile) { | ||||||
|         List<String> command = ffmpegWebVideo(executable, fileName); |         List<String> command = ffmpegWebVideo(executable, fileName); | ||||||
|  |  | ||||||
|         if (this.debug) { |         if (this.DEBUG) { | ||||||
|             addDebug(command, 50, 120); |             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<VideoStream> videoStreams = filterStreamsByType(streams, "video"); | ||||||
|         List<SubtitleStream> subtitleStreams = filterStreamsByType(streams, "subtitle"); |         List<SubtitleStream> subtitleStreams = filterSubtitleStreams(filterStreamsByType(streams, | ||||||
|  |                 "subtitle"), subtitleLang, preventSignsAndSongs); | ||||||
|         audioStreams = filterAudioStreams(audioStreams, audioLang); |  | ||||||
|         subtitleStreams = filterSubtitleStreams(subtitleStreams, subtitleLang, preventSignsAndSongs); |  | ||||||
|  |  | ||||||
|         VideoStream videoStream = null; |         VideoStream videoStream = null; | ||||||
|         AudioStream audioStream = null; |         AudioStream audioStream = null; | ||||||
| @@ -87,6 +89,9 @@ public class AnimeConverter extends Converter { | |||||||
|         if (videoStreams.size() > 0) { |         if (videoStreams.size() > 0) { | ||||||
|             videoStream = videoStreams.get(0); |             videoStream = videoStreams.get(0); | ||||||
|         } |         } | ||||||
|  |         if (videoStream == null) { | ||||||
|  |             throw new IllegalArgumentException("The file does not have any valid video streams."); | ||||||
|  |         } | ||||||
|         if (audioStreams.size() > 0) { |         if (audioStreams.size() > 0) { | ||||||
|             audioStream = audioStreams.get(0); |             audioStream = audioStreams.get(0); | ||||||
|         } |         } | ||||||
| @@ -94,10 +99,19 @@ public class AnimeConverter extends Converter { | |||||||
|             subtitleStream = subtitleStreams.get(0); |             subtitleStream = subtitleStreams.get(0); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (videoStream == null) { |         addAudioStreams(command, audioStream); | ||||||
|             throw new IllegalArgumentException("The file does not have any valid video streams."); |         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) { |         if (audioStream != null) { | ||||||
|             command.add("-map"); |             command.add("-map"); | ||||||
|             command.add("0:" + audioStream.getAbsoluteIndex()); |             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"); |                 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()) { |         if (subtitleStream != null && subtitleStream.getIsImageSubtitle()) { | ||||||
|             command.add("-filter_complex"); |             command.add("-filter_complex"); | ||||||
|             String filter = String.format("[0:v:%d][0:%d]overlay", videoStream.getAbsoluteIndex(), |             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("-map"); | ||||||
|             command.add(String.format("0:%d", videoStream.getAbsoluteIndex())); |             command.add(String.format("0:%d", videoStream.getAbsoluteIndex())); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         command.add(outFile); |  | ||||||
|         return command.toArray(new String[0]); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
| @@ -10,6 +10,12 @@ import java.util.List; | |||||||
| public class AudioConverter extends Converter { | public class AudioConverter extends Converter { | ||||||
|     private String newExt; |     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) { |     public AudioConverter(String ffprobePath, String ffmpegPath, String newExt) { | ||||||
|         this.ffprobePath = ffprobePath; |         this.ffprobePath = ffprobePath; | ||||||
|         this.ffmpegPath = ffmpegPath; |         this.ffmpegPath = ffmpegPath; | ||||||
| @@ -17,11 +23,11 @@ public class AudioConverter extends Converter { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Reads streams from a file, and converts it to an mp4. |      * Processes a file conversion | ||||||
|      * |      * @param folder <p>The work folder containing the file.</p> | ||||||
|      * @param folder        The folder of the file to process |      * @param file <p>The file to convert.</p> | ||||||
|      * @param file          The file to process |      * @param newExt <p>The extension of the new file.</p> | ||||||
|      * @throws IOException  If the BufferedReader fails |      * @throws IOException <p>If the file cannot be converted.</p> | ||||||
|      */ |      */ | ||||||
|     private void processFile(File folder, File file, String newExt) throws IOException { |     private void processFile(File folder, File file, String newExt) throws IOException { | ||||||
|         List<StreamObject> streams = probeFile(ffprobePath, file); |         List<StreamObject> streams = probeFile(ffprobePath, file); | ||||||
| @@ -34,12 +40,11 @@ public class AudioConverter extends Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Generates a command for a ProcessBuilder. |      * Generates a command for a ProcessBuilder. | ||||||
|      * |      * @param executable <p>The executable file for ffmpeg.</p> | ||||||
|      * @param executable    The executable file for ffmpeg |      * @param fileName <p>The input file.</p> | ||||||
|      * @param fileName      The input file |      * @param streams <p>A list of ffprobe streams.</p> | ||||||
|      * @param streams       A list of ffprobe streams |      * @param outFile <p>The output file.</p> | ||||||
|      * @param outFile       The output file |      * @return <p>A list of commands.</p> | ||||||
|      * @return              A list of commands |  | ||||||
|      */ |      */ | ||||||
|     private String[] builderCommand(String executable, String fileName, List<StreamObject> streams, String outFile) { |     private String[] builderCommand(String executable, String fileName, List<StreamObject> streams, String outFile) { | ||||||
|         List<String> command = generalFile(executable, fileName); |         List<String> command = generalFile(executable, fileName); | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ import java.util.function.Predicate; | |||||||
| public abstract class Converter { | public abstract class Converter { | ||||||
|     String ffprobePath; |     String ffprobePath; | ||||||
|     String ffmpegPath; |     String ffmpegPath; | ||||||
|  |     boolean DEBUG = false; | ||||||
|      |      | ||||||
|     private static final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out)); |     private static final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out)); | ||||||
|  |  | ||||||
| @@ -41,10 +42,10 @@ public abstract class Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Gets streams from a file |      * Gets streams from a file | ||||||
|      * @param ffprobePath The path/command to ffprobe |      * @param ffprobePath <p>The path/command to ffprobe.</p> | ||||||
|      * @param file The file to probe |      * @param file <p>The file to probe.</p> | ||||||
|      * @return A list of StreamObjects |      * @return <p>A list of StreamObjects.</p> | ||||||
|      * @throws IOException If the process can't be read |      * @throws IOException <p>If the process can't be read.</p> | ||||||
|      */ |      */ | ||||||
|     static List<StreamObject> probeFile(String ffprobePath, File file) throws IOException { |     static List<StreamObject> probeFile(String ffprobePath, File file) throws IOException { | ||||||
|         ProcessBuilder builderProbe = new ProcessBuilder( |         ProcessBuilder builderProbe = new ProcessBuilder( | ||||||
| @@ -71,20 +72,27 @@ public abstract class Converter { | |||||||
|         return parseStreams(stringBetween(output.toString(), "[STREAM]", "[/STREAM]")); |         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) { |     static String fileCollisionPrevention(String targetPath, String extension) { | ||||||
|         File file = new File(targetPath); |         File newFile = new File(targetPath); | ||||||
|  |         String fileName = stripExtension(targetPath); | ||||||
|         int i = 1; |         int i = 1; | ||||||
|         while (file.exists()) { |         while (newFile.exists()) { | ||||||
|             file = new File(stripExtension(file) + "(" + i + ")" + "." + extension); |             newFile = new File(fileName + "(" + i++ + ")" + "." + extension); | ||||||
|         } |         } | ||||||
|         return file.toString(); |         return newFile.toString(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Starts and prints output of a process |      * Starts and prints output of a process | ||||||
|      * @param process The process to run |      * @param process <p>The process to run.</p> | ||||||
|      * @param folder The folder the process should run in |      * @param folder <p>The folder the process should run in.</p> | ||||||
|      * @throws IOException If the process can't be read |      * @throws IOException <p>If the process can't be read.</p> | ||||||
|      */ |      */ | ||||||
|     static void convertProcess(ProcessBuilder process, File folder) throws IOException { |     static void convertProcess(ProcessBuilder process, File folder) throws IOException { | ||||||
|         print("Command to be run: "); |         print("Command to be run: "); | ||||||
| @@ -99,15 +107,14 @@ public abstract class Converter { | |||||||
|                 printl(read); |                 printl(read); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         printl("FFMPEG is finished."); |         printl("Process is finished."); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Reads from a process reader. |      * Reads from a process reader | ||||||
|      * |      * @param reader <p>The reader of a process.</p> | ||||||
|      * @param reader        The reader of a process |      * @return <p>The output from the read.</p> | ||||||
|      * @return              The output from the read |      * @throws IOException <p>On reader failure.</p> | ||||||
|      * @throws IOException  On reader failure |  | ||||||
|      */ |      */ | ||||||
|     private static String read(BufferedReader reader, String spacer) throws IOException { |     private static String read(BufferedReader reader, String spacer) throws IOException { | ||||||
|         String line; |         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) { |     static List<String> ffmpegWebVideo(String executable, String fileName) { | ||||||
|         List<String> command = generalFile(executable, 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) { |     static List<String> generalFile(String executable, String fileName) { | ||||||
|         List<String> command = new ArrayList<>(); |         List<String> command = new ArrayList<>(); | ||||||
| @@ -148,9 +161,9 @@ public abstract class Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Adds debugging parameters for only converting parts of a file |      * Adds debugging parameters for only converting parts of a file | ||||||
|      * @param command The list containing the command to run |      * @param command <p>The list containing the command to run.</p> | ||||||
|      * @param start The offset before converting |      * @param start <p>The offset before converting.</p> | ||||||
|      * @param length The offset for stopping the conversion |      * @param length <p>The offset for stopping the conversion.</p> | ||||||
|      */ |      */ | ||||||
|     static void addDebug(List<String> command, int start, int length) { |     static void addDebug(List<String> command, int start, int length) { | ||||||
|         command.add("-ss"); |         command.add("-ss"); | ||||||
| @@ -160,10 +173,9 @@ public abstract class Converter { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Lists all indexes fulfilling a predicate. |      * Lists all indexes fulfilling a predicate | ||||||
|      * |      * @param list <p>A list of ffprobe indexes.</p> | ||||||
|      * @param list  A list of ffprobe indexes |      * @return <p>An integer list containing just the wanted indexes</p> | ||||||
|      * @return      An integer list containing just the wanted indexes |  | ||||||
|      */ |      */ | ||||||
|     private static List<Integer> listIndexes(String[] list, Predicate<String> p) { |     private static List<Integer> listIndexes(String[] list, Predicate<String> p) { | ||||||
|         List<Integer> indexes = new ArrayList<>(); |         List<Integer> indexes = new ArrayList<>(); | ||||||
| @@ -177,9 +189,9 @@ public abstract class Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Tests a predicate on a list |      * Tests a predicate on a list | ||||||
|      * @param list A list |      * @param list <p>A list.</p> | ||||||
|      * @param p A predicate |      * @param p <p>A predicate</p> | ||||||
|      * @param <T> Any type |      * @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 |      * @return True if the list have an element for which the predicate is true | ||||||
|      */ |      */ | ||||||
|     private static <T> boolean testPredicate(T[] list, Predicate<T> p) { |     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. |      * Finds all substrings between two substrings in a string | ||||||
|      * |      * @param string <p>The string containing the substrings.</p> | ||||||
|      * @param string The string containing the substrings |      * @param start <p>The substring before the wanted substring.</p> | ||||||
|      * @param start  The substring before the wanted substring |      * @param end <p>The substring after the wanted substring.</p> | ||||||
|      * @param end    The substring after the wanted substring |      * @return <p>A list of all occurrences of the substring.</p> | ||||||
|      * @return      A list of all occurrences of the substring |  | ||||||
|      */ |      */ | ||||||
|     private static String[] stringBetween(String string, String start, String end) { |     private static String[] stringBetween(String string, String start, String end) { | ||||||
|         int startPos = string.indexOf(start) + start.length(); |         int startPos = string.indexOf(start) + start.length(); | ||||||
| @@ -211,12 +222,11 @@ public abstract class Converter { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Finds a substring between two substrings in a string. |      * Finds a substring between two substrings in a string | ||||||
|      * |      * @param string <p>The string containing the substrings.</p> | ||||||
|      * @param string The string containing the substrings |      * @param start <p>The substring before the wanted substring.</p> | ||||||
|      * @param start  The substring before the wanted substring |      * @param end <p>The substring after the wanted substring.</p> | ||||||
|      * @param end    The substring after the wanted substring |      * @return <p>The wanted substring.</p> | ||||||
|      * @return The wanted substring. |  | ||||||
|      */ |      */ | ||||||
|     private static String stringBetweenSingle(String string, String start, String end) { |     private static String stringBetweenSingle(String string, String start, String end) { | ||||||
|         int startPos = string.indexOf(start) + start.length(); |         int startPos = string.indexOf(start) + start.length(); | ||||||
| @@ -228,8 +238,8 @@ public abstract class Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Gets filename without extension from File object |      * Gets filename without extension from File object | ||||||
|      * @param file A file object |      * @param file <p>A file object.</p> | ||||||
|      * @return A filename |      * @return <p>A filename.</p> | ||||||
|      */ |      */ | ||||||
|     static String stripExtension(File file) { |     static String stripExtension(File file) { | ||||||
|         return file.getName().substring(0, file.getName().lastIndexOf('.')); |         return file.getName().substring(0, file.getName().lastIndexOf('.')); | ||||||
| @@ -237,8 +247,8 @@ public abstract class Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Removes the extension from a file name |      * Removes the extension from a file name | ||||||
|      * @param file A filename |      * @param file <p>A filename.</p> | ||||||
|      * @return A filename without its extension |      * @return <p>A filename without its extension.</p> | ||||||
|      */ |      */ | ||||||
|     static String stripExtension(String file) { |     static String stripExtension(String file) { | ||||||
|         return file.substring(0, file.lastIndexOf('.')); |         return file.substring(0, file.lastIndexOf('.')); | ||||||
| @@ -246,28 +256,27 @@ public abstract class Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Combines two arrays to one |      * Combines two arrays to one | ||||||
|      * |      * @param listA <p>The first array.</p> | ||||||
|      * @param a     The first array |      * @param listB <p>The second array.</p> | ||||||
|      * @param b     The second array |      * @param <T> <p>The type of the two lists.</p> | ||||||
|      * @param <T>   Any type |      * @return <p>A new array containing all elements from the two arrays.</p> | ||||||
|      * @return      A new array containing all elements from the two arrays |  | ||||||
|      */ |      */ | ||||||
|     public static <T> T[] concatenate(T[] a, T[] b) { |     public static <T> T[] concatenate(T[] listA, T[] listB) { | ||||||
|         int aLen = a.length; |         int listALength = listA.length; | ||||||
|         int bLen = b.length; |         int listBLength = listB.length; | ||||||
|         @SuppressWarnings("unchecked") |         @SuppressWarnings("unchecked") | ||||||
|         T[] c = (T[]) Array.newInstance(a.getClass().getComponentType(), aLen + bLen); |         T[] resultList = (T[]) Array.newInstance(listA.getClass().getComponentType(), listALength + listBLength); | ||||||
|         System.arraycopy(a, 0, c, 0, aLen); |         System.arraycopy(listA, 0, resultList, 0, listALength); | ||||||
|         System.arraycopy(b, 0, c, aLen, bLen); |         System.arraycopy(listB, 0, resultList, listALength, listBLength); | ||||||
|         return c; |         return resultList; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Filters parsed streams into one of the stream types |      * Filters parsed streams into one of the stream types | ||||||
|      * @param streams A list of stream objects |      * @param streams <p>A list of stream objects.</p> | ||||||
|      * @param codecType The codec type of the streams to select |      * @param codecType <p>The codec type of the streams to select.</p> | ||||||
|      * @param <G> The correct object type for the streams with the selected codec type |      * @param <G> <p>The correct object type for the streams with the selected codec type.</p> | ||||||
|      * @return A potentially shorter list of streams |      * @return <p>A potentially shorter list of streams.</p> | ||||||
|      */ |      */ | ||||||
|     static <G extends StreamObject> List<G> filterStreamsByType(List<StreamObject> streams, String codecType) { |     static <G extends StreamObject> List<G> filterStreamsByType(List<StreamObject> streams, String codecType) { | ||||||
|         Iterator<StreamObject> i = streams.iterator(); |         Iterator<StreamObject> i = streams.iterator(); | ||||||
| @@ -283,9 +292,9 @@ public abstract class Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Filters and sorts audio streams according to chosen languages |      * Filters and sorts audio streams according to chosen languages | ||||||
|      * @param audioStreams A list of audio streams |      * @param audioStreams <p>A list of audio streams.</p> | ||||||
|      * @param audioLanguages A list of languages |      * @param audioLanguages <p>A list of languages.</p> | ||||||
|      * @return A list containing just audio tracks of chosen languages, sorted in order of languages |      * @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) { |     static List<AudioStream> filterAudioStreams(List<AudioStream> audioStreams, String[] audioLanguages) { | ||||||
|         List<AudioStream> filtered = new ArrayList<>(); |         List<AudioStream> filtered = new ArrayList<>(); | ||||||
| @@ -303,10 +312,10 @@ public abstract class Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Filters and sorts subtitle streams according to chosen languages |      * Filters and sorts subtitle streams according to chosen languages | ||||||
|      * @param subtitleStreams A list of subtitle streams |      * @param subtitleStreams <p>A list of subtitle streams.</p> | ||||||
|      * @param subtitleLanguages A list of languages |      * @param subtitleLanguages <p>A list of languages.</p> | ||||||
|      * @param preventSignsAndSongs Whether partial subtitles should be avoided |      * @param preventSignsAndSongs <p>Whether partial subtitles should be avoided.</p> | ||||||
|      * @return A list containing just subtitles of chosen languages, sorted in order of languages |      * @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, |     static List<SubtitleStream> filterSubtitleStreams(List<SubtitleStream> subtitleStreams, String[] subtitleLanguages, | ||||||
|                                                       boolean preventSignsAndSongs) { |                                                       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 |      * 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 |      * @param streams <p>A list of all streams for the current file.</p> | ||||||
|      * @return A list of StreamObjects |      * @return <p>A list of StreamObjects.</p> | ||||||
|      */ |      */ | ||||||
|     private static List<StreamObject> parseStreams(String[] streams) { |     private static List<StreamObject> parseStreams(String[] streams) { | ||||||
|         List<StreamObject> parsedStreams = new ArrayList<>(); |         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 |      * Parses a list of video stream parameters to a video stream object | ||||||
|      * @param streamParts A list of parameters belonging to an video stream |      * @param streamParts <p>A list of parameters belonging to an video stream.</p> | ||||||
|      * @param relativeIndex The relative index of the video stream |      * @param relativeIndex <p>The relative index of the video stream.</p> | ||||||
|      * @return A SubtitleStream object |      * @return <p>A SubtitleStream object.</p> | ||||||
|      * @throws NumberFormatException If codec index contains a non-numeric value |      * @throws NumberFormatException <p>If codec index contains a non-numeric value.</p> | ||||||
|      */ |      */ | ||||||
|     private static VideoStream parseVideoStream(String[] streamParts, int relativeIndex) throws NumberFormatException { |     private static VideoStream parseVideoStream(String[] streamParts, int relativeIndex) throws NumberFormatException { | ||||||
|         String codec = null; |         String codec = null; | ||||||
| @@ -371,10 +380,10 @@ public abstract class Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Parses a list of audio stream parameters to an audio stream object |      * Parses a list of audio stream parameters to an audio stream object | ||||||
|      * @param streamParts A list of parameters belonging to an audio stream |      * @param streamParts <p>A list of parameters belonging to an audio stream.</p> | ||||||
|      * @param relativeIndex The relative index of the audio stream |      * @param relativeIndex <p>The relative index of the audio stream.</p> | ||||||
|      * @return A SubtitleStream object |      * @return <p>A SubtitleStream object.</p> | ||||||
|      * @throws NumberFormatException If codec index contains a non-numeric value |      * @throws NumberFormatException <p>If codec index contains a non-numeric value.</p> | ||||||
|      */ |      */ | ||||||
|     private static AudioStream parseAudioStream(String[] streamParts, int relativeIndex) throws NumberFormatException { |     private static AudioStream parseAudioStream(String[] streamParts, int relativeIndex) throws NumberFormatException { | ||||||
|         String codec = null; |         String codec = null; | ||||||
| @@ -400,12 +409,13 @@ public abstract class Converter { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Parses a list of subtitle stream parameters to a subtitle stream object |      * Parses a list of subtitle stream parameters to a subtitle stream object | ||||||
|      * @param streamParts A list of parameters belonging to a subtitle stream |      * @param streamParts <p>A list of parameters belonging to a subtitle stream.</p> | ||||||
|      * @param relativeIndex The relative index of the subtitle |      * @param relativeIndex <p>The relative index of the subtitle.</p> | ||||||
|      * @return A SubtitleStream object |      * @return <p>A SubtitleStream object.</p> | ||||||
|      * @throws NumberFormatException If codec index contains a non-numeric value |      * @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; |         String codecName = null; | ||||||
|         int absoluteIndex = -1; |         int absoluteIndex = -1; | ||||||
|         String language = null; |         String language = null; | ||||||
| @@ -424,6 +434,11 @@ public abstract class Converter { | |||||||
|         return new SubtitleStream(codecName, absoluteIndex, relativeIndex, language, title); |         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) { |     static String escapeSpecialCharactersInFileName(String fileName) { | ||||||
|         return fileName.replace("'", "\\\\\\'") |         return fileName.replace("'", "\\\\\\'") | ||||||
|                 .replace(",", "\\\\\\,") |                 .replace(",", "\\\\\\,") | ||||||
| @@ -432,6 +447,11 @@ public abstract class Converter { | |||||||
|                 .replace("[", "\\["); |                 .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 { |     static void print(String input) throws IOException { | ||||||
|         if (!input.equals("")) { |         if (!input.equals("")) { | ||||||
|             writer.write(input); |             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 { |     static void printl(String input) throws IOException { | ||||||
|         if (!input.equals("")) { |         if (!input.equals("")) { | ||||||
|             writer.write(input); |             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.newLine(); | ||||||
|         writer.flush(); |         writer.flush(); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -10,8 +10,13 @@ import java.util.List; | |||||||
|  |  | ||||||
| public class VideoConverter extends Converter { | public class VideoConverter extends Converter { | ||||||
|     private String newExt; |     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) { |     public VideoConverter(String ffprobePath, String ffmpegPath, String newExt) { | ||||||
|         this.ffprobePath = ffprobePath; |         this.ffprobePath = ffprobePath; | ||||||
|         this.ffmpegPath = ffmpegPath; |         this.ffmpegPath = ffmpegPath; | ||||||
| @@ -19,11 +24,10 @@ public class VideoConverter extends Converter { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Reads streams from a file, and converts it to an mp4. |      * Reads streams from a file, and converts it to an mp4 | ||||||
|      * |      * @param folder <p>The folder of the file to process.</p> | ||||||
|      * @param folder        The folder of the file to process |      * @param file <p>The file to process.</p> | ||||||
|      * @param file          The file to process |      * @throws IOException <p>If the BufferedReader fails.</p> | ||||||
|      * @throws IOException  If the BufferedReader fails |  | ||||||
|      */ |      */ | ||||||
|     private void processFile(File folder, File file, String newExt) throws IOException { |     private void processFile(File folder, File file, String newExt) throws IOException { | ||||||
|         List<StreamObject> streams = probeFile(ffprobePath, file); |         List<StreamObject> streams = probeFile(ffprobePath, file); | ||||||
| @@ -35,18 +39,17 @@ public class VideoConverter extends Converter { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Generates a command for a ProcessBuilder. |      * Generates a command for a ProcessBuilder | ||||||
|      * |      * @param executable <p>The executable file for ffmpeg.</p> | ||||||
|      * @param executable    The executable file for ffmpeg |      * @param fileName <p>The input file.</p> | ||||||
|      * @param fileName      The input file |      * @param streams <p>A list of ffprobe streams.</p> | ||||||
|      * @param streams       A list of ffprobe streams |      * @param outFile <p>The output file.</p> | ||||||
|      * @param outFile       The output file |      * @return <p>A list of commands</p> | ||||||
|      * @return              A list of commands |  | ||||||
|      */ |      */ | ||||||
|     private String[] builderCommand(String executable, String fileName, List<StreamObject> streams, String outFile, File folder) { |     private String[] builderCommand(String executable, String fileName, List<StreamObject> streams, String outFile, File folder) { | ||||||
|         List<String> command = generalFile(executable, fileName); |         List<String> command = generalFile(executable, fileName); | ||||||
|  |  | ||||||
|         if (this.debug) { |         if (this.DEBUG) { | ||||||
|             addDebug(command, 50, 120); |             addDebug(command, 50, 120); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -62,26 +65,9 @@ public class VideoConverter extends Converter { | |||||||
|             audioStream = audioStreams.get(0); |             audioStream = audioStreams.get(0); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         String ext = hasExternalSubtitle(folder.getAbsolutePath(), fileName); |         boolean videoAdded = addSubtitles(command, folder, fileName, videoStream); | ||||||
|         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"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (ext2.equals("") || !ext.equals("")) { |         if (!videoAdded && videoStreams.size() > 0) { | ||||||
|             if (videoStreams.size() > 0) { |  | ||||||
|             command.add("-map"); |             command.add("-map"); | ||||||
|             command.add("0:" + videoStream); |             command.add("0:" + videoStream); | ||||||
|         } |         } | ||||||
| @@ -89,29 +75,80 @@ public class VideoConverter extends Converter { | |||||||
|             command.add("-map"); |             command.add("-map"); | ||||||
|             command.add("0:" + audioStream); |             command.add("0:" + audioStream); | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|         command.add("-af"); |         convertToStereo(command); | ||||||
|         command.add("pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL|FR < 1.0*FR + 0.707*FC + 0.707*BR"); |  | ||||||
|  |  | ||||||
|         command.add(outFile); |         command.add(outFile); | ||||||
|         return command.toArray(new String[0]); |         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) { |     private String hasExternalImageSubtitle(String directory, String file) { | ||||||
|         String path = stripExtension(file); |         String path = stripExtension(file); | ||||||
|         for (String s : new String[] {".idx", ".sub"}) { |         for (String subtitleExtension : new String[] {".idx", ".sub"}) { | ||||||
|             if (new File(directory + File.separator + path + s).exists()) { |             if (new File(directory + File.separator + path + subtitleExtension).exists()) { | ||||||
|                 return s; |                 return subtitleExtension; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         return ""; |         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) { |     private String hasExternalSubtitle(String directory, String file) { | ||||||
|         String path = stripExtension(file); |         String path = stripExtension(file); | ||||||
|         for (String s : new String[] {".srt", ".ass"}) { |         for (String subtitleExtension : new String[] {".srt", ".ass"}) { | ||||||
|             if (new File(directory + File.separator + path + s).exists()) { |             if (new File(directory + File.separator + path + subtitleExtension).exists()) { | ||||||
|                 return s; |                 return subtitleExtension; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         return ""; |         return ""; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user