diff --git a/.idea/FFmpegConvert1.iml b/.idea/FFmpegConvert1.iml
new file mode 100644
index 0000000..c90834f
--- /dev/null
+++ b/.idea/FFmpegConvert1.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..f091b54
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules.xml b/.idea/modules.xml
similarity index 52%
rename from modules.xml
rename to .idea/modules.xml
index 96cef43..20f6263 100644
--- a/modules.xml
+++ b/.idea/modules.xml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MANIFEST.MF b/META-INF/MANIFEST.MF
similarity index 94%
rename from MANIFEST.MF
rename to META-INF/MANIFEST.MF
index cf70b04..079a349 100644
--- a/MANIFEST.MF
+++ b/META-INF/MANIFEST.MF
@@ -1,3 +1,3 @@
-Manifest-Version: 1.0
-Main-Class: ffmpegconverter.Main
-
+Manifest-Version: 1.0
+Main-Class: ffmpegconverter.Main
+
diff --git a/misc.xml b/misc.xml
deleted file mode 100644
index bb91d91..0000000
--- a/misc.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/ffmpegconverter/Main.java b/src/ffmpegconverter/Main.java
new file mode 100644
index 0000000..9e7beb9
--- /dev/null
+++ b/src/ffmpegconverter/Main.java
@@ -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(""));
+ break;
+ case 3:
+ con = new VideoConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice(""));
+ break;
+ default:
+ System.exit(1);
+ }
+
+ int recursionSteps = 1;
+
+ System.out.println(" [Recursions]");
+ List 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 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 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 readInput(int max) {
+ List 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 Anything which can be stored in a list
+ * @return
+ */
+ private static boolean listContains(T[] list, Predicate 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;
+ }
+}
diff --git a/src/ffmpegconverter/converter/AnimeConverter.java b/src/ffmpegconverter/converter/AnimeConverter.java
new file mode 100644
index 0000000..1834de5
--- /dev/null
+++ b/src/ffmpegconverter/converter/AnimeConverter.java
@@ -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 command = ffmpegWebVideo(executable, fileName);
+ for (String lang : audioLang) {
+ List 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 videoStreams, List command, String fileName) {
+ for (String lang : subtitleLang) {
+ List 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;
+ }
+}
diff --git a/src/ffmpegconverter/converter/AudioConverter.java b/src/ffmpegconverter/converter/AudioConverter.java
new file mode 100644
index 0000000..d13e970
--- /dev/null
+++ b/src/ffmpegconverter/converter/AudioConverter.java
@@ -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 command = generalFile(executable, fileName);
+ List 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 {
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return false;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return null;
+ }
+
+ @Override
+ public Object[] toArray() {
+ return new Object[0];
+ }
+
+ @Override
+ public 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 listIterator() {
+ return null;
+ }
+
+ @Override
+ public ListIterator listIterator(int index) {
+ return null;
+ }
+
+ @Override
+ public List subList(int fromIndex, int toIndex) {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ffmpegconverter/converter/Converter.java b/src/ffmpegconverter/converter/Converter.java
new file mode 100644
index 0000000..1a7ffe8
--- /dev/null
+++ b/src/ffmpegconverter/converter/Converter.java
@@ -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 ffmpegWebVideo(String executable, String fileName) {
+ List 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 generalFile(String executable, String fileName) {
+ List 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 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 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 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 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 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 listSubtitlesRelative(String[] list, String lang, String[] illegal) {
+ List relative = listSubtitlesRelative(list, lang);
+ List subtitles = listSubtitles(list, lang);
+ List 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 listSubtitlesRelative(String[] list, String lang) {
+ list = subList(list, (string) -> string.contains("codec_type=subtitle"));
+ List 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 listIndexes(String[] list, Predicate p) {
+ List 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 p) {
+ List 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 Any type
+ * @return A new array containing all elements from the two arrays
+ */
+ public static T[] concatenate(T[] a, T[] b) {
+ int aLen = a.length;
+ int bLen = b.length;
+ @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;
+ }
+}
diff --git a/src/ffmpegconverter/converter/VideoConverter.java b/src/ffmpegconverter/converter/VideoConverter.java
new file mode 100644
index 0000000..54bd95d
--- /dev/null
+++ b/src/ffmpegconverter/converter/VideoConverter.java
@@ -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 command = generalFile(executable, fileName);
+ List videoStreams = listVideo(streams);
+ if (videoStreams.size() > 0) {
+ command.add("-map");
+ command.add("0:" + videoStreams.get(0));
+ }
+ List 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);
+ }
+}
\ No newline at end of file
diff --git a/uiDesigner.xml b/uiDesigner.xml
deleted file mode 100644
index e96534f..0000000
--- a/uiDesigner.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
-
\ No newline at end of file