Adds a new converter for purely re-ordering and filtering streams
	
		
			
	
		
	
	
		
	
		
			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:
		@@ -8,6 +8,7 @@ import net.knarcraft.ffmpegconverter.converter.DownScaleConverter;
 | 
				
			|||||||
import net.knarcraft.ffmpegconverter.converter.MKVToMP4Transcoder;
 | 
					import net.knarcraft.ffmpegconverter.converter.MKVToMP4Transcoder;
 | 
				
			||||||
import net.knarcraft.ffmpegconverter.converter.MkvH264Converter;
 | 
					import net.knarcraft.ffmpegconverter.converter.MkvH264Converter;
 | 
				
			||||||
import net.knarcraft.ffmpegconverter.converter.MkvH265ReducedConverter;
 | 
					import net.knarcraft.ffmpegconverter.converter.MkvH265ReducedConverter;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.StreamOrderConverter;
 | 
				
			||||||
import net.knarcraft.ffmpegconverter.converter.SubtitleEmbed;
 | 
					import net.knarcraft.ffmpegconverter.converter.SubtitleEmbed;
 | 
				
			||||||
import net.knarcraft.ffmpegconverter.converter.VideoConverter;
 | 
					import net.knarcraft.ffmpegconverter.converter.VideoConverter;
 | 
				
			||||||
import net.knarcraft.ffmpegconverter.converter.WebAnimeConverter;
 | 
					import net.knarcraft.ffmpegconverter.converter.WebAnimeConverter;
 | 
				
			||||||
