Skip to content

Commit 150649a

Browse files
committed
Player/handleIntent: Don’t delete queue when clicking on timestamp
Fixes #11013 We finally are at the point where we can have good logic around clicking on timestamps. This is pretty straightforward: 1) if we are already playing the stream (usual case), we skip to the correct second directly 2) If we don’t have a queue yet, create a trivial one with the stream 3) If we have a queue, we insert the video as next item and start playing it. The skipping logic in 1) is similar to the one further down in the old optimization block, but will always correctly fire for timestamps now. I copied it because it’s not quite the same code, and moving into a separate method at this stage would complicate the code too much.
1 parent 3803d49 commit 150649a

File tree

4 files changed

+78
-11
lines changed

4 files changed

+78
-11
lines changed

app/src/main/java/org/schabi/newpipe/player/Player.java

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -403,9 +403,8 @@ public void handleIntent(@NonNull final Intent intent) {
403403
if (newQueue == null) {
404404
return;
405405
}
406-
final int currentIndex = playQueue.getIndex();
407-
playQueue.append(newQueue.getStreams());
408-
playQueue.move(playQueue.size() - 1, currentIndex + 1);
406+
final PlayQueueItem newItem = newQueue.getStreams().get(0);
407+
newQueue.enqueueNext(newItem, false);
409408
}
410409
return;
411410
}
@@ -417,9 +416,43 @@ public void handleIntent(@NonNull final Intent intent) {
417416
streamItemDisposable.add(single.subscribeOn(Schedulers.io())
418417
.observeOn(AndroidSchedulers.mainThread())
419418
.subscribe(info -> {
420-
final PlayQueue newPlayQueue = new SinglePlayQueue(info,
421-
dat.getSeconds() * 1000L);
422-
NavigationHelper.playOnPopupPlayer(context, playQueue, false);
419+
final @Nullable PlayQueue oldPlayQueue = playQueue;
420+
info.setStartPosition(dat.getSeconds());
421+
final PlayQueueItem playQueueItem = new PlayQueueItem(info);
422+
423+
// If the stream is already playing,
424+
// we can just seek to the appropriate timestamp
425+
if (oldPlayQueue != null
426+
&& playQueueItem.isSameItem(oldPlayQueue.getItem())) {
427+
// Player can have state = IDLE when playback is stopped or failed
428+
// and we should retry in this case
429+
if (simpleExoPlayer.getPlaybackState()
430+
== com.google.android.exoplayer2.Player.STATE_IDLE) {
431+
simpleExoPlayer.prepare();
432+
}
433+
simpleExoPlayer.seekTo(oldPlayQueue.getIndex(),
434+
dat.getSeconds() * 1000L);
435+
simpleExoPlayer.setPlayWhenReady(playWhenReady);
436+
437+
} else {
438+
final PlayQueue newPlayQueue;
439+
440+
// If there is no queue yet, just add our item
441+
if (oldPlayQueue == null) {
442+
newPlayQueue = new SinglePlayQueue(playQueueItem);
443+
444+
// else we add the timestamped stream behind the current video
445+
// and start playing it.
446+
} else {
447+
oldPlayQueue.enqueueNext(playQueueItem, true);
448+
oldPlayQueue.offsetIndex(1);
449+
newPlayQueue = oldPlayQueue;
450+
}
451+
initPlayback(newPlayQueue, playWhenReady);
452+
}
453+
454+
handleIntentPost(oldPlayerType);
455+
423456
}, throwable -> {
424457
// This will only show a snackbar if the passed context has a root view:
425458
// otherwise it will resort to showing a notification, so we are safe
@@ -456,7 +489,7 @@ public void handleIntent(@NonNull final Intent intent) {
456489
if (!exoPlayerIsNull()
457490
&& newQueue.size() == 1 && newQueue.getItem() != null
458491
&& playQueue != null && playQueue.size() == 1 && playQueue.getItem() != null
459-
&& newQueue.getItem().getUrl().equals(playQueue.getItem().getUrl())
492+
&& newQueue.getItem().isSameItem(playQueue.getItem())
460493
&& newQueue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
461494
// Player can have state = IDLE when playback is stopped or failed
462495
// and we should retry in this case
@@ -522,6 +555,7 @@ public void handleIntent(@NonNull final Intent intent) {
522555
handleIntentPost(oldPlayerType);
523556
}
524557

558+
525559
private void handleIntentPost(final PlayerType oldPlayerType) {
526560
if (oldPlayerType != playerType && playQueue != null) {
527561
// If playerType changes from one to another we should reload the player

app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,22 @@ public synchronized void append(@NonNull final List<PlayQueueItem> items) {
291291
broadcast(new AppendEvent(itemList.size()));
292292
}
293293

294+
/**
295+
* Add the given item after the current stream.
296+
*
297+
* @param item item to add.
298+
* @param skipIfSame if set, skip adding if the next stream is the same stream.
299+
*/
300+
public void enqueueNext(@NonNull final PlayQueueItem item, final boolean skipIfSame) {
301+
final int currentIndex = getIndex();
302+
// if the next item is the same item as the one we want to enqueue, skip if flag is true
303+
if (skipIfSame && item.isSameItem(getItem(currentIndex + 1))) {
304+
return;
305+
}
306+
append(List.of(item));
307+
move(size() - 1, currentIndex + 1);
308+
}
309+
294310
/**
295311
* Removes the item at the given index from the play queue.
296312
* <p>
@@ -529,8 +545,7 @@ public boolean equalStreams(@Nullable final PlayQueue other) {
529545
final PlayQueueItem stream = streams.get(i);
530546
final PlayQueueItem otherStream = other.streams.get(i);
531547
// Check is based on serviceId and URL
532-
if (stream.getServiceId() != otherStream.getServiceId()
533-
|| !stream.getUrl().equals(otherStream.getUrl())) {
548+
if (!stream.isSameItem(otherStream)) {
534549
return false;
535550
}
536551
}

app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class PlayQueueItem implements Serializable {
3838
private long recoveryPosition;
3939
private Throwable error;
4040

41-
PlayQueueItem(@NonNull final StreamInfo info) {
41+
public PlayQueueItem(@NonNull final StreamInfo info) {
4242
this(info.getName(), info.getUrl(), info.getServiceId(), info.getDuration(),
4343
info.getThumbnails(), info.getUploaderName(),
4444
info.getUploaderUrl(), info.getStreamType());
@@ -71,6 +71,22 @@ private PlayQueueItem(@Nullable final String name, @Nullable final String url,
7171
this.recoveryPosition = RECOVERY_UNSET;
7272
}
7373

74+
/** Whether these two items should be treated as the same stream
75+
* for the sake of keeping the same player running when e.g. jumping between timestamps.
76+
*
77+
* @param other the {@link PlayQueueItem} to compare against.
78+
* @return whether the two items are the same so the stream can be re-used.
79+
*/
80+
public boolean isSameItem(@Nullable final PlayQueueItem other) {
81+
if (other == null) {
82+
return false;
83+
}
84+
// We assume that the same service & URL uniquely determines
85+
// that we can keep the same stream running.
86+
return serviceId == other.serviceId
87+
&& url.equals(other.url);
88+
}
89+
7490
@NonNull
7591
public String getTitle() {
7692
return title;

app/src/main/java/org/schabi/newpipe/player/playqueue/SinglePlayQueue.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ public SinglePlayQueue(final StreamInfoItem item) {
1616
public SinglePlayQueue(final StreamInfo info) {
1717
super(0, List.of(new PlayQueueItem(info)));
1818
}
19-
19+
public SinglePlayQueue(final PlayQueueItem item) {
20+
super(0, List.of(item));
21+
}
2022
public SinglePlayQueue(final StreamInfo info, final long startPosition) {
2123
super(0, List.of(new PlayQueueItem(info)));
2224
getItem().setRecoveryPosition(startPosition);

0 commit comments

Comments
 (0)