Skip to content

Commit c8a1f06

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 bc7e340 commit c8a1f06

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
@@ -88,8 +88,8 @@
8888
import org.schabi.newpipe.error.ErrorPanelHelper;
8989
import org.schabi.newpipe.error.ErrorUtil;
9090
import org.schabi.newpipe.error.UserAction;
91-
import org.schabi.newpipe.extractor.stream.AudioStream;
9291
import org.schabi.newpipe.extractor.Image;
92+
import org.schabi.newpipe.extractor.stream.AudioStream;
9393
import org.schabi.newpipe.extractor.stream.StreamInfo;
9494
import org.schabi.newpipe.extractor.stream.StreamType;
9595
import org.schabi.newpipe.extractor.stream.VideoStream;
@@ -123,9 +123,9 @@
123123
import org.schabi.newpipe.util.ListHelper;
124124
import org.schabi.newpipe.util.NavigationHelper;
125125
import org.schabi.newpipe.util.PermissionHelper;
126-
import org.schabi.newpipe.util.image.PicassoHelper;
127126
import org.schabi.newpipe.util.SerializedCache;
128127
import org.schabi.newpipe.util.StreamTypeUtil;
128+
import org.schabi.newpipe.util.image.PicassoHelper;
129129

130130
import java.util.List;
131131
import java.util.Optional;
@@ -396,9 +396,8 @@ public void handleIntent(@NonNull final Intent intent) {
396396
if (newQueue == null) {
397397
return;
398398
}
399-
final int currentIndex = playQueue.getIndex();
400-
playQueue.append(newQueue.getStreams());
401-
playQueue.move(playQueue.size() - 1, currentIndex + 1);
399+
final PlayQueueItem newItem = newQueue.getStreams().get(0);
400+
newQueue.enqueueNext(newItem, false);
402401
}
403402
return;
404403
}
@@ -410,11 +409,43 @@ public void handleIntent(@NonNull final Intent intent) {
410409
streamItemDisposable.add(single.subscribeOn(Schedulers.io())
411410
.observeOn(AndroidSchedulers.mainThread())
412411
.subscribe(info -> {
413-
final PlayQueue newPlayQueue =
414-
new SinglePlayQueue(info, dat.getSeconds() * 1000L);
415-
// TODO: add back the “already playing stream” optimization here
416-
initPlayback(newPlayQueue, playWhenReady);
412+
final @Nullable PlayQueue oldPlayQueue = playQueue;
413+
info.setStartPosition(dat.getSeconds());
414+
final PlayQueueItem playQueueItem = new PlayQueueItem(info);
415+
416+
// If the stream is already playing,
417+
// we can just seek to the appropriate timestamp
418+
if (oldPlayQueue != null
419+
&& playQueueItem.isSameItem(oldPlayQueue.getItem())) {
420+
// Player can have state = IDLE when playback is stopped or failed
421+
// and we should retry in this case
422+
if (simpleExoPlayer.getPlaybackState()
423+
== com.google.android.exoplayer2.Player.STATE_IDLE) {
424+
simpleExoPlayer.prepare();
425+
}
426+
simpleExoPlayer.seekTo(oldPlayQueue.getIndex(),
427+
dat.getSeconds() * 1000L);
428+
simpleExoPlayer.setPlayWhenReady(playWhenReady);
429+
430+
} else {
431+
final PlayQueue newPlayQueue;
432+
433+
// If there is no queue yet, just add our item
434+
if (oldPlayQueue == null) {
435+
newPlayQueue = new SinglePlayQueue(playQueueItem);
436+
437+
// else we add the timestamped stream behind the current video
438+
// and start playing it.
439+
} else {
440+
oldPlayQueue.enqueueNext(playQueueItem, true);
441+
oldPlayQueue.offsetIndex(1);
442+
newPlayQueue = oldPlayQueue;
443+
}
444+
initPlayback(newPlayQueue, playWhenReady);
445+
}
446+
417447
handleIntentPost(oldPlayerType);
448+
418449
}, throwable -> {
419450
if (DEBUG) {
420451
Log.e(TAG, "Could not play on popup: " + dat.getUrl(), throwable);
@@ -454,7 +485,7 @@ public void handleIntent(@NonNull final Intent intent) {
454485
if (!exoPlayerIsNull()
455486
&& newQueue.size() == 1 && newQueue.getItem() != null
456487
&& playQueue != null && playQueue.size() == 1 && playQueue.getItem() != null
457-
&& newQueue.getItem().getUrl().equals(playQueue.getItem().getUrl())
488+
&& newQueue.getItem().isSameItem(playQueue.getItem())
458489
&& newQueue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
459490
// Player can have state = IDLE when playback is stopped or failed
460491
// and we should retry in this case
@@ -520,6 +551,7 @@ public void handleIntent(@NonNull final Intent intent) {
520551
handleIntentPost(oldPlayerType);
521552
}
522553

554+
523555
private void handleIntentPost(final PlayerType oldPlayerType) {
524556
if (oldPlayerType != playerType && playQueue != null) {
525557
// 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)