mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-12 21:30:30 +05:30
refactor: simplify and improve playlist export logic
This commit is contained in:
parent
e4bbd51ef0
commit
b062c6165e
@ -14,10 +14,7 @@ import com.github.libretube.extensions.parallelMap
|
|||||||
import com.github.libretube.extensions.toID
|
import com.github.libretube.extensions.toID
|
||||||
import com.github.libretube.helpers.PreferenceHelper
|
import com.github.libretube.helpers.PreferenceHelper
|
||||||
import com.github.libretube.helpers.ProxyHelper
|
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.obj.PipedImportPlaylist
|
||||||
import com.github.libretube.ui.dialogs.ShareDialog.Companion.YOUTUBE_FRONTEND_URL
|
|
||||||
import com.github.libretube.util.deArrow
|
import com.github.libretube.util.deArrow
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
@ -201,35 +198,11 @@ object PlaylistsHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun exportPipedPlaylists(): List<PipedImportPlaylist> = withContext(Dispatchers.IO) {
|
suspend fun getAllPlaylistsWithVideos(playlistIds: List<String>? = null): List<Playlist> =
|
||||||
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<FreeTubeImportPlaylist> =
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
getPlaylists()
|
(playlistIds ?: getPlaylists().map { it.id!! })
|
||||||
.map { async { getPlaylist(it.id!!) } }
|
.map { async { getPlaylist(it) } }
|
||||||
.awaitAll()
|
.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? {
|
suspend fun clonePlaylist(playlistId: String): String? {
|
||||||
|
@ -15,8 +15,10 @@ import com.github.libretube.db.DatabaseHolder.Database
|
|||||||
import com.github.libretube.db.obj.WatchHistoryItem
|
import com.github.libretube.db.obj.WatchHistoryItem
|
||||||
import com.github.libretube.enums.ImportFormat
|
import com.github.libretube.enums.ImportFormat
|
||||||
import com.github.libretube.extensions.TAG
|
import com.github.libretube.extensions.TAG
|
||||||
|
import com.github.libretube.extensions.toID
|
||||||
import com.github.libretube.extensions.toastFromMainDispatcher
|
import com.github.libretube.extensions.toastFromMainDispatcher
|
||||||
import com.github.libretube.obj.FreeTubeImportPlaylist
|
import com.github.libretube.obj.FreeTubeImportPlaylist
|
||||||
|
import com.github.libretube.obj.FreeTubeVideo
|
||||||
import com.github.libretube.obj.FreetubeSubscription
|
import com.github.libretube.obj.FreetubeSubscription
|
||||||
import com.github.libretube.obj.FreetubeSubscriptions
|
import com.github.libretube.obj.FreetubeSubscriptions
|
||||||
import com.github.libretube.obj.NewPipeSubscription
|
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.PipedImportPlaylist
|
||||||
import com.github.libretube.obj.PipedPlaylistFile
|
import com.github.libretube.obj.PipedPlaylistFile
|
||||||
import com.github.libretube.obj.YouTubeWatchHistoryFileItem
|
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 com.github.libretube.util.TextUtils
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
@ -34,6 +37,7 @@ import java.util.stream.Collectors
|
|||||||
|
|
||||||
object ImportHelper {
|
object ImportHelper {
|
||||||
private const val IMPORT_THUMBNAIL_QUALITY = "mqdefault"
|
private const val IMPORT_THUMBNAIL_QUALITY = "mqdefault"
|
||||||
|
private const val VIDEO_ID_LENGTH = 11
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import subscriptions by a file uri
|
* Import subscriptions by a file uri
|
||||||
@ -70,7 +74,7 @@ object ImportHelper {
|
|||||||
JsonHelper.json.decodeFromStream<NewPipeSubscriptions>(it)
|
JsonHelper.json.decodeFromStream<NewPipeSubscriptions>(it)
|
||||||
}
|
}
|
||||||
subscriptions?.subscriptions.orEmpty().map {
|
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<FreetubeSubscriptions>(it)
|
JsonHelper.json.decodeFromStream<FreetubeSubscriptions>(it)
|
||||||
}
|
}
|
||||||
subscriptions?.subscriptions.orEmpty().map {
|
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) {
|
when (importFormat) {
|
||||||
ImportFormat.NEWPIPE -> {
|
ImportFormat.NEWPIPE -> {
|
||||||
val newPipeChannels = subs.map {
|
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)
|
val newPipeSubscriptions = NewPipeSubscriptions(subscriptions = newPipeChannels)
|
||||||
activity.contentResolver.openOutputStream(uri)?.use {
|
activity.contentResolver.openOutputStream(uri)?.use {
|
||||||
@ -127,7 +131,7 @@ object ImportHelper {
|
|||||||
FreetubeSubscription(
|
FreetubeSubscription(
|
||||||
it.name,
|
it.name,
|
||||||
"",
|
"",
|
||||||
"${ShareDialog.YOUTUBE_FRONTEND_URL}${it.url}"
|
"$YOUTUBE_FRONTEND_URL${it.url}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val freeTubeSubscriptions = FreetubeSubscriptions(subscriptions = freeTubeChannels)
|
val freeTubeSubscriptions = FreetubeSubscriptions(subscriptions = freeTubeChannels)
|
||||||
@ -158,7 +162,7 @@ object ImportHelper {
|
|||||||
|
|
||||||
// convert the YouTube URLs to videoIds
|
// convert the YouTube URLs to videoIds
|
||||||
importPlaylists.forEach { playlist ->
|
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
|
// convert the YouTube URLs to videoIds
|
||||||
importPlaylists.forEach { importPlaylist ->
|
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()
|
playlist.videos = inputStream.bufferedReader().readLines()
|
||||||
.flatMap { it.split(",") }
|
.flatMap { it.split(",") }
|
||||||
.mapNotNull { videoUrlOrId ->
|
.mapNotNull { videoUrlOrId ->
|
||||||
if (videoUrlOrId.length == 11) {
|
if (videoUrlOrId.length == VIDEO_ID_LENGTH) {
|
||||||
videoUrlOrId
|
videoUrlOrId
|
||||||
} else {
|
} else {
|
||||||
TextUtils.getVideoIdFromUri(videoUrlOrId.toUri())
|
TextUtils.getVideoIdFromUri(videoUrlOrId.toUri())
|
||||||
@ -272,11 +276,22 @@ object ImportHelper {
|
|||||||
* Export Playlists
|
* Export Playlists
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
suspend fun exportPlaylists(activity: Activity, uri: Uri, importFormat: ImportFormat) {
|
suspend fun exportPlaylists(
|
||||||
|
activity: Activity,
|
||||||
|
uri: Uri,
|
||||||
|
importFormat: ImportFormat,
|
||||||
|
selectedPlaylistIds: List<String>? = null
|
||||||
|
) {
|
||||||
|
val playlists = PlaylistsHelper.getAllPlaylistsWithVideos(selectedPlaylistIds)
|
||||||
|
|
||||||
when (importFormat) {
|
when (importFormat) {
|
||||||
ImportFormat.PIPED -> {
|
ImportFormat.PIPED -> {
|
||||||
val playlists = PlaylistsHelper.exportPipedPlaylists()
|
val playlistFile = PipedPlaylistFile(playlists = playlists.map {
|
||||||
val playlistFile = PipedPlaylistFile(playlists = playlists)
|
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 {
|
activity.contentResolver.openOutputStream(uri)?.use {
|
||||||
JsonHelper.json.encodeToStream(playlistFile, it)
|
JsonHelper.json.encodeToStream(playlistFile, it)
|
||||||
@ -285,17 +300,38 @@ object ImportHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImportFormat.FREETUBE -> {
|
ImportFormat.FREETUBE -> {
|
||||||
val playlists = PlaylistsHelper.exportFreeTubePlaylists()
|
val freeTubeExportDb = playlists.map { playlist ->
|
||||||
|
val videos = playlist.relatedStreams.map { videoInfo ->
|
||||||
val freeTubeExportDb = playlists.joinToString("\n") { playlist ->
|
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)
|
JsonHelper.json.encodeToString(playlist)
|
||||||
}
|
}
|
||||||
|
|
||||||
activity.contentResolver.openOutputStream(uri)?.use {
|
activity.contentResolver.openOutputStream(uri)?.use {
|
||||||
it.write(freeTubeExportDb.toByteArray())
|
it.write(freeTubeExportDb.toByteArray())
|
||||||
}
|
}
|
||||||
activity.toastFromMainDispatcher(R.string.exportsuccess)
|
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
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,7 +347,7 @@ object ImportHelper {
|
|||||||
.filter { it.activityControls.contains("YouTube watch history") && it.subtitles.isNotEmpty() && it.titleUrl.isNotEmpty() }
|
.filter { it.activityControls.contains("YouTube watch history") && it.subtitles.isNotEmpty() && it.titleUrl.isNotEmpty() }
|
||||||
.reversed()
|
.reversed()
|
||||||
.map {
|
.map {
|
||||||
val videoId = it.titleUrl.substring(it.titleUrl.length - 11)
|
val videoId = it.titleUrl.takeLast(VIDEO_ID_LENGTH)
|
||||||
|
|
||||||
WatchHistoryItem(
|
WatchHistoryItem(
|
||||||
videoId = videoId,
|
videoId = videoId,
|
||||||
|
Loading…
Reference in New Issue
Block a user