mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-02-02 20:29:15 +00:00
-Added load debouncing to MediaSourceManager to prevent mass loading due to rapid timeline change.
-Added marquee title to main video player. -Modified destroyPlayer to always dispose play queue and media source manager. -Remove unused code from players.
This commit is contained in:
parent
1fb3774e03
commit
b4fdbdeb1b
@ -19,18 +19,13 @@
|
|||||||
|
|
||||||
package org.schabi.newpipe.player;
|
package org.schabi.newpipe.player;
|
||||||
|
|
||||||
import android.animation.Animator;
|
|
||||||
import android.animation.AnimatorListenerAdapter;
|
|
||||||
import android.animation.ValueAnimator;
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -67,11 +62,11 @@ import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListene
|
|||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
|
||||||
import org.schabi.newpipe.player.playback.PlaybackListener;
|
|
||||||
import org.schabi.newpipe.player.helper.AudioReactor;
|
import org.schabi.newpipe.player.helper.AudioReactor;
|
||||||
import org.schabi.newpipe.player.helper.CacheFactory;
|
import org.schabi.newpipe.player.helper.CacheFactory;
|
||||||
import org.schabi.newpipe.player.helper.LoadController;
|
import org.schabi.newpipe.player.helper.LoadController;
|
||||||
|
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
||||||
|
import org.schabi.newpipe.player.playback.PlaybackListener;
|
||||||
import org.schabi.newpipe.playlist.PlayQueue;
|
import org.schabi.newpipe.playlist.PlayQueue;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueAdapter;
|
import org.schabi.newpipe.playlist.PlayQueueAdapter;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
@ -93,14 +88,13 @@ import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
|
|||||||
*
|
*
|
||||||
* @author mauriciocolli
|
* @author mauriciocolli
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
@SuppressWarnings({"WeakerAccess"})
|
||||||
public abstract class BasePlayer implements Player.EventListener, PlaybackListener {
|
public abstract class BasePlayer implements Player.EventListener, PlaybackListener {
|
||||||
|
|
||||||
public static final boolean DEBUG = true;
|
public static final boolean DEBUG = true;
|
||||||
public static final String TAG = "BasePlayer";
|
public static final String TAG = "BasePlayer";
|
||||||
|
|
||||||
protected Context context;
|
protected Context context;
|
||||||
protected SharedPreferences sharedPreferences;
|
|
||||||
|
|
||||||
protected BroadcastReceiver broadcastReceiver;
|
protected BroadcastReceiver broadcastReceiver;
|
||||||
protected IntentFilter intentFilter;
|
protected IntentFilter intentFilter;
|
||||||
@ -156,7 +150,6 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
|
|
||||||
public BasePlayer(Context context) {
|
public BasePlayer(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
|
|
||||||
this.broadcastReceiver = new BroadcastReceiver() {
|
this.broadcastReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
@ -234,17 +227,15 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
|
|
||||||
// Re-initialization
|
// Re-initialization
|
||||||
destroyPlayer();
|
destroyPlayer();
|
||||||
if (playQueue != null) playQueue.dispose();
|
|
||||||
if (playbackManager != null) playbackManager.dispose();
|
|
||||||
initPlayer();
|
initPlayer();
|
||||||
setRepeatMode(repeatMode);
|
setRepeatMode(repeatMode);
|
||||||
setPlaybackParameters(playbackSpeed, playbackPitch);
|
setPlaybackParameters(playbackSpeed, playbackPitch);
|
||||||
|
|
||||||
// Good to go...
|
// Good to go...
|
||||||
initPlayback(this, queue);
|
initPlayback(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initPlayback(@NonNull final PlaybackListener listener, @NonNull final PlayQueue queue) {
|
protected void initPlayback(@NonNull final PlayQueue queue) {
|
||||||
playQueue = queue;
|
playQueue = queue;
|
||||||
playQueue.init();
|
playQueue.init();
|
||||||
playbackManager = new MediaSourceManager(this, playQueue);
|
playbackManager = new MediaSourceManager(this, playQueue);
|
||||||
@ -279,6 +270,8 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
simpleExoPlayer.release();
|
simpleExoPlayer.release();
|
||||||
}
|
}
|
||||||
if (isProgressLoopRunning()) stopProgressLoop();
|
if (isProgressLoopRunning()) stopProgressLoop();
|
||||||
|
if (playQueue != null) playQueue.dispose();
|
||||||
|
if (playbackManager != null) playbackManager.dispose();
|
||||||
if (audioReactor != null) audioReactor.abandonAudioFocus();
|
if (audioReactor != null) audioReactor.abandonAudioFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,9 +281,6 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
clearThumbnailCache();
|
clearThumbnailCache();
|
||||||
unregisterBroadcastReceiver();
|
unregisterBroadcastReceiver();
|
||||||
|
|
||||||
if (playQueue != null) playQueue.dispose();
|
|
||||||
if (playbackManager != null) playbackManager.dispose();
|
|
||||||
|
|
||||||
trackSelector = null;
|
trackSelector = null;
|
||||||
simpleExoPlayer = null;
|
simpleExoPlayer = null;
|
||||||
}
|
}
|
||||||
@ -602,7 +592,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
// If the user selects a new track, then the discontinuity occurs after the index is changed.
|
// If the user selects a new track, then the discontinuity occurs after the index is changed.
|
||||||
// Therefore, the only source that causes a discrepancy would be autoplay,
|
// Therefore, the only source that causes a discrepancy would be autoplay,
|
||||||
// which can only offset the current track by +1.
|
// which can only offset the current track by +1.
|
||||||
if (newWindowIndex != playQueue.getIndex()) playQueue.offsetIndex(+1);
|
if (newWindowIndex != playQueue.getIndex() && playbackManager != null) {
|
||||||
|
playQueue.offsetIndex(+1);
|
||||||
|
playbackManager.load();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -659,9 +652,6 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
if (DEBUG) Log.d(TAG, "Shutting down...");
|
if (DEBUG) Log.d(TAG, "Shutting down...");
|
||||||
|
|
||||||
playbackManager.dispose();
|
|
||||||
playQueue.dispose();
|
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -817,35 +807,6 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void animateAudio(final float from, final float to, int duration) {
|
|
||||||
ValueAnimator valueAnimator = new ValueAnimator();
|
|
||||||
valueAnimator.setFloatValues(from, to);
|
|
||||||
valueAnimator.setDuration(duration);
|
|
||||||
valueAnimator.addListener(new AnimatorListenerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationStart(Animator animation) {
|
|
||||||
if (simpleExoPlayer != null) simpleExoPlayer.setVolume(from);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationCancel(Animator animation) {
|
|
||||||
if (simpleExoPlayer != null) simpleExoPlayer.setVolume(to);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
if (simpleExoPlayer != null) simpleExoPlayer.setVolume(to);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationUpdate(ValueAnimator animation) {
|
|
||||||
if (simpleExoPlayer != null) simpleExoPlayer.setVolume(((float) animation.getAnimatedValue()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
valueAnimator.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Getters and Setters
|
// Getters and Setters
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
@ -854,10 +815,6 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
return simpleExoPlayer;
|
return simpleExoPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SharedPreferences getSharedPreferences() {
|
|
||||||
return sharedPreferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AudioReactor getAudioReactor() {
|
public AudioReactor getAudioReactor() {
|
||||||
return audioReactor;
|
return audioReactor;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
playerImpl.getPlayPauseButton().setImageResource(R.drawable.ic_play_arrow_white);
|
playerImpl.getPlayPauseButton().setImageResource(R.drawable.ic_play_arrow_white);
|
||||||
|
|
||||||
playerImpl.getPlayer().setPlayWhenReady(playerImpl.wasPlaying);
|
playerImpl.getPlayer().setPlayWhenReady(playerImpl.wasPlaying);
|
||||||
playerImpl.initPlayback(playerImpl, playerImpl.playQueue);
|
playerImpl.initPlayback(playerImpl.playQueue);
|
||||||
|
|
||||||
if (playerImpl.trackSelector != null && parameters != null) {
|
if (playerImpl.trackSelector != null && parameters != null) {
|
||||||
playerImpl.trackSelector.setParameters(parameters);
|
playerImpl.trackSelector.setParameters(parameters);
|
||||||
@ -265,6 +265,9 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
this.playPreviousButton = rootView.findViewById(R.id.playPreviousButton);
|
this.playPreviousButton = rootView.findViewById(R.id.playPreviousButton);
|
||||||
this.playNextButton = rootView.findViewById(R.id.playNextButton);
|
this.playNextButton = rootView.findViewById(R.id.playNextButton);
|
||||||
|
|
||||||
|
titleTextView.setSelected(true);
|
||||||
|
channelTextView.setSelected(true);
|
||||||
|
|
||||||
getRootView().setKeepScreenOn(true);
|
getRootView().setKeepScreenOn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,9 +353,9 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
this.getPlaybackQuality()
|
this.getPlaybackQuality()
|
||||||
);
|
);
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
destroyPlayer();
|
|
||||||
|
|
||||||
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
|
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
|
||||||
|
destroy();
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,7 +884,7 @@ public final class PopupVideoPlayer extends Service {
|
|||||||
mainHandler.post(new Runnable() {
|
mainHandler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
playerImpl.initPlayback(playerImpl, new SinglePlayQueue(info));
|
playerImpl.initPlayback(new SinglePlayQueue(info));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -475,7 +475,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||||||
if (selectedStreamIndex == menuItemIndex ||
|
if (selectedStreamIndex == menuItemIndex ||
|
||||||
availableStreams == null || availableStreams.size() <= menuItemIndex) return true;
|
availableStreams == null || availableStreams.size() <= menuItemIndex) return true;
|
||||||
|
|
||||||
final String oldResolution = getPlaybackQuality();
|
|
||||||
final String newResolution = availableStreams.get(menuItemIndex).resolution;
|
final String newResolution = availableStreams.get(menuItemIndex).resolution;
|
||||||
setRecovery();
|
setRecovery();
|
||||||
setPlaybackQuality(newResolution);
|
setPlaybackQuality(newResolution);
|
||||||
|
@ -17,13 +17,16 @@ import org.schabi.newpipe.playlist.events.RemoveEvent;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.annotations.NonNull;
|
import io.reactivex.annotations.NonNull;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.disposables.SerialDisposable;
|
import io.reactivex.disposables.SerialDisposable;
|
||||||
import io.reactivex.functions.Consumer;
|
import io.reactivex.functions.Consumer;
|
||||||
|
import io.reactivex.subjects.PublishSubject;
|
||||||
|
|
||||||
public class MediaSourceManager implements DeferredMediaSource.Callback {
|
public class MediaSourceManager {
|
||||||
private final String TAG = "MediaSourceManager@" + Integer.toHexString(hashCode());
|
private final String TAG = "MediaSourceManager@" + Integer.toHexString(hashCode());
|
||||||
// One-side rolling window size for default loading
|
// One-side rolling window size for default loading
|
||||||
// Effectively loads windowSize * 2 + 1 streams, must be greater than 0
|
// Effectively loads windowSize * 2 + 1 streams, must be greater than 0
|
||||||
@ -31,6 +34,16 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||||||
private final PlaybackListener playbackListener;
|
private final PlaybackListener playbackListener;
|
||||||
private final PlayQueue playQueue;
|
private final PlayQueue playQueue;
|
||||||
|
|
||||||
|
// Process only the last load order when receiving a stream of load orders (lessens IO)
|
||||||
|
// The lower it is, the faster the error processing during loading
|
||||||
|
// The higher it is, the less loading occurs during rapid timeline changes
|
||||||
|
// Not recommended to go below 50ms or above 500ms
|
||||||
|
private final long loadDebounceMillis;
|
||||||
|
private final PublishSubject<Long> loadSignal;
|
||||||
|
private final Disposable debouncedLoader;
|
||||||
|
|
||||||
|
private final DeferredMediaSource.Callback sourceBuilder;
|
||||||
|
|
||||||
private DynamicConcatenatingMediaSource sources;
|
private DynamicConcatenatingMediaSource sources;
|
||||||
|
|
||||||
private Subscription playQueueReactor;
|
private Subscription playQueueReactor;
|
||||||
@ -40,17 +53,27 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||||||
|
|
||||||
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
||||||
@NonNull final PlayQueue playQueue) {
|
@NonNull final PlayQueue playQueue) {
|
||||||
this(listener, playQueue, 1);
|
this(listener, playQueue, 1, 200L);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
private MediaSourceManager(@NonNull final PlaybackListener listener,
|
||||||
@NonNull final PlayQueue playQueue,
|
@NonNull final PlayQueue playQueue,
|
||||||
final int windowSize) {
|
final int windowSize,
|
||||||
|
final long loadDebounceMillis) {
|
||||||
|
if (windowSize <= 0) {
|
||||||
|
throw new UnsupportedOperationException("MediaSourceManager window size must be greater than 0");
|
||||||
|
}
|
||||||
|
|
||||||
this.playbackListener = listener;
|
this.playbackListener = listener;
|
||||||
this.playQueue = playQueue;
|
this.playQueue = playQueue;
|
||||||
this.windowSize = windowSize;
|
this.windowSize = windowSize;
|
||||||
|
this.loadDebounceMillis = loadDebounceMillis;
|
||||||
|
|
||||||
this.syncReactor = new SerialDisposable();
|
this.syncReactor = new SerialDisposable();
|
||||||
|
this.loadSignal = PublishSubject.create();
|
||||||
|
this.debouncedLoader = getDebouncedLoader();
|
||||||
|
|
||||||
|
this.sourceBuilder = getSourceBuilder();
|
||||||
|
|
||||||
this.sources = new DynamicConcatenatingMediaSource();
|
this.sources = new DynamicConcatenatingMediaSource();
|
||||||
|
|
||||||
@ -63,9 +86,13 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||||||
// DeferredMediaSource listener
|
// DeferredMediaSource listener
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
private DeferredMediaSource.Callback getSourceBuilder() {
|
||||||
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
|
return new DeferredMediaSource.Callback() {
|
||||||
return playbackListener.sourceOf(item, info);
|
@Override
|
||||||
|
public MediaSource sourceOf(PlayQueueItem item, StreamInfo info) {
|
||||||
|
return playbackListener.sourceOf(item, info);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
@ -75,6 +102,8 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||||||
* Dispose the manager and releases all message buses and loaders.
|
* Dispose the manager and releases all message buses and loaders.
|
||||||
* */
|
* */
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
|
if (loadSignal != null) loadSignal.onComplete();
|
||||||
|
if (debouncedLoader != null) debouncedLoader.dispose();
|
||||||
if (playQueueReactor != null) playQueueReactor.cancel();
|
if (playQueueReactor != null) playQueueReactor.cancel();
|
||||||
if (syncReactor != null) syncReactor.dispose();
|
if (syncReactor != null) syncReactor.dispose();
|
||||||
if (sources != null) sources.releaseSource();
|
if (sources != null) sources.releaseSource();
|
||||||
@ -90,23 +119,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||||||
* Unblocks the player once the item at the current index is loaded.
|
* Unblocks the player once the item at the current index is loaded.
|
||||||
* */
|
* */
|
||||||
public void load() {
|
public void load() {
|
||||||
// The current item has higher priority
|
loadSignal.onNext(System.currentTimeMillis());
|
||||||
final int currentIndex = playQueue.getIndex();
|
|
||||||
final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
|
|
||||||
if (currentItem == null) return;
|
|
||||||
load(currentItem);
|
|
||||||
|
|
||||||
// The rest are just for seamless playback
|
|
||||||
final int leftBound = Math.max(0, currentIndex - windowSize);
|
|
||||||
final int rightLimit = currentIndex + windowSize + 1;
|
|
||||||
final int rightBound = Math.min(playQueue.size(), rightLimit);
|
|
||||||
final List<PlayQueueItem> items = new ArrayList<>(playQueue.getStreams().subList(leftBound, rightBound));
|
|
||||||
|
|
||||||
// Do a round robin
|
|
||||||
final int excess = rightLimit - playQueue.size();
|
|
||||||
if (excess >= 0) items.addAll(playQueue.getStreams().subList(0, Math.min(playQueue.size(), excess)));
|
|
||||||
|
|
||||||
for (final PlayQueueItem item: items) load(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,7 +254,27 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||||||
syncReactor.set(currentItem.getStream().subscribe(syncPlayback, onError));
|
syncReactor.set(currentItem.getStream().subscribe(syncPlayback, onError));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void load(@Nullable final PlayQueueItem item) {
|
private void loadInternal() {
|
||||||
|
// The current item has higher priority
|
||||||
|
final int currentIndex = playQueue.getIndex();
|
||||||
|
final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
|
||||||
|
if (currentItem == null) return;
|
||||||
|
loadItem(currentItem);
|
||||||
|
|
||||||
|
// The rest are just for seamless playback
|
||||||
|
final int leftBound = Math.max(0, currentIndex - windowSize);
|
||||||
|
final int rightLimit = currentIndex + windowSize + 1;
|
||||||
|
final int rightBound = Math.min(playQueue.size(), rightLimit);
|
||||||
|
final List<PlayQueueItem> items = new ArrayList<>(playQueue.getStreams().subList(leftBound, rightBound));
|
||||||
|
|
||||||
|
// Do a round robin
|
||||||
|
final int excess = rightLimit - playQueue.size();
|
||||||
|
if (excess >= 0) items.addAll(playQueue.getStreams().subList(0, Math.min(playQueue.size(), excess)));
|
||||||
|
|
||||||
|
for (final PlayQueueItem item: items) loadItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadItem(@Nullable final PlayQueueItem item) {
|
||||||
if (item == null) return;
|
if (item == null) return;
|
||||||
|
|
||||||
final int index = playQueue.indexOf(item);
|
final int index = playQueue.indexOf(item);
|
||||||
@ -261,10 +294,21 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||||||
if (sources == null) return;
|
if (sources == null) return;
|
||||||
|
|
||||||
for (final PlayQueueItem item : playQueue.getStreams()) {
|
for (final PlayQueueItem item : playQueue.getStreams()) {
|
||||||
insert(playQueue.indexOf(item), new DeferredMediaSource(item, this));
|
insert(playQueue.indexOf(item), new DeferredMediaSource(item, sourceBuilder));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Disposable getDebouncedLoader() {
|
||||||
|
return loadSignal
|
||||||
|
.debounce(loadDebounceMillis, TimeUnit.MILLISECONDS)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(new Consumer<Long>() {
|
||||||
|
@Override
|
||||||
|
public void accept(Long timestamp) throws Exception {
|
||||||
|
loadInternal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Media Source List Manipulation
|
// Media Source List Manipulation
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
@ -287,7 +331,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||||||
* If the play queue index does not exist, the removal is ignored.
|
* If the play queue index does not exist, the removal is ignored.
|
||||||
* */
|
* */
|
||||||
private void remove(final int queueIndex) {
|
private void remove(final int queueIndex) {
|
||||||
if (queueIndex < 0) return;
|
if (queueIndex < 0 || queueIndex > sources.getSize()) return;
|
||||||
|
|
||||||
sources.removeMediaSource(queueIndex);
|
sources.removeMediaSource(queueIndex);
|
||||||
}
|
}
|
||||||
|
@ -158,11 +158,16 @@
|
|||||||
android:id="@+id/titleTextView"
|
android:id="@+id/titleTextView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="end"
|
android:ellipsize="marquee"
|
||||||
android:maxLines="1"
|
android:fadingEdge="horizontal"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:scrollHorizontally="true"
|
||||||
|
android:singleLine="true"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
tools:ignore="RtlHardcoded"
|
tools:ignore="RtlHardcoded"
|
||||||
tools:text="The Video Title LONG very LONG"/>
|
tools:text="The Video Title LONG very LONG"/>
|
||||||
|
|
||||||
@ -170,10 +175,15 @@
|
|||||||
android:id="@+id/channelTextView"
|
android:id="@+id/channelTextView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="end"
|
android:ellipsize="marquee"
|
||||||
android:maxLines="1"
|
android:fadingEdge="horizontal"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:scrollHorizontally="true"
|
||||||
|
android:singleLine="true"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
tools:text="The Video Artist LONG very LONG very Long"/>
|
tools:text="The Video Artist LONG very LONG very Long"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user