From b85982301163f9e389ae56c371f60b1d2201e1a2 Mon Sep 17 00:00:00 2001 From: John Zhen M Date: Fri, 1 Sep 2017 12:10:36 -0700 Subject: [PATCH] -Hooking playback manager and play queue into main video player. --- .../fragments/playlist/PlaylistFragment.java | 34 +++++++++++- .../org/schabi/newpipe/player/BasePlayer.java | 8 +-- .../newpipe/player/MainVideoPlayer.java | 12 +++- .../newpipe/player/PlaybackManager.java | 55 ++++++++++++++----- .../schabi/newpipe/player/VideoPlayer.java | 38 ++++++++++++- .../newpipe/playlist/ExternalPlayQueue.java | 13 +---- .../schabi/newpipe/playlist/PlayQueue.java | 11 ++-- .../newpipe/playlist/PlayQueueItem.java | 5 -- app/src/main/res/layout/playlist_header.xml | 15 +++++ 9 files changed, 147 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java index 032b227e8..dd58b6567 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java @@ -1,5 +1,6 @@ package org.schabi.newpipe.fragments.playlist; +import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -17,6 +18,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; @@ -29,14 +31,19 @@ import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.playlist.PlayListExtractor; import org.schabi.newpipe.extractor.playlist.PlayListInfo; +import org.schabi.newpipe.extractor.stream_info.StreamInfo; import org.schabi.newpipe.fragments.BaseFragment; import org.schabi.newpipe.fragments.search.OnScrollBelowItemsListener; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.player.BasePlayer; +import org.schabi.newpipe.player.MainVideoPlayer; +import org.schabi.newpipe.player.VideoPlayer; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.Utils; import java.io.IOException; import java.io.Serializable; @@ -78,6 +85,7 @@ public class PlaylistFragment extends BaseFragment { private ImageView headerBannerView; private ImageView headerAvatarView; private TextView headerTitleView; + private Button headerPlayAllButton; /*////////////////////////////////////////////////////////////////////////*/ // Reactors @@ -95,6 +103,15 @@ public class PlaylistFragment extends BaseFragment { return instance; } + public void play(Context context, Class targetClazz) { + Intent mIntent = new Intent(context, targetClazz) + .putExtra("url", playlistUrl) + .putExtra("nextPage", 1) + .putExtra("index", 0) + .putExtra("stream", currentPlaylistInfo); + startActivity(mIntent); + } + /*////////////////////////////////////////////////////////////////////////// // Fragment's LifeCycle //////////////////////////////////////////////////////////////////////////*/ @@ -246,6 +263,9 @@ public class PlaylistFragment extends BaseFragment { headerBannerView = headerRootLayout.findViewById(R.id.playlist_banner_image); headerAvatarView = headerRootLayout.findViewById(R.id.playlist_avatar_view); headerTitleView = headerRootLayout.findViewById(R.id.playlist_title_view); + + headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_play_all_button); + headerPlayAllButton.setVisibility(View.VISIBLE); } protected void initListeners() { @@ -266,6 +286,13 @@ public class PlaylistFragment extends BaseFragment { loadMore(true); } }); + + headerPlayAllButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + play(activity, MainVideoPlayer.class); + } + }); } @@ -434,7 +461,7 @@ public class PlaylistFragment extends BaseFragment { } private void handlePlayListInfo(PlayListInfo info, boolean onlyVideos, boolean addVideos) { - currentPlaylistInfo = info; + if (currentPlaylistInfo == null) currentPlaylistInfo = info; animateView(errorPanel, false, 300); animateView(playlistStreams, true, 200); @@ -468,7 +495,10 @@ public class PlaylistFragment extends BaseFragment { if (!hasNextPage) infoListAdapter.showFooter(false); //if (!listRestored) { - if (addVideos) infoListAdapter.addInfoItemList(info.related_streams); + if (addVideos) { + infoListAdapter.addInfoItemList(info.related_streams); + currentPlaylistInfo.related_streams.addAll(info.related_streams); + } //} } 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 df588df5d..17c2f0b5a 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -72,6 +72,7 @@ import org.schabi.newpipe.Downloader; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream_info.StreamInfo; import org.schabi.newpipe.playlist.PlayQueue; +import org.schabi.newpipe.util.Utils; import java.io.File; import java.text.DecimalFormat; @@ -257,7 +258,6 @@ public abstract class BasePlayer implements Player.EventListener, changeState(STATE_LOADING); isPrepared = false; - mediaSource = buildMediaSource(url, format); if (simpleExoPlayer.getPlaybackState() != Player.STATE_IDLE) simpleExoPlayer.stop(); if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos); @@ -548,7 +548,7 @@ public abstract class BasePlayer implements Player.EventListener, @Override public void onPositionDiscontinuity() { int newIndex = simpleExoPlayer.getCurrentWindowIndex(); - + playbackManager.refreshMedia(newIndex); } /*////////////////////////////////////////////////////////////////////////// @@ -567,12 +567,12 @@ public abstract class BasePlayer implements Player.EventListener, @Override public void sync(final StreamInfo info) { - + videoTitle = info.title; + channelName = info.uploader; } @Override public MediaSource sourceOf(final StreamInfo info) { - return null; } 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 301200dfc..ebb5b52aa 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java @@ -454,9 +454,15 @@ public class MainVideoPlayer extends Activity { @Override 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 (e.getX() > playerImpl.getRootView().getWidth() / 2) playerImpl.onFastForward(); - else playerImpl.onFastRewind(); + //if (!playerImpl.isPlaying()) return false; + + if (e.getX() > playerImpl.getRootView().getWidth() / 2) + playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1); + //playerImpl.onFastForward(); + else + playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() - 1); + //playerImpl.onFastRewind(); + return true; } diff --git a/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java b/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java index 72f1daede..a98d9d3a1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java @@ -15,10 +15,14 @@ import java.util.Collections; import java.util.List; import io.reactivex.Maybe; +import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.annotations.NonNull; +import io.reactivex.schedulers.Schedulers; public class PlaybackManager { + private static final int WINDOW_SIZE = 5; + private DynamicConcatenatingMediaSource mediaSource; private List queueSource; private int sourceIndex; @@ -58,8 +62,11 @@ public class PlaybackManager { load(0); } - public void changeSource(final int index) { - + public void changeSource(final MediaSource newSource) { + listener.block(); + this.mediaSource.removeMediaSource(0); + this.mediaSource.addMediaSource(0, newSource); + listener.unblock(); } public void refreshMedia(final int newMediaIndex) { @@ -71,7 +78,7 @@ public class PlaybackManager { queueSource.remove(0); } else { //something went wrong - init(); + reload(); } } @@ -85,7 +92,8 @@ public class PlaybackManager { private Subscription loaderReactor; private void load() { - if (mediaSource.getSize() < 5 && queueSource.size() < 5) load(mediaSource.getSize()); + if (mediaSource.getSize() < WINDOW_SIZE && queueSource.size() < WINDOW_SIZE) + load(mediaSource.getSize()); } private void load(final int from) { @@ -94,23 +102,33 @@ public class PlaybackManager { if (loaderReactor != null) loaderReactor.cancel(); List> maybes = new ArrayList<>(); - for (int i = from; i < 5; i++) { + for (int i = from; i < WINDOW_SIZE; i++) { final int index = playQueue.getIndex() + i; final PlayQueueItem item = playQueue.get(index); - queueSource.set(i, item); + + if (queueSource.size() > i) queueSource.set(i, item); + else queueSource.add(item); + maybes.add(item.getStream()); } - Maybe.concat(maybes).subscribe(new Subscriber() { + Maybe.concat(maybes).subscribe(getSubscriber()); + } + + private Subscriber getSubscriber() { + return new Subscriber() { @Override public void onSubscribe(Subscription s) { + if (loaderReactor != null) loaderReactor.cancel(); loaderReactor = s; + s.request(1); } @Override public void onNext(StreamInfo streamInfo) { mediaSource.addMediaSource(listener.sourceOf(streamInfo)); - onLoaded(); + tryUnblock(); + loaderReactor.request(1); } @Override @@ -120,11 +138,13 @@ public class PlaybackManager { @Override public void onComplete() { + if (loaderReactor != null) loaderReactor.cancel(); + loaderReactor = null; } - }); + }; } - private void onLoaded() { + private void tryUnblock() { if (mediaSource.getSize() > 0 && queueSource.size() > 0) listener.unblock(); } @@ -134,11 +154,15 @@ public class PlaybackManager { } private void clear(int from) { - listener.block(); while (mediaSource.getSize() > from) { queueSource.remove(from); mediaSource.removeMediaSource(from); } + } + + private void clear() { + listener.block(); + clear(0); listener.unblock(); } @@ -153,7 +177,7 @@ public class PlaybackManager { @Override public void onNext(@NonNull PlayQueueEvent event) { - if (playQueue.getStreams().size() - playQueue.getIndex() < 10 && !playQueue.isComplete()) { + if (playQueue.getStreams().size() - playQueue.getIndex() < WINDOW_SIZE && !playQueue.isComplete()) { listener.block(); playQueue.fetch(); } @@ -177,14 +201,14 @@ public class PlaybackManager { load(1); break; case CLEAR: - clear(0); + clear(); break; case NEXT: default: break; } - onLoaded(); + tryUnblock(); if (playQueueReactor != null) playQueueReactor.request(1); } @@ -195,12 +219,13 @@ public class PlaybackManager { @Override public void onComplete() { - // Never completes, only canceled + dispose(); } }; } public void dispose() { if (playQueueReactor != null) playQueueReactor.cancel(); + playQueueReactor = null; } } 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 fa25cc957..644deaf32 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -56,7 +56,11 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.extractor.stream.StreamInfo; +import org.schabi.newpipe.extractor.playlist.PlayListInfo; +import org.schabi.newpipe.playlist.ExternalPlayQueue; import org.schabi.newpipe.util.AnimationUtils; +import org.schabi.newpipe.util.Utils; import java.io.Serializable; import java.util.ArrayList; @@ -198,7 +202,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. } @SuppressWarnings("unchecked") - public void handleIntent(Intent intent) { + public void handleIntent2(Intent intent) { super.handleIntent(intent); if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]"); if (intent == null) return; @@ -217,6 +221,38 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. play(true); } + @Override + public MediaSource sourceOf(final StreamInfo info) { + videoStreamsList = Utils.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false); + videoOnlyAudioStream = Utils.getHighestQualityAudio(info.audio_streams); + + return buildMediaSource(getSelectedVideoStream().url, MediaFormat.getSuffixById(getSelectedVideoStream().format)); + } + + @Override + public void unblock() { + play(true); + super.unblock(); + } + + public void handleIntent(Intent intent) { + if (intent == null) return; + + selectedIndexStream = 0; + + String url = intent.getStringExtra("url"); + int nextPage = intent.getIntExtra("nextPage", 0); + int index = intent.getIntExtra("index", 0); + + PlayListInfo info; + Serializable serializable = intent.getSerializableExtra("stream"); + if (serializable instanceof PlayListInfo) info = (PlayListInfo) serializable; + else return; + + playQueue = new ExternalPlayQueue(url, info, nextPage, index); + playbackManager = new PlaybackManager(this, playQueue); + mediaSource = playbackManager.getMediaSource(); + } public void play(boolean autoPlay) { playUrl(getSelectedVideoStream().url, MediaFormat.getSuffixById(getSelectedVideoStream().format), autoPlay); diff --git a/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java index 3d1831a0e..4fab68a1b 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java @@ -34,12 +34,11 @@ public class ExternalPlayQueue extends PlayQueue { final PlayListInfo info, final int nextPage, final int index) { - super(index); + super(index, extractPlaylistItems(info)); + this.service = getService(info.service_id); this.pageNumber = new AtomicInteger(nextPage); this.playlistUrl = playlistUrl; - - getStreams().addAll(extractPlaylistItems(info)); } @Override @@ -47,12 +46,6 @@ public class ExternalPlayQueue extends PlayQueue { return isComplete; } - @Override - public void load(int index) { - if (index > getStreams().size() || getStreams().get(index) == null) return; - getStreams().get(index).load(); - } - @Override public PlayQueueItem get(int index) { if (index > getStreams().size() || getStreams().get(index) == null) return null; @@ -93,7 +86,7 @@ public class ExternalPlayQueue extends PlayQueue { if (fetchReactor != null) fetchReactor.dispose(); } - private List extractPlaylistItems(final PlayListInfo info) { + private static List extractPlaylistItems(final PlayListInfo info) { List result = new ArrayList<>(); for (final InfoItem stream : info.related_streams) { if (stream instanceof StreamInfoItem) { diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java index 67adc0cf2..99f46261e 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java @@ -25,8 +25,14 @@ public abstract class PlayQueue { private BehaviorSubject changeBroadcast; private Flowable playQueueFlowable; - PlayQueue(final int index) { + PlayQueue() { + this(0, Collections.emptyList()); + } + + PlayQueue(final int index, final List startWith) { streams = Collections.synchronizedList(new ArrayList()); + streams.addAll(startWith); + queueIndex = new AtomicInteger(index); changeBroadcast = BehaviorSubject.create(); @@ -37,9 +43,6 @@ public abstract class PlayQueue { // single stream or local queues are always complete public abstract boolean isComplete(); - // load in the background the item at index, may do nothing if the queue is incomplete - public abstract void load(int index); - // load partial queue in the background, does nothing if the queue is complete public abstract void fetch(); 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 0f2cb3202..a028d33e1 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java @@ -69,10 +69,6 @@ public class PlayQueueItem { return stream; } - public void load() { - stream.subscribe(); - } - @NonNull private Maybe getInfo() { final Callable task = new Callable() { @@ -101,7 +97,6 @@ public class PlayQueueItem { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnError(onError) - .onErrorComplete() .doOnComplete(onComplete) .cache(); } diff --git a/app/src/main/res/layout/playlist_header.xml b/app/src/main/res/layout/playlist_header.xml index 192363359..7de6f79a9 100644 --- a/app/src/main/res/layout/playlist_header.xml +++ b/app/src/main/res/layout/playlist_header.xml @@ -78,4 +78,19 @@ tools:ignore="RtlHardcoded" tools:text="234 videos"/> +