From b062c6165ea7e4cb00edc45e2d6e2d7576dbdf92 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Thu, 5 Dec 2024 21:41:35 +0100 Subject: [PATCH] refactor: simplify and improve playlist export logic --- .../github/libretube/api/PlaylistsHelper.kt | 33 +--------- .../github/libretube/helpers/ImportHelper.kt | 66 ++++++++++++++----- 2 files changed, 54 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/com/github/libretube/api/PlaylistsHelper.kt b/app/src/main/java/com/github/libretube/api/PlaylistsHelper.kt index 5e7629a5c..5fe00f3f6 100644 --- a/app/src/main/java/com/github/libretube/api/PlaylistsHelper.kt +++ b/app/src/main/java/com/github/libretube/api/PlaylistsHelper.kt @@ -14,10 +14,7 @@ import com.github.libretube.extensions.parallelMap import com.github.libretube.extensions.toID import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.helpers.ProxyHelper -import com.github.libretube.obj.FreeTubeImportPlaylist -import com.github.libretube.obj.FreeTubeVideo import com.github.libretube.obj.PipedImportPlaylist -import com.github.libretube.ui.dialogs.ShareDialog.Companion.YOUTUBE_FRONTEND_URL import com.github.libretube.util.deArrow import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -201,35 +198,11 @@ object PlaylistsHelper { } } - suspend fun exportPipedPlaylists(): List = withContext(Dispatchers.IO) { - getPlaylists() - .map { async { getPlaylist(it.id!!) } } - .awaitAll() - .map { - val videos = it.relatedStreams.map { item -> - "$YOUTUBE_FRONTEND_URL/watch?v=${item.url!!.toID()}" - } - PipedImportPlaylist(it.name, "playlist", "private", videos) - } - } - - suspend fun exportFreeTubePlaylists(): List = + suspend fun getAllPlaylistsWithVideos(playlistIds: List? = null): List = withContext(Dispatchers.IO) { - getPlaylists() - .map { async { getPlaylist(it.id!!) } } + (playlistIds ?: getPlaylists().map { it.id!! }) + .map { async { getPlaylist(it) } } .awaitAll() - .map { playlist -> - val videos = playlist.relatedStreams.map { videoInfo -> - FreeTubeVideo( - videoId = videoInfo.url.orEmpty().toID(), - title = videoInfo.title.orEmpty(), - author = videoInfo.uploaderName.orEmpty(), - authorId = videoInfo.uploaderUrl.orEmpty().toID(), - lengthSeconds = videoInfo.duration ?: 0L - ) - } - FreeTubeImportPlaylist(playlist.name.orEmpty(), videos) - } } suspend fun clonePlaylist(playlistId: String): String? { diff --git a/app/src/main/java/com/github/libretube/helpers/ImportHelper.kt b/app/src/main/java/com/github/libretube/helpers/ImportHelper.kt index e141107de..cef160301 100644 --- a/app/src/main/java/com/github/libretube/helpers/ImportHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/ImportHelper.kt @@ -15,8 +15,10 @@ import com.github.libretube.db.DatabaseHolder.Database import com.github.libretube.db.obj.WatchHistoryItem import com.github.libretube.enums.ImportFormat import com.github.libretube.extensions.TAG +import com.github.libretube.extensions.toID import com.github.libretube.extensions.toastFromMainDispatcher import com.github.libretube.obj.FreeTubeImportPlaylist +import com.github.libretube.obj.FreeTubeVideo import com.github.libretube.obj.FreetubeSubscription import com.github.libretube.obj.FreetubeSubscriptions import com.github.libretube.obj.NewPipeSubscription @@ -24,7 +26,8 @@ import com.github.libretube.obj.NewPipeSubscriptions import com.github.libretube.obj.PipedImportPlaylist import com.github.libretube.obj.PipedPlaylistFile import com.github.libretube.obj.YouTubeWatchHistoryFileItem -import com.github.libretube.ui.dialogs.ShareDialog +import com.github.libretube.ui.dialogs.ShareDialog.Companion.YOUTUBE_FRONTEND_URL +import com.github.libretube.ui.dialogs.ShareDialog.Companion.YOUTUBE_SHORT_URL import com.github.libretube.util.TextUtils import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.encodeToString @@ -34,6 +37,7 @@ import java.util.stream.Collectors object ImportHelper { private const val IMPORT_THUMBNAIL_QUALITY = "mqdefault" + private const val VIDEO_ID_LENGTH = 11 /** * Import subscriptions by a file uri @@ -70,7 +74,7 @@ object ImportHelper { JsonHelper.json.decodeFromStream(it) } subscriptions?.subscriptions.orEmpty().map { - it.url.replace("${ShareDialog.YOUTUBE_FRONTEND_URL}/channel/", "") + it.url.replace("$YOUTUBE_FRONTEND_URL/channel/", "") } } @@ -79,7 +83,7 @@ object ImportHelper { JsonHelper.json.decodeFromStream(it) } subscriptions?.subscriptions.orEmpty().map { - it.url.replace("${ShareDialog.YOUTUBE_FRONTEND_URL}/channel/", "") + it.url.replace("$YOUTUBE_FRONTEND_URL/channel/", "") } } @@ -114,7 +118,7 @@ object ImportHelper { when (importFormat) { ImportFormat.NEWPIPE -> { val newPipeChannels = subs.map { - NewPipeSubscription(it.name, 0, "${ShareDialog.YOUTUBE_FRONTEND_URL}${it.url}") + NewPipeSubscription(it.name, 0, "$YOUTUBE_FRONTEND_URL${it.url}") } val newPipeSubscriptions = NewPipeSubscriptions(subscriptions = newPipeChannels) activity.contentResolver.openOutputStream(uri)?.use { @@ -127,7 +131,7 @@ object ImportHelper { FreetubeSubscription( it.name, "", - "${ShareDialog.YOUTUBE_FRONTEND_URL}${it.url}" + "$YOUTUBE_FRONTEND_URL${it.url}" ) } val freeTubeSubscriptions = FreetubeSubscriptions(subscriptions = freeTubeChannels) @@ -158,7 +162,7 @@ object ImportHelper { // convert the YouTube URLs to videoIds importPlaylists.forEach { playlist -> - playlist.videos = playlist.videos.map { it.takeLast(11) } + playlist.videos = playlist.videos.map { it.takeLast(VIDEO_ID_LENGTH) } } } @@ -225,7 +229,7 @@ object ImportHelper { // convert the YouTube URLs to videoIds importPlaylists.forEach { importPlaylist -> - importPlaylist.videos = importPlaylist.videos.map { it.takeLast(11) } + importPlaylist.videos = importPlaylist.videos.map { it.takeLast(VIDEO_ID_LENGTH) } } } @@ -236,7 +240,7 @@ object ImportHelper { playlist.videos = inputStream.bufferedReader().readLines() .flatMap { it.split(",") } .mapNotNull { videoUrlOrId -> - if (videoUrlOrId.length == 11) { + if (videoUrlOrId.length == VIDEO_ID_LENGTH) { videoUrlOrId } else { TextUtils.getVideoIdFromUri(videoUrlOrId.toUri()) @@ -272,11 +276,22 @@ object ImportHelper { * Export Playlists */ @OptIn(ExperimentalSerializationApi::class) - suspend fun exportPlaylists(activity: Activity, uri: Uri, importFormat: ImportFormat) { + suspend fun exportPlaylists( + activity: Activity, + uri: Uri, + importFormat: ImportFormat, + selectedPlaylistIds: List? = null + ) { + val playlists = PlaylistsHelper.getAllPlaylistsWithVideos(selectedPlaylistIds) + when (importFormat) { ImportFormat.PIPED -> { - val playlists = PlaylistsHelper.exportPipedPlaylists() - val playlistFile = PipedPlaylistFile(playlists = playlists) + val playlistFile = PipedPlaylistFile(playlists = playlists.map { + val videos = it.relatedStreams.map { item -> + "$YOUTUBE_FRONTEND_URL/watch?v=${item.url!!.toID()}" + } + PipedImportPlaylist(it.name, "playlist", "private", videos) + }) activity.contentResolver.openOutputStream(uri)?.use { JsonHelper.json.encodeToStream(playlistFile, it) @@ -285,17 +300,38 @@ object ImportHelper { } ImportFormat.FREETUBE -> { - val playlists = PlaylistsHelper.exportFreeTubePlaylists() - - val freeTubeExportDb = playlists.joinToString("\n") { playlist -> + val freeTubeExportDb = playlists.map { playlist -> + val videos = playlist.relatedStreams.map { videoInfo -> + FreeTubeVideo( + videoId = videoInfo.url.orEmpty().toID(), + title = videoInfo.title.orEmpty(), + author = videoInfo.uploaderName.orEmpty(), + authorId = videoInfo.uploaderUrl.orEmpty().toID(), + lengthSeconds = videoInfo.duration ?: 0L + ) + } + FreeTubeImportPlaylist(playlist.name.orEmpty(), videos) + }.joinToString("\n") { playlist -> JsonHelper.json.encodeToString(playlist) } + activity.contentResolver.openOutputStream(uri)?.use { it.write(freeTubeExportDb.toByteArray()) } activity.toastFromMainDispatcher(R.string.exportsuccess) } + ImportFormat.URLSORIDS -> { + val urlListExport = playlists + .flatMap { it.relatedStreams } + .joinToString("\n") { YOUTUBE_SHORT_URL + "/watch?v=" + it.url!!.toID() } + + activity.contentResolver.openOutputStream(uri)?.use { + it.write(urlListExport.toByteArray()) + } + activity.toastFromMainDispatcher(R.string.exportsuccess) + } + else -> Unit } } @@ -311,7 +347,7 @@ object ImportHelper { .filter { it.activityControls.contains("YouTube watch history") && it.subtitles.isNotEmpty() && it.titleUrl.isNotEmpty() } .reversed() .map { - val videoId = it.titleUrl.substring(it.titleUrl.length - 11) + val videoId = it.titleUrl.takeLast(VIDEO_ID_LENGTH) WatchHistoryItem( videoId = videoId,