Adds missing files
This commit is contained in:
parent
0b61ba6bef
commit
f9f52e4425
11
.idea/FFmpegConvert1.iml
Normal file
11
.idea/FFmpegConvert1.iml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
7
.idea/misc.xml
Normal file
7
.idea/misc.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
|
||||
</project>
|
@ -2,7 +2,7 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/FFmpegConvert.iml" filepath="$PROJECT_DIR$/FFmpegConvert.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/FFmpegConvert1.iml" filepath="$PROJECT_DIR$/.idea/FFmpegConvert1.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
10
misc.xml
10
misc.xml
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavadocGenerationManager">
|
||||
<option name="OUTPUT_DIRECTORY" value="$USER_HOME$/Desktop/FFmpegConvert" />
|
||||
<option name="OPTION_SCOPE" value="private" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
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);
|
||||
}
|
||||
}
|
124
uiDesigner.xml
124
uiDesigner.xml
@ -1,124 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
Loading…
Reference in New Issue
Block a user