mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-14 22:30:30 +05:30
Add functionality for exporting and importing playlists
This commit is contained in:
parent
c1793691ed
commit
5616934817
@ -6,17 +6,21 @@ import com.github.libretube.R
|
|||||||
import com.github.libretube.api.obj.Playlist
|
import com.github.libretube.api.obj.Playlist
|
||||||
import com.github.libretube.api.obj.PlaylistId
|
import com.github.libretube.api.obj.PlaylistId
|
||||||
import com.github.libretube.api.obj.Playlists
|
import com.github.libretube.api.obj.Playlists
|
||||||
|
import com.github.libretube.constants.YOUTUBE_FRONTEND_URL
|
||||||
import com.github.libretube.db.DatabaseHolder
|
import com.github.libretube.db.DatabaseHolder
|
||||||
import com.github.libretube.db.obj.LocalPlaylist
|
import com.github.libretube.db.obj.LocalPlaylist
|
||||||
import com.github.libretube.enums.PlaylistType
|
import com.github.libretube.enums.PlaylistType
|
||||||
import com.github.libretube.extensions.TAG
|
import com.github.libretube.extensions.TAG
|
||||||
import com.github.libretube.extensions.awaitQuery
|
import com.github.libretube.extensions.awaitQuery
|
||||||
|
import com.github.libretube.extensions.toID
|
||||||
import com.github.libretube.extensions.toLocalPlaylistItem
|
import com.github.libretube.extensions.toLocalPlaylistItem
|
||||||
import com.github.libretube.extensions.toStreamItem
|
import com.github.libretube.extensions.toStreamItem
|
||||||
import com.github.libretube.extensions.toastFromMainThread
|
import com.github.libretube.extensions.toastFromMainThread
|
||||||
import com.github.libretube.obj.ImportPlaylist
|
import com.github.libretube.obj.ImportPlaylist
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.util.PreferenceHelper
|
||||||
import com.github.libretube.util.ProxyHelper
|
import com.github.libretube.util.ProxyHelper
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@ -47,9 +51,9 @@ object PlaylistsHelper {
|
|||||||
return playlists
|
return playlists
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getPlaylist(playlistType: PlaylistType, playlistId: String): Playlist {
|
suspend fun getPlaylist(playlistId: String): Playlist {
|
||||||
// load locally stored playlists with the auth api
|
// load locally stored playlists with the auth api
|
||||||
return when (playlistType) {
|
return when (getPlaylistType()) {
|
||||||
PlaylistType.PRIVATE -> RetrofitInstance.authApi.getPlaylist(playlistId)
|
PlaylistType.PRIVATE -> RetrofitInstance.authApi.getPlaylist(playlistId)
|
||||||
PlaylistType.PUBLIC -> RetrofitInstance.api.getPlaylist(playlistId)
|
PlaylistType.PUBLIC -> RetrofitInstance.api.getPlaylist(playlistId)
|
||||||
PlaylistType.LOCAL -> {
|
PlaylistType.LOCAL -> {
|
||||||
@ -66,7 +70,10 @@ object PlaylistsHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun createPlaylist(playlistName: String, appContext: Context, onSuccess: () -> Unit) {
|
suspend fun createPlaylist(
|
||||||
|
playlistName: String,
|
||||||
|
appContext: Context
|
||||||
|
): String? {
|
||||||
if (!loggedIn()) {
|
if (!loggedIn()) {
|
||||||
awaitQuery {
|
awaitQuery {
|
||||||
DatabaseHolder.Database.localPlaylistsDao().createPlaylist(
|
DatabaseHolder.Database.localPlaylistsDao().createPlaylist(
|
||||||
@ -76,8 +83,9 @@ object PlaylistsHelper {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onSuccess.invoke()
|
return awaitQuery {
|
||||||
return
|
DatabaseHolder.Database.localPlaylistsDao().getAll()
|
||||||
|
}.last().playlist.id.toString()
|
||||||
}
|
}
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.authApi.createPlaylist(
|
RetrofitInstance.authApi.createPlaylist(
|
||||||
@ -86,18 +94,17 @@ object PlaylistsHelper {
|
|||||||
)
|
)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
appContext.toastFromMainThread(R.string.unknown_error)
|
appContext.toastFromMainThread(R.string.unknown_error)
|
||||||
return
|
return null
|
||||||
} catch (e: HttpException) {
|
} catch (e: HttpException) {
|
||||||
Log.e(TAG(), e.toString())
|
Log.e(TAG(), e.toString())
|
||||||
appContext.toastFromMainThread(R.string.server_error)
|
appContext.toastFromMainThread(R.string.server_error)
|
||||||
return
|
return null
|
||||||
}
|
}
|
||||||
if (response.playlistId != null) {
|
if (response.playlistId != null) {
|
||||||
appContext.toastFromMainThread(R.string.playlistCreated)
|
appContext.toastFromMainThread(R.string.playlistCreated)
|
||||||
onSuccess.invoke()
|
return response.playlistId!!
|
||||||
} else {
|
|
||||||
appContext.toastFromMainThread(R.string.unknown_error)
|
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun addToPlaylist(playlistId: String, videoId: String): Boolean {
|
suspend fun addToPlaylist(playlistId: String, videoId: String): Boolean {
|
||||||
@ -176,19 +183,51 @@ object PlaylistsHelper {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun importPlaylists(playlist: List<ImportPlaylist>) {
|
suspend fun importPlaylists(appContext: Context, playlists: List<ImportPlaylist>) {
|
||||||
|
for (playlist in playlists) {
|
||||||
|
val playlistId = createPlaylist(playlist.name!!, appContext) ?: continue
|
||||||
|
runBlocking {
|
||||||
|
val tasks = playlist.videos.map { videoId ->
|
||||||
|
async { addToPlaylist(playlistId, videoId.substringAfter("=")) }
|
||||||
|
}
|
||||||
|
tasks.forEach {
|
||||||
|
it.await()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun exportPlaylists(): List<ImportPlaylist> {
|
suspend fun exportPlaylists(): List<ImportPlaylist> {
|
||||||
|
val playlists = getPlaylists()
|
||||||
|
val importLists = mutableListOf<ImportPlaylist>()
|
||||||
|
runBlocking {
|
||||||
|
val tasks = playlists.map {
|
||||||
|
async {
|
||||||
|
val list = getPlaylist(it.id!!)
|
||||||
|
importLists.add(
|
||||||
|
ImportPlaylist(
|
||||||
|
name = list.name,
|
||||||
|
type = "playlist",
|
||||||
|
visibility = "private",
|
||||||
|
videos = list.relatedStreams.orEmpty().map {
|
||||||
|
YOUTUBE_FRONTEND_URL + it.url!!.toID()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.forEach {
|
||||||
|
it.await()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return importLists
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPrivateType(): PlaylistType {
|
fun getPlaylistType(): PlaylistType {
|
||||||
return if (loggedIn()) PlaylistType.PRIVATE else PlaylistType.LOCAL
|
return if (loggedIn()) PlaylistType.PRIVATE else PlaylistType.LOCAL
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPrivateType(playlistId: String): PlaylistType {
|
fun getPlaylistType(playlistId: String): PlaylistType {
|
||||||
if (playlistId.all { it.isDigit() }) return PlaylistType.LOCAL
|
if (playlistId.all { it.isDigit() }) return PlaylistType.LOCAL
|
||||||
if (playlistId.matches(pipedPlaylistRegex)) return PlaylistType.PRIVATE
|
if (playlistId.matches(pipedPlaylistRegex)) return PlaylistType.PRIVATE
|
||||||
return PlaylistType.PUBLIC
|
return PlaylistType.PUBLIC
|
||||||
|
@ -108,7 +108,7 @@ class HomeFragment : BaseFragment() {
|
|||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
makeVisible(binding.playlistsRV, binding.playlistsTV)
|
makeVisible(binding.playlistsRV, binding.playlistsTV)
|
||||||
binding.playlistsRV.layoutManager = LinearLayoutManager(context)
|
binding.playlistsRV.layoutManager = LinearLayoutManager(context)
|
||||||
binding.playlistsRV.adapter = PlaylistsAdapter(playlists.toMutableList(), PlaylistsHelper.getPrivateType())
|
binding.playlistsRV.adapter = PlaylistsAdapter(playlists.toMutableList(), PlaylistsHelper.getPlaylistType())
|
||||||
binding.playlistsRV.adapter?.registerAdapterDataObserver(object :
|
binding.playlistsRV.adapter?.registerAdapterDataObserver(object :
|
||||||
RecyclerView.AdapterDataObserver() {
|
RecyclerView.AdapterDataObserver() {
|
||||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||||
|
@ -123,7 +123,7 @@ class LibraryFragment : BaseFragment() {
|
|||||||
|
|
||||||
val playlistsAdapter = PlaylistsAdapter(
|
val playlistsAdapter = PlaylistsAdapter(
|
||||||
playlists.toMutableList(),
|
playlists.toMutableList(),
|
||||||
PlaylistsHelper.getPrivateType()
|
PlaylistsHelper.getPlaylistType()
|
||||||
)
|
)
|
||||||
|
|
||||||
// listen for playlists to become deleted
|
// listen for playlists to become deleted
|
||||||
|
@ -99,7 +99,7 @@ class PlaylistFragment : BaseFragment() {
|
|||||||
binding.playlistScrollview.visibility = View.GONE
|
binding.playlistScrollview.visibility = View.GONE
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
PlaylistsHelper.getPlaylist(playlistType, playlistId!!)
|
PlaylistsHelper.getPlaylist(playlistId!!)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||||
|
@ -110,8 +110,8 @@ object PlayingQueue {
|
|||||||
fun insertPlaylist(playlistId: String, newCurrentStream: StreamItem) {
|
fun insertPlaylist(playlistId: String, newCurrentStream: StreamItem) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
try {
|
try {
|
||||||
val playlistType = PlaylistsHelper.getPrivateType(playlistId)
|
val playlistType = PlaylistsHelper.getPlaylistType(playlistId)
|
||||||
val playlist = PlaylistsHelper.getPlaylist(playlistType, playlistId)
|
val playlist = PlaylistsHelper.getPlaylist(playlistId)
|
||||||
add(
|
add(
|
||||||
*playlist.relatedStreams
|
*playlist.relatedStreams
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
|
@ -82,12 +82,9 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) :
|
|||||||
var success = true
|
var success = true
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val task = async {
|
|
||||||
SubscriptionHelper.getFeed()
|
|
||||||
}
|
|
||||||
// fetch the users feed
|
// fetch the users feed
|
||||||
val videoFeed = try {
|
val videoFeed = try {
|
||||||
task.await()
|
SubscriptionHelper.getFeed()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
success = false
|
success = false
|
||||||
return@runBlocking
|
return@runBlocking
|
||||||
|
Loading…
Reference in New Issue
Block a user