mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-06-26 15:13:00 +00:00
Player/handleIntent: Don’t delete queue when clicking on timestamp
Fixes https://github.com/TeamNewPipe/NewPipe/issues/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.
This commit is contained in:
parent
44e840c1a5
commit
a9153c6dd9
@ -90,8 +90,8 @@ import org.schabi.newpipe.error.ErrorInfo;
|
|||||||
import org.schabi.newpipe.error.ErrorPanelHelper;
|
import org.schabi.newpipe.error.ErrorPanelHelper;
|
||||||
import org.schabi.newpipe.error.ErrorUtil;
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
|
||||||
import org.schabi.newpipe.extractor.Image;
|
import org.schabi.newpipe.extractor.Image;
|
||||||
|
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
@ -125,9 +125,9 @@ import org.schabi.newpipe.util.ExtractorHelper;
|
|||||||
import org.schabi.newpipe.util.ListHelper;
|
import org.schabi.newpipe.util.ListHelper;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
|
||||||
import org.schabi.newpipe.util.SerializedCache;
|
import org.schabi.newpipe.util.SerializedCache;
|
||||||
import org.schabi.newpipe.util.StreamTypeUtil;
|
import org.schabi.newpipe.util.StreamTypeUtil;
|
||||||
|
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -407,9 +407,8 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
if (newQueue == null) {
|
if (newQueue == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final int currentIndex = playQueue.getIndex();
|
final PlayQueueItem newItem = newQueue.getStreams().get(0);
|
||||||
playQueue.append(newQueue.getStreams());
|
newQueue.enqueueNext(newItem, false);
|
||||||
playQueue.move(playQueue.size() - 1, currentIndex + 1);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -421,11 +420,43 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
streamItemDisposable.add(single.subscribeOn(Schedulers.io())
|
streamItemDisposable.add(single.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(info -> {
|
.subscribe(info -> {
|
||||||
final PlayQueue newPlayQueue =
|
final @Nullable PlayQueue oldPlayQueue = playQueue;
|
||||||
new SinglePlayQueue(info, dat.getSeconds() * 1000L);
|
info.setStartPosition(dat.getSeconds());
|
||||||
// TODO: add back the “already playing stream” optimization here
|
final PlayQueueItem playQueueItem = new PlayQueueItem(info);
|
||||||
|
|
||||||
|
// If the stream is already playing,
|
||||||
|
// we can just seek to the appropriate timestamp
|
||||||
|
if (oldPlayQueue != null
|
||||||
|
&& playQueueItem.isSameItem(oldPlayQueue.getItem())) {
|
||||||
|
// Player can have state = IDLE when playback is stopped or failed
|
||||||
|
// and we should retry in this case
|
||||||
|
if (simpleExoPlayer.getPlaybackState()
|
||||||
|
== com.google.android.exoplayer2.Player.STATE_IDLE) {
|
||||||
|
simpleExoPlayer.prepare();
|
||||||
|
}
|
||||||
|
simpleExoPlayer.seekTo(oldPlayQueue.getIndex(),
|
||||||
|
dat.getSeconds() * 1000L);
|
||||||
|
simpleExoPlayer.setPlayWhenReady(playWhenReady);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
final PlayQueue newPlayQueue;
|
||||||
|
|
||||||
|
// If there is no queue yet, just add our item
|
||||||
|
if (oldPlayQueue == null) {
|
||||||
|
newPlayQueue = new SinglePlayQueue(playQueueItem);
|
||||||
|
|
||||||
|
// else we add the timestamped stream behind the current video
|
||||||
|
// and start playing it.
|
||||||
|
} else {
|
||||||
|
oldPlayQueue.enqueueNext(playQueueItem, true);
|
||||||
|
oldPlayQueue.offsetIndex(1);
|
||||||
|
newPlayQueue = oldPlayQueue;
|
||||||
|
}
|
||||||
initPlayback(newPlayQueue, playWhenReady);
|
initPlayback(newPlayQueue, playWhenReady);
|
||||||
|
}
|
||||||
|
|
||||||
handleIntentPost(oldPlayerType);
|
handleIntentPost(oldPlayerType);
|
||||||
|
|
||||||
}, throwable -> {
|
}, throwable -> {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.e(TAG, "Could not play on popup: " + dat.getUrl(), throwable);
|
Log.e(TAG, "Could not play on popup: " + dat.getUrl(), throwable);
|
||||||
@ -465,7 +496,7 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
if (!exoPlayerIsNull()
|
if (!exoPlayerIsNull()
|
||||||
&& newQueue.size() == 1 && newQueue.getItem() != null
|
&& newQueue.size() == 1 && newQueue.getItem() != null
|
||||||
&& playQueue != null && playQueue.size() == 1 && playQueue.getItem() != null
|
&& playQueue != null && playQueue.size() == 1 && playQueue.getItem() != null
|
||||||
&& newQueue.getItem().getUrl().equals(playQueue.getItem().getUrl())
|
&& newQueue.getItem().isSameItem(playQueue.getItem())
|
||||||
&& newQueue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
|
&& newQueue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
|
||||||
// Player can have state = IDLE when playback is stopped or failed
|
// Player can have state = IDLE when playback is stopped or failed
|
||||||
// and we should retry in this case
|
// and we should retry in this case
|
||||||
@ -531,6 +562,7 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
handleIntentPost(oldPlayerType);
|
handleIntentPost(oldPlayerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void handleIntentPost(final PlayerType oldPlayerType) {
|
private void handleIntentPost(final PlayerType oldPlayerType) {
|
||||||
if (oldPlayerType != playerType && playQueue != null) {
|
if (oldPlayerType != playerType && playQueue != null) {
|
||||||
// If playerType changes from one to another we should reload the player
|
// If playerType changes from one to another we should reload the player
|
||||||
|
@ -291,6 +291,22 @@ public abstract class PlayQueue implements Serializable {
|
|||||||
broadcast(new AppendEvent(itemList.size()));
|
broadcast(new AppendEvent(itemList.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the given item after the current stream.
|
||||||
|
*
|
||||||
|
* @param item item to add.
|
||||||
|
* @param skipIfSame if set, skip adding if the next stream is the same stream.
|
||||||
|
*/
|
||||||
|
public void enqueueNext(@NonNull final PlayQueueItem item, final boolean skipIfSame) {
|
||||||
|
final int currentIndex = getIndex();
|
||||||
|
// if the next item is the same item as the one we want to enqueue, skip if flag is true
|
||||||
|
if (skipIfSame && item.isSameItem(getItem(currentIndex + 1))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
append(List.of(item));
|
||||||
|
move(size() - 1, currentIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the item at the given index from the play queue.
|
* Removes the item at the given index from the play queue.
|
||||||
* <p>
|
* <p>
|
||||||
@ -529,8 +545,7 @@ public abstract class PlayQueue implements Serializable {
|
|||||||
final PlayQueueItem stream = streams.get(i);
|
final PlayQueueItem stream = streams.get(i);
|
||||||
final PlayQueueItem otherStream = other.streams.get(i);
|
final PlayQueueItem otherStream = other.streams.get(i);
|
||||||
// Check is based on serviceId and URL
|
// Check is based on serviceId and URL
|
||||||
if (stream.getServiceId() != otherStream.getServiceId()
|
if (!stream.isSameItem(otherStream)) {
|
||||||
|| !stream.getUrl().equals(otherStream.getUrl())) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ public class PlayQueueItem implements Serializable {
|
|||||||
private long recoveryPosition;
|
private long recoveryPosition;
|
||||||
private Throwable error;
|
private Throwable error;
|
||||||
|
|
||||||
PlayQueueItem(@NonNull final StreamInfo info) {
|
public PlayQueueItem(@NonNull final StreamInfo info) {
|
||||||
this(info.getName(), info.getUrl(), info.getServiceId(), info.getDuration(),
|
this(info.getName(), info.getUrl(), info.getServiceId(), info.getDuration(),
|
||||||
info.getThumbnails(), info.getUploaderName(),
|
info.getThumbnails(), info.getUploaderName(),
|
||||||
info.getUploaderUrl(), info.getStreamType());
|
info.getUploaderUrl(), info.getStreamType());
|
||||||
@ -71,6 +71,22 @@ public class PlayQueueItem implements Serializable {
|
|||||||
this.recoveryPosition = RECOVERY_UNSET;
|
this.recoveryPosition = RECOVERY_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Whether these two items should be treated as the same stream
|
||||||
|
* for the sake of keeping the same player running when e.g. jumping between timestamps.
|
||||||
|
*
|
||||||
|
* @param other the {@link PlayQueueItem} to compare against.
|
||||||
|
* @return whether the two items are the same so the stream can be re-used.
|
||||||
|
*/
|
||||||
|
public boolean isSameItem(@Nullable final PlayQueueItem other) {
|
||||||
|
if (other == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// We assume that the same service & URL uniquely determines
|
||||||
|
// that we can keep the same stream running.
|
||||||
|
return getServiceId() == other.getServiceId()
|
||||||
|
&& getUrl().equals(other.getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return title;
|
return title;
|
||||||
|
@ -16,7 +16,9 @@ public final class SinglePlayQueue extends PlayQueue {
|
|||||||
public SinglePlayQueue(final StreamInfo info) {
|
public SinglePlayQueue(final StreamInfo info) {
|
||||||
super(0, List.of(new PlayQueueItem(info)));
|
super(0, List.of(new PlayQueueItem(info)));
|
||||||
}
|
}
|
||||||
|
public SinglePlayQueue(final PlayQueueItem item) {
|
||||||
|
super(0, List.of(item));
|
||||||
|
}
|
||||||
public SinglePlayQueue(final StreamInfo info, final long startPosition) {
|
public SinglePlayQueue(final StreamInfo info, final long startPosition) {
|
||||||
super(0, List.of(new PlayQueueItem(info)));
|
super(0, List.of(new PlayQueueItem(info)));
|
||||||
getItem().setRecoveryPosition(startPosition);
|
getItem().setRecoveryPosition(startPosition);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user