Adds filtering for audio description tracks
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good

This commit is contained in:
Kristian Knarvik 2024-05-05 03:50:07 +02:00
parent d46f12e690
commit dae93b9f81
6 changed files with 102 additions and 11 deletions

View File

@ -20,6 +20,7 @@ import net.knarcraft.ffmpegconverter.converter.module.output.SetVideoCodecModule
import net.knarcraft.ffmpegconverter.converter.sorter.AudioLanguageSorter; import net.knarcraft.ffmpegconverter.converter.sorter.AudioLanguageSorter;
import net.knarcraft.ffmpegconverter.converter.sorter.ForcedFirstSorter; import net.knarcraft.ffmpegconverter.converter.sorter.ForcedFirstSorter;
import net.knarcraft.ffmpegconverter.converter.sorter.MinimalSubtitleSorter; 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.StreamSorter;
import net.knarcraft.ffmpegconverter.converter.sorter.SubtitleLanguageSorter; import net.knarcraft.ffmpegconverter.converter.sorter.SubtitleLanguageSorter;
import net.knarcraft.ffmpegconverter.converter.sorter.SubtitleTitleSorter; import net.knarcraft.ffmpegconverter.converter.sorter.SubtitleTitleSorter;
@ -95,8 +96,9 @@ public class AnimeConverter extends AbstractConverter {
modules.add(new MapAllModule<>(probeResult.getVideoStreams())); modules.add(new MapAllModule<>(probeResult.getVideoStreams()));
//Get the first audio stream in accordance with chosen languages //Get the first audio stream in accordance with chosen languages
StreamSorter<AudioStream> audioSorter = new AudioLanguageSorter(this.audioLanguages).append( StreamSorter<AudioStream> audioSorter = new AudioLanguageSorter(this.audioLanguages)
new ForcedFirstSorter<>(this.forcedAudioIndex)); .append(new ForcedFirstSorter<>(this.forcedAudioIndex))
.append(new SpecialAudioSorter(MinimalSubtitlePreference.REJECT));
List<AudioStream> sortedAudio = audioSorter.chainSort(probeResult.getAudioStreams()); List<AudioStream> sortedAudio = audioSorter.chainSort(probeResult.getAudioStreams());
modules.add(new MapAllModule<>(sortedAudio)); modules.add(new MapAllModule<>(sortedAudio));
modules.add(new SetDefaultStreamModule<>(sortedAudio, 0)); modules.add(new SetDefaultStreamModule<>(sortedAudio, 0));

View File

@ -36,7 +36,7 @@ public class BurnSubtitleModule implements ConverterModule {
FFMpegHelper.mapStream(command, videoStream); FFMpegHelper.mapStream(command, videoStream);
} }
if (subtitleStream.getIsImageSubtitle()) { if (subtitleStream.isImageSubtitle()) {
command.addOutputFileOption("-filter_complex", command.addOutputFileOption("-filter_complex",
String.format("[%d:%d]scale=width=%d:height=%d,crop=w=%d:h=%d:x=0:y=out_h[sub];[%d:%d][sub]overlay", String.format("[%d:%d]scale=width=%d:height=%d,crop=w=%d:h=%d:x=0:y=out_h[sub];[%d:%d][sub]overlay",
subtitleStream.getInputIndex(), subtitleStream.getAbsoluteIndex(), videoStream.getWidth(), subtitleStream.getInputIndex(), subtitleStream.getAbsoluteIndex(), videoStream.getWidth(),

View File

@ -29,7 +29,7 @@ public class MinimalSubtitleSorter extends AbstractSorter<SubtitleStream> {
List<SubtitleStream> fullSubtitles = new ArrayList<>(); List<SubtitleStream> fullSubtitles = new ArrayList<>();
List<SubtitleStream> minimalSubtitles = new ArrayList<>(); List<SubtitleStream> minimalSubtitles = new ArrayList<>();
for (SubtitleStream subtitleStream : input) { for (SubtitleStream subtitleStream : input) {
if (subtitleStream.getIsFullSubtitle()) { if (subtitleStream.isFullSubtitle()) {
fullSubtitles.add(subtitleStream); fullSubtitles.add(subtitleStream);
} else { } else {
minimalSubtitles.add(subtitleStream); minimalSubtitles.add(subtitleStream);

View File

@ -0,0 +1,67 @@
package net.knarcraft.ffmpegconverter.converter.sorter;
import net.knarcraft.ffmpegconverter.property.MinimalSubtitlePreference;
import net.knarcraft.ffmpegconverter.streams.AudioStream;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* A sorter for sorting/filtering subtitles by a minimal subtitle preference
*/
public class SpecialAudioSorter extends AbstractSorter<AudioStream> {
private final MinimalSubtitlePreference minimalSubtitlePreference;
/**
* Instantiates a new special audio preference sorter
*
* @param minimalSubtitlePreference <p>The minimal subtitle preference sort/filter by</p>
*/
public SpecialAudioSorter(@NotNull MinimalSubtitlePreference minimalSubtitlePreference) {
this.minimalSubtitlePreference = minimalSubtitlePreference;
}
@Override
public @NotNull List<AudioStream> sort(@NotNull List<AudioStream> input) {
// Split all subtitles into full and minimal
List<AudioStream> normalAudio = new ArrayList<>();
List<AudioStream> specialAudio = new ArrayList<>();
for (AudioStream audioStream : input) {
if (audioStream.isSpecialAudio()) {
specialAudio.add(audioStream);
} else {
normalAudio.add(audioStream);
}
}
// Sort/filter subtitles based on full and minimal
switch (this.minimalSubtitlePreference) {
case REJECT -> {
// Only return full subtitles
return normalAudio;
}
case REQUIRE -> {
// Only return minimal subtitles
return specialAudio;
}
case NO_PREFERENCE -> {
// Don't change order
return input;
}
case PREFER -> {
// Sort minimal subtitles first, and full subtitles last
specialAudio.addAll(normalAudio);
return specialAudio;
}
case AVOID -> {
// Sort full subtitles first, and minimal subtitles last
normalAudio.addAll(specialAudio);
return normalAudio;
}
default -> throw new IllegalStateException("Unknown enum value encountered");
}
}
}

View File

@ -11,6 +11,7 @@ import java.util.Map;
public class AudioStream extends AbstractStream implements StreamObject { public class AudioStream extends AbstractStream implements StreamObject {
private final int channels; private final int channels;
private final boolean isSpecialAudio;
/** /**
* Instantiates a new audio stream * Instantiates a new audio stream
@ -22,6 +23,16 @@ public class AudioStream extends AbstractStream implements StreamObject {
public AudioStream(@NotNull Map<StreamTag, String> streamInfo, int inputIndex, int relativeIndex) { public AudioStream(@NotNull Map<StreamTag, String> streamInfo, int inputIndex, int relativeIndex) {
super(streamInfo, inputIndex, relativeIndex); super(streamInfo, inputIndex, relativeIndex);
this.channels = ValueParsingHelper.parseInt(streamInfo.get(StreamTag.CHANNELS), 0); this.channels = ValueParsingHelper.parseInt(streamInfo.get(StreamTag.CHANNELS), 0);
this.isSpecialAudio = checkIfIsSpecialAudio();
}
/**
* Gets whether this audio stream is a special audio stream such as audio description or music only
*
* @return <p>True if this audio stream is a special audio stream</p>
*/
public boolean isSpecialAudio() {
return this.isSpecialAudio;
} }
/** /**
@ -38,4 +49,14 @@ public class AudioStream extends AbstractStream implements StreamObject {
return 'a'; return 'a';
} }
/**
* Checks whether this audio stream is a special audio stream
*
* @return <p>True if this is a special audio stream</p>
*/
private boolean checkIfIsSpecialAudio() {
String titleLowercase = getTitle().toLowerCase().trim();
return titleLowercase.matches(".*audio description.*");
}
} }

View File

@ -9,8 +9,8 @@ import java.util.Map;
*/ */
public class SubtitleStream extends AbstractStream implements StreamObject { public class SubtitleStream extends AbstractStream implements StreamObject {
final private boolean isFullSubtitle; private final boolean isFullSubtitle;
final private boolean isImageSubtitle; private final boolean isImageSubtitle;
/** /**
* Instantiates a new subtitle stream * Instantiates a new subtitle stream
@ -21,7 +21,7 @@ public class SubtitleStream extends AbstractStream implements StreamObject {
*/ */
public SubtitleStream(@NotNull Map<StreamTag, String> streamInfo, int inputIndex, int relativeIndex) { public SubtitleStream(@NotNull Map<StreamTag, String> streamInfo, int inputIndex, int relativeIndex) {
super(streamInfo, inputIndex, relativeIndex); super(streamInfo, inputIndex, relativeIndex);
this.isFullSubtitle = isFullSubtitle(); this.isFullSubtitle = checkIfIsFullSubtitle();
this.isImageSubtitle = codecName != null && this.isImageSubtitle = codecName != null &&
(getCodecName().equals("hdmv_pgs_subtitle") || getCodecName().equals("dvd_subtitle")); (getCodecName().equals("hdmv_pgs_subtitle") || getCodecName().equals("dvd_subtitle"));
} }
@ -31,7 +31,7 @@ public class SubtitleStream extends AbstractStream implements StreamObject {
* *
* @return <p>Whether the subtitles is an image subtitle.</p> * @return <p>Whether the subtitles is an image subtitle.</p>
*/ */
public boolean getIsImageSubtitle() { public boolean isImageSubtitle() {
return this.isImageSubtitle; return this.isImageSubtitle;
} }
@ -40,7 +40,7 @@ public class SubtitleStream extends AbstractStream implements StreamObject {
* *
* @return <p>Whether the subtitle is a full subtitle.</p> * @return <p>Whether the subtitle is a full subtitle.</p>
*/ */
public boolean getIsFullSubtitle() { public boolean isFullSubtitle() {
return this.isFullSubtitle; return this.isFullSubtitle;
} }
@ -49,7 +49,7 @@ 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 isFullSubtitle() { private boolean checkIfIsFullSubtitle() {
String titleLowercase = getTitle().toLowerCase().trim(); String titleLowercase = getTitle().toLowerCase().trim();
return !titleLowercase.matches(".*si(ng|gn)s?[ &/a-z]+songs?.*") && return !titleLowercase.matches(".*si(ng|gn)s?[ &/a-z]+songs?.*") &&
!titleLowercase.matches(".*songs?[ &/a-z]+si(gn|ng)s?.*") && !titleLowercase.matches(".*songs?[ &/a-z]+si(gn|ng)s?.*") &&
@ -59,7 +59,8 @@ public class SubtitleStream extends AbstractStream implements StreamObject {
!titleLowercase.matches("songs?") && !titleLowercase.matches("songs?") &&
!titleLowercase.matches(".*signs only.*") && !titleLowercase.matches(".*signs only.*") &&
!titleLowercase.matches(".* signs .*") && !titleLowercase.matches(".* signs .*") &&
!titleLowercase.matches("signs@.*"); !titleLowercase.matches("signs@.*") &&
!titleLowercase.matches("signs -.*");
} }
@Override @Override