-Changed intents to start all players, including player swap.
-Make play queue and items serializable -Removed now deprecated code for playing url in exoplayer
This commit is contained in:
parent
705028c79d
commit
b54d18d888
12 changed files with 368 additions and 261 deletions
|
@ -20,15 +20,12 @@ import org.schabi.newpipe.extractor.ListExtractor;
|
|||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.player.MainVideoPlayer;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
@ -141,7 +138,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
}
|
||||
|
||||
imageLoader.displayImage(result.uploader_avatar_url, headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS);
|
||||
headerStreamCount.setText(result.stream_count + " videos");
|
||||
headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count));
|
||||
|
||||
if (!result.errors.isEmpty()) {
|
||||
showSnackBarError(result.errors, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.service_id), result.url, 0);
|
||||
|
@ -150,19 +147,15 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
play();
|
||||
final Intent intent = NavigationHelper.getExternalPlaylistIntent(
|
||||
activity, MainVideoPlayer.class, currentInfo, infoListAdapter.getItemsList(), 0
|
||||
);
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void play() {
|
||||
Intent mIntent = new Intent(activity, MainVideoPlayer.class)
|
||||
.putExtra("serviceId", serviceId)
|
||||
.putExtra("index", 0)
|
||||
.putExtra("streams", infoListAdapter.getItemsList())
|
||||
.putExtra("nextPageUrl", currentInfo.next_streams_url);
|
||||
startActivity(mIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(ListExtractor.NextItemsResult result) {
|
||||
|
|
|
@ -268,9 +268,9 @@ public class BackgroundPlayer extends Service {
|
|||
@Override
|
||||
public void handleIntent(Intent intent) {
|
||||
super.handleIntent(intent);
|
||||
Serializable serializable = intent.getSerializableExtra(BackgroundPlayer.AUDIO_STREAM);
|
||||
if (serializable instanceof AudioStream) audioStream = (AudioStream) serializable;
|
||||
playUrl(audioStream.url, MediaFormat.getSuffixById(audioStream.format), true);
|
||||
|
||||
notBuilder = createNotification();
|
||||
startForeground(NOTIFICATION_ID, notBuilder.build());
|
||||
|
||||
if (bigNotRemoteView != null) bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
|
||||
if (notRemoteView != null) notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
|
||||
|
@ -294,14 +294,6 @@ public class BackgroundPlayer extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playUrl(String url, String format, boolean autoPlay) {
|
||||
super.playUrl(url, format, autoPlay);
|
||||
|
||||
notBuilder = createNotification();
|
||||
startForeground(NOTIFICATION_ID, notBuilder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepared(boolean playWhenReady) {
|
||||
super.onPrepared(playWhenReady);
|
||||
|
@ -348,15 +340,13 @@ public class BackgroundPlayer extends Service {
|
|||
|
||||
@Override
|
||||
public void onFastRewind() {
|
||||
// super.onFastRewind();
|
||||
simpleExoPlayer.seekTo(0, 0);
|
||||
playQueue.setIndex(playQueue.getIndex() - 1);
|
||||
triggerProgressUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFastForward() {
|
||||
// super.onFastForward();
|
||||
simpleExoPlayer.seekTo(2, 0);
|
||||
playQueue.setIndex(playQueue.getIndex() + 1);
|
||||
triggerProgressUpdate();
|
||||
}
|
||||
|
||||
|
@ -380,6 +370,15 @@ public class BackgroundPlayer extends Service {
|
|||
@Override
|
||||
public void onError(Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback Listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
super.shutdown();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
|
|
|
@ -70,13 +70,21 @@ import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListene
|
|||
|
||||
import org.schabi.newpipe.Downloader;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.playlist.ExternalPlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Formatter;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
|
@ -90,7 +98,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
AudioManager.OnAudioFocusChangeListener, MediaSourceManager.PlaybackListener {
|
||||
// TODO: Check api version for deprecated audio manager methods
|
||||
|
||||
public static final boolean DEBUG = false;
|
||||
public static final boolean DEBUG = true;
|
||||
public static final String TAG = "BasePlayer";
|
||||
|
||||
protected Context context;
|
||||
|
@ -104,6 +112,11 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
// Intent
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static final String INTENT_TYPE = "intent_type";
|
||||
public static final String SINGLE_STREAM = "single";
|
||||
public static final String EXTERNAL_PLAYLIST = "external";
|
||||
public static final String INTERNAL_PLAYLIST = "internal";
|
||||
|
||||
public static final String VIDEO_URL = "video_url";
|
||||
public static final String VIDEO_TITLE = "video_title";
|
||||
public static final String VIDEO_THUMBNAIL_URL = "video_thumbnail_url";
|
||||
|
@ -111,6 +124,9 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
public static final String CHANNEL_NAME = "channel_name";
|
||||
public static final String PLAYBACK_SPEED = "playback_speed";
|
||||
|
||||
public static final String RESTORE_QUEUE_INDEX = "restore_queue_index";
|
||||
public static final String RESTORE_WINDOW_POS = "restore_window_pos";
|
||||
|
||||
protected Bitmap videoThumbnail = null;
|
||||
protected String videoUrl = "";
|
||||
protected String videoTitle = "";
|
||||
|
@ -125,8 +141,8 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
protected MediaSourceManager playbackManager;
|
||||
protected PlayQueue playQueue;
|
||||
|
||||
private int windowIndex;
|
||||
private long windowPos;
|
||||
protected int restoreQueueIndex;
|
||||
protected long restoreWindowPos;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Player
|
||||
|
@ -219,15 +235,54 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
||||
if (intent == null) return;
|
||||
|
||||
videoUrl = intent.getStringExtra(VIDEO_URL);
|
||||
videoTitle = intent.getStringExtra(VIDEO_TITLE);
|
||||
videoThumbnailUrl = intent.getStringExtra(VIDEO_THUMBNAIL_URL);
|
||||
videoStartPos = intent.getLongExtra(START_POSITION, -1L);
|
||||
uploaderName = intent.getStringExtra(CHANNEL_NAME);
|
||||
restoreQueueIndex = intent.getIntExtra(RESTORE_QUEUE_INDEX, 0);
|
||||
restoreWindowPos = intent.getLongExtra(START_POSITION, 0);
|
||||
setPlaybackSpeed(intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed()));
|
||||
|
||||
switch (intent.getStringExtra(INTENT_TYPE)) {
|
||||
case SINGLE_STREAM:
|
||||
handleSinglePlaylistIntent(intent);
|
||||
break;
|
||||
case EXTERNAL_PLAYLIST:
|
||||
handleExternalPlaylistIntent(intent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
initThumbnail();
|
||||
//play(getSelectedVideoStream(), true);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleExternalPlaylistIntent(Intent intent) {
|
||||
final int serviceId = intent.getIntExtra(ExternalPlayQueue.SERVICE_ID, -1);
|
||||
final int index = intent.getIntExtra(ExternalPlayQueue.INDEX, 0);
|
||||
final Serializable serializable = intent.getSerializableExtra(ExternalPlayQueue.STREAMS);
|
||||
final String nextPageUrl = intent.getStringExtra(ExternalPlayQueue.NEXT_PAGE_URL);
|
||||
|
||||
List<InfoItem> info = new ArrayList<>();
|
||||
if (serializable instanceof List) {
|
||||
for (final Object o : (List) serializable) {
|
||||
if (o instanceof InfoItem) info.add((StreamInfoItem) o);
|
||||
}
|
||||
}
|
||||
|
||||
playQueue = new ExternalPlayQueue(serviceId, nextPageUrl, info, index);
|
||||
playQueue.init();
|
||||
|
||||
playbackManager = new MediaSourceManager(this, playQueue);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleSinglePlaylistIntent(Intent intent) {
|
||||
final Serializable serializable = intent.getSerializableExtra(SinglePlayQueue.STREAM);
|
||||
if (!(serializable instanceof StreamInfo)) return;
|
||||
|
||||
playQueue = new SinglePlayQueue((StreamInfo) serializable, PlayQueueItem.DEFAULT_QUALITY);
|
||||
playQueue.init();
|
||||
|
||||
playbackManager = new MediaSourceManager(this, playQueue);
|
||||
}
|
||||
|
||||
public void initThumbnail() {
|
||||
|
@ -247,27 +302,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
});
|
||||
}
|
||||
|
||||
public void playUrl(String url, String format, boolean autoPlay) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "play() called with: url = [" + url + "], autoPlay = [" + autoPlay + "]");
|
||||
}
|
||||
|
||||
if (url == null || simpleExoPlayer == null) {
|
||||
RuntimeException runtimeException = new RuntimeException((url == null ? "Url " : "Player ") + " null");
|
||||
onError(runtimeException);
|
||||
throw runtimeException;
|
||||
}
|
||||
|
||||
changeState(STATE_LOADING);
|
||||
|
||||
isPrepared = false;
|
||||
|
||||
if (simpleExoPlayer.getPlaybackState() != Player.STATE_IDLE) simpleExoPlayer.stop();
|
||||
if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos);
|
||||
simpleExoPlayer.prepare(mediaSource);
|
||||
simpleExoPlayer.setPlayWhenReady(autoPlay);
|
||||
}
|
||||
|
||||
public void destroyPlayer() {
|
||||
if (DEBUG) Log.d(TAG, "destroyPlayer() called");
|
||||
if (simpleExoPlayer != null) {
|
||||
|
@ -466,7 +500,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
|
||||
public void onRepeatClicked() {
|
||||
if (DEBUG) Log.d(TAG, "onRepeatClicked() called");
|
||||
// TODO: implement repeat all when playlist is implemented
|
||||
|
||||
final int mode;
|
||||
|
||||
|
@ -482,7 +515,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
mode = Player.REPEAT_MODE_OFF;
|
||||
break;
|
||||
}
|
||||
// Switch the modes between DISABLED and REPEAT_ONE, till playlist is implemented
|
||||
|
||||
simpleExoPlayer.setRepeatMode(mode);
|
||||
if (DEBUG) Log.d(TAG, "onRepeatClicked() currentRepeatMode = " + simpleExoPlayer.getRepeatMode());
|
||||
}
|
||||
|
@ -566,8 +599,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
Log.d(TAG, "Blocking...");
|
||||
|
||||
simpleExoPlayer.stop();
|
||||
windowIndex = simpleExoPlayer.getCurrentWindowIndex();
|
||||
windowPos = Math.max(0, simpleExoPlayer.getContentPosition());
|
||||
|
||||
changeState(STATE_BUFFERING);
|
||||
}
|
||||
|
@ -576,42 +607,51 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
public void unblock() {
|
||||
Log.d(TAG, "Unblocking...");
|
||||
|
||||
if (windowIndex != playbackManager.getCurrentSourceIndex()) {
|
||||
windowIndex = playbackManager.getCurrentSourceIndex();
|
||||
windowPos = 0;
|
||||
if (restoreQueueIndex != playQueue.getIndex()) {
|
||||
restoreQueueIndex = playQueue.getIndex();
|
||||
restoreWindowPos = 0;
|
||||
}
|
||||
|
||||
simpleExoPlayer.prepare(playbackManager.getMediaSource());
|
||||
simpleExoPlayer.seekTo(windowIndex, windowPos);
|
||||
simpleExoPlayer.setPlayWhenReady(true);
|
||||
simpleExoPlayer.seekTo(playbackManager.getCurrentSourceIndex(), restoreWindowPos);
|
||||
simpleExoPlayer.setPlayWhenReady(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sync(final int windowIndex, final StreamInfo info) {
|
||||
public void sync(final StreamInfo info, final int sortedStreamsIndex) {
|
||||
Log.d(TAG, "Syncing...");
|
||||
|
||||
videoUrl = info.url;
|
||||
videoThumbnailUrl = info.thumbnail_url;
|
||||
videoTitle = info.name;
|
||||
|
||||
if (simpleExoPlayer.getCurrentWindowIndex() != windowIndex) {
|
||||
if (simpleExoPlayer.getCurrentWindowIndex() != playbackManager.getCurrentSourceIndex()) {
|
||||
Log.w(TAG, "Rewinding to correct window");
|
||||
simpleExoPlayer.seekTo(windowIndex, 0L);
|
||||
simpleExoPlayer.seekTo(playbackManager.getCurrentSourceIndex(), 0L);
|
||||
}
|
||||
|
||||
simpleExoPlayer.setPlayWhenReady(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaSource sourceOf(final StreamInfo info) {
|
||||
public MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
Log.d(TAG, "Shutting down...");
|
||||
|
||||
playbackManager.dispose();
|
||||
playQueue.dispose();
|
||||
destroy();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// General Player
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void onError(Exception exception){
|
||||
destroy();
|
||||
}
|
||||
public abstract void onError(Exception exception);
|
||||
|
||||
public void onPrepared(boolean playWhenReady) {
|
||||
if (DEBUG) Log.d(TAG, "onPrepared() called with: playWhenReady = [" + playWhenReady + "]");
|
||||
|
@ -840,4 +880,12 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
public void setPlaybackSpeed(float speed) {
|
||||
simpleExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, 1f));
|
||||
}
|
||||
|
||||
public int getCurrentQueueIndex() {
|
||||
return playQueue != null ? playQueue.getIndex() : -1;
|
||||
}
|
||||
|
||||
public PlayQueue getPlayQueue() {
|
||||
return playQueue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,8 @@ public class MainVideoPlayer extends Activity {
|
|||
if (activityPaused) {
|
||||
playerImpl.initPlayer();
|
||||
playerImpl.getPlayPauseButton().setImageResource(R.drawable.ic_play_arrow_white);
|
||||
playerImpl.play(false);
|
||||
playerImpl.playQueue.init();
|
||||
//playerImpl.play(false);
|
||||
activityPaused = false;
|
||||
}
|
||||
}
|
||||
|
@ -230,21 +231,25 @@ public class MainVideoPlayer extends Activity {
|
|||
channelTextView.setText(getUploaderName());
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback Listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void sync(final int windowIndex, final StreamInfo info) {
|
||||
super.sync(windowIndex, info);
|
||||
public void shutdown() {
|
||||
super.shutdown();
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sync(final StreamInfo info, final int sortedStreamsIndex) {
|
||||
super.sync(info, sortedStreamsIndex);
|
||||
titleTextView.setText(getVideoTitle());
|
||||
channelTextView.setText(getUploaderName());
|
||||
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playUrl(String url, String format, boolean autoPlay) {
|
||||
super.playUrl(url, format, autoPlay);
|
||||
playPauseButton.setImageResource(autoPlay ? R.drawable.ic_pause_white : R.drawable.ic_play_arrow_white);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFullScreenButtonClicked() {
|
||||
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
|
||||
|
@ -331,7 +336,6 @@ public class MainVideoPlayer extends Activity {
|
|||
public void onError(Exception exception) {
|
||||
exception.printStackTrace();
|
||||
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
|
||||
//finish();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.schabi.newpipe.player;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
|
@ -19,7 +18,6 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.MaybeObserver;
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
|
@ -67,13 +65,15 @@ class MediaSourceManager {
|
|||
* Signals to the listener to synchronize the player's window to the manager's
|
||||
* window.
|
||||
* */
|
||||
void sync(final int windowIndex, final StreamInfo info);
|
||||
void sync(final StreamInfo info, final int sortedStreamsIndex);
|
||||
|
||||
/*
|
||||
* Requests the listener to resolve a stream info into a media source respective
|
||||
* of the listener's implementation (background, popup or main video player),
|
||||
* */
|
||||
MediaSource sourceOf(final StreamInfo info);
|
||||
MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex);
|
||||
|
||||
void shutdown();
|
||||
}
|
||||
|
||||
MediaSourceManager(@NonNull final MediaSourceManager.PlaybackListener listener,
|
||||
|
@ -118,9 +118,7 @@ class MediaSourceManager {
|
|||
|
||||
void report(final Exception error) {
|
||||
// ignore error checking for now, just remove the current index
|
||||
if (error != null) {
|
||||
tryBlock();
|
||||
}
|
||||
if (error == null || !tryBlock()) return;
|
||||
|
||||
final int index = playQueue.getIndex();
|
||||
playQueue.remove(index);
|
||||
|
@ -129,6 +127,19 @@ class MediaSourceManager {
|
|||
load();
|
||||
}
|
||||
|
||||
int queueIndexOf(final int sourceIndex) {
|
||||
return sourceIndex < sourceToQueueIndex.size() ? sourceToQueueIndex.get(sourceIndex) : -1;
|
||||
}
|
||||
|
||||
void updateCurrent(final int newSortedStreamsIndex) {
|
||||
if (!tryBlock()) return;
|
||||
|
||||
PlayQueueItem item = playQueue.getCurrent();
|
||||
item.setSortedQualityIndex(newSortedStreamsIndex);
|
||||
resetSources();
|
||||
load();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (loadingReactor != null) loadingReactor.cancel();
|
||||
if (playQueueReactor != null) playQueueReactor.cancel();
|
||||
|
@ -180,7 +191,10 @@ class MediaSourceManager {
|
|||
if (!isPlayQueueReady()) {
|
||||
tryBlock();
|
||||
playQueue.fetch();
|
||||
} else if (playQueue.isEmpty()) {
|
||||
playbackListener.shutdown();
|
||||
}
|
||||
|
||||
if (playQueueReactor != null) playQueueReactor.request(1);
|
||||
}
|
||||
|
||||
|
@ -240,14 +254,16 @@ class MediaSourceManager {
|
|||
}
|
||||
|
||||
private void sync() {
|
||||
final PlayQueueItem currentItem = playQueue.getCurrent();
|
||||
|
||||
final Consumer<StreamInfo> onSuccess = new Consumer<StreamInfo>() {
|
||||
@Override
|
||||
public void accept(StreamInfo streamInfo) throws Exception {
|
||||
playbackListener.sync(getCurrentSourceIndex(), streamInfo);
|
||||
playbackListener.sync(streamInfo, currentItem.getSortedQualityIndex());
|
||||
}
|
||||
};
|
||||
|
||||
playQueue.getCurrent().getStream().subscribe(onSuccess);
|
||||
currentItem.getStream().subscribe(onSuccess);
|
||||
}
|
||||
|
||||
private void load() {
|
||||
|
@ -280,7 +296,7 @@ class MediaSourceManager {
|
|||
|
||||
@Override
|
||||
public void onSuccess(@NonNull StreamInfo streamInfo) {
|
||||
final MediaSource source = playbackListener.sourceOf(streamInfo);
|
||||
final MediaSource source = playbackListener.sourceOf(streamInfo, item.getSortedQualityIndex());
|
||||
insert(playQueue.indexOf(item), source);
|
||||
if (tryUnblock()) sync();
|
||||
}
|
||||
|
@ -305,13 +321,12 @@ class MediaSourceManager {
|
|||
// Media Source List Manipulation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void replace(final int queueIndex, final MediaSource source) {
|
||||
private void reset(final int queueIndex) {
|
||||
if (queueIndex < 0) return;
|
||||
|
||||
final int sourceIndex = sourceToQueueIndex.indexOf(queueIndex);
|
||||
if (sourceIndex != -1) {
|
||||
// Add the source after the one to remove, so the window will remain the same in the player
|
||||
sources.addMediaSource(sourceIndex + 1, source);
|
||||
sourceToQueueIndex.remove(sourceIndex);
|
||||
sources.removeMediaSource(sourceIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -272,10 +272,11 @@ public class PopupVideoPlayer extends Service {
|
|||
notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, 77);
|
||||
break;
|
||||
case Player.REPEAT_MODE_ONE:
|
||||
notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, 255);
|
||||
//todo change image
|
||||
notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, 168);
|
||||
break;
|
||||
case Player.REPEAT_MODE_ALL:
|
||||
// Waiting :)
|
||||
notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, 255);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -390,18 +391,6 @@ public class PopupVideoPlayer extends Service {
|
|||
super("VideoPlayerImpl" + PopupVideoPlayer.TAG, PopupVideoPlayer.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playUrl(String url, String format, boolean autoPlay) {
|
||||
super.playUrl(url, format, autoPlay);
|
||||
|
||||
windowLayoutParams.width = (int) popupWidth;
|
||||
windowLayoutParams.height = (int) getMinimumVideoHeight(popupWidth);
|
||||
windowManager.updateViewLayout(getRootView(), windowLayoutParams);
|
||||
|
||||
notBuilder = createNotification();
|
||||
startForeground(NOTIFICATION_ID, notBuilder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initViews(View rootView) {
|
||||
super.initViews(rootView);
|
||||
|
@ -475,7 +464,6 @@ public class PopupVideoPlayer extends Service {
|
|||
public void onError(Exception exception) {
|
||||
exception.printStackTrace();
|
||||
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -486,6 +474,16 @@ public class PopupVideoPlayer extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback Listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
super.shutdown();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Broadcast Receiver
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -583,8 +581,14 @@ public class PopupVideoPlayer extends Service {
|
|||
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() > popupWidth / 2) playerImpl.onFastForward();
|
||||
else playerImpl.onFastRewind();
|
||||
if (e.getX() > popupWidth / 2) {
|
||||
//playerImpl.onFastForward();
|
||||
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1);
|
||||
} else {
|
||||
//playerImpl.onFastRewind();
|
||||
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() - 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -766,7 +770,7 @@ public class PopupVideoPlayer extends Service {
|
|||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
playerImpl.play(true);
|
||||
playerImpl.playQueue.init();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -53,13 +53,13 @@ import com.google.android.exoplayer2.source.MergingMediaSource;
|
|||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.playlist.ExternalPlayQueue;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
|
||||
|
@ -89,7 +89,10 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
public static final String INDEX_SEL_VIDEO_STREAM = "index_selected_video_stream";
|
||||
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
|
||||
|
||||
private int selectedIndexStream;
|
||||
public static final String PLAY_QUEUE = "play_queue";
|
||||
public static final String PLAYER_INTENT = "player_intent";
|
||||
|
||||
private int selectedIndexStream = -1;
|
||||
private ArrayList<VideoStream> videoStreamsList = new ArrayList<>();
|
||||
private AudioStream videoOnlyAudioStream;
|
||||
|
||||
|
@ -202,65 +205,55 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
simpleExoPlayer.setVideoListener(this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleSingleStreamIntent(Intent intent) {
|
||||
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
||||
if (intent == null) return;
|
||||
|
||||
selectedIndexStream = intent.getIntExtra(INDEX_SEL_VIDEO_STREAM, -1);
|
||||
|
||||
Serializable serializable = intent.getSerializableExtra(VIDEO_STREAMS_LIST);
|
||||
|
||||
if (serializable instanceof ArrayList) videoStreamsList = (ArrayList<VideoStream>) serializable;
|
||||
if (serializable instanceof Vector) videoStreamsList = new ArrayList<>((List<VideoStream>) serializable);
|
||||
|
||||
Serializable audioStream = intent.getSerializableExtra(VIDEO_ONLY_AUDIO_STREAM);
|
||||
if (audioStream != null) videoOnlyAudioStream = (AudioStream) audioStream;
|
||||
|
||||
startedFromNewPipe = intent.getBooleanExtra(STARTED_FROM_NEWPIPE, true);
|
||||
play(true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleIntent(Intent intent) {
|
||||
super.handleIntent(intent);
|
||||
|
||||
if (intent == null) return;
|
||||
|
||||
handleExternalPlaylistIntent(intent);
|
||||
if (intent.getStringExtra(INTENT_TYPE).equals(PLAYER_INTENT)) {
|
||||
handlePlayerIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleExternalPlaylistIntent(Intent intent) {
|
||||
selectedIndexStream = 0;
|
||||
public void handleSinglePlaylistIntent(Intent intent) {
|
||||
final Serializable serializable = intent.getSerializableExtra(SinglePlayQueue.STREAM);
|
||||
if (!(serializable instanceof StreamInfo)) return;
|
||||
|
||||
final int serviceId = intent.getIntExtra("serviceId", -1);
|
||||
final int index = intent.getIntExtra("index", 0);
|
||||
final Serializable serializable = intent.getSerializableExtra("streams");
|
||||
final String nextPageUrl = intent.getStringExtra("nextPageUrl");
|
||||
final int sortedStreamsIndex = intent.getIntExtra(INDEX_SEL_VIDEO_STREAM, -1);
|
||||
|
||||
List<InfoItem> info = new ArrayList<>();
|
||||
if (serializable instanceof List) {
|
||||
for (final Object o : (List) serializable) {
|
||||
if (o instanceof InfoItem) info.add((StreamInfoItem) o);
|
||||
}
|
||||
}
|
||||
playQueue = new SinglePlayQueue((StreamInfo) serializable, sortedStreamsIndex);
|
||||
playQueue.init();
|
||||
|
||||
playQueue = new ExternalPlayQueue(serviceId, nextPageUrl, info, index);
|
||||
playbackManager = new MediaSourceManager(this, playQueue);
|
||||
}
|
||||
|
||||
public void play(boolean autoPlay) {
|
||||
playUrl(getSelectedVideoStream().url, MediaFormat.getSuffixById(getSelectedVideoStream().format), autoPlay);
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handlePlayerIntent(Intent intent) {
|
||||
final Serializable serializable = intent.getSerializableExtra(PLAY_QUEUE);
|
||||
if (!(serializable instanceof PlayQueue)) return;
|
||||
|
||||
selectedIndexStream = intent.getIntExtra(INDEX_SEL_VIDEO_STREAM, -1);
|
||||
|
||||
playQueue = (PlayQueue) serializable;
|
||||
playQueue.init();
|
||||
|
||||
playbackManager = new MediaSourceManager(this, playQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sync(final int windowIndex, final StreamInfo info) {
|
||||
super.sync(windowIndex, info);
|
||||
public void sync(final StreamInfo info, final int sortedStreamsIndex) {
|
||||
super.sync(info, sortedStreamsIndex);
|
||||
|
||||
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
|
||||
videoStreamsList = new ArrayList<>(videos);
|
||||
selectedIndexStream = ListHelper.getDefaultResolutionIndex(context, videos);
|
||||
|
||||
if (sortedStreamsIndex == PlayQueueItem.DEFAULT_QUALITY) {
|
||||
selectedIndexStream = ListHelper.getDefaultResolutionIndex(context, videos);
|
||||
} else {
|
||||
selectedIndexStream = sortedStreamsIndex;
|
||||
}
|
||||
|
||||
qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId);
|
||||
buildQualityMenu(qualityPopupMenu);
|
||||
|
@ -270,9 +263,16 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
}
|
||||
|
||||
@Override
|
||||
public MediaSource sourceOf(final StreamInfo info) {
|
||||
public MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex) {
|
||||
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
|
||||
final VideoStream video = videos.get(ListHelper.getDefaultResolutionIndex(context, videos));
|
||||
|
||||
final VideoStream video;
|
||||
if (sortedStreamsIndex == PlayQueueItem.DEFAULT_QUALITY) {
|
||||
final int index = ListHelper.getDefaultResolutionIndex(context, videos);
|
||||
video = videos.get(index);
|
||||
} else {
|
||||
video = videos.get(sortedStreamsIndex);
|
||||
}
|
||||
|
||||
final MediaSource mediaSource = super.buildMediaSource(video.url, MediaFormat.getSuffixById(video.format));
|
||||
if (!video.isVideoOnly) return mediaSource;
|
||||
|
@ -282,26 +282,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
return new MergingMediaSource(mediaSource, new ExtractorMediaSource(audioUri, cacheDataSourceFactory, extractorsFactory, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playUrl(String url, String format, boolean autoPlay) {
|
||||
if (DEBUG) Log.d(TAG, "play() called with: url = [" + url + "], autoPlay = [" + autoPlay + "]");
|
||||
qualityChanged = false;
|
||||
|
||||
if (url == null || simpleExoPlayer == null) {
|
||||
RuntimeException runtimeException = new RuntimeException((url == null ? "Url " : "Player ") + " null");
|
||||
onError(runtimeException);
|
||||
throw runtimeException;
|
||||
}
|
||||
|
||||
qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId);
|
||||
buildQualityMenu(qualityPopupMenu);
|
||||
|
||||
playbackSpeedPopupMenu.getMenu().removeGroup(playbackSpeedPopupMenuGroupId);
|
||||
buildPlaybackSpeedMenu(playbackSpeedPopupMenu);
|
||||
|
||||
super.playUrl(url, format, autoPlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaSource buildMediaSource(String url, String overrideExtension) {
|
||||
MediaSource mediaSource = super.buildMediaSource(url, overrideExtension);
|
||||
|
@ -460,6 +440,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
|
||||
if (duration != playbackSeekBar.getMax()) {
|
||||
playbackEndTime.setText(getTimeString(duration));
|
||||
playbackSeekBar.setMax(duration);
|
||||
}
|
||||
if (currentState != STATE_PAUSED) {
|
||||
if (currentState != STATE_PAUSED_SEEK) playbackSeekBar.setProgress(currentProgress);
|
||||
|
@ -478,7 +459,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
if (DEBUG) Log.d(TAG, "onVideoPlayPauseRepeat() called");
|
||||
if (qualityChanged) {
|
||||
setVideoStartPos(0);
|
||||
play(true);
|
||||
//play(true);
|
||||
} else super.onVideoPlayPauseRepeat();
|
||||
}
|
||||
|
||||
|
@ -528,11 +509,10 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
|
||||
if (qualityPopupMenuGroupId == menuItem.getGroupId()) {
|
||||
if (selectedIndexStream == menuItem.getItemId()) return true;
|
||||
setVideoStartPos(simpleExoPlayer.getCurrentPosition());
|
||||
|
||||
selectedIndexStream = menuItem.getItemId();
|
||||
if (!(getCurrentState() == STATE_COMPLETED)) play(wasPlaying);
|
||||
else qualityChanged = true;
|
||||
restoreQueueIndex = playQueue.getIndex();
|
||||
restoreWindowPos = simpleExoPlayer.getCurrentPosition();
|
||||
playbackManager.updateCurrent(menuItem.getItemId());
|
||||
|
||||
qualityTextView.setText(menuItem.getTitle());
|
||||
return true;
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.util.Log;
|
|||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
|
@ -18,8 +17,14 @@ import io.reactivex.annotations.NonNull;
|
|||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class ExternalPlayQueue extends PlayQueue {
|
||||
public final class ExternalPlayQueue extends PlayQueue {
|
||||
private final String TAG = "ExternalPlayQueue@" + Integer.toHexString(hashCode());
|
||||
|
||||
public static final String SERVICE_ID = "service_id";
|
||||
public static final String INDEX = "index";
|
||||
public static final String STREAMS = "streams";
|
||||
public static final String NEXT_PAGE_URL = "next_page_url";
|
||||
|
||||
private static final int RETRY_COUNT = 2;
|
||||
|
||||
private boolean isComplete;
|
||||
|
@ -27,7 +32,7 @@ public class ExternalPlayQueue extends PlayQueue {
|
|||
private int serviceId;
|
||||
private String playlistUrl;
|
||||
|
||||
private Disposable fetchReactor;
|
||||
private transient Disposable fetchReactor;
|
||||
|
||||
public ExternalPlayQueue(final int serviceId,
|
||||
final String nextPageUrl,
|
||||
|
@ -46,12 +51,6 @@ public class ExternalPlayQueue extends PlayQueue {
|
|||
return isComplete;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayQueueItem get(int index) {
|
||||
if (index > getStreams().size() || getStreams().get(index) == null) return null;
|
||||
return getStreams().get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch() {
|
||||
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.playlistUrl)
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.schabi.newpipe.playlist.events.RemoveEvent;
|
|||
import org.schabi.newpipe.playlist.events.SelectEvent;
|
||||
import org.schabi.newpipe.playlist.events.SwapEvent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -26,27 +27,33 @@ import io.reactivex.BackpressureStrategy;
|
|||
import io.reactivex.Flowable;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
public abstract class PlayQueue {
|
||||
public abstract class PlayQueue implements Serializable {
|
||||
private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode());
|
||||
public static final boolean DEBUG = true;
|
||||
|
||||
private final List<PlayQueueItem> streams;
|
||||
private final ArrayList<PlayQueueItem> streams;
|
||||
private final AtomicInteger queueIndex;
|
||||
|
||||
private final BehaviorSubject<PlayQueueMessage> eventBroadcast;
|
||||
private final Flowable<PlayQueueMessage> broadcastReceiver;
|
||||
private Subscription reportingReactor;
|
||||
private transient BehaviorSubject<PlayQueueMessage> eventBroadcast;
|
||||
private transient Flowable<PlayQueueMessage> broadcastReceiver;
|
||||
private transient Subscription reportingReactor;
|
||||
|
||||
PlayQueue() {
|
||||
this(0, Collections.<PlayQueueItem>emptyList());
|
||||
}
|
||||
|
||||
PlayQueue(final int index, final List<PlayQueueItem> startWith) {
|
||||
streams = Collections.synchronizedList(new ArrayList<PlayQueueItem>());
|
||||
streams = new ArrayList<>();
|
||||
streams.addAll(startWith);
|
||||
|
||||
queueIndex = new AtomicInteger(index);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playlist actions
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void init() {
|
||||
eventBroadcast = BehaviorSubject.create();
|
||||
broadcastReceiver = eventBroadcast
|
||||
.startWith(new InitEvent())
|
||||
|
@ -55,9 +62,12 @@ public abstract class PlayQueue {
|
|||
if (DEBUG) broadcastReceiver.subscribe(getSelfReporter());
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playlist actions
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
public void dispose() {
|
||||
eventBroadcast.onComplete();
|
||||
|
||||
if (reportingReactor != null) reportingReactor.cancel();
|
||||
reportingReactor = null;
|
||||
}
|
||||
|
||||
// a queue is complete if it has loaded all items in an external playlist
|
||||
// single stream or local queues are always complete
|
||||
|
@ -66,21 +76,27 @@ public abstract class PlayQueue {
|
|||
// load partial queue in the background, does nothing if the queue is complete
|
||||
public abstract void fetch();
|
||||
|
||||
public abstract PlayQueueItem get(int index);
|
||||
|
||||
public void dispose() {
|
||||
eventBroadcast.onComplete();
|
||||
|
||||
if (reportingReactor != null) reportingReactor.cancel();
|
||||
reportingReactor = null;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Readonly ops
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public int getIndex() {
|
||||
return queueIndex.get();
|
||||
}
|
||||
|
||||
public PlayQueueItem getCurrent() {
|
||||
return streams.get(getIndex());
|
||||
return get(getIndex());
|
||||
}
|
||||
|
||||
public PlayQueueItem get(int index) {
|
||||
if (index >= streams.size() || streams.get(index) == null) return null;
|
||||
return streams.get(index);
|
||||
}
|
||||
|
||||
public int indexOf(final PlayQueueItem item) {
|
||||
// reference equality, can't think of a better way to do this
|
||||
// todo: better than this
|
||||
return streams.indexOf(item);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
|
@ -101,36 +117,26 @@ public abstract class PlayQueue {
|
|||
return broadcastReceiver;
|
||||
}
|
||||
|
||||
public int indexOf(final PlayQueueItem item) {
|
||||
// reference equality, can't think of a better way to do this
|
||||
// todo: better than this
|
||||
return streams.indexOf(item);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return queueIndex.get();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Write ops
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void setIndex(final int index) {
|
||||
public synchronized void setIndex(final int index) {
|
||||
queueIndex.set(Math.min(Math.max(0, index), streams.size() - 1));
|
||||
broadcast(new SelectEvent(index));
|
||||
}
|
||||
|
||||
protected void append(final PlayQueueItem item) {
|
||||
protected synchronized void append(final PlayQueueItem item) {
|
||||
streams.add(item);
|
||||
broadcast(new AppendEvent(1));
|
||||
}
|
||||
|
||||
protected void append(final Collection<PlayQueueItem> items) {
|
||||
protected synchronized void append(final Collection<PlayQueueItem> items) {
|
||||
streams.addAll(items);
|
||||
broadcast(new AppendEvent(items.size()));
|
||||
}
|
||||
|
||||
public void remove(final int index) {
|
||||
public synchronized void remove(final int index) {
|
||||
if (index >= streams.size()) return;
|
||||
|
||||
streams.remove(index);
|
||||
|
@ -142,7 +148,7 @@ public abstract class PlayQueue {
|
|||
broadcast(new RemoveEvent(index));
|
||||
}
|
||||
|
||||
protected void swap(final int source, final int target) {
|
||||
protected synchronized void swap(final int source, final int target) {
|
||||
final List<PlayQueueItem> items = streams;
|
||||
if (source < items.size() && target < items.size()) {
|
||||
// Swap two items
|
||||
|
|
|
@ -7,20 +7,35 @@ import org.schabi.newpipe.extractor.stream.StreamInfo;
|
|||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class PlayQueueItem {
|
||||
public class PlayQueueItem implements Serializable {
|
||||
|
||||
final public static int DEFAULT_QUALITY = -1;
|
||||
|
||||
final private String title;
|
||||
final private String url;
|
||||
final private int serviceId;
|
||||
final private long duration;
|
||||
|
||||
// Externally mutable, not sure if this is a good idea here
|
||||
private int sortedQualityIndex;
|
||||
|
||||
private Throwable error;
|
||||
private Single<StreamInfo> stream;
|
||||
|
||||
PlayQueueItem(final StreamInfo streamInfo, final int sortedQualityIndex) {
|
||||
this.title = streamInfo.name;
|
||||
this.url = streamInfo.url;
|
||||
this.serviceId = streamInfo.service_id;
|
||||
this.duration = streamInfo.duration;
|
||||
|
||||
this.sortedQualityIndex = sortedQualityIndex;
|
||||
}
|
||||
|
||||
PlayQueueItem(final StreamInfoItem streamInfoItem) {
|
||||
this.title = streamInfoItem.name;
|
||||
|
@ -28,9 +43,10 @@ public class PlayQueueItem {
|
|||
this.serviceId = streamInfoItem.service_id;
|
||||
this.duration = streamInfoItem.duration;
|
||||
|
||||
this.stream = getInfo();
|
||||
this.sortedQualityIndex = DEFAULT_QUALITY;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public String getTitle() {
|
||||
return title;
|
||||
|
@ -49,6 +65,14 @@ public class PlayQueueItem {
|
|||
return duration;
|
||||
}
|
||||
|
||||
public int getSortedQualityIndex() {
|
||||
return sortedQualityIndex;
|
||||
}
|
||||
|
||||
public void setSortedQualityIndex(int sortedQualityIndex) {
|
||||
this.sortedQualityIndex = sortedQualityIndex;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Throwable getError() {
|
||||
return error;
|
||||
|
@ -56,11 +80,6 @@ public class PlayQueueItem {
|
|||
|
||||
@NonNull
|
||||
public Single<StreamInfo> getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Single<StreamInfo> getInfo() {
|
||||
final Consumer<Throwable> onError = new Consumer<Throwable>() {
|
||||
@Override
|
||||
public void accept(Throwable throwable) throws Exception {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package org.schabi.newpipe.playlist;
|
||||
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public final class SinglePlayQueue extends PlayQueue {
|
||||
public static final String STREAM = "stream";
|
||||
|
||||
public SinglePlayQueue(final StreamInfo info, final int selectedQualityIndex) {
|
||||
super(0, Collections.singletonList(new PlayQueueItem(info, selectedQualityIndex)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch() {}
|
||||
}
|
|
@ -13,10 +13,12 @@ import org.schabi.newpipe.MainActivity;
|
|||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.about.AboutActivity;
|
||||
import org.schabi.newpipe.download.DownloadActivity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.ServiceList;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.fragments.MainFragment;
|
||||
|
@ -30,9 +32,12 @@ import org.schabi.newpipe.history.HistoryActivity;
|
|||
import org.schabi.newpipe.player.BackgroundPlayer;
|
||||
import org.schabi.newpipe.player.BasePlayer;
|
||||
import org.schabi.newpipe.player.VideoPlayer;
|
||||
import org.schabi.newpipe.playlist.ExternalPlayQueue;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.settings.SettingsActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public class NavigationHelper {
|
||||
|
@ -43,46 +48,60 @@ public class NavigationHelper {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static Intent getOpenVideoPlayerIntent(Context context, Class targetClazz, StreamInfo info, int selectedStreamIndex) {
|
||||
Intent mIntent = new Intent(context, targetClazz)
|
||||
.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(VideoPlayer.INDEX_SEL_VIDEO_STREAM, selectedStreamIndex)
|
||||
.putExtra(VideoPlayer.VIDEO_STREAMS_LIST, new ArrayList<>(ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false)))
|
||||
.putExtra(VideoPlayer.VIDEO_ONLY_AUDIO_STREAM, ListHelper.getHighestQualityAudio(info.audio_streams));
|
||||
if (info.start_position > 0) mIntent.putExtra(BasePlayer.START_POSITION, info.start_position * 1000L);
|
||||
return mIntent;
|
||||
return new Intent(context, targetClazz)
|
||||
.putExtra(BasePlayer.INTENT_TYPE, VideoPlayer.SINGLE_STREAM)
|
||||
.putExtra(SinglePlayQueue.STREAM, info)
|
||||
.putExtra(VideoPlayer.INDEX_SEL_VIDEO_STREAM, selectedStreamIndex);
|
||||
}
|
||||
|
||||
public static Intent getExternalPlaylistIntent(Context context,
|
||||
Class targetClazz,
|
||||
PlaylistInfo info,
|
||||
ArrayList<InfoItem> streams,
|
||||
int index) {
|
||||
return new Intent(context, targetClazz)
|
||||
.putExtra(BasePlayer.INTENT_TYPE, VideoPlayer.EXTERNAL_PLAYLIST)
|
||||
.putExtra(ExternalPlayQueue.SERVICE_ID, info.service_id)
|
||||
.putExtra(ExternalPlayQueue.INDEX, index)
|
||||
.putExtra(ExternalPlayQueue.STREAMS, streams)
|
||||
.putExtra(ExternalPlayQueue.NEXT_PAGE_URL, info.next_streams_url);
|
||||
}
|
||||
|
||||
public static Intent getOpenVideoPlayerIntent(Context context, Class targetClazz, VideoPlayer instance) {
|
||||
return new Intent(context, targetClazz)
|
||||
.putExtra(BasePlayer.VIDEO_TITLE, instance.getVideoTitle())
|
||||
.putExtra(BasePlayer.VIDEO_URL, instance.getVideoUrl())
|
||||
.putExtra(BasePlayer.VIDEO_THUMBNAIL_URL, instance.getVideoThumbnailUrl())
|
||||
.putExtra(BasePlayer.CHANNEL_NAME, instance.getUploaderName())
|
||||
.putExtra(BasePlayer.INTENT_TYPE, VideoPlayer.PLAYER_INTENT)
|
||||
.putExtra(VideoPlayer.PLAY_QUEUE, instance.getPlayQueue())
|
||||
.putExtra(VideoPlayer.INDEX_SEL_VIDEO_STREAM, instance.getSelectedStreamIndex())
|
||||
.putExtra(VideoPlayer.VIDEO_STREAMS_LIST, instance.getVideoStreamsList())
|
||||
.putExtra(VideoPlayer.VIDEO_ONLY_AUDIO_STREAM, instance.getAudioStream())
|
||||
.putExtra(VideoPlayer.RESTORE_QUEUE_INDEX, instance.getCurrentQueueIndex())
|
||||
.putExtra(BasePlayer.START_POSITION, instance.getPlayer().getCurrentPosition())
|
||||
.putExtra(BasePlayer.PLAYBACK_SPEED, instance.getPlaybackSpeed());
|
||||
}
|
||||
|
||||
public static Intent getOpenBackgroundPlayerIntent(Context context, StreamInfo info) {
|
||||
return getOpenBackgroundPlayerIntent(context, info, info.audio_streams.get(ListHelper.getDefaultAudioFormat(context, info.audio_streams)));
|
||||
return new Intent(context, BackgroundPlayer.class)
|
||||
.putExtra(BasePlayer.INTENT_TYPE, VideoPlayer.SINGLE_STREAM)
|
||||
.putExtra(SinglePlayQueue.STREAM, info);
|
||||
}
|
||||
|
||||
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;
|
||||
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
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
Loading…
Reference in a new issue