-Added auto-queuing to allow next or related streams to queue up when the last item on play queue is playing.
-Added toggle to enable auto-queuing. -Modified main video player to only pause the video onPause. -Fixed main video player not saving play queue state onStop.
This commit is contained in:
parent
7f068b691b
commit
d01aeab242
6 changed files with 115 additions and 26 deletions
|
@ -68,6 +68,7 @@ import org.schabi.newpipe.player.playback.PlaybackListener;
|
|||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueAdapter;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.util.SerializedCache;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -818,22 +819,32 @@ public abstract class BasePlayer implements
|
|||
initThumbnail(info == null ? item.getThumbnailUrl() : info.getThumbnailUrl());
|
||||
}
|
||||
|
||||
final int currentSourceIndex = playQueue.indexOf(item);
|
||||
onMetadataChanged(item, info, currentSourceIndex, hasPlayQueueItemChanged);
|
||||
final int currentPlayQueueIndex = playQueue.indexOf(item);
|
||||
onMetadataChanged(item, info, currentPlayQueueIndex, hasPlayQueueItemChanged);
|
||||
|
||||
// Check if on wrong window
|
||||
if (simpleExoPlayer == null) return;
|
||||
if (currentSourceIndex != playQueue.getIndex()) {
|
||||
Log.e(TAG, "Play Queue may be desynchronized: item index=[" + currentSourceIndex +
|
||||
"], queue index=[" + playQueue.getIndex() + "]");
|
||||
final int currentPlaylistIndex = simpleExoPlayer.getCurrentWindowIndex();
|
||||
// Check if on wrong window
|
||||
if (currentPlayQueueIndex != playQueue.getIndex()) {
|
||||
Log.e(TAG, "Play Queue may be desynchronized: item " +
|
||||
"index=[" + currentPlayQueueIndex + "], " +
|
||||
"queue index=[" + playQueue.getIndex() + "]");
|
||||
|
||||
// on metadata changed
|
||||
} else if (simpleExoPlayer.getCurrentWindowIndex() != currentSourceIndex || !isPlaying()) {
|
||||
final long startPos = info != null ? info.start_position : 0;
|
||||
if (DEBUG) Log.d(TAG, "Rewinding to correct window=[" + currentSourceIndex + "]," +
|
||||
} else if (currentPlaylistIndex != currentPlayQueueIndex || !isPlaying()) {
|
||||
final long startPos = info != null ? info.start_position : C.TIME_UNSET;
|
||||
if (DEBUG) Log.d(TAG, "Rewinding to correct" +
|
||||
" window=[" + currentPlayQueueIndex + "]," +
|
||||
" at=[" + getTimeString((int)startPos) + "]," +
|
||||
" from=[" + simpleExoPlayer.getCurrentWindowIndex() + "].");
|
||||
simpleExoPlayer.seekTo(currentSourceIndex, startPos);
|
||||
simpleExoPlayer.seekTo(currentPlayQueueIndex, startPos);
|
||||
}
|
||||
|
||||
// when starting playback on the last item, maybe auto queue
|
||||
if (info != null && currentPlayQueueIndex == playQueue.size() - 1 &&
|
||||
PlayerHelper.isAutoQueueEnabled(context)) {
|
||||
final PlayQueue autoQueue = PlayerHelper.autoQueueOf(info, playQueue.getStreams());
|
||||
if (autoQueue != null) playQueue.append(autoQueue.getStreams());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -129,6 +129,7 @@ public final class MainVideoPlayer extends Activity {
|
|||
super.onSaveInstanceState(outState);
|
||||
if (this.playerImpl == null) return;
|
||||
|
||||
playerImpl.setRecovery();
|
||||
final Intent intent = NavigationHelper.getPlayerIntent(
|
||||
getApplicationContext(),
|
||||
this.getClass(),
|
||||
|
@ -156,31 +157,27 @@ public final class MainVideoPlayer extends Activity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (DEBUG) Log.d(TAG, "onStop() called");
|
||||
activityPaused = true;
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
if (DEBUG) Log.d(TAG, "onPause() called");
|
||||
|
||||
if (playerImpl.getPlayer() != null) {
|
||||
playerImpl.wasPlaying = playerImpl.getPlayer().getPlayWhenReady();
|
||||
playerImpl.setRecovery();
|
||||
playerImpl.destroyPlayer();
|
||||
if (playerImpl.getPlayer() != null && playerImpl.isPlaying() && !activityPaused) {
|
||||
playerImpl.wasPlaying = true;
|
||||
playerImpl.onVideoPlayPause();
|
||||
}
|
||||
activityPaused = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (DEBUG) Log.d(TAG, "onResume() called");
|
||||
if (activityPaused) {
|
||||
playerImpl.initPlayer();
|
||||
playerImpl.getPlayPauseButton().setImageResource(R.drawable.ic_play_arrow_white);
|
||||
|
||||
playerImpl.getPlayer().setPlayWhenReady(playerImpl.wasPlaying);
|
||||
playerImpl.initPlayback(playerImpl.playQueue);
|
||||
|
||||
activityPaused = false;
|
||||
if (playerImpl.getPlayer() != null && playerImpl.wasPlaying()
|
||||
&& !playerImpl.isPlaying() && activityPaused) {
|
||||
playerImpl.onVideoPlayPause();
|
||||
}
|
||||
activityPaused = false;
|
||||
|
||||
if(globalScreenOrientationLocked()) {
|
||||
boolean lastOrientationWasLandscape
|
||||
= defaultPreferences.getBoolean(getString(R.string.last_orientation_landscape_key), false);
|
||||
|
@ -873,6 +870,13 @@ public final class MainVideoPlayer extends Activity {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
if (DEBUG) Log.d(TAG, "onDown() called with: e = [" + e + "]");
|
||||
|
||||
return super.onDown(e);
|
||||
}
|
||||
|
||||
private final boolean isPlayerGestureEnabled = PlayerHelper.isPlayerGestureEnabled(getApplicationContext());
|
||||
|
||||
private final float stepsBrightness = 15, stepBrightness = (1f / stepsBrightness), minBrightness = .01f;
|
||||
|
|
|
@ -4,22 +4,33 @@ import android.content.Context;
|
|||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.SeekParameters;
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.Subtitles;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.SubtitlesFormat;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Formatter;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
|
@ -76,6 +87,7 @@ public class PlayerHelper {
|
|||
return displayName + (subtitles.isAutoGenerated() ? " (" + context.getString(R.string.caption_auto_generated)+ ")" : "");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String resizeTypeOf(@NonNull final Context context,
|
||||
@AspectRatioFrameLayout.ResizeMode final int resizeMode) {
|
||||
switch (resizeMode) {
|
||||
|
@ -86,14 +98,58 @@ public class PlayerHelper {
|
|||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String cacheKeyOf(@NonNull final StreamInfo info, @NonNull VideoStream video) {
|
||||
return info.getUrl() + video.getResolution() + video.getFormat().getName();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String cacheKeyOf(@NonNull final StreamInfo info, @NonNull AudioStream audio) {
|
||||
return info.getUrl() + audio.getAverageBitrate() + audio.getFormat().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a {@link StreamInfo} and the existing queue items, provide the
|
||||
* {@link SinglePlayQueue} consisting of the next video for auto queuing.
|
||||
* <br><br>
|
||||
* This method detects and prevents cycle by naively checking if a
|
||||
* candidate next video's url already exists in the existing items.
|
||||
* <br><br>
|
||||
* To select the next video, {@link StreamInfo#getNextVideo()} is first
|
||||
* checked. If it is nonnull and is not part of the existing items, then
|
||||
* it will be used as the next video. Otherwise, an random item with
|
||||
* non-repeating url will be selected from the {@link StreamInfo#getRelatedStreams()}.
|
||||
* */
|
||||
@Nullable
|
||||
public static PlayQueue autoQueueOf(@NonNull final StreamInfo info,
|
||||
@NonNull final List<PlayQueueItem> existingItems) {
|
||||
Set<String> urls = new HashSet<>(existingItems.size());
|
||||
for (final PlayQueueItem item : existingItems) {
|
||||
urls.add(item.getUrl());
|
||||
}
|
||||
|
||||
final StreamInfoItem nextVideo = info.getNextVideo();
|
||||
if (nextVideo != null && !urls.contains(nextVideo.getUrl())) {
|
||||
return new SinglePlayQueue(nextVideo);
|
||||
}
|
||||
|
||||
final List<InfoItem> relatedItems = info.getRelatedStreams();
|
||||
if (relatedItems == null) return null;
|
||||
|
||||
List<StreamInfoItem> autoQueueItems = new ArrayList<>();
|
||||
for (final InfoItem item : info.getRelatedStreams()) {
|
||||
if (item instanceof StreamInfoItem && !urls.contains(item.getUrl())) {
|
||||
autoQueueItems.add((StreamInfoItem) item);
|
||||
}
|
||||
}
|
||||
Collections.shuffle(autoQueueItems);
|
||||
return autoQueueItems.isEmpty() ? null : new SinglePlayQueue(autoQueueItems.get(0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Settings Resolution
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) {
|
||||
return isResumeAfterAudioFocusGain(context, false);
|
||||
}
|
||||
|
@ -110,6 +166,10 @@ public class PlayerHelper {
|
|||
return isRememberingPopupDimensions(context, true);
|
||||
}
|
||||
|
||||
public static boolean isAutoQueueEnabled(@NonNull final Context context) {
|
||||
return isAutoQueueEnabled(context, false);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static SeekParameters getSeekParameters(@NonNull final Context context) {
|
||||
return isUsingInexactSeek(context, false) ?
|
||||
|
@ -187,4 +247,8 @@ public class PlayerHelper {
|
|||
private static boolean isUsingInexactSeek(@NonNull final Context context, final boolean b) {
|
||||
return getPreferences(context).getBoolean(context.getString(R.string.use_inexact_seek_key), b);
|
||||
}
|
||||
|
||||
private static boolean isAutoQueueEnabled(@NonNull final Context context, final boolean b) {
|
||||
return getPreferences(context).getBoolean(context.getString(R.string.auto_queue_key), b);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<string name="resume_on_audio_focus_gain_key" translatable="false">resume_on_audio_focus_gain</string>
|
||||
<string name="popup_remember_size_pos_key" translatable="false">popup_remember_size_pos_key</string>
|
||||
<string name="use_inexact_seek_key" translatable="false">use_inexact_seek_key</string>
|
||||
<string name="auto_queue_key" translatable="false">auto_queue_key</string>
|
||||
|
||||
<string name="default_resolution_key" translatable="false">default_resolution</string>
|
||||
<string name="default_resolution_value" translatable="false">360p</string>
|
||||
|
|
|
@ -74,6 +74,8 @@
|
|||
<string name="popup_remember_size_pos_summary">Remember last size and position of popup</string>
|
||||
<string name="use_inexact_seek_title">Use fast inexact seek</string>
|
||||
<string name="use_inexact_seek_summary">Inexact seek allows the player to seek to positions faster with reduced precision</string>
|
||||
<string name="auto_queue_title">Auto-queue next stream</string>
|
||||
<string name="auto_queue_summary">Automatically append a related stream when playback starts on the last stream in play queue.</string>
|
||||
<string name="player_gesture_controls_title">Player gesture controls</string>
|
||||
<string name="player_gesture_controls_summary">Use gestures to control the brightness and volume of the player</string>
|
||||
<string name="show_search_suggestions_title">Search suggestions</string>
|
||||
|
|
|
@ -30,6 +30,13 @@
|
|||
android:key="@string/show_search_suggestions_key"
|
||||
android:summary="@string/show_search_suggestions_summary"
|
||||
android:title="@string/show_search_suggestions_title"/>
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="@string/auto_queue_key"
|
||||
android:summary="@string/auto_queue_summary"
|
||||
android:title="@string/auto_queue_title"/>
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="@string/kiosk_page_key"
|
||||
android:entries="@array/main_page_content_names"
|
||||
|
|
Loading…
Add table
Reference in a new issue