From 724eac9168356eac08b9389ae57c9af7ef9cfe1b Mon Sep 17 00:00:00 2001 From: karyogamy Date: Mon, 4 Apr 2022 20:17:33 -0400 Subject: [PATCH 1/3] fixed: player caption auto-selection not reflected in gui. fixed: player caption selection skipping on multiple language variants. --- .../org/schabi/newpipe/player/Player.java | 89 +++++++++---------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 2c8be5d78..bb3997a81 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -62,7 +62,6 @@ import static org.schabi.newpipe.player.helper.PlayerHelper.savePlaybackParamete import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex; import static org.schabi.newpipe.util.ListHelper.getResolutionIndex; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; -import static org.schabi.newpipe.util.Localization.containsCaseInsensitive; import static java.util.concurrent.TimeUnit.MILLISECONDS; import android.animation.Animator; @@ -130,6 +129,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player.PositionInfo; @@ -138,7 +138,6 @@ import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.TracksInfo; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.TrackGroup; -import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; @@ -2530,7 +2529,7 @@ public final class Player implements Log.d(TAG, "ExoPlayer - onTracksChanged(), " + "track group size = " + tracksInfo.getTrackGroupInfos().size()); } - onTextTracksChanged(); + onTextTracksChanged(tracksInfo); } @Override @@ -3516,17 +3515,7 @@ public final class Player implements return; } captionPopupMenu.getMenu().removeGroup(POPUP_MENU_ID_CAPTION); - - final String userPreferredLanguage = - prefs.getString(context.getString(R.string.caption_user_set_key), null); - /* - * only search for autogenerated cc as fallback - * if "(auto-generated)" was not already selected - * we are only looking for "(" instead of "(auto-generated)" to hopefully get all - * internationalized variants such as "(automatisch-erzeugt)" and so on - */ - boolean searchForAutogenerated = userPreferredLanguage != null - && !userPreferredLanguage.contains("("); + captionPopupMenu.setOnDismissListener(this); // Add option for turning off caption final MenuItem captionOffItem = captionPopupMenu.getMenu().add(POPUP_MENU_ID_CAPTION, @@ -3549,6 +3538,9 @@ public final class Player implements captionItem.setOnMenuItemClickListener(menuItem -> { final int textRendererIndex = getCaptionRendererIndex(); if (textRendererIndex != RENDERER_UNAVAILABLE) { + // DefaultTrackSelector will select for tracks with similar language names + // if a track of userPreferredLanguage is not found + // This means (auto-generated) will be resolved automatically. trackSelector.setParameters(trackSelector.buildUponParameters() .setPreferredTextLanguage(captionLanguage) .setRendererDisabled(textRendererIndex, false)); @@ -3557,22 +3549,23 @@ public final class Player implements } return true; }); - // apply caption language from previous user preference - if (userPreferredLanguage != null - && (captionLanguage.equals(userPreferredLanguage) - || (searchForAutogenerated && captionLanguage.startsWith(userPreferredLanguage)) - || (userPreferredLanguage.contains("(") && captionLanguage.startsWith( - userPreferredLanguage.substring(0, userPreferredLanguage.indexOf('(')))))) { - final int textRendererIndex = getCaptionRendererIndex(); - if (textRendererIndex != RENDERER_UNAVAILABLE) { - trackSelector.setParameters(trackSelector.buildUponParameters() - .setPreferredTextLanguage(captionLanguage) - .setRendererDisabled(textRendererIndex, false)); - } - searchForAutogenerated = false; - } } - captionPopupMenu.setOnDismissListener(this); + + // apply caption language from previous user preference + final List selectedPreferredLanguages = + trackSelector.getParameters().preferredTextLanguages; + final String userPreferredLanguage = + prefs.getString(context.getString(R.string.caption_user_set_key), null); + final int textRendererIndex = getCaptionRendererIndex(); + + if (userPreferredLanguage != null + && availableLanguages.contains(userPreferredLanguage) + && !selectedPreferredLanguages.contains(userPreferredLanguage) + && textRendererIndex != RENDERER_UNAVAILABLE) { + trackSelector.setParameters(trackSelector.buildUponParameters() + .setPreferredTextLanguage(userPreferredLanguage) + .setRendererDisabled(textRendererIndex, false)); + } } /** @@ -3668,41 +3661,45 @@ public final class Player implements binding.subtitleView.setStyle(captionStyle); } - private void onTextTracksChanged() { - final int textRenderer = getCaptionRendererIndex(); - + private void onTextTracksChanged(@NonNull final TracksInfo currentTrackInfo) { if (binding == null) { return; } + if (trackSelector.getCurrentMappedTrackInfo() == null - || textRenderer == RENDERER_UNAVAILABLE) { + || !currentTrackInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_TEXT)) { binding.captionTextView.setVisibility(View.GONE); return; } - final TrackGroupArray textTracks = trackSelector.getCurrentMappedTrackInfo() - .getTrackGroups(textRenderer); - // Extract all loaded languages - final List availableLanguages = new ArrayList<>(textTracks.length); - for (int i = 0; i < textTracks.length; i++) { - final TrackGroup textTrack = textTracks.get(i); + final List textTracks = currentTrackInfo + .getTrackGroupInfos() + .stream() + .filter(trackGroupInfo -> C.TRACK_TYPE_TEXT == trackGroupInfo.getTrackType()) + .collect(Collectors.toList()); + final List availableLanguages = new ArrayList<>(textTracks.size()); + for (int i = 0; i < textTracks.size(); i++) { + final TrackGroup textTrack = textTracks.get(i).getTrackGroup(); if (textTrack.length > 0) { availableLanguages.add(textTrack.getFormat(0).language); } } - // Normalize mismatching language strings - final String preferredLanguage = trackSelector.getParameters() - .preferredTextLanguages.stream().findFirst().orElse(null); + // Find selected text track + final Optional selectedTracks = textTracks.stream() + .filter(TracksInfo.TrackGroupInfo::isSelected) + .filter(info -> info.getTrackGroup().length >= 1) + .map(info -> info.getTrackGroup().getFormat(0)) + .findFirst(); + // Build UI buildCaptionMenu(availableLanguages); - if (trackSelector.getParameters().getRendererDisabled(textRenderer) - || preferredLanguage == null || (!availableLanguages.contains(preferredLanguage) - && !containsCaseInsensitive(availableLanguages, preferredLanguage))) { + if (trackSelector.getParameters().getRendererDisabled(getCaptionRendererIndex()) + || !selectedTracks.isPresent()) { binding.captionTextView.setText(R.string.caption_none); } else { - binding.captionTextView.setText(preferredLanguage); + binding.captionTextView.setText(selectedTracks.get().language); } binding.captionTextView.setVisibility( availableLanguages.isEmpty() ? View.GONE : View.VISIBLE); From 55fc3fc177497f484b2aa13440e731a3f6d28243 Mon Sep 17 00:00:00 2001 From: karyogamy Date: Fri, 8 Apr 2022 18:21:30 -0400 Subject: [PATCH 2/3] added: caption language stem utility to support language variant conversion between videos. --- .../java/org/schabi/newpipe/player/Player.java | 7 +++++-- .../newpipe/player/helper/PlayerHelper.java | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index bb3997a81..9f1fda981 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -46,6 +46,7 @@ import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZ import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE; import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP; import static org.schabi.newpipe.player.helper.PlayerHelper.buildCloseOverlayLayoutParams; +import static org.schabi.newpipe.player.helper.PlayerHelper.captionLanguageStemOf; import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed; import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimizeOnExitAction; import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimumVideoHeight; @@ -3542,7 +3543,8 @@ public final class Player implements // if a track of userPreferredLanguage is not found // This means (auto-generated) will be resolved automatically. trackSelector.setParameters(trackSelector.buildUponParameters() - .setPreferredTextLanguage(captionLanguage) + .setPreferredTextLanguages(captionLanguage, + captionLanguageStemOf(captionLanguage)) .setRendererDisabled(textRendererIndex, false)); prefs.edit().putString(context.getString(R.string.caption_user_set_key), captionLanguage).apply(); @@ -3563,7 +3565,8 @@ public final class Player implements && !selectedPreferredLanguages.contains(userPreferredLanguage) && textRendererIndex != RENDERER_UNAVAILABLE) { trackSelector.setParameters(trackSelector.buildUponParameters() - .setPreferredTextLanguage(userPreferredLanguage) + .setPreferredTextLanguages(userPreferredLanguage, + captionLanguageStemOf(userPreferredLanguage)) .setRendererDisabled(textRendererIndex, false)); } } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index 6a7c27bdc..e21b21d35 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -144,6 +144,21 @@ public final class PlayerHelper { ? " (" + context.getString(R.string.caption_auto_generated) + ")" : ""); } + @NonNull + public static String captionLanguageStemOf(@NonNull final String language) { + if (!language.contains("(") || !language.contains(")")) { + return language; + } + + if (language.startsWith("(")) { + // language text is right-to-left + final String[] parts = language.split("\\)"); + return parts[parts.length - 1].trim(); + } + + return language.split("\\(")[0].trim(); + } + @NonNull public static String resizeTypeOf(@NonNull final Context context, @ResizeMode final int resizeMode) { From 9818f179c49d0b12aa49364ef16251c4ea8c126f Mon Sep 17 00:00:00 2001 From: karyogamy Date: Mon, 11 Apr 2022 22:06:43 -0400 Subject: [PATCH 3/3] fixed: auto-generated captions to have lower selection priority as manual captions. --- .../org/schabi/newpipe/player/Player.java | 33 ++++++++++--------- .../resolver/VideoPlaybackResolver.java | 5 +++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 9f1fda981..82ae0df27 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -46,7 +46,6 @@ import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZ import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE; import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP; import static org.schabi.newpipe.player.helper.PlayerHelper.buildCloseOverlayLayoutParams; -import static org.schabi.newpipe.player.helper.PlayerHelper.captionLanguageStemOf; import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed; import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimizeOnExitAction; import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimumVideoHeight; @@ -138,7 +137,6 @@ import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.TracksInfo; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; @@ -212,7 +210,6 @@ import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.views.ExpandableSurfaceView; import org.schabi.newpipe.views.player.PlayerFastSeekOverlay; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -3539,12 +3536,18 @@ public final class Player implements captionItem.setOnMenuItemClickListener(menuItem -> { final int textRendererIndex = getCaptionRendererIndex(); if (textRendererIndex != RENDERER_UNAVAILABLE) { - // DefaultTrackSelector will select for tracks with similar language names - // if a track of userPreferredLanguage is not found - // This means (auto-generated) will be resolved automatically. + // DefaultTrackSelector will select for text tracks in the following order. + // When multiple tracks share the same rank, a random track will be chosen. + // 1. ANY track exactly matching preferred language name + // 2. ANY track exactly matching preferred language stem + // 3. ROLE_FLAG_CAPTION track matching preferred language stem + // 4. ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND track matching preferred language stem + // This means if a caption track of preferred language is not available, + // then an auto-generated track of that language will be chosen automatically. trackSelector.setParameters(trackSelector.buildUponParameters() .setPreferredTextLanguages(captionLanguage, - captionLanguageStemOf(captionLanguage)) + PlayerHelper.captionLanguageStemOf(captionLanguage)) + .setPreferredTextRoleFlags(C.ROLE_FLAG_CAPTION) .setRendererDisabled(textRendererIndex, false)); prefs.edit().putString(context.getString(R.string.caption_user_set_key), captionLanguage).apply(); @@ -3561,12 +3564,12 @@ public final class Player implements final int textRendererIndex = getCaptionRendererIndex(); if (userPreferredLanguage != null - && availableLanguages.contains(userPreferredLanguage) && !selectedPreferredLanguages.contains(userPreferredLanguage) && textRendererIndex != RENDERER_UNAVAILABLE) { trackSelector.setParameters(trackSelector.buildUponParameters() .setPreferredTextLanguages(userPreferredLanguage, - captionLanguageStemOf(userPreferredLanguage)) + PlayerHelper.captionLanguageStemOf(userPreferredLanguage)) + .setPreferredTextRoleFlags(C.ROLE_FLAG_CAPTION) .setRendererDisabled(textRendererIndex, false)); } } @@ -3681,13 +3684,11 @@ public final class Player implements .stream() .filter(trackGroupInfo -> C.TRACK_TYPE_TEXT == trackGroupInfo.getTrackType()) .collect(Collectors.toList()); - final List availableLanguages = new ArrayList<>(textTracks.size()); - for (int i = 0; i < textTracks.size(); i++) { - final TrackGroup textTrack = textTracks.get(i).getTrackGroup(); - if (textTrack.length > 0) { - availableLanguages.add(textTrack.getFormat(0).language); - } - } + final List availableLanguages = textTracks.stream() + .map(TracksInfo.TrackGroupInfo::getTrackGroup) + .filter(textTrack -> textTrack.length > 0) + .map(textTrack -> textTrack.getFormat(0).language) + .collect(Collectors.toList()); // Find selected text track final Optional selectedTracks = textTracks.stream() 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 565f0b23e..1aa7a5a18 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 @@ -6,6 +6,7 @@ import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MergingMediaSource; @@ -116,9 +117,13 @@ public class VideoPlaybackResolver implements PlaybackResolver { if (mimeType == null) { continue; } + final @C.RoleFlags int textRoleFlag = subtitle.isAutoGenerated() + ? C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND + : C.ROLE_FLAG_CAPTION; final MediaItem.SubtitleConfiguration textMediaItem = new MediaItem.SubtitleConfiguration.Builder(Uri.parse(subtitle.getUrl())) .setMimeType(mimeType) + .setRoleFlags(textRoleFlag) .setLanguage(PlayerHelper.captionLanguageOf(context, subtitle)) .build(); final MediaSource textSource = dataSource