diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/ExportPlaylist.java b/app/src/main/java/org/schabi/newpipe/local/playlist/ExportPlaylist.java deleted file mode 100644 index 94002dd5b..000000000 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/ExportPlaylist.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.schabi.newpipe.local.playlist; - -import static com.google.common.collect.Streams.stream; -import static org.apache.commons.collections4.IterableUtils.reversedIterable; -import static java.util.Collections.reverse; - -import android.content.Context; - -import org.schabi.newpipe.R; -import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; -import org.schabi.newpipe.database.stream.model.StreamEntity; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import okhttp3.HttpUrl; - - - -final class ExportPlaylist { - - private ExportPlaylist() { - } - - static String export(final PlayListShareMode shareMode, - final List playlist, - final Context context) { - - return switch (shareMode) { - - case WITH_TITLES -> exportWithTitles(playlist, context); - case JUST_URLS -> exportJustUrls(playlist); - case YOUTUBE_TEMP_PLAYLIST -> exportAsYoutubeTempPlaylist(playlist); - }; - } - - static String exportWithTitles(final List playlist, - final Context context) { - - return playlist.stream() - .map(PlaylistStreamEntry::getStreamEntity) - .map(entity -> context.getString(R.string.video_details_list_item, - entity.getTitle(), - entity.getUrl() - ) - ) - .collect(Collectors.joining("\n")); - } - - static String exportJustUrls(final List playlist) { - - return playlist.stream() - .map(PlaylistStreamEntry::getStreamEntity) - .map(StreamEntity::getUrl) - .collect(Collectors.joining("\n")); - } - - static String exportAsYoutubeTempPlaylist(final List playlist) { - - final List videoIDs = - stream(reversedIterable(playlist)) - .map(PlaylistStreamEntry::getStreamEntity) - .map(entity -> getYouTubeId(entity.getUrl())) - .filter(Objects::nonNull) - .limit(50) - .collect(Collectors.toList()); - - reverse(videoIDs); - - final String commaSeparatedVideoIDs = videoIDs.stream() - .collect(Collectors.joining(",")); - - return "http://www.youtube.com/watch_videos?video_ids=" + commaSeparatedVideoIDs; - } - - /** - * Gets the video id from a YouTube URL. - * - * @param url YouTube URL - * @return the video id - */ - static String getYouTubeId(final String url) { - - final HttpUrl httpUrl = HttpUrl.parse(url); - - return httpUrl == null ? null - : httpUrl.queryParameter("v"); - } -} diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/ExportPlaylist.kt b/app/src/main/java/org/schabi/newpipe/local/playlist/ExportPlaylist.kt new file mode 100644 index 000000000..953cb7d17 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/ExportPlaylist.kt @@ -0,0 +1,72 @@ +package org.schabi.newpipe.local.playlist + +import android.content.Context +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import org.schabi.newpipe.R +import org.schabi.newpipe.database.playlist.PlaylistStreamEntry +import org.schabi.newpipe.local.playlist.PlayListShareMode.JUST_URLS +import org.schabi.newpipe.local.playlist.PlayListShareMode.WITH_TITLES +import org.schabi.newpipe.local.playlist.PlayListShareMode.YOUTUBE_TEMP_PLAYLIST +import java.util.Objects.nonNull + +fun export( + shareMode: PlayListShareMode, + playlist: List, + context: Context +): String { + return when (shareMode) { + WITH_TITLES -> exportWithTitles(playlist, context) + JUST_URLS -> exportJustUrls(playlist) + YOUTUBE_TEMP_PLAYLIST -> exportAsYoutubeTempPlaylist(playlist) + } +} + +fun exportWithTitles( + playlist: List, + context: Context +): String { + + return playlist.asSequence() + .map { it.streamEntity } + .map { entity -> + context.getString( + R.string.video_details_list_item, + entity.title, + entity.url + ) + } + .joinToString(separator = "\n") +} + +fun exportJustUrls(playlist: List): String { + + return playlist.asSequence() + .map { it.streamEntity.url } + .joinToString(separator = "\n") +} + +fun exportAsYoutubeTempPlaylist(playlist: List): String { + + val videoIDs = playlist.asReversed().asSequence() + .map { it.streamEntity } + .map { getYouTubeId(it.url) } + .filter(::nonNull) + .take(50) + .toList() + .asReversed() + .joinToString(separator = ",") + + return "http://www.youtube.com/watch_videos?video_ids=$videoIDs" +} + +/** + * Gets the video id from a YouTube URL. + * + * @param url YouTube URL + * @return the video id + */ +fun getYouTubeId(url: String): String? { + val httpUrl = url.toHttpUrlOrNull() + + return httpUrl?.queryParameter("v") +} diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index 812d9fb38..61c12bfd4 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -2,7 +2,7 @@ package org.schabi.newpipe.local.playlist; import static org.schabi.newpipe.error.ErrorUtil.showUiErrorSnackbar; import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.local.playlist.ExportPlaylist.export; +import static org.schabi.newpipe.local.playlist.ExportPlaylistKt.export; import static org.schabi.newpipe.local.playlist.PlayListShareMode.JUST_URLS; import static org.schabi.newpipe.local.playlist.PlayListShareMode.WITH_TITLES; import static org.schabi.newpipe.local.playlist.PlayListShareMode.YOUTUBE_TEMP_PLAYLIST; diff --git a/app/src/test/java/org/schabi/newpipe/local/playlist/ExportPlaylistTest.java b/app/src/test/java/org/schabi/newpipe/local/playlist/ExportPlaylistTest.java deleted file mode 100644 index f90fd78bb..000000000 --- a/app/src/test/java/org/schabi/newpipe/local/playlist/ExportPlaylistTest.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.schabi.newpipe.local.playlist; - -import static org.schabi.newpipe.local.playlist.ExportPlaylist.export; -import static org.schabi.newpipe.local.playlist.PlayListShareMode.JUST_URLS; -import static org.schabi.newpipe.local.playlist.PlayListShareMode.YOUTUBE_TEMP_PLAYLIST; - -import androidx.annotation.NonNull; - -import org.junit.Assert; -import org.junit.Test; -import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; -import org.schabi.newpipe.database.stream.model.StreamEntity; -import org.schabi.newpipe.extractor.stream.StreamType; - -import java.util.List; -import java.util.stream.Stream; - -public class ExportPlaylistTest { - - @Test - public void exportAsYouTubeTempPlaylist() { - - final List playlist = asPlaylist( - - "https://www.youtube.com/watch?v=1", - "https://soundcloud.com/cautious-clayofficial/cold-war-2", // non-Youtube URLs should be - "https://www.youtube.com/watch?v=2", // ignored - "https://www.youtube.com/watch?v=3" - ); - - final String url = export(YOUTUBE_TEMP_PLAYLIST, playlist, null); - - Assert.assertEquals("http://www.youtube.com/watch_videos?video_ids=1,2,3", url); - } - - @Test - public void exportMoreThan50Items() { - /* - * Playlist has more than 50 items => take the last 50 - * (YouTube limitation) - */ - - final List ids = List.of( - - -1, 0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 - ); - - final List playlist = asPlaylist( - - ids.stream() - .map(id -> "https://www.youtube.com/watch?v=" + id) - ); - - final String url = export(YOUTUBE_TEMP_PLAYLIST, playlist, null); - - Assert.assertEquals( - - "http://www.youtube.com/watch_videos?video_ids=" - + "1,2,3,4,5,6,7,8,9,10," - + "11,12,13,14,15,16,17,18,19,20," - + "21,22,23,24,25,26,27,28,29,30," - + "31,32,33,34,35,36,37,38,39,40," - + "41,42,43,44,45,46,47,48,49,50", - - url - ); - } - - @Test - public void exportJustUrls() { - - final List playlist = asPlaylist( - - "https://www.youtube.com/watch?v=1", - "https://www.youtube.com/watch?v=2", - "https://www.youtube.com/watch?v=3" - ); - - final String exported = export(JUST_URLS, playlist, null); - - Assert.assertEquals(""" - https://www.youtube.com/watch?v=1 - https://www.youtube.com/watch?v=2 - https://www.youtube.com/watch?v=3""", exported); - } - - @NonNull - static List asPlaylist(final String... urls) { - - return asPlaylist(Stream.of(urls)); - } - - @NonNull - static List asPlaylist(final Stream urls) { - - return urls - .map(ExportPlaylistTest::newPlaylistStreamEntry) - .toList(); - } - - @NonNull - private static PlaylistStreamEntry newPlaylistStreamEntry(final String url) { - - return new PlaylistStreamEntry(newStreamEntity(url), 0, 0, 0); - } - - @NonNull - static StreamEntity newStreamEntity(final String url) { - - return new StreamEntity( - - 0, - 1, - url, - "Title", - StreamType.VIDEO_STREAM, - 100, - "Uploader", - null, - null, - null, - null, - null, - null - ); - } -} diff --git a/app/src/test/java/org/schabi/newpipe/local/playlist/ExportPlaylistTest.kt b/app/src/test/java/org/schabi/newpipe/local/playlist/ExportPlaylistTest.kt new file mode 100644 index 000000000..42622fe9c --- /dev/null +++ b/app/src/test/java/org/schabi/newpipe/local/playlist/ExportPlaylistTest.kt @@ -0,0 +1,110 @@ +package org.schabi.newpipe.local.playlist + +import android.content.Context +import org.junit.Assert.assertEquals +import org.junit.Test +import org.mockito.Mockito.mock +import org.schabi.newpipe.database.playlist.PlaylistStreamEntry +import org.schabi.newpipe.database.stream.model.StreamEntity +import org.schabi.newpipe.extractor.stream.StreamType +import org.schabi.newpipe.local.playlist.PlayListShareMode.JUST_URLS +import org.schabi.newpipe.local.playlist.PlayListShareMode.YOUTUBE_TEMP_PLAYLIST +import java.util.stream.Stream + +class ExportPlaylistTest { + + @Test + fun exportAsYouTubeTempPlaylist() { + val playlist = asPlaylist( + "https://www.youtube.com/watch?v=1", + "https://soundcloud.com/cautious-clayofficial/cold-war-2", // non-Youtube URLs should be ignored + "https://www.youtube.com/watch?v=2", + "https://www.youtube.com/watch?v=3" + ) + + val url = export(YOUTUBE_TEMP_PLAYLIST, playlist, mock(Context::class.java)) + + assertEquals("http://www.youtube.com/watch_videos?video_ids=1,2,3", url) + } + + @Test + fun exportMoreThan50Items() { + /* + * Playlist has more than 50 items => take the last 50 + * (YouTube limitation) + */ + + val ids = listOf( + -1, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 + ) + + val playlist = asPlaylist( + ids.stream() + .map { id: Int -> "https://www.youtube.com/watch?v=$id" } + ) + + val url = export(YOUTUBE_TEMP_PLAYLIST, playlist, mock(Context::class.java)) + + assertEquals( + "http://www.youtube.com/watch_videos?video_ids=" + + "1,2,3,4,5,6,7,8,9,10," + + "11,12,13,14,15,16,17,18,19,20," + + "21,22,23,24,25,26,27,28,29,30," + + "31,32,33,34,35,36,37,38,39,40," + + "41,42,43,44,45,46,47,48,49,50", + + url + ) + } + + @Test + fun exportJustUrls() { + val playlist = asPlaylist( + "https://www.youtube.com/watch?v=1", + "https://www.youtube.com/watch?v=2", + "https://www.youtube.com/watch?v=3" + ) + + val exported = export(JUST_URLS, playlist, mock(Context::class.java)) + + assertEquals( + """ + https://www.youtube.com/watch?v=1 + https://www.youtube.com/watch?v=2 + https://www.youtube.com/watch?v=3 + """.trimIndent(), + exported + ) + } +} + +fun asPlaylist(vararg urls: String): List { + return asPlaylist(Stream.of(*urls)) +} + +fun asPlaylist(urls: Stream): List { + return urls + .map { url: String -> newPlaylistStreamEntry(url) } + .toList() +} + +fun newPlaylistStreamEntry(url: String): PlaylistStreamEntry { + return PlaylistStreamEntry(newStreamEntity(url), 0, 0, 0) +} + +fun newStreamEntity(url: String): StreamEntity { + return StreamEntity( + 0, + 1, + url, + "Title", + StreamType.VIDEO_STREAM, + 100, + "Uploader" + ) +}