From 366c39d4c6e52d1c3a9875421d99d38b300bfd0a Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sun, 19 Mar 2023 01:15:36 +0100 Subject: [PATCH] feat: add language selector to audio player --- .../newpipe/player/PlayQueueActivity.java | 89 +++++++++++++++++++ .../org/schabi/newpipe/player/Player.java | 28 +++++- .../player/event/PlayerEventListener.java | 1 + .../player/mediaitem/MediaItemTag.java | 14 +-- .../player/mediaitem/StreamInfoTag.java | 26 ++++-- .../resolver/AudioPlaybackResolver.java | 69 ++++++++------ .../resolver/VideoPlaybackResolver.java | 26 +++--- .../newpipe/player/ui/VideoPlayerUi.java | 66 +++++++------- .../org/schabi/newpipe/util/ListHelper.java | 8 ++ app/src/main/res/layout/player.xml | 2 +- app/src/main/res/menu/menu_play_queue.xml | 8 ++ app/src/main/res/values/strings.xml | 2 + 12 files changed, 241 insertions(+), 98 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java index 9ce99c15b..fa3668e8b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java @@ -13,6 +13,7 @@ import android.provider.Settings; import android.util.Log; import android.view.Menu; import android.view.MenuItem; +import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; import android.widget.SeekBar; @@ -27,11 +28,13 @@ import com.google.android.exoplayer2.PlaybackParameters; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.ActivityPlayerQueueControlBinding; +import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.local.dialog.PlaylistDialog; import org.schabi.newpipe.player.event.PlayerEventListener; import org.schabi.newpipe.player.helper.PlaybackParameterDialog; +import org.schabi.newpipe.player.mediaitem.MediaItemTag; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueAdapter; import org.schabi.newpipe.player.playqueue.PlayQueueItem; @@ -44,6 +47,10 @@ import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.ThemeHelper; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + public final class PlayQueueActivity extends AppCompatActivity implements PlayerEventListener, SeekBar.OnSeekBarChangeListener, View.OnClickListener, PlaybackParameterDialog.Callback { @@ -52,6 +59,8 @@ public final class PlayQueueActivity extends AppCompatActivity private static final int SMOOTH_SCROLL_MAXIMUM_DISTANCE = 80; + private static final int MENU_ID_AUDIO_TRACK = 71; + private Player player; private boolean serviceBound; @@ -97,6 +106,7 @@ public final class PlayQueueActivity extends AppCompatActivity this.menu = m; getMenuInflater().inflate(R.menu.menu_play_queue, m); getMenuInflater().inflate(R.menu.menu_play_queue_bg, m); + buildAudioTrackMenu(); onMaybeMuteChanged(); // to avoid null reference if (player != null) { @@ -153,6 +163,12 @@ public final class PlayQueueActivity extends AppCompatActivity NavigationHelper.playOnBackgroundPlayer(this, player.getPlayQueue(), true); return true; } + + if (item.getGroupId() == MENU_ID_AUDIO_TRACK) { + onAudioTrackClick(item.getItemId()); + return true; + } + return super.onOptionsItemSelected(item); } @@ -591,4 +607,77 @@ public final class PlayQueueActivity extends AppCompatActivity item.setIcon(player.isMuted() ? R.drawable.ic_volume_off : R.drawable.ic_volume_up); } } + + @Override + public void onAudioTrackUpdate() { + buildAudioTrackMenu(); + } + + private void buildAudioTrackMenu() { + if (menu == null) { + return; + } + + final MenuItem audioTrackSelector = menu.findItem(R.id.action_audio_track); + final List availableStreams = + Optional.ofNullable(player.getCurrentMetadata()) + .flatMap(MediaItemTag::getMaybeAudioTrack) + .map(MediaItemTag.AudioTrack::getAudioStreams) + .orElse(null); + + if (availableStreams == null || availableStreams.size() < 2) { + audioTrackSelector.setVisible(false); + } else { + final SubMenu audioTrackMenu = audioTrackSelector.getSubMenu(); + audioTrackMenu.clear(); + + for (int i = 0; i < availableStreams.size(); i++) { + final AudioStream audioStream = availableStreams.get(i); + if (audioStream.getAudioTrackName() == null) { + continue; + } + + audioTrackMenu.add(MENU_ID_AUDIO_TRACK, i, Menu.NONE, + audioStream.getAudioTrackName()); + } + + player.getSelectedAudioStream().ifPresent(s -> { + final String trackName = Objects.toString(s.getAudioTrackName(), ""); + audioTrackSelector.setTitle(getString(R.string.play_queue_audio_track) + trackName); + + final String shortName = s.getAudioLocale() != null + ? s.getAudioLocale().getLanguage() : trackName; + audioTrackSelector.setTitleCondensed( + shortName.substring(0, Math.min(shortName.length(), 2))); + audioTrackSelector.setVisible(true); + }); + + } + } + + /** + * Called when an item from the audio track selector is selected. + * + * @param itemId index of the selected item + */ + private void onAudioTrackClick(final int itemId) { + @Nullable final MediaItemTag currentMetadata = player.getCurrentMetadata(); + if (currentMetadata == null || currentMetadata.getMaybeAudioTrack().isEmpty()) { + return; + } + + final MediaItemTag.AudioTrack audioTrack = + currentMetadata.getMaybeAudioTrack().get(); + final List availableStreams = audioTrack.getAudioStreams(); + final int selectedStreamIndex = audioTrack.getSelectedAudioStreamIndex(); + if (selectedStreamIndex == itemId || availableStreams.size() <= itemId) { + return; + } + + player.saveStreamProgressState(); + final String newAudioTrack = availableStreams.get(itemId).getAudioTrackId(); + player.setRecovery(); + player.setAudioTrack(newAudioTrack); + player.reloadPlayQueueManager(); + } } 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 69b161631..2d6d23a23 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -1243,6 +1243,9 @@ public final class Player implements PlaybackListener, Listener { } final StreamInfo previousInfo = Optional.ofNullable(currentMetadata) .flatMap(MediaItemTag::getMaybeStreamInfo).orElse(null); + final MediaItemTag.AudioTrack previousAudioTrack = + Optional.ofNullable(currentMetadata) + .flatMap(MediaItemTag::getMaybeAudioTrack).orElse(null); currentMetadata = tag; if (!currentMetadata.getErrors().isEmpty()) { @@ -1263,6 +1266,12 @@ public final class Player implements PlaybackListener, Listener { if (previousInfo == null || !previousInfo.getUrl().equals(info.getUrl())) { // only update with the new stream info if it has actually changed updateMetadataWith(info); + } else if (previousAudioTrack == null + || tag.getMaybeAudioTrack() + .map(t -> t.getSelectedAudioStreamIndex() + != previousAudioTrack.getSelectedAudioStreamIndex()) + .orElse(false)) { + notifyAudioTrackUpdateToListeners(); } }); }); @@ -1759,6 +1768,7 @@ public final class Player implements PlaybackListener, Listener { registerStreamViewed(); notifyMetadataUpdateToListeners(); + notifyAudioTrackUpdateToListeners(); UIs.call(playerUi -> playerUi.onMetadataChanged(info)); } @@ -1890,8 +1900,8 @@ public final class Player implements PlaybackListener, Listener { public Optional getSelectedAudioStream() { return Optional.ofNullable(currentMetadata) - .flatMap(MediaItemTag::getMaybeAudioLanguage) - .map(MediaItemTag.AudioLanguage::getSelectedAudioStream); + .flatMap(MediaItemTag::getMaybeAudioTrack) + .map(MediaItemTag.AudioTrack::getSelectedAudioStream); } //endregion @@ -2024,6 +2034,15 @@ public final class Player implements PlaybackListener, Listener { } } + private void notifyAudioTrackUpdateToListeners() { + if (fragmentListener != null) { + fragmentListener.onAudioTrackUpdate(); + } + if (activityListener != null) { + activityListener.onAudioTrackUpdate(); + } + } + public void useVideoSource(final boolean videoEnabled) { if (playQueue == null || isAudioOnly == !videoEnabled || audioPlayerSelected()) { return; @@ -2185,8 +2204,9 @@ public final class Player implements PlaybackListener, Listener { videoResolver.setPlaybackQuality(quality); } - public void setAudioLanguage(@Nullable final String language) { - videoResolver.setAudioLanguage(language); + public void setAudioTrack(@Nullable final String audioTrackId) { + videoResolver.setAudioTrack(audioTrackId); + audioResolver.setAudioTrack(audioTrackId); } diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java index 84bd9d277..2cca259c2 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java @@ -11,5 +11,6 @@ public interface PlayerEventListener { PlaybackParameters parameters); void onProgressUpdate(int currentProgress, int duration, int bufferPercent); void onMetadataUpdate(StreamInfo info, PlayQueue queue); + default void onAudioTrackUpdate() { } void onServiceStopped(); } diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java index 0ef1eaaf1..1119fb903 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java @@ -57,7 +57,7 @@ public interface MediaItemTag { } @NonNull - default Optional getMaybeAudioLanguage() { + default Optional getMaybeAudioTrack() { return Optional.empty(); } @@ -135,20 +135,20 @@ public interface MediaItemTag { } } - final class AudioLanguage { + final class AudioTrack { @NonNull private final List audioStreams; private final int selectedAudioStreamIndex; - private AudioLanguage(@NonNull final List audioStreams, - final int selectedAudioStreamIndex) { + private AudioTrack(@NonNull final List audioStreams, + final int selectedAudioStreamIndex) { this.audioStreams = audioStreams; this.selectedAudioStreamIndex = selectedAudioStreamIndex; } - static AudioLanguage of(@NonNull final List audioStreams, - final int selectedAudioStreamIndex) { - return new AudioLanguage(audioStreams, selectedAudioStreamIndex); + static AudioTrack of(@NonNull final List audioStreams, + final int selectedAudioStreamIndex) { + return new AudioTrack(audioStreams, selectedAudioStreamIndex); } @NonNull diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java index 5379fc5a6..689f5c72b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java @@ -26,17 +26,17 @@ public final class StreamInfoTag implements MediaItemTag { @Nullable private final MediaItemTag.Quality quality; @Nullable - private final MediaItemTag.AudioLanguage audioLanguage; + private final MediaItemTag.AudioTrack audioTrack; @Nullable private final Object extras; private StreamInfoTag(@NonNull final StreamInfo streamInfo, @Nullable final MediaItemTag.Quality quality, - @Nullable final MediaItemTag.AudioLanguage audioLanguage, + @Nullable final MediaItemTag.AudioTrack audioTrack, @Nullable final Object extras) { this.streamInfo = streamInfo; this.quality = quality; - this.audioLanguage = audioLanguage; + this.audioTrack = audioTrack; this.extras = extras; } @@ -46,9 +46,17 @@ public final class StreamInfoTag implements MediaItemTag { @NonNull final List audioStreams, final int selectedAudioStreamIndex) { final Quality quality = Quality.of(sortedVideoStreams, selectedVideoStreamIndex); - final AudioLanguage audioLanguage = - AudioLanguage.of(audioStreams, selectedAudioStreamIndex); - return new StreamInfoTag(streamInfo, quality, audioLanguage, null); + final AudioTrack audioTrack = + AudioTrack.of(audioStreams, selectedAudioStreamIndex); + return new StreamInfoTag(streamInfo, quality, audioTrack, null); + } + + public static StreamInfoTag of(@NonNull final StreamInfo streamInfo, + @NonNull final List audioStreams, + final int selectedAudioStreamIndex) { + final AudioTrack audioTrack = + AudioTrack.of(audioStreams, selectedAudioStreamIndex); + return new StreamInfoTag(streamInfo, null, audioTrack, null); } public static StreamInfoTag of(@NonNull final StreamInfo streamInfo) { @@ -114,8 +122,8 @@ public final class StreamInfoTag implements MediaItemTag { @NonNull @Override - public Optional getMaybeAudioLanguage() { - return Optional.ofNullable(audioLanguage); + public Optional getMaybeAudioTrack() { + return Optional.ofNullable(audioTrack); } @Override @@ -125,6 +133,6 @@ public final class StreamInfoTag implements MediaItemTag { @Override public StreamInfoTag withExtras(@NonNull final Object extra) { - return new StreamInfoTag(streamInfo, quality, audioLanguage, extra); + return new StreamInfoTag(streamInfo, quality, audioTrack, extra); } } 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 e87c93114..2e7b5c7d5 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 @@ -1,5 +1,6 @@ package org.schabi.newpipe.player.resolver; +import static org.schabi.newpipe.util.ListHelper.getFilteredAudioStreams; import static org.schabi.newpipe.util.ListHelper.getNonTorrentStreams; import android.content.Context; @@ -28,6 +29,8 @@ public class AudioPlaybackResolver implements PlaybackResolver { private final Context context; @NonNull private final PlayerDataSource dataSource; + @Nullable + private String audioTrack; public AudioPlaybackResolver(@NonNull final Context context, @NonNull final PlayerDataSource dataSource) { @@ -43,12 +46,36 @@ public class AudioPlaybackResolver implements PlaybackResolver { return liveSource; } - final Stream stream = getAudioSource(info); - if (stream == null) { - return null; - } + final List audioStreams = + getFilteredAudioStreams(context, info.getAudioStreams()); + final Stream stream; + final MediaItemTag tag; - final MediaItemTag tag = StreamInfoTag.of(info); + 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; + } + } + } + stream = getStreamForIndex(audioIndex, audioStreams); + tag = StreamInfoTag.of(info, audioStreams, audioIndex); + } else { + final List videoStreams = getNonTorrentStreams(info.getVideoStreams()); + if (!videoStreams.isEmpty()) { + final int index = ListHelper.getDefaultResolutionIndex(context, videoStreams); + stream = getStreamForIndex(index, videoStreams); + tag = StreamInfoTag.of(info); + } else { + return null; + } + } try { return PlaybackResolver.buildMediaSource( @@ -59,29 +86,6 @@ public class AudioPlaybackResolver implements PlaybackResolver { } } - /** - * Get a stream to be played as audio. If a service has no separate {@link AudioStream}s we - * use a video stream as audio source to support audio background playback. - * - * @param info of the stream - * @return the audio source to use or null if none could be found - */ - @Nullable - private Stream getAudioSource(@NonNull final StreamInfo info) { - final List audioStreams = getNonTorrentStreams(info.getAudioStreams()); - if (!audioStreams.isEmpty()) { - final int index = ListHelper.getDefaultAudioFormat(context, audioStreams); - return getStreamForIndex(index, audioStreams); - } else { - final List videoStreams = getNonTorrentStreams(info.getVideoStreams()); - if (!videoStreams.isEmpty()) { - final int index = ListHelper.getDefaultResolutionIndex(context, videoStreams); - return getStreamForIndex(index, videoStreams); - } - } - return null; - } - @Nullable Stream getStreamForIndex(final int index, @NonNull final List streams) { if (index >= 0 && index < streams.size()) { @@ -89,4 +93,13 @@ public class AudioPlaybackResolver implements PlaybackResolver { } return null; } + + @Nullable + public String getAudioTrack() { + return audioTrack; + } + + public void setAudioTrack(@Nullable final String audioLanguage) { + this.audioTrack = audioLanguage; + } } 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 2f0c59325..64349409d 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 @@ -46,7 +46,7 @@ public class VideoPlaybackResolver implements PlaybackResolver { @Nullable private String playbackQuality; @Nullable - private String audioLanguage; + private String audioTrack; public enum SourceType { LIVE_STREAM, @@ -91,11 +91,11 @@ public class VideoPlaybackResolver implements PlaybackResolver { } int audioIndex = 0; - if (audioLanguage != null) { + if (audioTrack != null) { for (int i = 0; i < audioStreamsList.size(); i++) { final AudioStream stream = audioStreamsList.get(i); if (stream.getAudioTrackId() != null - && stream.getAudioTrackId().equals(audioLanguage)) { + && stream.getAudioTrackId().equals(audioTrack)) { audioIndex = i; break; } @@ -107,8 +107,8 @@ public class VideoPlaybackResolver implements PlaybackResolver { @Nullable final VideoStream video = tag.getMaybeQuality() .map(MediaItemTag.Quality::getSelectedVideoStream) .orElse(null); - @Nullable final AudioStream audio = tag.getMaybeAudioLanguage() - .map(MediaItemTag.AudioLanguage::getSelectedAudioStream) + @Nullable final AudioStream audio = tag.getMaybeAudioTrack() + .map(MediaItemTag.AudioTrack::getSelectedAudioStream) .orElse(null); if (video != null) { @@ -124,7 +124,7 @@ public class VideoPlaybackResolver implements PlaybackResolver { // Use the audio stream if there is no video stream, or // merge with audio stream in case if video does not contain audio - if (audio != null && (video == null || video.isVideoOnly() || audioLanguage != null)) { + if (audio != null && (video == null || video.isVideoOnly() || audioTrack != null)) { try { final MediaSource audioSource = PlaybackResolver.buildMediaSource( dataSource, audio, info, PlaybackResolver.cacheKeyOf(info, audio), tag); @@ -198,12 +198,12 @@ public class VideoPlaybackResolver implements PlaybackResolver { } @Nullable - public String getAudioLanguage() { - return audioLanguage; + public String getAudioTrack() { + return audioTrack; } - public void setAudioLanguage(@Nullable final String audioLanguage) { - this.audioLanguage = audioLanguage; + public void setAudioTrack(@Nullable final String audioLanguage) { + this.audioTrack = audioLanguage; } public interface QualityResolver { @@ -211,10 +211,4 @@ public class VideoPlaybackResolver implements PlaybackResolver { int getOverrideResolutionIndex(List sortedVideos, String playbackQuality); } - - public interface AudioLanguageResolver { - int getDefaultLanguageIndex(List audioStreams); - - int getOverrideLanguageIndex(List audioStreams, String audioLanguage); - } } diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java index 13cbc9fd3..df966d591 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java @@ -118,13 +118,13 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa //////////////////////////////////////////////////////////////////////////*/ private static final int POPUP_MENU_ID_QUALITY = 69; - private static final int POPUP_MENU_ID_LANGUAGE = 70; + private static final int POPUP_MENU_ID_AUDIO_TRACK = 70; private static final int POPUP_MENU_ID_PLAYBACK_SPEED = 79; private static final int POPUP_MENU_ID_CAPTION = 89; protected boolean isSomePopupMenuVisible = false; private PopupMenu qualityPopupMenu; - private PopupMenu languagePopupMenu; + private PopupMenu audioTrackPopupMenu; protected PopupMenu playbackSpeedPopupMenu; private PopupMenu captionPopupMenu; @@ -176,7 +176,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa R.style.DarkPopupMenu); qualityPopupMenu = new PopupMenu(themeWrapper, binding.qualityTextView); - languagePopupMenu = new PopupMenu(themeWrapper, binding.languageTextView); + audioTrackPopupMenu = new PopupMenu(themeWrapper, binding.audioTrackTextView); playbackSpeedPopupMenu = new PopupMenu(context, binding.playbackSpeed); captionPopupMenu = new PopupMenu(themeWrapper, binding.captionTextView); @@ -194,8 +194,8 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa protected void initListeners() { binding.qualityTextView.setOnClickListener(makeOnClickListener(this::onQualityClicked)); - binding.languageTextView.setOnClickListener( - makeOnClickListener(this::onAudioLanguageClicked)); + binding.audioTrackTextView.setOnClickListener( + makeOnClickListener(this::onAudioTracksClicked)); binding.playbackSpeed.setOnClickListener(makeOnClickListener(this::onPlaybackSpeedClicked)); binding.playbackSeekBar.setOnSeekBarChangeListener(this); @@ -272,7 +272,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa protected void deinitListeners() { binding.qualityTextView.setOnClickListener(null); - binding.languageTextView.setOnClickListener(null); + binding.audioTrackTextView.setOnClickListener(null); binding.playbackSpeed.setOnClickListener(null); binding.playbackSeekBar.setOnSeekBarChangeListener(null); binding.captionTextView.setOnClickListener(null); @@ -426,7 +426,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa binding.topControls.setPaddingRelative(controlsPad, playerTopPad, controlsPad, 0); binding.bottomControls.setPaddingRelative(controlsPad, 0, controlsPad, 0); binding.qualityTextView.setPadding(buttonsPad, buttonsPad, buttonsPad, buttonsPad); - binding.languageTextView.setPadding(buttonsPad, buttonsPad, buttonsPad, buttonsPad); + binding.audioTrackTextView.setPadding(buttonsPad, buttonsPad, buttonsPad, buttonsPad); binding.playbackSpeed.setPadding(buttonsPad, buttonsPad, buttonsPad, buttonsPad); binding.playbackSpeed.setMinimumWidth(buttonsMinWidth); binding.captionTextView.setPadding(buttonsPad, buttonsPad, buttonsPad, buttonsPad); @@ -992,7 +992,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa private void updateStreamRelatedViews() { player.getCurrentStreamInfo().ifPresent(info -> { binding.qualityTextView.setVisibility(View.GONE); - binding.languageTextView.setVisibility(View.GONE); + binding.audioTrackTextView.setVisibility(View.GONE); binding.playbackSpeed.setVisibility(View.GONE); binding.playbackEndTime.setVisibility(View.GONE); @@ -1028,7 +1028,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa } buildQualityMenu(); - buildLanguageMenu(); + buildAudioTrackMenu(); binding.qualityTextView.setVisibility(View.VISIBLE); binding.surfaceView.setVisibility(View.VISIBLE); @@ -1077,15 +1077,15 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa .ifPresent(s -> binding.qualityTextView.setText(s.getResolution())); } - private void buildLanguageMenu() { - if (languagePopupMenu == null) { + private void buildAudioTrackMenu() { + if (audioTrackPopupMenu == null) { return; } - languagePopupMenu.getMenu().removeGroup(POPUP_MENU_ID_LANGUAGE); + audioTrackPopupMenu.getMenu().removeGroup(POPUP_MENU_ID_AUDIO_TRACK); final List availableStreams = Optional.ofNullable(player.getCurrentMetadata()) - .flatMap(MediaItemTag::getMaybeAudioLanguage) - .map(MediaItemTag.AudioLanguage::getAudioStreams) + .flatMap(MediaItemTag::getMaybeAudioTrack) + .map(MediaItemTag.AudioTrack::getAudioStreams) .orElse(null); if (availableStreams == null || availableStreams.size() < 2) { return; @@ -1096,15 +1096,15 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa if (audioStream.getAudioTrackName() == null) { continue; } - languagePopupMenu.getMenu().add(POPUP_MENU_ID_LANGUAGE, i, Menu.NONE, + audioTrackPopupMenu.getMenu().add(POPUP_MENU_ID_AUDIO_TRACK, i, Menu.NONE, audioStream.getAudioTrackName()); } player.getSelectedAudioStream() - .ifPresent(s -> binding.languageTextView.setText(s.getAudioTrackName())); - binding.languageTextView.setVisibility(View.VISIBLE); - languagePopupMenu.setOnMenuItemClickListener(this); - languagePopupMenu.setOnDismissListener(this); + .ifPresent(s -> binding.audioTrackTextView.setText(s.getAudioTrackName())); + binding.audioTrackTextView.setVisibility(View.VISIBLE); + audioTrackPopupMenu.setOnMenuItemClickListener(this); + audioTrackPopupMenu.setOnDismissListener(this); } private void buildPlaybackSpeedMenu() { @@ -1215,13 +1215,13 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa .ifPresent(binding.qualityTextView::setText); } - private void onAudioLanguageClicked() { - languagePopupMenu.show(); + private void onAudioTracksClicked() { + audioTrackPopupMenu.show(); isSomePopupMenuVisible = true; player.getSelectedAudioStream() .map(AudioStream::getAudioTrackName) - .ifPresent(binding.languageTextView::setText); + .ifPresent(binding.audioTrackTextView::setText); } /** @@ -1238,8 +1238,8 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa if (menuItem.getGroupId() == POPUP_MENU_ID_QUALITY) { onQualityItemClick(menuItem); return true; - } else if (menuItem.getGroupId() == POPUP_MENU_ID_LANGUAGE) { - onLanguageItemClick(menuItem); + } else if (menuItem.getGroupId() == POPUP_MENU_ID_AUDIO_TRACK) { + onAudioTrackItemClick(menuItem); return true; } else if (menuItem.getGroupId() == POPUP_MENU_ID_PLAYBACK_SPEED) { final int speedIndex = menuItem.getItemId(); @@ -1275,28 +1275,28 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa binding.qualityTextView.setText(menuItem.getTitle()); } - private void onLanguageItemClick(@NonNull final MenuItem menuItem) { + private void onAudioTrackItemClick(@NonNull final MenuItem menuItem) { final int menuItemIndex = menuItem.getItemId(); @Nullable final MediaItemTag currentMetadata = player.getCurrentMetadata(); - if (currentMetadata == null || currentMetadata.getMaybeAudioLanguage().isEmpty()) { + if (currentMetadata == null || currentMetadata.getMaybeAudioTrack().isEmpty()) { return; } - final MediaItemTag.AudioLanguage language = - currentMetadata.getMaybeAudioLanguage().get(); - final List availableStreams = language.getAudioStreams(); - final int selectedStreamIndex = language.getSelectedAudioStreamIndex(); + final MediaItemTag.AudioTrack audioTrack = + currentMetadata.getMaybeAudioTrack().get(); + final List availableStreams = audioTrack.getAudioStreams(); + final int selectedStreamIndex = audioTrack.getSelectedAudioStreamIndex(); if (selectedStreamIndex == menuItemIndex || availableStreams.size() <= menuItemIndex) { return; } player.saveStreamProgressState(); - final String newLanguage = availableStreams.get(menuItemIndex).getAudioTrackId(); + final String newAudioTrack = availableStreams.get(menuItemIndex).getAudioTrackId(); player.setRecovery(); - player.setAudioLanguage(newLanguage); + player.setAudioTrack(newAudioTrack); player.reloadPlayQueueManager(); - binding.languageTextView.setText(menuItem.getTitle()); + binding.audioTrackTextView.setText(menuItem.getTitle()); } /** 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 ef40692d7..a2e8216a3 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java @@ -188,6 +188,14 @@ public final class ListHelper { videoOnlyStreams, ascendingOrder, preferVideoOnlyStreams); } + /** + * 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 audioStreams the list of audio streams + * @return the sorted, filtered list + */ public static List getFilteredAudioStreams( @NonNull final Context context, @Nullable final List audioStreams) { diff --git a/app/src/main/res/layout/player.xml b/app/src/main/res/layout/player.xml index 82760be3a..89f1ed88e 100644 --- a/app/src/main/res/layout/player.xml +++ b/app/src/main/res/layout/player.xml @@ -158,7 +158,7 @@ + + + + Remove Details Audio Settings + Audio: + Audio track Hold to enqueue Show channel details Enqueue