Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
2d25a9ad7a
19 changed files with 641 additions and 208 deletions
|
@ -46,6 +46,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
||||||
* Search channels
|
* Search channels
|
||||||
* Watch videos from a channel
|
* Watch videos from a channel
|
||||||
* Orbot/Tor support (not yet directly)
|
* Orbot/Tor support (not yet directly)
|
||||||
|
* 1080p/2k/4k support
|
||||||
|
|
||||||
### Coming Features
|
### Coming Features
|
||||||
|
|
||||||
|
@ -56,7 +57,6 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
||||||
* Search/Watch Playlists
|
* Search/Watch Playlists
|
||||||
* Queeing videos
|
* Queeing videos
|
||||||
* Subtitles support
|
* Subtitles support
|
||||||
* 1080p support
|
|
||||||
* livestream support
|
* livestream support
|
||||||
* ... and many more
|
* ... and many more
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6ab3dc876ebab4ed32f4ae60d3d04d000a7ea0e8
|
Subproject commit 08457de7631253a23ba57cd547fe056c00eebc21
|
|
@ -413,4 +413,9 @@ public class ChannelFragment extends Fragment implements ChannelExtractorWorker.
|
||||||
public void onError(int messageId) {
|
public void onError(int messageId) {
|
||||||
Toast.makeText(activity, messageId, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, messageId, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnrecoverableError(Exception exception) {
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ class ActionBarHandler {
|
||||||
VideoStream item = videoStreams.get(i);
|
VideoStream item = videoStreams.get(i);
|
||||||
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
||||||
}
|
}
|
||||||
int defaultResolution = Utils.getPreferredResolution(activity, videoStreams);
|
int defaultResolution = Utils.getDefaultResolution(activity, videoStreams);
|
||||||
|
|
||||||
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
||||||
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
||||||
|
|
|
@ -88,6 +88,7 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
|
|
||||||
private AppCompatActivity activity;
|
private AppCompatActivity activity;
|
||||||
private OnItemSelectedListener onItemSelectedListener;
|
private OnItemSelectedListener onItemSelectedListener;
|
||||||
|
private ArrayList<VideoStream> sortedStreamVideosList;
|
||||||
private ActionBarHandler actionBarHandler;
|
private ActionBarHandler actionBarHandler;
|
||||||
|
|
||||||
private InfoItemBuilder infoItemBuilder = null;
|
private InfoItemBuilder infoItemBuilder = null;
|
||||||
|
@ -98,8 +99,11 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
private String videoUrl;
|
private String videoUrl;
|
||||||
private int serviceId = -1;
|
private int serviceId = -1;
|
||||||
|
|
||||||
|
private AtomicBoolean wasLoading = new AtomicBoolean(false);
|
||||||
private AtomicBoolean isLoading = new AtomicBoolean(false);
|
private AtomicBoolean isLoading = new AtomicBoolean(false);
|
||||||
private boolean needUpdate = false;
|
private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1;
|
||||||
|
private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2;
|
||||||
|
private int updateFlags = 0;
|
||||||
|
|
||||||
private boolean autoPlayEnabled;
|
private boolean autoPlayEnabled;
|
||||||
private boolean showRelatedStreams;
|
private boolean showRelatedStreams;
|
||||||
|
@ -203,7 +207,8 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
public void onViewCreated(View rootView, Bundle savedInstanceState) {
|
public void onViewCreated(View rootView, Bundle savedInstanceState) {
|
||||||
initViews(rootView);
|
initViews(rootView);
|
||||||
initListeners();
|
initListeners();
|
||||||
isLoading.set(true);
|
selectAndLoadVideo(serviceId, videoUrl, videoTitle);
|
||||||
|
wasLoading.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -251,19 +256,23 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
|
|
||||||
// Currently only used for enable/disable related videos
|
// Currently only used for enable/disable related videos
|
||||||
// but can be extended for other live settings changes
|
// but can be extended for other live settings changes
|
||||||
if (needUpdate) {
|
if (updateFlags != 0) {
|
||||||
if (relatedStreamsView != null) initRelatedVideos(currentStreamInfo);
|
if (!isLoading.get()) {
|
||||||
needUpdate = false;
|
if ((updateFlags & RELATED_STREAMS_UPDATE_FLAG) != 0) initRelatedVideos(currentStreamInfo);
|
||||||
|
if ((updateFlags & RESOLUTIONS_MENU_UPDATE_FLAG) != 0) setupActionBarHandler(currentStreamInfo);
|
||||||
|
}
|
||||||
|
updateFlags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it was loading when the activity was stopped/paused,
|
// Check if it was loading when the activity was stopped/paused,
|
||||||
// because when this happen, the curExtractorWorker is cancelled
|
// because when this happen, the curExtractorWorker is cancelled
|
||||||
if (isLoading.get()) selectAndLoadVideo(serviceId, videoUrl, videoTitle);
|
if (wasLoading.getAndSet(false)) selectAndLoadVideo(serviceId, videoUrl, videoTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
wasLoading.set(curExtractorWorker.isRunning());
|
||||||
if (curExtractorWorker != null && curExtractorWorker.isRunning()) curExtractorWorker.cancel();
|
if (curExtractorWorker != null && curExtractorWorker.isRunning()) curExtractorWorker.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,7 +310,11 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
if (key.equals(getString(R.string.show_next_video_key))) {
|
if (key.equals(getString(R.string.show_next_video_key))) {
|
||||||
showRelatedStreams = sharedPreferences.getBoolean(key, true);
|
showRelatedStreams = sharedPreferences.getBoolean(key, true);
|
||||||
needUpdate = true;
|
updateFlags |= RELATED_STREAMS_UPDATE_FLAG;
|
||||||
|
} else if (key.equals(getString(R.string.preferred_video_format_key))
|
||||||
|
|| key.equals(getString(R.string.default_resolution_key))
|
||||||
|
|| key.equals(getString(R.string.show_higher_resolutions_key))) {
|
||||||
|
updateFlags |= RESOLUTIONS_MENU_UPDATE_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,7 +493,8 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
actionBarHandler.setupStreamList(info.video_streams);
|
sortedStreamVideosList = Utils.getSortedStreamVideosList(activity, info.video_streams, info.video_only_streams, false);
|
||||||
|
actionBarHandler.setupStreamList(sortedStreamVideosList);
|
||||||
actionBarHandler.setOnShareListener(new ActionBarHandler.OnActionListener() {
|
actionBarHandler.setOnShareListener(new ActionBarHandler.OnActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onActionSelected(int selectedStreamId) {
|
public void onActionSelected(int selectedStreamId) {
|
||||||
|
@ -517,15 +531,10 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
}
|
}
|
||||||
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
||||||
|
|
||||||
Intent i = new Intent(activity, PopupVideoPlayer.class);
|
|
||||||
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||||
i.putExtra(AbstractPlayer.VIDEO_TITLE, info.title)
|
Intent mIntent = NavigationHelper.getOpenPlayerIntent(activity, PopupVideoPlayer.class, info, selectedStreamId);
|
||||||
.putExtra(AbstractPlayer.CHANNEL_NAME, info.uploader)
|
if (info.start_position > 0) mIntent.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
|
||||||
.putExtra(AbstractPlayer.VIDEO_URL, info.webpage_url)
|
activity.startService(mIntent);
|
||||||
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, selectedStreamId)
|
|
||||||
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, new ArrayList<>(info.video_streams));
|
|
||||||
if (info.start_position > 0) i.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
|
|
||||||
activity.startService(i);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -586,8 +595,8 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
args.putString(DownloadDialog.FILE_SUFFIX_AUDIO, audioSuffix);
|
args.putString(DownloadDialog.FILE_SUFFIX_AUDIO, audioSuffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.video_streams != null) {
|
if (sortedStreamVideosList != null) {
|
||||||
VideoStream selectedStreamItem = info.video_streams.get(selectedStreamId);
|
VideoStream selectedStreamItem = sortedStreamVideosList.get(selectedStreamId);
|
||||||
String videoSuffix = "." + MediaFormat.getSuffixById(selectedStreamItem.format);
|
String videoSuffix = "." + MediaFormat.getSuffixById(selectedStreamItem.format);
|
||||||
args.putString(DownloadDialog.FILE_SUFFIX_VIDEO, videoSuffix);
|
args.putString(DownloadDialog.FILE_SUFFIX_VIDEO, videoSuffix);
|
||||||
args.putString(DownloadDialog.VIDEO_URL, selectedStreamItem.url);
|
args.putString(DownloadDialog.VIDEO_URL, selectedStreamItem.url);
|
||||||
|
@ -737,6 +746,7 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadSelectedVideo() {
|
public void loadSelectedVideo() {
|
||||||
|
isLoading.set(true);
|
||||||
pushToStack(videoUrl, videoTitle);
|
pushToStack(videoUrl, videoTitle);
|
||||||
|
|
||||||
if (curExtractorWorker != null && curExtractorWorker.isRunning()) curExtractorWorker.cancel();
|
if (curExtractorWorker != null && curExtractorWorker.isRunning()) curExtractorWorker.cancel();
|
||||||
|
@ -753,6 +763,7 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
if (scrollY < 30) animateView(videoTitleTextView, false, 200, new Runnable() {
|
if (scrollY < 30) animateView(videoTitleTextView, false, 200, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
if (videoTitleTextView == null) return;
|
||||||
videoTitleTextView.setText(videoTitle != null ? videoTitle : "");
|
videoTitleTextView.setText(videoTitle != null ? videoTitle : "");
|
||||||
animateView(videoTitleTextView, true, 400, null);
|
animateView(videoTitleTextView, true, 400, null);
|
||||||
}
|
}
|
||||||
|
@ -775,12 +786,11 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
|
|
||||||
curExtractorWorker = new StreamExtractorWorker(activity, serviceId, videoUrl, this);
|
curExtractorWorker = new StreamExtractorWorker(activity, serviceId, videoUrl, this);
|
||||||
curExtractorWorker.start();
|
curExtractorWorker.start();
|
||||||
isLoading.set(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playVideo(StreamInfo info) {
|
public void playVideo(StreamInfo info) {
|
||||||
// ----------- THE MAGIC MOMENT ---------------
|
// ----------- THE MAGIC MOMENT ---------------
|
||||||
VideoStream selectedVideoStream = info.video_streams.get(actionBarHandler.getSelectedVideoStream());
|
VideoStream selectedVideoStream = sortedStreamVideosList.get(actionBarHandler.getSelectedVideoStream());
|
||||||
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
|
if (PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
|
||||||
|
|
||||||
|
@ -813,30 +823,24 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Intent intent;
|
Intent mIntent;
|
||||||
boolean useOldPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
|
boolean useOldPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
.getBoolean(getString(R.string.use_old_player_key), false)
|
.getBoolean(getString(R.string.use_old_player_key), false)
|
||||||
|| (Build.VERSION.SDK_INT < 16);
|
|| (Build.VERSION.SDK_INT < 16);
|
||||||
if (!useOldPlayer) {
|
if (!useOldPlayer) {
|
||||||
// ExoPlayer
|
// ExoPlayer
|
||||||
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
||||||
intent = new Intent(activity, ExoPlayerActivity.class)
|
mIntent = NavigationHelper.getOpenPlayerIntent(activity, ExoPlayerActivity.class, info, actionBarHandler.getSelectedVideoStream());
|
||||||
.putExtra(AbstractPlayer.VIDEO_TITLE, info.title)
|
if (info.start_position > 0) mIntent.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
|
||||||
.putExtra(AbstractPlayer.VIDEO_URL, info.webpage_url)
|
|
||||||
.putExtra(AbstractPlayer.CHANNEL_NAME, info.uploader)
|
|
||||||
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, actionBarHandler.getSelectedVideoStream())
|
|
||||||
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, new ArrayList<>(info.video_streams));
|
|
||||||
if (info.start_position > 0) intent.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
|
|
||||||
} else {
|
} else {
|
||||||
// Internal Player
|
// Internal Player
|
||||||
intent = new Intent(activity, PlayVideoActivity.class)
|
mIntent = new Intent(activity, PlayVideoActivity.class)
|
||||||
.putExtra(PlayVideoActivity.VIDEO_TITLE, info.title)
|
.putExtra(PlayVideoActivity.VIDEO_TITLE, info.title)
|
||||||
.putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.url)
|
.putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.url)
|
||||||
.putExtra(PlayVideoActivity.VIDEO_URL, info.webpage_url)
|
.putExtra(PlayVideoActivity.VIDEO_URL, info.webpage_url)
|
||||||
.putExtra(PlayVideoActivity.START_POSITION, info.start_position);
|
.putExtra(PlayVideoActivity.START_POSITION, info.start_position);
|
||||||
}
|
}
|
||||||
//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
startActivity(mIntent);
|
||||||
startActivity(intent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -928,6 +932,7 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
|
|
||||||
// Since newpipe is designed to work even if certain information is not available,
|
// Since newpipe is designed to work even if certain information is not available,
|
||||||
// the UI has to react on missing information.
|
// the UI has to react on missing information.
|
||||||
|
videoTitle = info.title;
|
||||||
videoTitleTextView.setText(info.title);
|
videoTitleTextView.setText(info.title);
|
||||||
if (!info.uploader.isEmpty()) uploaderTextView.setText(info.uploader);
|
if (!info.uploader.isEmpty()) uploaderTextView.setText(info.uploader);
|
||||||
uploaderTextView.setVisibility(!info.uploader.isEmpty() ? View.VISIBLE : View.GONE);
|
uploaderTextView.setVisibility(!info.uploader.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
@ -1031,4 +1036,9 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.not_available_monkey));
|
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.not_available_monkey));
|
||||||
Toast.makeText(activity, R.string.content_not_available, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, R.string.content_not_available, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnrecoverableError(Exception exception) {
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
||||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.MergingMediaSource;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
||||||
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
|
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
|
||||||
|
@ -60,6 +61,7 @@ import com.google.android.exoplayer2.util.Util;
|
||||||
import org.schabi.newpipe.ActivityCommunicator;
|
import org.schabi.newpipe.ActivityCommunicator;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -93,6 +95,7 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
|
|
||||||
public static final String VIDEO_URL = "video_url";
|
public static final String VIDEO_URL = "video_url";
|
||||||
public static final String VIDEO_STREAMS_LIST = "video_streams_list";
|
public static final String VIDEO_STREAMS_LIST = "video_streams_list";
|
||||||
|
public static final String VIDEO_ONLY_AUDIO_STREAM = "video_only_audio_stream";
|
||||||
public static final String VIDEO_TITLE = "video_title";
|
public static final String VIDEO_TITLE = "video_title";
|
||||||
public static final String INDEX_SEL_VIDEO_STREAM = "index_selected_video_stream";
|
public static final String INDEX_SEL_VIDEO_STREAM = "index_selected_video_stream";
|
||||||
public static final String START_POSITION = "start_position";
|
public static final String START_POSITION = "start_position";
|
||||||
|
@ -105,7 +108,8 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
private Bitmap videoThumbnail;
|
private Bitmap videoThumbnail;
|
||||||
private String channelName = "";
|
private String channelName = "";
|
||||||
private int selectedIndexStream;
|
private int selectedIndexStream;
|
||||||
private ArrayList<VideoStream> videoStreamsList;
|
private ArrayList<VideoStream> videoStreamsList = new ArrayList<>();
|
||||||
|
private AudioStream videoOnlyAudioStream;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Player
|
// Player
|
||||||
|
@ -277,6 +281,9 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
if (serializable instanceof ArrayList) videoStreamsList = (ArrayList<VideoStream>) serializable;
|
if (serializable instanceof ArrayList) videoStreamsList = (ArrayList<VideoStream>) serializable;
|
||||||
if (serializable instanceof Vector) videoStreamsList = new ArrayList<>((List<VideoStream>) serializable);
|
if (serializable instanceof Vector) videoStreamsList = new ArrayList<>((List<VideoStream>) serializable);
|
||||||
|
|
||||||
|
Serializable audioStream = intent.getSerializableExtra(VIDEO_ONLY_AUDIO_STREAM);
|
||||||
|
if (audioStream != null) videoOnlyAudioStream = (AudioStream) audioStream;
|
||||||
|
|
||||||
videoUrl = intent.getStringExtra(VIDEO_URL);
|
videoUrl = intent.getStringExtra(VIDEO_URL);
|
||||||
videoTitle = intent.getStringExtra(VIDEO_TITLE);
|
videoTitle = intent.getStringExtra(VIDEO_TITLE);
|
||||||
videoStartPos = intent.getIntExtra(START_POSITION, -1);
|
videoStartPos = intent.getIntExtra(START_POSITION, -1);
|
||||||
|
@ -288,13 +295,15 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
playVideo(getSelectedStreamUri(), true);
|
playVideo(getSelectedVideoStream(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playVideo(Uri videoURI, boolean autoPlay) {
|
public void playVideo(VideoStream videoStream, boolean autoPlay) {
|
||||||
if (DEBUG) Log.d(TAG, "playVideo() called with: videoURI = [" + videoURI + "], autoPlay = [" + autoPlay + "]");
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "playVideo() called with: videoStream = [" + videoStream + ", " + videoStream.url + ", isVideoOnly = " + videoStream.isVideoOnly + "], autoPlay = [" + autoPlay + "]");
|
||||||
|
}
|
||||||
|
|
||||||
if (videoURI == null || simpleExoPlayer == null) {
|
if (videoStream == null || videoStream.url == null || simpleExoPlayer == null) {
|
||||||
onError();
|
onError();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -305,7 +314,7 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId);
|
qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId);
|
||||||
buildQualityMenu(qualityPopupMenu);
|
buildQualityMenu(qualityPopupMenu);
|
||||||
|
|
||||||
videoSource = buildMediaSource(videoURI, MediaFormat.getSuffixById(videoStreamsList.get(selectedIndexStream).format));
|
videoSource = buildMediaSource(videoStream, MediaFormat.getSuffixById(getSelectedVideoStream().format));
|
||||||
|
|
||||||
if (simpleExoPlayer.getPlaybackState() != ExoPlayer.STATE_IDLE) simpleExoPlayer.stop();
|
if (simpleExoPlayer.getPlaybackState() != ExoPlayer.STATE_IDLE) simpleExoPlayer.stop();
|
||||||
if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos);
|
if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos);
|
||||||
|
@ -323,22 +332,34 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
if (progressLoop != null) stopProgressLoop();
|
if (progressLoop != null) stopProgressLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
|
private MediaSource buildMediaSource(VideoStream videoStream, String overrideExtension) {
|
||||||
if (DEBUG) Log.d(TAG, "buildMediaSource() called with: uri = [" + uri + "], overrideExtension = [" + overrideExtension + "]");
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "buildMediaSource() called with: videoStream = [" + videoStream + ", " + videoStream.url + "isVideoOnly = " + videoStream.isVideoOnly + "], overrideExtension = [" + overrideExtension + "]");
|
||||||
|
}
|
||||||
|
Uri uri = Uri.parse(videoStream.url);
|
||||||
int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension);
|
int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension);
|
||||||
|
MediaSource mediaSource;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case C.TYPE_SS:
|
case C.TYPE_SS:
|
||||||
return new SsMediaSource(uri, cacheDataSourceFactory, new DefaultSsChunkSource.Factory(cacheDataSourceFactory), null, null);
|
mediaSource = new SsMediaSource(uri, cacheDataSourceFactory, new DefaultSsChunkSource.Factory(cacheDataSourceFactory), null, null);
|
||||||
|
break;
|
||||||
case C.TYPE_DASH:
|
case C.TYPE_DASH:
|
||||||
return new DashMediaSource(uri, cacheDataSourceFactory, new DefaultDashChunkSource.Factory(cacheDataSourceFactory), null, null);
|
mediaSource = new DashMediaSource(uri, cacheDataSourceFactory, new DefaultDashChunkSource.Factory(cacheDataSourceFactory), null, null);
|
||||||
|
break;
|
||||||
case C.TYPE_HLS:
|
case C.TYPE_HLS:
|
||||||
return new HlsMediaSource(uri, cacheDataSourceFactory, null, null);
|
mediaSource = new HlsMediaSource(uri, cacheDataSourceFactory, null, null);
|
||||||
|
break;
|
||||||
case C.TYPE_OTHER:
|
case C.TYPE_OTHER:
|
||||||
return new ExtractorMediaSource(uri, cacheDataSourceFactory, extractorsFactory, null, null);
|
mediaSource = new ExtractorMediaSource(uri, cacheDataSourceFactory, extractorsFactory, null, null);
|
||||||
|
break;
|
||||||
default: {
|
default: {
|
||||||
throw new IllegalStateException("Unsupported type: " + type);
|
throw new IllegalStateException("Unsupported type: " + type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!videoStream.isVideoOnly) return mediaSource;
|
||||||
|
|
||||||
|
Uri audioUri = Uri.parse(videoOnlyAudioStream.url);
|
||||||
|
return new MergingMediaSource(mediaSource, new ExtractorMediaSource(audioUri, cacheDataSourceFactory, extractorsFactory, null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void buildQualityMenu(PopupMenu popupMenu) {
|
public void buildQualityMenu(PopupMenu popupMenu) {
|
||||||
|
@ -346,7 +367,7 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
VideoStream videoStream = videoStreamsList.get(i);
|
VideoStream videoStream = videoStreamsList.get(i);
|
||||||
popupMenu.getMenu().add(qualityPopupMenuGroupId, i, Menu.NONE, MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
|
popupMenu.getMenu().add(qualityPopupMenuGroupId, i, Menu.NONE, MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
|
||||||
}
|
}
|
||||||
qualityTextView.setText(videoStreamsList.get(selectedIndexStream).resolution);
|
qualityTextView.setText(getSelectedVideoStream().resolution);
|
||||||
popupMenu.setOnMenuItemClickListener(this);
|
popupMenu.setOnMenuItemClickListener(this);
|
||||||
popupMenu.setOnDismissListener(this);
|
popupMenu.setOnDismissListener(this);
|
||||||
|
|
||||||
|
@ -590,7 +611,7 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
if (DEBUG) Log.d(TAG, "onVideoPlayPause() called");
|
if (DEBUG) Log.d(TAG, "onVideoPlayPause() called");
|
||||||
if (currentState == STATE_COMPLETED) {
|
if (currentState == STATE_COMPLETED) {
|
||||||
changeState(STATE_LOADING);
|
changeState(STATE_LOADING);
|
||||||
if (qualityChanged) playVideo(getSelectedStreamUri(), true);
|
if (qualityChanged) playVideo(getSelectedVideoStream(), true);
|
||||||
simpleExoPlayer.seekTo(0);
|
simpleExoPlayer.seekTo(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -632,10 +653,10 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
if (selectedIndexStream == menuItem.getItemId()) return true;
|
if (selectedIndexStream == menuItem.getItemId()) return true;
|
||||||
setVideoStartPos((int) getPlayer().getCurrentPosition());
|
setVideoStartPos((int) getPlayer().getCurrentPosition());
|
||||||
|
|
||||||
if (!(getCurrentState() == STATE_COMPLETED)) playVideo(Uri.parse(getVideoStreamsList().get(menuItem.getItemId()).url), wasPlaying);
|
selectedIndexStream = menuItem.getItemId();
|
||||||
|
if (!(getCurrentState() == STATE_COMPLETED)) playVideo(getSelectedVideoStream(), wasPlaying);
|
||||||
else qualityChanged = true;
|
else qualityChanged = true;
|
||||||
|
|
||||||
selectedIndexStream = menuItem.getItemId();
|
|
||||||
qualityTextView.setText(menuItem.getTitle());
|
qualityTextView.setText(menuItem.getTitle());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -647,7 +668,7 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
public void onDismiss(PopupMenu menu) {
|
public void onDismiss(PopupMenu menu) {
|
||||||
if (DEBUG) Log.d(TAG, "onDismiss() called with: menu = [" + menu + "]");
|
if (DEBUG) Log.d(TAG, "onDismiss() called with: menu = [" + menu + "]");
|
||||||
isQualityPopupMenuVisible = false;
|
isQualityPopupMenuVisible = false;
|
||||||
qualityTextView.setText(videoStreamsList.get(selectedIndexStream).resolution);
|
qualityTextView.setText(getSelectedVideoStream().resolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void onFullScreenButtonClicked();
|
public abstract void onFullScreenButtonClicked();
|
||||||
|
@ -658,7 +679,7 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
isQualityPopupMenuVisible = true;
|
isQualityPopupMenuVisible = true;
|
||||||
animateView(getControlsRoot(), true, 300, 0);
|
animateView(getControlsRoot(), true, 300, 0);
|
||||||
|
|
||||||
VideoStream videoStream = videoStreamsList.get(selectedIndexStream);
|
VideoStream videoStream = getSelectedVideoStream();
|
||||||
qualityTextView.setText(MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
|
qualityTextView.setText(MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
|
||||||
wasPlaying = isPlaying();
|
wasPlaying = isPlaying();
|
||||||
}
|
}
|
||||||
|
@ -967,8 +988,12 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
return currentState;
|
return currentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VideoStream getSelectedVideoStream() {
|
||||||
|
return videoStreamsList.get(selectedIndexStream);
|
||||||
|
}
|
||||||
|
|
||||||
public Uri getSelectedStreamUri() {
|
public Uri getSelectedStreamUri() {
|
||||||
return Uri.parse(videoStreamsList.get(selectedIndexStream).url);
|
return Uri.parse(getSelectedVideoStream().url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getQualityPopupMenuGroupId() {
|
public int getQualityPopupMenuGroupId() {
|
||||||
|
@ -1015,7 +1040,7 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
this.channelName = channelName;
|
this.channelName = channelName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSelectedIndexStream() {
|
public int getSelectedStreamIndex() {
|
||||||
return selectedIndexStream;
|
return selectedIndexStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1023,6 +1048,14 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
this.selectedIndexStream = selectedIndexStream;
|
this.selectedIndexStream = selectedIndexStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAudioStream(AudioStream audioStream) {
|
||||||
|
this.videoOnlyAudioStream = audioStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AudioStream getAudioStream() {
|
||||||
|
return videoOnlyAudioStream;
|
||||||
|
}
|
||||||
|
|
||||||
public ArrayList<VideoStream> getVideoStreamsList() {
|
public ArrayList<VideoStream> getVideoStreamsList() {
|
||||||
return videoStreamsList;
|
return videoStreamsList;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import android.content.IntentFilter;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
@ -24,6 +23,8 @@ import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
@ -105,10 +106,9 @@ public class ExoPlayerActivity extends Activity {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (DEBUG) Log.d(TAG, "onResume() called");
|
if (DEBUG) Log.d(TAG, "onResume() called");
|
||||||
if (activityPaused) {
|
if (activityPaused) {
|
||||||
//playerImpl.getPlayer().setPlayWhenReady(true);
|
|
||||||
playerImpl.getPlayPauseButton().setImageResource(R.drawable.ic_play_arrow_white);
|
playerImpl.getPlayPauseButton().setImageResource(R.drawable.ic_play_arrow_white);
|
||||||
playerImpl.initPlayer();
|
playerImpl.initPlayer();
|
||||||
playerImpl.playVideo(playerImpl.getSelectedStreamUri(), false);
|
playerImpl.playVideo(playerImpl.getSelectedVideoStream(), false);
|
||||||
activityPaused = false;
|
activityPaused = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,8 +238,8 @@ public class ExoPlayerActivity extends Activity {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playVideo(Uri videoURI, boolean autoPlay) {
|
public void playVideo(VideoStream videoStream, boolean autoPlay) {
|
||||||
super.playVideo(videoURI, autoPlay);
|
super.playVideo(videoStream, autoPlay);
|
||||||
playPauseButton.setImageResource(autoPlay ? R.drawable.ic_pause_white : R.drawable.ic_play_arrow_white);
|
playPauseButton.setImageResource(autoPlay ? R.drawable.ic_pause_white : R.drawable.ic_play_arrow_white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,16 +254,10 @@ public class ExoPlayerActivity extends Activity {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent i = new Intent(ExoPlayerActivity.this, PopupVideoPlayer.class);
|
if (playerImpl != null) playerImpl.destroy();
|
||||||
i.putExtra(AbstractPlayer.VIDEO_TITLE, getVideoTitle())
|
context.startService(NavigationHelper.getOpenPlayerIntent(context, PopupVideoPlayer.class, playerImpl));
|
||||||
.putExtra(AbstractPlayer.CHANNEL_NAME, getChannelName())
|
|
||||||
.putExtra(AbstractPlayer.VIDEO_URL, getVideoUrl())
|
|
||||||
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, getSelectedIndexStream())
|
|
||||||
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, getVideoStreamsList())
|
|
||||||
.putExtra(AbstractPlayer.START_POSITION, ((int) getPlayer().getCurrentPosition()));
|
|
||||||
context.startService(i);
|
|
||||||
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
|
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
|
||||||
if (playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(false);
|
|
||||||
ExoPlayerActivity.this.finish();
|
ExoPlayerActivity.this.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +340,7 @@ public class ExoPlayerActivity extends Activity {
|
||||||
@Override
|
@Override
|
||||||
public void onDismiss(PopupMenu menu) {
|
public void onDismiss(PopupMenu menu) {
|
||||||
super.onDismiss(menu);
|
super.onDismiss(menu);
|
||||||
if (isPlaying()) animateView(getControlsRoot(), false, 500, 0, true);
|
if (isPlaying()) animateView(getControlsRoot(), false, 500, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -12,7 +12,6 @@ import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
@ -36,18 +35,16 @@ import org.schabi.newpipe.ActivityCommunicator;
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
import org.schabi.newpipe.util.Utils;
|
import org.schabi.newpipe.util.Utils;
|
||||||
|
import org.schabi.newpipe.workers.StreamExtractorWorker;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service Popup Player implementing AbstractPlayer
|
* Service Popup Player implementing AbstractPlayer
|
||||||
|
@ -85,6 +82,7 @@ public class PopupVideoPlayer extends Service {
|
||||||
private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build();
|
private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build();
|
||||||
|
|
||||||
private AbstractPlayerImpl playerImpl;
|
private AbstractPlayerImpl playerImpl;
|
||||||
|
private StreamExtractorWorker currentExtractorWorker;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Service LifeCycle
|
// Service LifeCycle
|
||||||
|
@ -110,8 +108,8 @@ public class PopupVideoPlayer extends Service {
|
||||||
if (imageLoader != null) imageLoader.clearMemoryCache();
|
if (imageLoader != null) imageLoader.clearMemoryCache();
|
||||||
if (intent.getStringExtra(Constants.KEY_URL) != null) {
|
if (intent.getStringExtra(Constants.KEY_URL) != null) {
|
||||||
playerImpl.setStartedFromNewPipe(false);
|
playerImpl.setStartedFromNewPipe(false);
|
||||||
Thread fetcher = new Thread(new FetcherRunnable(intent));
|
currentExtractorWorker = new StreamExtractorWorker(this, 0, intent.getStringExtra(Constants.KEY_URL), new FetcherRunnable(this));
|
||||||
fetcher.start();
|
currentExtractorWorker.start();
|
||||||
} else {
|
} else {
|
||||||
playerImpl.setStartedFromNewPipe(true);
|
playerImpl.setStartedFromNewPipe(true);
|
||||||
playerImpl.handleIntent(intent);
|
playerImpl.handleIntent(intent);
|
||||||
|
@ -135,6 +133,10 @@ public class PopupVideoPlayer extends Service {
|
||||||
if (imageLoader != null) imageLoader.clearMemoryCache();
|
if (imageLoader != null) imageLoader.clearMemoryCache();
|
||||||
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
|
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
|
||||||
if (broadcastReceiver != null) unregisterReceiver(broadcastReceiver);
|
if (broadcastReceiver != null) unregisterReceiver(broadcastReceiver);
|
||||||
|
if (currentExtractorWorker != null) {
|
||||||
|
currentExtractorWorker.cancel();
|
||||||
|
currentExtractorWorker = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -306,8 +308,8 @@ public class PopupVideoPlayer extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playVideo(Uri videoURI, boolean autoPlay) {
|
public void playVideo(VideoStream videoStream, boolean autoPlay) {
|
||||||
super.playVideo(videoURI, autoPlay);
|
super.playVideo(videoStream, autoPlay);
|
||||||
|
|
||||||
windowLayoutParams.width = (int) getMinimumVideoWidth(currentPopupHeight);
|
windowLayoutParams.width = (int) getMinimumVideoWidth(currentPopupHeight);
|
||||||
windowManager.updateViewLayout(getRootView(), windowLayoutParams);
|
windowManager.updateViewLayout(getRootView(), windowLayoutParams);
|
||||||
|
@ -321,18 +323,8 @@ public class PopupVideoPlayer extends Service {
|
||||||
public void onFullScreenButtonClicked() {
|
public void onFullScreenButtonClicked() {
|
||||||
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
|
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
|
||||||
Intent intent;
|
Intent intent;
|
||||||
//if (getSharedPreferences().getBoolean(getResources().getString(R.string.use_exoplayer_key), false)) {
|
if (!getSharedPreferences().getBoolean(getResources().getString(R.string.use_old_player_key), false)) {
|
||||||
// TODO: Remove this check when ExoPlayer is the default
|
intent = NavigationHelper.getOpenPlayerIntent(context, ExoPlayerActivity.class, playerImpl);
|
||||||
// For now just disable the non-exoplayer player
|
|
||||||
//noinspection ConstantConditions,ConstantIfStatement
|
|
||||||
if (true) {
|
|
||||||
intent = new Intent(PopupVideoPlayer.this, ExoPlayerActivity.class)
|
|
||||||
.putExtra(AbstractPlayer.VIDEO_TITLE, getVideoTitle())
|
|
||||||
.putExtra(AbstractPlayer.VIDEO_URL, getVideoUrl())
|
|
||||||
.putExtra(AbstractPlayer.CHANNEL_NAME, getChannelName())
|
|
||||||
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, getSelectedIndexStream())
|
|
||||||
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, getVideoStreamsList())
|
|
||||||
.putExtra(AbstractPlayer.START_POSITION, ((int) getPlayer().getCurrentPosition()));
|
|
||||||
if (!playerImpl.isStartedFromNewPipe()) intent.putExtra(AbstractPlayer.STARTED_FROM_NEWPIPE, false);
|
if (!playerImpl.isStartedFromNewPipe()) intent.putExtra(AbstractPlayer.STARTED_FROM_NEWPIPE, false);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
} else {
|
} else {
|
||||||
|
@ -343,8 +335,9 @@ public class PopupVideoPlayer extends Service {
|
||||||
.putExtra(PlayVideoActivity.START_POSITION, Math.round(getPlayer().getCurrentPosition() / 1000f));
|
.putExtra(PlayVideoActivity.START_POSITION, Math.round(getPlayer().getCurrentPosition() / 1000f));
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
}
|
}
|
||||||
context.startActivity(intent);
|
|
||||||
stopSelf();
|
stopSelf();
|
||||||
|
if (playerImpl != null) playerImpl.destroy();
|
||||||
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -510,84 +503,123 @@ public class PopupVideoPlayer extends Service {
|
||||||
/**
|
/**
|
||||||
* Fetcher used if open by a link out of NewPipe
|
* Fetcher used if open by a link out of NewPipe
|
||||||
*/
|
*/
|
||||||
private class FetcherRunnable implements Runnable {
|
private class FetcherRunnable implements StreamExtractorWorker.OnStreamInfoReceivedListener {
|
||||||
private final Intent intent;
|
private final Context context;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
|
|
||||||
FetcherRunnable(Intent intent) {
|
FetcherRunnable(Context context) {
|
||||||
this.intent = intent;
|
|
||||||
this.mainHandler = new Handler(PopupVideoPlayer.this.getMainLooper());
|
this.mainHandler = new Handler(PopupVideoPlayer.this.getMainLooper());
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void onReceive(StreamInfo info) {
|
||||||
StreamExtractor streamExtractor;
|
playerImpl.setVideoTitle(info.title);
|
||||||
try {
|
playerImpl.setVideoUrl(info.webpage_url);
|
||||||
StreamingService service = NewPipe.getService(0);
|
playerImpl.setChannelName(info.uploader);
|
||||||
if (service == null) return;
|
|
||||||
streamExtractor = service.getExtractorInstance(intent.getStringExtra(Constants.KEY_URL));
|
|
||||||
StreamInfo info = StreamInfo.getVideoInfo(streamExtractor);
|
|
||||||
playerImpl.setVideoStreamsList(info.video_streams instanceof ArrayList
|
|
||||||
? (ArrayList<VideoStream>) info.video_streams
|
|
||||||
: new ArrayList<>(info.video_streams));
|
|
||||||
|
|
||||||
int defaultResolution = Utils.getPreferredResolution(PopupVideoPlayer.this, info.video_streams);
|
playerImpl.setVideoStreamsList(Utils.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false));
|
||||||
playerImpl.setSelectedIndexStream(defaultResolution);
|
playerImpl.setAudioStream(Utils.getHighestQualityAudio(info.audio_streams));
|
||||||
|
|
||||||
if (DEBUG) {
|
int defaultResolution = Utils.getPopupDefaultResolution(context, playerImpl.getVideoStreamsList());
|
||||||
Log.d(TAG, "FetcherRunnable.StreamExtractor: chosen = "
|
playerImpl.setSelectedIndexStream(defaultResolution);
|
||||||
+ MediaFormat.getNameById(info.video_streams.get(defaultResolution).format) + " "
|
|
||||||
+ info.video_streams.get(defaultResolution).resolution + " > "
|
|
||||||
+ info.video_streams.get(defaultResolution).url);
|
|
||||||
}
|
|
||||||
|
|
||||||
playerImpl.setVideoUrl(info.webpage_url);
|
if (DEBUG) {
|
||||||
playerImpl.setVideoTitle(info.title);
|
Log.d(TAG, "FetcherRunnable.StreamExtractor: chosen = "
|
||||||
playerImpl.setChannelName(info.uploader);
|
+ MediaFormat.getNameById(info.video_streams.get(defaultResolution).format) + " "
|
||||||
if (info.start_position > 0) playerImpl.setVideoStartPos(info.start_position * 1000);
|
+ info.video_streams.get(defaultResolution).resolution + " > "
|
||||||
else playerImpl.setVideoStartPos(-1);
|
+ info.video_streams.get(defaultResolution).url);
|
||||||
|
|
||||||
mainHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
playerImpl.playVideo(playerImpl.getSelectedStreamUri(), true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
imageLoader.resume();
|
|
||||||
imageLoader.loadImage(info.thumbnail_url, displayImageOptions, new SimpleImageLoadingListener() {
|
|
||||||
@Override
|
|
||||||
public void onLoadingComplete(String imageUri, View view, final Bitmap loadedImage) {
|
|
||||||
mainHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
playerImpl.setVideoThumbnail(loadedImage);
|
|
||||||
if (loadedImage != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
|
|
||||||
updateNotification(-1);
|
|
||||||
ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = loadedImage;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (IOException ie) {
|
|
||||||
if (DEBUG) ie.printStackTrace();
|
|
||||||
mainHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast.makeText(PopupVideoPlayer.this, R.string.network_error, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
stopSelf();
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (DEBUG) e.printStackTrace();
|
|
||||||
mainHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast.makeText(PopupVideoPlayer.this, R.string.content_not_available, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
stopSelf();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info.start_position > 0) playerImpl.setVideoStartPos(info.start_position * 1000);
|
||||||
|
else playerImpl.setVideoStartPos(-1);
|
||||||
|
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
playerImpl.playVideo(playerImpl.getSelectedVideoStream(), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
imageLoader.resume();
|
||||||
|
imageLoader.loadImage(info.thumbnail_url, displayImageOptions, new SimpleImageLoadingListener() {
|
||||||
|
@Override
|
||||||
|
public void onLoadingComplete(String imageUri, View view, final Bitmap loadedImage) {
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
playerImpl.setVideoThumbnail(loadedImage);
|
||||||
|
if (loadedImage != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
|
||||||
|
updateNotification(-1);
|
||||||
|
ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = loadedImage;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final int messageId) {
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(context, messageId, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReCaptchaException() {
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Starting ReCaptcha Challenge Activity
|
||||||
|
Intent intent = new Intent(context, ReCaptchaActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(intent);
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBlockedByGemaError() {
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(context, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContentErrorWithMessage(final int messageId) {
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(context, messageId, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContentError() {
|
||||||
|
mainHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnrecoverableError(Exception exception) {
|
||||||
|
stopSelf();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
Intent intent = new Intent(context, ErrorActivity.class);
|
Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
intent.putExtra(ERROR_INFO, errorInfo);
|
intent.putExtra(ERROR_INFO, errorInfo);
|
||||||
intent.putExtra(ERROR_LIST, elToSl(el));
|
intent.putExtra(ERROR_LIST, elToSl(el));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
}).show();
|
}).show();
|
||||||
|
@ -130,6 +131,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
Intent intent = new Intent(context, ErrorActivity.class);
|
Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
intent.putExtra(ERROR_INFO, errorInfo);
|
intent.putExtra(ERROR_INFO, errorInfo);
|
||||||
intent.putExtra(ERROR_LIST, elToSl(el));
|
intent.putExtra(ERROR_LIST, elToSl(el));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,7 +182,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
Intent intent = new Intent(context, ErrorActivity.class);
|
Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
intent.putExtra(ERROR_INFO, errorInfo);
|
intent.putExtra(ERROR_INFO, errorInfo);
|
||||||
intent.putExtra(ERROR_LIST, el);
|
intent.putExtra(ERROR_LIST, el);
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||||
|
|
||||||
|
@ -53,6 +52,7 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener prefListener;
|
SharedPreferences.OnSharedPreferenceChangeListener prefListener;
|
||||||
// get keys
|
// get keys
|
||||||
String DEFAULT_RESOLUTION_PREFERENCE;
|
String DEFAULT_RESOLUTION_PREFERENCE;
|
||||||
|
String DEFAULT_POPUP_RESOLUTION_PREFERENCE;
|
||||||
String PREFERRED_VIDEO_FORMAT_PREFERENCE;
|
String PREFERRED_VIDEO_FORMAT_PREFERENCE;
|
||||||
String DEFAULT_AUDIO_FORMAT_PREFERENCE;
|
String DEFAULT_AUDIO_FORMAT_PREFERENCE;
|
||||||
String SEARCH_LANGUAGE_PREFERENCE;
|
String SEARCH_LANGUAGE_PREFERENCE;
|
||||||
|
@ -61,6 +61,7 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
String USE_TOR_KEY;
|
String USE_TOR_KEY;
|
||||||
String THEME;
|
String THEME;
|
||||||
private ListPreference defaultResolutionPreference;
|
private ListPreference defaultResolutionPreference;
|
||||||
|
private ListPreference defaultPopupResolutionPreference;
|
||||||
private ListPreference preferredVideoFormatPreference;
|
private ListPreference preferredVideoFormatPreference;
|
||||||
private ListPreference defaultAudioFormatPreference;
|
private ListPreference defaultAudioFormatPreference;
|
||||||
private ListPreference searchLanguagePreference;
|
private ListPreference searchLanguagePreference;
|
||||||
|
@ -80,6 +81,7 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
|
|
||||||
// get keys
|
// get keys
|
||||||
DEFAULT_RESOLUTION_PREFERENCE = getString(R.string.default_resolution_key);
|
DEFAULT_RESOLUTION_PREFERENCE = getString(R.string.default_resolution_key);
|
||||||
|
DEFAULT_POPUP_RESOLUTION_PREFERENCE = getString(R.string.default_popup_resolution_key);
|
||||||
PREFERRED_VIDEO_FORMAT_PREFERENCE = getString(R.string.preferred_video_format_key);
|
PREFERRED_VIDEO_FORMAT_PREFERENCE = getString(R.string.preferred_video_format_key);
|
||||||
DEFAULT_AUDIO_FORMAT_PREFERENCE = getString(R.string.default_audio_format_key);
|
DEFAULT_AUDIO_FORMAT_PREFERENCE = getString(R.string.default_audio_format_key);
|
||||||
SEARCH_LANGUAGE_PREFERENCE = getString(R.string.search_language_key);
|
SEARCH_LANGUAGE_PREFERENCE = getString(R.string.search_language_key);
|
||||||
|
@ -91,6 +93,8 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
// get pref objects
|
// get pref objects
|
||||||
defaultResolutionPreference =
|
defaultResolutionPreference =
|
||||||
(ListPreference) findPreference(DEFAULT_RESOLUTION_PREFERENCE);
|
(ListPreference) findPreference(DEFAULT_RESOLUTION_PREFERENCE);
|
||||||
|
defaultPopupResolutionPreference =
|
||||||
|
(ListPreference) findPreference(DEFAULT_POPUP_RESOLUTION_PREFERENCE);
|
||||||
preferredVideoFormatPreference =
|
preferredVideoFormatPreference =
|
||||||
(ListPreference) findPreference(PREFERRED_VIDEO_FORMAT_PREFERENCE);
|
(ListPreference) findPreference(PREFERRED_VIDEO_FORMAT_PREFERENCE);
|
||||||
defaultAudioFormatPreference =
|
defaultAudioFormatPreference =
|
||||||
|
@ -103,6 +107,9 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
|
|
||||||
final String currentTheme = defaultPreferences.getString(THEME, "Light");
|
final String currentTheme = defaultPreferences.getString(THEME, "Light");
|
||||||
|
|
||||||
|
// TODO: Clean this, as the class is already implementing the class
|
||||||
|
// and those double equals...
|
||||||
|
|
||||||
prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
||||||
|
@ -260,6 +267,9 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
defaultResolutionPreference.setSummary(
|
defaultResolutionPreference.setSummary(
|
||||||
defaultPreferences.getString(DEFAULT_RESOLUTION_PREFERENCE,
|
defaultPreferences.getString(DEFAULT_RESOLUTION_PREFERENCE,
|
||||||
getString(R.string.default_resolution_value)));
|
getString(R.string.default_resolution_value)));
|
||||||
|
defaultPopupResolutionPreference.setSummary(
|
||||||
|
defaultPreferences.getString(DEFAULT_POPUP_RESOLUTION_PREFERENCE,
|
||||||
|
getString(R.string.default_popup_resolution_value)));
|
||||||
preferredVideoFormatPreference.setSummary(
|
preferredVideoFormatPreference.setSummary(
|
||||||
defaultPreferences.getString(PREFERRED_VIDEO_FORMAT_PREFERENCE,
|
defaultPreferences.getString(PREFERRED_VIDEO_FORMAT_PREFERENCE,
|
||||||
getString(R.string.preferred_video_format_default)));
|
getString(R.string.preferred_video_format_default)));
|
||||||
|
|
|
@ -8,12 +8,36 @@ import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
||||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
|
import org.schabi.newpipe.player.AbstractPlayer;
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
public class NavigationHelper {
|
public class NavigationHelper {
|
||||||
|
|
||||||
|
public static Intent getOpenPlayerIntent(Context context, Class targetClazz, StreamInfo info, int selectedStreamIndex) {
|
||||||
|
return new Intent(context, targetClazz)
|
||||||
|
.putExtra(AbstractPlayer.VIDEO_TITLE, info.title)
|
||||||
|
.putExtra(AbstractPlayer.VIDEO_URL, info.webpage_url)
|
||||||
|
.putExtra(AbstractPlayer.CHANNEL_NAME, info.uploader)
|
||||||
|
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, selectedStreamIndex)
|
||||||
|
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, Utils.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false))
|
||||||
|
.putExtra(AbstractPlayer.VIDEO_ONLY_AUDIO_STREAM, Utils.getHighestQualityAudio(info.audio_streams));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Intent getOpenPlayerIntent(Context context, Class targetClazz, AbstractPlayer instance) {
|
||||||
|
return new Intent(context, targetClazz)
|
||||||
|
.putExtra(AbstractPlayer.VIDEO_TITLE, instance.getVideoTitle())
|
||||||
|
.putExtra(AbstractPlayer.VIDEO_URL, instance.getVideoUrl())
|
||||||
|
.putExtra(AbstractPlayer.CHANNEL_NAME, instance.getChannelName())
|
||||||
|
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, instance.getSelectedStreamIndex())
|
||||||
|
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, instance.getVideoStreamsList())
|
||||||
|
.putExtra(AbstractPlayer.VIDEO_ONLY_AUDIO_STREAM, instance.getAudioStream())
|
||||||
|
.putExtra(AbstractPlayer.START_POSITION, ((int) instance.getPlayer().getCurrentPosition()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Through Interface (faster)
|
// Through Interface (faster)
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
|
@ -9,28 +9,31 @@ import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
|
||||||
|
private static final List<String> HIGH_RESOLUTION_LIST = Arrays.asList("1440p", "2160p", "1440p60", "2160p60");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the index of the default stream in the list, based on the
|
* Return the index of the default stream in the list, based on the parameters
|
||||||
* preferred resolution and format chosen in the settings
|
* defaultResolution and preferredFormat
|
||||||
|
*
|
||||||
|
* @param videoStreams the list that will be extracted the index
|
||||||
*
|
*
|
||||||
* @param videoStreams the list that will be extracted the index
|
|
||||||
* @return index of the preferred resolution&format
|
* @return index of the preferred resolution&format
|
||||||
*/
|
*/
|
||||||
public static int getPreferredResolution(Context context, List<VideoStream> videoStreams) {
|
public static int getDefaultResolution(String defaultResolution, String preferredFormat, List<VideoStream> videoStreams) {
|
||||||
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
if (defaultPreferences == null) return 0;
|
|
||||||
|
|
||||||
String defaultResolution = defaultPreferences
|
if (defaultResolution.equals("Best resolution")) {
|
||||||
.getString(context.getString(R.string.default_resolution_key),
|
return 0;
|
||||||
context.getString(R.string.default_resolution_value));
|
}
|
||||||
|
|
||||||
String preferredFormat = defaultPreferences
|
|
||||||
.getString(context.getString(R.string.preferred_video_format_key),
|
|
||||||
context.getString(R.string.preferred_video_format_default));
|
|
||||||
|
|
||||||
// first try to find the one with the right resolution
|
// first try to find the one with the right resolution
|
||||||
int selectedFormat = 0;
|
int selectedFormat = 0;
|
||||||
|
@ -50,16 +53,69 @@ public class Utils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedFormat == 0 && !videoStreams.get(selectedFormat).resolution.contains(defaultResolution.replace("p60", "p"))) {
|
||||||
|
// Maybe there's no 60 fps variant available, so fallback to the normal version
|
||||||
|
String replace = defaultResolution.replace("p60", "p");
|
||||||
|
for (int i = 0; i < videoStreams.size(); i++) {
|
||||||
|
VideoStream item = videoStreams.get(i);
|
||||||
|
if (replace.equals(item.resolution)) selectedFormat = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// than try to find the one with the right resolution and format
|
||||||
|
for (int i = 0; i < videoStreams.size(); i++) {
|
||||||
|
VideoStream item = videoStreams.get(i);
|
||||||
|
if (replace.equals(item.resolution)
|
||||||
|
&& preferredFormat.equals(MediaFormat.getNameById(item.format))) {
|
||||||
|
selectedFormat = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// this is actually an error,
|
// this is actually an error,
|
||||||
// but maybe there is really no stream fitting to the default value.
|
// but maybe there is really no stream fitting to the default value.
|
||||||
return selectedFormat;
|
return selectedFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #getDefaultResolution(String, String, List)
|
||||||
|
*/
|
||||||
|
public static int getDefaultResolution(Context context, List<VideoStream> videoStreams) {
|
||||||
|
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
if (defaultPreferences == null) return 0;
|
||||||
|
|
||||||
|
String defaultResolution = defaultPreferences
|
||||||
|
.getString(context.getString(R.string.default_resolution_key), context.getString(R.string.default_resolution_value));
|
||||||
|
|
||||||
|
String preferredFormat = defaultPreferences
|
||||||
|
.getString(context.getString(R.string.preferred_video_format_key), context.getString(R.string.preferred_video_format_default));
|
||||||
|
|
||||||
|
return getDefaultResolution(defaultResolution, preferredFormat, videoStreams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #getDefaultResolution(String, String, List)
|
||||||
|
*/
|
||||||
|
public static int getPopupDefaultResolution(Context context, List<VideoStream> videoStreams) {
|
||||||
|
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
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));
|
||||||
|
|
||||||
|
String preferredFormat = defaultPreferences
|
||||||
|
.getString(context.getString(R.string.preferred_video_format_key), context.getString(R.string.preferred_video_format_default));
|
||||||
|
|
||||||
|
return getDefaultResolution(defaultResolution, preferredFormat, videoStreams);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the index of the default stream in the list, based on the
|
* Return the index of the default stream in the list, based on the
|
||||||
* preferred audio format chosen in the settings
|
* preferred audio format chosen in the settings
|
||||||
*
|
*
|
||||||
|
* @param context context to get the preferred audio format
|
||||||
* @param audioStreams the list that will be extracted the index
|
* @param audioStreams the list that will be extracted the index
|
||||||
|
*
|
||||||
* @return index of the preferred format
|
* @return index of the preferred format
|
||||||
*/
|
*/
|
||||||
public static int getPreferredAudioFormat(Context context, List<AudioStream> audioStreams) {
|
public static int getPreferredAudioFormat(Context context, List<AudioStream> audioStreams) {
|
||||||
|
@ -88,4 +144,125 @@ public class Utils {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the audio from the list with the highest bitrate
|
||||||
|
*
|
||||||
|
* @param audioStreams list the audio streams
|
||||||
|
* @return audio with highest average bitrate
|
||||||
|
*/
|
||||||
|
public static AudioStream getHighestQualityAudio(List<AudioStream> audioStreams) {
|
||||||
|
int highestQualityIndex = 0;
|
||||||
|
|
||||||
|
for (int i = 1; i < audioStreams.size(); i++) {
|
||||||
|
AudioStream audioStream = audioStreams.get(i);
|
||||||
|
if (audioStream.avgBitrate > audioStreams.get(highestQualityIndex).avgBitrate) highestQualityIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioStreams.get(highestQualityIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join the two lists of video streams (video_only and normal videos), and sort them according with preferred format
|
||||||
|
* chosen by the user
|
||||||
|
*
|
||||||
|
* @param context context to search for the format to give preference
|
||||||
|
* @param videoStreams normal videos list
|
||||||
|
* @param videoOnlyStreams video only stream list
|
||||||
|
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest
|
||||||
|
* @return the sorted list
|
||||||
|
*/
|
||||||
|
public static ArrayList<VideoStream> getSortedStreamVideosList(Context context, List<VideoStream> videoStreams, List<VideoStream> videoOnlyStreams, boolean ascendingOrder) {
|
||||||
|
boolean showHigherResolutions = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(context.getString(R.string.show_higher_resolutions_key), false);
|
||||||
|
String preferredFormatString = PreferenceManager.getDefaultSharedPreferences(context).getString(context.getString(R.string.preferred_video_format_key), context.getString(R.string.preferred_video_format_default));
|
||||||
|
MediaFormat preferredFormat = MediaFormat.WEBM;
|
||||||
|
switch (preferredFormatString) {
|
||||||
|
case "WebM":
|
||||||
|
preferredFormat = MediaFormat.WEBM;
|
||||||
|
break;
|
||||||
|
case "MPEG-4":
|
||||||
|
preferredFormat = MediaFormat.MPEG_4;
|
||||||
|
break;
|
||||||
|
case "3GPP":
|
||||||
|
preferredFormat = MediaFormat.v3GPP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return getSortedStreamVideosList(preferredFormat, showHigherResolutions, videoStreams, videoOnlyStreams, ascendingOrder);
|
||||||
|
}
|
||||||
|
//show_higher_resolutions_key
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join the two lists of video streams (video_only and normal videos), and sort them according with preferred format
|
||||||
|
* chosen by the user
|
||||||
|
*
|
||||||
|
* @param preferredFormat format to give preference
|
||||||
|
* @param showHigherResolutions
|
||||||
|
* @param videoStreams normal videos list
|
||||||
|
* @param videoOnlyStreams video only stream list
|
||||||
|
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest @return the sorted list
|
||||||
|
* @return the sorted list
|
||||||
|
*/
|
||||||
|
public static ArrayList<VideoStream> getSortedStreamVideosList(MediaFormat preferredFormat, boolean showHigherResolutions, List<VideoStream> videoStreams, List<VideoStream> videoOnlyStreams, boolean ascendingOrder) {
|
||||||
|
ArrayList<VideoStream> retList = new ArrayList<>();
|
||||||
|
HashMap<String, VideoStream> hashMap = new HashMap<>();
|
||||||
|
|
||||||
|
if (videoOnlyStreams != null) {
|
||||||
|
for (VideoStream stream : videoOnlyStreams) {
|
||||||
|
if (!showHigherResolutions && HIGH_RESOLUTION_LIST.contains(stream.resolution)) continue;
|
||||||
|
retList.add(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (videoStreams != null) {
|
||||||
|
for (VideoStream stream : videoStreams) {
|
||||||
|
if (!showHigherResolutions && HIGH_RESOLUTION_LIST.contains(stream.resolution)) continue;
|
||||||
|
retList.add(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all to the hashmap
|
||||||
|
for (VideoStream videoStream : retList) hashMap.put(videoStream.resolution, videoStream);
|
||||||
|
|
||||||
|
// Override the values when the key == resolution, with the preferredFormat
|
||||||
|
for (VideoStream videoStream : retList) {
|
||||||
|
if (videoStream.format == preferredFormat.id) hashMap.put(videoStream.resolution, videoStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
retList.clear();
|
||||||
|
retList.addAll(hashMap.values());
|
||||||
|
sortStreamList(retList, ascendingOrder);
|
||||||
|
return retList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the streams list depending on the parameter ascendingOrder;
|
||||||
|
* <p>
|
||||||
|
* It works like that:<br>
|
||||||
|
* - Take a string resolution, remove the letters, replace "0p60" (for 60fps videos) with "1"
|
||||||
|
* and sort by the greatest:<br>
|
||||||
|
* <blockquote><pre>
|
||||||
|
* 720p -> 720
|
||||||
|
* 720p60 -> 721
|
||||||
|
* 360p -> 360
|
||||||
|
* 1080p -> 1080
|
||||||
|
* 1080p60 -> 1081
|
||||||
|
* <p>
|
||||||
|
* ascendingOrder ? 360 < 720 < 721 < 1080 < 1081
|
||||||
|
* !ascendingOrder ? 1081 < 1080 < 721 < 720 < 360/pre></blockquote>
|
||||||
|
* <p>
|
||||||
|
* @param videoStreams list that the sorting will be applied
|
||||||
|
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest
|
||||||
|
*/
|
||||||
|
public static void sortStreamList(List<VideoStream> videoStreams, final boolean ascendingOrder) {
|
||||||
|
Collections.sort(videoStreams, new Comparator<VideoStream>() {
|
||||||
|
@Override
|
||||||
|
public int compare(VideoStream o1, VideoStream o2) {
|
||||||
|
int res1 = Integer.parseInt(o1.resolution.replace("0p60", "1").replaceAll("[^\\d.]", ""));
|
||||||
|
int res2 = Integer.parseInt(o2.resolution.replace("0p60", "1").replaceAll("[^\\d.]", ""));
|
||||||
|
|
||||||
|
return ascendingOrder ? res1 - res2 : res2 - res1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,11 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
public interface OnChannelInfoReceive {
|
public interface OnChannelInfoReceive {
|
||||||
void onReceive(ChannelInfo info);
|
void onReceive(ChannelInfo info);
|
||||||
void onError(int messageId);
|
void onError(int messageId);
|
||||||
|
/**
|
||||||
|
* Called when an unrecoverable error has occurred.
|
||||||
|
* <p> This is a good place to finish the caller. </p>
|
||||||
|
*/
|
||||||
|
void onUnrecoverableError(Exception exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,9 +79,11 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleException(Exception exception, int serviceId, String url) {
|
protected void handleException(final Exception exception, int serviceId, String url) {
|
||||||
|
if (callback == null || getHandler() == null || isInterrupted()) return;
|
||||||
|
|
||||||
if (exception instanceof IOException) {
|
if (exception instanceof IOException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onError(R.string.network_error);
|
callback.onError(R.string.network_error);
|
||||||
|
@ -84,10 +91,20 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
});
|
});
|
||||||
} else if (exception instanceof ParsingException || exception instanceof ExtractionException) {
|
} else if (exception instanceof ParsingException || exception instanceof ExtractionException) {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.parsing_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.parsing_error));
|
||||||
finishIfActivity();
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.general_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.general_error));
|
||||||
finishIfActivity();
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,13 +135,6 @@ public abstract class ExtractorWorker extends Thread {
|
||||||
this.service = null;
|
this.service = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If the context passed in the constructor is an {@link Activity}, finish it.
|
|
||||||
*/
|
|
||||||
protected void finishIfActivity() {
|
|
||||||
if (getContext() instanceof Activity) ((Activity) getContext()).finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Handler getHandler() {
|
public Handler getHandler() {
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,12 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
void onBlockedByGemaError();
|
void onBlockedByGemaError();
|
||||||
void onContentErrorWithMessage(int messageId);
|
void onContentErrorWithMessage(int messageId);
|
||||||
void onContentError();
|
void onContentError();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an unrecoverable error has occurred.
|
||||||
|
* <p> This is a good place to finish the caller. </p>
|
||||||
|
*/
|
||||||
|
void onUnrecoverableError(Exception exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,7 +68,7 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
|
|
||||||
if (streamInfo != null && !streamInfo.errors.isEmpty()) handleErrorsDuringExtraction(streamInfo.errors, ErrorActivity.REQUESTED_STREAM);
|
if (streamInfo != null && !streamInfo.errors.isEmpty()) handleErrorsDuringExtraction(streamInfo.errors, ErrorActivity.REQUESTED_STREAM);
|
||||||
|
|
||||||
if (callback != null && streamInfo != null && !isInterrupted()) getHandler().post(new Runnable() {
|
if (callback != null && getHandler() != null && streamInfo != null && !isInterrupted()) getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (isInterrupted() || callback == null) return;
|
if (isInterrupted() || callback == null) return;
|
||||||
|
@ -75,37 +81,39 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleException(final Exception exception, int serviceId, String url) {
|
protected void handleException(final Exception exception, int serviceId, final String url) {
|
||||||
|
if (callback == null || getHandler() == null || isInterrupted()) return;
|
||||||
|
|
||||||
if (exception instanceof ReCaptchaException) {
|
if (exception instanceof ReCaptchaException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onReCaptchaException();
|
callback.onReCaptchaException();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (exception instanceof IOException) {
|
} else if (exception instanceof IOException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onError(R.string.network_error);
|
callback.onError(R.string.network_error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
|
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onBlockedByGemaError();
|
callback.onBlockedByGemaError();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
|
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onContentErrorWithMessage(R.string.live_streams_not_supported);
|
callback.onContentErrorWithMessage(R.string.live_streams_not_supported);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (exception instanceof StreamExtractor.ContentNotAvailableException) {
|
} else if (exception instanceof StreamExtractor.ContentNotAvailableException) {
|
||||||
if (callback != null) getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
callback.onContentError();
|
callback.onContentError();
|
||||||
|
@ -114,7 +122,12 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
} else if (exception instanceof YoutubeStreamExtractor.DecryptException) {
|
} else if (exception instanceof YoutubeStreamExtractor.DecryptException) {
|
||||||
// custom service related exceptions
|
// custom service related exceptions
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.youtube_signature_decryption_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.youtube_signature_decryption_error));
|
||||||
finishIfActivity();
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (exception instanceof StreamInfo.StreamExctractException) {
|
} else if (exception instanceof StreamInfo.StreamExctractException) {
|
||||||
if (!streamInfo.errors.isEmpty()) {
|
if (!streamInfo.errors.isEmpty()) {
|
||||||
// !!! if this case ever kicks in someone gets kicked out !!!
|
// !!! if this case ever kicks in someone gets kicked out !!!
|
||||||
|
@ -122,13 +135,29 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
} else {
|
} else {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), streamInfo.errors, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.could_not_get_stream));
|
ErrorActivity.reportError(getHandler(), getContext(), streamInfo.errors, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.could_not_get_stream));
|
||||||
}
|
}
|
||||||
finishIfActivity();
|
|
||||||
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (exception instanceof ParsingException) {
|
} else if (exception instanceof ParsingException) {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.parsing_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.parsing_error));
|
||||||
finishIfActivity();
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.general_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.general_error));
|
||||||
finishIfActivity();
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onUnrecoverableError(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,24 @@
|
||||||
<string name="default_resolution_key" translatable="false">default_resolution_preference</string>
|
<string name="default_resolution_key" translatable="false">default_resolution_preference</string>
|
||||||
<string name="default_resolution_value" translatable="false">360p</string>
|
<string name="default_resolution_value" translatable="false">360p</string>
|
||||||
<string-array name="resolution_list">
|
<string-array name="resolution_list">
|
||||||
|
<item>Best resolution</item>
|
||||||
|
<item>1080p60</item>
|
||||||
|
<item>1080p</item>
|
||||||
|
<item>720p60</item>
|
||||||
<item>720p</item>
|
<item>720p</item>
|
||||||
|
<item>480p</item>
|
||||||
<item>360p</item>
|
<item>360p</item>
|
||||||
<item>240p</item>
|
<item>240p</item>
|
||||||
<item>144p</item>
|
<item>144p</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string name="preferred_video_format_key" translatable="false">preferrfed_video_format</string>
|
<string name="default_popup_resolution_key" translatable="false">default_popup_resolution_key</string>
|
||||||
<string name="preferred_video_format_default" translatable="false">WebM</string>
|
<string name="default_popup_resolution_value" translatable="false">480p</string>
|
||||||
|
|
||||||
|
<string name="show_higher_resolutions_key" translatable="false">show_higher_resolutions_key</string>
|
||||||
|
|
||||||
|
<string name="preferred_video_format_key" translatable="false">preferred_video_format</string>
|
||||||
|
<string name="preferred_video_format_default" translatable="false">MPEG-4</string>
|
||||||
<string-array name="video_format_list">
|
<string-array name="video_format_list">
|
||||||
<item>WebM</item>
|
<item>WebM</item>
|
||||||
<item>MPEG-4</item>
|
<item>MPEG-4</item>
|
||||||
|
@ -33,12 +43,7 @@
|
||||||
<string name="show_play_with_kodi_key" translatable="false">show_play_with_kodi</string>
|
<string name="show_play_with_kodi_key" translatable="false">show_play_with_kodi</string>
|
||||||
|
|
||||||
<string name="theme_key" translatable="false">theme</string>
|
<string name="theme_key" translatable="false">theme</string>
|
||||||
<string name="default_theme_value" translatable="false">@string/light_theme_title</string>
|
<string name="default_theme_value" translatable="false">@string/dark_theme_title</string>
|
||||||
<string-array name="theme_description_list">
|
|
||||||
<item>@string/light_theme_title</item>
|
|
||||||
<item>@string/dark_theme_title</item>
|
|
||||||
<item>@string/black_theme_title</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="theme_list">
|
<string-array name="theme_list">
|
||||||
<item>@string/light_theme_title</item>
|
<item>@string/light_theme_title</item>
|
||||||
<item>@string/dark_theme_title</item>
|
<item>@string/dark_theme_title</item>
|
||||||
|
|
|
@ -38,6 +38,9 @@
|
||||||
<string name="autoplay_by_calling_app_title">Autoplay when called from another app</string>
|
<string name="autoplay_by_calling_app_title">Autoplay when called from another app</string>
|
||||||
<string name="autoplay_by_calling_app_summary">Automatically play a video when NewPipe is called from another app.</string>
|
<string name="autoplay_by_calling_app_summary">Automatically play a video when NewPipe is called from another app.</string>
|
||||||
<string name="default_resolution_title">Default resolution</string>
|
<string name="default_resolution_title">Default resolution</string>
|
||||||
|
<string name="default_popup_resolution_title">Default popup resolution</string>
|
||||||
|
<string name="show_higher_resolutions_title">Show higher resolutions</string>
|
||||||
|
<string name="show_higher_resolutions_summary">Only some devices support playing 2k/4k videos</string>
|
||||||
<string name="play_with_kodi_title">Play with Kodi</string>
|
<string name="play_with_kodi_title">Play with Kodi</string>
|
||||||
<string name="kore_not_found">Kore app not found. Install Kore?</string>
|
<string name="kore_not_found">Kore app not found. Install Kore?</string>
|
||||||
<string name="fdroid_kore_url" translatable="false">https://f-droid.org/repository/browse/?fdfilter=Kore&fdid=org.xbmc.kore</string>
|
<string name="fdroid_kore_url" translatable="false">https://f-droid.org/repository/browse/?fdfilter=Kore&fdid=org.xbmc.kore</string>
|
||||||
|
@ -67,6 +70,12 @@
|
||||||
<item>Audio</item>
|
<item>Audio</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="theme_description_list">
|
||||||
|
<item>@string/light_theme_title</item>
|
||||||
|
<item>@string/dark_theme_title</item>
|
||||||
|
<item>@string/black_theme_title</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string name="next_video_title">Next video</string>
|
<string name="next_video_title">Next video</string>
|
||||||
<string name="show_next_and_similar_title">Show next and similar videos</string>
|
<string name="show_next_and_similar_title">Show next and similar videos</string>
|
||||||
<string name="url_not_supported_toast">URL not supported</string>
|
<string name="url_not_supported_toast">URL not supported</string>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:title="@string/settings_activity_title"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:key="general_preferences">
|
android:title="@string/settings_activity_title"
|
||||||
|
android:key="general_preferences">
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="@string/settings_category_video_audio"
|
android:key="@string/settings_category_video_audio"
|
||||||
|
@ -25,12 +26,26 @@
|
||||||
android:entryValues="@array/resolution_list"
|
android:entryValues="@array/resolution_list"
|
||||||
android:defaultValue="@string/default_resolution_value"/>
|
android:defaultValue="@string/default_resolution_value"/>
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:key="@string/default_popup_resolution_key"
|
||||||
|
android:title="@string/default_popup_resolution_title"
|
||||||
|
android:entries="@array/resolution_list"
|
||||||
|
android:entryValues="@array/resolution_list"
|
||||||
|
android:defaultValue="@string/default_popup_resolution_value"/>
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="@string/show_higher_resolutions_key"
|
||||||
|
android:title="@string/show_higher_resolutions_title"
|
||||||
|
android:summary="@string/show_higher_resolutions_summary"
|
||||||
|
android:defaultValue="false"/>
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="@string/preferred_video_format_key"
|
android:key="@string/preferred_video_format_key"
|
||||||
android:title="@string/preferred_video_format_title"
|
android:title="@string/preferred_video_format_title"
|
||||||
android:entries="@array/video_format_list"
|
android:entries="@array/video_format_list"
|
||||||
android:entryValues="@array/video_format_list"
|
android:entryValues="@array/video_format_list"
|
||||||
android:defaultValue="@string/preferred_video_format_default"/>
|
android:defaultValue="@string/preferred_video_format_default"
|
||||||
|
tools:summary="MPEG-4"/>
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="@string/default_audio_format_key"
|
android:key="@string/default_audio_format_key"
|
||||||
|
|
78
app/src/test/java/org/schabi/newpipe/util/UtilsTest.java
Normal file
78
app/src/test/java/org/schabi/newpipe/util/UtilsTest.java
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class UtilsTest {
|
||||||
|
private List<AudioStream> audioStreamsTestList = Arrays.asList(
|
||||||
|
new AudioStream("", MediaFormat.M4A.id, /**/ 120, 0, 0),
|
||||||
|
new AudioStream("", MediaFormat.WEBMA.id, /**/ 190, 0, 0),
|
||||||
|
new AudioStream("", MediaFormat.M4A.id, /**/ 130, 0, 0),
|
||||||
|
new AudioStream("", MediaFormat.WEBMA.id, /**/ 60, 0, 0),
|
||||||
|
new AudioStream("", MediaFormat.M4A.id, /**/ 320, 0, 0),
|
||||||
|
new AudioStream("", MediaFormat.WEBMA.id, /**/ 320, 0, 0));
|
||||||
|
|
||||||
|
private List<VideoStream> videoStreamsTestList = Arrays.asList(
|
||||||
|
new VideoStream("", /**/ MediaFormat.MPEG_4.id, /**/ "720p"),
|
||||||
|
new VideoStream("", /**/ MediaFormat.v3GPP.id, /**/ "240p"),
|
||||||
|
new VideoStream("", /**/ MediaFormat.WEBM.id, /**/ "480p"),
|
||||||
|
new VideoStream("", /**/ MediaFormat.v3GPP.id, /**/ "144p"),
|
||||||
|
new VideoStream("", /**/ MediaFormat.MPEG_4.id, /**/ "360p"),
|
||||||
|
new VideoStream("", /**/ MediaFormat.WEBM.id, /**/ "360p"));
|
||||||
|
|
||||||
|
private List<VideoStream> videoOnlyStreamsTestList = Arrays.asList(
|
||||||
|
new VideoStream(true, "", /**/ MediaFormat.MPEG_4.id, /**/ "720p"),
|
||||||
|
new VideoStream(true, "", /**/ MediaFormat.MPEG_4.id, /**/ "720p"),
|
||||||
|
new VideoStream(true, "", /**/ MediaFormat.MPEG_4.id, /**/ "2160p"),
|
||||||
|
new VideoStream(true, "", /**/ MediaFormat.MPEG_4.id, /**/ "1440p60"),
|
||||||
|
new VideoStream(true, "", /**/ MediaFormat.WEBM.id, /**/ "720p60"),
|
||||||
|
new VideoStream(true, "", /**/ MediaFormat.MPEG_4.id, /**/ "2160p60"),
|
||||||
|
new VideoStream(true, "", /**/ MediaFormat.MPEG_4.id, /**/ "720p60"),
|
||||||
|
new VideoStream(true, "", /**/ MediaFormat.MPEG_4.id, /**/ "1080p"),
|
||||||
|
new VideoStream(true, "", /**/ MediaFormat.MPEG_4.id, /**/ "1080p60"));
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getHighestQualityAudioTest() throws Exception {
|
||||||
|
assertEquals(320, Utils.getHighestQualityAudio(audioStreamsTestList).avgBitrate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getSortedStreamVideosListTest() throws Exception {
|
||||||
|
List<VideoStream> result = Utils.getSortedStreamVideosList(MediaFormat.MPEG_4, true, videoStreamsTestList, videoOnlyStreamsTestList, true);
|
||||||
|
|
||||||
|
List<String> expected = Arrays.asList("144p", "240p", "360p", "480p", "720p", "720p60", "1080p", "1080p60", "1440p60", "2160p", "2160p60");
|
||||||
|
//for (VideoStream videoStream : result) System.out.println(videoStream.resolution + " > " + MediaFormat.getSuffixById(videoStream.format) + " > " + videoStream.isVideoOnly);
|
||||||
|
|
||||||
|
assertEquals(result.size(), expected.size());
|
||||||
|
for (int i = 0; i < result.size(); i++) {
|
||||||
|
assertEquals(result.get(i).resolution, expected.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// Reverse Order //
|
||||||
|
//////////////////
|
||||||
|
|
||||||
|
result = Utils.getSortedStreamVideosList(MediaFormat.MPEG_4, true, videoStreamsTestList, videoOnlyStreamsTestList, false);
|
||||||
|
|
||||||
|
expected = Arrays.asList("2160p60", "2160p", "1440p60", "1080p60", "1080p", "720p60", "720p", "480p", "360p", "240p", "144p");
|
||||||
|
assertEquals(result.size(), expected.size());
|
||||||
|
for (int i = 0; i < result.size(); i++) assertEquals(result.get(i).resolution, expected.get(i));
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
// Don't show Higher resolutions //
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
result = Utils.getSortedStreamVideosList(MediaFormat.MPEG_4, false, videoStreamsTestList, videoOnlyStreamsTestList, false);
|
||||||
|
expected = Arrays.asList("1080p60", "1080p", "720p60", "720p", "480p", "360p", "240p", "144p");
|
||||||
|
assertEquals(result.size(), expected.size());
|
||||||
|
for (int i = 0; i < result.size(); i++) assertEquals(result.get(i).resolution, expected.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue