Improves configuration handling and stuff
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good
This commit is contained in:
parent
ac25ca1986
commit
380a1b800a
@ -1,7 +1,6 @@
|
||||
package net.knarcraft.ffmpegconverter;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.config.ConfigHandler;
|
||||
import net.knarcraft.ffmpegconverter.config.ConfigKey;
|
||||
import net.knarcraft.ffmpegconverter.config.Configuration;
|
||||
import net.knarcraft.ffmpegconverter.converter.AnimeConverter;
|
||||
import net.knarcraft.ffmpegconverter.converter.AudioConverter;
|
||||
import net.knarcraft.ffmpegconverter.converter.Converter;
|
||||
@ -15,9 +14,7 @@ import net.knarcraft.ffmpegconverter.converter.WebAnimeConverter;
|
||||
import net.knarcraft.ffmpegconverter.converter.WebVideoConverter;
|
||||
import net.knarcraft.ffmpegconverter.property.MinimalSubtitlePreference;
|
||||
import net.knarcraft.ffmpegconverter.utility.FileUtil;
|
||||
import net.knarcraft.ffmpegconverter.utility.ListUtil;
|
||||
import net.knarcraft.ffmpegconverter.utility.OutputUtil;
|
||||
import org.apache.commons.configuration2.Configuration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -38,19 +35,10 @@ public class FFMpegConvert {
|
||||
private static final String FFMPEG_PATH = "ffmpeg"; //Can be just ffmpeg if it's in the path
|
||||
private static final Scanner READER = new Scanner(System.in, StandardCharsets.UTF_8);
|
||||
private static Converter converter = null;
|
||||
private static final ConfigHandler configHandler = new ConfigHandler();
|
||||
private static boolean debug = false;
|
||||
private static boolean useHardwareAcceleration = false;
|
||||
private static final Configuration configuration = new Configuration();
|
||||
|
||||
public static void main(@NotNull String[] arguments) throws IOException {
|
||||
Configuration configuration = configHandler.load();
|
||||
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());
|
||||
}
|
||||
OutputUtil.setDebug(configuration.isDebugEnabled());
|
||||
|
||||
converter = loadConverter();
|
||||
|
||||
@ -85,27 +73,10 @@ public class FFMpegConvert {
|
||||
* @return <p>The configuration handler</p>
|
||||
*/
|
||||
@NotNull
|
||||
public static ConfigHandler getConfigHandler() {
|
||||
return configHandler;
|
||||
public static Configuration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether debug mode is enabled
|
||||
*
|
||||
* @return <p>True if debug mode is enabled</p>
|
||||
*/
|
||||
public static boolean isDebugEnabled() {
|
||||
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
|
||||
@ -230,51 +201,37 @@ public class FFMpegConvert {
|
||||
*/
|
||||
@Nullable
|
||||
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] [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;
|
||||
OutputUtil.println("[Forced audio index 0-n] [Forced subtitle index 0-n] [Force video encoding true/false] " +
|
||||
"[Force audio encoding true/false] [Subtitle name filter]\nYour input: ");
|
||||
List<String> input = readInput(5);
|
||||
int forcedAudioIndex = 0;
|
||||
int forcedSubtitleIndex = 0;
|
||||
String subtitleNameFilter = "";
|
||||
boolean forceVideoEncoding = false;
|
||||
boolean forceAudioEncoding = false;
|
||||
|
||||
if (!input.isEmpty()) {
|
||||
audioLanguage = ListUtil.getListFromCommaSeparatedString(input.get(0));
|
||||
}
|
||||
if (input.size() > 1) {
|
||||
subtitleLanguage = ListUtil.getListFromCommaSeparatedString(input.get(1));
|
||||
}
|
||||
if (input.size() > 2) {
|
||||
subtitlePreference = MinimalSubtitlePreference.valueOf(input.get(2).toUpperCase());
|
||||
}
|
||||
try {
|
||||
if (input.size() > 3) {
|
||||
forcedAudioIndex = Integer.parseInt(input.get(3));
|
||||
if (!input.isEmpty()) {
|
||||
forcedAudioIndex = Integer.parseInt(input.get(0));
|
||||
}
|
||||
if (input.size() > 4) {
|
||||
forcedSubtitleIndex = Integer.parseInt(input.get(4));
|
||||
if (input.size() > 1) {
|
||||
forcedSubtitleIndex = Integer.parseInt(input.get(1));
|
||||
}
|
||||
} 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() > 2) {
|
||||
forceVideoEncoding = Boolean.parseBoolean(input.get(2));
|
||||
}
|
||||
if (input.size() > 6) {
|
||||
forceAudioEncoding = Boolean.parseBoolean(input.get(6));
|
||||
if (input.size() > 3) {
|
||||
forceAudioEncoding = Boolean.parseBoolean(input.get(3));
|
||||
}
|
||||
if (input.size() > 7) {
|
||||
subtitleNameFilter = input.get(7);
|
||||
if (input.size() > 4) {
|
||||
subtitleNameFilter = input.get(4);
|
||||
}
|
||||
return new AnimeConverter(FFPROBE_PATH, FFMPEG_PATH, audioLanguage, subtitleLanguage, subtitlePreference,
|
||||
forcedAudioIndex, forcedSubtitleIndex, subtitleNameFilter, forceVideoEncoding, forceAudioEncoding);
|
||||
return new AnimeConverter(FFPROBE_PATH, FFMPEG_PATH, forcedAudioIndex, forcedSubtitleIndex, subtitleNameFilter,
|
||||
forceVideoEncoding, forceAudioEncoding);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -284,12 +241,9 @@ public class FFMpegConvert {
|
||||
*/
|
||||
@Nullable
|
||||
private static Converter generateWebAnimeConverter() {
|
||||
OutputUtil.println("[Audio languages jpn,eng,ger,fre] [Subtitle languages eng,ger,fre] [Convert to stereo if " +
|
||||
"necessary true/false] [Prevent signs&songs subtitles true/false] [Forced audio index 0-n] " +
|
||||
OutputUtil.println("[Convert to stereo if necessary true/false] [Prevent signs&songs subtitles true/false] [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", "0"};
|
||||
String[] subtitleLanguage = new String[]{"eng", "0"};
|
||||
List<String> input = readInput(5);
|
||||
boolean toStereo = true;
|
||||
MinimalSubtitlePreference subtitlePreference = MinimalSubtitlePreference.AVOID;
|
||||
int forcedAudioIndex = 0;
|
||||
@ -297,32 +251,26 @@ public class FFMpegConvert {
|
||||
String subtitleNameFilter = "";
|
||||
|
||||
if (!input.isEmpty()) {
|
||||
audioLanguage = ListUtil.getListFromCommaSeparatedString(input.get(0));
|
||||
toStereo = Boolean.parseBoolean(input.get(0));
|
||||
}
|
||||
if (input.size() > 1) {
|
||||
subtitleLanguage = ListUtil.getListFromCommaSeparatedString(input.get(1));
|
||||
}
|
||||
if (input.size() > 2) {
|
||||
toStereo = Boolean.parseBoolean(input.get(2));
|
||||
}
|
||||
if (input.size() > 3) {
|
||||
subtitlePreference = MinimalSubtitlePreference.valueOf(input.get(3).toUpperCase());
|
||||
subtitlePreference = MinimalSubtitlePreference.valueOf(input.get(1).toUpperCase());
|
||||
}
|
||||
try {
|
||||
if (input.size() > 4) {
|
||||
forcedAudioIndex = Integer.parseInt(input.get(4));
|
||||
if (input.size() > 2) {
|
||||
forcedAudioIndex = Integer.parseInt(input.get(2));
|
||||
}
|
||||
if (input.size() > 5) {
|
||||
forcedSubtitleIndex = Integer.parseInt(input.get(5));
|
||||
if (input.size() > 3) {
|
||||
forcedSubtitleIndex = Integer.parseInt(input.get(3));
|
||||
}
|
||||
} catch (NumberFormatException exception) {
|
||||
OutputUtil.println("Forced audio or subtitle index is not a number");
|
||||
return null;
|
||||
}
|
||||
if (input.size() > 6) {
|
||||
subtitleNameFilter = input.get(6);
|
||||
if (input.size() > 4) {
|
||||
subtitleNameFilter = input.get(4);
|
||||
}
|
||||
return new WebAnimeConverter(FFPROBE_PATH, FFMPEG_PATH, audioLanguage, subtitleLanguage, toStereo,
|
||||
return new WebAnimeConverter(FFPROBE_PATH, FFMPEG_PATH, toStereo,
|
||||
subtitlePreference, forcedAudioIndex, forcedSubtitleIndex, subtitleNameFilter);
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,12 @@ import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
|
||||
import org.apache.commons.configuration2.builder.fluent.Configurations;
|
||||
import org.apache.commons.configuration2.ex.ConfigurationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -23,6 +25,18 @@ public class ConfigHandler {
|
||||
private final Configurations configurations = new Configurations();
|
||||
private final File settingsFile = new File(configFolder, "config.properties");
|
||||
private FileBasedConfigurationBuilder<PropertiesConfiguration> builder = configurations.propertiesBuilder(settingsFile);
|
||||
private final Map<ConfigKey<?>, Object> optionValues = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Gets the current value of a configuration option
|
||||
*
|
||||
* @param key <p>The configuration key to get the value of</p>
|
||||
* @return <p>The current value</p>
|
||||
*/
|
||||
@Nullable
|
||||
public Object getValue(@NotNull ConfigKey<?> key) {
|
||||
return optionValues.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a writable configuration used for changing settings
|
||||
@ -64,11 +78,9 @@ public class ConfigHandler {
|
||||
|
||||
/**
|
||||
* Loads the saved configuration file
|
||||
*
|
||||
* @return <p>The loaded configuration</p>
|
||||
*/
|
||||
@NotNull
|
||||
public Configuration load() throws IOException {
|
||||
public void load() throws IOException {
|
||||
optionValues.clear();
|
||||
Configuration configuration;
|
||||
if (!settingsFile.exists()) {
|
||||
configuration = new PropertiesConfiguration();
|
||||
@ -86,7 +98,14 @@ public class ConfigHandler {
|
||||
}
|
||||
// Reload contents in the builder
|
||||
builder = configurations.propertiesBuilder(settingsFile);
|
||||
return configuration;
|
||||
|
||||
for (@NotNull ConfigKey<?> key : ConfigKey.values()) {
|
||||
if (configuration.containsKey(key.toString())) {
|
||||
optionValues.put(key, configuration.get(key.getType(), key.toString()));
|
||||
} else {
|
||||
optionValues.put(key, key.getDefaultValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,31 +2,106 @@ package net.knarcraft.ffmpegconverter.config;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A representation of all configuration keys
|
||||
*/
|
||||
public enum ConfigKey {
|
||||
public class ConfigKey<T> {
|
||||
|
||||
private static final Set<ConfigKey<?>> allKeys = new HashSet<>();
|
||||
|
||||
/**
|
||||
* The configuration key for the list of hardware-accelerated encoders available on the system
|
||||
*/
|
||||
HARDWARE_ACCELERATED_ENCODERS("encoders-hardware-accelerated"),
|
||||
public static final ConfigKey<String> HARDWARE_ACCELERATED_ENCODERS = new ConfigKey<>("encoders-hardware-accelerated", String.class, "qsv,cuda,dxva2,d3d11va,opencl,vulkan");
|
||||
|
||||
/**
|
||||
* The configuration key for toggling debug mode
|
||||
*/
|
||||
DEBUG("debug"),
|
||||
public static final ConfigKey<Boolean> DEBUG = createKey("debug", Boolean.class, false);
|
||||
|
||||
/**
|
||||
* The configuration key for toggling hardware acceleration
|
||||
* The configuration key for toggling hardware encoding
|
||||
*/
|
||||
USE_HARDWARE_ACCELERATION("hardware-acceleration"),
|
||||
;
|
||||
public static final ConfigKey<Boolean> USE_HARDWARE_ENCODING = createKey("hardware-acceleration-encode", Boolean.class, false);
|
||||
|
||||
/**
|
||||
* The configuration key for toggling hardware decoding
|
||||
*/
|
||||
public static final ConfigKey<Boolean> USE_HARDWARE_DECODING = createKey("hardware-acceleration-decode", Boolean.class, true);
|
||||
|
||||
/**
|
||||
* The configuration key for toggling forced flac encoding
|
||||
*/
|
||||
public static final ConfigKey<Boolean> ENCODE_FLAC_ALWAYS = createKey("encode-flac-always", Boolean.class, false);
|
||||
|
||||
/**
|
||||
* The configuration key for anime audio languages
|
||||
*/
|
||||
public static final ConfigKey<String> AUDIO_LANGUAGES_ANIME = createKey("audio-languages-anime", String.class, "jpn,eng,*");
|
||||
|
||||
/**
|
||||
* The configuration key for anime subtitle languages
|
||||
*/
|
||||
public static final ConfigKey<String> SUBTITLE_LANGUAGES_ANIME = createKey("subtitle-languages-anime", String.class, "eng,*");
|
||||
|
||||
/**
|
||||
* The configuration key for the minimal subtitle preference
|
||||
*/
|
||||
public static final ConfigKey<String> MINIMAL_SUBTITLE_PREFERENCE = createKey("minimal-subtitle-preference", String.class, "AVOID");
|
||||
|
||||
private final String configKey;
|
||||
private final T defaultValue;
|
||||
private final Class<T> type;
|
||||
|
||||
ConfigKey(@NotNull String configKey) {
|
||||
/**
|
||||
* Instantiates a new config key
|
||||
*
|
||||
* @param configKey <p>The storage key in the configuration file</p>
|
||||
* @param type <p>The type of this option's value</p>
|
||||
* @param defaultValue <p>The default value of this option</p>
|
||||
*/
|
||||
private ConfigKey(@NotNull String configKey, @NotNull Class<T> type, @NotNull T defaultValue) {
|
||||
this.configKey = configKey;
|
||||
this.type = type;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new config key
|
||||
*
|
||||
* @param configKey <p>The storage key in the configuration file</p>
|
||||
* @param type <p>The type of this option's value</p>
|
||||
* @param defaultValue <p>The default value of this option</p>
|
||||
* @param <F> <p>The type of key to create</p>
|
||||
* @return <p>The new config key</p>
|
||||
*/
|
||||
private static <F> ConfigKey<F> createKey(@NotNull String configKey, @NotNull Class<F> type, @NotNull F defaultValue) {
|
||||
ConfigKey<F> key = new ConfigKey<>(configKey, type, defaultValue);
|
||||
allKeys.add(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of this option's value
|
||||
*
|
||||
* @return <p>The type of the value</p>
|
||||
*/
|
||||
@NotNull
|
||||
public Class<T> getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default value for the option represented by this key
|
||||
*
|
||||
* @return <p>The default value</p>
|
||||
*/
|
||||
@NotNull
|
||||
public T getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -34,4 +109,14 @@ public enum ConfigKey {
|
||||
return this.configKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all configuration keys
|
||||
*
|
||||
* @return <p>All configuration keys</p>
|
||||
*/
|
||||
@NotNull
|
||||
public static ConfigKey<?>[] values() {
|
||||
return allKeys.toArray(new ConfigKey[0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,150 @@
|
||||
package net.knarcraft.ffmpegconverter.config;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.property.MinimalSubtitlePreference;
|
||||
import net.knarcraft.ffmpegconverter.utility.ConfigHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A configuration used for FFMpegConvert
|
||||
*/
|
||||
public class Configuration {
|
||||
|
||||
private final ConfigHandler configHandler;
|
||||
private boolean debug;
|
||||
private boolean useHardwareEncoding;
|
||||
private boolean useHardwareDecoding;
|
||||
private List<String> hardwareEncoders;
|
||||
private boolean alwaysEncodeFlac;
|
||||
private List<String> animeAudioLanguages;
|
||||
private List<String> animeSubtitleLanguages;
|
||||
private MinimalSubtitlePreference minimalSubtitlePreference;
|
||||
|
||||
/**
|
||||
* Instantiates and loads a new configuration
|
||||
*/
|
||||
public Configuration() {
|
||||
this.configHandler = new ConfigHandler();
|
||||
try {
|
||||
this.configHandler.load();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads/reloads this configuration
|
||||
*/
|
||||
public void load() {
|
||||
try {
|
||||
this.configHandler.load();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
debug = ConfigHelper.asBoolean(configHandler.getValue(ConfigKey.DEBUG));
|
||||
useHardwareEncoding = ConfigHelper.asBoolean(configHandler.getValue(ConfigKey.USE_HARDWARE_ENCODING));
|
||||
useHardwareDecoding = ConfigHelper.asBoolean(configHandler.getValue(ConfigKey.USE_HARDWARE_DECODING));
|
||||
hardwareEncoders = ConfigHelper.asStringList(configHandler.getValue(ConfigKey.HARDWARE_ACCELERATED_ENCODERS));
|
||||
alwaysEncodeFlac = ConfigHelper.asBoolean(configHandler.getValue(ConfigKey.ENCODE_FLAC_ALWAYS));
|
||||
animeAudioLanguages = ConfigHelper.asStringList(configHandler.getValue(ConfigKey.AUDIO_LANGUAGES_ANIME));
|
||||
animeSubtitleLanguages = ConfigHelper.asStringList(configHandler.getValue(ConfigKey.SUBTITLE_LANGUAGES_ANIME));
|
||||
try {
|
||||
minimalSubtitlePreference = MinimalSubtitlePreference.valueOf(String.valueOf(configHandler.getValue(
|
||||
ConfigKey.MINIMAL_SUBTITLE_PREFERENCE)));
|
||||
} catch (IllegalArgumentException | NullPointerException exception) {
|
||||
minimalSubtitlePreference = MinimalSubtitlePreference.AVOID;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying config handler for this configuration
|
||||
*
|
||||
* @return <p>The underlying config handler</p>
|
||||
*/
|
||||
@NotNull
|
||||
public ConfigHandler getConfigHandler() {
|
||||
return this.configHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether debug mode is enabled
|
||||
*
|
||||
* @return <p>True if debug mode is enabled</p>
|
||||
*/
|
||||
public boolean isDebugEnabled() {
|
||||
return this.debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether hardware accelerated encoding is enabled
|
||||
*
|
||||
* @return <p>True if hardware accelerated encoding is enabled</p>
|
||||
*/
|
||||
public boolean useHardwareEncoding() {
|
||||
return this.useHardwareEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether hardware accelerated decoding is enabled
|
||||
*
|
||||
* @return <p>True if hardware accelerated decoding is enabled</p>
|
||||
*/
|
||||
public boolean useHardwareDecoding() {
|
||||
return this.useHardwareDecoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether FLAC audio tracks should always be re-encoded
|
||||
*
|
||||
* @return <p>Whether FLAC tracks should always be re-encoded</p>
|
||||
*/
|
||||
public boolean alwaysEncodeFlac() {
|
||||
return this.alwaysEncodeFlac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all hardware encoders defined in the configuration
|
||||
*
|
||||
* @return <p>The specified hardware encoders</p>
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> hardwareEncoders() {
|
||||
return this.hardwareEncoders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the audio language priorities for usage when converting anime
|
||||
*
|
||||
* @return <p>The anime audio language priorities</p>
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getAnimeAudioLanguages() {
|
||||
return this.animeAudioLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subtitle language priorities for usage when converting anime
|
||||
*
|
||||
* @return <p>The anime subtitle language priorities</p>
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getAnimeSubtitleLanguages() {
|
||||
return this.animeSubtitleLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the preference for minimal subtitles
|
||||
*
|
||||
* @return <p>The minimal subtitle preference</p>
|
||||
*/
|
||||
@NotNull
|
||||
public MinimalSubtitlePreference getMinimalSubtitlePreference() {
|
||||
return this.minimalSubtitlePreference;
|
||||
}
|
||||
|
||||
}
|
@ -4,6 +4,7 @@ import net.knarcraft.ffmpegconverter.FFMpegConvert;
|
||||
import net.knarcraft.ffmpegconverter.container.FFMpegCommand;
|
||||
import net.knarcraft.ffmpegconverter.container.StreamProbeResult;
|
||||
import net.knarcraft.ffmpegconverter.handler.AvailableHardwareEncoderHandler;
|
||||
import net.knarcraft.ffmpegconverter.streams.StreamObject;
|
||||
import net.knarcraft.ffmpegconverter.utility.FFMpegHelper;
|
||||
import net.knarcraft.ffmpegconverter.utility.FileHelper;
|
||||
import net.knarcraft.ffmpegconverter.utility.FileUtil;
|
||||
@ -21,7 +22,7 @@ import java.util.List;
|
||||
*/
|
||||
public abstract class AbstractConverter implements Converter {
|
||||
|
||||
final boolean debug = FFMpegConvert.isDebugEnabled();
|
||||
final boolean debug = FFMpegConvert.getConfiguration().isDebugEnabled();
|
||||
private final String newExtension;
|
||||
protected String ffprobePath;
|
||||
protected String ffmpegPath;
|
||||
@ -136,4 +137,18 @@ public abstract class AbstractConverter implements Converter {
|
||||
return encoderHandler.availableHardwareEncodings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the output indexes for the given streams
|
||||
*
|
||||
* <p>This will basically mark the given streams' order as the order they will appear in the output file. This is
|
||||
* used when specifying specific codecs per-stream in the output file.</p>
|
||||
*
|
||||
* @param streams <p>The streams to set the output indexes for</p>
|
||||
*/
|
||||
protected <K extends StreamObject> void setOutputIndexes(@NotNull List<K> streams) {
|
||||
for (int i = 0; i < streams.size(); i++) {
|
||||
streams.get(i).setOutputIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.knarcraft.ffmpegconverter.converter;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.FFMpegConvert;
|
||||
import net.knarcraft.ffmpegconverter.config.Configuration;
|
||||
import net.knarcraft.ffmpegconverter.container.FFMpegCommand;
|
||||
import net.knarcraft.ffmpegconverter.container.StreamProbeResult;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.ConverterModule;
|
||||
@ -16,6 +17,7 @@ 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;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.SetStreamLanguageModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.SetVideoCodecModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.sorter.AudioLanguageSorter;
|
||||
import net.knarcraft.ffmpegconverter.converter.sorter.ForcedFirstSorter;
|
||||
@ -29,10 +31,12 @@ 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 net.knarcraft.ffmpegconverter.utility.OutputUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -40,8 +44,8 @@ import java.util.List;
|
||||
*/
|
||||
public class AnimeConverter extends AbstractConverter {
|
||||
|
||||
private final String[] audioLanguages;
|
||||
private final String[] subtitleLanguages;
|
||||
private final List<String> audioLanguages;
|
||||
private final List<String> subtitleLanguages;
|
||||
private final MinimalSubtitlePreference subtitlePreference;
|
||||
private final int forcedAudioIndex;
|
||||
private final int forcedSubtitleIndex;
|
||||
@ -54,25 +58,21 @@ public class AnimeConverter extends AbstractConverter {
|
||||
*
|
||||
* @param ffprobePath <p>Path/command to ffprobe.</p>
|
||||
* @param ffmpegPath <p>Path/command to ffmpeg.</p>
|
||||
* @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 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,
|
||||
public AnimeConverter(@NotNull String ffprobePath, @NotNull String ffmpegPath, int forcedAudioIndex,
|
||||
int forcedSubtitleIndex, @NotNull String subtitleNameFilter, boolean forceVideoEncoding,
|
||||
boolean forceAudioEncoding) {
|
||||
super("mkv");
|
||||
Configuration configuration = FFMpegConvert.getConfiguration();
|
||||
this.ffprobePath = ffprobePath;
|
||||
this.ffmpegPath = ffmpegPath;
|
||||
this.audioLanguages = audioLanguages;
|
||||
this.subtitleLanguages = subtitleLanguages;
|
||||
this.subtitlePreference = subtitlePreference;
|
||||
this.audioLanguages = configuration.getAnimeAudioLanguages();
|
||||
this.subtitleLanguages = configuration.getAnimeSubtitleLanguages();
|
||||
this.subtitlePreference = configuration.getMinimalSubtitlePreference();
|
||||
this.forcedAudioIndex = forcedAudioIndex;
|
||||
this.forcedSubtitleIndex = forcedSubtitleIndex;
|
||||
this.subtitleNameFilter = subtitleNameFilter;
|
||||
@ -85,6 +85,7 @@ public class AnimeConverter extends AbstractConverter {
|
||||
public FFMpegCommand generateConversionCommand(@NotNull String executable,
|
||||
@NotNull StreamProbeResult probeResult,
|
||||
@NotNull String outFile) {
|
||||
Configuration configuration = FFMpegConvert.getConfiguration();
|
||||
FFMpegCommand command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, probeResult.parsedFiles());
|
||||
List<ConverterModule> modules = new ArrayList<>();
|
||||
if (this.debug) {
|
||||
@ -92,16 +93,20 @@ public class AnimeConverter extends AbstractConverter {
|
||||
}
|
||||
modules.add(new FastStartModule());
|
||||
|
||||
//Get the first video stream
|
||||
modules.add(new MapAllModule<>(probeResult.getVideoStreams()));
|
||||
//Map all video streams
|
||||
List<VideoStream> videoStreams = probeResult.getVideoStreams();
|
||||
modules.add(new MapAllModule<>(videoStreams));
|
||||
setOutputIndexes(videoStreams);
|
||||
|
||||
//Get the first audio stream in accordance with chosen languages
|
||||
StreamSorter<AudioStream> audioSorter = new AudioLanguageSorter(this.audioLanguages)
|
||||
.append(new ForcedFirstSorter<>(this.forcedAudioIndex))
|
||||
.append(new SpecialAudioSorter(MinimalSubtitlePreference.REJECT));
|
||||
List<AudioStream> sortedAudio = audioSorter.chainSort(probeResult.getAudioStreams());
|
||||
setOutputIndexes(sortedAudio);
|
||||
modules.add(new MapAllModule<>(sortedAudio));
|
||||
modules.add(new SetDefaultStreamModule<>(sortedAudio, 0));
|
||||
modules.add(new SetStreamLanguageModule<>(sortedAudio));
|
||||
|
||||
//Get the first subtitle stream in accordance with chosen languages and signs and songs prevention
|
||||
StreamSorter<SubtitleStream> subtitleSorter = new SubtitleTitleSorter(
|
||||
@ -109,13 +114,24 @@ public class AnimeConverter extends AbstractConverter {
|
||||
.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));
|
||||
List<SubtitleStream> sortedSubtitles = subtitleSorter.chainSort(probeResult.getSubtitleStreams());
|
||||
setOutputIndexes(sortedSubtitles);
|
||||
modules.add(new MapAllModule<>(sortedSubtitles));
|
||||
modules.add(new SetDefaultStreamModule<>(sortedSubtitles, 0));
|
||||
modules.add(new SetStreamLanguageModule<>(sortedSubtitles));
|
||||
OutputUtil.printDebug("Subtitle preference: " + this.subtitleLanguages + ", Subtitle name filter: " +
|
||||
this.subtitleNameFilter + ", Minimal Subtitle Preference: " + this.subtitlePreference.name() +
|
||||
", Sorted order: " + Arrays.toString(sortedSubtitles.toArray()));
|
||||
|
||||
modules.add(new HardwareDecodeModule());
|
||||
if (!this.forceAudioEncoding) {
|
||||
modules.add(new CopyAudioModule());
|
||||
if (configuration.useHardwareDecoding()) {
|
||||
modules.add(new HardwareDecodeModule());
|
||||
}
|
||||
|
||||
boolean encodeFlac = this.forceAudioEncoding || FFMpegConvert.getConfiguration().alwaysEncodeFlac();
|
||||
for (AudioStream audioStream : sortedAudio) {
|
||||
if (!encodeFlac || !audioStream.getCodecName().equalsIgnoreCase("flac")) {
|
||||
modules.add(new CopyAudioModule(audioStream));
|
||||
}
|
||||
}
|
||||
modules.add(new CopySubtitlesModule());
|
||||
|
||||
@ -129,10 +145,10 @@ public class AnimeConverter extends AbstractConverter {
|
||||
|
||||
if (encodingNecessary || this.forceVideoEncoding) {
|
||||
List<String> availableHWAcceleration = getAvailableHardwareEncodingMethods();
|
||||
if (FFMpegConvert.useHardwareAcceleration() && availableHWAcceleration.contains("qsv")) {
|
||||
if (configuration.useHardwareEncoding() && availableHWAcceleration.contains("qsv")) {
|
||||
modules.add(new SetVideoCodecModule("hevc_qsv"));
|
||||
modules.add(new SetQualityModule(17, "veryslow"));
|
||||
} else if (FFMpegConvert.useHardwareAcceleration() && availableHWAcceleration.contains("cuda")) {
|
||||
} else if (configuration.useHardwareEncoding() && availableHWAcceleration.contains("cuda")) {
|
||||
modules.add(new H265HardwareEncodingModule(20));
|
||||
} else {
|
||||
modules.add(new SetVideoCodecModule("hevc"));
|
||||
|
@ -12,6 +12,7 @@ import net.knarcraft.ffmpegconverter.converter.module.output.CopyAudioModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.CopySubtitlesModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.FastStartModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.SetOutputFileModule;
|
||||
import net.knarcraft.ffmpegconverter.streams.AudioStream;
|
||||
import net.knarcraft.ffmpegconverter.streams.StreamObject;
|
||||
import net.knarcraft.ffmpegconverter.utility.FFMpegHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -64,9 +65,11 @@ public class MkvH264Converter extends AbstractConverter {
|
||||
}
|
||||
|
||||
// Map audio if present
|
||||
if (!probeResult.getAudioStreams().isEmpty()) {
|
||||
modules.add(new MapAllModule<>(probeResult.getAudioStreams()));
|
||||
modules.add(new CopyAudioModule());
|
||||
List<AudioStream> audioStreams = probeResult.getAudioStreams();
|
||||
if (!audioStreams.isEmpty()) {
|
||||
modules.add(new MapAllModule<>(audioStreams));
|
||||
setOutputIndexes(audioStreams);
|
||||
modules.add(new CopyAudioModule(audioStreams));
|
||||
}
|
||||
|
||||
// Map subtitles if present
|
||||
|
@ -69,7 +69,8 @@ public class MkvH265ReducedConverter extends AbstractConverter {
|
||||
List<AudioStream> audioStreams = probeResult.getAudioStreams();
|
||||
if (!audioStreams.isEmpty()) {
|
||||
modules.add(new MapAllModule<>(audioStreams));
|
||||
modules.add(new CopyAudioModule());
|
||||
setOutputIndexes(audioStreams);
|
||||
modules.add(new CopyAudioModule(audioStreams));
|
||||
}
|
||||
|
||||
// Map subtitles if present
|
||||
|
@ -8,8 +8,10 @@ import net.knarcraft.ffmpegconverter.converter.module.ModuleExecutor;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.hardwarecoding.HardwareDecodeModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.mapping.MapAllModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.CopyAudioModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.CopyVideoModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.MovTextModule;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.output.SetOutputFileModule;
|
||||
import net.knarcraft.ffmpegconverter.streams.AudioStream;
|
||||
import net.knarcraft.ffmpegconverter.utility.FFMpegHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -53,8 +55,10 @@ public class SubtitleEmbed extends AbstractConverter {
|
||||
|
||||
modules.add(new MapAllModule<>(probeResult.parsedStreams()));
|
||||
modules.add(new HardwareDecodeModule());
|
||||
modules.add(new CopyAudioModule());
|
||||
modules.add(new CopyAudioModule());
|
||||
List<AudioStream> audioStreams = probeResult.getAudioStreams();
|
||||
setOutputIndexes(audioStreams);
|
||||
modules.add(new CopyAudioModule(audioStreams));
|
||||
modules.add(new CopyVideoModule());
|
||||
modules.add(new MovTextModule());
|
||||
modules.add(new SetOutputFileModule(outFile));
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.knarcraft.ffmpegconverter.converter;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.FFMpegConvert;
|
||||
import net.knarcraft.ffmpegconverter.config.Configuration;
|
||||
import net.knarcraft.ffmpegconverter.container.FFMpegCommand;
|
||||
import net.knarcraft.ffmpegconverter.container.StreamProbeResult;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.ConverterModule;
|
||||
@ -33,8 +35,8 @@ import static net.knarcraft.ffmpegconverter.utility.FFMpegHelper.getNthSteam;
|
||||
*/
|
||||
public class WebAnimeConverter extends AbstractConverter {
|
||||
|
||||
private final String[] audioLanguages;
|
||||
private final String[] subtitleLanguages;
|
||||
private final List<String> audioLanguages;
|
||||
private final List<String> subtitleLanguages;
|
||||
private final boolean toStereo;
|
||||
private final MinimalSubtitlePreference subtitlePreference;
|
||||
private final int forcedAudioIndex;
|
||||
@ -46,22 +48,20 @@ public class WebAnimeConverter extends AbstractConverter {
|
||||
*
|
||||
* @param ffprobePath <p>Path/command to ffprobe.</p>
|
||||
* @param ffmpegPath <p>Path/command to ffmpeg.</p>
|
||||
* @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 toStereo <p>Convert video with several audio channels to stereo.</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>
|
||||
*/
|
||||
public WebAnimeConverter(@NotNull String ffprobePath, @NotNull String ffmpegPath, @NotNull String[] audioLanguages,
|
||||
@NotNull String[] subtitleLanguages, boolean toStereo,
|
||||
public WebAnimeConverter(@NotNull String ffprobePath, @NotNull String ffmpegPath, boolean toStereo,
|
||||
@NotNull MinimalSubtitlePreference subtitlePreference, int forcedAudioIndex,
|
||||
int forcedSubtitleIndex, @NotNull String subtitleNameFilter) {
|
||||
super("mp4");
|
||||
Configuration configuration = FFMpegConvert.getConfiguration();
|
||||
this.ffprobePath = ffprobePath;
|
||||
this.ffmpegPath = ffmpegPath;
|
||||
this.audioLanguages = audioLanguages;
|
||||
this.subtitleLanguages = subtitleLanguages;
|
||||
this.audioLanguages = configuration.getAnimeAudioLanguages();
|
||||
this.subtitleLanguages = configuration.getAnimeSubtitleLanguages();
|
||||
this.toStereo = toStereo;
|
||||
this.subtitlePreference = subtitlePreference;
|
||||
this.forcedAudioIndex = forcedAudioIndex;
|
||||
|
@ -2,16 +2,56 @@ package net.knarcraft.ffmpegconverter.converter.module.output;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.container.FFMpegCommand;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.ConverterModule;
|
||||
import net.knarcraft.ffmpegconverter.streams.AudioStream;
|
||||
import net.knarcraft.ffmpegconverter.streams.StreamObject;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A module for making FFMpeg copy the audio codec
|
||||
*/
|
||||
public class CopyAudioModule implements ConverterModule {
|
||||
|
||||
private final List<AudioStream> streams;
|
||||
|
||||
/**
|
||||
* Instantiates a new copy audio module
|
||||
*
|
||||
* @param streams <p>The streams to specify the copy flag for, or null to not use a per-stream selector</p>
|
||||
*/
|
||||
public CopyAudioModule(@NotNull List<AudioStream> streams) {
|
||||
this.streams = streams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new copy audio module
|
||||
*
|
||||
* @param stream <p>The stream to specify the copy flag for</p>
|
||||
*/
|
||||
public CopyAudioModule(@NotNull AudioStream stream) {
|
||||
this.streams = List.of(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new copy audio module
|
||||
*/
|
||||
public CopyAudioModule() {
|
||||
this.streams = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addArguments(@NotNull FFMpegCommand command) {
|
||||
command.addOutputFileOption("-c:a", "copy");
|
||||
if (this.streams != null) {
|
||||
for (StreamObject streamObject : this.streams) {
|
||||
int outputIndex = streamObject.getOutputIndex();
|
||||
if (outputIndex != -1) {
|
||||
command.addOutputFileOption("-c:a:" + outputIndex, "copy");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
command.addOutputFileOption("-c:a", "copy");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package net.knarcraft.ffmpegconverter.converter.module.output;
|
||||
|
||||
import net.knarcraft.ffmpegconverter.container.FFMpegCommand;
|
||||
import net.knarcraft.ffmpegconverter.converter.module.ConverterModule;
|
||||
import net.knarcraft.ffmpegconverter.streams.StreamObject;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A module for setting the language of some streams
|
||||
*
|
||||
* @param <K> <p>The type of stream to set language for</p>
|
||||
*/
|
||||
public class SetStreamLanguageModule<K extends StreamObject> implements ConverterModule {
|
||||
|
||||
private final List<K> streams;
|
||||
|
||||
/**
|
||||
* Instantiates a new set stream language module
|
||||
*
|
||||
* @param streams <p>The streams to set language for</p>
|
||||
*/
|
||||
public SetStreamLanguageModule(@NotNull List<K> streams) {
|
||||
this.streams = streams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addArguments(@NotNull FFMpegCommand command) {
|
||||
for (StreamObject stream : this.streams) {
|
||||
if (!stream.getLanguage().equalsIgnoreCase("und") && !stream.getLanguage().isBlank()) {
|
||||
command.addOutputFileOption(String.format("-metadata:s:%s:%d", stream.streamTypeCharacter(),
|
||||
stream.getOutputIndex()), String.format("language=%s", stream.getLanguage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -71,7 +71,7 @@ public abstract class AbstractSorter<L extends StreamObject> implements StreamSo
|
||||
*/
|
||||
@NotNull
|
||||
protected <G extends StreamObject> List<G> sortStreamsByLanguage(@NotNull List<G> streams,
|
||||
@NotNull String[] languages) {
|
||||
@NotNull List<String> languages) {
|
||||
List<G> sorted = new ArrayList<>();
|
||||
for (String language : languages) {
|
||||
for (G stream : streams) {
|
||||
|
@ -10,14 +10,14 @@ import java.util.List;
|
||||
*/
|
||||
public class AudioLanguageSorter extends AbstractSorter<AudioStream> {
|
||||
|
||||
private final String[] languageOrder;
|
||||
private final List<String> languageOrder;
|
||||
|
||||
/**
|
||||
* Instantiates a new audio language sorter
|
||||
*
|
||||
* @param languageOrder <p>The order of preference for audio languages</p>
|
||||
*/
|
||||
public AudioLanguageSorter(@NotNull String[] languageOrder) {
|
||||
public AudioLanguageSorter(@NotNull List<String> languageOrder) {
|
||||
this.languageOrder = languageOrder;
|
||||
}
|
||||
|
||||
|
@ -11,14 +11,14 @@ import java.util.List;
|
||||
*/
|
||||
public class SubtitleLanguageSorter extends AbstractSorter<SubtitleStream> {
|
||||
|
||||
private final String[] languageOrder;
|
||||
private final List<String> languageOrder;
|
||||
|
||||
/**
|
||||
* Instantiates a new subtitle language sorter
|
||||
*
|
||||
* @param languageOrder <p>The order of preference for subtitle languages</p>
|
||||
*/
|
||||
public SubtitleLanguageSorter(@NotNull String[] languageOrder) {
|
||||
public SubtitleLanguageSorter(@NotNull List<String> languageOrder) {
|
||||
this.languageOrder = languageOrder;
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ public class SubtitleTitleSorter extends AbstractSorter<SubtitleStream> {
|
||||
|
||||
boolean isRegEx = isValidRegularExpression(filter) && hasSpecialRegexCharacters(filter);
|
||||
|
||||
if (FFMpegConvert.isDebugEnabled()) {
|
||||
if (FFMpegConvert.getConfiguration().isDebugEnabled()) {
|
||||
System.out.println("Filtering subtitles by filter " + filter + ". RegEx is " + isRegEx);
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ 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;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -17,7 +16,7 @@ import java.util.List;
|
||||
*/
|
||||
public record AvailableHardwareEncoderHandler(@NotNull List<String> availableHardwareEncodings) {
|
||||
|
||||
private static final ConfigHandler configHandler = FFMpegConvert.getConfigHandler();
|
||||
private static final ConfigHandler configHandler = FFMpegConvert.getConfiguration().getConfigHandler();
|
||||
|
||||
/**
|
||||
* Gets all hardware encodings
|
||||
@ -44,7 +43,7 @@ public record AvailableHardwareEncoderHandler(@NotNull List<String> availableHar
|
||||
*/
|
||||
public void save() {
|
||||
PropertiesConfiguration configuration = configHandler.getWritableConfiguration();
|
||||
configuration.setProperty(ConfigKey.HARDWARE_ACCELERATED_ENCODERS.toString(), this.availableHardwareEncodings);
|
||||
configuration.setProperty(ConfigKey.HARDWARE_ACCELERATED_ENCODERS.toString(), String.join(",", this.availableHardwareEncodings));
|
||||
configHandler.writeConfiguration();
|
||||
OutputUtil.printDebug("Saved available hardware encoder handler");
|
||||
}
|
||||
@ -56,14 +55,12 @@ public record AvailableHardwareEncoderHandler(@NotNull List<String> availableHar
|
||||
*/
|
||||
@NotNull
|
||||
public static AvailableHardwareEncoderHandler load() {
|
||||
Configuration configuration;
|
||||
try {
|
||||
configuration = configHandler.load();
|
||||
configHandler.load();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
List<String> getEncodings = configuration.getList(String.class, ConfigKey.HARDWARE_ACCELERATED_ENCODERS.toString());
|
||||
return new AvailableHardwareEncoderHandler(getEncodings);
|
||||
return new AvailableHardwareEncoderHandler(FFMpegConvert.getConfiguration().hardwareEncoders());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ public abstract class AbstractStream implements StreamObject {
|
||||
protected final String language;
|
||||
protected final boolean isDefault;
|
||||
protected final String title;
|
||||
protected int outputIndex;
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract stream
|
||||
@ -28,11 +29,12 @@ public abstract class AbstractStream implements StreamObject {
|
||||
protected AbstractStream(@NotNull Map<StreamTag, String> streamInfo, int inputIndex, int relativeIndex) {
|
||||
this.codecName = ValueParsingHelper.parseString(streamInfo.get(StreamTag.CODEC_NAME), "");
|
||||
this.absoluteIndex = ValueParsingHelper.parseInt(streamInfo.get(StreamTag.INDEX), -1);
|
||||
this.language = ValueParsingHelper.parseString(streamInfo.get(StreamTag.TAG_LANGUAGE), "und");
|
||||
this.language = parseLanguage(streamInfo);
|
||||
this.isDefault = ValueParsingHelper.parseBoolean(streamInfo.get(StreamTag.DISPOSITION_DEFAULT), false);
|
||||
this.title = ValueParsingHelper.parseString(streamInfo.get(StreamTag.TAG_TITLE), "");
|
||||
this.inputIndex = inputIndex;
|
||||
this.relativeIndex = relativeIndex;
|
||||
this.outputIndex = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -73,4 +75,40 @@ public abstract class AbstractStream implements StreamObject {
|
||||
return this.inputIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOutputIndex() {
|
||||
return this.outputIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOutputIndex(int newIndex) {
|
||||
this.outputIndex = newIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the correct language of a stream
|
||||
*
|
||||
* <p>As some people tend to set weird incorrect languages for anime streams, this tries to correct that by
|
||||
* detecting streams that are actually in english, but set to something else.</p>
|
||||
*
|
||||
* @param streamInfo <p>All info about the stream</p>
|
||||
* @return <p>The actual language</p>
|
||||
*/
|
||||
@NotNull
|
||||
private String parseLanguage(@NotNull Map<StreamTag, String> streamInfo) {
|
||||
String languageString = ValueParsingHelper.parseString(streamInfo.get(StreamTag.TAG_LANGUAGE), "und");
|
||||
String title = ValueParsingHelper.parseString(streamInfo.get(StreamTag.TAG_TITLE), "");
|
||||
if (languageString.equalsIgnoreCase("zxx") ||
|
||||
(title.toLowerCase().matches(".*english.*") && languageString.equalsIgnoreCase("jpn"))) {
|
||||
return "eng";
|
||||
}
|
||||
return languageString;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String toString() {
|
||||
return this.title + " | Input index: " + inputIndex + " | Language: " + language;
|
||||
}
|
||||
|
||||
}
|
@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* An object describing a generic video file stream
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface StreamObject {
|
||||
|
||||
/**
|
||||
@ -68,4 +69,21 @@ public interface StreamObject {
|
||||
*/
|
||||
char streamTypeCharacter();
|
||||
|
||||
/**
|
||||
* Gets the index of this stream in the output file
|
||||
*
|
||||
* @return <p>The index of this stream in the output file, or -1 if not set</p>
|
||||
*/
|
||||
int getOutputIndex();
|
||||
|
||||
/**
|
||||
* Sets the index of this stream in the output file
|
||||
*
|
||||
* <p>After sorting and selecting streams, setting the output index makes it much easier to set properties for this
|
||||
* specific stream, without having to rely on loops</p>
|
||||
*
|
||||
* @param newIndex <p>The new output index</p>
|
||||
*/
|
||||
void setOutputIndex(int newIndex);
|
||||
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public class SubtitleStream extends AbstractStream implements StreamObject {
|
||||
*/
|
||||
private boolean checkIfIsFullSubtitle() {
|
||||
String titleLowercase = getTitle().toLowerCase().trim();
|
||||
return !titleLowercase.matches(".*si(ng|gn)s?[ &/a-z]+songs?.*") &&
|
||||
return !titleLowercase.matches(".*si(ng|gn)s?[ &/a-z+]+songs?.*") &&
|
||||
!titleLowercase.matches(".*songs?[ &/a-z]+si(gn|ng)s?.*") &&
|
||||
!titleLowercase.matches(".*forced.*") &&
|
||||
!titleLowercase.matches(".*s&s.*") &&
|
||||
@ -68,4 +68,10 @@ public class SubtitleStream extends AbstractStream implements StreamObject {
|
||||
return 's';
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String toString() {
|
||||
return super.toString() + " | Is full: " + this.isFullSubtitle;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package net.knarcraft.ffmpegconverter.utility;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A helper class for dealing with configuration value types
|
||||
*/
|
||||
public final class ConfigHelper {
|
||||
|
||||
private ConfigHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the given value as a string list
|
||||
*
|
||||
* @param value <p>The raw string list value</p>
|
||||
* @return <p>The value as a string list, or null if not compatible</p>
|
||||
*/
|
||||
public static @NotNull List<String> asStringList(@Nullable Object value) {
|
||||
if (value == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
if (value instanceof String string) {
|
||||
return List.of((string).split(","));
|
||||
} else if (value instanceof List<?> list) {
|
||||
List<String> strings = new ArrayList<>();
|
||||
for (Object object : list) {
|
||||
strings.add(String.valueOf(object));
|
||||
}
|
||||
return strings;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the given value as a boolean
|
||||
*
|
||||
* <p>This will throw an exception if used for a non-boolean value</p>
|
||||
*
|
||||
* @param value <p>The object value to get</p>
|
||||
* @return <p>The value of the given object as a boolean</p>
|
||||
* @throws ClassCastException <p>If the given value is not a boolean</p>
|
||||
*/
|
||||
public static boolean asBoolean(@Nullable Object value) throws ClassCastException {
|
||||
if (value instanceof Boolean booleanValue) {
|
||||
return booleanValue;
|
||||
} else if (value instanceof String) {
|
||||
return Boolean.parseBoolean((String) value);
|
||||
} else {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -3,4 +3,16 @@
|
||||
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
|
||||
hardware-acceleration-encode=false
|
||||
# Hardware decoding can often speed up the conversion, but might be troublesome at times
|
||||
hardware-acceleration-decode=true
|
||||
# The available hardware encoders
|
||||
encoders-hardware-accelerated=qsv,cuda,vaapi,dxva2,d3d11va,opencl,vulkan,d3d12va
|
||||
# As FLAC can increase file size significantly, this option enabled automatic re-encode of flac tracks
|
||||
encode-flac-always=false
|
||||
# The preference for audio languages when converting anime (0 = undefined, * = any)
|
||||
audio-languages-anime=jpn,eng,*
|
||||
# The preference for subtitle languages when converting anime (0 = undefined, * = any)
|
||||
subtitle-languages-anime=eng,*
|
||||
# The preference for minimal subtitles, AKA Signs & Songs (REQUIRE/PREFER/NO_PREFERENCE/AVOID/REJECT)
|
||||
minimal-subtitle-preference=AVOID
|
Loading…
Reference in New Issue
Block a user