diff --git a/src/main/java/net/knarcraft/ffmpegconverter/FFMpegConvert.java b/src/main/java/net/knarcraft/ffmpegconverter/FFMpegConvert.java
index ac27f46..eab5108 100644
--- a/src/main/java/net/knarcraft/ffmpegconverter/FFMpegConvert.java
+++ b/src/main/java/net/knarcraft/ffmpegconverter/FFMpegConvert.java
@@ -8,6 +8,7 @@ import net.knarcraft.ffmpegconverter.converter.DownScaleConverter;
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;
@@ -94,7 +95,8 @@ public class FFMpegConvert {
7. MKV to MP4 transcoder
8. DownScaleConverter
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) {
case 1 -> generateWebAnimeConverter();
@@ -107,6 +109,7 @@ public class FFMpegConvert {
case 8 -> generateDownScaleConverter();
case 9 -> new SubtitleEmbed(FFPROBE_PATH, FFMPEG_PATH);
case 10 -> generateAnimeConverter();
+ case 11 -> generateStreamOrderConverter();
default -> null;
};
}
@@ -194,6 +197,34 @@ public class FFMpegConvert {
}
}
+ /**
+ * 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(" [Subtitle name filter]");
+ List 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
*
diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/StreamOrderConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/StreamOrderConverter.java
new file mode 100644
index 0000000..9d39b40
--- /dev/null
+++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/StreamOrderConverter.java
@@ -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 audioLanguages;
+ private final List subtitleLanguages;
+ private final MinimalSubtitlePreference subtitlePreference;
+ private final String subtitleNameFilter;
+
+ /**
+ * Instantiates a new anime converter
+ *
+ * @param ffprobePath Path/command to ffprobe.
+ * @param ffmpegPath Path/command to ffmpeg.
+ * @param audioLanguages The language priority for languages
+ * @param subtitleLanguages The language priority for subtitles
+ */
+ 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 modules = new ArrayList<>();
+ if (this.debug) {
+ modules.add(new DebugModule(90, 120));
+ }
+ modules.add(new FastStartModule());
+
+ //Map all video streams
+ List videoStreams = probeResult.getVideoStreams();
+ modules.add(new MapAllModule<>(videoStreams));
+ setOutputIndexes(videoStreams);
+
+ //Get the first audio stream in accordance with chosen languages
+ StreamSorter audioSorter = new AudioLanguageSorter(this.audioLanguages)
+ .append(new SpecialAudioSorter(MinimalSubtitlePreference.REJECT));
+ List 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 subtitleSorter = new SubtitleTitleSorter(
+ List.of(this.subtitleNameFilter.split(",")))
+ .append(new SubtitleLanguageSorter(this.subtitleLanguages))
+ .append(new MinimalSubtitleSorter(this.subtitlePreference));
+ List 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 getValidFormats() {
+ return this.videoFormats;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/sorter/SubtitleTitleSorter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/sorter/SubtitleTitleSorter.java
index b3837bd..ef3d151 100644
--- a/src/main/java/net/knarcraft/ffmpegconverter/converter/sorter/SubtitleTitleSorter.java
+++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/sorter/SubtitleTitleSorter.java
@@ -1,7 +1,7 @@
package net.knarcraft.ffmpegconverter.converter.sorter;
-import net.knarcraft.ffmpegconverter.FFMpegConvert;
import net.knarcraft.ffmpegconverter.streams.SubtitleStream;
+import net.knarcraft.ffmpegconverter.utility.OutputUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -45,17 +45,15 @@ public class SubtitleTitleSorter extends AbstractSorter {
boolean isRegEx = isValidRegularExpression(filter) && hasSpecialRegexCharacters(filter);
- if (FFMpegConvert.getConfiguration().isDebugEnabled()) {
- System.out.println("Filtering subtitles by filter " + filter + ". RegEx is " + isRegEx);
- }
+ OutputUtil.printDebug("Filtering subtitles by filter " + filter + ". RegEx is " + isRegEx);
for (SubtitleStream subtitleStream : input) {
String title = subtitleStream.getTitle().trim().toLowerCase();
// Add the subtitle if the filter matches, and it hasn't been added already
- boolean matches = filter.trim().equals("*") || (isRegEx && title.matches(filter)) ||
- (!isRegEx && title.contains(filter));
+ boolean matches = filter.trim().equals("*") || (isRegEx && title.matches(filter.toLowerCase())) ||
+ (!isRegEx && title.contains(filter.toLowerCase()));
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);
}
}
diff --git a/src/main/java/net/knarcraft/ffmpegconverter/streams/SubtitleStream.java b/src/main/java/net/knarcraft/ffmpegconverter/streams/SubtitleStream.java
index 81adee0..f516dbc 100644
--- a/src/main/java/net/knarcraft/ffmpegconverter/streams/SubtitleStream.java
+++ b/src/main/java/net/knarcraft/ffmpegconverter/streams/SubtitleStream.java
@@ -50,17 +50,8 @@ public class SubtitleStream extends AbstractStream implements StreamObject {
* @return True if the subtitle translates everything.
*/
private boolean checkIfIsFullSubtitle() {
- String titleLowercase = getTitle().toLowerCase().trim();
- 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.*") &&
- !titleLowercase.matches("signs?") &&
- !titleLowercase.matches("songs?") &&
- !titleLowercase.matches(".*signs only.*") &&
- !titleLowercase.matches(".* signs .*") &&
- !titleLowercase.matches("signs@.*") &&
- !titleLowercase.matches("signs -.*");
+ return !getTitle().toLowerCase().trim().matches(".*si(ng|gn)s?[ &/a-z+]+(songs?)?([$ @-]+.*)?|" +
+ ".*songs?[ &/a-z]+(si(gn|ng)s?)?([$ @-]+.*)?|.*forced.*|.*s&s.*|songs($| |-|@)+|si(gn|ng)s($| |-|@)+");
}
@Override