diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java index 2e7b5c7d5..e94295724 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java @@ -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 { diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java index 64349409d..4f5465751 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java @@ -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() diff --git a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java index 971ee2759..1be020eae 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java @@ -38,11 +38,17 @@ public final class ListHelper { // Audio format in order of quality. 0=lowest quality, n=highest quality private static final List 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 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 HIGH_RESOLUTION_LIST = Set.of("1440p", "2160p"); + // Audio track types in order of priotity. 0=lowest, n=highest + private static final List 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 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 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 collectedStreams = new HashMap<>(); - final Comparator 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 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 trackCmp = - getAudioTrackComparator(preferredLanguageOrEnglish, preferDescriptiveAudio); - // Filter unknown audio tracks if there are multiple tracks java.util.stream.Stream 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 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 audioStreams) { - return getAudioIndexByHighestRank(format, audioStreams, - // The "reversed()" is important -> Compares ascending (first = highest rank) - getAudioStreamComparator(AUDIO_FORMAT_EFFICIENCY_RANKING).reversed()); - } - - private static Comparator getAudioStreamComparator( - final List 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 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 getAudioStreamFormatComparator( + @NonNull final Context context) { + final boolean limitDataUsage = isLimitingDataUsage(context); + final List formatRanking = limitDataUsage + ? AUDIO_FORMAT_EFFICIENCY_RANKING : AUDIO_FORMAT_QUALITY_RANKING; + + Comparator 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 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 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)); } } diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 9ed34bf26..36dcf2b33 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -192,6 +192,8 @@ @string/audio_webm_key + prefer_original_audio + prefer_descriptive_audio last_resize_mode @@ -260,7 +262,6 @@ show_next_video show_description show_meta_info - prefer_descriptive_audio stream_info_selected_tab show_hold_to_append content_language diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 91d618852..475585879 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -94,6 +94,8 @@ Turn off to hide video description and additional information Show meta info Turn off to hide meta info boxes with additional information about the stream creator, stream content or a search request + Prefer original audio + Select the original audio track regardless of the language Prefer descriptive audio Select an audio track with descriptions for visually impaired people if available Image cache wiped diff --git a/app/src/main/res/xml/content_settings.xml b/app/src/main/res/xml/content_settings.xml index 684b9e558..fddb966c8 100644 --- a/app/src/main/res/xml/content_settings.xml +++ b/app/src/main/res/xml/content_settings.xml @@ -114,14 +114,6 @@ app:singleLineTitle="false" app:iconSpaceReserved="false" /> - - + + + +