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 9f46f7f6b..4fb47496b 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -69,7 +69,6 @@ import org.schabi.newpipe.util.ThemeHelper; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -84,7 +83,7 @@ import us.shandian.giga.service.DownloadManagerService; import us.shandian.giga.service.DownloadManagerService.DownloadManagerBinder; import us.shandian.giga.service.MissionState; -import static org.schabi.newpipe.util.ListHelper.keepStreamsWithDelivery; +import static org.schabi.newpipe.util.ListHelper.getStreamsOfSpecifiedDelivery; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; public class DownloadDialog extends DialogFragment @@ -149,25 +148,24 @@ public class DownloadDialog extends DialogFragment public static DownloadDialog newInstance(final Context context, @NonNull final StreamInfo info) { // TODO: Adapt this code when the downloader support other types of stream deliveries - final List videoStreams = new ArrayList<>(info.getVideoStreams()); final List progressiveHttpVideoStreams = - keepStreamsWithDelivery(videoStreams, DeliveryMethod.PROGRESSIVE_HTTP); + getStreamsOfSpecifiedDelivery(info.getVideoStreams(), + DeliveryMethod.PROGRESSIVE_HTTP); - final List videoOnlyStreams = new ArrayList<>(info.getVideoOnlyStreams()); final List progressiveHttpVideoOnlyStreams = - keepStreamsWithDelivery(videoOnlyStreams, DeliveryMethod.PROGRESSIVE_HTTP); + getStreamsOfSpecifiedDelivery(info.getVideoOnlyStreams(), + DeliveryMethod.PROGRESSIVE_HTTP); - final List audioStreams = new ArrayList<>(info.getAudioStreams()); final List progressiveHttpAudioStreams = - keepStreamsWithDelivery(audioStreams, DeliveryMethod.PROGRESSIVE_HTTP); + getStreamsOfSpecifiedDelivery(info.getAudioStreams(), + DeliveryMethod.PROGRESSIVE_HTTP); - final List subtitlesStreams = new ArrayList<>(info.getSubtitles()); final List progressiveHttpSubtitlesStreams = - keepStreamsWithDelivery(subtitlesStreams, DeliveryMethod.PROGRESSIVE_HTTP); + getStreamsOfSpecifiedDelivery(info.getSubtitles(), + DeliveryMethod.PROGRESSIVE_HTTP); - final List videoStreamsList = new ArrayList<>( - ListHelper.getSortedStreamVideosList(context, progressiveHttpVideoStreams, - progressiveHttpVideoOnlyStreams, false, false)); + final List videoStreamsList = ListHelper.getSortedStreamVideosList(context, + progressiveHttpVideoStreams, progressiveHttpVideoOnlyStreams, false, false); final DownloadDialog instance = new DownloadDialog(); instance.setInfo(info); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index bb09681f5..ff2114b83 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -31,6 +31,7 @@ import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.RelativeLayout; +import android.widget.Toast; import androidx.annotation.AttrRes; import androidx.annotation.NonNull; @@ -122,7 +123,7 @@ import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientat import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired; import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; -import static org.schabi.newpipe.util.ListHelper.removeNonUrlAndTorrentStreams; +import static org.schabi.newpipe.util.ListHelper.getNonUrlAndNonTorrentStreams; public final class VideoDetailFragment extends BaseStateFragment @@ -1092,9 +1093,6 @@ public final class VideoDetailFragment } private void openBackgroundPlayer(final boolean append) { - final AudioStream audioStream = currentInfo.getAudioStreams() - .get(ListHelper.getDefaultAudioFormat(activity, currentInfo.getAudioStreams())); - final boolean useExternalAudioPlayer = PreferenceManager .getDefaultSharedPreferences(activity) .getBoolean(activity.getString(R.string.use_external_audio_player_key), false); @@ -1109,7 +1107,17 @@ public final class VideoDetailFragment if (!useExternalAudioPlayer) { openNormalBackgroundPlayer(append); } else { - startOnExternalPlayer(activity, currentInfo, audioStream); + final List audioStreams = getNonUrlAndNonTorrentStreams( + currentInfo.getAudioStreams()); + final int index = ListHelper.getDefaultAudioFormat(activity, audioStreams); + + if (index == -1) { + Toast.makeText(activity, R.string.no_audio_streams_available_for_external_players, + Toast.LENGTH_SHORT).show(); + return; + } + + startOnExternalPlayer(activity, currentInfo, audioStreams.get(index)); } } @@ -2147,10 +2155,10 @@ public final class VideoDetailFragment return; } - final List videoStreams = removeNonUrlAndTorrentStreams( - new ArrayList<>(currentInfo.getVideoStreams())); - final List videoOnlyStreams = removeNonUrlAndTorrentStreams( - new ArrayList<>(currentInfo.getVideoOnlyStreams())); + final List videoStreams = getNonUrlAndNonTorrentStreams( + currentInfo.getVideoStreams()); + final List videoOnlyStreams = getNonUrlAndNonTorrentStreams( + currentInfo.getVideoOnlyStreams()); final AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.select_quality_external_players); 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 85c15faf1..3e166c339 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 @@ -1,6 +1,6 @@ package org.schabi.newpipe.player.resolver; -import static org.schabi.newpipe.util.ListHelper.removeTorrentStreams; +import static org.schabi.newpipe.util.ListHelper.getNonTorrentStreams; import android.content.Context; import android.util.Log; @@ -18,7 +18,6 @@ import org.schabi.newpipe.player.mediaitem.StreamInfoTag; import org.schabi.newpipe.util.ListHelper; import java.io.IOException; -import java.util.ArrayList; import java.util.List; public class AudioPlaybackResolver implements PlaybackResolver { @@ -43,8 +42,7 @@ public class AudioPlaybackResolver implements PlaybackResolver { return liveSource; } - final List audioStreams = new ArrayList<>(info.getAudioStreams()); - removeTorrentStreams(audioStreams); + final List audioStreams = getNonTorrentStreams(info.getAudioStreams()); final int index = ListHelper.getDefaultAudioFormat(context, audioStreams); if (index < 0 || index >= info.getAudioStreams().size()) { 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 317c49fc9..fd00d0ed9 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 @@ -29,8 +29,8 @@ import java.util.List; import java.util.Optional; import static com.google.android.exoplayer2.C.TIME_UNSET; -import static org.schabi.newpipe.util.ListHelper.removeNonUrlAndTorrentStreams; -import static org.schabi.newpipe.util.ListHelper.removeTorrentStreams; +import static org.schabi.newpipe.util.ListHelper.getNonUrlAndNonTorrentStreams; +import static org.schabi.newpipe.util.ListHelper.getNonTorrentStreams; public class VideoPlaybackResolver implements PlaybackResolver { private static final String TAG = VideoPlaybackResolver.class.getSimpleName(); @@ -70,24 +70,21 @@ public class VideoPlaybackResolver implements PlaybackResolver { } final List mediaSources = new ArrayList<>(); - final List videoStreams = new ArrayList<>(info.getVideoStreams()); - final List videoOnlyStreams = new ArrayList<>(info.getVideoOnlyStreams()); - - removeTorrentStreams(videoStreams); - removeTorrentStreams(videoOnlyStreams); // Create video stream source - final List videos = ListHelper.getSortedStreamVideosList(context, - videoStreams, videoOnlyStreams, false, true); + final List videoStreamsList = ListHelper.getSortedStreamVideosList(context, + getNonTorrentStreams(info.getVideoStreams()), + getNonTorrentStreams(info.getVideoOnlyStreams()), false, true); final int index; - if (videos.isEmpty()) { + if (videoStreamsList.isEmpty()) { index = -1; } else if (playbackQuality == null) { - index = qualityResolver.getDefaultResolutionIndex(videos); + index = qualityResolver.getDefaultResolutionIndex(videoStreamsList); } else { - index = qualityResolver.getOverrideResolutionIndex(videos, getPlaybackQuality()); + index = qualityResolver.getOverrideResolutionIndex(videoStreamsList, + getPlaybackQuality()); } - final MediaItemTag tag = StreamInfoTag.of(info, videos, index); + final MediaItemTag tag = StreamInfoTag.of(info, videoStreamsList, index); @Nullable final VideoStream video = tag.getMaybeQuality() .map(MediaItemTag.Quality::getSelectedVideoStream) .orElse(null); @@ -104,8 +101,7 @@ public class VideoPlaybackResolver implements PlaybackResolver { } // Create optional audio stream source - final List audioStreams = info.getAudioStreams(); - removeTorrentStreams(audioStreams); + final List audioStreams = getNonTorrentStreams(info.getAudioStreams()); final AudioStream audio = audioStreams.isEmpty() ? null : audioStreams.get( ListHelper.getDefaultAudioFormat(context, audioStreams)); @@ -129,13 +125,14 @@ public class VideoPlaybackResolver implements PlaybackResolver { if (mediaSources.isEmpty()) { return null; } + // Below are auxiliary media sources // Create subtitle sources final List subtitlesStreams = info.getSubtitles(); if (subtitlesStreams != null) { // Torrent and non URL subtitles are not supported by ExoPlayer - final List nonTorrentAndUrlStreams = removeNonUrlAndTorrentStreams( + final List nonTorrentAndUrlStreams = getNonUrlAndNonTorrentStreams( subtitlesStreams); for (final SubtitlesStream subtitle : nonTorrentAndUrlStreams) { final MediaFormat mediaFormat = subtitle.getFormat(); diff --git a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java index 3a03e0b30..33c7a2f49 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java @@ -23,10 +23,10 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; public final class ListHelper { @@ -116,27 +116,17 @@ public final class ListHelper { * Return a {@link Stream} list which uses the given delivery method from a {@link Stream} * list. * - * @param streamList the original stream list - * @param deliveryMethod the delivery method + * @param streamList the original {@link Stream stream} list + * @param deliveryMethod the {@link DeliveryMethod delivery method} * @param the item type's class that extends {@link Stream} - * @return a stream list which uses the given delivery method + * @return a {@link Stream stream} list which uses the given delivery method */ @NonNull - public static List keepStreamsWithDelivery( - @NonNull final List streamList, + public static List getStreamsOfSpecifiedDelivery( + final List streamList, final DeliveryMethod deliveryMethod) { - if (streamList.isEmpty()) { - return Collections.emptyList(); - } - - final Iterator streamListIterator = streamList.iterator(); - while (streamListIterator.hasNext()) { - if (streamListIterator.next().getDeliveryMethod() != deliveryMethod) { - streamListIterator.remove(); - } - } - - return streamList; + return getFilteredStreamList(streamList, + stream -> stream.getDeliveryMethod() == deliveryMethod); } /** @@ -147,21 +137,10 @@ public final class ListHelper { * @return a stream list which only contains URL streams and non-torrent streams */ @NonNull - public static List removeNonUrlAndTorrentStreams( - @NonNull final List streamList) { - if (streamList.isEmpty()) { - return Collections.emptyList(); - } - - final Iterator streamListIterator = streamList.iterator(); - while (streamListIterator.hasNext()) { - final S stream = streamListIterator.next(); - if (!stream.isUrl() || stream.getDeliveryMethod() == DeliveryMethod.TORRENT) { - streamListIterator.remove(); - } - } - - return streamList; + public static List getNonUrlAndNonTorrentStreams( + final List streamList) { + return getFilteredStreamList(streamList, + stream -> stream.isUrl() && stream.getDeliveryMethod() != DeliveryMethod.TORRENT); } /** @@ -172,21 +151,10 @@ public final class ListHelper { * @return a stream list which only contains non-torrent streams */ @NonNull - public static List removeTorrentStreams( - @NonNull final List streamList) { - if (streamList.isEmpty()) { - return Collections.emptyList(); - } - - final Iterator streamListIterator = streamList.iterator(); - while (streamListIterator.hasNext()) { - final S stream = streamListIterator.next(); - if (stream.getDeliveryMethod() == DeliveryMethod.TORRENT) { - streamListIterator.remove(); - } - } - - return streamList; + public static List getNonTorrentStreams( + final List streamList) { + return getFilteredStreamList(streamList, + stream -> stream.getDeliveryMethod() != DeliveryMethod.TORRENT); } /** @@ -224,6 +192,26 @@ public final class ListHelper { // Utils //////////////////////////////////////////////////////////////////////////*/ + /** + * Get a filtered stream list, by using Java 8 Stream's API and the given predicate. + * + * @param streamList the stream list to filter + * @param streamListPredicate the predicate which will be used to filter streams + * @param the item type's class that extends {@link Stream} + * @return a new stream list filtered using the given predicate + */ + private static List getFilteredStreamList( + final List streamList, + final Predicate streamListPredicate) { + if (streamList == null) { + return Collections.emptyList(); + } + + return streamList.stream() + .filter(streamListPredicate) + .collect(Collectors.toList()); + } + private static String computeDefaultResolution(final Context context, final int key, final int value) { final SharedPreferences preferences 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 c3246857e..ffc7433a0 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -63,7 +63,7 @@ import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.List; -import static org.schabi.newpipe.util.ListHelper.removeNonUrlAndTorrentStreams; +import static org.schabi.newpipe.util.ListHelper.getNonUrlAndNonTorrentStreams; public final class NavigationHelper { public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag"; @@ -221,39 +221,42 @@ public final class NavigationHelper { public static void playOnExternalAudioPlayer(@NonNull final Context context, @NonNull final StreamInfo info) { final List audioStreams = info.getAudioStreams(); - if (audioStreams.isEmpty()) { + if (audioStreams == null || audioStreams.isEmpty()) { Toast.makeText(context, R.string.audio_streams_empty, Toast.LENGTH_SHORT).show(); return; } - final List audioStreamsForExternalPlayers = removeNonUrlAndTorrentStreams( - audioStreams); + + final List audioStreamsForExternalPlayers = + getNonUrlAndNonTorrentStreams(audioStreams); if (audioStreamsForExternalPlayers.isEmpty()) { Toast.makeText(context, R.string.no_audio_streams_available_for_external_players, Toast.LENGTH_SHORT).show(); return; } - final int index = ListHelper.getDefaultAudioFormat(context, - audioStreamsForExternalPlayers); + final int index = ListHelper.getDefaultAudioFormat(context, audioStreamsForExternalPlayers); final AudioStream audioStream = audioStreamsForExternalPlayers.get(index); + playOnExternalPlayer(context, info.getName(), info.getUploaderName(), audioStream); } public static void playOnExternalVideoPlayer(final Context context, @NonNull final StreamInfo info) { final List videoStreams = info.getVideoStreams(); - if (videoStreams.isEmpty()) { + if (videoStreams == null || videoStreams.isEmpty()) { Toast.makeText(context, R.string.video_streams_empty, Toast.LENGTH_SHORT).show(); return; } + final List videoStreamsForExternalPlayers = ListHelper.getSortedStreamVideosList(context, - removeNonUrlAndTorrentStreams(videoStreams), null, false, false); + getNonUrlAndNonTorrentStreams(videoStreams), null, false, false); if (videoStreamsForExternalPlayers.isEmpty()) { Toast.makeText(context, R.string.no_video_streams_available_for_external_players, Toast.LENGTH_SHORT).show(); return; } + final int index = ListHelper.getDefaultResolutionIndex(context, videoStreamsForExternalPlayers); @@ -267,42 +270,41 @@ public final class NavigationHelper { @NonNull final Stream stream) { final DeliveryMethod deliveryMethod = stream.getDeliveryMethod(); final String mimeType; - if (deliveryMethod == DeliveryMethod.PROGRESSIVE_HTTP) { - if (stream.getFormat() != null) { - mimeType = stream.getFormat().getMimeType(); - } else { - if (stream instanceof AudioStream) { - mimeType = "audio/*"; - } else if (stream instanceof VideoStream) { - mimeType = "video/*"; + + if (!stream.isUrl() || deliveryMethod == DeliveryMethod.TORRENT) { + Toast.makeText(context, R.string.selected_stream_external_player_not_supported, + Toast.LENGTH_SHORT).show(); + return; + } + + switch (deliveryMethod) { + case PROGRESSIVE_HTTP: + if (stream.getFormat() == null) { + if (stream instanceof AudioStream) { + mimeType = "audio/*"; + } else if (stream instanceof VideoStream) { + mimeType = "video/*"; + } else { + // This should never be reached, because subtitles are not opened in + // external players + return; + } } else { - // This should never be reached, because subtitles are not opened in external - // players - return; + mimeType = stream.getFormat().getMimeType(); } - } - } else { - if (!stream.isUrl() || deliveryMethod == DeliveryMethod.TORRENT) { - Toast.makeText(context, R.string.selected_stream_external_player_not_supported, - Toast.LENGTH_SHORT).show(); - return; - } else { - switch (deliveryMethod) { - case HLS: - mimeType = "application/x-mpegURL"; - break; - case DASH: - mimeType = "application/dash+xml"; - break; - case SS: - mimeType = "application/vnd.ms-sstr+xml"; - break; - default: - // Progressive HTTP streams are handled above and torrents streams are not - // exposed to external players - mimeType = ""; - } - } + break; + case HLS: + mimeType = "application/x-mpegURL"; + break; + case DASH: + mimeType = "application/dash+xml"; + break; + case SS: + mimeType = "application/vnd.ms-sstr+xml"; + break; + default: + // Torrent streams are not exposed to external players + mimeType = ""; } final Intent intent = new Intent();