package net.knarcraft.ffmpegconverter; import net.knarcraft.ffmpegconverter.config.Configuration; import net.knarcraft.ffmpegconverter.converter.AnimeConverter; import net.knarcraft.ffmpegconverter.converter.AudioConverter; import net.knarcraft.ffmpegconverter.converter.AudioToVorbisConverter; import net.knarcraft.ffmpegconverter.converter.Converter; import net.knarcraft.ffmpegconverter.converter.DownScaleConverter; import net.knarcraft.ffmpegconverter.converter.LetterboxCropper; import net.knarcraft.ffmpegconverter.converter.MKVToMP4Transcoder; import net.knarcraft.ffmpegconverter.converter.MkvH264Converter; import net.knarcraft.ffmpegconverter.converter.MkvH265ReducedConverter; import net.knarcraft.ffmpegconverter.converter.StreamOrderConverter; import net.knarcraft.ffmpegconverter.converter.SubtitleEmbed; import net.knarcraft.ffmpegconverter.converter.VideoConverter; 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.OutputUtil; 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.List; import java.util.Scanner; import static net.knarcraft.ffmpegconverter.utility.Parser.tokenize; /** * The main class for starting the software */ 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 Configuration configuration = new Configuration(); public static void main(@NotNull String[] arguments) throws IOException { OutputUtil.setDebug(configuration.isDebugEnabled()); converter = loadConverter(); if (converter == null) { System.exit(1); return; } List input; do { OutputUtil.println(" [Recursions]:"); input = readInput(2); } while (input.isEmpty()); File folder = new File(input.get(0)); int recursionSteps = 1; if (input.size() > 1) { try { recursionSteps = Integer.parseInt(input.get(1)); } catch (NumberFormatException e) { OutputUtil.println("Recursion steps is invalid and will be ignored."); } } convertAllFiles(folder, recursionSteps); OutputUtil.close(); } /** * Gets the configuration handler * * @return

The configuration handler

*/ @NotNull public static Configuration getConfiguration() { return configuration; } /** * 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? 1. Anime to web mp4 2. Audio converter 3. Video converter 4. Web video converter 5. MKV to h264 converter 6. MKV to h265 reduced converter 7. MKV to MP4 transcoder 8. DownScaleConverter 9. mp4 Subtitle Embed 10. Anime to h265 all streams 11. Stream reorder 12. Letterbox cropper 13. Video's Audio to vorbis converter""", 1, 13); return switch (choice) { case 1 -> generateWebAnimeConverter(); case 2 -> new AudioConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("")); case 3 -> new VideoConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("")); case 4 -> new WebVideoConverter(FFPROBE_PATH, FFMPEG_PATH, getChoice("")); case 5 -> new MkvH264Converter(FFPROBE_PATH, FFMPEG_PATH); case 6 -> new MkvH265ReducedConverter(FFPROBE_PATH, FFMPEG_PATH); case 7 -> generateMKVToMP4Transcoder(); case 8 -> generateDownScaleConverter(); case 9 -> new SubtitleEmbed(FFPROBE_PATH, FFMPEG_PATH); case 10 -> generateAnimeConverter(); case 11 -> generateStreamOrderConverter(); case 12 -> new LetterboxCropper(FFPROBE_PATH, FFMPEG_PATH); case 13 -> new AudioToVorbisConverter(FFPROBE_PATH, FFMPEG_PATH); default -> null; }; } /** * Converts the file(s) as specified * * @param fileOrFolder

A file or a folder.

* @param recursionSteps

The depth to recurse if a folder is given.

* @throws IOException

If conversion or writing fails.

*/ 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) { for (File file : files) { converter.convert(file); } } else { OutputUtil.println("No valid files found in folder."); } } else if (fileOrFolder.exists()) { String path = fileOrFolder.getPath(); if (converter.getValidFormats().stream().anyMatch((format) -> format.equalsIgnoreCase( path.substring(path.lastIndexOf('.') + 1)))) { converter.convert(fileOrFolder); } else { OutputUtil.println("The specified file " + fileOrFolder.getAbsolutePath() + " is not supported for " + "the selected converter."); } } else { OutputUtil.println("Path " + fileOrFolder.getAbsolutePath() + " does not point to any file or folder."); } } /** * Initializes and returns the downscale converter * * @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); int newWidth; int newHeight; try { newWidth = Integer.parseInt(input.get(0)); newHeight = Integer.parseInt(input.get(1)); return new DownScaleConverter(FFPROBE_PATH, FFMPEG_PATH, newWidth, newHeight); } catch (NumberFormatException exception) { OutputUtil.println("Width or height is not a number"); return null; } } /** * Initializes and returns the MKV to MP4 transcoder * * @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); int audioStreamIndex = -1; int subtitleStreamIndex = -1; int videoStreamIndex = -1; try { if (!input.isEmpty()) { audioStreamIndex = Integer.parseInt(input.get(0)); } if (input.size() > 1) { subtitleStreamIndex = Integer.parseInt(input.get(1)); } if (input.size() > 2) { videoStreamIndex = Integer.parseInt(input.get(2)); } return new MKVToMP4Transcoder(FFPROBE_PATH, FFMPEG_PATH, audioStreamIndex, subtitleStreamIndex, videoStreamIndex); } catch (NumberFormatException exception) { OutputUtil.println("Audio, Subtitle or Video stream index is not a number"); return null; } } /** * Initializes and returns a stream reorder converter * * @return

The initialized stream order converter

*/ @Nullable private static Converter generateStreamOrderConverter() { OutputUtil.println("Note that a * in the sort order matches any stream not yet matched, and 0 matches " + "undefined language streams. The subtitle name filter will include any streams with titles " + "containing the filter, but if it contains RegEx expressions, a RegEx match will be performed " + "instead.\nYour input: "); OutputUtil.println("