Fixes various issues
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good
Makes it possible to turn off all types of hardware acceleration for all converters Only encodes anime to hevc if not already hevc Adds an option to force hevc to hevc encoding, for the anime converter Adds an option to force encoding of audio, for the anime converter Fixes a bug causing the codec name to not be parsed Fixes an exception when trying to sort an empty list Fixes order of sorting in the anime converter Adds another signs songs filter
This commit is contained in:
parent
d487df0e78
commit
3c9fa55585
@ -1,6 +1,7 @@
|
||||
package net.knarcraft.ffmpegconverter;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.config.ConfigHandler;
|
||||
import net.knarcraft.ffmpegconverter.config.ConfigKey;
|
||||
import net.knarcraft.ffmpegconverter.converter.AnimeConverter;
|
||||
import net.knarcraft.ffmpegconverter.converter.AudioConverter;
|
||||
import net.knarcraft.ffmpegconverter.converter.Converter;
|
||||
@ -39,13 +40,18 @@ public class FFMpegConvert {
|
||||
private static Converter converter = null;
|
||||
private static final ConfigHandler configHandler = new ConfigHandler();
|
||||
private static boolean debug = false;
|
||||
private static boolean useHardwareAcceleration = false;
|
||||
|
||||
public static void main(@NotNull String[] arguments) throws IOException {
|
||||
Configuration configuration = configHandler.load();
|
||||
if (configuration.containsKey("debug")) {
|
||||
debug = configuration.getBoolean("debug");
|
||||
if (configuration.containsKey(ConfigKey.DEBUG.toString())) {
|
||||
debug = configuration.getBoolean(ConfigKey.DEBUG.toString());
|
||||
}
|
||||
OutputUtil.setDebug(debug);
|
||||
if (configuration.containsKey(ConfigKey.USE_HARDWARE_ACCELERATION.toString())) {
|
||||
useHardwareAcceleration = configuration.getBoolean(ConfigKey.USE_HARDWARE_ACCELERATION.toString());
|
||||
}
|
||||
|
||||
converter = loadConverter();
|
||||
|
||||
if (converter == null) {
|
||||
@ -92,6 +98,15 @@ public class FFMpegConvert {
|
||||
return debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether hardware accelerated encoding is enabled
|
||||
*
|
||||
* @return <p>True if hardware accelerated encoding is enabled</p>
|
||||
*/
|
||||
public static boolean useHardwareAcceleration() {
|
||||
return useHardwareAcceleration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the user which converter they want, and assigns a converter instance to the converter variable
|
||||
*/
|
||||
@ -217,14 +232,17 @@ public class FFMpegConvert {
|
||||
private static Converter generateAnimeConverter() {
|
||||
OutputUtil.println("[Audio languages jpn,eng,ger,fre] [Subtitle languages eng,ger,fre] [Minimal subtitle " +
|
||||
"preference REQUIRE/PREFER/NO_PREFERENCE/AVOID/REJECT] [Forced audio index 0-n] " +
|
||||
"[Forced subtitle index 0-n] [Subtitle name filter]\nYour input: ");
|
||||
List<String> input = readInput(7);
|
||||
String[] audioLanguage = new String[]{"jpn", "eng", "0"};
|
||||
"[Forced subtitle index 0-n] [Force video encoding true/false] [Force audio encoding true/false] " +
|
||||
"[Subtitle name filter]\nYour input: ");
|
||||
List<String> input = readInput(8);
|
||||
String[] audioLanguage = new String[]{"jpn", "nor", "eng", "0"};
|
||||
String[] subtitleLanguage = new String[]{"nob", "nor", "eng", "jpn", "0"};
|
||||
MinimalSubtitlePreference subtitlePreference = MinimalSubtitlePreference.AVOID;
|
||||
int forcedAudioIndex = 0;
|
||||
int forcedSubtitleIndex = 0;
|
||||
String subtitleNameFilter = "";
|
||||
boolean forceVideoEncoding = false;
|
||||
boolean forceAudioEncoding = false;
|
||||
|
||||
if (!input.isEmpty()) {
|
||||
audioLanguage = ListUtil.getListFromCommaSeparatedString(input.get(0));
|
||||
@ -232,25 +250,31 @@ public class FFMpegConvert {
|
||||
if (input.size() > 1) {
|
||||
subtitleLanguage = ListUtil.getListFromCommaSeparatedString(input.get(1));
|
||||
}
|
||||
if (input.size() > 3) {
|
||||
subtitlePreference = MinimalSubtitlePreference.valueOf(input.get(3).toUpperCase());
|
||||
if (input.size() > 2) {
|
||||
subtitlePreference = MinimalSubtitlePreference.valueOf(input.get(2).toUpperCase());
|
||||
}
|
||||
try {
|
||||
if (input.size() > 4) {
|
||||
forcedAudioIndex = Integer.parseInt(input.get(4));
|
||||
if (input.size() > 3) {
|
||||
forcedAudioIndex = Integer.parseInt(input.get(3));
|
||||
}
|
||||
if (input.size() > 5) {
|
||||
forcedSubtitleIndex = Integer.parseInt(input.get(5));
|
||||
if (input.size() > 4) {
|
||||
forcedSubtitleIndex = Integer.parseInt(input.get(4));
|
||||
}
|
||||
} catch (NumberFormatException exception) {
|
||||
OutputUtil.println("Forced audio or subtitle index is not a number");
|
||||
return null;
|
||||
}
|
||||
if (input.size() > 5) {
|
||||
forceVideoEncoding = Boolean.parseBoolean(input.get(5));
|
||||
}
|
||||
if (input.size() > 6) {
|
||||
subtitleNameFilter = input.get(6);
|
||||
forceAudioEncoding = Boolean.parseBoolean(input.get(6));
|
||||
}
|
||||
if (input.size() > 7) {
|
||||
subtitleNameFilter = input.get(7);
|
||||
}
|
||||
return new AnimeConverter(FFPROBE_PATH, FFMPEG_PATH, audioLanguage, subtitleLanguage, subtitlePreference,
|
||||
forcedAudioIndex, forcedSubtitleIndex, subtitleNameFilter);
|
||||
forcedAudioIndex, forcedSubtitleIndex, subtitleNameFilter, forceVideoEncoding, forceAudioEncoding);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,37 @@
|
||||
package net.knarcraft.ffmpegconverter.config;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A representation of all configuration keys
|
||||
*/
|
||||
public enum ConfigKey {
|
||||
|
||||
/**
|
||||
* The configuration key for the list of hardware-accelerated encoders available on the system
|
||||
*/
|
||||
HARDWARE_ACCELERATED_ENCODERS("encoders-hardware-accelerated"),
|
||||
|
||||
/**
|
||||
* The configuration key for toggling debug mode
|
||||
*/
|
||||
DEBUG("debug"),
|
||||
|
||||
/**
|
||||
* The configuration key for toggling hardware acceleration
|
||||
*/
|
||||
USE_HARDWARE_ACCELERATION("hardware-acceleration"),
|
||||
;
|
||||
|
||||
private final String configKey;
|
||||
|
||||
ConfigKey(@NotNull String configKey) {
|
||||
this.configKey = configKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.configKey;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package net.knarcraft.ffmpegconverter.converter;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.FFMpegConvert;
|
||||
import net.knarcraft.ffmpegconverter.container.FFMpegCommand;
|
||||
import net.knarcraft.ffmpegconverter.container.StreamProbeResult;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.ConverterModule;
|
||||
@ -11,6 +12,7 @@ import net.knarcraft.ffmpegconverter.converter.module.mapping.MapAllModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.mapping.SetDefaultStreamModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.CopyAudioModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.CopySubtitlesModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.CopyVideoModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.FastStartModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.SetOutputFileModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.SetQualityModule;
|
||||
@ -24,6 +26,7 @@ import net.knarcraft.ffmpegconverter.converter.sorter.SubtitleTitleSorter;
|
||||
import net.knarcraft.ffmpegconverter.property.MinimalSubtitlePreference;
|
||||
import net.knarcraft.ffmpegconverter.streams.AudioStream;
|
||||
import net.knarcraft.ffmpegconverter.streams.SubtitleStream;
|
||||
import net.knarcraft.ffmpegconverter.streams.VideoStream;
|
||||
import net.knarcraft.ffmpegconverter.utility.FFMpegHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -42,6 +45,8 @@ public class AnimeConverter extends AbstractConverter {
|
||||
private final int forcedAudioIndex;
|
||||
private final int forcedSubtitleIndex;
|
||||
private final String subtitleNameFilter;
|
||||
private final boolean forceVideoEncoding;
|
||||
private final boolean forceAudioEncoding;
|
||||
|
||||
/**
|
||||
* Instantiates a new anime converter
|
||||
@ -51,13 +56,16 @@ public class AnimeConverter extends AbstractConverter {
|
||||
* @param audioLanguages <p>List of wanted audio languages in descending order.</p>
|
||||
* @param subtitleLanguages <p>List of wanted subtitle languages in descending order.</p>
|
||||
* @param subtitlePreference <p>How minimal subtitles should be prioritized</p>
|
||||
* @param forcedAudioIndex <p>A specific audio stream to force. 0-indexed from the first audio stream found</p>
|
||||
* @param forcedSubtitleIndex <p>A specific subtitle stream to force. 0-indexed for the first subtitle stream found</p>
|
||||
* @param forcedAudioIndex <p>A specific audio stream to force as default. 0-indexed from the first audio stream found</p>
|
||||
* @param forcedSubtitleIndex <p>A specific subtitle stream to force as default. 0-indexed for the first subtitle stream found</p>
|
||||
* @param forceVideoEncoding <p>Whether to enforce encoding on the video, even if already hevc</p>
|
||||
* @param forceAudioEncoding <p>Whether to convert audio to web-playable, even though there should be no need</p>
|
||||
*/
|
||||
public AnimeConverter(@NotNull String ffprobePath, @NotNull String ffmpegPath, @NotNull String[] audioLanguages,
|
||||
@NotNull String[] subtitleLanguages,
|
||||
@NotNull MinimalSubtitlePreference subtitlePreference, int forcedAudioIndex,
|
||||
int forcedSubtitleIndex, @NotNull String subtitleNameFilter) {
|
||||
int forcedSubtitleIndex, @NotNull String subtitleNameFilter, boolean forceVideoEncoding,
|
||||
boolean forceAudioEncoding) {
|
||||
super("mkv");
|
||||
this.ffprobePath = ffprobePath;
|
||||
this.ffmpegPath = ffmpegPath;
|
||||
@ -67,6 +75,8 @@ public class AnimeConverter extends AbstractConverter {
|
||||
this.forcedAudioIndex = forcedAudioIndex;
|
||||
this.forcedSubtitleIndex = forcedSubtitleIndex;
|
||||
this.subtitleNameFilter = subtitleNameFilter;
|
||||
this.forceVideoEncoding = forceVideoEncoding;
|
||||
this.forceAudioEncoding = forceAudioEncoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -93,26 +103,42 @@ public class AnimeConverter extends AbstractConverter {
|
||||
|
||||
//Get the first subtitle stream in accordance with chosen languages and signs and songs prevention
|
||||
StreamSorter<SubtitleStream> subtitleSorter = new SubtitleTitleSorter(this.subtitleNameFilter)
|
||||
.append(new MinimalSubtitleSorter(this.subtitlePreference))
|
||||
.append(new SubtitleLanguageSorter(this.subtitleLanguages))
|
||||
.append(new MinimalSubtitleSorter(this.subtitlePreference))
|
||||
.append(new ForcedFirstSorter<>(this.forcedSubtitleIndex));
|
||||
List<SubtitleStream> sorted = subtitleSorter.chainSort(probeResult.getSubtitleStreams());
|
||||
modules.add(new MapAllModule<>(sorted));
|
||||
modules.add(new SetDefaultStreamModule<>(sorted, 0));
|
||||
|
||||
modules.add(new HardwareDecodeModule());
|
||||
modules.add(new CopyAudioModule());
|
||||
if (!this.forceAudioEncoding) {
|
||||
modules.add(new CopyAudioModule());
|
||||
}
|
||||
modules.add(new CopySubtitlesModule());
|
||||
|
||||
List<String> availableHWAcceleration = getAvailableHardwareEncodingMethods();
|
||||
if (availableHWAcceleration.contains("qsv")) {
|
||||
modules.add(new SetVideoCodecModule("hevc_qsv"));
|
||||
modules.add(new SetQualityModule(17, "veryslow"));
|
||||
} else if (availableHWAcceleration.contains("cuda")) {
|
||||
modules.add(new H265HardwareEncodingModule(20));
|
||||
} else {
|
||||
modules.add(new SetVideoCodecModule("hevc"));
|
||||
boolean encodingNecessary = false;
|
||||
for (VideoStream videoStream : probeResult.getVideoStreams()) {
|
||||
if (!videoStream.getCodecName().trim().equalsIgnoreCase("hevc")) {
|
||||
encodingNecessary = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (encodingNecessary || this.forceVideoEncoding) {
|
||||
List<String> availableHWAcceleration = getAvailableHardwareEncodingMethods();
|
||||
if (FFMpegConvert.useHardwareAcceleration() && availableHWAcceleration.contains("qsv")) {
|
||||
modules.add(new SetVideoCodecModule("hevc_qsv"));
|
||||
modules.add(new SetQualityModule(17, "veryslow"));
|
||||
} else if (FFMpegConvert.useHardwareAcceleration() && availableHWAcceleration.contains("cuda")) {
|
||||
modules.add(new H265HardwareEncodingModule(20));
|
||||
} else {
|
||||
modules.add(new SetVideoCodecModule("hevc"));
|
||||
modules.add(new SetQualityModule(19, "medium"));
|
||||
}
|
||||
} else {
|
||||
modules.add(new CopyVideoModule());
|
||||
}
|
||||
|
||||
modules.add(new SetOutputFileModule(outFile));
|
||||
|
||||
new ModuleExecutor(command, modules).execute();
|
||||
|
@ -0,0 +1,17 @@
|
||||
package net.knarcraft.ffmpegconverter.converter.module.output;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.container.FFMpegCommand;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.ConverterModule;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A module for making FFMpeg copy the video codec
|
||||
*/
|
||||
public class CopyVideoModule implements ConverterModule {
|
||||
|
||||
@Override
|
||||
public void addArguments(@NotNull FFMpegCommand command) {
|
||||
command.addOutputFileOption("-c:v", "copy");
|
||||
}
|
||||
|
||||
}
|
@ -26,6 +26,10 @@ public class ForcedFirstSorter<K extends StreamObject> extends AbstractSorter<K>
|
||||
|
||||
@Override
|
||||
public @NotNull List<K> sort(@NotNull List<K> input) {
|
||||
if (input.isEmpty()) {
|
||||
return input;
|
||||
}
|
||||
|
||||
int listIndex = 0;
|
||||
for (int i = 0; i < input.size(); i++) {
|
||||
if (input.get(i).getRelativeIndex() == forcedIndex) {
|
||||
|
@ -2,6 +2,7 @@ package net.knarcraft.ffmpegconverter.handler;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.FFMpegConvert;
|
||||
import net.knarcraft.ffmpegconverter.config.ConfigHandler;
|
||||
import net.knarcraft.ffmpegconverter.config.ConfigKey;
|
||||
import net.knarcraft.ffmpegconverter.utility.OutputUtil;
|
||||
import org.apache.commons.configuration2.Configuration;
|
||||
import org.apache.commons.configuration2.PropertiesConfiguration;
|
||||
@ -43,7 +44,7 @@ public record AvailableHardwareEncoderHandler(@NotNull List<String> availableHar
|
||||
*/
|
||||
public void save() {
|
||||
PropertiesConfiguration configuration = configHandler.getWritableConfiguration();
|
||||
configuration.setProperty("encoder.hardware", this.availableHardwareEncodings);
|
||||
configuration.setProperty(ConfigKey.HARDWARE_ACCELERATED_ENCODERS.toString(), this.availableHardwareEncodings);
|
||||
configHandler.writeConfiguration();
|
||||
OutputUtil.printDebug("Saved available hardware encoder handler");
|
||||
}
|
||||
@ -61,7 +62,7 @@ public record AvailableHardwareEncoderHandler(@NotNull List<String> availableHar
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
List<String> getEncodings = configuration.getList(String.class, "encoder.hardware");
|
||||
List<String> getEncodings = configuration.getList(String.class, ConfigKey.HARDWARE_ACCELERATED_ENCODERS.toString());
|
||||
return new AvailableHardwareEncoderHandler(getEncodings);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ public enum StreamTag {
|
||||
*
|
||||
* <p>Applicable for all 3 stream types</p>
|
||||
*/
|
||||
CODEC_NAME("codec_name="),
|
||||
CODEC_NAME("codec_name"),
|
||||
|
||||
/**
|
||||
* The long name of the codec, useful for displaying information
|
||||
|
@ -57,7 +57,8 @@ public class SubtitleStream extends AbstractStream implements StreamObject {
|
||||
!titleLowercase.matches(".*s&s.*") &&
|
||||
!titleLowercase.matches("signs?") &&
|
||||
!titleLowercase.matches("songs?") &&
|
||||
!titleLowercase.matches(".*signs only.*");
|
||||
!titleLowercase.matches(".*signs only.*") &&
|
||||
!titleLowercase.matches(".* signs .*");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -160,7 +160,7 @@ public final class FFMpegHelper {
|
||||
}
|
||||
try {
|
||||
int exitCode = process.waitFor();
|
||||
OutputUtil.println("Process finished.");
|
||||
OutputUtil.println("Process finished with exit code: " + exitCode);
|
||||
return new ProcessResult(exitCode, output.toString());
|
||||
} catch (InterruptedException e) {
|
||||
return new ProcessResult(1, output.toString());
|
||||
|
@ -1 +1,6 @@
|
||||
# Enabling debug mode will only output part of videos, so different settings can be tested more quickly.
|
||||
# Debug mode also prints more information about probe results, and potentially useful output.
|
||||
debug=false
|
||||
# Enabling hardware acceleration will try to use hardware acceleration for converters where it's available. Note that
|
||||
# software encoders generally produce a lower file-size relative to the output quality.
|
||||
hardware-acceleration=false
|
Loading…
Reference in New Issue
Block a user