-Added "add to playlist" button to service player play queue item drop down.
-Refactored playlist manipulations out from media source manager. -Fixed potential ArrayOutOfBound exception when checking if player window is live. -Fixed service player play queue potentially not refreshing when current play queue is replaced.
This commit is contained in:
parent
1d017d3cbc
commit
b0a09c7876
4 changed files with 232 additions and 99 deletions
|
@ -640,7 +640,7 @@ public abstract class BasePlayer implements
|
|||
seekTo(recoveryPositionMillis);
|
||||
playQueue.unsetRecovery(currentSourceIndex);
|
||||
|
||||
} else if (isSynchronizing && simpleExoPlayer.isCurrentWindowDynamic()) {
|
||||
} else if (isSynchronizing && isLive()) {
|
||||
if (DEBUG) Log.d(TAG, "Playback - Synchronizing livestream to default time");
|
||||
// Is still synchronizing?
|
||||
seekToDefault();
|
||||
|
@ -789,7 +789,7 @@ public abstract class BasePlayer implements
|
|||
@Override
|
||||
public boolean isNearPlaybackEdge(final long timeToEndMillis) {
|
||||
// If live, then not near playback edge
|
||||
if (simpleExoPlayer == null || simpleExoPlayer.isCurrentWindowDynamic()) return false;
|
||||
if (simpleExoPlayer == null || isLive()) return false;
|
||||
|
||||
final long currentPositionMillis = simpleExoPlayer.getCurrentPosition();
|
||||
final long currentDurationMillis = simpleExoPlayer.getDuration();
|
||||
|
@ -1127,9 +1127,7 @@ public abstract class BasePlayer implements
|
|||
|
||||
/** Checks if the current playback is a livestream AND is playing at or beyond the live edge */
|
||||
public boolean isLiveEdge() {
|
||||
if (simpleExoPlayer == null) return false;
|
||||
final boolean isLive = simpleExoPlayer.isCurrentWindowDynamic();
|
||||
if (!isLive) return false;
|
||||
if (simpleExoPlayer == null || !isLive()) return false;
|
||||
|
||||
final Timeline currentTimeline = simpleExoPlayer.getCurrentTimeline();
|
||||
final int currentWindowIndex = simpleExoPlayer.getCurrentWindowIndex();
|
||||
|
@ -1143,6 +1141,16 @@ public abstract class BasePlayer implements
|
|||
return timelineWindow.getDefaultPositionMs() <= simpleExoPlayer.getCurrentPosition();
|
||||
}
|
||||
|
||||
public boolean isLive() {
|
||||
if (simpleExoPlayer == null) return false;
|
||||
try {
|
||||
return simpleExoPlayer.isCurrentWindowDynamic();
|
||||
} catch (@NonNull IndexOutOfBoundsException ignored) {
|
||||
// Why would this even happen =(
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
final int state = simpleExoPlayer.getPlaybackState();
|
||||
return (state == Player.STATE_READY || state == Player.STATE_BUFFERING)
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
|||
import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||
import org.schabi.newpipe.player.helper.PlaybackParameterDialog;
|
||||
import org.schabi.newpipe.playlist.PlayQueueAdapter;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItemHolder;
|
||||
|
@ -40,6 +41,9 @@ import org.schabi.newpipe.util.Localization;
|
|||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.formatPitch;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed;
|
||||
|
||||
|
@ -151,7 +155,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
finish();
|
||||
return true;
|
||||
case R.id.action_append_playlist:
|
||||
appendToPlaylist();
|
||||
appendAllToPlaylist();
|
||||
return true;
|
||||
case R.id.action_settings:
|
||||
NavigationHelper.openSettings(this);
|
||||
|
@ -187,13 +191,6 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
|
||||
private void appendToPlaylist() {
|
||||
if (this.player != null && this.player.getPlayQueue() != null) {
|
||||
PlaylistAppendDialog.fromPlayQueueItems(this.player.getPlayQueue().getStreams())
|
||||
.show(getSupportFragmentManager(), getTag());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Service Connection
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -319,7 +316,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
|
||||
private void buildItemPopupMenu(final PlayQueueItem item, final View view) {
|
||||
final PopupMenu menu = new PopupMenu(this, view);
|
||||
final MenuItem remove = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 0, Menu.NONE, R.string.play_queue_remove);
|
||||
final MenuItem remove = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, /*pos=*/0,
|
||||
Menu.NONE, R.string.play_queue_remove);
|
||||
remove.setOnMenuItemClickListener(menuItem -> {
|
||||
if (player == null) return false;
|
||||
|
||||
|
@ -328,12 +326,20 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
return true;
|
||||
});
|
||||
|
||||
final MenuItem detail = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 1, Menu.NONE, R.string.play_queue_stream_detail);
|
||||
final MenuItem detail = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, /*pos=*/1,
|
||||
Menu.NONE, R.string.play_queue_stream_detail);
|
||||
detail.setOnMenuItemClickListener(menuItem -> {
|
||||
onOpenDetail(item.getServiceId(), item.getUrl(), item.getTitle());
|
||||
return true;
|
||||
});
|
||||
|
||||
final MenuItem append = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, /*pos=*/2,
|
||||
Menu.NONE, R.string.append_playlist);
|
||||
append.setOnMenuItemClickListener(menuItem -> {
|
||||
openPlaylistAppendDialog(Collections.singletonList(item));
|
||||
return true;
|
||||
});
|
||||
|
||||
menu.show();
|
||||
}
|
||||
|
||||
|
@ -488,6 +494,21 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
seeking = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Playlist append
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void appendAllToPlaylist() {
|
||||
if (player != null && player.getPlayQueue() != null) {
|
||||
openPlaylistAppendDialog(player.getPlayQueue().getStreams());
|
||||
}
|
||||
}
|
||||
|
||||
private void openPlaylistAppendDialog(final List<PlayQueueItem> playlist) {
|
||||
PlaylistAppendDialog.fromPlayQueueItems(playlist)
|
||||
.show(getSupportFragmentManager(), getTag());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Binding Service Listener
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -497,6 +518,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
onStateChanged(state);
|
||||
onPlayModeChanged(repeatMode, shuffled);
|
||||
onPlaybackParameterChanged(parameters);
|
||||
onMaybePlaybackAdapterChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -609,4 +631,12 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
playbackPitchButton.setText(formatPitch(parameters.pitch));
|
||||
}
|
||||
}
|
||||
|
||||
private void onMaybePlaybackAdapterChanged() {
|
||||
if (itemsList == null || player == null) return;
|
||||
final PlayQueueAdapter maybeNewAdapter = player.getPlayQueueAdapter();
|
||||
if (maybeNewAdapter != null && itemsList.getAdapter() != maybeNewAdapter) {
|
||||
itemsList.setAdapter(maybeNewAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
package org.schabi.newpipe.player.mediasource;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource;
|
||||
import com.google.android.exoplayer2.source.ShuffleOrder;
|
||||
|
||||
public class ManagedMediaSourcePlaylist {
|
||||
@NonNull private final DynamicConcatenatingMediaSource internalSource;
|
||||
|
||||
public ManagedMediaSourcePlaylist() {
|
||||
internalSource = new DynamicConcatenatingMediaSource(/*isPlaylistAtomic=*/false,
|
||||
new ShuffleOrder.UnshuffledShuffleOrder(0));
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// MediaSource Delegations
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public int size() {
|
||||
return internalSource.getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ManagedMediaSource} at the given index of the playlist.
|
||||
* If the index is invalid, then null is returned.
|
||||
* */
|
||||
@Nullable
|
||||
public ManagedMediaSource get(final int index) {
|
||||
return (index < 0 || index >= size()) ?
|
||||
null : (ManagedMediaSource) internalSource.getMediaSource(index);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
internalSource.releaseSource();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public DynamicConcatenatingMediaSource getParentMediaSource() {
|
||||
return internalSource;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playlist Manipulation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/**
|
||||
* Expands the {@link DynamicConcatenatingMediaSource} by appending it with a
|
||||
* {@link PlaceholderMediaSource}.
|
||||
*
|
||||
* @see #append(ManagedMediaSource)
|
||||
* */
|
||||
public synchronized void expand() {
|
||||
append(new PlaceholderMediaSource());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a {@link ManagedMediaSource} to the end of {@link DynamicConcatenatingMediaSource}.
|
||||
* @see DynamicConcatenatingMediaSource#addMediaSource
|
||||
* */
|
||||
public synchronized void append(@NonNull final ManagedMediaSource source) {
|
||||
internalSource.addMediaSource(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a {@link ManagedMediaSource} from {@link DynamicConcatenatingMediaSource}
|
||||
* at the given index. If this index is out of bound, then the removal is ignored.
|
||||
* @see DynamicConcatenatingMediaSource#removeMediaSource(int)
|
||||
* */
|
||||
public synchronized void remove(final int index) {
|
||||
if (index < 0 || index > internalSource.getSize()) return;
|
||||
|
||||
internalSource.removeMediaSource(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a {@link ManagedMediaSource} in {@link DynamicConcatenatingMediaSource}
|
||||
* from the given source index to the target index. If either index is out of bound,
|
||||
* then the call is ignored.
|
||||
* @see DynamicConcatenatingMediaSource#moveMediaSource(int, int)
|
||||
* */
|
||||
public synchronized void move(final int source, final int target) {
|
||||
if (source < 0 || target < 0) return;
|
||||
if (source >= internalSource.getSize() || target >= internalSource.getSize()) return;
|
||||
|
||||
internalSource.moveMediaSource(source, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the {@link ManagedMediaSource} at the given index by replacing it
|
||||
* with a {@link PlaceholderMediaSource}.
|
||||
* @see #invalidate(int, Runnable)
|
||||
* @see #update(int, ManagedMediaSource, Runnable)
|
||||
* */
|
||||
public synchronized void invalidate(final int index) {
|
||||
invalidate(index, /*doNothing=*/null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the {@link ManagedMediaSource} at the given index by replacing it
|
||||
* with a {@link PlaceholderMediaSource}.
|
||||
* @see #update(int, ManagedMediaSource, Runnable)
|
||||
* */
|
||||
public synchronized void invalidate(final int index,
|
||||
@Nullable final Runnable finalizingAction) {
|
||||
if (get(index) instanceof PlaceholderMediaSource) return;
|
||||
update(index, new PlaceholderMediaSource(), finalizingAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link ManagedMediaSource} in {@link DynamicConcatenatingMediaSource}
|
||||
* at the given index with a given {@link ManagedMediaSource}.
|
||||
* @see #update(int, ManagedMediaSource, Runnable)
|
||||
* */
|
||||
public synchronized void update(final int index, @NonNull final ManagedMediaSource source) {
|
||||
update(index, source, /*doNothing=*/null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link ManagedMediaSource} in {@link DynamicConcatenatingMediaSource}
|
||||
* at the given index with a given {@link ManagedMediaSource}. If the index is out of bound,
|
||||
* then the replacement is ignored.
|
||||
* @see DynamicConcatenatingMediaSource#addMediaSource
|
||||
* @see DynamicConcatenatingMediaSource#removeMediaSource(int, Runnable)
|
||||
* */
|
||||
public synchronized void update(final int index, @NonNull final ManagedMediaSource source,
|
||||
@Nullable final Runnable finalizingAction) {
|
||||
if (index < 0 || index >= internalSource.getSize()) return;
|
||||
|
||||
// Add and remove are sequential on the same thread, therefore here, the exoplayer
|
||||
// message queue must receive and process add before remove.
|
||||
|
||||
// However, finalizing action occurs strictly after the timeline has completed
|
||||
// all its changes on the playback thread, so it is possible, in the meantime, other calls
|
||||
// that modifies the playlist media source may occur in between. Therefore,
|
||||
// it is not safe to call remove as the finalizing action of add.
|
||||
internalSource.addMediaSource(index + 1, source);
|
||||
|
||||
// Also, because of the above, it is thus only safe to synchronize the player
|
||||
// in the finalizing action AFTER the removal is complete and the timeline has changed.
|
||||
internalSource.removeMediaSource(index, finalizingAction);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import android.util.Log;
|
|||
|
||||
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.ShuffleOrder;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
|
@ -14,6 +13,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo;
|
|||
import org.schabi.newpipe.player.mediasource.FailedMediaSource;
|
||||
import org.schabi.newpipe.player.mediasource.LoadedMediaSource;
|
||||
import org.schabi.newpipe.player.mediasource.ManagedMediaSource;
|
||||
import org.schabi.newpipe.player.mediasource.ManagedMediaSourcePlaylist;
|
||||
import org.schabi.newpipe.player.mediasource.PlaceholderMediaSource;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
|
@ -52,7 +52,6 @@ public class MediaSourceManager {
|
|||
* streams before will only be cached for future usage.
|
||||
*
|
||||
* @see #onMediaSourceReceived(PlayQueueItem, ManagedMediaSource)
|
||||
* @see #update(int, MediaSource, Runnable)
|
||||
* */
|
||||
private final static int WINDOW_SIZE = 1;
|
||||
|
||||
|
@ -103,7 +102,7 @@ public class MediaSourceManager {
|
|||
|
||||
@NonNull private final AtomicBoolean isBlocked;
|
||||
|
||||
@NonNull private DynamicConcatenatingMediaSource sources;
|
||||
@NonNull private ManagedMediaSourcePlaylist playlist;
|
||||
|
||||
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
||||
@NonNull final PlayQueue playQueue) {
|
||||
|
@ -143,7 +142,7 @@ public class MediaSourceManager {
|
|||
|
||||
this.isBlocked = new AtomicBoolean(false);
|
||||
|
||||
this.sources = new DynamicConcatenatingMediaSource();
|
||||
this.playlist = new ManagedMediaSourcePlaylist();
|
||||
|
||||
this.loadingItems = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
|
@ -167,7 +166,7 @@ public class MediaSourceManager {
|
|||
playQueueReactor.cancel();
|
||||
loaderReactor.dispose();
|
||||
syncReactor.dispose();
|
||||
sources.releaseSource();
|
||||
playlist.dispose();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -215,17 +214,18 @@ public class MediaSourceManager {
|
|||
break;
|
||||
case REMOVE:
|
||||
final RemoveEvent removeEvent = (RemoveEvent) event;
|
||||
remove(removeEvent.getRemoveIndex());
|
||||
playlist.remove(removeEvent.getRemoveIndex());
|
||||
break;
|
||||
case MOVE:
|
||||
final MoveEvent moveEvent = (MoveEvent) event;
|
||||
move(moveEvent.getFromIndex(), moveEvent.getToIndex());
|
||||
playlist.move(moveEvent.getFromIndex(), moveEvent.getToIndex());
|
||||
break;
|
||||
case REORDER:
|
||||
// Need to move to ensure the playing index from play queue matches that of
|
||||
// the source timeline, and then window correction can take care of the rest
|
||||
final ReorderEvent reorderEvent = (ReorderEvent) event;
|
||||
move(reorderEvent.getFromSelectedIndex(), reorderEvent.getToSelectedIndex());
|
||||
playlist.move(reorderEvent.getFromSelectedIndex(),
|
||||
reorderEvent.getToSelectedIndex());
|
||||
break;
|
||||
case RECOVERY:
|
||||
default:
|
||||
|
@ -266,10 +266,9 @@ public class MediaSourceManager {
|
|||
}
|
||||
|
||||
private boolean isPlaybackReady() {
|
||||
if (sources.getSize() != playQueue.size()) return false;
|
||||
final ManagedMediaSource mediaSource = playlist.get(playQueue.getIndex());
|
||||
if (mediaSource == null) return false;
|
||||
|
||||
final ManagedMediaSource mediaSource =
|
||||
(ManagedMediaSource) sources.getMediaSource(playQueue.getIndex());
|
||||
final PlayQueueItem playQueueItem = playQueue.getItem();
|
||||
return mediaSource.isStreamEqual(playQueueItem);
|
||||
}
|
||||
|
@ -290,7 +289,7 @@ public class MediaSourceManager {
|
|||
|
||||
if (isPlayQueueReady() && isPlaybackReady() && isBlocked.get()) {
|
||||
isBlocked.set(false);
|
||||
playbackListener.onPlaybackUnblock(sources);
|
||||
playbackListener.onPlaybackUnblock(playlist.getParentMediaSource());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,6 +321,7 @@ public class MediaSourceManager {
|
|||
}
|
||||
|
||||
private void maybeSynchronizePlayer() {
|
||||
cleanSweep();
|
||||
maybeUnblock();
|
||||
maybeSync();
|
||||
}
|
||||
|
@ -383,7 +383,7 @@ public class MediaSourceManager {
|
|||
|
||||
private void maybeLoadItem(@NonNull final PlayQueueItem item) {
|
||||
if (DEBUG) Log.d(TAG, "maybeLoadItem() called.");
|
||||
if (playQueue.indexOf(item) >= sources.getSize()) return;
|
||||
if (playQueue.indexOf(item) >= playlist.size()) return;
|
||||
|
||||
if (!loadingItems.contains(item) && isCorrectionNeeded(item)) {
|
||||
if (DEBUG) Log.d(TAG, "MediaSource - Loading=[" + item.getTitle() +
|
||||
|
@ -429,7 +429,7 @@ public class MediaSourceManager {
|
|||
if (itemIndex >= playQueue.getIndex() && isCorrectionNeeded(item)) {
|
||||
if (DEBUG) Log.d(TAG, "MediaSource - Updating index=[" + itemIndex + "] with " +
|
||||
"title=[" + item.getTitle() + "] at url=[" + item.getUrl() + "]");
|
||||
update(itemIndex, mediaSource, this::maybeSynchronizePlayer);
|
||||
playlist.update(itemIndex, mediaSource, this::maybeSynchronizePlayer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -445,10 +445,8 @@ public class MediaSourceManager {
|
|||
* */
|
||||
private boolean isCorrectionNeeded(@NonNull final PlayQueueItem item) {
|
||||
final int index = playQueue.indexOf(item);
|
||||
if (index == -1 || index >= sources.getSize()) return false;
|
||||
|
||||
final ManagedMediaSource mediaSource = (ManagedMediaSource) sources.getMediaSource(index);
|
||||
return mediaSource.shouldBeReplacedWith(item,
|
||||
final ManagedMediaSource mediaSource = playlist.get(index);
|
||||
return mediaSource != null && mediaSource.shouldBeReplacedWith(item,
|
||||
/*mightBeInProgress=*/index != playQueue.getIndex());
|
||||
}
|
||||
|
||||
|
@ -465,10 +463,9 @@ public class MediaSourceManager {
|
|||
* */
|
||||
private void maybeRenewCurrentIndex() {
|
||||
final int currentIndex = playQueue.getIndex();
|
||||
if (sources.getSize() <= currentIndex) return;
|
||||
final ManagedMediaSource currentSource = playlist.get(currentIndex);
|
||||
if (currentSource == null) return;
|
||||
|
||||
final ManagedMediaSource currentSource =
|
||||
(ManagedMediaSource) sources.getMediaSource(currentIndex);
|
||||
final PlayQueueItem currentItem = playQueue.getItem();
|
||||
if (!currentSource.shouldBeReplacedWith(currentItem, /*canInterruptOnRenew=*/true)) {
|
||||
maybeSynchronizePlayer();
|
||||
|
@ -477,7 +474,19 @@ public class MediaSourceManager {
|
|||
|
||||
if (DEBUG) Log.d(TAG, "MediaSource - Reloading currently playing, " +
|
||||
"index=[" + currentIndex + "], item=[" + currentItem.getTitle() + "]");
|
||||
update(currentIndex, new PlaceholderMediaSource(), this::loadImmediate);
|
||||
playlist.invalidate(currentIndex, this::loadImmediate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the entire playlist for {@link MediaSource}s that requires correction,
|
||||
* and replace these sources with a {@link PlaceholderMediaSource}.
|
||||
* */
|
||||
private void cleanSweep() {
|
||||
for (int index = 0; index < playlist.size(); index++) {
|
||||
if (isCorrectionNeeded(playQueue.getItem(index))) {
|
||||
playlist.invalidate(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// MediaSource Playlist Helpers
|
||||
|
@ -486,72 +495,14 @@ public class MediaSourceManager {
|
|||
private void resetSources() {
|
||||
if (DEBUG) Log.d(TAG, "resetSources() called.");
|
||||
|
||||
this.sources.releaseSource();
|
||||
this.sources = new DynamicConcatenatingMediaSource(false,
|
||||
// Shuffling is done on PlayQueue, thus no need to use ExoPlayer's shuffle order
|
||||
new ShuffleOrder.UnshuffledShuffleOrder(0));
|
||||
playlist.dispose();
|
||||
playlist = new ManagedMediaSourcePlaylist();
|
||||
}
|
||||
|
||||
private void populateSources() {
|
||||
if (DEBUG) Log.d(TAG, "populateSources() called.");
|
||||
if (sources.getSize() >= playQueue.size()) return;
|
||||
|
||||
for (int index = sources.getSize() - 1; index < playQueue.size(); index++) {
|
||||
emplace(index, new PlaceholderMediaSource());
|
||||
while (playlist.size() < playQueue.size()) {
|
||||
playlist.expand();
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// MediaSource Playlist Manipulation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/**
|
||||
* Places a {@link MediaSource} into the {@link DynamicConcatenatingMediaSource}
|
||||
* with position in respect to the play queue only if no {@link MediaSource}
|
||||
* already exists at the given index.
|
||||
* */
|
||||
private synchronized void emplace(final int index, @NonNull final MediaSource source) {
|
||||
if (index < sources.getSize()) return;
|
||||
|
||||
sources.addMediaSource(index, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a {@link MediaSource} from {@link DynamicConcatenatingMediaSource}
|
||||
* at the given index. If this index is out of bound, then the removal is ignored.
|
||||
* */
|
||||
private synchronized void remove(final int index) {
|
||||
if (index < 0 || index > sources.getSize()) return;
|
||||
|
||||
sources.removeMediaSource(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a {@link MediaSource} in {@link DynamicConcatenatingMediaSource}
|
||||
* from the given source index to the target index. If either index is out of bound,
|
||||
* then the call is ignored.
|
||||
* */
|
||||
private synchronized void move(final int source, final int target) {
|
||||
if (source < 0 || target < 0) return;
|
||||
if (source >= sources.getSize() || target >= sources.getSize()) return;
|
||||
|
||||
sources.moveMediaSource(source, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link MediaSource} in {@link DynamicConcatenatingMediaSource}
|
||||
* at the given index with a given {@link MediaSource}. If the index is out of bound,
|
||||
* then the replacement is ignored.
|
||||
* <br><br>
|
||||
* Not recommended to use on indices LESS THAN the currently playing index, since
|
||||
* this will modify the playback timeline prior to the index and may cause desynchronization
|
||||
* on the playing item between {@link PlayQueue} and {@link DynamicConcatenatingMediaSource}.
|
||||
* */
|
||||
private synchronized void update(final int index, @NonNull final MediaSource source,
|
||||
@Nullable final Runnable finalizingAction) {
|
||||
if (index < 0 || index >= sources.getSize()) return;
|
||||
|
||||
sources.addMediaSource(index + 1, source, () ->
|
||||
sources.removeMediaSource(index, finalizingAction));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue