Skip to content

Commit a9153c6

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 44e840c commit a9153c6

4 files changed

Lines changed: 79 additions & 14 deletions

File tree

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

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@
9090
import org.schabi.newpipe.error.ErrorPanelHelper;
9191
import org.schabi.newpipe.error.ErrorUtil;
9292
import org.schabi.newpipe.error.UserAction;
93-
import org.schabi.newpipe.extractor.stream.AudioStream;
9493
import org.schabi.newpipe.extractor.Image;
94+
import org.schabi.newpipe.extractor.stream.AudioStream;
9595
import org.schabi.newpipe.extractor.stream.StreamInfo;
9696
import org.schabi.newpipe.extractor.stream.StreamType;
9797
import org.schabi.newpipe.extractor.stream.VideoStream;
@@ -125,9 +125,9 @@
125125
import org.schabi.newpipe.util.ListHelper;
126126
import org.schabi.newpipe.util.NavigationHelper;
127127
import org.schabi.newpipe.util.PermissionHelper;
128-
import org.schabi.newpipe.util.image.PicassoHelper;
129128
import org.schabi.newpipe.util.SerializedCache;
130129
import org.schabi.newpipe.util.StreamTypeUtil;
130+
import org.schabi.newpipe.util.image.PicassoHelper;
131131

132132
import java.util.List;
133133
import java.util.Optional;
@@ -407,9 +407,8 @@ public void handleIntent(@NonNull final Intent intent) {
407407
if (newQueue == null) {
408408
return;
409409
}
410-
final int currentIndex = playQueue.getIndex();
411-
playQueue.append(newQueue.getStreams());
412-
playQueue.move(playQueue.size() - 1, currentIndex + 1);
410+
final PlayQueueItem newItem = newQueue.getStreams().get(0);
411+
newQueue.enqueueNext(newItem, false);
413412
}
414413
return;
415414
}
@@ -421,11 +420,43 @@ public void handleIntent(@NonNull final Intent intent) {
421420
streamItemDisposable.add(single.subscribeOn(Schedulers.io())
422421
.observeOn(AndroidSchedulers.mainThread())
423422
.subscribe(info -> {
424-
final PlayQueue newPlayQueue =
425-
new SinglePlayQueue(info, dat.getSeconds() * 1000L);
426-
// TODO: add back the “already playing stream” optimization here
427-
initPlayback(newPlayQueue, playWhenReady);
423+
final @Nullable PlayQueue oldPlayQueue = playQueue;
424+
info.setStartPosition(dat.getSeconds());
425+
final PlayQueueItem playQueueItem = new PlayQueueItem(info);
426+
427+
// If the stream is already playing,
428+
// we can just seek to the appropriate timestamp
429+
if (oldPlayQueue != null
430+
&& playQueueItem.isSameItem(oldPlayQueue.getItem())) {
431+
// Player can have state = IDLE when playback is stopped or failed
432+
// and we should retry in this case
433+
if (simpleExoPlayer.getPlaybackState()
434+
== com.google.android.exoplayer2.Player.STATE_IDLE) {
435+
simpleExoPlayer.prepare();
436+
}
437+
simpleExoPlayer.seekTo(oldPlayQueue.getIndex(),
438+
dat.getSeconds() * 1000L);
439+
simpleExoPlayer.setPlayWhenReady(playWhenReady);
440+
441+
} else {
442+
final PlayQueue newPlayQueue;
443+
444+
// If there is no queue yet, just add our item
445+
if (oldPlayQueue == null) {
446+
newPlayQueue = new SinglePlayQueue(playQueueItem);
447+
448+
// else we add the timestamped stream behind the current video
449+
// and start playing it.
450+
} else {
451+
oldPlayQueue.enqueueNext(playQueueItem, true);
452+
oldPlayQueue.offsetIndex(1);
453+
newPlayQueue = oldPlayQueue;
454+
}
455+
initPlayback(newPlayQueue, playWhenReady);
456+
}
457+
428458
handleIntentPost(oldPlayerType);
459+
429460
}, throwable -> {
430461
if (DEBUG) {
431462
Log.e(TAG, "Could not play on popup: " + dat.getUrl(), throwable);
@@ -465,7 +496,7 @@ public void handleIntent(@NonNull final Intent intent) {
465496
if (!exoPlayerIsNull()
466497
&& newQueue.size() == 1 && newQueue.getItem() != null
467498
&& playQueue != null && playQueue.size() == 1 && playQueue.getItem() != null
468-
&& newQueue.getItem().getUrl().equals(playQueue.getItem().getUrl())
499+
&& newQueue.getItem().isSameItem(playQueue.getItem())
469500
&& newQueue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
470501
// Player can have state = IDLE when playback is stopped or failed
471502
// and we should retry in this case
@@ -531,6 +562,7 @@ public void handleIntent(@NonNull final Intent intent) {
531562
handleIntentPost(oldPlayerType);
532563
}
533564

565+
534566
private void handleIntentPost(final PlayerType oldPlayerType) {
535567
if (oldPlayerType != playerType && playQueue != null) {
536568
// 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 getServiceId() == other.getServiceId()
87+
&& getUrl().equals(other.getUrl());
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)