diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 4bb0c2cca..2ec6d4dd5 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -724,7 +724,7 @@ public class VideoDetailFragment extends BaseStateFragment implement .getBoolean(activity.getString(R.string.use_external_audio_player_key), false); if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) { - openNormalBackgroundPlayer(audioStream); + openNormalBackgroundPlayer(); } else { openExternalBackgroundPlayer(audioStream); } @@ -763,8 +763,8 @@ public class VideoDetailFragment extends BaseStateFragment implement } - private void openNormalBackgroundPlayer(AudioStream audioStream) { - activity.startService(NavigationHelper.getOpenBackgroundPlayerIntent(activity, currentInfo, audioStream)); + private void openNormalBackgroundPlayer() { + activity.startService(NavigationHelper.getOpenBackgroundPlayerIntent(activity, currentInfo)); Toast.makeText(activity, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show(); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 20d1eadeb..6ffc10370 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -21,7 +21,9 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; +import org.schabi.newpipe.player.BackgroundPlayer; import org.schabi.newpipe.player.MainVideoPlayer; +import org.schabi.newpipe.player.PopupVideoPlayer; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.NavigationHelper; @@ -44,6 +46,8 @@ public class PlaylistFragment extends BaseListInfoFragment { private TextView headerStreamCount; private Button headerPlayAllButton; + private Button headerPopupButton; + private Button headerBackgroundButton; public static PlaylistFragment getInstance(int serviceId, String url, String name) { PlaylistFragment instance = new PlaylistFragment(); @@ -71,7 +75,10 @@ public class PlaylistFragment extends BaseListInfoFragment { headerUploaderName = headerRootLayout.findViewById(R.id.uploader_name); headerUploaderAvatar = headerRootLayout.findViewById(R.id.uploader_avatar_view); headerStreamCount = headerRootLayout.findViewById(R.id.playlist_stream_count); + headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_play_all_button); + headerPopupButton = headerRootLayout.findViewById(R.id.playlist_play_popup_button); + headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_play_bg_button); return headerRootLayout; } @@ -138,7 +145,7 @@ public class PlaylistFragment extends BaseListInfoFragment { } imageLoader.displayImage(result.uploader_avatar_url, headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS); - headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count)); + headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count, (int) result.stream_count)); if (!result.errors.isEmpty()) { showSnackBarError(result.errors, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.service_id), result.url, 0); @@ -147,15 +154,28 @@ public class PlaylistFragment extends BaseListInfoFragment { headerPlayAllButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - final Intent intent = NavigationHelper.getExternalPlaylistIntent( - activity, MainVideoPlayer.class, currentInfo, infoListAdapter.getItemsList(), 0 - ); - - startActivity(intent); + startActivity(buildPlaylistIntent(MainVideoPlayer.class)); + } + }); + headerPopupButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + activity.startService(buildPlaylistIntent(PopupVideoPlayer.class)); + } + }); + headerBackgroundButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + activity.startService(buildPlaylistIntent(BackgroundPlayer.class)); } }); } + private Intent buildPlaylistIntent(final Class targetClazz) { + return NavigationHelper.getExternalPlaylistIntent( + activity, targetClazz, currentInfo, infoListAdapter.getItemsList(), 0 + ); + } @Override public void handleNextItems(ListExtractor.NextItemsResult result) { diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 4bf8b1421..34865b1ba 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -36,6 +36,7 @@ import android.util.Log; import android.widget.RemoteViews; import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.source.MediaSource; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.MainActivity; @@ -43,11 +44,11 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.stream.AudioStream; +import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.ThemeHelper; -import java.io.Serializable; - /** * Base players joining the common properties @@ -65,9 +66,6 @@ public class BackgroundPlayer extends Service { public static final String ACTION_FAST_REWIND = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_REWIND"; public static final String ACTION_FAST_FORWARD = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_FORWARD"; - public static final String AUDIO_STREAM = "video_only_audio_stream"; - private AudioStream audioStream; - private BasePlayerImpl basePlayerImpl; private PowerManager powerManager; private WifiManager wifiManager; @@ -177,8 +175,8 @@ public class BackgroundPlayer extends Service { private void setupNotification(RemoteViews remoteViews) { //if (videoThumbnail != null) remoteViews.setImageViewBitmap(R.id.notificationCover, videoThumbnail); ///else remoteViews.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail); - remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle()); - remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName()); +// remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle()); +// remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName()); remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause, PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT)); @@ -340,12 +338,16 @@ public class BackgroundPlayer extends Service { @Override public void onFastRewind() { + if (isPlayerBuffering()) return; + playQueue.setIndex(playQueue.getIndex() - 1); triggerProgressUpdate(); } @Override public void onFastForward() { + if (isPlayerBuffering()) return; + playQueue.setIndex(playQueue.getIndex() + 1); triggerProgressUpdate(); } @@ -376,6 +378,26 @@ public class BackgroundPlayer extends Service { // Playback Listener //////////////////////////////////////////////////////////////////////////*/ + @Override + public void sync(final StreamInfo info, final int sortedStreamsIndex) { + super.sync(info, sortedStreamsIndex); + + basePlayerImpl.setVideoTitle(info.name); + basePlayerImpl.setUploaderName(info.uploader_name); + + notRemoteView.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle()); + notRemoteView.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName()); + bigNotRemoteView.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle()); + bigNotRemoteView.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName()); + updateNotification(-1); + } + + @Override + public MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex) { + final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams); + return buildMediaSource(audio.url, MediaFormat.getSuffixById(audio.format)); + } + @Override public void shutdown() { super.shutdown(); diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 90e206c84..773080d94 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -35,6 +35,7 @@ import android.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; import android.view.View; +import android.widget.Toast; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; @@ -249,8 +250,6 @@ public abstract class BasePlayer implements Player.EventListener, default: break; } - - initThumbnail(); } @@ -586,8 +585,13 @@ public abstract class BasePlayer implements Player.EventListener, @Override public void onPositionDiscontinuity() { + // Refresh the playback if there is a transition to the next video int newIndex = simpleExoPlayer.getCurrentWindowIndex(); - if (playbackManager.getCurrentSourceIndex() != newIndex) playbackManager.refresh(newIndex); + if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called with: index = [" + newIndex + "]"); + + if (newIndex == playbackManager.getCurrentSourceIndex() + 1) { + playbackManager.refresh(newIndex); + } } /*////////////////////////////////////////////////////////////////////////// @@ -596,6 +600,7 @@ public abstract class BasePlayer implements Player.EventListener, @Override public void block() { + if (simpleExoPlayer == null) return; Log.d(TAG, "Blocking..."); simpleExoPlayer.stop(); @@ -605,6 +610,7 @@ public abstract class BasePlayer implements Player.EventListener, @Override public void unblock() { + if (simpleExoPlayer == null) return; Log.d(TAG, "Unblocking..."); if (restoreQueueIndex != playQueue.getIndex()) { @@ -619,25 +625,28 @@ public abstract class BasePlayer implements Player.EventListener, @Override public void sync(final StreamInfo info, final int sortedStreamsIndex) { + if (simpleExoPlayer == null) return; Log.d(TAG, "Syncing..."); videoUrl = info.url; videoThumbnailUrl = info.thumbnail_url; videoTitle = info.name; + initThumbnail(); + if (simpleExoPlayer.getCurrentWindowIndex() != playbackManager.getCurrentSourceIndex()) { Log.w(TAG, "Rewinding to correct window"); - simpleExoPlayer.seekTo(playbackManager.getCurrentSourceIndex(), 0L); + if (simpleExoPlayer.getCurrentTimeline().getWindowCount() > playbackManager.getCurrentSourceIndex()) { + simpleExoPlayer.seekToDefaultPosition(playbackManager.getCurrentSourceIndex()); + } else { + Toast.makeText(context, "Player out of sync", Toast.LENGTH_SHORT).show(); + simpleExoPlayer.seekToDefaultPosition(); + } } simpleExoPlayer.setPlayWhenReady(true); } - @Override - public MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex) { - return null; - } - @Override public void shutdown() { Log.d(TAG, "Shutting down..."); @@ -888,4 +897,8 @@ public abstract class BasePlayer implements Player.EventListener, public PlayQueue getPlayQueue() { return playQueue; } + + public boolean isPlayerBuffering() { + return currentState == STATE_BUFFERING; + } } diff --git a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java index a6d647a49..ef3741f24 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java @@ -471,6 +471,7 @@ public class MainVideoPlayer extends Activity { public boolean onDoubleTap(MotionEvent e) { if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); //if (!playerImpl.isPlaying()) return false; + if (playerImpl.isPlayerBuffering()) return false; if (e.getX() > playerImpl.getRootView().getWidth() / 2) playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1); diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java index 9d4fa8caa..b739c1c18 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java @@ -580,7 +580,8 @@ public class PopupVideoPlayer extends Service { public boolean onDoubleTap(MotionEvent e) { if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); - if (!playerImpl.isPlaying()) return false; + if (!playerImpl.isPlaying() || playerImpl.isPlayerBuffering()) return false; + if (e.getX() > popupWidth / 2) { //playerImpl.onFastForward(); playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1); diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java index 335ace207..97a476e50 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -274,7 +274,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. video = videos.get(sortedStreamsIndex); } - final MediaSource mediaSource = super.buildMediaSource(video.url, MediaFormat.getSuffixById(video.format)); + final MediaSource mediaSource = buildMediaSource(video.url, MediaFormat.getSuffixById(video.format)); if (!video.isVideoOnly) return mediaSource; final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams); @@ -282,15 +282,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. return new MergingMediaSource(mediaSource, new ExtractorMediaSource(audioUri, cacheDataSourceFactory, extractorsFactory, null, null)); } - @Override - public MediaSource buildMediaSource(String url, String overrideExtension) { - MediaSource mediaSource = super.buildMediaSource(url, overrideExtension); - if (!getSelectedVideoStream().isVideoOnly || videoOnlyAudioStream == null) return mediaSource; - - Uri audioUri = Uri.parse(videoOnlyAudioStream.url); - return new MergingMediaSource(mediaSource, new ExtractorMediaSource(audioUri, cacheDataSourceFactory, extractorsFactory, null, null)); - } - public void buildQualityMenu(PopupMenu popupMenu) { for (int i = 0; i < videoStreamsList.size(); i++) { VideoStream videoStream = videoStreamsList.get(i); diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java index 80e1341e3..3f5182375 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java @@ -28,6 +28,8 @@ public class PlayQueueItem implements Serializable { private Throwable error; + private transient Single stream; + PlayQueueItem(final StreamInfo streamInfo, final int sortedQualityIndex) { this.title = streamInfo.name; this.url = streamInfo.url; @@ -80,6 +82,11 @@ public class PlayQueueItem implements Serializable { @NonNull public Single getStream() { + return stream == null ? stream = getInfo() : stream; + } + + @NonNull + private Single getInfo() { final Consumer onError = new Consumer() { @Override public void accept(Throwable throwable) throws Exception { @@ -90,7 +97,6 @@ public class PlayQueueItem implements Serializable { return ExtractorHelper.getStreamInfo(this.serviceId, this.url, false) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .retry(3) .doOnError(onError); } } diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 2af8fb907..a6d1f9505 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -83,25 +83,6 @@ public class NavigationHelper { .putExtra(SinglePlayQueue.STREAM, info); } - public static Intent getOpenBackgroundPlayerIntent(Context context, StreamInfo info, AudioStream audioStream) { - return getOpenBackgroundPlayerIntent(context, info); - } - -// public static Intent getOpenBackgroundPlayerIntent(Context context, StreamInfo info) { -// return getOpenBackgroundPlayerIntent(context, info, info.audio_streams.get(ListHelper.getDefaultAudioFormat(context, info.audio_streams))); -// } -// -// public static Intent getOpenBackgroundPlayerIntent(Context context, StreamInfo info, AudioStream audioStream) { -// Intent mIntent = new Intent(context, BackgroundPlayer.class) -// .putExtra(BasePlayer.VIDEO_TITLE, info.name) -// .putExtra(BasePlayer.VIDEO_URL, info.url) -// .putExtra(BasePlayer.VIDEO_THUMBNAIL_URL, info.thumbnail_url) -// .putExtra(BasePlayer.CHANNEL_NAME, info.uploader_name) -// .putExtra(BackgroundPlayer.AUDIO_STREAM, audioStream); -// if (info.start_position > 0) mIntent.putExtra(BasePlayer.START_POSITION, info.start_position * 1000L); -// return mIntent; -// } - /*////////////////////////////////////////////////////////////////////////// // Through FragmentManager //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/res/layout/playlist_header.xml b/app/src/main/res/layout/playlist_header.xml index 9e136ba0f..26dc9c4e0 100644 --- a/app/src/main/res/layout/playlist_header.xml +++ b/app/src/main/res/layout/playlist_header.xml @@ -9,10 +9,87 @@ android:background="?attr/contrast_background_color" android:paddingBottom="6dp"> + + + android:layout_below="@+id/playlist_title_view" + android:id="@+id/playlist_meta"> + + + + + + + + + + + + +