From 9dde296a512504963fbb56db381bcb4ff3f8607c Mon Sep 17 00:00:00 2001 From: Mauricio Colli Date: Sun, 21 Jun 2020 14:17:55 -0300 Subject: [PATCH 1/5] Add support for different types of stream delivery For example, some streams have a direct link to them, and others use something like DASH or a HLS playlist. This adds support for them. The downloader doesn't support them yet, so when downloading these types of streams, they will be hidden. --- app/build.gradle | 2 +- .../newpipe/download/DownloadDialog.java | 74 +++++++++-- .../resolver/AudioPlaybackResolver.java | 2 +- .../player/resolver/PlaybackResolver.java | 57 +++++--- .../resolver/VideoPlaybackResolver.java | 6 +- .../schabi/newpipe/util/NavigationHelper.java | 15 ++- .../newpipe/util/StreamItemAdapter.java | 9 +- .../giga/get/DownloadMissionRecover.java | 16 ++- app/src/main/res/values/strings.xml | 2 + .../schabi/newpipe/util/ListHelperTest.java | 123 +++++++++--------- 10 files changed, 202 insertions(+), 104 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 434584bf01c..25e8747baf8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -163,7 +163,7 @@ dependencies { exclude module: 'support-annotations' } - implementation 'com.github.TeamNewPipe:NewPipeExtractor:bda83fe6a5b9a8a0751669fbc444fa49d72d0d2f' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:c234a6d2bdce228ed3a9e84952e7389afca13fa8' implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751" implementation "org.jsoup:jsoup:1.13.1" diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index cad0258da69..4d21babf8bc 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -44,6 +44,7 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.extractor.stream.AudioStream; +import org.schabi.newpipe.extractor.stream.DeliveryFormat; import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.SubtitlesStream; @@ -64,6 +65,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -149,7 +151,11 @@ private void setInfo(final StreamInfo info) { } public void setAudioStreams(final List audioStreams) { - setAudioStreams(new StreamSizeWrapper<>(audioStreams, getContext())); + // TODO: Remove this when the downloader support other types of stream deliveries + final List listAudioStreams = new ArrayList<>(audioStreams); + wasSomeStreamRemoved = removeNotDirectStreams(listAudioStreams) || wasSomeStreamRemoved; + + setAudioStreams(new StreamSizeWrapper<>(listAudioStreams, getContext())); } public void setAudioStreams(final StreamSizeWrapper was) { @@ -157,7 +163,47 @@ public void setAudioStreams(final StreamSizeWrapper was) { } public void setVideoStreams(final List videoStreams) { - setVideoStreams(new StreamSizeWrapper<>(videoStreams, getContext())); + // TODO: Remove this when the downloader support other types of stream deliveries + final List listVideoStreams = new ArrayList<>(videoStreams); + wasSomeStreamRemoved = removeNotDirectStreams(listVideoStreams) || wasSomeStreamRemoved; + + setVideoStreams(new StreamSizeWrapper<>(listVideoStreams, getContext())); + } + + public void setSubtitleStreams(final List subtitleStreams) { + // TODO: Remove this when the downloader support other types of stream deliveries + final List listSubtitleStreams = new ArrayList<>(subtitleStreams); + wasSomeStreamRemoved = removeNotDirectStreams(listSubtitleStreams) || wasSomeStreamRemoved; + + setSubtitleStreams(new StreamSizeWrapper<>(listSubtitleStreams, getContext())); + } + + private Toast removedStreamToast = null; + boolean wasSomeStreamRemoved = false; + + private void showIfStreamsWereRemovedMessage() { + if (wasSomeStreamRemoved && removedStreamToast == null) { + removedStreamToast = Toast.makeText( + requireContext(), + R.string.streams_hidden_download_not_supported_yet, + Toast.LENGTH_LONG + ); + removedStreamToast.show(); + } + } + + private static boolean removeNotDirectStreams(final List streamList) { + boolean wasSomeStreamRemoved = false; + final Iterator streamIterator = streamList.iterator(); + while (streamIterator.hasNext()) { + final Stream stream = streamIterator.next(); + if (!(stream.getDeliveryFormat() instanceof DeliveryFormat.Direct)) { + streamIterator.remove(); + wasSomeStreamRemoved = true; + } + } + + return wasSomeStreamRemoved; } /*////////////////////////////////////////////////////////////////////////// @@ -168,10 +214,6 @@ public void setVideoStreams(final StreamSizeWrapper wvs) { this.wrappedVideoStreams = wvs; } - public void setSubtitleStreams(final List subtitleStreams) { - setSubtitleStreams(new StreamSizeWrapper<>(subtitleStreams, getContext())); - } - public void setSubtitleStreams( final StreamSizeWrapper wss) { this.wrappedSubtitleStreams = wss; @@ -204,6 +246,7 @@ public void onCreate(@Nullable final Bundle savedInstanceState) { } context = getContext(); + showIfStreamsWereRemovedMessage(); setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context)); Icepick.restoreInstanceState(this, savedInstanceState); @@ -277,8 +320,10 @@ public void onViewCreated(@NonNull final View view, @Nullable final Bundle saved super.onViewCreated(view, savedInstanceState); nameEditText = view.findViewById(R.id.file_name); nameEditText.setText(FilenameUtils.createFilename(getContext(), currentInfo.getName())); - selectedAudioIndex = ListHelper - .getDefaultAudioFormat(getContext(), currentInfo.getAudioStreams()); + selectedAudioIndex = ListHelper.getDefaultAudioFormat(getContext(), + wrappedAudioStreams.getStreamsList()); + final int videoStreamListSize = wrappedVideoStreams.getStreamsList().size(); + selectedVideoIndex = Math.max(0, Math.min(selectedVideoIndex, videoStreamListSize - 1)); selectedSubtitleIndex = getSubtitleIndexBy(subtitleStreamsAdapter.getAll()); @@ -898,16 +943,25 @@ private void continueSelectedDownload(@NonNull final StoredFileHelper storage) { return; } + if (!(selectedStream.getDeliveryFormat() instanceof DeliveryFormat.Direct)) { + throw new IllegalArgumentException("Unsupported stream delivery format"); + } + if (secondaryStream == null) { urls = new String[]{ - selectedStream.getUrl() + ((DeliveryFormat.Direct) selectedStream.getDeliveryFormat()).getUrl() }; recoveryInfo = new MissionRecoveryInfo[]{ new MissionRecoveryInfo(selectedStream) }; } else { + if (!(secondaryStream.getDeliveryFormat() instanceof DeliveryFormat.Direct)) { + throw new IllegalArgumentException("Unsupported stream delivery format"); + } + urls = new String[]{ - selectedStream.getUrl(), secondaryStream.getUrl() + ((DeliveryFormat.Direct) selectedStream.getDeliveryFormat()).getUrl(), + ((DeliveryFormat.Direct) secondaryStream.getDeliveryFormat()).getUrl() }; recoveryInfo = new MissionRecoveryInfo[]{new MissionRecoveryInfo(selectedStream), new MissionRecoveryInfo(secondaryStream)}; diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java index 29be402c53f..d7cdf4fbfba 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java @@ -41,7 +41,7 @@ public MediaSource resolve(@NonNull final StreamInfo info) { final AudioStream audio = info.getAudioStreams().get(index); final MediaSourceTag tag = new MediaSourceTag(info); - return buildMediaSource(dataSource, audio.getUrl(), PlayerHelper.cacheKeyOf(info, audio), + return buildMediaSource(dataSource, audio, PlayerHelper.cacheKeyOf(info, audio), MediaFormat.getSuffixById(audio.getFormatId()), tag); } } diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java index e06c0ff82f6..b383a33cd8e 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java @@ -1,19 +1,24 @@ package org.schabi.newpipe.player.resolver; import android.net.Uri; -import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.util.Util; +import com.google.android.exoplayer2.source.dash.manifest.DashManifest; +import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; +import org.schabi.newpipe.extractor.stream.DeliveryFormat; +import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.player.helper.PlayerDataSource; +import java.io.ByteArrayInputStream; +import java.io.IOException; + public interface PlaybackResolver extends Resolver { @Nullable @@ -57,29 +62,39 @@ default MediaSource buildLiveMediaSource(@NonNull final PlayerDataSource dataSou @NonNull default MediaSource buildMediaSource(@NonNull final PlayerDataSource dataSource, - @NonNull final String sourceUrl, + @NonNull final Stream sourceStream, @NonNull final String cacheKey, @NonNull final String overrideExtension, @NonNull final MediaSourceTag metadata) { - final Uri uri = Uri.parse(sourceUrl); - @C.ContentType final int type = TextUtils.isEmpty(overrideExtension) - ? Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension); + final DeliveryFormat deliveryFormat = sourceStream.getDeliveryFormat(); + if (deliveryFormat instanceof DeliveryFormat.Direct) { + final String url = ((DeliveryFormat.Direct) deliveryFormat).getUrl(); + return dataSource.getExtractorMediaSourceFactory(cacheKey).setTag(metadata) + .createMediaSource(Uri.parse(url)); - switch (type) { - case C.TYPE_SS: - return dataSource.getLiveSsMediaSourceFactory().setTag(metadata) - .createMediaSource(uri); - case C.TYPE_DASH: - return dataSource.getDashMediaSourceFactory().setTag(metadata) - .createMediaSource(uri); - case C.TYPE_HLS: - return dataSource.getHlsMediaSourceFactory().setTag(metadata) - .createMediaSource(uri); - case C.TYPE_OTHER: - return dataSource.getExtractorMediaSourceFactory(cacheKey).setTag(metadata) - .createMediaSource(uri); - default: - throw new IllegalStateException("Unsupported type: " + type); + } else if (deliveryFormat instanceof DeliveryFormat.HLS) { + final String url = ((DeliveryFormat.HLS) deliveryFormat).getUrl(); + return dataSource.getHlsMediaSourceFactory().setTag(metadata) + .createMediaSource(Uri.parse(url)); + + } else if (deliveryFormat instanceof DeliveryFormat.ManualDASH) { + final DashManifest dashManifest; + try { + final DeliveryFormat.ManualDASH manualDash = + (DeliveryFormat.ManualDASH) deliveryFormat; + final ByteArrayInputStream dashManifestInput = new ByteArrayInputStream( + manualDash.getManualDashManifest().getBytes("UTF-8")); + + dashManifest = new DashManifestParser().parse(Uri.parse(manualDash.getBaseUrl()), + dashManifestInput); + } catch (IOException e) { + throw new IllegalStateException("Error while parsing manual dash manifest", e); + } + + return dataSource.getDashMediaSourceFactory().setTag(metadata) + .createMediaSource(dashManifest); + } else { + throw new IllegalArgumentException("Unsupported delivery format: " + deliveryFormat); } } } diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java index 2eb766769bc..9e28f758665 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java @@ -69,7 +69,7 @@ public MediaSource resolve(@NonNull final StreamInfo info) { @Nullable final VideoStream video = tag.getSelectedVideoStream(); if (video != null) { - final MediaSource streamSource = buildMediaSource(dataSource, video.getUrl(), + final MediaSource streamSource = buildMediaSource(dataSource, video, PlayerHelper.cacheKeyOf(info, video), MediaFormat.getSuffixById(video.getFormatId()), tag); mediaSources.add(streamSource); @@ -82,7 +82,7 @@ public MediaSource resolve(@NonNull final StreamInfo info) { // Use the audio stream if there is no video stream, or // Merge with audio stream in case if video does not contain audio if (audio != null && (video == null || video.isVideoOnly)) { - final MediaSource audioSource = buildMediaSource(dataSource, audio.getUrl(), + final MediaSource audioSource = buildMediaSource(dataSource, audio, PlayerHelper.cacheKeyOf(info, audio), MediaFormat.getSuffixById(audio.getFormatId()), tag); mediaSources.add(audioSource); @@ -106,7 +106,7 @@ public MediaSource resolve(@NonNull final StreamInfo info) { SELECTION_FLAG_AUTOSELECT, PlayerHelper.captionLanguageOf(context, subtitle)); final MediaSource textSource = dataSource.getSampleMediaSourceFactory() - .createMediaSource(Uri.parse(subtitle.getURL()), textFormat, TIME_UNSET); + .createMediaSource(Uri.parse(subtitle.getUrl()), textFormat, TIME_UNSET); mediaSources.add(textSource); } } diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index ccaa79f9888..e648f4e7d8d 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -30,6 +30,7 @@ import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.stream.AudioStream; +import org.schabi.newpipe.extractor.stream.DeliveryFormat; import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.VideoStream; @@ -223,9 +224,21 @@ public static void playOnExternalVideoPlayer(final Context context, final Stream public static void playOnExternalPlayer(final Context context, final String name, final String artist, final Stream stream) { + final String url; + final DeliveryFormat deliveryFormat = stream.getDeliveryFormat(); + if (deliveryFormat instanceof DeliveryFormat.Direct) { + url = ((DeliveryFormat.Direct) deliveryFormat).getUrl(); + } else if (deliveryFormat instanceof DeliveryFormat.HLS) { + url = ((DeliveryFormat.HLS) deliveryFormat).getUrl(); + } else { + Toast.makeText(context, R.string.selected_stream_external_player_not_supported, + Toast.LENGTH_SHORT).show(); + return; + } + Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.parse(stream.getUrl()), stream.getFormat().getMimeType()); + intent.setDataAndType(Uri.parse(url), stream.getFormat().getMimeType()); intent.putExtra(Intent.EXTRA_TITLE, name); intent.putExtra("title", name); intent.putExtra("artist", artist); diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java index 6a244a69b67..465410934a0 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java @@ -13,6 +13,7 @@ import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.AudioStream; +import org.schabi.newpipe.extractor.stream.DeliveryFormat; import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.SubtitlesStream; import org.schabi.newpipe.extractor.stream.VideoStream; @@ -209,9 +210,15 @@ public static Single fetchSizeForWrapper( continue; } + final DeliveryFormat deliveryFormat = stream.getDeliveryFormat(); + if (!(deliveryFormat instanceof DeliveryFormat.Direct)) { + continue; + } + final long contentLength = DownloaderImpl.getInstance().getContentLength( - stream.getUrl()); + ((DeliveryFormat.Direct) deliveryFormat).getUrl()); streamsWrapper.setSize(stream, contentLength); + hasChanged = true; } return hasChanged; diff --git a/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java b/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java index 14ac392a0e9..8fc2c80ca62 100644 --- a/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java +++ b/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java @@ -6,6 +6,7 @@ import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.stream.AudioStream; +import org.schabi.newpipe.extractor.stream.DeliveryFormat; import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.stream.SubtitlesStream; import org.schabi.newpipe.extractor.stream.VideoStream; @@ -132,8 +133,9 @@ private void resolveStream() throws IOException, ExtractionException, HttpError switch (mRecovery.kind) { case 'a': for (AudioStream audio : mExtractor.getAudioStreams()) { - if (audio.average_bitrate == mRecovery.desiredBitrate && audio.getFormat() == mRecovery.format) { - url = audio.getUrl(); + if (audio.average_bitrate == mRecovery.desiredBitrate && audio.getFormat() == mRecovery.format + && audio.getDeliveryFormat() instanceof DeliveryFormat.Direct) { + url = ((DeliveryFormat.Direct) audio.getDeliveryFormat()).getUrl(); break; } } @@ -145,8 +147,9 @@ private void resolveStream() throws IOException, ExtractionException, HttpError else videoStreams = mExtractor.getVideoStreams(); for (VideoStream video : videoStreams) { - if (video.resolution.equals(mRecovery.desired) && video.getFormat() == mRecovery.format) { - url = video.getUrl(); + if (video.resolution.equals(mRecovery.desired) && video.getFormat() == mRecovery.format + && video.getDeliveryFormat() instanceof DeliveryFormat.Direct) { + url = ((DeliveryFormat.Direct) video.getDeliveryFormat()).getUrl(); break; } } @@ -154,8 +157,9 @@ private void resolveStream() throws IOException, ExtractionException, HttpError case 's': for (SubtitlesStream subtitles : mExtractor.getSubtitles(mRecovery.format)) { String tag = subtitles.getLanguageTag(); - if (tag.equals(mRecovery.desired) && subtitles.isAutoGenerated() == mRecovery.desired2) { - url = subtitles.getURL(); + if (tag.equals(mRecovery.desired) && subtitles.isAutoGenerated() == mRecovery.desired2 + && subtitles.getDeliveryFormat() instanceof DeliveryFormat.Direct) { + url = ((DeliveryFormat.Direct) subtitles.getDeliveryFormat()).getUrl(); break; } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f2e79a79b39..32c061b5a7b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -230,6 +230,8 @@ Restore defaults Do you want to restore defaults? Give permission to display over other apps + Some streams were hidden because they are not supported by the downloader yet + The selected stream is not supported by external players Sorry, that should not have happened. Guru Meditation. diff --git a/app/src/test/java/org/schabi/newpipe/util/ListHelperTest.java b/app/src/test/java/org/schabi/newpipe/util/ListHelperTest.java index 0baa2a167ac..ca900c0a3d2 100644 --- a/app/src/test/java/org/schabi/newpipe/util/ListHelperTest.java +++ b/app/src/test/java/org/schabi/newpipe/util/ListHelperTest.java @@ -3,6 +3,7 @@ import org.junit.Test; import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.stream.AudioStream; +import org.schabi.newpipe.extractor.stream.DeliveryFormat; import org.schabi.newpipe.extractor.stream.VideoStream; import java.util.ArrayList; @@ -13,36 +14,38 @@ public class ListHelperTest { private static final String BEST_RESOLUTION_KEY = "best_resolution"; + private static final DeliveryFormat DEFAULT_DELIVERY_FORMAT = DeliveryFormat.direct(""); + private static final List AUDIO_STREAMS_TEST_LIST = Arrays.asList( - new AudioStream("", MediaFormat.M4A, /**/ 128), - new AudioStream("", MediaFormat.WEBMA, /**/ 192), - new AudioStream("", MediaFormat.MP3, /**/ 64), - new AudioStream("", MediaFormat.WEBMA, /**/ 192), - new AudioStream("", MediaFormat.M4A, /**/ 128), - new AudioStream("", MediaFormat.MP3, /**/ 128), - new AudioStream("", MediaFormat.WEBMA, /**/ 64), - new AudioStream("", MediaFormat.M4A, /**/ 320), - new AudioStream("", MediaFormat.MP3, /**/ 192), - new AudioStream("", MediaFormat.WEBMA, /**/ 320)); + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 128), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MP3, /**/ 64), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 128), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MP3, /**/ 128), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 64), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 320), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MP3, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 320)); private static final List VIDEO_STREAMS_TEST_LIST = Arrays.asList( - new VideoStream("", MediaFormat.MPEG_4, /**/ "720p"), - new VideoStream("", MediaFormat.v3GPP, /**/ "240p"), - new VideoStream("", MediaFormat.WEBM, /**/ "480p"), - new VideoStream("", MediaFormat.v3GPP, /**/ "144p"), - new VideoStream("", MediaFormat.MPEG_4, /**/ "360p"), - new VideoStream("", MediaFormat.WEBM, /**/ "360p")); + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "720p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.v3GPP, /**/ "240p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBM, /**/ "480p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.v3GPP, /**/ "144p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "360p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBM, /**/ "360p")); private static final List VIDEO_ONLY_STREAMS_TEST_LIST = Arrays.asList( - new VideoStream("", MediaFormat.MPEG_4, /**/ "720p", true), - new VideoStream("", MediaFormat.MPEG_4, /**/ "720p", true), - new VideoStream("", MediaFormat.MPEG_4, /**/ "2160p", true), - new VideoStream("", MediaFormat.MPEG_4, /**/ "1440p60", true), - new VideoStream("", MediaFormat.WEBM, /**/ "720p60", true), - new VideoStream("", MediaFormat.MPEG_4, /**/ "2160p60", true), - new VideoStream("", MediaFormat.MPEG_4, /**/ "720p60", true), - new VideoStream("", MediaFormat.MPEG_4, /**/ "1080p", true), - new VideoStream("", MediaFormat.MPEG_4, /**/ "1080p60", true)); + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "720p", true), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "720p", true), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "2160p", true), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "1440p60", true), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBM, /**/ "720p60", true), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "2160p60", true), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "720p60", true), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "1080p", true), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "1080p60", true)); @Test public void getSortedStreamVideosListTest() { @@ -95,14 +98,14 @@ public void getSortedStreamVideosExceptHighResolutionsTest() { @Test public void getDefaultResolutionTest() { List testList = Arrays.asList( - new VideoStream("", MediaFormat.MPEG_4, /**/ "720p"), - new VideoStream("", MediaFormat.v3GPP, /**/ "240p"), - new VideoStream("", MediaFormat.WEBM, /**/ "480p"), - new VideoStream("", MediaFormat.WEBM, /**/ "240p"), - new VideoStream("", MediaFormat.MPEG_4, /**/ "240p"), - new VideoStream("", MediaFormat.WEBM, /**/ "144p"), - new VideoStream("", MediaFormat.MPEG_4, /**/ "360p"), - new VideoStream("", MediaFormat.WEBM, /**/ "360p")); + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "720p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.v3GPP, /**/ "240p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBM, /**/ "480p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBM, /**/ "240p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "240p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBM, /**/ "144p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "360p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBM, /**/ "360p")); VideoStream result = testList.get(ListHelper.getDefaultResolutionIndex( "720p", BEST_RESOLUTION_KEY, MediaFormat.MPEG_4, testList)); assertEquals("720p", result.resolution); @@ -177,8 +180,8 @@ public void getHighestQualityAudioFormatPreferredAbsent() { //////////////////////////////////////// List testList = Arrays.asList( - new AudioStream("", MediaFormat.M4A, /**/ 128), - new AudioStream("", MediaFormat.WEBMA, /**/ 192)); + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 128), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192)); // List doesn't contains this format // It should fallback to the highest bitrate audio no matter what format it is AudioStream stream = testList.get(ListHelper.getHighestQualityAudioIndex( @@ -191,13 +194,13 @@ public void getHighestQualityAudioFormatPreferredAbsent() { ////////////////////////////////////////////////////// testList = new ArrayList<>(Arrays.asList( - new AudioStream("", MediaFormat.WEBMA, /**/ 192), - new AudioStream("", MediaFormat.M4A, /**/ 192), - new AudioStream("", MediaFormat.WEBMA, /**/ 192), - new AudioStream("", MediaFormat.M4A, /**/ 192), - new AudioStream("", MediaFormat.WEBMA, /**/ 192), - new AudioStream("", MediaFormat.M4A, /**/ 192), - new AudioStream("", MediaFormat.WEBMA, /**/ 192))); + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192))); // List doesn't contains this format, it should fallback to the highest bitrate audio and // the highest quality format. stream = testList.get(ListHelper.getHighestQualityAudioIndex(MediaFormat.MP3, testList)); @@ -206,7 +209,7 @@ public void getHighestQualityAudioFormatPreferredAbsent() { // Adding a new format and bitrate. Adding another stream will have no impact since // it's not a prefered format. - testList.add(new AudioStream("", MediaFormat.WEBMA, /**/ 192)); + testList.add(new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192)); stream = testList.get(ListHelper.getHighestQualityAudioIndex(MediaFormat.MP3, testList)); assertEquals(192, stream.average_bitrate); assertEquals(MediaFormat.M4A, stream.getFormat()); @@ -244,8 +247,8 @@ public void getLowestQualityAudioFormatPreferredAbsent() { //////////////////////////////////////// List testList = new ArrayList<>(Arrays.asList( - new AudioStream("", MediaFormat.M4A, /**/ 128), - new AudioStream("", MediaFormat.WEBMA, /**/ 192))); + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 128), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192))); // List doesn't contains this format // It should fallback to the most compact audio no matter what format it is. AudioStream stream = testList.get(ListHelper.getMostCompactAudioIndex( @@ -254,7 +257,7 @@ public void getLowestQualityAudioFormatPreferredAbsent() { assertEquals(MediaFormat.M4A, stream.getFormat()); // WEBMA is more compact than M4A - testList.add(new AudioStream("", MediaFormat.WEBMA, /**/ 128)); + testList.add(new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 128)); stream = testList.get(ListHelper.getMostCompactAudioIndex(MediaFormat.MP3, testList)); assertEquals(128, stream.average_bitrate); assertEquals(MediaFormat.WEBMA, stream.getFormat()); @@ -264,12 +267,12 @@ public void getLowestQualityAudioFormatPreferredAbsent() { ////////////////////////////////////////////////////// testList = new ArrayList<>(Arrays.asList( - new AudioStream("", MediaFormat.WEBMA, /**/ 192), - new AudioStream("", MediaFormat.M4A, /**/ 192), - new AudioStream("", MediaFormat.WEBMA, /**/ 256), - new AudioStream("", MediaFormat.M4A, /**/ 192), - new AudioStream("", MediaFormat.WEBMA, /**/ 192), - new AudioStream("", MediaFormat.M4A, /**/ 192))); + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 256), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBMA, /**/ 192), + new AudioStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.M4A, /**/ 192))); // List doesn't contain this format // It should fallback to the most compact audio no matter what format it is. stream = testList.get(ListHelper.getMostCompactAudioIndex(MediaFormat.MP3, testList)); @@ -291,14 +294,14 @@ public void getLowestQualityAudioNull() { @Test public void getVideoDefaultStreamIndexCombinations() { List testList = Arrays.asList( - new VideoStream("", MediaFormat.MPEG_4, /**/ "1080p"), - new VideoStream("", MediaFormat.MPEG_4, /**/ "720p60"), - new VideoStream("", MediaFormat.MPEG_4, /**/ "720p"), - new VideoStream("", MediaFormat.WEBM, /**/ "480p"), - new VideoStream("", MediaFormat.MPEG_4, /**/ "360p"), - new VideoStream("", MediaFormat.WEBM, /**/ "360p"), - new VideoStream("", MediaFormat.v3GPP, /**/ "240p60"), - new VideoStream("", MediaFormat.WEBM, /**/ "144p")); + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "1080p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "720p60"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "720p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBM, /**/ "480p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.MPEG_4, /**/ "360p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBM, /**/ "360p"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.v3GPP, /**/ "240p60"), + new VideoStream(DEFAULT_DELIVERY_FORMAT, MediaFormat.WEBM, /**/ "144p")); // exact matches assertEquals(1, ListHelper.getVideoStreamIndex("720p60", MediaFormat.MPEG_4, testList)); From d0449ab3c64c1ecd64a68a6a2708619bc4eded03 Mon Sep 17 00:00:00 2001 From: Mauricio Colli Date: Wed, 24 Jun 2020 01:39:30 -0300 Subject: [PATCH 2/5] Call the correct dismiss method for the DownloadDialog --- .../main/java/org/schabi/newpipe/download/DownloadDialog.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index 4d21babf8bc..a6ef00f07fc 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -241,7 +241,7 @@ public void onCreate(@Nullable final Bundle savedInstanceState) { if (!PermissionHelper.checkStoragePermissions(getActivity(), PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { - getDialog().dismiss(); + dismiss(); return; } @@ -572,7 +572,7 @@ protected void setupDownloadOptions() { } else { Toast.makeText(getContext(), R.string.no_streams_available_download, Toast.LENGTH_SHORT).show(); - getDialog().dismiss(); + dismiss(); } } From 9970d8dc76b29f64a08eaef0f5510ba9f0d54431 Mon Sep 17 00:00:00 2001 From: Mauricio Colli Date: Wed, 24 Jun 2020 01:39:31 -0300 Subject: [PATCH 3/5] Temporary fix for trying to download an item with no supported streams --- .../java/org/schabi/newpipe/RouterActivity.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 39f6b217dfd..9a3af2984fc 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -473,9 +473,15 @@ private void handleChoice(final String selectedChoiceKey) { finish(); } + private Disposable downloadDisposable; + @SuppressLint("CheckResult") private void openDownloadDialog() { - ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true) + if (downloadDisposable != null) { + return; + } + + downloadDisposable = ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe((@NonNull StreamInfo result) -> { @@ -492,9 +498,14 @@ private void openDownloadDialog() { downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex); downloadDialog.show(fm, "downloadDialog"); fm.executePendingTransactions(); - downloadDialog.getDialog().setOnDismissListener(dialog -> { + // TODO: Improve this dialog dismiss logic + if (downloadDialog.getDialog() != null) { + downloadDialog.getDialog().setOnDismissListener(dialog -> { + finish(); + }); + } else { finish(); - }); + } }, (@NonNull Throwable throwable) -> { onError(); }); From 34a84bbced28de14b8e8da44fb3e2894845fc56b Mon Sep 17 00:00:00 2001 From: Mauricio Colli Date: Wed, 24 Jun 2020 01:39:32 -0300 Subject: [PATCH 4/5] [SoundCloud] Lower the expiration time Streams delivered by SoundCloud are not valid for very long, so a lower expiration time is needed. --- app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java b/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java index dacf7d8442c..630f3deb353 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java @@ -153,7 +153,7 @@ private static void setSelectedServicePreferences(final Context context, public static long getCacheExpirationMillis(final int serviceId) { if (serviceId == SoundCloud.getServiceId()) { - return TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES); + return TimeUnit.MILLISECONDS.convert(2, TimeUnit.MINUTES); } else { return TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS); } From efbf79fe12c77f511b8338fc649bff05ac2b4c64 Mon Sep 17 00:00:00 2001 From: Mauricio Colli Date: Wed, 24 Jun 2020 01:39:33 -0300 Subject: [PATCH 5/5] TEMP: Extractor Version --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 25e8747baf8..b64172f5b0d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -163,7 +163,7 @@ dependencies { exclude module: 'support-annotations' } - implementation 'com.github.TeamNewPipe:NewPipeExtractor:c234a6d2bdce228ed3a9e84952e7389afca13fa8' + implementation 'com.github.mauriciocolli:NewPipeExtractor:c234a6d2bdce228ed3a9e84952e7389afca13fa8' implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751" implementation "org.jsoup:jsoup:1.13.1"