feat: add prefer original option, improve audio stream ordering

This commit is contained in:
ThetaDev 2023-03-19 20:40:27 +01:00
parent 87a88e4df7
commit 7aed2eed8a
7 changed files with 113 additions and 123 deletions

View file

@ -52,18 +52,8 @@ public class AudioPlaybackResolver implements PlaybackResolver {
final MediaItemTag tag;
if (!audioStreams.isEmpty()) {
int audioIndex = 0;
if (audioTrack != null) {
for (int i = 0; i < audioStreams.size(); i++) {
final AudioStream audioStream = audioStreams.get(i);
if (audioStream.getAudioTrackId() != null
&& audioStream.getAudioTrackId().equals(audioTrack)) {
audioIndex = i;
break;
}
}
}
final int audioIndex =
ListHelper.getAudioFormatIndex(context, audioStreams, audioTrack);
stream = getStreamForIndex(audioIndex, audioStreams);
tag = StreamInfoTag.of(info, audioStreams, audioIndex);
} else {

View file

@ -90,18 +90,8 @@ public class VideoPlaybackResolver implements PlaybackResolver {
getPlaybackQuality());
}
int audioIndex = 0;
if (audioTrack != null) {
for (int i = 0; i < audioStreamsList.size(); i++) {
final AudioStream stream = audioStreamsList.get(i);
if (stream.getAudioTrackId() != null
&& stream.getAudioTrackId().equals(audioTrack)) {
audioIndex = i;
break;
}
}
}
final int audioIndex =
ListHelper.getAudioFormatIndex(context, audioStreamsList, audioTrack);
final MediaItemTag tag =
StreamInfoTag.of(info, videoStreamsList, videoIndex, audioStreamsList, audioIndex);
@Nullable final VideoStream video = tag.getMaybeQuality()

View file

@ -38,11 +38,17 @@ public final class ListHelper {
// Audio format in order of quality. 0=lowest quality, n=highest quality
private static final List<MediaFormat> AUDIO_FORMAT_QUALITY_RANKING =
List.of(MediaFormat.MP3, MediaFormat.WEBMA, MediaFormat.M4A);
// Audio format in order of efficiency. 0=most efficient, n=least efficient
// Audio format in order of efficiency. 0=least efficient, n=most efficient
private static final List<MediaFormat> AUDIO_FORMAT_EFFICIENCY_RANKING =
List.of(MediaFormat.WEBMA, MediaFormat.M4A, MediaFormat.MP3);
List.of(MediaFormat.MP3, MediaFormat.M4A, MediaFormat.WEBMA);
// Use a Set for better performance
private static final Set<String> HIGH_RESOLUTION_LIST = Set.of("1440p", "2160p");
// Audio track types in order of priotity. 0=lowest, n=highest
private static final List<AudioTrackType> AUDIO_TRACK_TYPE_RANKING =
List.of(AudioTrackType.DESCRIPTIVE, AudioTrackType.DUBBED, AudioTrackType.ORIGINAL);
// Audio track types in order of priotity when descriptive audio is preferred.
private static final List<AudioTrackType> AUDIO_TRACK_TYPE_RANKING_DESCRIPTIVE =
List.of(AudioTrackType.ORIGINAL, AudioTrackType.DUBBED, AudioTrackType.DESCRIPTIVE);
private ListHelper() {
}
@ -104,13 +110,23 @@ public final class ListHelper {
final MediaFormat defaultFormat = getDefaultFormat(context,
R.string.default_audio_format_key, R.string.default_audio_format_value);
// If the user has chosen to limit resolution to conserve mobile data
// usage then we should also limit our audio usage.
if (isLimitingDataUsage(context)) {
return getMostCompactAudioIndex(defaultFormat, audioStreams);
} else {
return getHighestQualityAudioIndex(defaultFormat, audioStreams);
return getAudioIndexByHighestRank(defaultFormat, audioStreams,
getAudioStreamComparator(context));
}
public static int getAudioFormatIndex(final Context context,
final List<AudioStream> audioStreams,
@Nullable final String trackId) {
if (trackId != null) {
for (int i = 0; i < audioStreams.size(); i++) {
final AudioStream s = audioStreams.get(i);
if (s.getAudioTrackId() != null
&& s.getAudioTrackId().equals(trackId)) {
return i;
}
}
}
return getDefaultAudioFormat(context, audioStreams);
}
/**
@ -193,7 +209,7 @@ public final class ListHelper {
* Filter the list of audio streams and return a list with the preferred stream for
* each audio track. Streams are sorted with the preferred language in the first position.
*
* @param context the context to search for the track to give preference
* @param context the context to search for the track to give preference
* @param audioStreams the list of audio streams
* @return the sorted, filtered list
*/
@ -206,15 +222,7 @@ public final class ListHelper {
final HashMap<String, AudioStream> collectedStreams = new HashMap<>();
final Comparator<AudioStream> cmp;
if (isLimitingDataUsage(context)) {
cmp = getAudioStreamComparator(AUDIO_FORMAT_EFFICIENCY_RANKING);
} else {
cmp = getAudioStreamComparator(AUDIO_FORMAT_QUALITY_RANKING);
}
final String preferredLanguage = Localization.getPreferredLocale(context).getISO3Language();
boolean hasPreferredLanguage = false;
final Comparator<AudioStream> cmp = getAudioStreamFormatComparator(context);
for (final AudioStream stream : audioStreams) {
if (stream.getDeliveryMethod() == DeliveryMethod.TORRENT) {
@ -226,33 +234,18 @@ public final class ListHelper {
final AudioStream presentStream = collectedStreams.get(trackId);
if (presentStream == null || cmp.compare(stream, presentStream) > 0) {
collectedStreams.put(trackId, stream);
if (stream.getAudioLocale() != null
&& stream.getAudioLocale().getISO3Language().equals(preferredLanguage)) {
hasPreferredLanguage = true;
}
}
}
// Fall back to English if the preferred language was not found
final String preferredLanguageOrEnglish =
hasPreferredLanguage ? preferredLanguage : Locale.ENGLISH.getISO3Language();
final SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(context);
final boolean preferDescriptiveAudio =
preferences.getBoolean(context.getString(R.string.prefer_descriptive_audio_key),
false);
final Comparator<AudioStream> trackCmp =
getAudioTrackComparator(preferredLanguageOrEnglish, preferDescriptiveAudio);
// Filter unknown audio tracks if there are multiple tracks
java.util.stream.Stream<AudioStream> cs = collectedStreams.values().stream();
if (collectedStreams.size() > 1) {
cs = cs.filter(s -> s.getAudioTrackId() != null);
}
// Sort collected streams
return cs.sorted(trackCmp).collect(Collectors.toList());
// Sort collected streams by name
return cs.sorted(Comparator.comparing(audioStream ->
Localization.audioTrackName(context, audioStream))).collect(Collectors.toList());
}
/*//////////////////////////////////////////////////////////////////////////
@ -420,42 +413,6 @@ public final class ListHelper {
return videoStreams;
}
/**
* Get the audio from the list with the highest quality.
* Format will be ignored if it yields no results.
*
* @param format The target format type or null if it doesn't matter
* @param audioStreams List of audio streams
* @return Index of audio stream that produces the most compact results or -1 if not found
*/
static int getHighestQualityAudioIndex(@Nullable final MediaFormat format,
@Nullable final List<AudioStream> audioStreams) {
return getAudioIndexByHighestRank(format, audioStreams,
// Compares descending (last = highest rank)
getAudioStreamComparator(AUDIO_FORMAT_QUALITY_RANKING));
}
/**
* Get the audio from the list with the lowest bitrate and most efficient format.
* Format will be ignored if it yields no results.
*
* @param format The target format type or null if it doesn't matter
* @param audioStreams List of audio streams
* @return Index of audio stream that produces the most compact results or -1 if not found
*/
static int getMostCompactAudioIndex(@Nullable final MediaFormat format,
@Nullable final List<AudioStream> audioStreams) {
return getAudioIndexByHighestRank(format, audioStreams,
// The "reversed()" is important -> Compares ascending (first = highest rank)
getAudioStreamComparator(AUDIO_FORMAT_EFFICIENCY_RANKING).reversed());
}
private static Comparator<AudioStream> getAudioStreamComparator(
final List<MediaFormat> formatRanking) {
return Comparator.nullsLast(Comparator.comparingInt(AudioStream::getAverageBitrate))
.thenComparingInt(stream -> formatRanking.indexOf(stream.getFormat()));
}
/**
* Get the audio-stream from the list with the highest rank, depending on the comparator.
* Format will be ignored if it yields no results.
@ -674,23 +631,65 @@ public final class ListHelper {
return manager.isActiveNetworkMetered();
}
private static Comparator<AudioStream> getAudioTrackComparator(
final String preferredLanguage, final boolean preferDescriptiveAudio) {
return Comparator.comparing(AudioStream::getAudioLocale, (o1, o2) -> Boolean.compare(
o1 == null || !o1.getISO3Language().equals(preferredLanguage),
o2 == null || !o2.getISO3Language().equals(preferredLanguage))
).thenComparing(s -> s.getAudioTrackType() == AudioTrackType.DESCRIPTIVE, (o1, o2) ->
Boolean.compare(o1 ^ preferDescriptiveAudio, o2 ^ preferDescriptiveAudio)
).thenComparing(AudioStream::getAudioTrackName, (o1, o2) -> {
if (o1 != null) {
if (o2 != null) {
return o1.compareTo(o2);
} else {
return -1;
}
} else {
return 1;
}
});
/**
* Get a {@link Comparator} to compare {@link AudioStream}s by their format and bitrate.
*
* @param context App context
* @return Comparator
*/
private static Comparator<AudioStream> getAudioStreamFormatComparator(
@NonNull final Context context) {
final boolean limitDataUsage = isLimitingDataUsage(context);
final List<MediaFormat> formatRanking = limitDataUsage
? AUDIO_FORMAT_EFFICIENCY_RANKING : AUDIO_FORMAT_QUALITY_RANKING;
Comparator<AudioStream> bitrateComparator =
Comparator.comparingInt(AudioStream::getAverageBitrate);
if (limitDataUsage) {
bitrateComparator = bitrateComparator.reversed();
}
return bitrateComparator.thenComparingInt(
stream -> formatRanking.indexOf(stream.getFormat()));
}
/**
* Get a {@link Comparator} to compare {@link AudioStream}s by their language, format
* and bitrate.
*
* @param context App context
* @return Comparator
*/
private static Comparator<AudioStream> getAudioStreamComparator(
@NonNull final Context context) {
final SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(context);
final boolean preferOriginalAudio =
preferences.getBoolean(context.getString(R.string.prefer_original_audio_key),
false);
final boolean preferDescriptiveAudio =
preferences.getBoolean(context.getString(R.string.prefer_descriptive_audio_key),
false);
final String preferredLanguage = Localization.getPreferredLocale(context).getISO3Language();
final List<AudioTrackType> trackTypeRanking = preferDescriptiveAudio
? AUDIO_TRACK_TYPE_RANKING_DESCRIPTIVE : AUDIO_TRACK_TYPE_RANKING;
return Comparator.comparing(AudioStream::getAudioTrackType, (o1, o2) -> {
if (preferOriginalAudio) {
return Boolean.compare(
o1 == AudioTrackType.ORIGINAL, o2 == AudioTrackType.ORIGINAL);
}
return 0;
}).thenComparing(AudioStream::getAudioLocale,
Comparator.nullsFirst(Comparator.comparing(
locale -> locale.getISO3Language().equals(preferredLanguage))))
.thenComparing(AudioStream::getAudioTrackType,
Comparator.nullsLast(Comparator.comparingInt(trackTypeRanking::indexOf)))
.thenComparing(AudioStream::getAudioLocale,
Comparator.nullsFirst(Comparator.comparing(
locale -> locale.getISO3Language().equals(
Locale.ENGLISH.getISO3Language()))))
.thenComparing(getAudioStreamFormatComparator(context));
}
}

View file

@ -192,6 +192,8 @@
<item>@string/audio_webm_key</item>
</string-array>
<string name="prefer_original_audio_key">prefer_original_audio</string>
<string name="prefer_descriptive_audio_key">prefer_descriptive_audio</string>
<string name="last_resize_mode">last_resize_mode</string>
<!-- DEBUG ONLY -->
@ -260,7 +262,6 @@
<string name="show_next_video_key">show_next_video</string>
<string name="show_description_key">show_description</string>
<string name="show_meta_info_key">show_meta_info</string>
<string name="prefer_descriptive_audio_key">prefer_descriptive_audio</string>
<string name="stream_info_selected_tab_key">stream_info_selected_tab</string>
<string name="show_hold_to_append_key">show_hold_to_append</string>
<string name="content_language_key">content_language</string>

View file

@ -94,6 +94,8 @@
<string name="show_description_summary">Turn off to hide video description and additional information</string>
<string name="show_meta_info_title">Show meta info</string>
<string name="show_meta_info_summary">Turn off to hide meta info boxes with additional information about the stream creator, stream content or a search request</string>
<string name="prefer_original_audio_title">Prefer original audio</string>
<string name="prefer_original_audio_summary">Select the original audio track regardless of the language</string>
<string name="prefer_descriptive_audio_title">Prefer descriptive audio</string>
<string name="prefer_descriptive_audio_summary">Select an audio track with descriptions for visually impaired people if available</string>
<string name="thumbnail_cache_wipe_complete_notice">Image cache wiped</string>

View file

@ -114,14 +114,6 @@
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/prefer_descriptive_audio_key"
android:summary="@string/prefer_descriptive_audio_summary"
android:title="@string/prefer_descriptive_audio_title"
app:singleLineTitle="false"
app:iconSpaceReserved="false"/>
<Preference
android:key="@string/import_data"
android:summary="@string/import_data_summary"

View file

@ -61,6 +61,22 @@
app:iconSpaceReserved="false"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/prefer_original_audio_key"
android:summary="@string/prefer_original_audio_summary"
android:title="@string/prefer_original_audio_title"
app:singleLineTitle="false"
app:iconSpaceReserved="false"/>
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/prefer_descriptive_audio_key"
android:summary="@string/prefer_descriptive_audio_summary"
android:title="@string/prefer_descriptive_audio_title"
app:singleLineTitle="false"
app:iconSpaceReserved="false"/>
<ListPreference
android:defaultValue="@string/progressive_load_interval_default_value"
android:entries="@array/progressive_load_interval_descriptions"