@@ -94,7 +95,8 @@ public class FFMpegConvert {
 | 
				
			|||||||
                7. MKV to MP4 transcoder
 | 
					                7. MKV to MP4 transcoder
 | 
				
			||||||
                8. DownScaleConverter
 | 
					                8. DownScaleConverter
 | 
				
			||||||
                9. mp4 Subtitle Embed
 | 
					                9. mp4 Subtitle Embed
 | 
				
			||||||
                10. Anime to h265 all streams""", 1, 10);
 | 
					                10. Anime to h265 all streams
 | 
				
			||||||
 | 
					                11. Stream reorder""", 1, 11);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return switch (choice) {
 | 
					        return switch (choice) {
 | 
				
			||||||
            case 1 -> generateWebAnimeConverter();
 | 
					            case 1 -> generateWebAnimeConverter();
 | 
				
			||||||
@@ -107,6 +109,7 @@ public class FFMpegConvert {
 | 
				
			|||||||
            case 8 -> generateDownScaleConverter();
 | 
					            case 8 -> generateDownScaleConverter();
 | 
				
			||||||
            case 9 -> new SubtitleEmbed(FFPROBE_PATH, FFMPEG_PATH);
 | 
					            case 9 -> new SubtitleEmbed(FFPROBE_PATH, FFMPEG_PATH);
 | 
				
			||||||
            case 10 -> generateAnimeConverter();
 | 
					            case 10 -> generateAnimeConverter();
 | 
				
			||||||
 | 
					            case 11 -> generateStreamOrderConverter();
 | 
				
			||||||
            default -> null;
 | 
					            default -> null;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -194,6 +197,34 @@ public class FFMpegConvert {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initializes and returns a stream reorder converter
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return <p>The initialized stream order converter</p>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @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("<Audio sort order> <Subtitle sort order> [Subtitle name filter]");
 | 
				
			||||||
 | 
					        List<String> input = readInput(3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (input.size() < 2) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String audioLanguages = input.get(0);
 | 
				
			||||||
 | 
					        String subtitleLanguages = input.get(1);
 | 
				
			||||||
 | 
					        String subtitleNameFilter = "*";
 | 
				
			||||||
 | 
					        if (input.size() > 2) {
 | 
				
			||||||
 | 
					            subtitleNameFilter = input.get(2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new StreamOrderConverter(FFPROBE_PATH, FFMPEG_PATH, audioLanguages, subtitleLanguages, subtitleNameFilter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Initializes and returns the anime converter
 | 
					     * Initializes and returns the anime converter
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					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;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.module.DebugModule;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.module.ModuleExecutor;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.module.mapping.MapAllModule;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.module.mapping.SetDefaultStreamModule;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.module.output.CopyAudioModule;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.module.output.CopySubtitlesModule;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.module.output.CopyVideoModule;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.module.output.FastStartModule;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.module.output.SetOutputFileModule;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.module.output.SetStreamLanguageModule;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.sorter.AudioLanguageSorter;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.sorter.MinimalSubtitleSorter;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.sorter.SpecialAudioSorter;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.sorter.StreamSorter;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.sorter.SubtitleLanguageSorter;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.converter.sorter.SubtitleTitleSorter;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.property.MinimalSubtitlePreference;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.streams.AudioStream;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.streams.SubtitleStream;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.streams.VideoStream;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.utility.FFMpegHelper;
 | 
				
			||||||
 | 
					import 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A converter for converting anime, keeping all streams
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class StreamOrderConverter extends AbstractConverter {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final List<String> audioLanguages;
 | 
				
			||||||
 | 
					    private final List<String> subtitleLanguages;
 | 
				
			||||||
 | 
					    private final MinimalSubtitlePreference subtitlePreference;
 | 
				
			||||||
 | 
					    private final String subtitleNameFilter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Instantiates a new anime converter
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ffprobePath       <p>Path/command to ffprobe.</p>
 | 
				
			||||||
 | 
					     * @param ffmpegPath        <p>Path/command to ffmpeg.</p>
 | 
				
			||||||
 | 
					     * @param audioLanguages    <p>The language priority for languages</p>
 | 
				
			||||||
 | 
					     * @param subtitleLanguages <p>The language priority for subtitles</p>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public StreamOrderConverter(@NotNull String ffprobePath, @NotNull String ffmpegPath,
 | 
				
			||||||
 | 
					                                @NotNull String audioLanguages, @NotNull String subtitleLanguages,
 | 
				
			||||||
 | 
					                                @NotNull String subtitleNameFilter) {
 | 
				
			||||||
 | 
					        super("mkv");
 | 
				
			||||||
 | 
					        Configuration configuration = FFMpegConvert.getConfiguration();
 | 
				
			||||||
 | 
					        this.ffprobePath = ffprobePath;
 | 
				
			||||||
 | 
					        this.ffmpegPath = ffmpegPath;
 | 
				
			||||||
 | 
					        this.audioLanguages = List.of(audioLanguages.split(","));
 | 
				
			||||||
 | 
					        this.subtitleLanguages = List.of(subtitleLanguages.split(","));
 | 
				
			||||||
 | 
					        this.subtitlePreference = configuration.getMinimalSubtitlePreference();
 | 
				
			||||||
 | 
					        this.subtitleNameFilter = subtitleNameFilter;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    public FFMpegCommand generateConversionCommand(@NotNull String executable,
 | 
				
			||||||
 | 
					                                                   @NotNull StreamProbeResult probeResult,
 | 
				
			||||||
 | 
					                                                   @NotNull String outFile) {
 | 
				
			||||||
 | 
					        FFMpegCommand command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, probeResult.parsedFiles());
 | 
				
			||||||
 | 
					        List<ConverterModule> modules = new ArrayList<>();
 | 
				
			||||||
 | 
					        if (this.debug) {
 | 
				
			||||||
 | 
					            modules.add(new DebugModule(90, 120));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        modules.add(new FastStartModule());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //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 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(
 | 
				
			||||||
 | 
					                List.of(this.subtitleNameFilter.split(",")))
 | 
				
			||||||
 | 
					                .append(new SubtitleLanguageSorter(this.subtitleLanguages))
 | 
				
			||||||
 | 
					                .append(new MinimalSubtitleSorter(this.subtitlePreference));
 | 
				
			||||||
 | 
					        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()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (AudioStream audioStream : sortedAudio) {
 | 
				
			||||||
 | 
					            modules.add(new CopyAudioModule(audioStream));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        modules.add(new CopySubtitlesModule());
 | 
				
			||||||
 | 
					        modules.add(new CopyVideoModule());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Map all attached streams, such as fonts and covers
 | 
				
			||||||
 | 
					        modules.add(new MapAllModule<>(probeResult.getOtherStreams()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        modules.add(new SetOutputFileModule(outFile));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        new ModuleExecutor(command, modules).execute();
 | 
				
			||||||
 | 
					        return command;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    @NotNull
 | 
				
			||||||
 | 
					    public List<String> getValidFormats() {
 | 
				
			||||||
 | 
					        return this.videoFormats;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
package net.knarcraft.ffmpegconverter.converter.sorter;
 | 
					package net.knarcraft.ffmpegconverter.converter.sorter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import net.knarcraft.ffmpegconverter.FFMpegConvert;
 | 
					 | 
				
			||||||
import net.knarcraft.ffmpegconverter.streams.SubtitleStream;
 | 
					import net.knarcraft.ffmpegconverter.streams.SubtitleStream;
 | 
				
			||||||
 | 
					import net.knarcraft.ffmpegconverter.utility.OutputUtil;
 | 
				
			||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
@@ -45,17 +45,15 @@ public class SubtitleTitleSorter extends AbstractSorter<SubtitleStream> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            boolean isRegEx = isValidRegularExpression(filter) && hasSpecialRegexCharacters(filter);
 | 
					            boolean isRegEx = isValidRegularExpression(filter) && hasSpecialRegexCharacters(filter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (FFMpegConvert.getConfiguration().isDebugEnabled()) {
 | 
					            OutputUtil.printDebug("Filtering subtitles by filter " + filter + ". RegEx is " + isRegEx);
 | 
				
			||||||
                System.out.println("Filtering subtitles by filter " + filter + ". RegEx is " + isRegEx);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (SubtitleStream subtitleStream : input) {
 | 
					            for (SubtitleStream subtitleStream : input) {
 | 
				
			||||||
                String title = subtitleStream.getTitle().trim().toLowerCase();
 | 
					                String title = subtitleStream.getTitle().trim().toLowerCase();
 | 
				
			||||||
                // Add the subtitle if the filter matches, and it hasn't been added already
 | 
					                // Add the subtitle if the filter matches, and it hasn't been added already
 | 
				
			||||||
                boolean matches = filter.trim().equals("*") || (isRegEx && title.matches(filter)) ||
 | 
					                boolean matches = filter.trim().equals("*") || (isRegEx && title.matches(filter.toLowerCase())) ||
 | 
				
			||||||
                        (!isRegEx && title.contains(filter));
 | 
					                        (!isRegEx && title.contains(filter.toLowerCase()));
 | 
				
			||||||
                if (matches && !output.contains(subtitleStream)) {
 | 
					                if (matches && !output.contains(subtitleStream)) {
 | 
				
			||||||
                    System.out.println("Subtitle stream with title " + title + " matches");
 | 
					                    OutputUtil.printDebug("Subtitle stream with title " + title + " matches");
 | 
				
			||||||
                    output.add(subtitleStream);
 | 
					                    output.add(subtitleStream);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,17 +50,8 @@ public class SubtitleStream extends AbstractStream implements StreamObject {
 | 
				
			|||||||
     * @return <p>True if the subtitle translates everything.</p>
 | 
					     * @return <p>True if the subtitle translates everything.</p>
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private boolean checkIfIsFullSubtitle() {
 | 
					    private boolean checkIfIsFullSubtitle() {
 | 
				
			||||||
        String titleLowercase = getTitle().toLowerCase().trim();
 | 
					        return !getTitle().toLowerCase().trim().matches(".*si(ng|gn)s?[ &/a-z+]+(songs?)?([$ @-]+.*)?|" +
 | 
				
			||||||
        return !titleLowercase.matches(".*si(ng|gn)s?[ &/a-z+]+songs?.*") &&
 | 
					                ".*songs?[ &/a-z]+(si(gn|ng)s?)?([$ @-]+.*)?|.*forced.*|.*s&s.*|songs($| |-|@)+|si(gn|ng)s($| |-|@)+");
 | 
				
			||||||
                !titleLowercase.matches(".*songs?[ &/a-z]+si(gn|ng)s?.*") &&
 | 
					 | 
				
			||||||
                !titleLowercase.matches(".*forced.*") &&
 | 
					 | 
				
			||||||
                !titleLowercase.matches(".*s&s.*") &&
 | 
					 | 
				
			||||||
                !titleLowercase.matches("signs?") &&
 | 
					 | 
				
			||||||
                !titleLowercase.matches("songs?") &&
 | 
					 | 
				
			||||||
                !titleLowercase.matches(".*signs only.*") &&
 | 
					 | 
				
			||||||
                !titleLowercase.matches(".* signs .*") &&
 | 
					 | 
				
			||||||
                !titleLowercase.matches("signs@.*") &&
 | 
					 | 
				
			||||||
                !titleLowercase.matches("signs -.*");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user