mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-02 23:13:12 +01:00
-Changed quality resolution to persist across player.
-Updated ExoPlayer to 2.5.4. -Expanded button size in main video player play queue. -Removed Quality event. -Extracted player error strings to xml.
This commit is contained in:
parent
d0e626c6ee
commit
0806344ffb
@ -66,7 +66,7 @@ dependencies {
|
||||
compile 'de.hdodenhof:circleimageview:2.1.0'
|
||||
compile 'com.github.nirhart:parallaxscroll:1.0'
|
||||
compile 'com.nononsenseapps:filepicker:3.0.1'
|
||||
compile 'com.google.android.exoplayer:exoplayer:r2.5.3'
|
||||
compile 'com.google.android.exoplayer:exoplayer:r2.5.4'
|
||||
|
||||
debugCompile 'com.facebook.stetho:stetho:1.5.0'
|
||||
debugCompile 'com.facebook.stetho:stetho-urlconnection:1.5.0'
|
||||
|
@ -788,14 +788,14 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
((HistoryListener) activity).onVideoPlayed(currentInfo, getSelectedVideoStream());
|
||||
}
|
||||
|
||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo, actionBarHandler.getSelectedVideoStream());
|
||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
|
||||
final Intent intent;
|
||||
if (append) {
|
||||
Toast.makeText(activity, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
|
||||
intent = NavigationHelper.getPlayerEnqueueIntent(activity, PopupVideoPlayer.class, playQueue);
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||
intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue);
|
||||
intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue, getSelectedVideoStream().resolution);
|
||||
}
|
||||
activity.startService(intent);
|
||||
}
|
||||
@ -866,8 +866,8 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
|| (Build.VERSION.SDK_INT < 16);
|
||||
if (!useOldPlayer) {
|
||||
// ExoPlayer
|
||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo, actionBarHandler.getSelectedVideoStream());
|
||||
mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue);
|
||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
|
||||
mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue, getSelectedVideoStream().resolution);
|
||||
} else {
|
||||
// Internal Player
|
||||
mIntent = new Intent(activity, PlayVideoActivity.class)
|
||||
|
@ -388,13 +388,23 @@ public final class BackgroundPlayer extends Service {
|
||||
@Override
|
||||
public void onRecoverableError(Exception exception) {
|
||||
exception.printStackTrace();
|
||||
Toast.makeText(context, "Failed to play this audio", Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (errorToast == null) {
|
||||
errorToast = Toast.makeText(context, R.string.player_audio_failure, Toast.LENGTH_SHORT);
|
||||
errorToast.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnrecoverableError(Exception exception) {
|
||||
exception.printStackTrace();
|
||||
Toast.makeText(context, "Unexpected error occurred", Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (errorToast != null) {
|
||||
errorToast.cancel();
|
||||
}
|
||||
errorToast = Toast.makeText(context, R.string.player_unexpected_failure, Toast.LENGTH_SHORT);
|
||||
errorToast.show();
|
||||
|
||||
shutdown();
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
@ -125,6 +126,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||
public static final String REPEAT_MODE = "repeat_mode";
|
||||
public static final String PLAYBACK_PITCH = "playback_pitch";
|
||||
public static final String PLAYBACK_SPEED = "playback_speed";
|
||||
public static final String PLAYBACK_QUALITY = "playback_quality";
|
||||
public static final String PLAY_QUEUE = "play_queue";
|
||||
public static final String APPEND_ONLY = "append_only";
|
||||
|
||||
@ -141,6 +143,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||
protected StreamInfo currentInfo;
|
||||
protected PlayQueueItem currentItem;
|
||||
|
||||
protected Toast errorToast;
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Player
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@ -659,8 +662,8 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||
* {@link ExoPlaybackException#TYPE_SOURCE TYPE_SOURCE}: <br><br>
|
||||
* If the current {@link com.google.android.exoplayer2.Timeline.Window window} has
|
||||
* duration and position greater than 0, then we know the current window is working correctly
|
||||
* and the error is produced by transitioning into a bad window, therefore we simply increment
|
||||
* the current index. Otherwise, we report an error to the play queue.
|
||||
* and the error is produced by transitioning into a bad window, therefore we report an error
|
||||
* to the play queue based on if the current error can be skipped.
|
||||
*
|
||||
* This is done because ExoPlayer reports the source exceptions before window is
|
||||
* transitioned on seamless playback.
|
||||
@ -668,27 +671,33 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||
* Because player error causes ExoPlayer to go back to {@link Player#STATE_IDLE STATE_IDLE},
|
||||
* we reset and prepare the media source again to resume playback.<br><br>
|
||||
*
|
||||
* {@link ExoPlaybackException#TYPE_RENDERER TYPE_RENDERER} and
|
||||
* {@link ExoPlaybackException#TYPE_UNEXPECTED TYPE_UNEXPECTED}: <br><br>
|
||||
* If renderer failed or unexpected exceptions occurred, treat the error as unrecoverable.
|
||||
* If a runtime error occurred, then we can try to recover it by restarting the playback
|
||||
* after setting the timestamp recovery.
|
||||
*
|
||||
* {@link ExoPlaybackException#TYPE_RENDERER TYPE_RENDERER}: <br><br>
|
||||
* If the renderer failed, treat the error as unrecoverable.
|
||||
*
|
||||
* @see Player.EventListener#onPlayerError(ExoPlaybackException)
|
||||
* */
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
if (DEBUG) Log.d(TAG, "onPlayerError() called with: error = [" + error + "]");
|
||||
if (errorToast != null) {
|
||||
errorToast.cancel();
|
||||
errorToast = null;
|
||||
}
|
||||
|
||||
switch (error.type) {
|
||||
case ExoPlaybackException.TYPE_SOURCE:
|
||||
if (simpleExoPlayer.getDuration() < 0 || simpleExoPlayer.getCurrentPosition() < 0) {
|
||||
playQueue.error();
|
||||
onRecoverableError(error);
|
||||
} else {
|
||||
playQueue.offsetIndex(+1);
|
||||
}
|
||||
|
||||
playbackManager.reset();
|
||||
playbackManager.load();
|
||||
final boolean skippable = simpleExoPlayer.getDuration() >= 0 && simpleExoPlayer.getCurrentPosition() >= 0;
|
||||
playQueue.error(skippable);
|
||||
onRecoverableError(error);
|
||||
break;
|
||||
case ExoPlaybackException.TYPE_UNEXPECTED:
|
||||
onRecoverableError(error);
|
||||
setRecovery();
|
||||
reload();
|
||||
break;
|
||||
default:
|
||||
onUnrecoverableError(error);
|
||||
@ -883,6 +892,13 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||
return pitchFormatter.format(pitch);
|
||||
}
|
||||
|
||||
protected void reload() {
|
||||
if (playbackManager != null) {
|
||||
playbackManager.reset();
|
||||
playbackManager.load();
|
||||
}
|
||||
}
|
||||
|
||||
protected void startProgressLoop() {
|
||||
if (progressUpdateReactor != null) progressUpdateReactor.dispose();
|
||||
progressUpdateReactor = getProgressReactor();
|
||||
|
@ -344,7 +344,8 @@ public final class MainVideoPlayer extends Activity {
|
||||
this.getPlayQueue(),
|
||||
this.simpleExoPlayer.getRepeatMode(),
|
||||
this.getPlaybackSpeed(),
|
||||
this.getPlaybackPitch()
|
||||
this.getPlaybackPitch(),
|
||||
this.getPlaybackQuality()
|
||||
);
|
||||
context.startService(intent);
|
||||
destroyPlayer();
|
||||
@ -432,13 +433,23 @@ public final class MainVideoPlayer extends Activity {
|
||||
@Override
|
||||
public void onRecoverableError(Exception exception) {
|
||||
exception.printStackTrace();
|
||||
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (errorToast == null) {
|
||||
errorToast = Toast.makeText(context, R.string.player_video_failure, Toast.LENGTH_SHORT);
|
||||
errorToast.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnrecoverableError(Exception exception) {
|
||||
exception.printStackTrace();
|
||||
Toast.makeText(context, "Unexpected error occurred", Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (errorToast != null) {
|
||||
errorToast.cancel();
|
||||
}
|
||||
errorToast = Toast.makeText(context, R.string.player_unexpected_failure, Toast.LENGTH_SHORT);
|
||||
errorToast.show();
|
||||
|
||||
shutdown();
|
||||
}
|
||||
|
||||
@ -447,6 +458,12 @@ public final class MainVideoPlayer extends Activity {
|
||||
return ListHelper.getDefaultResolutionIndex(context, sortedVideos);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
|
||||
final String playbackQuality) {
|
||||
return ListHelper.getDefaultResolutionIndex(context, sortedVideos, playbackQuality);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// States
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -441,7 +441,8 @@ public final class PopupVideoPlayer extends Service {
|
||||
this.getPlayQueue(),
|
||||
this.simpleExoPlayer.getRepeatMode(),
|
||||
this.getPlaybackSpeed(),
|
||||
this.getPlaybackPitch()
|
||||
this.getPlaybackPitch(),
|
||||
this.getPlaybackQuality()
|
||||
);
|
||||
if (!isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
@ -468,13 +469,23 @@ public final class PopupVideoPlayer extends Service {
|
||||
@Override
|
||||
public void onRecoverableError(Exception exception) {
|
||||
exception.printStackTrace();
|
||||
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (errorToast == null) {
|
||||
errorToast = Toast.makeText(context, R.string.player_video_failure, Toast.LENGTH_SHORT);
|
||||
errorToast.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnrecoverableError(Exception exception) {
|
||||
exception.printStackTrace();
|
||||
Toast.makeText(context, "Unexpected error occurred", Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (errorToast != null) {
|
||||
errorToast.cancel();
|
||||
}
|
||||
errorToast = Toast.makeText(context, R.string.player_unexpected_failure, Toast.LENGTH_SHORT);
|
||||
errorToast.show();
|
||||
|
||||
shutdown();
|
||||
}
|
||||
|
||||
@ -503,6 +514,12 @@ public final class PopupVideoPlayer extends Service {
|
||||
return ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
|
||||
final String playbackQuality) {
|
||||
return ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos, playbackQuality);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Activity Event Listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -25,6 +25,7 @@ import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
@ -48,6 +49,7 @@ import android.widget.TextView;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.MergingMediaSource;
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||
@ -82,15 +84,17 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
|
||||
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
|
||||
|
||||
private ArrayList<VideoStream> availableStreams;
|
||||
private int selectedStreamIndex;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Player
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds
|
||||
|
||||
private ArrayList<VideoStream> availableStreams;
|
||||
private int selectedStreamIndex;
|
||||
|
||||
protected String playbackQuality;
|
||||
|
||||
private boolean startedFromNewPipe = true;
|
||||
protected boolean wasPlaying = false;
|
||||
|
||||
@ -125,7 +129,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
private Handler controlsVisibilityHandler = new Handler();
|
||||
|
||||
private boolean isSomePopupMenuVisible = false;
|
||||
private boolean qualityChanged = false;
|
||||
private int qualityPopupMenuGroupId = 69;
|
||||
private PopupMenu qualityPopupMenu;
|
||||
|
||||
@ -197,6 +200,17 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleIntent(final Intent intent) {
|
||||
if (intent == null) return;
|
||||
|
||||
if (intent.hasExtra(PLAYBACK_QUALITY)) {
|
||||
setPlaybackQuality(intent.getStringExtra(PLAYBACK_QUALITY));
|
||||
}
|
||||
|
||||
super.handleIntent(intent);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// UI Builders
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@ -232,6 +246,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
|
||||
protected abstract int getDefaultResolutionIndex(final List<VideoStream> sortedVideos);
|
||||
|
||||
protected abstract int getOverrideResolutionIndex(final List<VideoStream> sortedVideos, final String playbackQuality);
|
||||
|
||||
@Override
|
||||
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
|
||||
super.sync(item, info);
|
||||
@ -241,11 +257,10 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
if (info != null) {
|
||||
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
|
||||
availableStreams = new ArrayList<>(videos);
|
||||
final int qualityIndex = item.getQualityIndex();
|
||||
if (qualityIndex == PlayQueueItem.DEFAULT_QUALITY) {
|
||||
if (playbackQuality == null) {
|
||||
selectedStreamIndex = getDefaultResolutionIndex(videos);
|
||||
} else {
|
||||
selectedStreamIndex = qualityIndex;
|
||||
selectedStreamIndex = getOverrideResolutionIndex(videos, getPlaybackQuality());
|
||||
}
|
||||
|
||||
buildQualityMenu();
|
||||
@ -255,17 +270,17 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
|
||||
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
|
||||
final int sortedStreamsIndex = item.getQualityIndex();
|
||||
if (videos.isEmpty() || sortedStreamsIndex >= videos.size()) return null;
|
||||
|
||||
final VideoStream video;
|
||||
if (sortedStreamsIndex == PlayQueueItem.DEFAULT_QUALITY) {
|
||||
if (playbackQuality == null) {
|
||||
final int index = getDefaultResolutionIndex(videos);
|
||||
video = videos.get(index);
|
||||
} else {
|
||||
video = videos.get(sortedStreamsIndex);
|
||||
final int index = getOverrideResolutionIndex(videos, getPlaybackQuality());
|
||||
video = videos.get(index);
|
||||
}
|
||||
|
||||
final MediaSource streamSource = buildMediaSource(video.url, MediaFormat.getSuffixById(video.format));
|
||||
@ -455,10 +470,15 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]");
|
||||
|
||||
if (qualityPopupMenuGroupId == menuItem.getGroupId()) {
|
||||
if (selectedStreamIndex == menuItem.getItemId()) return true;
|
||||
final int menuItemIndex = menuItem.getItemId();
|
||||
if (selectedStreamIndex == menuItemIndex ||
|
||||
availableStreams == null || availableStreams.size() <= menuItemIndex) return true;
|
||||
|
||||
final String oldResolution = getPlaybackQuality();
|
||||
final String newResolution = availableStreams.get(menuItemIndex).resolution;
|
||||
setRecovery();
|
||||
playQueue.setQuality(playQueue.getIndex(), menuItem.getItemId());
|
||||
setPlaybackQuality(newResolution);
|
||||
reload();
|
||||
|
||||
qualityTextView.setText(menuItem.getTitle());
|
||||
return true;
|
||||
@ -489,8 +509,9 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
isSomePopupMenuVisible = true;
|
||||
showControls(300);
|
||||
|
||||
VideoStream videoStream = getSelectedVideoStream();
|
||||
qualityTextView.setText(MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
|
||||
final VideoStream videoStream = getSelectedVideoStream();
|
||||
final String qualityText = MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution;
|
||||
qualityTextView.setText(qualityText);
|
||||
wasPlaying = simpleExoPlayer.getPlayWhenReady();
|
||||
}
|
||||
|
||||
@ -648,6 +669,14 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
// Getters and Setters
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void setPlaybackQuality(final String quality) {
|
||||
this.playbackQuality = quality;
|
||||
}
|
||||
|
||||
public String getPlaybackQuality() {
|
||||
return playbackQuality;
|
||||
}
|
||||
|
||||
public AspectRatioFrameLayout getAspectRatioFrameLayout() {
|
||||
return aspectRatioFrameLayout;
|
||||
}
|
||||
|
@ -158,8 +158,8 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
||||
// why no pattern matching in Java =(
|
||||
switch (event.type()) {
|
||||
case INIT:
|
||||
case QUALITY:
|
||||
case REORDER:
|
||||
case ERROR:
|
||||
reset();
|
||||
break;
|
||||
case APPEND:
|
||||
@ -177,7 +177,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
||||
final MoveEvent moveEvent = (MoveEvent) event;
|
||||
move(moveEvent.getFromIndex(), moveEvent.getToIndex());
|
||||
break;
|
||||
case ERROR:
|
||||
case RECOVERY:
|
||||
default:
|
||||
break;
|
||||
|
@ -14,7 +14,6 @@ import org.schabi.newpipe.playlist.events.RecoveryEvent;
|
||||
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
||||
import org.schabi.newpipe.playlist.events.ReorderEvent;
|
||||
import org.schabi.newpipe.playlist.events.SelectEvent;
|
||||
import org.schabi.newpipe.playlist.events.QualityEvent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
@ -247,15 +246,22 @@ public abstract class PlayQueue implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report an exception for the item at the current index in order to remove it.
|
||||
* Report an exception for the item at the current index in order and the course of action:
|
||||
* if the error can be skipped or the current item should be removed.
|
||||
*
|
||||
* This is done as a separate event as the underlying manager may have
|
||||
* different implementation regarding exceptions.
|
||||
* */
|
||||
public synchronized void error() {
|
||||
public synchronized void error(final boolean skippable) {
|
||||
final int index = getIndex();
|
||||
removeInternal(index);
|
||||
broadcast(new ErrorEvent(index));
|
||||
|
||||
if (skippable) {
|
||||
queueIndex.incrementAndGet();
|
||||
} else {
|
||||
removeInternal(index);
|
||||
}
|
||||
|
||||
broadcast(new ErrorEvent(index, skippable));
|
||||
}
|
||||
|
||||
private synchronized void removeInternal(final int index) {
|
||||
@ -301,21 +307,6 @@ public abstract class PlayQueue implements Serializable {
|
||||
broadcast(new MoveEvent(source, target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the quality index at the given item index.
|
||||
*
|
||||
* Broadcasts an update event, signalling to all recipients that they should reset.
|
||||
* */
|
||||
public synchronized void setQuality(final int queueIndex, final int qualityIndex) {
|
||||
if (queueIndex < 0 || queueIndex >= streams.size()) return;
|
||||
|
||||
final PlayQueueItem item = streams.get(queueIndex);
|
||||
final int oldQualityIndex = item.getQualityIndex();
|
||||
|
||||
item.setQualityIndex(qualityIndex);
|
||||
broadcast(new QualityEvent(queueIndex, oldQualityIndex, qualityIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recovery record of the item at the index.
|
||||
*
|
||||
|
@ -102,7 +102,6 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
private void onPlayQueueChanged(final PlayQueueEvent message) {
|
||||
switch (message.type()) {
|
||||
case RECOVERY:
|
||||
case QUALITY:
|
||||
// Do nothing.
|
||||
break;
|
||||
case SELECT:
|
||||
@ -116,12 +115,14 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
break;
|
||||
case ERROR:
|
||||
final ErrorEvent errorEvent = (ErrorEvent) message;
|
||||
notifyItemRangeRemoved(errorEvent.index(), 1);
|
||||
if (!errorEvent.isSkippable()) {
|
||||
notifyItemRemoved(errorEvent.index());
|
||||
}
|
||||
notifyItemChanged(errorEvent.index());
|
||||
break;
|
||||
case REMOVE:
|
||||
final RemoveEvent removeEvent = (RemoveEvent) message;
|
||||
notifyItemRangeRemoved(removeEvent.index(), 1);
|
||||
notifyItemRemoved(removeEvent.index());
|
||||
notifyItemChanged(removeEvent.index());
|
||||
break;
|
||||
case MOVE:
|
||||
|
@ -15,7 +15,6 @@ import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class PlayQueueItem implements Serializable {
|
||||
final public static int DEFAULT_QUALITY = Integer.MIN_VALUE;
|
||||
final public static long RECOVERY_UNSET = Long.MIN_VALUE;
|
||||
|
||||
final private String title;
|
||||
@ -25,7 +24,6 @@ public class PlayQueueItem implements Serializable {
|
||||
final private String thumbnailUrl;
|
||||
final private String uploader;
|
||||
|
||||
private int qualityIndex;
|
||||
private long recoveryPosition;
|
||||
private Throwable error;
|
||||
|
||||
@ -36,11 +34,6 @@ public class PlayQueueItem implements Serializable {
|
||||
this.stream = Single.just(info);
|
||||
}
|
||||
|
||||
PlayQueueItem(@NonNull final StreamInfo info, final int qualityIndex) {
|
||||
this(info);
|
||||
this.qualityIndex = qualityIndex;
|
||||
}
|
||||
|
||||
PlayQueueItem(@NonNull final StreamInfoItem item) {
|
||||
this(item.name, item.url, item.service_id, item.duration, item.thumbnail_url, item.uploader_name);
|
||||
}
|
||||
@ -54,7 +47,6 @@ public class PlayQueueItem implements Serializable {
|
||||
this.thumbnailUrl = thumbnailUrl;
|
||||
this.uploader = uploader;
|
||||
|
||||
this.qualityIndex = DEFAULT_QUALITY;
|
||||
this.recoveryPosition = RECOVERY_UNSET;
|
||||
}
|
||||
|
||||
@ -86,10 +78,6 @@ public class PlayQueueItem implements Serializable {
|
||||
return uploader;
|
||||
}
|
||||
|
||||
public int getQualityIndex() {
|
||||
return qualityIndex;
|
||||
}
|
||||
|
||||
public long getRecoveryPosition() {
|
||||
return recoveryPosition;
|
||||
}
|
||||
@ -123,10 +111,6 @@ public class PlayQueueItem implements Serializable {
|
||||
// Item States, keep external access out
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*package-private*/ void setQualityIndex(final int qualityIndex) {
|
||||
this.qualityIndex = qualityIndex;
|
||||
}
|
||||
|
||||
/*package-private*/ void setRecoveryPosition(final long recoveryPosition) {
|
||||
this.recoveryPosition = recoveryPosition;
|
||||
}
|
||||
|
@ -9,10 +9,6 @@ public final class SinglePlayQueue extends PlayQueue {
|
||||
super(0, Collections.singletonList(new PlayQueueItem(info)));
|
||||
}
|
||||
|
||||
public SinglePlayQueue(final StreamInfo info, final int qualityIndex) {
|
||||
super(0, Collections.singletonList(new PlayQueueItem(info, qualityIndex)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return true;
|
||||
|
@ -3,17 +3,23 @@ package org.schabi.newpipe.playlist.events;
|
||||
|
||||
public class ErrorEvent implements PlayQueueEvent {
|
||||
final private int index;
|
||||
final private boolean skippable;
|
||||
|
||||
@Override
|
||||
public PlayQueueEventType type() {
|
||||
return PlayQueueEventType.ERROR;
|
||||
}
|
||||
|
||||
public ErrorEvent(final int index) {
|
||||
public ErrorEvent(final int index, final boolean skippable) {
|
||||
this.index = index;
|
||||
this.skippable = skippable;
|
||||
}
|
||||
|
||||
public int index() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public boolean isSkippable() {
|
||||
return skippable;
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,6 @@ public enum PlayQueueEventType {
|
||||
// sent when queue is shuffled
|
||||
REORDER,
|
||||
|
||||
// sent when quality index is set on a stream
|
||||
QUALITY,
|
||||
|
||||
// sent when recovery record is set on a stream
|
||||
RECOVERY,
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
|
||||
|
||||
public class QualityEvent implements PlayQueueEvent {
|
||||
final private int streamIndex;
|
||||
final private int oldQualityIndex;
|
||||
final private int newQualityIndex;
|
||||
|
||||
@Override
|
||||
public PlayQueueEventType type() {
|
||||
return PlayQueueEventType.QUALITY;
|
||||
}
|
||||
|
||||
public QualityEvent(final int streamIndex, final int oldQualityIndex, final int newQualityIndex) {
|
||||
this.streamIndex = streamIndex;
|
||||
this.oldQualityIndex = oldQualityIndex;
|
||||
this.newQualityIndex = newQualityIndex;
|
||||
}
|
||||
|
||||
public int getStreamIndex() {
|
||||
return streamIndex;
|
||||
}
|
||||
|
||||
public int getOldQualityIndex() {
|
||||
return oldQualityIndex;
|
||||
}
|
||||
|
||||
public int getNewQualityIndex() {
|
||||
return newQualityIndex;
|
||||
}
|
||||
}
|
@ -56,6 +56,13 @@ public final class ListHelper {
|
||||
if (defaultPreferences == null) return 0;
|
||||
|
||||
String defaultResolution = defaultPreferences.getString(context.getString(R.string.default_resolution_key), context.getString(R.string.default_resolution_value));
|
||||
return getDefaultResolutionIndex(context, videoStreams, defaultResolution);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
|
||||
*/
|
||||
public static int getDefaultResolutionIndex(Context context, List<VideoStream> videoStreams, String defaultResolution) {
|
||||
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
|
||||
}
|
||||
|
||||
@ -67,6 +74,13 @@ public final class ListHelper {
|
||||
if (defaultPreferences == null) return 0;
|
||||
|
||||
String defaultResolution = defaultPreferences.getString(context.getString(R.string.default_popup_resolution_key), context.getString(R.string.default_popup_resolution_value));
|
||||
return getPopupDefaultResolutionIndex(context, videoStreams, defaultResolution);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
|
||||
*/
|
||||
public static int getPopupDefaultResolutionIndex(Context context, List<VideoStream> videoStreams, String defaultResolution) {
|
||||
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
|
||||
}
|
||||
|
||||
|
@ -39,21 +39,19 @@ public class NavigationHelper {
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
public static Intent getPlayerIntent(final Context context,
|
||||
final Class targetClazz,
|
||||
final PlayQueue playQueue) {
|
||||
return new Intent(context, targetClazz)
|
||||
final PlayQueue playQueue,
|
||||
final String quality) {
|
||||
Intent intent = new Intent(context, targetClazz)
|
||||
.putExtra(VideoPlayer.PLAY_QUEUE, playQueue);
|
||||
if (quality != null) intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality);
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent getPlayerIntent(final Context context,
|
||||
final Class targetClazz,
|
||||
final PlayQueue playQueue,
|
||||
final int repeatMode,
|
||||
final float playbackSpeed,
|
||||
final float playbackPitch) {
|
||||
return getPlayerIntent(context, targetClazz, playQueue)
|
||||
.putExtra(BasePlayer.REPEAT_MODE, repeatMode)
|
||||
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed)
|
||||
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch);
|
||||
final PlayQueue playQueue) {
|
||||
return getPlayerIntent(context, targetClazz, playQueue, null);
|
||||
}
|
||||
|
||||
public static Intent getPlayerEnqueueIntent(final Context context,
|
||||
@ -63,6 +61,19 @@ public class NavigationHelper {
|
||||
.putExtra(BasePlayer.APPEND_ONLY, true);
|
||||
}
|
||||
|
||||
public static Intent getPlayerIntent(final Context context,
|
||||
final Class targetClazz,
|
||||
final PlayQueue playQueue,
|
||||
final int repeatMode,
|
||||
final float playbackSpeed,
|
||||
final float playbackPitch,
|
||||
final String playbackQuality) {
|
||||
return getPlayerIntent(context, targetClazz, playQueue, playbackQuality)
|
||||
.putExtra(BasePlayer.REPEAT_MODE, repeatMode)
|
||||
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed)
|
||||
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Through FragmentManager
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -57,13 +57,14 @@
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playQueueClose"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginRight="40dp"
|
||||
android:layout_marginEnd="40dp"
|
||||
android:padding="10dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:scaleType="fitXY"
|
||||
@ -73,13 +74,14 @@
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/repeatButton"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginLeft="40dp"
|
||||
android:layout_marginStart="40dp"
|
||||
android:padding="10dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:scaleType="fitXY"
|
||||
@ -89,12 +91,11 @@
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/shuffleButton"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@id/repeatButton"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginStart="15dp"
|
||||
android:padding="10dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:scaleType="fitXY"
|
||||
@ -301,7 +302,9 @@
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:background="#00000000"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_pause_white"
|
||||
tools:ignore="ContentDescription"/>
|
||||
@ -315,7 +318,9 @@
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_toLeftOf="@id/playPauseButton"
|
||||
android:layout_toStartOf="@id/playPauseButton"
|
||||
android:background="#00000000"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/exo_controls_previous"
|
||||
tools:ignore="ContentDescription"/>
|
||||
@ -329,7 +334,9 @@
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_toRightOf="@id/playPauseButton"
|
||||
android:layout_toEndOf="@id/playPauseButton"
|
||||
android:background="#00000000"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/exo_controls_next"
|
||||
tools:ignore="ContentDescription"/>
|
||||
|
@ -136,6 +136,10 @@
|
||||
<string name="could_not_get_stream">Could not get any stream</string>
|
||||
<string name="could_not_load_image">Could not load image</string>
|
||||
<string name="app_ui_crash">App/UI crashed</string>
|
||||
<string name="player_video_failure">Failed to play this video</string>
|
||||
<string name="player_audio_failure">Failed to play this audio</string>
|
||||
<string name="player_unexpected_failure">Unexpected player error occurred</string>
|
||||
|
||||
<!-- error activity -->
|
||||
<string name="sorry_string">Sorry, that should not have happened.</string>
|
||||
<string name="guru_meditation" translatable="false">Guru Meditation.</string>
|
||||
|
Loading…
Reference in New Issue
Block a user