367 lines
14 KiB
Java
367 lines
14 KiB
Java
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 String fileCollisionPrevention(String targetPath, String extension) {
|
|
File file = new File(targetPath);
|
|
int i = 1;
|
|
while (file.exists()) {
|
|
file = new File(stripExtension(file) + "(" + i + ")" + "." + extension);
|
|
}
|
|
return file.toString();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void addDebug(List<String> command, int start, int length) {
|
|
command.add("-ss");
|
|
command.add("" + start);
|
|
command.add("-t");
|
|
command.add("" + length);
|
|
}
|
|
|
|
//TODO: Create a new object for subtitle containing language, relative index, absolute index and codec. Also signs & songs.
|
|
|
|
/**
|
|
* 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");
|
|
//Filename .sup
|
|
}
|
|
|
|
/**
|
|
* 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 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> listSubtitles(String[] list, String lang, String[] illegal) {
|
|
List<Integer> subtitles = listIndexes(list, (string) -> string.contains("codec_type=subtitle")
|
|
&& string.contains("language=" + lang));
|
|
List<Integer> notWanted = listIndexes(list, (string) -> {
|
|
for (String s : illegal) {
|
|
if (string.contains(s)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
subtitles.removeAll(notWanted);
|
|
return subtitles;
|
|
}
|
|
|
|
/**
|
|
* 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 subtitle streams relatively to the number of subtitle streams.
|
|
* 0-based.
|
|
*
|
|
* @param list A list of ffprobe indexes
|
|
* @return An integer list containing just the wanted indexes
|
|
*/
|
|
static List<Integer> listSubtitlesRelative(String[] list) {
|
|
list = subList(list, (string) -> string.contains("codec_type=subtitle"));
|
|
List<Integer> wanted = new ArrayList<>();
|
|
for (int i = 0; i < list.length; i++) {
|
|
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());
|
|
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('.'));
|
|
}
|
|
|
|
static String stripExtension(String file) {
|
|
return file.substring(0, file.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;
|
|
}
|
|
}
|