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(),
 | 
			
		||||
@@ -117,16 +141,13 @@ public class AnimeConverter extends Converter {
 | 
			
		||||
            command.add(String.format("0:%d", videoStream.getAbsoluteIndex()));
 | 
			
		||||
            command.add("-vf");
 | 
			
		||||
            String safeFileName = escapeSpecialCharactersInFileName(fileName);
 | 
			
		||||
            String subtitleCommand = String.format("subtitles='%s':si=%d", safeFileName, 
 | 
			
		||||
            String subtitleCommand = String.format("subtitles='%s':si=%d", safeFileName,
 | 
			
		||||
                    subtitleStream.getRelativeIndex());
 | 
			
		||||
            command.add(subtitleCommand);
 | 
			
		||||
        } else {
 | 
			
		||||
            command.add("-map");
 | 
			
		||||
            command.add(String.format("0:%d", videoStream.getAbsoluteIndex()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        command.add(outFile);
 | 
			
		||||
        return command.toArray(new String[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -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(",", "\\\\\\,")
 | 
			
		||||
@@ -431,18 +446,36 @@ 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 {
 | 
			
		||||
        if (!input.equals("")) {
 | 
			
		||||
            writer.write(input);
 | 
			
		||||
            writer.flush();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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,56 +65,90 @@ public class VideoConverter extends Converter {
 | 
			
		||||
            audioStream = audioStreams.get(0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String ext = hasExternalSubtitle(folder.getAbsolutePath(), fileName);
 | 
			
		||||
        String ext2 = hasExternalImageSubtitle(folder.getAbsolutePath(), fileName);
 | 
			
		||||
        if (!ext.equals("")) {
 | 
			
		||||
            command.add("-vf");
 | 
			
		||||
            command.add("subtitles=" + stripExtension(fileName) + ext);
 | 
			
		||||
        } else if (!ext2.equals("")) {
 | 
			
		||||
            command.add("-i");
 | 
			
		||||
            command.add(stripExtension(fileName) + ext2);
 | 
			
		||||
            if (this.debug) {
 | 
			
		||||
                addDebug(command, 50, 120);
 | 
			
		||||
            }
 | 
			
		||||
            //TODO: Scale subtitles to video
 | 
			
		||||
            command.add("-filter_complex");
 | 
			
		||||
            command.add("[1:s]scale=width=1920:height=800,crop=w=1920:h=800:x=0:y=out_h[sub];[" + videoStream + ":v][sub]overlay");
 | 
			
		||||
            command.add("-profile:v");
 | 
			
		||||
            command.add("baseline");
 | 
			
		||||
        boolean videoAdded = addSubtitles(command, folder, fileName, videoStream);
 | 
			
		||||
 | 
			
		||||
        if (!videoAdded && videoStreams.size() > 0) {
 | 
			
		||||
            command.add("-map");
 | 
			
		||||
            command.add("0:" + videoStream);
 | 
			
		||||
        }
 | 
			
		||||
        if (audioStreams.size() > 0) {
 | 
			
		||||
            command.add("-map");
 | 
			
		||||
            command.add("0:" + audioStream);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ext2.equals("") || !ext.equals("")) {
 | 
			
		||||
            if (videoStreams.size() > 0) {
 | 
			
		||||
                command.add("-map");
 | 
			
		||||
                command.add("0:" + videoStream);
 | 
			
		||||
            }
 | 
			
		||||
            if (audioStreams.size() > 0) {
 | 
			
		||||
                command.add("-map");
 | 
			
		||||
                command.add("0:" + audioStream);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        command.add("-af");
 | 
			
		||||
        command.add("pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL|FR < 1.0*FR + 0.707*FC + 0.707*BR");
 | 
			
		||||
        convertToStereo(command);
 | 
			
		||||
 | 
			
		||||
        command.add(outFile);
 | 
			
		||||
        return command.toArray(new String[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds subtitles to a command
 | 
			
		||||
     * @param command <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