Adds missing files
This commit is contained in:
		
							
								
								
									
										190
									
								
								src/ffmpegconverter/Main.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								src/ffmpegconverter/Main.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
			
		||||
package ffmpegconverter;
 | 
			
		||||
 | 
			
		||||
import ffmpegconverter.converter.AnimeConverter;
 | 
			
		||||
import ffmpegconverter.converter.AudioConverter;
 | 
			
		||||
import ffmpegconverter.converter.Converter;
 | 
			
		||||
import ffmpegconverter.converter.VideoConverter;
 | 
			
		||||
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Scanner;
 | 
			
		||||
import java.util.function.Predicate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Converts a files or files in a folder to a web playable mp4.
 | 
			
		||||
 */
 | 
			
		||||
public class Main {
 | 
			
		||||
    private static final String FFPROBE_PATH = "ffprobe"; //Can be just ffprobe if it's in the path
 | 
			
		||||
    private static final String FFMPEG_PATH = "ffmpeg"; //Can be just ffmpeg if it's in the path
 | 
			
		||||
    private static Scanner in = new Scanner(System.in);
 | 
			
		||||
    private static Converter con = null;
 | 
			
		||||
 | 
			
		||||
    public static void main(String[] args) throws IOException {
 | 
			
		||||
        int choice = getChoice("Which converter do you want do use?\n1. Anime to web mp4\n2. Audio converter\n3. Video converter", 1, 3);
 | 
			
		||||
 | 
			
		||||
        System.out.println("Input for this converter:");
 | 
			
		||||
        switch (choice) {
 | 
			
		||||
            case 1:
 | 
			
		||||
                animeConverter();
 | 
			
		||||
                break;
 | 
			
		||||
            case 2:
 | 
			
		||||
                con = new AudioConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output extension>"));
 | 
			
		||||
                break;
 | 
			
		||||
            case 3:
 | 
			
		||||
                con = new VideoConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output extension>"));
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                System.exit(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int recursionSteps = 1;
 | 
			
		||||
 | 
			
		||||
        System.out.println("<Folder/File> [Recursions]");
 | 
			
		||||
        List<String> input = readInput(2);
 | 
			
		||||
        while (input.size() == 0) {
 | 
			
		||||
            System.out.print("File path required.");
 | 
			
		||||
            input = readInput(2);
 | 
			
		||||
        }
 | 
			
		||||
        File folder = new File(input.get(0));
 | 
			
		||||
        if (input.size() > 1) {
 | 
			
		||||
            try {
 | 
			
		||||
                recursionSteps = Integer.parseInt(input.get(1));
 | 
			
		||||
            } catch (NumberFormatException e) {
 | 
			
		||||
                System.out.println("Recursion steps is invalid and will be ignored.");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (folder.isDirectory()) {
 | 
			
		||||
            File[] files = listFilesRec(folder, con.getValidFormats(), recursionSteps);
 | 
			
		||||
            if (files != null && files.length > 0) {
 | 
			
		||||
                for (File file : files) {
 | 
			
		||||
                    con.convert(file);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                System.out.println("No valid files found in folder.");
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            con.convert(folder);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void animeConverter() {
 | 
			
		||||
        System.out.println("[Audio languages jap,eng,ger,fre] [Subtitle languages eng,ger,fre] [Convert to stareo if necessary true/false] [Prevent signs&songs subtitles true/false]\nYour input: ");
 | 
			
		||||
        List<String> input = readInput(4);
 | 
			
		||||
        String[] audioLang = new String[]{"jpn"};
 | 
			
		||||
        String[] subtitleLang = new String[]{"eng"};
 | 
			
		||||
        boolean toStereo = true;
 | 
			
		||||
        boolean preventSigns = true;
 | 
			
		||||
        if (input.size() > 0 && getList(input, 0) != null) {
 | 
			
		||||
            audioLang = getList(input, 0);
 | 
			
		||||
        }
 | 
			
		||||
        if (input.size() > 1 && getList(input, 1) != null) {
 | 
			
		||||
            subtitleLang = getList(input, 1);
 | 
			
		||||
        }
 | 
			
		||||
        if (input.size() > 2) {
 | 
			
		||||
            toStereo = Boolean.parseBoolean(input.get(2));
 | 
			
		||||
        }
 | 
			
		||||
        if (input.size() > 3) {
 | 
			
		||||
            preventSigns = Boolean.parseBoolean(input.get(3));
 | 
			
		||||
        }
 | 
			
		||||
        con = new AnimeConverter(FFPROBE_PATH, FFMPEG_PATH, audioLang, subtitleLang, toStereo, preventSigns);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String[] getList(List<String> list, int index) {
 | 
			
		||||
        String[] result = null;
 | 
			
		||||
        if (list.size() > index) {
 | 
			
		||||
            if (list.get(index).contains(",")) {
 | 
			
		||||
                result = list.get(index).split(",");
 | 
			
		||||
            } else {
 | 
			
		||||
                result = new String[]{list.get(index)};
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static List<String> readInput(int max) {
 | 
			
		||||
        List<String> input = new ArrayList<>();
 | 
			
		||||
        String rx = "[^\"\\s]+|\"(\\\\.|[^\\\\\"])*\"";
 | 
			
		||||
        for (int i = 0; i < max; i++) {
 | 
			
		||||
            String line = in.findInLine(rx);
 | 
			
		||||
            if (line != null) {
 | 
			
		||||
                if (line.startsWith("\"") && line.endsWith("\"")) {
 | 
			
		||||
                    input.add(line.substring(1, line.length() - 1));
 | 
			
		||||
                } else {
 | 
			
		||||
                    input.add(line);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        in.nextLine();
 | 
			
		||||
        return input;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String getChoice(String prompt) {
 | 
			
		||||
        System.out.println(prompt);
 | 
			
		||||
        String choice = "";
 | 
			
		||||
        while (choice.equals("")) {
 | 
			
		||||
            System.out.println("Your input: ");
 | 
			
		||||
            choice = in.nextLine();
 | 
			
		||||
        }
 | 
			
		||||
        return choice;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int getChoice(String prompt, int min, int max) {
 | 
			
		||||
        System.out.println(prompt);
 | 
			
		||||
        int choice = 0;
 | 
			
		||||
        while (choice < min || choice > max) {
 | 
			
		||||
            System.out.println("Your input: ");
 | 
			
		||||
            try {
 | 
			
		||||
                choice = Integer.parseInt(in.next());
 | 
			
		||||
            } catch (NumberFormatException e) {
 | 
			
		||||
                System.out.println("Invalid choice. Please try again.");
 | 
			
		||||
            } finally {
 | 
			
		||||
                in.nextLine();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return choice;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tests if any element in a list fulfills a condition.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list      The list to test against
 | 
			
		||||
     * @param predicate A predicate to use on every element in the list
 | 
			
		||||
     * @param <T>       Anything which can be stored in a list
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    private static <T> boolean listContains(T[] list, Predicate<T> predicate) {
 | 
			
		||||
        for (T item : list) {
 | 
			
		||||
            if (predicate.test(item)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Recursively lists all files in a folder
 | 
			
		||||
     *
 | 
			
		||||
     * @param folder    The folder to start from
 | 
			
		||||
     * @param maxRec    Maximum number of recursions
 | 
			
		||||
     * @return          A list of files
 | 
			
		||||
     */
 | 
			
		||||
    private static File[] listFilesRec(File folder, String[] extensions, int maxRec) {
 | 
			
		||||
        if (maxRec == 0) { return null; }
 | 
			
		||||
        File[] listOfFiles = folder.listFiles((file) -> file.isFile() && listContains(extensions, (item) -> file.getName().endsWith(item)));
 | 
			
		||||
        if (listOfFiles == null) { return null; }
 | 
			
		||||
        if (maxRec > 1) {
 | 
			
		||||
            File[] listOfFolders = folder.listFiles((dir, name) -> new File(dir, name).isDirectory());
 | 
			
		||||
            if (listOfFolders != null) {
 | 
			
		||||
                for (File file : listOfFolders) {
 | 
			
		||||
                    File[] nextLevel = listFilesRec(file, extensions, maxRec - 1);
 | 
			
		||||
                    if (nextLevel != null) {
 | 
			
		||||
                        listOfFiles = Converter.concatenate(listOfFiles, nextLevel);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return listOfFiles;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										126
									
								
								src/ffmpegconverter/converter/AnimeConverter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/ffmpegconverter/converter/AnimeConverter.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
package ffmpegconverter.converter;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class AnimeConverter extends Converter {
 | 
			
		||||
    private String[] audioLang;
 | 
			
		||||
    private String[] subtitleLang;
 | 
			
		||||
    private boolean toStereo;
 | 
			
		||||
    private boolean preventSignsAndSongs;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param ffprobePath           Path/command to ffprobe
 | 
			
		||||
     * @param ffmpegPath            Path/command to ffmpeg
 | 
			
		||||
     * @param audioLang             List of wanted audio languages in descending order
 | 
			
		||||
     * @param subtitleLang          List of wanted subtitle languages in descending order
 | 
			
		||||
     * @param toStereo              Convert video with several audio channels to stereo
 | 
			
		||||
     * @param preventSignsAndSongs  Prevent subtitles only converting signs and songs (not speech)
 | 
			
		||||
     */
 | 
			
		||||
    public AnimeConverter(String ffprobePath, String ffmpegPath, String[] audioLang, String[] subtitleLang, boolean toStereo, boolean preventSignsAndSongs) {
 | 
			
		||||
        this.ffprobePath = ffprobePath;
 | 
			
		||||
        this.ffmpegPath = ffmpegPath;
 | 
			
		||||
        this.audioLang = audioLang;
 | 
			
		||||
        this.subtitleLang = subtitleLang;
 | 
			
		||||
        this.toStereo = toStereo;
 | 
			
		||||
        this.preventSignsAndSongs = preventSignsAndSongs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
     */
 | 
			
		||||
    private void processFile(File folder, File file) throws IOException {
 | 
			
		||||
        String[] streams = probeFile(ffprobePath, file);
 | 
			
		||||
        if (streams.length == 0) {
 | 
			
		||||
            throw new IllegalArgumentException("The file has no streams");
 | 
			
		||||
        }
 | 
			
		||||
        String noExt = file.getName().substring(0, file.getName().lastIndexOf('.'));
 | 
			
		||||
        String newPath = noExt + ".mp4";
 | 
			
		||||
        boolean isMP4 = newPath.equals(file.getName());
 | 
			
		||||
        if (isMP4) {
 | 
			
		||||
            newPath = noExt + ".tmp.mp4";
 | 
			
		||||
        }
 | 
			
		||||
        convertProcess(new ProcessBuilder(builderCommand(ffmpegPath, file.getName(), streams, newPath)), folder);
 | 
			
		||||
        if (isMP4) {
 | 
			
		||||
            File oldFile = new File(newPath);
 | 
			
		||||
            if (!oldFile.renameTo(new File(noExt + ".mp4"))) {
 | 
			
		||||
                System.out.println("Failed to move converted file.");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generates a command for a ProcessBuilder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param executable    The executable file for ffmpeg
 | 
			
		||||
     * @param fileName      The input file
 | 
			
		||||
     * @param streams       A list of ffprobe streams
 | 
			
		||||
     * @param outFile       The output file
 | 
			
		||||
     * @return              A list of commands
 | 
			
		||||
     */
 | 
			
		||||
    private String[] builderCommand(String executable, String fileName, String[] streams, String outFile) {
 | 
			
		||||
        List<String> command = ffmpegWebVideo(executable, fileName);
 | 
			
		||||
        for (String lang : audioLang) {
 | 
			
		||||
            List<Integer> audioStreams = listAudio(streams, lang);
 | 
			
		||||
            if (audioStreams.size() > 0) {
 | 
			
		||||
                command.add("-map");
 | 
			
		||||
                command.add("0:" + audioStreams.get(0));
 | 
			
		||||
                String[] channels = stringBetween(streams[audioStreams.get(0)], "channels=", " ");
 | 
			
		||||
                if (toStereo && channels.length > 0 && Integer.parseInt(channels[0]) > 2) {
 | 
			
		||||
                    command.add("-af");
 | 
			
		||||
                    command.add("pan=stereo|FL=FC+0.30*FL+0.30*BL|FR=FC+0.30*FR+0.30*BR");
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            } else {
 | 
			
		||||
                audioStreams = listAudio(streams);
 | 
			
		||||
                if (audioStreams.size() > 0) {
 | 
			
		||||
                    command.add("-map");
 | 
			
		||||
                    command.add("0:" + audioStreams.get(0));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        addSubtitles(streams, listVideo(streams), command, fileName);
 | 
			
		||||
        command.add(outFile);
 | 
			
		||||
        return command.toArray(new String[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void addSubtitles(String[] streams, List<Integer> videoStreams, List<String> command, String fileName) {
 | 
			
		||||
        for (String lang : subtitleLang) {
 | 
			
		||||
            List<Integer> subtitleStreams = listSubtitles(streams, lang);
 | 
			
		||||
            if (subtitleStreams.size() > 0 && videoStreams.size() > 0 && isImageSub(streams, subtitleStreams.get(0))) {
 | 
			
		||||
                command.add("-filter_complex");
 | 
			
		||||
                command.add("[0:v:" + listVideo(streams).get(0) + "][0:" + subtitleStreams.get(0) + "]overlay");
 | 
			
		||||
                break;
 | 
			
		||||
            } else if (subtitleStreams.size() > 0) {
 | 
			
		||||
                if (videoStreams.size() > 0) {
 | 
			
		||||
                    command.add("-map");
 | 
			
		||||
                    command.add("0:" + listVideo(streams).get(0));
 | 
			
		||||
                }
 | 
			
		||||
                if (preventSignsAndSongs) {
 | 
			
		||||
                    subtitleStreams = listSubtitlesRelative(streams, lang, new String[]{"title=Signs"});
 | 
			
		||||
                } else {
 | 
			
		||||
                    subtitleStreams = listSubtitlesRelative(streams, lang);
 | 
			
		||||
                }
 | 
			
		||||
                if (subtitleStreams.size() > 0) {
 | 
			
		||||
                    command.add("-vf");
 | 
			
		||||
                    command.add("subtitles='" + fileName.replace("'", "\'") + "':si=" + subtitleStreams.get(0));
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String[] getValidFormats() {
 | 
			
		||||
        return VIDEO_FORMATS;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										180
									
								
								src/ffmpegconverter/converter/AudioConverter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								src/ffmpegconverter/converter/AudioConverter.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
			
		||||
package ffmpegconverter.converter;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.ListIterator;
 | 
			
		||||
 | 
			
		||||
public class AudioConverter extends Converter {
 | 
			
		||||
    private String newExt;
 | 
			
		||||
 | 
			
		||||
    public AudioConverter(String ffprobePath, String ffmpegPath, String newExt) {
 | 
			
		||||
        this.ffprobePath = ffprobePath;
 | 
			
		||||
        this.ffmpegPath = ffmpegPath;
 | 
			
		||||
        this.newExt = newExt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reads streams from a file, and converts it to an mp4.
 | 
			
		||||
     *
 | 
			
		||||
     * @param folder        The folder of the file to process
 | 
			
		||||
     * @param file          The file to process
 | 
			
		||||
     * @throws IOException  If the BufferedReader fails
 | 
			
		||||
     */
 | 
			
		||||
    private void processFile(File folder, File file, String newExt) throws IOException {
 | 
			
		||||
        String[] streams = probeFile(ffprobePath, file);
 | 
			
		||||
        if (streams.length == 0) {
 | 
			
		||||
            throw new IllegalArgumentException("The file has no streams");
 | 
			
		||||
        }
 | 
			
		||||
        String newPath = stripExtension(file) + "." + newExt;
 | 
			
		||||
        convertProcess(new ProcessBuilder(builderCommand(ffmpegPath, file.getName(), streams, newPath)), folder);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generates a command for a ProcessBuilder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param executable    The executable file for ffmpeg
 | 
			
		||||
     * @param fileName      The input file
 | 
			
		||||
     * @param streams       A list of ffprobe streams
 | 
			
		||||
     * @param outFile       The output file
 | 
			
		||||
     * @return              A list of commands
 | 
			
		||||
     */
 | 
			
		||||
    private String[] builderCommand(String executable, String fileName, String[] streams, String outFile) {
 | 
			
		||||
        List<String> command = generalFile(executable, fileName);
 | 
			
		||||
        List<Integer> audioStreams = listAudio(streams);
 | 
			
		||||
        if (audioStreams.size() > 0) {
 | 
			
		||||
            command.add("-map");
 | 
			
		||||
            command.add("0:" + audioStreams.get(0));
 | 
			
		||||
        }
 | 
			
		||||
        command.add(outFile);
 | 
			
		||||
        return command.toArray(new String[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String[] getValidFormats() {
 | 
			
		||||
        return AUDIO_FORMATS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void convert(File file) throws IOException {
 | 
			
		||||
        processFile(file.getParentFile(), file, newExt);
 | 
			
		||||
    }
 | 
			
		||||
    public class StringList implements List<String> {
 | 
			
		||||
        @Override
 | 
			
		||||
        public int size() {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean isEmpty() {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean contains(Object o) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public Iterator<String> iterator() {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public Object[] toArray() {
 | 
			
		||||
            return new Object[0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public <T> T[] toArray(T[] a) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean add(String string) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean remove(Object o) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean containsAll(Collection<?> c) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean addAll(Collection<? extends String> c) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean addAll(int index, Collection<? extends String> c) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean removeAll(Collection<?> c) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean retainAll(Collection<?> c) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void clear() {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public String get(int index) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public String set(int index, String element) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void add(int index, String element) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public String remove(int index) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public int indexOf(Object o) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public int lastIndexOf(Object o) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public ListIterator<String> listIterator() {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public ListIterator<String> listIterator(int index) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public List<String> subList(int fromIndex, int toIndex) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										302
									
								
								src/ffmpegconverter/converter/Converter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								src/ffmpegconverter/converter/Converter.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,302 @@
 | 
			
		||||
package ffmpegconverter.converter;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedReader;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStreamReader;
 | 
			
		||||
import java.lang.reflect.Array;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.function.Predicate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implements all methods which can be usefull for any implementation of a converter.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Converter {
 | 
			
		||||
    String ffprobePath;
 | 
			
		||||
    String ffmpegPath;
 | 
			
		||||
 | 
			
		||||
    public abstract String[] getValidFormats();
 | 
			
		||||
    public abstract void convert(File file) throws IOException;
 | 
			
		||||
 | 
			
		||||
    final String[] AUDIO_FORMATS = new String[] {".3gp", ".aa", ".aac", ".aax", ".act", ".aiff", ".amr", ".ape", ".au", ".awb", ".dct", ".dss", ".dvf", ".flac", ".gsm", ".iklax", ".ivs", ".m4a", ".m4b", ".m4p", ".mmf", ".mp3", ".mpc", ".msv", ".ogg", ".oga", ".mogg", ".opus", ".ra", ".rm", ".raw", ".sln", ".tta", ".vox", ".wav", ".wma", ".wv", ".webm", ".8svx"};
 | 
			
		||||
    final String[] VIDEO_FORMATS = new String[] {".avi", ".mpg", ".mpeg", ".mkv", ".wmv", ".flv", ".webm", ".3gp", ".rmvb", ".3gpp", ".mts", ".m4v", ".mov", ".rm", ".asf", ".mp4", ".vob", ".ogv", ".drc", ".qt", ".yuv", ".asm", ".m4p", ".mp2", ".mpe", ".mpv", ".m2v", ".svi", ".3g2", ".roq", ".nsv"};
 | 
			
		||||
 | 
			
		||||
    static String[] probeFile(String ffprobePath, File file) throws IOException {
 | 
			
		||||
        ProcessBuilder builderProbe = new ProcessBuilder(
 | 
			
		||||
                ffprobePath,
 | 
			
		||||
                "-v",
 | 
			
		||||
                "error",
 | 
			
		||||
                "-show_entries",
 | 
			
		||||
                "stream_tags=language,title:stream=index,codec_name,codec_type,channels",
 | 
			
		||||
                file.toString()
 | 
			
		||||
        );
 | 
			
		||||
        System.out.println(builderProbe.command());
 | 
			
		||||
        builderProbe.redirectErrorStream(true);
 | 
			
		||||
        Process processProbe = builderProbe.start();
 | 
			
		||||
        BufferedReader readerProbe = new BufferedReader(new InputStreamReader(processProbe.getInputStream()));
 | 
			
		||||
        StringBuilder output = new StringBuilder();
 | 
			
		||||
        while (processProbe.isAlive()) {
 | 
			
		||||
            String read = read(readerProbe, " ");
 | 
			
		||||
            if (!read.equals("")) {
 | 
			
		||||
                System.out.print(read);
 | 
			
		||||
                output.append(read);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return stringBetween(output.toString(), "[STREAM]", "[/STREAM]");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void convertProcess(ProcessBuilder process, File folder) throws IOException {
 | 
			
		||||
        System.out.println(process.command());
 | 
			
		||||
        process.directory(folder);
 | 
			
		||||
        process.redirectErrorStream(true);
 | 
			
		||||
        Process processConvert = process.start();
 | 
			
		||||
        BufferedReader readerConvert = new BufferedReader(new InputStreamReader(processConvert.getInputStream()));
 | 
			
		||||
        while (processConvert.isAlive()) {
 | 
			
		||||
            String read = read(readerConvert, "\n");
 | 
			
		||||
            if (!read.equals("")) {
 | 
			
		||||
                System.out.println(read);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reads from a process reader.
 | 
			
		||||
     *
 | 
			
		||||
     * @param reader        The reader of a process
 | 
			
		||||
     * @return              The output from the read
 | 
			
		||||
     * @throws IOException  On reader failure
 | 
			
		||||
     */
 | 
			
		||||
    private static String read(BufferedReader reader, String spacer) throws IOException {
 | 
			
		||||
        String line;
 | 
			
		||||
        StringBuilder text = new StringBuilder();
 | 
			
		||||
        while (reader.ready() && (line = reader.readLine()) != null && !line.equals("") && !line.equals("\n")) {
 | 
			
		||||
            text.append(line).append(spacer);
 | 
			
		||||
        }
 | 
			
		||||
        return text.toString().trim();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return  A base list of ffmpeg commands for converting a video for web
 | 
			
		||||
     */
 | 
			
		||||
    static List<String> ffmpegWebVideo(String executable, String fileName) {
 | 
			
		||||
        List<String> command = generalFile(executable, fileName);
 | 
			
		||||
        command.add("-vcodec");
 | 
			
		||||
        command.add("h264");
 | 
			
		||||
        command.add("-pix_fmt");
 | 
			
		||||
        command.add("yuv420p");
 | 
			
		||||
        command.add("-ar");
 | 
			
		||||
        command.add("48000");
 | 
			
		||||
        command.add("-movflags");
 | 
			
		||||
        command.add("+faststart");
 | 
			
		||||
        return command;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return  A base list of ffmpeg commands for converting a file
 | 
			
		||||
     */
 | 
			
		||||
    static List<String> generalFile(String executable, String fileName) {
 | 
			
		||||
        List<String> command = new ArrayList<>();
 | 
			
		||||
        command.add(executable);
 | 
			
		||||
        command.add("-nostdin");
 | 
			
		||||
        command.add("-i");
 | 
			
		||||
        command.add(fileName);
 | 
			
		||||
        return command;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks for the occurrence of otf attachments signifying the existence of image based subtitles.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list  A list of ffprobe indexes
 | 
			
		||||
     * @return      True if otf attachments were found.
 | 
			
		||||
     */
 | 
			
		||||
    static boolean isImageSub(String[] list, int index) {
 | 
			
		||||
        return list[index].contains("codec_name=hdmv_pgs_subtitle");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lists all video streams.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list  A list of ffprobe indexes
 | 
			
		||||
     * @return      An integer list containing just the wanted indexes
 | 
			
		||||
     */
 | 
			
		||||
    static List<Integer> listVideo(String[] list) {
 | 
			
		||||
        return listIndexes(list, (string) -> string.contains("codec_type=video"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lists all audio streams.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list  A list of ffprobe indexes
 | 
			
		||||
     * @return      An integer list containing just the wanted indexes
 | 
			
		||||
     */
 | 
			
		||||
    static List<Integer> listAudio(String[] list) {
 | 
			
		||||
        return listIndexes(list, (string) -> string.contains("codec_type=audio"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lists all audio streams of a certain language.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list  A list of ffprobe indexes
 | 
			
		||||
     * @param lang  The wanted language
 | 
			
		||||
     * @return      An integer list containing just the wanted indexes
 | 
			
		||||
     */
 | 
			
		||||
    static List<Integer> listAudio(String[] list, String lang) {
 | 
			
		||||
        return listIndexes(list, (string) -> string.contains("codec_type=audio") && string.contains("language=" + lang));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lists all subtitle streams of a certain language.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list  A list of ffprobe indexes
 | 
			
		||||
     * @param lang  The wanted language
 | 
			
		||||
     * @return      An integer list containing just the wanted indexes
 | 
			
		||||
     */
 | 
			
		||||
    static List<Integer> listSubtitles(String[] list, String lang) {
 | 
			
		||||
        return listIndexes(list, (string) -> string.contains("codec_type=subtitle") && string.contains("language=" + lang));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lists all subtitle indexes for video.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list  A list of ffprobe indexes
 | 
			
		||||
     * @return      An integer list containing just the wanted indexes
 | 
			
		||||
     */
 | 
			
		||||
    static List<Integer> listSubtitles(String[] list) {
 | 
			
		||||
        return listIndexes(list, (string) -> string.contains("codec_type=subtitle"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lists all subtitle streams of a certain language relatively to the number of subtitle streams.
 | 
			
		||||
     * 0-based.
 | 
			
		||||
     * Filters out all indexes containing anything in illegal
 | 
			
		||||
     *
 | 
			
		||||
     * @param list      A list of ffprobe indexes
 | 
			
		||||
     * @param lang      The wanted language
 | 
			
		||||
     * @param illegal   A list of strings not to allow (Songs & Signs for example)
 | 
			
		||||
     * @return          An integer list containing just the wanted indexes
 | 
			
		||||
     */
 | 
			
		||||
    static List<Integer> listSubtitlesRelative(String[] list, String lang, String[] illegal) {
 | 
			
		||||
        List<Integer> relative = listSubtitlesRelative(list, lang);
 | 
			
		||||
        List<Integer> subtitles = listSubtitles(list, lang);
 | 
			
		||||
        List<Integer> notWanted = new ArrayList<>();
 | 
			
		||||
        for (int i = 0; i < relative.size(); i++) {
 | 
			
		||||
            for (String ill : illegal) {
 | 
			
		||||
                if (list[subtitles.get(i)].contains(ill)) {
 | 
			
		||||
                    notWanted.add(i);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        relative.removeAll(notWanted);
 | 
			
		||||
        return relative;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lists all subtitle streams of a certain language relatively to the number of subtitle streams.
 | 
			
		||||
     * 0-based.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list  A list of ffprobe indexes
 | 
			
		||||
     * @param lang  The wanted language
 | 
			
		||||
     * @return      An integer list containing just the wanted indexes
 | 
			
		||||
     */
 | 
			
		||||
    static List<Integer> listSubtitlesRelative(String[] list, String lang) {
 | 
			
		||||
        list = subList(list, (string) -> string.contains("codec_type=subtitle"));
 | 
			
		||||
        List<Integer> wanted = new ArrayList<>();
 | 
			
		||||
        for (int i = 0; i < list.length; i++) {
 | 
			
		||||
            if (list[i].contains("language=" + lang)) {
 | 
			
		||||
                wanted.add(i);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return wanted;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lists all indexes fulfilling a predicate.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list  A list of ffprobe indexes
 | 
			
		||||
     * @return      An integer list containing just the wanted indexes
 | 
			
		||||
     */
 | 
			
		||||
    private static List<Integer> listIndexes(String[] list, Predicate<String> p) {
 | 
			
		||||
        List<Integer> indexes = new ArrayList<>();
 | 
			
		||||
        for (String str : list) {
 | 
			
		||||
            if (p.test(str)) {
 | 
			
		||||
                indexes.add(Integer.parseInt(stringBetweenSingle(str, "index=", " ")));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return indexes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns a new list of the string for which the predicate is true.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list  A list of ffprobe indexes
 | 
			
		||||
     * @param p     The predicate to test
 | 
			
		||||
     * @return      A sublist of the original list
 | 
			
		||||
     */
 | 
			
		||||
    private static String[] subList(String[] list, Predicate<String> p) {
 | 
			
		||||
        List<String> indexes = new ArrayList<>();
 | 
			
		||||
        for (String str : list) {
 | 
			
		||||
            if (p.test(str)) {
 | 
			
		||||
                indexes.add(str);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return indexes.toArray(new String[]{});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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
 | 
			
		||||
     */
 | 
			
		||||
    static String[] stringBetween(String string, String start, String end) {
 | 
			
		||||
        int startPos = string.indexOf(start) + start.length();
 | 
			
		||||
        if (!string.contains(start) || string.indexOf(end, startPos) < startPos) {
 | 
			
		||||
            return new String[]{};
 | 
			
		||||
        }
 | 
			
		||||
        int endPos = string.indexOf(end, startPos);
 | 
			
		||||
        String outString = string.substring(startPos, endPos).trim();
 | 
			
		||||
        String nextString = string.substring(endPos + end.length(), string.length());
 | 
			
		||||
        return concatenate(new String[]{outString}, stringBetween(nextString, start, end));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Finds a substring between two substrings in a string.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string The string containing the substrings
 | 
			
		||||
     * @param start  The substring before the wanted substring
 | 
			
		||||
     * @param end    The substring after the wanted substring
 | 
			
		||||
     * @return The wanted substring.
 | 
			
		||||
     */
 | 
			
		||||
    private static String stringBetweenSingle(String string, String start, String end) {
 | 
			
		||||
        int startPos = string.indexOf(start) + start.length();
 | 
			
		||||
        if (!string.contains(start) || string.indexOf(end, startPos) < startPos) {
 | 
			
		||||
            return "";
 | 
			
		||||
        }
 | 
			
		||||
        return string.substring(startPos, string.indexOf(end, startPos));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String stripExtension(File file) {
 | 
			
		||||
        return file.getName().substring(0, file.getName().lastIndexOf('.'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> T[] concatenate(T[] a, T[] b) {
 | 
			
		||||
        int aLen = a.length;
 | 
			
		||||
        int bLen = b.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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								src/ffmpegconverter/converter/VideoConverter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/ffmpegconverter/converter/VideoConverter.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
package ffmpegconverter.converter;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class VideoConverter extends Converter {
 | 
			
		||||
    private String newExt;
 | 
			
		||||
 | 
			
		||||
    public VideoConverter(String ffprobePath, String ffmpegPath, String newExt) {
 | 
			
		||||
        this.ffprobePath = ffprobePath;
 | 
			
		||||
        this.ffmpegPath = ffmpegPath;
 | 
			
		||||
        this.newExt = newExt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reads streams from a file, and converts it to an mp4.
 | 
			
		||||
     *
 | 
			
		||||
     * @param folder        The folder of the file to process
 | 
			
		||||
     * @param file          The file to process
 | 
			
		||||
     * @throws IOException  If the BufferedReader fails
 | 
			
		||||
     */
 | 
			
		||||
    private void processFile(File folder, File file, String newExt) throws IOException {
 | 
			
		||||
        String[] streams = probeFile(ffprobePath, file);
 | 
			
		||||
        if (streams.length == 0) {
 | 
			
		||||
            throw new IllegalArgumentException("The file has no streams");
 | 
			
		||||
        }
 | 
			
		||||
        String newPath = stripExtension(file) + Math.random() + "." + newExt;
 | 
			
		||||
        convertProcess(new ProcessBuilder(builderCommand(ffmpegPath, file.getName(), streams, newPath)), folder);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generates a command for a ProcessBuilder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param executable    The executable file for ffmpeg
 | 
			
		||||
     * @param fileName      The input file
 | 
			
		||||
     * @param streams       A list of ffprobe streams
 | 
			
		||||
     * @param outFile       The output file
 | 
			
		||||
     * @return              A list of commands
 | 
			
		||||
     */
 | 
			
		||||
    private String[] builderCommand(String executable, String fileName, String[] streams, String outFile) {
 | 
			
		||||
        List<String> command = generalFile(executable, fileName);
 | 
			
		||||
        List<Integer> videoStreams = listVideo(streams);
 | 
			
		||||
        if (videoStreams.size() > 0) {
 | 
			
		||||
            command.add("-map");
 | 
			
		||||
            command.add("0:" + videoStreams.get(0));
 | 
			
		||||
        }
 | 
			
		||||
        List<Integer> audioStreams = listAudio(streams);
 | 
			
		||||
        if (audioStreams.size() > 0) {
 | 
			
		||||
            command.add("-map");
 | 
			
		||||
            command.add("0:" + audioStreams.get(0));
 | 
			
		||||
        }
 | 
			
		||||
        command.add(outFile);
 | 
			
		||||
        return command.toArray(new String[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String[] getValidFormats() {
 | 
			
		||||
        return VIDEO_FORMATS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void convert(File file) throws IOException {
 | 
			
		||||
        processFile(file.getParentFile(), file, newExt);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user