Compare commits
No commits in common. "master" and "stream-reordering" have entirely different histories.
master
...
stream-reo
@ -3,7 +3,6 @@ package net.knarcraft.ffmpegconverter;
|
||||
import net.knarcraft.ffmpegconverter.config.Configuration;
|
||||
import net.knarcraft.ffmpegconverter.converter.AnimeConverter;
|
||||
import net.knarcraft.ffmpegconverter.converter.AudioConverter;
|
||||
import net.knarcraft.ffmpegconverter.converter.AudioExtractor;
|
||||
import net.knarcraft.ffmpegconverter.converter.AudioToVorbisConverter;
|
||||
import net.knarcraft.ffmpegconverter.converter.Converter;
|
||||
import net.knarcraft.ffmpegconverter.converter.DownScaleConverter;
|
||||
@ -101,14 +100,13 @@ public class FFMpegConvert {
|
||||
10. Anime to h265 all streams
|
||||
11. Stream reorder
|
||||
12. Letterbox cropper
|
||||
13. Video's Audio to vorbis converter
|
||||
14. Audio from video extractor""", 1, 14, Integer.MIN_VALUE);
|
||||
13. Video's Audio to vorbis converter""", 1, 13);
|
||||
|
||||
return switch (choice) {
|
||||
case 1 -> generateWebAnimeConverter();
|
||||
case 2 -> new AudioConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output audio extension>", null));
|
||||
case 3 -> new VideoConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output video extension>", null));
|
||||
case 4 -> new WebVideoConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output video extension>", null));
|
||||
case 2 -> new AudioConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output extension>"));
|
||||
case 3 -> new VideoConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output extension>"));
|
||||
case 4 -> new WebVideoConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output extension>"));
|
||||
case 5 -> new MkvH264Converter(FFPROBE_PATH, FFMPEG_PATH);
|
||||
case 6 -> new MkvH265ReducedConverter(FFPROBE_PATH, FFMPEG_PATH);
|
||||
case 7 -> generateMKVToMP4Transcoder();
|
||||
@ -118,8 +116,6 @@ public class FFMpegConvert {
|
||||
case 11 -> generateStreamOrderConverter();
|
||||
case 12 -> new LetterboxCropper(FFPROBE_PATH, FFMPEG_PATH);
|
||||
case 13 -> new AudioToVorbisConverter(FFPROBE_PATH, FFMPEG_PATH);
|
||||
case 14 -> new AudioExtractor(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output audio extension>",
|
||||
"mp3"), getChoice("<stream to extract>", 0, 1000, 0));
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
@ -186,9 +182,9 @@ public class FFMpegConvert {
|
||||
private static Converter generateMKVToMP4Transcoder() {
|
||||
OutputUtil.println("[Audio stream index 0-n] [Subtitle stream index 0-n] [Video stream index 0-n]\nYour input: ");
|
||||
List<String> input = readInput(3);
|
||||
int audioStreamIndex = 0;
|
||||
int subtitleStreamIndex = 0;
|
||||
int videoStreamIndex = 0;
|
||||
int audioStreamIndex = -1;
|
||||
int subtitleStreamIndex = -1;
|
||||
int videoStreamIndex = -1;
|
||||
|
||||
try {
|
||||
if (!input.isEmpty()) {
|
||||
@ -335,20 +331,16 @@ public class FFMpegConvert {
|
||||
/**
|
||||
* Gets the user's choice
|
||||
*
|
||||
* @param prompt <p>The prompt shown to the user.</p>
|
||||
* @param defaultValue <p>The default value, if no input is given</p>
|
||||
* @param prompt <p>The prompt shown to the user.</p>
|
||||
* @return <p>The non-empty choice given by the user.</p>
|
||||
*/
|
||||
@NotNull
|
||||
private static String getChoice(@NotNull String prompt, @Nullable Object defaultValue) {
|
||||
private static String getChoice(@NotNull String prompt) {
|
||||
OutputUtil.println(prompt);
|
||||
String choice = "";
|
||||
while (choice.isEmpty()) {
|
||||
OutputUtil.println("Your input: ");
|
||||
choice = READER.nextLine();
|
||||
if (choice.isEmpty() && defaultValue != null) {
|
||||
return String.valueOf(defaultValue);
|
||||
}
|
||||
}
|
||||
return choice;
|
||||
}
|
||||
@ -361,7 +353,7 @@ public class FFMpegConvert {
|
||||
* @param max The maximum allowed value
|
||||
* @return The value given by the user
|
||||
*/
|
||||
private static int getChoice(@NotNull String prompt, int min, int max, int defaultValue) {
|
||||
private static int getChoice(@NotNull String prompt, int min, int max) {
|
||||
OutputUtil.println(prompt);
|
||||
int choice = Integer.MIN_VALUE;
|
||||
do {
|
||||
@ -369,9 +361,6 @@ public class FFMpegConvert {
|
||||
try {
|
||||
choice = Integer.parseInt(READER.next());
|
||||
} catch (NumberFormatException e) {
|
||||
if (defaultValue != Integer.MIN_VALUE) {
|
||||
return defaultValue;
|
||||
}
|
||||
OutputUtil.println("Invalid choice. Please try again.");
|
||||
} finally {
|
||||
READER.nextLine();
|
||||
|
@ -62,11 +62,6 @@ public class ConfigKey<T> {
|
||||
*/
|
||||
public static final ConfigKey<Boolean> COPY_ATTACHED_IMAGES = createKey("copy-attached-images", Boolean.class, false);
|
||||
|
||||
/**
|
||||
* The configuration key for whether to overwrite the original file when converting
|
||||
*/
|
||||
public static final ConfigKey<Boolean> OVERWRITE_FILES = createKey("overwrite-files", Boolean.class, false);
|
||||
|
||||
private final String configKey;
|
||||
private final T defaultValue;
|
||||
private final Class<T> type;
|
||||
|
@ -23,7 +23,6 @@ public class Configuration {
|
||||
private MinimalSubtitlePreference minimalSubtitlePreference;
|
||||
private boolean deInterlaceVideo;
|
||||
private boolean copyAttachedImages;
|
||||
private boolean overwriteFiles;
|
||||
|
||||
/**
|
||||
* Instantiates and loads a new configuration
|
||||
@ -58,7 +57,6 @@ public class Configuration {
|
||||
animeSubtitleLanguages = ConfigHelper.asStringList(configHandler.getValue(ConfigKey.SUBTITLE_LANGUAGES_ANIME));
|
||||
deInterlaceVideo = ConfigHelper.asBoolean(configHandler.getValue(ConfigKey.DE_INTERLACE_VIDEO));
|
||||
copyAttachedImages = ConfigHelper.asBoolean(configHandler.getValue(ConfigKey.COPY_ATTACHED_IMAGES));
|
||||
overwriteFiles = ConfigHelper.asBoolean(configHandler.getValue(ConfigKey.OVERWRITE_FILES));
|
||||
try {
|
||||
minimalSubtitlePreference = MinimalSubtitlePreference.valueOf(String.valueOf(configHandler.getValue(
|
||||
ConfigKey.MINIMAL_SUBTITLE_PREFERENCE)));
|
||||
@ -173,13 +171,4 @@ public class Configuration {
|
||||
return this.copyAttachedImages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the original files should be overwritten after conversion has been finished
|
||||
*
|
||||
* @return <p>True if the original file should be overwritten</p>
|
||||
*/
|
||||
public boolean overwriteFiles() {
|
||||
return this.overwriteFiles;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ public abstract class AbstractConverter implements Converter {
|
||||
|
||||
@Override
|
||||
public void convert(@NotNull File file) throws IOException {
|
||||
StreamProbeResult probeResult = FFMpegHelper.probeFile(this.ffprobePath, file, this.subtitleFormats);
|
||||
StreamProbeResult probeResult = FFMpegHelper.probeFile(this.ffprobePath, file, this.subtitleFormats,
|
||||
this.audioFormats);
|
||||
if (probeResult.parsedStreams().isEmpty()) {
|
||||
throw new IllegalArgumentException("The file has no valid streams. Please make sure the file exists and" +
|
||||
" is not corrupt.");
|
||||
@ -85,16 +86,6 @@ public abstract class AbstractConverter implements Converter {
|
||||
int exitCode = FFMpegHelper.runProcess(processBuilder, file.getParentFile(), "\n", true).exitCode();
|
||||
if (exitCode != 0) {
|
||||
handleError(ffMpegCommand, file, newPath);
|
||||
} else if (FFMpegConvert.getConfiguration().overwriteFiles() &&
|
||||
FileUtil.getExtension(newPath).equalsIgnoreCase(FileUtil.getExtension(file.getPath()))) {
|
||||
File outputFile = new File(newPath);
|
||||
if (!file.delete()) {
|
||||
OutputUtil.println("Unable to remove original file.");
|
||||
System.exit(1);
|
||||
} else if (!outputFile.renameTo(file)) {
|
||||
OutputUtil.println("Failed to re-name file after conversion!");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,65 +0,0 @@
|
||||
package net.knarcraft.ffmpegconverter.converter;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.container.FFMpegCommand;
|
||||
import net.knarcraft.ffmpegconverter.container.StreamProbeResult;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.ConverterModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.DebugModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.ModuleExecutor;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.mapping.NthAudioStreamModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.SetOutputFileModule;
|
||||
import net.knarcraft.ffmpegconverter.utility.FFMpegHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An extractor for getting audio streams from video files
|
||||
*/
|
||||
public class AudioExtractor extends AbstractConverter {
|
||||
|
||||
private final int streamToExtract;
|
||||
|
||||
/**
|
||||
* Instantiates a new audio extractor
|
||||
*
|
||||
* @param ffprobePath <p>Path/command to ffprobe.</p>
|
||||
* @param ffmpegPath <p>Path/command to ffmpeg.</p>
|
||||
* @param newExtension <p>The extension of the new file.</p>
|
||||
* @param streamToExtract <p>The stream to be extracted from the video file</p>
|
||||
*/
|
||||
public AudioExtractor(@NotNull String ffprobePath, @NotNull String ffmpegPath, @NotNull String newExtension,
|
||||
int streamToExtract) {
|
||||
super(newExtension);
|
||||
this.ffprobePath = ffprobePath;
|
||||
this.ffmpegPath = ffmpegPath;
|
||||
this.streamToExtract = Math.max(0, streamToExtract);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult,
|
||||
@NotNull String outFile) {
|
||||
FFMpegCommand command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, probeResult.parsedFiles());
|
||||
List<ConverterModule> modules = new ArrayList<>();
|
||||
|
||||
if (this.debug) {
|
||||
modules.add(new DebugModule());
|
||||
}
|
||||
|
||||
//Gets the first audio stream from the file and adds it to the output file
|
||||
modules.add(new NthAudioStreamModule(probeResult.getAudioStreams(), streamToExtract));
|
||||
modules.add(new SetOutputFileModule(outFile));
|
||||
|
||||
new ModuleExecutor(command, modules).execute();
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<String> getValidFormats() {
|
||||
return videoFormats;
|
||||
}
|
||||
|
||||
}
|
@ -27,13 +27,6 @@ public enum StreamType {
|
||||
*/
|
||||
COVER_IMAGE,
|
||||
|
||||
/**
|
||||
* Binary data
|
||||
*
|
||||
* <p>Binary data streams only cause problems, as they cannot, for example, be included in an MKV file.</p>
|
||||
*/
|
||||
DATA,
|
||||
|
||||
/**
|
||||
* None of the above
|
||||
*/
|
||||
|
@ -41,13 +41,15 @@ public final class FFMpegHelper {
|
||||
* @param ffprobePath <p>The path/command to ffprobe</p>
|
||||
* @param file <p>The file to probe</p>
|
||||
* @param subtitleFormats <p>The extensions to accept for external subtitles</p>
|
||||
* @param audioFormats <p>The extensions to accept for external audio files</p>
|
||||
* @return <p>A list of StreamObjects</p>
|
||||
* @throws IOException <p>If the process can't be readProcess</p>
|
||||
*/
|
||||
@NotNull
|
||||
public static StreamProbeResult probeFile(@NotNull String ffprobePath, @NotNull File file,
|
||||
@NotNull List<String> subtitleFormats) throws IOException {
|
||||
return parseStreams(ffprobePath, probeForStreams(ffprobePath, file), file, subtitleFormats);
|
||||
@NotNull List<String> subtitleFormats,
|
||||
@NotNull List<String> audioFormats) throws IOException {
|
||||
return parseStreams(ffprobePath, probeForStreams(ffprobePath, file), file, subtitleFormats, audioFormats);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -275,14 +277,17 @@ public final class FFMpegHelper {
|
||||
* @param streams <p>A list of all streams for the current file.</p>
|
||||
* @param file <p>The file currently being converted.</p>
|
||||
* @param subtitleFormats <p>The extensions to accept for external subtitles</p>
|
||||
* @param audioFormats <p>The extensions to accept for external audio tracks</p>
|
||||
* @return <p>A list of StreamObjects.</p>
|
||||
*/
|
||||
@NotNull
|
||||
private static StreamProbeResult parseStreams(@NotNull String ffprobePath, @NotNull List<String> streams,
|
||||
@NotNull File file, @NotNull List<String> subtitleFormats) throws IOException {
|
||||
@NotNull File file, @NotNull List<String> subtitleFormats,
|
||||
@NotNull List<String> audioFormats) throws IOException {
|
||||
StreamProbeResult probeResult = new StreamProbeResult(new ArrayList<>(List.of(file)),
|
||||
parseStreamObjects(streams));
|
||||
getExternalStreams(probeResult, ffprobePath, file.getParentFile(), file.getName(), subtitleFormats);
|
||||
getExternalStreams(probeResult, ffprobePath, file.getParentFile(), file.getName(), audioFormats);
|
||||
return probeResult;
|
||||
}
|
||||
|
||||
@ -314,8 +319,6 @@ public final class FFMpegHelper {
|
||||
parsedStreams.add(new OtherStream(streamInfo, 0, true));
|
||||
}
|
||||
}
|
||||
case DATA -> OutputUtil.print("A binary stream was found. Those are ignored they will generally " +
|
||||
"cause the conversion to fail.");
|
||||
}
|
||||
}
|
||||
return parsedStreams;
|
||||
@ -344,8 +347,6 @@ public final class FFMpegHelper {
|
||||
return StreamType.AUDIO;
|
||||
case "subtitle":
|
||||
return StreamType.SUBTITLE;
|
||||
case "data":
|
||||
return StreamType.DATA;
|
||||
default:
|
||||
return StreamType.OTHER;
|
||||
}
|
||||
|
@ -37,5 +37,4 @@ wma
|
||||
wv
|
||||
webm
|
||||
8svx
|
||||
mka
|
||||
ac3
|
||||
mka
|
@ -19,6 +19,4 @@ minimal-subtitle-preference=AVOID
|
||||
# The preference for whether video streams should be de-interlaced. It is recommended to only enable this when you notice that a video file is interlaced.
|
||||
de-interlace-video=false
|
||||
# Whether to copy attached cover images. FFMpeg sometimes throws errors when including attached images.
|
||||
copy-attached-images=false
|
||||
# Whether to overwrite original files after conversion. Note that if enabled, the original files are lost, which is troublesome if the conversion arguments are incorrect.
|
||||
overwrite-files=false
|
||||
copy-attached-images=false
|
Loading…
Reference in New Issue
Block a user