diff --git a/pom.xml b/pom.xml index 7920727..811c854 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ - net.knarcraft.ffmpegconverter.Main + net.knarcraft.ffmpegconverter.FFMpegConvert diff --git a/src/main/java/net/knarcraft/ffmpegconverter/Main.java b/src/main/java/net/knarcraft/ffmpegconverter/FFMpegConvert.java similarity index 89% rename from src/main/java/net/knarcraft/ffmpegconverter/Main.java rename to src/main/java/net/knarcraft/ffmpegconverter/FFMpegConvert.java index 9078f65..422775e 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/Main.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/FFMpegConvert.java @@ -1,5 +1,6 @@ package net.knarcraft.ffmpegconverter; +import net.knarcraft.ffmpegconverter.config.ConfigHandler; import net.knarcraft.ffmpegconverter.converter.AnimeConverter; import net.knarcraft.ffmpegconverter.converter.AudioConverter; import net.knarcraft.ffmpegconverter.converter.Converter; @@ -15,11 +16,13 @@ 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; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.List; import java.util.Scanner; @@ -28,15 +31,23 @@ import static net.knarcraft.ffmpegconverter.utility.Parser.tokenize; /** * The main class for starting the software */ -class Main { +public class FFMpegConvert { private static final String FFPROBE_PATH = "ffprobe"; //Can be just ffprobe if it's in the path private static final String FFMPEG_PATH = "ffmpeg"; //Can be just ffmpeg if it's in the path private static 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; - public static void main(String[] args) throws IOException { + public static void main(@NotNull String[] arguments) throws IOException { + Configuration configuration = configHandler.load(); + if (configuration.containsKey("debug")) { + debug = configuration.getBoolean("debug"); + } + OutputUtil.setDebug(debug); converter = loadConverter(); + if (converter == null) { System.exit(1); return; @@ -62,9 +73,29 @@ class Main { OutputUtil.close(); } + /** + * Gets the configuration handler + * + * @return

The configuration handler

+ */ + @NotNull + public static ConfigHandler getConfigHandler() { + return configHandler; + } + + /** + * Gets whether debug mode is enabled + * + * @return

True if debug mode is enabled

+ */ + public static boolean isDebugEnabled() { + return debug; + } + /** * Asks the user which converter they want, and assigns a converter instance to the converter variable */ + @Nullable private static Converter loadConverter() { int choice = getChoice(""" Which converter do you want do use? @@ -101,7 +132,7 @@ class Main { * @param recursionSteps

The depth to recurse if a folder is given.

* @throws IOException

If conversion or writing fails.

*/ - private static void convertAllFiles(File fileOrFolder, int recursionSteps) throws IOException { + private static void convertAllFiles(@NotNull File fileOrFolder, int recursionSteps) throws IOException { if (fileOrFolder.isDirectory()) { File[] files = FileUtil.listFilesRecursive(fileOrFolder, converter.getValidFormats(), recursionSteps); if (files != null && files.length > 0) { @@ -113,7 +144,7 @@ class Main { } } else if (fileOrFolder.exists()) { String path = fileOrFolder.getPath(); - if (Arrays.stream(converter.getValidFormats()).anyMatch((format) -> format.equalsIgnoreCase( + if (converter.getValidFormats().stream().anyMatch((format) -> format.equalsIgnoreCase( path.substring(path.lastIndexOf('.') + 1)))) { converter.convert(fileOrFolder); } else { @@ -130,6 +161,7 @@ class Main { * * @return

The initialized downscale converter

*/ + @Nullable private static Converter generateDownScaleConverter() { OutputUtil.println("(New width e.x. 1920) (New height e.x. 1080)\nYour input: "); List input = readInput(3); @@ -151,6 +183,7 @@ class Main { * * @return

The initialized transcoder

*/ + @Nullable private static Converter generateMKVToMP4Transcoder() { OutputUtil.println("[Audio stream index 0-n] [Subtitle stream index 0-n] [Video stream index 0-n]\nYour input: "); List input = readInput(3); @@ -180,6 +213,7 @@ class Main { * * @return

The initialized anime converter

*/ + @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] " + @@ -224,6 +258,7 @@ class Main { * * @return

The initialized anime converter

*/ + @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] " + @@ -274,6 +309,7 @@ class Main { * @param max

The number of tokens expected.

* @return

A list of tokens.

*/ + @NotNull private static List readInput(int max) { List tokens = tokenize(READER.nextLine()); if (max < tokens.size()) { @@ -289,7 +325,8 @@ class Main { * @param prompt

The prompt shown to the user.

* @return

The non-empty choice given by the user.

*/ - private static String getChoice(String prompt) { + @NotNull + private static String getChoice(@NotNull String prompt) { OutputUtil.println(prompt); String choice = ""; while (choice.isEmpty()) { @@ -307,7 +344,7 @@ class Main { * @param max The maximum allowed value * @return The value given by the user */ - private static int getChoice(String prompt, int min, int max) { + private static int getChoice(@NotNull String prompt, int min, int max) { OutputUtil.println(prompt); int choice = Integer.MIN_VALUE; do { diff --git a/src/main/java/net/knarcraft/ffmpegconverter/config/ConfigHandler.java b/src/main/java/net/knarcraft/ffmpegconverter/config/ConfigHandler.java new file mode 100644 index 0000000..0f23de8 --- /dev/null +++ b/src/main/java/net/knarcraft/ffmpegconverter/config/ConfigHandler.java @@ -0,0 +1,92 @@ +package net.knarcraft.ffmpegconverter.config; + +import net.knarcraft.ffmpegconverter.utility.FileHelper; +import net.knarcraft.ffmpegconverter.utility.OutputUtil; +import org.apache.commons.configuration2.Configuration; +import org.apache.commons.configuration2.PropertiesConfiguration; +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 java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.util.Map; + +/** + * A handler for dealing with configurations + */ +public class ConfigHandler { + + private final File configFolder = new File("conf").getAbsoluteFile(); + private final Configurations configurations = new Configurations(); + private final File settingsFile = new File(configFolder, "config.properties"); + private FileBasedConfigurationBuilder builder = configurations.propertiesBuilder(settingsFile); + + /** + * Gets a writable configuration used for changing settings + * + * @return

A writable properties configuration

+ */ + @NotNull + public PropertiesConfiguration getWritableConfiguration() { + try { + return builder.getConfiguration(); + } catch (ConfigurationException e) { + throw new RuntimeException(e); + } + } + + /** + * Writes the writable configuration to disk + */ + public void writeConfiguration() { + OutputUtil.printDebug("Preparing to save config"); + if (!configFolder.exists() && !configFolder.mkdir()) { + throw new RuntimeException("Unable to create config folder. Make sure to run this .jar file from a " + + "writable directory!"); + } + try { + if (!settingsFile.exists() && !settingsFile.createNewFile()) { + OutputUtil.println("Failed to create configuration file."); + } + } catch (IOException e) { + OutputUtil.println("Failed to create configuration file."); + } + try { + builder.save(); + } catch (ConfigurationException e) { + throw new RuntimeException(e); + } + OutputUtil.printDebug("Saved available hardware encoder handler"); + } + + /** + * Loads the saved configuration file + * + * @return

The loaded configuration

+ */ + @NotNull + public Configuration load() throws IOException { + Configuration configuration; + if (!settingsFile.exists()) { + configuration = new PropertiesConfiguration(); + BufferedReader reader = FileHelper.getBufferedReaderForInternalFile("/conf/config.properties"); + Map entries = FileHelper.readKeyValuePairs(reader, "="); + for (Map.Entry entry : entries.entrySet()) { + configuration.setProperty(entry.getKey(), entry.getValue()); + } + } else { + try { + configuration = configurations.properties(settingsFile); + } catch (ConfigurationException e) { + throw new RuntimeException(e); + } + } + // Reload contents in the builder + builder = configurations.propertiesBuilder(settingsFile); + return configuration; + } + +} diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java index 2f3c0c6..b987af0 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java @@ -1,12 +1,13 @@ 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.handler.AvailableHardwareEncoderHandler; import net.knarcraft.ffmpegconverter.utility.FFMpegHelper; +import net.knarcraft.ffmpegconverter.utility.FileHelper; import net.knarcraft.ffmpegconverter.utility.FileUtil; import net.knarcraft.ffmpegconverter.utility.OutputUtil; -import org.apache.commons.configuration2.ex.ConfigurationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -20,12 +21,13 @@ import java.util.List; */ public abstract class AbstractConverter implements Converter { - final boolean debug = false; + final boolean debug = FFMpegConvert.isDebugEnabled(); private final String newExtension; protected String ffprobePath; protected String ffmpegPath; - protected String[] audioFormats; - protected String[] videoFormats; + protected List audioFormats; + protected List videoFormats; + protected List subtitleFormats; protected AvailableHardwareEncoderHandler encoderHandler = null; @@ -34,31 +36,31 @@ public abstract class AbstractConverter implements Converter { */ AbstractConverter(@Nullable String newExtension) { this.newExtension = newExtension; - OutputUtil.setDebug(this.debug); try { - audioFormats = FileUtil.readFileLines("audio_formats.txt"); - videoFormats = FileUtil.readFileLines("video_formats.txt"); + this.audioFormats = FileHelper.readLines(FileHelper.getBufferedReaderForInternalFile("/audio_formats.txt")); + this.videoFormats = FileHelper.readLines(FileHelper.getBufferedReaderForInternalFile("/video_formats.txt")); + this.subtitleFormats = FileHelper.readLines(FileHelper.getBufferedReaderForInternalFile("/subtitle_formats.txt")); } catch (IOException e) { - System.out.println("Unable to read audio and/or video formats from internal files."); + OutputUtil.println("Unable to read audio and/or video formats from internal files."); System.exit(1); } } @Override public void convert(@NotNull File file) throws IOException { - StreamProbeResult probeResult = FFMpegHelper.probeFile(ffprobePath, file); + StreamProbeResult probeResult = FFMpegHelper.probeFile(this.ffprobePath, file, this.subtitleFormats); if (probeResult.parsedStreams().isEmpty()) { throw new IllegalArgumentException("The file has no valid streams. Please make sure the file exists and" + " is not corrupt."); } - String outExtension = newExtension != null ? newExtension : FileUtil.getExtension(file.getName()); + String outExtension = this.newExtension != null ? this.newExtension : FileUtil.getExtension(file.getName()); String newPath = FileUtil.getNonCollidingPath(file.getParentFile(), file, outExtension); OutputUtil.println(); OutputUtil.println("Preparing to start process..."); OutputUtil.println("Converting " + file); - FFMpegCommand ffMpegCommand = generateConversionCommand(ffmpegPath, probeResult, newPath); + FFMpegCommand ffMpegCommand = generateConversionCommand(this.ffmpegPath, probeResult, newPath); // If the command is null, that means the file does not need conversion if (ffMpegCommand == null) { return; @@ -73,11 +75,7 @@ public abstract class AbstractConverter implements Converter { ProcessBuilder processBuilder = new ProcessBuilder(command); int exitCode = FFMpegHelper.runProcess(processBuilder, file.getParentFile(), "\n", true).exitCode(); if (exitCode != 0) { - try { - handleError(ffMpegCommand, file, newPath); - } catch (ConfigurationException e) { - throw new RuntimeException(e); - } + handleError(ffMpegCommand, file, newPath); } } @@ -87,11 +85,10 @@ public abstract class AbstractConverter implements Converter { * @param ffMpegCommand

The failed ffmpeg command

* @param file

The file that was to be converted

* @param newPath

The path of the output file

- * @throws IOException

If unable to produce output

- * @throws ConfigurationException

If unable

+ * @throws IOException

If unable to produce output

*/ private void handleError(@NotNull FFMpegCommand ffMpegCommand, @NotNull File file, - @NotNull String newPath) throws IOException, ConfigurationException { + @NotNull String newPath) throws IOException { File outputFile = new File(newPath); if (outputFile.exists() && !outputFile.delete()) { OutputUtil.println("Failed to remove failed output file. Please remove it manually"); @@ -118,23 +115,24 @@ public abstract class AbstractConverter implements Converter { * * @return

Available hardware encoding methods

*/ + @NotNull protected List getAvailableHardwareEncodingMethods() { - try { - if (encoderHandler == null) { - encoderHandler = AvailableHardwareEncoderHandler.load(); - if (encoderHandler.availableHardwareEncodings().isEmpty()) { - List hardwareEncoding = new ArrayList<>(FFMpegHelper.getHWAcceleration(ffmpegPath)); - hardwareEncoding.remove(0); - encoderHandler = new AvailableHardwareEncoderHandler(hardwareEncoding); - encoderHandler.save(); + if (encoderHandler == null) { + encoderHandler = AvailableHardwareEncoderHandler.load(); + if (encoderHandler.availableHardwareEncodings().isEmpty()) { + List hardwareEncoding; + try { + hardwareEncoding = new ArrayList<>(FFMpegHelper.getHWAcceleration(ffmpegPath)); + } catch (IOException e) { + throw new RuntimeException(e); } + hardwareEncoding.remove(0); + encoderHandler = new AvailableHardwareEncoderHandler(hardwareEncoding); + encoderHandler.save(); } - - return encoderHandler.availableHardwareEncodings(); - } catch (ConfigurationException | IOException exception) { - OutputUtil.println("Unable to get available hardware encoders: " + exception.getMessage()); - return new ArrayList<>(); } + + return encoderHandler.availableHardwareEncodings(); } } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java index 52c70db..872cb85 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/AnimeConverter.java @@ -26,6 +26,7 @@ import net.knarcraft.ffmpegconverter.streams.AudioStream; import net.knarcraft.ffmpegconverter.streams.SubtitleStream; import net.knarcraft.ffmpegconverter.utility.FFMpegHelper; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -69,6 +70,7 @@ public class AnimeConverter extends AbstractConverter { } @Override + @Nullable public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile) { @@ -118,7 +120,8 @@ public class AnimeConverter extends AbstractConverter { } @Override - public @NotNull String[] getValidFormats() { + @NotNull + public List getValidFormats() { return this.videoFormats; } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java index 7e10a69..e12d7f1 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/AudioConverter.java @@ -9,6 +9,7 @@ import net.knarcraft.ffmpegconverter.converter.module.mapping.NthAudioStreamModu 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; @@ -25,13 +26,14 @@ public class AudioConverter extends AbstractConverter { * @param ffmpegPath

Path/command to ffmpeg.

* @param newExtension

The extension of the new file.

*/ - public AudioConverter(String ffprobePath, String ffmpegPath, String newExtension) { + public AudioConverter(@NotNull String ffprobePath, @NotNull String ffmpegPath, @NotNull String newExtension) { super(newExtension); this.ffprobePath = ffprobePath; this.ffmpegPath = ffmpegPath; } @Override + @Nullable public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile) { FFMpegCommand command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, probeResult.parsedFiles()); @@ -50,7 +52,8 @@ public class AudioConverter extends AbstractConverter { } @Override - public String[] getValidFormats() { + @NotNull + public List getValidFormats() { return audioFormats; } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/Converter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/Converter.java index 0cb5b7f..8044ffd 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/Converter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/Converter.java @@ -3,9 +3,11 @@ package net.knarcraft.ffmpegconverter.converter; import net.knarcraft.ffmpegconverter.container.FFMpegCommand; import net.knarcraft.ffmpegconverter.container.StreamProbeResult; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; +import java.util.List; /** * This interface describes a file converter @@ -18,7 +20,7 @@ public interface Converter { * @return

A list of valid input formats

*/ @NotNull - String[] getValidFormats(); + List getValidFormats(); /** * Converts the given file @@ -36,6 +38,7 @@ public interface Converter { * @param outFile

The output file

* @return

A list of commands

*/ + @Nullable FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/DownScaleConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/DownScaleConverter.java index 9b066d6..5e79c0e 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/DownScaleConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/DownScaleConverter.java @@ -16,6 +16,7 @@ import net.knarcraft.ffmpegconverter.streams.StreamObject; import net.knarcraft.ffmpegconverter.streams.VideoStream; import net.knarcraft.ffmpegconverter.utility.FFMpegHelper; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -36,7 +37,7 @@ public class DownScaleConverter extends AbstractConverter { * @param newWidth

The new width of the video

* @param newHeight

The new height of the video

*/ - public DownScaleConverter(String ffprobePath, String ffmpegPath, int newWidth, int newHeight) { + public DownScaleConverter(@NotNull String ffprobePath, @NotNull String ffmpegPath, int newWidth, int newHeight) { super(null); this.ffprobePath = ffprobePath; this.ffmpegPath = ffmpegPath; @@ -45,6 +46,7 @@ public class DownScaleConverter extends AbstractConverter { } @Override + @Nullable public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile) { List streams = probeResult.parsedStreams(); @@ -78,7 +80,8 @@ public class DownScaleConverter extends AbstractConverter { } @Override - public String[] getValidFormats() { + @NotNull + public List getValidFormats() { return videoFormats; } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/MKVToMP4Transcoder.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/MKVToMP4Transcoder.java index 3e2cc75..6fd4d32 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/MKVToMP4Transcoder.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/MKVToMP4Transcoder.java @@ -13,6 +13,7 @@ import net.knarcraft.ffmpegconverter.converter.module.output.CopyAllModule; 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; @@ -46,11 +47,13 @@ public class MKVToMP4Transcoder extends AbstractConverter { } @Override - public String[] getValidFormats() { - return new String[]{"mkv"}; + @NotNull + public List getValidFormats() { + return List.of("mkv"); } @Override + @Nullable public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile) { FFMpegCommand command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, probeResult.parsedFiles()); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/MkvH264Converter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/MkvH264Converter.java index bba355f..82afdf4 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/MkvH264Converter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/MkvH264Converter.java @@ -15,6 +15,7 @@ import net.knarcraft.ffmpegconverter.converter.module.output.SetOutputFileModule import net.knarcraft.ffmpegconverter.streams.StreamObject; import net.knarcraft.ffmpegconverter.utility.FFMpegHelper; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -37,11 +38,13 @@ public class MkvH264Converter extends AbstractConverter { } @Override - public String[] getValidFormats() { - return new String[]{"mkv"}; + @NotNull + public List getValidFormats() { + return List.of("mkv"); } @Override + @Nullable public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile) { FFMpegCommand command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, probeResult.parsedFiles()); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/MkvH265ReducedConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/MkvH265ReducedConverter.java index 0ad7d70..0cecae6 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/MkvH265ReducedConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/MkvH265ReducedConverter.java @@ -16,6 +16,7 @@ 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; import java.util.ArrayList; import java.util.List; @@ -38,11 +39,13 @@ public class MkvH265ReducedConverter extends AbstractConverter { } @Override - public String[] getValidFormats() { - return new String[]{"mkv"}; + @NotNull + public List getValidFormats() { + return List.of("mkv"); } @Override + @Nullable public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile) { FFMpegCommand command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, probeResult.parsedFiles()); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/SubtitleEmbed.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/SubtitleEmbed.java index d28f5d6..c671214 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/SubtitleEmbed.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/SubtitleEmbed.java @@ -12,6 +12,7 @@ import net.knarcraft.ffmpegconverter.converter.module.output.MovTextModule; 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; @@ -34,11 +35,13 @@ public class SubtitleEmbed extends AbstractConverter { } @Override - public String[] getValidFormats() { - return new String[]{"mp4"}; + @NotNull + public List getValidFormats() { + return List.of("mp4"); } @Override + @Nullable public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile) { FFMpegCommand command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, probeResult.parsedFiles()); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java index 0b2dd2a..e8cfdec 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/VideoConverter.java @@ -12,6 +12,7 @@ import net.knarcraft.ffmpegconverter.converter.module.output.SetOutputFileModule import net.knarcraft.ffmpegconverter.streams.StreamObject; import net.knarcraft.ffmpegconverter.utility.FFMpegHelper; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -35,6 +36,7 @@ public class VideoConverter extends AbstractConverter { } @Override + @Nullable public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile) { FFMpegCommand command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, probeResult.parsedFiles()); @@ -57,7 +59,8 @@ public class VideoConverter extends AbstractConverter { } @Override - public String[] getValidFormats() { + @NotNull + public List getValidFormats() { return videoFormats; } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/WebAnimeConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/WebAnimeConverter.java index 541efe2..8516e40 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/WebAnimeConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/WebAnimeConverter.java @@ -21,6 +21,7 @@ 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; import java.util.ArrayList; import java.util.List; @@ -69,6 +70,7 @@ public class WebAnimeConverter extends AbstractConverter { } @Override + @Nullable public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile) { FFMpegCommand command = FFMpegHelper.getFFMpegWebVideoCommand(executable, probeResult.parsedFiles()); @@ -114,7 +116,8 @@ public class WebAnimeConverter extends AbstractConverter { } @Override - public String[] getValidFormats() { + @NotNull + public List getValidFormats() { return this.videoFormats; } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/WebVideoConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/WebVideoConverter.java index 76e1cd5..64cf077 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/converter/WebVideoConverter.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/WebVideoConverter.java @@ -14,6 +14,7 @@ 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; import java.util.ArrayList; import java.util.List; @@ -39,11 +40,13 @@ public class WebVideoConverter extends AbstractConverter { } @Override - public String[] getValidFormats() { + @NotNull + public List getValidFormats() { return videoFormats; } @Override + @Nullable public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult, @NotNull String outFile) { FFMpegCommand command = FFMpegHelper.getFFMpegWebVideoCommand(executable, probeResult.parsedFiles()); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/handler/AvailableHardwareEncoderHandler.java b/src/main/java/net/knarcraft/ffmpegconverter/handler/AvailableHardwareEncoderHandler.java index 236de07..4fcdfb6 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/handler/AvailableHardwareEncoderHandler.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/handler/AvailableHardwareEncoderHandler.java @@ -1,14 +1,12 @@ package net.knarcraft.ffmpegconverter.handler; +import net.knarcraft.ffmpegconverter.FFMpegConvert; +import net.knarcraft.ffmpegconverter.config.ConfigHandler; import net.knarcraft.ffmpegconverter.utility.OutputUtil; import org.apache.commons.configuration2.Configuration; import org.apache.commons.configuration2.PropertiesConfiguration; -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 java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -18,8 +16,7 @@ import java.util.List; */ public record AvailableHardwareEncoderHandler(@NotNull List availableHardwareEncodings) { - private static final File configFolder = new File("conf").getAbsoluteFile(); - private static final Configurations configurations = new Configurations(); + private static final ConfigHandler configHandler = FFMpegConvert.getConfigHandler(); /** * Gets all hardware encodings @@ -42,30 +39,12 @@ public record AvailableHardwareEncoderHandler(@NotNull List availableHar } /** - * Saves settings fro this available hardware encoder handler - * - * @throws ConfigurationException

If the configuration file cannot be saved

+ * Saves settings for this available hardware encoder handler */ - public void save() throws ConfigurationException { - OutputUtil.printDebug("Preparing to save config"); - if (!configFolder.exists() && !configFolder.mkdir()) { - throw new RuntimeException("Unable to create config folder. Make sure to run this .jar file from a " + - "writable directory!"); - } - File settingsFile = new File(configFolder, "config.properties"); - try { - if (!settingsFile.exists() && !settingsFile.createNewFile()) { - OutputUtil.println("Failed to create configuration file."); - } - } catch (IOException e) { - OutputUtil.println("Failed to create configuration file."); - } - - FileBasedConfigurationBuilder builder = - configurations.propertiesBuilder(settingsFile); - PropertiesConfiguration config = builder.getConfiguration(); - config.setProperty("encoder.hardware", this.availableHardwareEncodings); - builder.save(); + public void save() { + PropertiesConfiguration configuration = configHandler.getWritableConfiguration(); + configuration.setProperty("encoder.hardware", this.availableHardwareEncodings); + configHandler.writeConfiguration(); OutputUtil.printDebug("Saved available hardware encoder handler"); } @@ -73,14 +52,15 @@ public record AvailableHardwareEncoderHandler(@NotNull List availableHar * Loads saved settings for an available hardware encoder handler * * @return

The loaded available hardware encoder handler, or a new one if no data has been saved

- * @throws ConfigurationException

If the configuration file cannot be loaded

*/ - public static AvailableHardwareEncoderHandler load() throws ConfigurationException { - File settingsFile = new File(configFolder, "config.properties"); - if (!settingsFile.exists()) { - return new AvailableHardwareEncoderHandler(new ArrayList<>()); + @NotNull + public static AvailableHardwareEncoderHandler load() { + Configuration configuration; + try { + configuration = configHandler.load(); + } catch (IOException e) { + throw new RuntimeException(e); } - Configuration configuration = configurations.properties(settingsFile); List getEncodings = configuration.getList(String.class, "encoder.hardware"); return new AvailableHardwareEncoderHandler(getEncodings); } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/parser/ConverterArgument.java b/src/main/java/net/knarcraft/ffmpegconverter/parser/ConverterArgument.java index 2b5f793..f2db2a0 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/parser/ConverterArgument.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/parser/ConverterArgument.java @@ -1,6 +1,7 @@ package net.knarcraft.ffmpegconverter.parser; import net.knarcraft.ffmpegconverter.utility.ListUtil; +import org.jetbrains.annotations.NotNull; /** * A class representing a command argument @@ -20,7 +21,8 @@ public class ConverterArgument { * @param valueRequired

Whether the argument must be followed by a valid value.

* @param valueType

The type of value the argument requires.

*/ - public ConverterArgument(String name, char shorthand, boolean valueRequired, ConverterArgumentValueType valueType) { + public ConverterArgument(@NotNull String name, char shorthand, boolean valueRequired, + @NotNull ConverterArgumentValueType valueType) { this.name = name; this.shorthand = shorthand; this.valueRequired = valueRequired; @@ -60,7 +62,7 @@ public class ConverterArgument { * @param value

The value to test.

* @return

True if the argument is valid. False otherwise.

*/ - public boolean testArgumentValue(String value) { + public boolean testArgumentValue(@NotNull String value) { if (value.isEmpty()) { return !valueRequired; } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/streams/AbstractStream.java b/src/main/java/net/knarcraft/ffmpegconverter/streams/AbstractStream.java index abf94f5..f6f62a9 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/streams/AbstractStream.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/streams/AbstractStream.java @@ -36,7 +36,8 @@ public abstract class AbstractStream implements StreamObject { } @Override - public @NotNull String getCodecName() { + @NotNull + public String getCodecName() { return this.codecName; } @@ -51,7 +52,8 @@ public abstract class AbstractStream implements StreamObject { } @Override - public @NotNull String getLanguage() { + @NotNull + public String getLanguage() { return this.language; } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/utility/FFMpegHelper.java b/src/main/java/net/knarcraft/ffmpegconverter/utility/FFMpegHelper.java index 6b44fd1..426bde6 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/utility/FFMpegHelper.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/utility/FFMpegHelper.java @@ -27,7 +27,6 @@ import java.util.Map; public final class FFMpegHelper { private static final String PROBE_SPLIT_CHARACTER = "øæåÆØå"; - private static String[] subtitleFormats = null; private FFMpegHelper() { @@ -70,14 +69,16 @@ public final class FFMpegHelper { /** * Gets streams from a file * - * @param ffprobePath

The path/command to ffprobe.

- * @param file

The file to probe.

- * @return

A list of StreamObjects.

- * @throws IOException

If the process can't be readProcess.

+ * @param ffprobePath

The path/command to ffprobe

+ * @param file

The file to probe

+ * @param subtitleFormats

The extensions to accept for external subtitles

+ * @return

A list of StreamObjects

+ * @throws IOException

If the process can't be readProcess

*/ @NotNull - public static StreamProbeResult probeFile(@NotNull String ffprobePath, @NotNull File file) throws IOException { - return parseStreams(ffprobePath, probeForStreams(ffprobePath, file), file); + public static StreamProbeResult probeFile(@NotNull String ffprobePath, @NotNull File file, + @NotNull List subtitleFormats) throws IOException { + return parseStreams(ffprobePath, probeForStreams(ffprobePath, file), file, subtitleFormats); } /** @@ -223,7 +224,8 @@ public final class FFMpegHelper { * @param fileName

The filename to escape.

* @return

A filename with known special characters escaped.

*/ - public static String escapeSpecialCharactersInFileName(String fileName) { + @NotNull + public static String escapeSpecialCharactersInFileName(@NotNull String fileName) { return fileName.replaceAll("\\\\", "\\\\\\\\\\\\\\\\") .replaceAll("'", "'\\\\\\\\\\\\\''") .replaceAll("%", "\\\\\\\\\\\\%") @@ -260,7 +262,8 @@ public final class FFMpegHelper { * @return

A list of streams.

* @throws IOException

If something goes wrong while probing.

*/ - private static List probeForStreams(String ffprobePath, File file) throws IOException { + @NotNull + private static List probeForStreams(@NotNull String ffprobePath, @NotNull File file) throws IOException { FFMpegCommand probeCommand = new FFMpegCommand(ffprobePath); probeCommand.addGlobalOption("-v", "error", "-show_streams"); probeCommand.addInputFile(file.toString()); @@ -276,13 +279,15 @@ public final class FFMpegHelper { /** * Takes a list of all streams and parses each stream into one of three objects * - * @param streams

A list of all streams for the current file.

- * @param file

The file currently being converted.

+ * @param ffprobePath

The path to the ffprobe executable

+ * @param streams

A list of all streams for the current file.

+ * @param file

The file currently being converted.

+ * @param subtitleFormats

The extensions to accept for external subtitles

* @return

A list of StreamObjects.

*/ @NotNull private static StreamProbeResult parseStreams(@NotNull String ffprobePath, @NotNull List streams, - @NotNull File file) throws IOException { + @NotNull File file, @NotNull List subtitleFormats) throws IOException { List parsedStreams = new ArrayList<>(); int relativeAudioIndex = 0; int relativeVideoIndex = 0; @@ -306,7 +311,7 @@ public final class FFMpegHelper { } } StreamProbeResult probeResult = new StreamProbeResult(List.of(file), parsedStreams); - getExternalSubtitles(probeResult, ffprobePath, file.getParentFile(), file.getName()); + getExternalSubtitles(probeResult, ffprobePath, file.getParentFile(), file.getName(), subtitleFormats); return probeResult; } @@ -335,17 +340,16 @@ public final class FFMpegHelper { /** * Tries to find any external subtitles adjacent to the first input file, and appends it to the given probe result * - * @param ffprobePath

The path/command to ffprobe

- * @param directory

The directory containing the file

- * @param convertingFile

The first/main file to be converted

+ * @param streamProbeResult

The stream probe result to append to

+ * @param ffprobePath

The path/command to ffprobe

+ * @param directory

The directory containing the file

+ * @param convertingFile

The first/main file to be converted

+ * @param subtitleFormats

The extensions to accept for external subtitles

*/ private static void getExternalSubtitles(@NotNull StreamProbeResult streamProbeResult, @NotNull String ffprobePath, @NotNull File directory, - @NotNull String convertingFile) throws IOException { + @NotNull String convertingFile, @NotNull List subtitleFormats) throws IOException { //Find all files in the same directory with external subtitle formats - if (subtitleFormats == null) { - subtitleFormats = FileUtil.readFileLines("subtitle_formats.txt"); - } File[] subtitleFiles = FileUtil.listFilesRecursive(directory, subtitleFormats, 1); // TODO: Generalize this for external audio tracks @@ -382,7 +386,8 @@ public final class FFMpegHelper { * @return

The output from the readProcess.

* @throws IOException

On reader failure.

*/ - private static String readProcess(BufferedReader reader, String spacer) throws IOException { + @NotNull + private static String readProcess(@NotNull BufferedReader reader, @NotNull String spacer) throws IOException { String line; StringBuilder text = new StringBuilder(); while (reader.ready() && (line = reader.readLine()) != null && !line.isEmpty() && !line.equals("\n")) { @@ -398,6 +403,7 @@ public final class FFMpegHelper { * @return

The available hardware acceleration methods

* @throws IOException

If the process fails

*/ + @NotNull public static List getHWAcceleration(@NotNull String ffmpegPath) throws IOException { FFMpegCommand probeCommand = new FFMpegCommand(ffmpegPath); probeCommand.addGlobalOption("-v", "error", "-hwaccels"); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/utility/FileHelper.java b/src/main/java/net/knarcraft/ffmpegconverter/utility/FileHelper.java new file mode 100644 index 0000000..c35c422 --- /dev/null +++ b/src/main/java/net/knarcraft/ffmpegconverter/utility/FileHelper.java @@ -0,0 +1,178 @@ +package net.knarcraft.ffmpegconverter.utility; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A helper class for dealing with files + */ +@SuppressWarnings("unused") +public final class FileHelper { + + private FileHelper() { + + } + + /** + * Gets a buffered reader for an internal file + * + * @param file

The name of the file to get a buffered reader for (start with a '/'). The file should reside in + * the resources directory.

+ * @return

A buffered read for reading the file

+ * @throws FileNotFoundException

If unable to get an input stream for the given file

+ */ + @NotNull + public static BufferedReader getBufferedReaderForInternalFile(@NotNull String file) throws FileNotFoundException { + InputStream inputStream = getInputStreamForInternalFile(file); + if (inputStream == null) { + throw new FileNotFoundException("Unable to read the given file"); + } + return getBufferedReaderFromInputStream(inputStream); + } + + /** + * Gets an input stream from a string pointing to an internal file + * + *

This is used for getting an input stream for reading a file contained within the compiled .jar file. The file + * should be in the resources directory, and the file path should start with a forward slash ("/") character.

+ * + * @param file

The file to read

+ * @return

An input stream for the file

+ */ + @Nullable + public static InputStream getInputStreamForInternalFile(@NotNull String file) { + return FileHelper.class.getResourceAsStream(file); + } + + /** + * Gets a buffered reader from a string pointing to a file + * + * @param file

The file to read

+ * @return

A buffered reader reading the file

+ * @throws FileNotFoundException

If the given file does not exist

+ */ + @NotNull + + public static BufferedReader getBufferedReaderFromString(@NotNull String file) throws FileNotFoundException { + FileInputStream fileInputStream = new FileInputStream(file); + return getBufferedReaderFromInputStream(fileInputStream); + } + + /** + * Gets a buffered reader given an input stream + * + * @param inputStream

The input stream to read

+ * @return

A buffered reader reading the input stream

+ */ + @NotNull + public static BufferedReader getBufferedReaderFromInputStream(@NotNull InputStream inputStream) { + InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + return new BufferedReader(inputStreamReader); + } + + /** + * Gets a buffered writer from a string pointing to a file + * + * @param file

The file to write to

+ * @return

A buffered writer writing to the file

+ * @throws FileNotFoundException

If the file does not exist

+ */ + @NotNull + public static BufferedWriter getBufferedWriterFromString(@NotNull String file) throws FileNotFoundException { + FileOutputStream fileOutputStream = new FileOutputStream(file); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8); + return new BufferedWriter(outputStreamWriter); + } + + /** + * Reads key/value pairs from a buffered reader + * + * @param bufferedReader

The buffered reader to read

+ * @param separator

The separator separating a key from a value

+ * @return

A map containing the read pairs

+ * @throws IOException

If unable to read from the stream

+ */ + @NotNull + public static Map readKeyValuePairs(@NotNull BufferedReader bufferedReader, + @NotNull String separator) throws IOException { + Map readPairs = new HashMap<>(); + List lines = readLines(bufferedReader); + + for (String line : lines) { + int separatorIndex = line.indexOf(separator); + if (separatorIndex == -1) { + continue; + } + + //Read the line + String key = line.substring(0, separatorIndex); + String value = line.substring(separatorIndex + 1); + readPairs.put(key, value); + } + + return readPairs; + } + + /** + * Reads a list from a buffered reader + * + * @param bufferedReader

The buffered reader to read

+ * @return

A list of the read strings

+ * @throws IOException

If unable to read from the stream

+ */ + @NotNull + public static List readLines(@NotNull BufferedReader bufferedReader) throws IOException { + List readLines = new ArrayList<>(); + + String line = bufferedReader.readLine(); + boolean firstLine = true; + while (line != null) { + //Strip UTF BOM from the first line + if (firstLine) { + line = removeUTF8BOM(line); + firstLine = false; + } + //Split at first separator + if (line.isEmpty()) { + line = bufferedReader.readLine(); + continue; + } + + readLines.add(line); + line = bufferedReader.readLine(); + } + bufferedReader.close(); + + return readLines; + } + + /** + * Removes the UTF-8 Byte Order Mark if present + * + * @param string

The string to remove the BOM from

+ * @return

A string guaranteed without a BOM

+ */ + private static @NotNull String removeUTF8BOM(@NotNull String string) { + String UTF8_BOM = "\uFEFF"; + if (string.startsWith(UTF8_BOM)) { + string = string.substring(1); + } + return string; + } + +} diff --git a/src/main/java/net/knarcraft/ffmpegconverter/utility/FileUtil.java b/src/main/java/net/knarcraft/ffmpegconverter/utility/FileUtil.java index f8d1c13..556ef4d 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/utility/FileUtil.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/utility/FileUtil.java @@ -1,10 +1,10 @@ package net.knarcraft.ffmpegconverter.utility; -import java.io.BufferedReader; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.util.List; /** * A class which helps with file handling @@ -23,7 +23,8 @@ public final class FileUtil { * @param outExtension

The extension of the output file.

* @return

A file name with the new extension and without any collisions.

*/ - public static String getNonCollidingPath(File folder, File file, String outExtension) { + @NotNull + public static String getNonCollidingPath(@NotNull File folder, @NotNull File file, @NotNull String outExtension) { return FileUtil.getNonCollidingFilename(folder.getAbsolutePath() + File.separator + FileUtil.stripExtension(file.getName()) + "." + outExtension, outExtension); } @@ -35,7 +36,8 @@ public final class FileUtil { * @param maxRecursions

Maximum number of recursions

* @return A list of files */ - public static File[] listFilesRecursive(File folder, String[] extensions, int maxRecursions) { + @Nullable + public static File[] listFilesRecursive(@NotNull File folder, @NotNull List extensions, int maxRecursions) { //Return if the target depth has been reached if (maxRecursions == 0) { return null; @@ -43,6 +45,7 @@ public final class FileUtil { //Get a list of all files which are folders and has one of the extensions specified File[] foundFiles = folder.listFiles((file) -> file.isFile() && ListUtil.listContains(extensions, (item) -> file.getName().toLowerCase().endsWith(item))); + //Return if recursion is finished if (maxRecursions == 1) { return foundFiles; @@ -68,36 +71,6 @@ public final class FileUtil { return foundFiles; } - /** - * Reads a file's contents to a string list - * - *

The file must contain the number of lines to read in the first line.

- * - * @param fileName

The file to read.

- * @return

A string list where each element is one line of the file.

- * @throws IOException

If the file cannot be read.

- */ - public static String[] readFileLines(String fileName) throws IOException { - BufferedReader reader = new BufferedReader(new InputStreamReader(getResourceAsStream(fileName))); - int numberOfLines = Integer.parseInt(reader.readLine()); - String[] lines = new String[numberOfLines]; - for (int i = 0; i < lines.length; i++) { - lines[i] = reader.readLine(); - } - return lines; - } - - /** - * Gets a resource as an InputStream - * - * @param resourceName

The name of the resource you want to read.

- * @return

An input stream which can be used to access the resource.

- */ - private static InputStream getResourceAsStream(String resourceName) { - ClassLoader classloader = Thread.currentThread().getContextClassLoader(); - return classloader.getResourceAsStream(resourceName); - } - /** * Adds parentheses with an integer if the output file already exists * @@ -105,7 +78,8 @@ public final class FileUtil { * @param extension

The extension of the target file.

* @return

A filename guaranteed not to collide with other files.

*/ - private static String getNonCollidingFilename(String targetPath, String extension) { + @NotNull + private static String getNonCollidingFilename(@NotNull String targetPath, @NotNull String extension) { File newFile = new File(targetPath); String fileName = stripExtension(targetPath).replaceAll("\\([0-9]+\\)$", ""); int i = 1; @@ -121,7 +95,8 @@ public final class FileUtil { * @param file

The filename to check

* @return

The file's extension

*/ - public static String getExtension(String file) { + @NotNull + public static String getExtension(@NotNull String file) { if (file.contains(".")) { return file.substring(file.lastIndexOf('.') + 1); } else { @@ -135,7 +110,8 @@ public final class FileUtil { * @param file

A filename.

* @return

A filename without its extension.

*/ - public static String stripExtension(String file) { + @NotNull + public static String stripExtension(@NotNull String file) { return file.substring(0, file.lastIndexOf('.')); } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/utility/ListUtil.java b/src/main/java/net/knarcraft/ffmpegconverter/utility/ListUtil.java index f8cba56..8854270 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/utility/ListUtil.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/utility/ListUtil.java @@ -1,5 +1,7 @@ package net.knarcraft.ffmpegconverter.utility; +import org.jetbrains.annotations.NotNull; + import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; @@ -22,7 +24,8 @@ public final class ListUtil { * @param

The type of the two lists.

* @return

A new array containing all elements from the two arrays.

*/ - static T[] concatenate(T[] listA, T[] listB) { + @NotNull + public static T[] concatenate(@NotNull T[] listA, @NotNull T[] listB) { int listALength = listA.length; int listBLength = listB.length; @SuppressWarnings("unchecked") @@ -40,7 +43,7 @@ public final class ListUtil { * @param

The type of the list.

* @return

A new list containing all matching elements.

*/ - static List getMatching(List list, Predicate predicate) { + public static List getMatching(List list, Predicate predicate) { List matching = new ArrayList<>(list); matching.removeIf(predicate.negate()); return matching; @@ -54,7 +57,7 @@ public final class ListUtil { * @param Anything which can be stored in a list * @return True if at least one element fulfills the predicate */ - static boolean listContains(T[] list, Predicate predicate) { + public static boolean listContains(@NotNull List list, @NotNull Predicate predicate) { for (T item : list) { if (predicate.test(item)) { return true; @@ -69,7 +72,8 @@ public final class ListUtil { * @param string

A string which may include commas.

* @return

A string list.

*/ - public static String[] getListFromCommaSeparatedString(String string) { + @NotNull + public static String[] getListFromCommaSeparatedString(@NotNull String string) { String[] result; if (string.contains(",")) { result = string.split(","); diff --git a/src/main/java/net/knarcraft/ffmpegconverter/utility/OutputUtil.java b/src/main/java/net/knarcraft/ffmpegconverter/utility/OutputUtil.java index 5964e85..b142229 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/utility/OutputUtil.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/utility/OutputUtil.java @@ -1,5 +1,7 @@ package net.knarcraft.ffmpegconverter.utility; +import org.jetbrains.annotations.NotNull; + import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; @@ -30,7 +32,7 @@ public final class OutputUtil { * * @param input

The text to print.

*/ - public static void println(String input) { + public static void println(@NotNull String input) { if (!input.isEmpty()) { try { writer.write(input); @@ -46,7 +48,7 @@ public final class OutputUtil { * * @param input

The string to print.

*/ - public static void print(String input) { + public static void print(@NotNull String input) { try { writer.write(input); writer.flush(); @@ -82,7 +84,7 @@ public final class OutputUtil { * * @param message

The debug message to show.

*/ - public static void printDebug(String message) { + public static void printDebug(@NotNull String message) { if (debug) { println(message); } diff --git a/src/main/java/net/knarcraft/ffmpegconverter/utility/Parser.java b/src/main/java/net/knarcraft/ffmpegconverter/utility/Parser.java index a361299..88f73e4 100644 --- a/src/main/java/net/knarcraft/ffmpegconverter/utility/Parser.java +++ b/src/main/java/net/knarcraft/ffmpegconverter/utility/Parser.java @@ -1,6 +1,7 @@ package net.knarcraft.ffmpegconverter.utility; import net.knarcraft.ffmpegconverter.parser.ConverterArgument; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.HashMap; @@ -23,7 +24,8 @@ public final class Parser { * @param validArguments

All arguments which are considered valid.

* @return

A map with all parsed arguments.

*/ - static Map parse(String input, List validArguments) { + @NotNull + static Map parse(@NotNull String input, @NotNull List validArguments) { return parse(tokenize(input), validArguments); } @@ -34,7 +36,8 @@ public final class Parser { * @param validArguments

A list of arguments which are considered valid.

* @return

A map with all parsed arguments.

*/ - private static Map parse(List tokens, List validArguments) { + @NotNull + private static Map parse(@NotNull List tokens, @NotNull List validArguments) { Map parsedArguments = new HashMap<>(); while (!tokens.isEmpty()) { @@ -50,7 +53,8 @@ public final class Parser { * @param converterArguments

A list of all the valid arguments in existence.

* @param parsedArguments

The map to store the parsed argument to.

*/ - private static void parseArgument(List tokens, List converterArguments, Map parsedArguments) { + private static void parseArgument(@NotNull List tokens, @NotNull List converterArguments, + @NotNull Map parsedArguments) { String currentToken = tokens.remove(0); List foundArguments; @@ -79,7 +83,8 @@ public final class Parser { * @param foundArgument

The found argument to store.

* @param parsedArguments

The map to store parsed arguments to.

*/ - private static void storeArgumentValue(List tokens, ConverterArgument foundArgument, Map parsedArguments) { + private static void storeArgumentValue(@NotNull List tokens, @NotNull ConverterArgument foundArgument, + @NotNull Map parsedArguments) { String argumentValue; if (tokens.isEmpty()) { argumentValue = ""; @@ -114,7 +119,8 @@ public final class Parser { * @param input

A string.

* @return

A list of tokens.

*/ - public static List tokenize(String input) { + @NotNull + public static List tokenize(@NotNull String input) { List tokens = new ArrayList<>(); boolean startedQuote = false; StringBuilder currentToken = new StringBuilder(); @@ -157,8 +163,8 @@ public final class Parser { * @param index

The index of the read character.

* @param tokens

The list of processed tokens.

*/ - private static void tokenizeNormalCharacter(StringBuilder currentToken, char character, int inputLength, int index, - List tokens) { + private static void tokenizeNormalCharacter(@NotNull StringBuilder currentToken, char character, int inputLength, + int index, @NotNull List tokens) { currentToken.append(character); if (index == inputLength - 1) { tokens.add(currentToken.toString()); @@ -173,7 +179,8 @@ public final class Parser { * @param tokens

The list of processed tokens.

* @return

True if the token is finished.

*/ - private static boolean tokenizeSpace(boolean startedQuote, StringBuilder currentToken, List tokens) { + private static boolean tokenizeSpace(boolean startedQuote, @NotNull StringBuilder currentToken, + @NotNull List tokens) { if (!startedQuote) { //If not inside "", a space marks the end of a parameter if (isNotEmpty(currentToken)) { @@ -192,7 +199,7 @@ public final class Parser { * @param builder

The string builder to check.

* @return

True if the string builder is non empty.

*/ - private static boolean isNotEmpty(StringBuilder builder) { + private static boolean isNotEmpty(@NotNull StringBuilder builder) { return !builder.toString().trim().isEmpty(); } diff --git a/src/main/resources/audio_formats.txt b/src/main/resources/audio_formats.txt index 4279515..badc645 100644 --- a/src/main/resources/audio_formats.txt +++ b/src/main/resources/audio_formats.txt @@ -1,4 +1,3 @@ -39 3gp aa aac diff --git a/src/main/resources/conf/config.properties b/src/main/resources/conf/config.properties new file mode 100644 index 0000000..aab92eb --- /dev/null +++ b/src/main/resources/conf/config.properties @@ -0,0 +1 @@ +debug=false \ No newline at end of file diff --git a/src/main/resources/subtitle_formats.txt b/src/main/resources/subtitle_formats.txt index 4dfd314..54de0e5 100644 --- a/src/main/resources/subtitle_formats.txt +++ b/src/main/resources/subtitle_formats.txt @@ -1,4 +1,3 @@ -5 idx sub srt diff --git a/src/main/resources/video_formats.txt b/src/main/resources/video_formats.txt index 4dc9a08..074e447 100644 --- a/src/main/resources/video_formats.txt +++ b/src/main/resources/video_formats.txt @@ -1,4 +1,3 @@ -31 avi mpg mpeg diff --git a/src/test/java/net/knarcraft/ffmpegconverter/utility/ListUtilTest.java b/src/test/java/net/knarcraft/ffmpegconverter/utility/ListUtilTest.java index df97a1b..14c684d 100644 --- a/src/test/java/net/knarcraft/ffmpegconverter/utility/ListUtilTest.java +++ b/src/test/java/net/knarcraft/ffmpegconverter/utility/ListUtilTest.java @@ -13,7 +13,7 @@ import static org.junit.Assert.assertFalse; public class ListUtilTest { private static List matchesList; - private static Integer[] containsList; + private static List containsList; @BeforeClass public static void setUp() { @@ -28,7 +28,7 @@ public class ListUtilTest { matchesList.add(19); matchesList.add(21); matchesList.add(23); - containsList = new Integer[]{1, 3, 5, 7, 234, 23, 45}; + containsList = List.of(1, 3, 5, 7, 234, 23, 45); } @Test