mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-27 23:40:33 +05:30
Merge pull request #2755 from Isira-Seneviratne/KotlinX_Serialization
Switch to Kotlinx Serialization.
This commit is contained in:
commit
dd1821ada3
@ -4,6 +4,7 @@ plugins {
|
|||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
id 'kotlin-android'
|
id 'kotlin-android'
|
||||||
id 'kotlin-kapt'
|
id 'kotlin-kapt'
|
||||||
|
id 'kotlinx-serialization'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -107,10 +108,11 @@ dependencies {
|
|||||||
implementation libs.exoplayer.extension.mediasession
|
implementation libs.exoplayer.extension.mediasession
|
||||||
implementation libs.exoplayer.dash
|
implementation libs.exoplayer.dash
|
||||||
|
|
||||||
/* Retrofit and Jackson */
|
/* Retrofit and Kotlinx Serialization */
|
||||||
implementation libs.square.retrofit
|
implementation libs.square.retrofit
|
||||||
implementation libs.square.retrofit.converterJackson
|
implementation libs.kotlinx.serialization
|
||||||
implementation libs.jacksonAnnotations
|
implementation libs.kotlinx.datetime
|
||||||
|
implementation libs.kotlinx.serialization.retrofit
|
||||||
|
|
||||||
/* Cronet and Coil */
|
/* Cronet and Coil */
|
||||||
coreLibraryDesugaring libs.desugaring
|
coreLibraryDesugaring libs.desugaring
|
||||||
|
23
app/proguard-rules.pro
vendored
23
app/proguard-rules.pro
vendored
@ -29,3 +29,26 @@
|
|||||||
# Keep data classes used for Retrofit
|
# Keep data classes used for Retrofit
|
||||||
-keep class com.github.libretube.obj.** { *; }
|
-keep class com.github.libretube.obj.** { *; }
|
||||||
-keep class com.github.libretube.obj.update.** { *; }
|
-keep class com.github.libretube.obj.update.** { *; }
|
||||||
|
|
||||||
|
# Keep rules required by Kotlinx Serialization
|
||||||
|
-if @kotlinx.serialization.Serializable class **
|
||||||
|
-keepclassmembers class <1> {
|
||||||
|
static <1>$Companion Companion;
|
||||||
|
}
|
||||||
|
|
||||||
|
-if @kotlinx.serialization.Serializable class ** {
|
||||||
|
static **$* *;
|
||||||
|
}
|
||||||
|
-keepclassmembers class <2>$<3> {
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
-if @kotlinx.serialization.Serializable class ** {
|
||||||
|
public static ** INSTANCE;
|
||||||
|
}
|
||||||
|
-keepclassmembers class <1> {
|
||||||
|
public static <1> INSTANCE;
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
|
||||||
|
9
app/src/main/java/com/github/libretube/api/JsonHelper.kt
Normal file
9
app/src/main/java/com/github/libretube/api/JsonHelper.kt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package com.github.libretube.api
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
object JsonHelper {
|
||||||
|
val json = Json {
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ import com.github.libretube.api.obj.SegmentData
|
|||||||
import com.github.libretube.api.obj.StreamItem
|
import com.github.libretube.api.obj.StreamItem
|
||||||
import com.github.libretube.api.obj.Streams
|
import com.github.libretube.api.obj.Streams
|
||||||
import com.github.libretube.api.obj.Subscribe
|
import com.github.libretube.api.obj.Subscribe
|
||||||
|
import com.github.libretube.api.obj.Subscribed
|
||||||
import com.github.libretube.api.obj.Subscription
|
import com.github.libretube.api.obj.Subscription
|
||||||
import com.github.libretube.api.obj.Token
|
import com.github.libretube.api.obj.Token
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
@ -114,7 +115,7 @@ interface PipedApi {
|
|||||||
suspend fun isSubscribed(
|
suspend fun isSubscribed(
|
||||||
@Query("channelId") channelId: String,
|
@Query("channelId") channelId: String,
|
||||||
@Header("Authorization") token: String
|
@Header("Authorization") token: String
|
||||||
): com.github.libretube.api.obj.Subscribed
|
): Subscribed
|
||||||
|
|
||||||
@GET("subscriptions")
|
@GET("subscriptions")
|
||||||
suspend fun subscriptions(@Header("Authorization") token: String): List<Subscription>
|
suspend fun subscriptions(@Header("Authorization") token: String): List<Subscription>
|
||||||
|
@ -93,10 +93,7 @@ object PlaylistsHelper {
|
|||||||
}.last().playlist.id.toString()
|
}.last().playlist.id.toString()
|
||||||
}
|
}
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.authApi.createPlaylist(
|
RetrofitInstance.authApi.createPlaylist(token, Playlists(name = playlistName))
|
||||||
token,
|
|
||||||
Playlists(name = playlistName)
|
|
||||||
)
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
appContext.toastFromMainThread(R.string.unknown_error)
|
appContext.toastFromMainThread(R.string.unknown_error)
|
||||||
return null
|
return null
|
||||||
@ -107,7 +104,7 @@ object PlaylistsHelper {
|
|||||||
}
|
}
|
||||||
if (response.playlistId != null) {
|
if (response.playlistId != null) {
|
||||||
appContext.toastFromMainThread(R.string.playlistCreated)
|
appContext.toastFromMainThread(R.string.playlistCreated)
|
||||||
return response.playlistId!!
|
return response.playlistId
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -141,13 +138,8 @@ object PlaylistsHelper {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return RetrofitInstance.authApi.addToPlaylist(
|
val playlist = PlaylistId(playlistId, videoIds = videos.map { it.url!!.toID() })
|
||||||
token,
|
return RetrofitInstance.authApi.addToPlaylist(token, playlist).message == "ok"
|
||||||
PlaylistId(
|
|
||||||
playlistId = playlistId,
|
|
||||||
videoIds = videos.toList().map { it.url!!.toID() }
|
|
||||||
)
|
|
||||||
).message == "ok"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun renamePlaylist(playlistId: String, newName: String): Boolean {
|
suspend fun renamePlaylist(playlistId: String, newName: String): Boolean {
|
||||||
@ -164,10 +156,7 @@ object PlaylistsHelper {
|
|||||||
|
|
||||||
return RetrofitInstance.authApi.renamePlaylist(
|
return RetrofitInstance.authApi.renamePlaylist(
|
||||||
token,
|
token,
|
||||||
PlaylistId(
|
PlaylistId(playlistId, newName = newName)
|
||||||
playlistId = playlistId,
|
|
||||||
newName = newName
|
|
||||||
)
|
|
||||||
).playlistId != null
|
).playlistId != null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,8 +240,8 @@ object PlaylistsHelper {
|
|||||||
name = list.name,
|
name = list.name,
|
||||||
type = "playlist",
|
type = "playlist",
|
||||||
visibility = "private",
|
visibility = "private",
|
||||||
videos = list.relatedStreams.orEmpty().map {
|
videos = list.relatedStreams.map {
|
||||||
YOUTUBE_FRONTEND_URL + "/watch?v=" + it.url!!.toID()
|
"$YOUTUBE_FRONTEND_URL/watch?v=${it.url!!.toID()}"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -278,19 +267,13 @@ object PlaylistsHelper {
|
|||||||
val newPlaylist = createPlaylist(playlist.name ?: "Unknown name", appContext)
|
val newPlaylist = createPlaylist(playlist.name ?: "Unknown name", appContext)
|
||||||
newPlaylist ?: return@launch
|
newPlaylist ?: return@launch
|
||||||
|
|
||||||
addToPlaylist(
|
addToPlaylist(newPlaylist, *playlist.relatedStreams.toTypedArray())
|
||||||
newPlaylist,
|
|
||||||
*playlist.relatedStreams.orEmpty().toTypedArray()
|
|
||||||
)
|
|
||||||
|
|
||||||
var nextPage = playlist.nextpage
|
var nextPage = playlist.nextpage
|
||||||
while (nextPage != null) {
|
while (nextPage != null) {
|
||||||
nextPage = try {
|
nextPage = try {
|
||||||
RetrofitInstance.api.getPlaylistNextPage(playlistId, nextPage).apply {
|
RetrofitInstance.api.getPlaylistNextPage(playlistId, nextPage).apply {
|
||||||
addToPlaylist(
|
addToPlaylist(newPlaylist, *relatedStreams.toTypedArray())
|
||||||
newPlaylist,
|
|
||||||
*relatedStreams.orEmpty().toTypedArray()
|
|
||||||
)
|
|
||||||
}.nextpage
|
}.nextpage
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return@launch
|
return@launch
|
||||||
|
@ -3,40 +3,43 @@ package com.github.libretube.api
|
|||||||
import com.github.libretube.constants.PIPED_API_URL
|
import com.github.libretube.constants.PIPED_API_URL
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.util.PreferenceHelper
|
||||||
|
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.jackson.JacksonConverterFactory
|
import retrofit2.create
|
||||||
|
|
||||||
object RetrofitInstance {
|
object RetrofitInstance {
|
||||||
lateinit var url: String
|
lateinit var url: String
|
||||||
lateinit var authUrl: String
|
lateinit var authUrl: String
|
||||||
val lazyMgr = resettableManager()
|
val lazyMgr = resettableManager()
|
||||||
val jacksonConverterFactory = JacksonConverterFactory.create()
|
private val kotlinxConverterFactory = JsonHelper.json
|
||||||
|
.asConverterFactory("application/json".toMediaType())
|
||||||
|
|
||||||
val api: PipedApi by resettableLazy(lazyMgr) {
|
val api by resettableLazy(lazyMgr) {
|
||||||
Retrofit.Builder()
|
Retrofit.Builder()
|
||||||
.baseUrl(url)
|
.baseUrl(url)
|
||||||
.callFactory(CronetHelper.callFactory)
|
.callFactory(CronetHelper.callFactory)
|
||||||
.addConverterFactory(jacksonConverterFactory)
|
.addConverterFactory(kotlinxConverterFactory)
|
||||||
.build()
|
.build()
|
||||||
.create(PipedApi::class.java)
|
.create<PipedApi>()
|
||||||
}
|
}
|
||||||
|
|
||||||
val authApi: PipedApi by resettableLazy(lazyMgr) {
|
val authApi by resettableLazy(lazyMgr) {
|
||||||
Retrofit.Builder()
|
Retrofit.Builder()
|
||||||
.baseUrl(authUrl)
|
.baseUrl(authUrl)
|
||||||
.callFactory(CronetHelper.callFactory)
|
.callFactory(CronetHelper.callFactory)
|
||||||
.addConverterFactory(jacksonConverterFactory)
|
.addConverterFactory(kotlinxConverterFactory)
|
||||||
.build()
|
.build()
|
||||||
.create(PipedApi::class.java)
|
.create<PipedApi>()
|
||||||
}
|
}
|
||||||
|
|
||||||
val externalApi: ExternalApi by resettableLazy(lazyMgr) {
|
val externalApi by resettableLazy(lazyMgr) {
|
||||||
Retrofit.Builder()
|
Retrofit.Builder()
|
||||||
.baseUrl(url)
|
.baseUrl(url)
|
||||||
.callFactory(CronetHelper.callFactory)
|
.callFactory(CronetHelper.callFactory)
|
||||||
.addConverterFactory(jacksonConverterFactory)
|
.addConverterFactory(kotlinxConverterFactory)
|
||||||
.build()
|
.build()
|
||||||
.create(ExternalApi::class.java)
|
.create<ExternalApi>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.api.obj.StreamItem
|
import com.github.libretube.api.obj.StreamItem
|
||||||
|
import com.github.libretube.api.obj.Subscribe
|
||||||
import com.github.libretube.api.obj.Subscription
|
import com.github.libretube.api.obj.Subscription
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
import com.github.libretube.db.DatabaseHolder.Companion.Database
|
import com.github.libretube.db.DatabaseHolder.Companion.Database
|
||||||
@ -25,7 +26,7 @@ object SubscriptionHelper {
|
|||||||
try {
|
try {
|
||||||
RetrofitInstance.authApi.subscribe(
|
RetrofitInstance.authApi.subscribe(
|
||||||
PreferenceHelper.getToken(),
|
PreferenceHelper.getToken(),
|
||||||
com.github.libretube.api.obj.Subscribe(channelId)
|
Subscribe(channelId)
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG(), e.toString())
|
Log.e(TAG(), e.toString())
|
||||||
@ -46,7 +47,7 @@ object SubscriptionHelper {
|
|||||||
try {
|
try {
|
||||||
RetrofitInstance.authApi.unsubscribe(
|
RetrofitInstance.authApi.unsubscribe(
|
||||||
PreferenceHelper.getToken(),
|
PreferenceHelper.getToken(),
|
||||||
com.github.libretube.api.obj.Subscribe(channelId)
|
Subscribe(channelId)
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG(), e.toString())
|
Log.e(TAG(), e.toString())
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Channel(
|
data class Channel(
|
||||||
var id: String? = null,
|
val id: String,
|
||||||
var name: String? = null,
|
val name: String,
|
||||||
var avatarUrl: String? = null,
|
val avatarUrl: String,
|
||||||
var bannerUrl: String? = null,
|
val bannerUrl: String,
|
||||||
var description: String? = null,
|
val description: String,
|
||||||
var nextpage: String? = null,
|
val nextpage: String? = null,
|
||||||
var subscriberCount: Long = 0,
|
val subscriberCount: Long = 0,
|
||||||
var verified: Boolean = false,
|
val verified: Boolean = false,
|
||||||
var relatedStreams: List<StreamItem>? = listOf(),
|
val relatedStreams: List<StreamItem> = emptyList(),
|
||||||
var tabs: List<ChannelTab>? = listOf()
|
val tabs: List<ChannelTab> = emptyList()
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class ChannelTab(
|
data class ChannelTab(
|
||||||
val name: String? = null,
|
val name: String,
|
||||||
val data: String? = null
|
val data: String
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ChannelTabResponse(
|
data class ChannelTabResponse(
|
||||||
val content: List<ContentItem> = listOf(),
|
val content: List<ContentItem> = emptyList(),
|
||||||
val nextpage: String? = null
|
val nextpage: String? = null
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class ChapterSegment(
|
data class ChapterSegment(
|
||||||
var title: String? = null,
|
val title: String? = null,
|
||||||
var image: String? = null,
|
val image: String? = null,
|
||||||
var start: Long? = null
|
val start: Long? = null
|
||||||
)
|
)
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Comment(
|
data class Comment(
|
||||||
val author: String? = null,
|
val author: String,
|
||||||
val commentId: String? = null,
|
val commentId: String,
|
||||||
val commentText: String? = null,
|
val commentText: String,
|
||||||
val commentedTime: String? = null,
|
val commentedTime: String,
|
||||||
val commentorUrl: String? = null,
|
val commentorUrl: String,
|
||||||
val repliesPage: String? = null,
|
val repliesPage: String? = null,
|
||||||
val hearted: Boolean? = null,
|
val hearted: Boolean,
|
||||||
val likeCount: Long? = null,
|
val likeCount: Long,
|
||||||
val pinned: Boolean? = null,
|
val pinned: Boolean,
|
||||||
val thumbnail: String? = null,
|
val thumbnail: String,
|
||||||
val verified: Boolean? = null,
|
val verified: Boolean,
|
||||||
val replyCount: Long? = null
|
val replyCount: Long
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class CommentsPage(
|
data class CommentsPage(
|
||||||
var comments: MutableList<Comment> = arrayListOf(),
|
var comments: List<Comment> = emptyList(),
|
||||||
val disabled: Boolean? = null,
|
val disabled: Boolean = false,
|
||||||
val nextpage: String? = null
|
val nextpage: String? = null
|
||||||
)
|
)
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class ContentItem(
|
data class ContentItem(
|
||||||
var url: String? = null,
|
val url: String,
|
||||||
val type: String? = null,
|
val type: String,
|
||||||
var thumbnail: String? = null,
|
val thumbnail: String,
|
||||||
var uploaderName: String? = null,
|
val uploaderName: String? = null,
|
||||||
var uploaded: Long? = null,
|
val uploaded: Long? = null,
|
||||||
var shortDescription: String? = null,
|
val shortDescription: String? = null,
|
||||||
// Video only attributes
|
// Video only attributes
|
||||||
var title: String? = null,
|
val title: String? = null,
|
||||||
var uploaderUrl: String? = null,
|
val uploaderUrl: String? = null,
|
||||||
var uploaderAvatar: String? = null,
|
val uploaderAvatar: String? = null,
|
||||||
var uploadedDate: String? = null,
|
val uploadedDate: String? = null,
|
||||||
var duration: Long? = null,
|
val duration: Long = -1,
|
||||||
var views: Long? = null,
|
val views: Long = -1,
|
||||||
var isShort: Boolean? = null,
|
val isShort: Boolean? = null,
|
||||||
var uploaderVerified: Boolean? = null,
|
val uploaderVerified: Boolean? = null,
|
||||||
// Channel and Playlist attributes
|
// Channel and Playlist attributes
|
||||||
var name: String? = null,
|
val name: String? = null,
|
||||||
var description: String? = null,
|
val description: String? = null,
|
||||||
var subscribers: Long? = -1,
|
val subscribers: Long = -1,
|
||||||
var videos: Long? = -1,
|
val videos: Long = -1,
|
||||||
var verified: Boolean? = null
|
val verified: Boolean? = null
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
data class DeleteUserRequest(
|
import kotlinx.serialization.Serializable
|
||||||
var password: String? = null
|
|
||||||
)
|
@Serializable
|
||||||
|
data class DeleteUserRequest(val password: String)
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Instances(
|
data class Instances(
|
||||||
var name: String? = null,
|
val name: String,
|
||||||
var api_url: String? = null,
|
@SerialName("api_url") val apiUrl: String,
|
||||||
var locations: String? = null,
|
val locations: String,
|
||||||
var version: String? = null,
|
val version: String,
|
||||||
var up_to_date: Boolean? = null,
|
@SerialName("up_to_date") val upToDate: Boolean,
|
||||||
var cdn: Boolean? = null,
|
val cdn: Boolean,
|
||||||
var registered: Long? = null,
|
val registered: Long,
|
||||||
var last_checked: Long? = null
|
@SerialName("last_checked") val lastChecked: Long
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Login(
|
data class Login(
|
||||||
val username: String? = null,
|
val username: String,
|
||||||
val password: String? = null
|
val password: String
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
data class Message(
|
import kotlinx.serialization.Serializable
|
||||||
var message: String? = null
|
|
||||||
)
|
@Serializable
|
||||||
|
data class Message(val message: String? = null)
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class PipedConfig(
|
data class PipedConfig(
|
||||||
val donationUrl: String? = null,
|
val donationUrl: String? = null,
|
||||||
val statusPageUrl: String? = null,
|
val statusPageUrl: String? = null,
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class PipedStream(
|
data class PipedStream(
|
||||||
var url: String? = null,
|
val url: String? = null,
|
||||||
var format: String? = null,
|
val format: String? = null,
|
||||||
var quality: String? = null,
|
val quality: String? = null,
|
||||||
var mimeType: String? = null,
|
val mimeType: String? = null,
|
||||||
var codec: String? = null,
|
val codec: String? = null,
|
||||||
var videoOnly: Boolean? = null,
|
val videoOnly: Boolean? = null,
|
||||||
var bitrate: Int? = null,
|
val bitrate: Int? = null,
|
||||||
var initStart: Int? = null,
|
val initStart: Int? = null,
|
||||||
var initEnd: Int? = null,
|
val initEnd: Int? = null,
|
||||||
var indexStart: Int? = null,
|
val indexStart: Int? = null,
|
||||||
var indexEnd: Int? = null,
|
val indexEnd: Int? = null,
|
||||||
var width: Int? = null,
|
val width: Int? = null,
|
||||||
var height: Int? = null,
|
val height: Int? = null,
|
||||||
var fps: Int? = null,
|
val fps: Int? = null,
|
||||||
val audioTrackName: String? = null,
|
val audioTrackName: String? = null,
|
||||||
val audioTrackId: String? = null
|
val audioTrackId: String? = null
|
||||||
)
|
)
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Playlist(
|
data class Playlist(
|
||||||
var name: String? = null,
|
val name: String? = null,
|
||||||
var thumbnailUrl: String? = null,
|
val thumbnailUrl: String? = null,
|
||||||
var bannerUrl: String? = null,
|
val bannerUrl: String? = null,
|
||||||
var nextpage: String? = null,
|
val nextpage: String? = null,
|
||||||
var uploader: String? = null,
|
val uploader: String? = null,
|
||||||
var uploaderUrl: String? = null,
|
val uploaderUrl: String? = null,
|
||||||
var uploaderAvatar: String? = null,
|
val uploaderAvatar: String? = null,
|
||||||
var videos: Int? = 0,
|
val videos: Int = 0,
|
||||||
var relatedStreams: List<StreamItem>? = null
|
val relatedStreams: List<StreamItem> = emptyList()
|
||||||
)
|
)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class PlaylistId(
|
data class PlaylistId(
|
||||||
var playlistId: String? = null,
|
val playlistId: String? = null,
|
||||||
var videoId: String? = null,
|
val videoId: String? = null,
|
||||||
var videoIds: List<String>? = null,
|
val videoIds: List<String> = emptyList(),
|
||||||
var newName: String? = null,
|
val newName: String? = null,
|
||||||
var index: Int = -1
|
val index: Int = -1
|
||||||
)
|
)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Playlists(
|
data class Playlists(
|
||||||
var id: String? = null,
|
val id: String? = null,
|
||||||
var name: String? = null,
|
val name: String? = null,
|
||||||
var shortDescription: String? = null,
|
val shortDescription: String? = null,
|
||||||
var thumbnail: String? = null,
|
val thumbnail: String? = null,
|
||||||
var videos: Long? = null
|
val videos: Long = 0
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class PreviewFrames(
|
data class PreviewFrames(
|
||||||
val urls: List<String>? = null,
|
val urls: List<String>? = null,
|
||||||
val frameWidth: Int? = null,
|
val frameWidth: Int? = null,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class SearchResult(
|
data class SearchResult(
|
||||||
val items: MutableList<ContentItem>? = arrayListOf(),
|
val items: List<ContentItem> = emptyList(),
|
||||||
val nextpage: String? = null,
|
val nextpage: String? = null,
|
||||||
val suggestion: String? = "",
|
val suggestion: String? = null,
|
||||||
val corrected: Boolean? = null
|
val corrected: Boolean? = null
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Segment(
|
data class Segment(
|
||||||
val UUID: String? = null,
|
val UUID: String? = null,
|
||||||
val actionType: String? = null,
|
val actionType: String? = null,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class SegmentData(
|
data class SegmentData(
|
||||||
val hash: String? = null,
|
val hash: String? = null,
|
||||||
val segments: List<Segment> = listOf(),
|
val segments: List<Segment> = listOf(),
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class StreamItem(
|
data class StreamItem(
|
||||||
var url: String? = null,
|
val url: String? = null,
|
||||||
val type: String? = null,
|
val type: String? = null,
|
||||||
var title: String? = null,
|
val title: String? = null,
|
||||||
var thumbnail: String? = null,
|
val thumbnail: String? = null,
|
||||||
var uploaderName: String? = null,
|
val uploaderName: String? = null,
|
||||||
var uploaderUrl: String? = null,
|
val uploaderUrl: String? = null,
|
||||||
var uploaderAvatar: String? = null,
|
val uploaderAvatar: String? = null,
|
||||||
var uploadedDate: String? = null,
|
val uploadedDate: String? = null,
|
||||||
var duration: Long? = null,
|
val duration: Long? = null,
|
||||||
var views: Long? = null,
|
val views: Long? = null,
|
||||||
var uploaderVerified: Boolean? = null,
|
val uploaderVerified: Boolean? = null,
|
||||||
var uploaded: Long? = null,
|
val uploaded: Long? = null,
|
||||||
var shortDescription: String? = null,
|
val shortDescription: String? = null,
|
||||||
val isShort: Boolean = false
|
val isShort: Boolean = false
|
||||||
)
|
)
|
||||||
|
@ -1,31 +1,32 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Streams(
|
data class Streams(
|
||||||
val title: String? = null,
|
val title: String,
|
||||||
val description: String? = null,
|
val description: String,
|
||||||
val uploadDate: String? = null,
|
val uploadDate: LocalDate,
|
||||||
val uploader: String? = null,
|
val uploader: String,
|
||||||
val uploaderUrl: String? = null,
|
val uploaderUrl: String,
|
||||||
val uploaderAvatar: String? = null,
|
val uploaderAvatar: String,
|
||||||
val thumbnailUrl: String? = null,
|
val thumbnailUrl: String,
|
||||||
val hls: String? = null,
|
val hls: String? = null,
|
||||||
val dash: String? = null,
|
val dash: String? = null,
|
||||||
val lbryId: String? = null,
|
val lbryId: String? = null,
|
||||||
val uploaderVerified: Boolean? = null,
|
val uploaderVerified: Boolean,
|
||||||
val duration: Long? = null,
|
val duration: Long,
|
||||||
val views: Long? = null,
|
val views: Long = 0,
|
||||||
val likes: Long? = null,
|
val likes: Long = 0,
|
||||||
val dislikes: Long? = null,
|
val dislikes: Long = 0,
|
||||||
val audioStreams: List<PipedStream>? = null,
|
val audioStreams: List<PipedStream> = emptyList(),
|
||||||
val videoStreams: List<PipedStream>? = null,
|
val videoStreams: List<PipedStream> = emptyList(),
|
||||||
val relatedStreams: List<StreamItem>? = null,
|
val relatedStreams: List<StreamItem> = emptyList(),
|
||||||
val subtitles: List<Subtitle>? = null,
|
val subtitles: List<Subtitle> = emptyList(),
|
||||||
val livestream: Boolean? = null,
|
val livestream: Boolean = false,
|
||||||
val proxyUrl: String? = null,
|
val proxyUrl: String? = null,
|
||||||
val chapters: List<ChapterSegment>? = null,
|
val chapters: List<ChapterSegment> = emptyList(),
|
||||||
val uploaderSubscriberCount: Long? = null,
|
val uploaderSubscriberCount: Long = 0,
|
||||||
val previewFrames: List<PreviewFrames>? = null
|
val previewFrames: List<PreviewFrames> = emptyList()
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Subscribe(
|
data class Subscribe(val channelId: String)
|
||||||
var channelId: String? = null
|
|
||||||
)
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Subscribed(
|
data class Subscribed(val subscribed: Boolean? = null)
|
||||||
var subscribed: Boolean? = null
|
|
||||||
)
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Subscription(
|
data class Subscription(
|
||||||
var url: String? = null,
|
val url: String,
|
||||||
var name: String? = null,
|
val name: String,
|
||||||
var avatar: String? = null,
|
val avatar: String,
|
||||||
var verified: Boolean? = null
|
val verified: Boolean
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Subtitle(
|
data class Subtitle(
|
||||||
val url: String? = null,
|
val url: String? = null,
|
||||||
val mimeType: String? = null,
|
val mimeType: String? = null,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Token(
|
data class Token(
|
||||||
var token: String? = null,
|
val token: String? = null,
|
||||||
var error: String? = null
|
val error: String? = null
|
||||||
)
|
)
|
||||||
|
@ -16,9 +16,9 @@ object DatabaseHelper {
|
|||||||
val watchHistoryItem = WatchHistoryItem(
|
val watchHistoryItem = WatchHistoryItem(
|
||||||
videoId,
|
videoId,
|
||||||
streams.title,
|
streams.title,
|
||||||
streams.uploadDate,
|
streams.uploadDate.toString(),
|
||||||
streams.uploader,
|
streams.uploader,
|
||||||
streams.uploaderUrl!!.toID(),
|
streams.uploaderUrl.toID(),
|
||||||
streams.uploaderAvatar,
|
streams.uploaderAvatar,
|
||||||
streams.thumbnailUrl,
|
streams.thumbnailUrl,
|
||||||
streams.duration
|
streams.duration
|
||||||
|
@ -3,7 +3,9 @@ package com.github.libretube.db.obj
|
|||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
@Entity(tableName = "customInstance")
|
@Entity(tableName = "customInstance")
|
||||||
class CustomInstance(
|
class CustomInstance(
|
||||||
@PrimaryKey var name: String = "",
|
@PrimaryKey var name: String = "",
|
||||||
|
@ -2,7 +2,9 @@ package com.github.libretube.db.obj
|
|||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
@Entity
|
@Entity
|
||||||
data class LocalPlaylist(
|
data class LocalPlaylist(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -3,7 +3,9 @@ package com.github.libretube.db.obj
|
|||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
@Entity
|
@Entity
|
||||||
data class LocalPlaylistItem(
|
data class LocalPlaylistItem(
|
||||||
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
||||||
|
@ -2,7 +2,9 @@ package com.github.libretube.db.obj
|
|||||||
|
|
||||||
import androidx.room.Embedded
|
import androidx.room.Embedded
|
||||||
import androidx.room.Relation
|
import androidx.room.Relation
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class LocalPlaylistWithVideos(
|
data class LocalPlaylistWithVideos(
|
||||||
@Embedded val playlist: LocalPlaylist = LocalPlaylist(),
|
@Embedded val playlist: LocalPlaylist = LocalPlaylist(),
|
||||||
@Relation(
|
@Relation(
|
||||||
|
@ -2,7 +2,9 @@ package com.github.libretube.db.obj
|
|||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
@Entity(tableName = "localSubscription")
|
@Entity(tableName = "localSubscription")
|
||||||
data class LocalSubscription(
|
data class LocalSubscription(
|
||||||
@PrimaryKey val channelId: String = ""
|
@PrimaryKey val channelId: String = ""
|
||||||
|
@ -2,7 +2,9 @@ package com.github.libretube.db.obj
|
|||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
@Entity(tableName = "playlistBookmark")
|
@Entity(tableName = "playlistBookmark")
|
||||||
data class PlaylistBookmark(
|
data class PlaylistBookmark(
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -2,7 +2,9 @@ package com.github.libretube.db.obj
|
|||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
@Entity(tableName = "searchHistoryItem")
|
@Entity(tableName = "searchHistoryItem")
|
||||||
data class SearchHistoryItem(
|
data class SearchHistoryItem(
|
||||||
@PrimaryKey val query: String = ""
|
@PrimaryKey val query: String = ""
|
||||||
|
@ -3,7 +3,9 @@ package com.github.libretube.db.obj
|
|||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
@Entity(tableName = "watchHistoryItem")
|
@Entity(tableName = "watchHistoryItem")
|
||||||
data class WatchHistoryItem(
|
data class WatchHistoryItem(
|
||||||
@PrimaryKey val videoId: String = "",
|
@PrimaryKey val videoId: String = "",
|
||||||
|
@ -3,7 +3,9 @@ package com.github.libretube.db.obj
|
|||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
@Entity(tableName = "watchPosition")
|
@Entity(tableName = "watchPosition")
|
||||||
data class WatchPosition(
|
data class WatchPosition(
|
||||||
@PrimaryKey val videoId: String = "",
|
@PrimaryKey val videoId: String = "",
|
||||||
|
@ -13,7 +13,7 @@ fun Streams.toStreamItem(videoId: String): StreamItem {
|
|||||||
uploaderName = uploader,
|
uploaderName = uploader,
|
||||||
uploaderUrl = uploaderUrl,
|
uploaderUrl = uploaderUrl,
|
||||||
uploaderAvatar = uploaderAvatar,
|
uploaderAvatar = uploaderAvatar,
|
||||||
uploadedDate = uploadDate,
|
uploadedDate = uploadDate.toString(),
|
||||||
uploaded = null,
|
uploaded = null,
|
||||||
duration = duration,
|
duration = duration,
|
||||||
views = views,
|
views = views,
|
||||||
|
@ -7,14 +7,16 @@ import com.github.libretube.db.obj.PlaylistBookmark
|
|||||||
import com.github.libretube.db.obj.SearchHistoryItem
|
import com.github.libretube.db.obj.SearchHistoryItem
|
||||||
import com.github.libretube.db.obj.WatchHistoryItem
|
import com.github.libretube.db.obj.WatchHistoryItem
|
||||||
import com.github.libretube.db.obj.WatchPosition
|
import com.github.libretube.db.obj.WatchPosition
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class BackupFile(
|
data class BackupFile(
|
||||||
var watchHistory: List<WatchHistoryItem>? = null,
|
var watchHistory: List<WatchHistoryItem> = emptyList(),
|
||||||
var watchPositions: List<WatchPosition>? = null,
|
var watchPositions: List<WatchPosition> = emptyList(),
|
||||||
var searchHistory: List<SearchHistoryItem>? = null,
|
var searchHistory: List<SearchHistoryItem> = emptyList(),
|
||||||
var localSubscriptions: List<LocalSubscription>? = null,
|
var localSubscriptions: List<LocalSubscription> = emptyList(),
|
||||||
var customInstances: List<CustomInstance>? = null,
|
var customInstances: List<CustomInstance> = emptyList(),
|
||||||
var playlistBookmarks: List<PlaylistBookmark>? = null,
|
var playlistBookmarks: List<PlaylistBookmark> = emptyList(),
|
||||||
var localPlaylists: List<LocalPlaylistWithVideos>? = null,
|
var localPlaylists: List<LocalPlaylistWithVideos> = emptyList(),
|
||||||
var preferences: List<PreferenceItem>? = null
|
var preferences: List<PreferenceItem> = emptyList()
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package com.github.libretube.obj
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ImportPlaylist(
|
data class ImportPlaylist(
|
||||||
var name: String? = null,
|
var name: String? = null,
|
||||||
val type: String? = null,
|
val type: String? = null,
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package com.github.libretube.obj
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ImportPlaylistFile(
|
data class ImportPlaylistFile(
|
||||||
val format: String? = null,
|
val format: String,
|
||||||
val version: Int? = null,
|
val version: Int,
|
||||||
val playlists: List<ImportPlaylist>? = null
|
val playlists: List<ImportPlaylist> = emptyList()
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package com.github.libretube.obj
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class NewPipeSubscription(
|
data class NewPipeSubscription(
|
||||||
val name: String? = null,
|
val name: String,
|
||||||
val service_id: Int? = null,
|
@SerialName("service_id") val serviceId: Int,
|
||||||
val url: String? = null
|
val url: String
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package com.github.libretube.obj
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class NewPipeSubscriptions(
|
data class NewPipeSubscriptions(
|
||||||
val app_version: String = "",
|
@SerialName("app_version") val appVersion: String = "",
|
||||||
val app_version_int: Int = 0,
|
@SerialName("app_version_int") val appVersionInt: Int = 0,
|
||||||
val subscriptions: List<NewPipeSubscription>? = null
|
val subscriptions: List<NewPipeSubscription> = emptyList()
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
package com.github.libretube.obj
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.JsonNull
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class PreferenceItem(
|
data class PreferenceItem(
|
||||||
val key: String? = null,
|
val key: String? = null,
|
||||||
val value: Any? = null
|
val value: JsonPrimitive = JsonNull
|
||||||
)
|
)
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
package com.github.libretube.obj.update
|
package com.github.libretube.obj.update
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import kotlinx.serialization.json.JsonNull
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Asset(
|
data class Asset(
|
||||||
val browser_download_url: String? = null,
|
@SerialName("browser_download_url") val browserDownloadUrl: String,
|
||||||
val content_type: String? = null,
|
@SerialName("content_type") val contentType: String,
|
||||||
val created_at: String? = null,
|
@SerialName("created_at") val createdAt: Instant,
|
||||||
val download_count: Int? = null,
|
@SerialName("download_count") val downloadCount: Int,
|
||||||
val id: Int? = null,
|
val id: Int,
|
||||||
val label: Any? = null,
|
val label: JsonElement = JsonNull,
|
||||||
val name: String? = null,
|
val name: String,
|
||||||
val node_id: String? = null,
|
@SerialName("node_id") val nodeId: String,
|
||||||
val size: Int? = null,
|
val size: Int,
|
||||||
val state: String? = null,
|
val state: String,
|
||||||
val updated_at: String? = null,
|
@SerialName("updated_at") val updatedAt: Instant,
|
||||||
val uploader: Uploader? = null,
|
val uploader: User,
|
||||||
val url: String? = null
|
val url: String
|
||||||
)
|
)
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
package com.github.libretube.obj.update
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
data class Author(
|
|
||||||
val avatar_url: String? = null,
|
|
||||||
val events_url: String? = null,
|
|
||||||
val followers_url: String? = null,
|
|
||||||
val following_url: String? = null,
|
|
||||||
val gists_url: String? = null,
|
|
||||||
val gravatar_id: String? = null,
|
|
||||||
val html_url: String? = null,
|
|
||||||
val id: Int? = null,
|
|
||||||
val login: String? = null,
|
|
||||||
val node_id: String? = null,
|
|
||||||
val organizations_url: String? = null,
|
|
||||||
val received_events_url: String? = null,
|
|
||||||
val repos_url: String? = null,
|
|
||||||
val site_admin: Boolean? = null,
|
|
||||||
val starred_url: String? = null,
|
|
||||||
val subscriptions_url: String? = null,
|
|
||||||
val type: String? = null,
|
|
||||||
val url: String? = null
|
|
||||||
)
|
|
@ -1,15 +1,16 @@
|
|||||||
package com.github.libretube.obj.update
|
package com.github.libretube.obj.update
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class Reactions(
|
data class Reactions(
|
||||||
val confused: Int? = null,
|
val confused: Int,
|
||||||
val eyes: Int? = null,
|
val eyes: Int,
|
||||||
val heart: Int? = null,
|
val heart: Int,
|
||||||
val hooray: Int? = null,
|
val hooray: Int,
|
||||||
val laugh: Int? = null,
|
val laugh: Int,
|
||||||
val rocket: Int? = null,
|
val rocket: Int,
|
||||||
val total_count: Int? = null,
|
@SerialName("total_count") val totalCount: Int,
|
||||||
val url: String? = null
|
val url: String
|
||||||
)
|
)
|
||||||
|
@ -1,27 +1,29 @@
|
|||||||
package com.github.libretube.obj.update
|
package com.github.libretube.obj.update
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@Serializable
|
||||||
data class UpdateInfo(
|
data class UpdateInfo(
|
||||||
val assets: List<Asset>? = null,
|
val assets: List<Asset> = emptyList(),
|
||||||
val assets_url: String? = null,
|
@SerialName("assets_url") val assetsUrl: String,
|
||||||
val author: Author? = null,
|
val author: User,
|
||||||
val body: String? = null,
|
val body: String,
|
||||||
val created_at: String? = null,
|
@SerialName("created_at") val createdAt: Instant,
|
||||||
val draft: Boolean? = null,
|
val draft: Boolean,
|
||||||
val html_url: String? = null,
|
@SerialName("html_url") val htmlUrl: String,
|
||||||
val id: Int? = null,
|
val id: Int,
|
||||||
val mentions_count: Int? = null,
|
@SerialName("mentions_count") val mentionsCount: Int,
|
||||||
val name: String? = null,
|
val name: String,
|
||||||
val node_id: String? = null,
|
@SerialName("node_id") val nodeId: String,
|
||||||
val prerelease: Boolean? = null,
|
val prerelease: Boolean,
|
||||||
val published_at: String? = null,
|
@SerialName("published_at") val publishedAt: Instant,
|
||||||
val reactions: Reactions? = null,
|
val reactions: Reactions,
|
||||||
val tag_name: String? = null,
|
@SerialName("tag_name") val tagName: String,
|
||||||
val tarball_url: String? = null,
|
@SerialName("tarball_url") val tarballUrl: String,
|
||||||
val target_commitish: String? = null,
|
@SerialName("target_commitish") val targetCommitish: String,
|
||||||
val upload_url: String? = null,
|
@SerialName("upload_url") val uploadUrl: String,
|
||||||
val url: String? = null,
|
val url: String,
|
||||||
val zipball_url: String? = null
|
@SerialName("zipball_url") val zipballUrl: String
|
||||||
)
|
)
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
package com.github.libretube.obj.update
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
data class Uploader(
|
|
||||||
val avatar_url: String? = null,
|
|
||||||
val events_url: String? = null,
|
|
||||||
val followers_url: String? = null,
|
|
||||||
val following_url: String? = null,
|
|
||||||
val gists_url: String? = null,
|
|
||||||
val gravatar_id: String? = null,
|
|
||||||
val html_url: String? = null,
|
|
||||||
val id: Int? = null,
|
|
||||||
val login: String? = null,
|
|
||||||
val node_id: String? = null,
|
|
||||||
val organizations_url: String? = null,
|
|
||||||
val received_events_url: String? = null,
|
|
||||||
val repos_url: String? = null,
|
|
||||||
val site_admin: Boolean? = null,
|
|
||||||
val starred_url: String? = null,
|
|
||||||
val subscriptions_url: String? = null,
|
|
||||||
val type: String? = null,
|
|
||||||
val url: String? = null
|
|
||||||
)
|
|
26
app/src/main/java/com/github/libretube/obj/update/User.kt
Normal file
26
app/src/main/java/com/github/libretube/obj/update/User.kt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package com.github.libretube.obj.update
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class User(
|
||||||
|
@SerialName("avatar_url") val avatarUrl: String,
|
||||||
|
@SerialName("events_url") val eventsUrl: String,
|
||||||
|
@SerialName("followers_url") val followersUrl: String,
|
||||||
|
@SerialName("following_url") val followingUrl: String,
|
||||||
|
@SerialName("gists_url") val gistsUrl: String,
|
||||||
|
@SerialName("gravatar_id") val gravatarId: String,
|
||||||
|
@SerialName("html_url") val htmlUrl: String,
|
||||||
|
val id: Int,
|
||||||
|
val login: String,
|
||||||
|
@SerialName("node_id") val nodeId: String,
|
||||||
|
@SerialName("organizations_url") val organizationsUrl: String,
|
||||||
|
@SerialName("received_events_url") val receivedEventsUrl: String,
|
||||||
|
@SerialName("repos_url") val reposUrl: String,
|
||||||
|
@SerialName("site_admin") val siteAdmin: Boolean,
|
||||||
|
@SerialName("starred_url") val starredUrl: String,
|
||||||
|
@SerialName("subscriptions_url") val subscriptionsUrl: String,
|
||||||
|
val type: String,
|
||||||
|
val url: String
|
||||||
|
)
|
@ -13,8 +13,8 @@ import android.os.Looper
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.app.ServiceCompat
|
import androidx.core.app.ServiceCompat
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.api.JsonHelper
|
||||||
import com.github.libretube.api.RetrofitInstance
|
import com.github.libretube.api.RetrofitInstance
|
||||||
import com.github.libretube.api.obj.Segment
|
import com.github.libretube.api.obj.Segment
|
||||||
import com.github.libretube.api.obj.SegmentData
|
import com.github.libretube.api.obj.SegmentData
|
||||||
@ -40,6 +40,7 @@ import com.google.android.exoplayer2.Player
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the selected videos audio in background mode with a notification area.
|
* Loads the selected videos audio in background mode with a notification area.
|
||||||
@ -318,11 +319,10 @@ class BackgroundMode : Service() {
|
|||||||
runCatching {
|
runCatching {
|
||||||
val categories = PlayerHelper.getSponsorBlockCategories()
|
val categories = PlayerHelper.getSponsorBlockCategories()
|
||||||
if (categories.isEmpty()) return@runCatching
|
if (categories.isEmpty()) return@runCatching
|
||||||
segmentData =
|
segmentData = RetrofitInstance.api.getSegments(
|
||||||
RetrofitInstance.api.getSegments(
|
videoId,
|
||||||
videoId,
|
JsonHelper.json.encodeToString(categories)
|
||||||
ObjectMapper().writeValueAsString(categories)
|
)
|
||||||
)
|
|
||||||
checkForSegments()
|
checkForSegments()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,21 +102,19 @@ class DownloadService : Service() {
|
|||||||
Database.downloadDao().insertDownload(
|
Database.downloadDao().insertDownload(
|
||||||
Download(
|
Download(
|
||||||
videoId = videoId,
|
videoId = videoId,
|
||||||
title = streams.title ?: "",
|
title = streams.title,
|
||||||
thumbnailPath = thumbnailTargetFile.absolutePath,
|
thumbnailPath = thumbnailTargetFile.absolutePath,
|
||||||
description = streams.description ?: "",
|
description = streams.description,
|
||||||
uploadDate = streams.uploadDate,
|
uploadDate = streams.uploadDate.toString(),
|
||||||
uploader = streams.uploader ?: ""
|
uploader = streams.uploader
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
streams.thumbnailUrl?.let { url ->
|
ImageHelper.downloadImage(
|
||||||
ImageHelper.downloadImage(
|
this@DownloadService,
|
||||||
this@DownloadService,
|
streams.thumbnailUrl,
|
||||||
url,
|
thumbnailTargetFile.absolutePath
|
||||||
thumbnailTargetFile.absolutePath
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val downloadItems = streams.toDownloadItems(
|
val downloadItems = streams.toDownloadItems(
|
||||||
videoId,
|
videoId,
|
||||||
|
@ -66,22 +66,21 @@ class CommentsAdapter(
|
|||||||
commentorImage.scaleY = REPLIES_ADAPTER_SCALE
|
commentorImage.scaleY = REPLIES_ADAPTER_SCALE
|
||||||
}
|
}
|
||||||
|
|
||||||
commentInfos.text =
|
commentInfos.text = comment.author + TextUtils.SEPARATOR + comment.commentedTime
|
||||||
comment.author.toString() + TextUtils.SEPARATOR + comment.commentedTime.toString()
|
|
||||||
commentText.text = HtmlCompat.fromHtml(
|
commentText.text = HtmlCompat.fromHtml(
|
||||||
comment.commentText.toString(),
|
comment.commentText,
|
||||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||||
)
|
)
|
||||||
|
|
||||||
ImageHelper.loadImage(comment.thumbnail, commentorImage)
|
ImageHelper.loadImage(comment.thumbnail, commentorImage)
|
||||||
likesTextView.text = comment.likeCount.formatShort()
|
likesTextView.text = comment.likeCount.formatShort()
|
||||||
|
|
||||||
if (comment.verified == true) verifiedImageView.visibility = View.VISIBLE
|
if (comment.verified) verifiedImageView.visibility = View.VISIBLE
|
||||||
if (comment.pinned == true) pinnedImageView.visibility = View.VISIBLE
|
if (comment.pinned) pinnedImageView.visibility = View.VISIBLE
|
||||||
if (comment.hearted == true) heartedImageView.visibility = View.VISIBLE
|
if (comment.hearted) heartedImageView.visibility = View.VISIBLE
|
||||||
if (comment.repliesPage != null) repliesAvailable.visibility = View.VISIBLE
|
if (comment.repliesPage != null) repliesAvailable.visibility = View.VISIBLE
|
||||||
if ((comment.replyCount ?: -1L) > 0L) {
|
if (comment.replyCount > 0L) {
|
||||||
repliesCount.text = comment.replyCount?.formatShort()
|
repliesCount.text = comment.replyCount.formatShort()
|
||||||
}
|
}
|
||||||
|
|
||||||
commentorImage.setOnClickListener {
|
commentorImage.setOnClickListener {
|
||||||
@ -99,7 +98,7 @@ class CommentsAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
root.setOnLongClickListener {
|
root.setOnLongClickListener {
|
||||||
ClipboardHelper(root.context).save(comment.commentText.toString())
|
ClipboardHelper(root.context).save(comment.commentText)
|
||||||
Toast.makeText(root.context, R.string.copied, Toast.LENGTH_SHORT).show()
|
Toast.makeText(root.context, R.string.copied, Toast.LENGTH_SHORT).show()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -83,13 +83,13 @@ class SearchAdapter(
|
|||||||
private fun bindWatch(item: ContentItem, binding: VideoRowBinding) {
|
private fun bindWatch(item: ContentItem, binding: VideoRowBinding) {
|
||||||
binding.apply {
|
binding.apply {
|
||||||
ImageHelper.loadImage(item.thumbnail, thumbnail)
|
ImageHelper.loadImage(item.thumbnail, thumbnail)
|
||||||
thumbnailDuration.setFormattedDuration(item.duration!!, item.isShort)
|
thumbnailDuration.setFormattedDuration(item.duration, item.isShort)
|
||||||
ImageHelper.loadImage(item.uploaderAvatar, channelImage)
|
ImageHelper.loadImage(item.uploaderAvatar, channelImage)
|
||||||
videoTitle.text = item.title
|
videoTitle.text = item.title
|
||||||
val viewsString = if (item.views?.toInt() != -1) item.views.formatShort() else ""
|
val viewsString = if (item.views != -1L) item.views.formatShort() else ""
|
||||||
val uploadDate = if (item.uploadedDate != null) item.uploadedDate else ""
|
val uploadDate = item.uploadedDate.orEmpty()
|
||||||
videoInfo.text =
|
videoInfo.text =
|
||||||
if (viewsString != "" && uploadDate != "") {
|
if (viewsString.isNotEmpty() && uploadDate.isNotEmpty()) {
|
||||||
"$viewsString • $uploadDate"
|
"$viewsString • $uploadDate"
|
||||||
} else {
|
} else {
|
||||||
viewsString + uploadDate
|
viewsString + uploadDate
|
||||||
@ -98,7 +98,7 @@ class SearchAdapter(
|
|||||||
root.setOnClickListener {
|
root.setOnClickListener {
|
||||||
NavigationHelper.navigateVideo(root.context, item.url)
|
NavigationHelper.navigateVideo(root.context, item.url)
|
||||||
}
|
}
|
||||||
val videoId = item.url!!.toID()
|
val videoId = item.url.toID()
|
||||||
val videoName = item.title!!
|
val videoName = item.title!!
|
||||||
root.setOnLongClickListener {
|
root.setOnLongClickListener {
|
||||||
VideoOptionsBottomSheet(videoId, videoName)
|
VideoOptionsBottomSheet(videoId, videoName)
|
||||||
@ -111,7 +111,7 @@ class SearchAdapter(
|
|||||||
channelContainer.setOnClickListener {
|
channelContainer.setOnClickListener {
|
||||||
NavigationHelper.navigateChannel(root.context, item.uploaderUrl)
|
NavigationHelper.navigateChannel(root.context, item.uploaderUrl)
|
||||||
}
|
}
|
||||||
watchProgress.setWatchProgressLength(videoId, item.duration!!)
|
watchProgress.setWatchProgressLength(videoId, item.duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,12 +135,12 @@ class SearchAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
root.setOnLongClickListener {
|
root.setOnLongClickListener {
|
||||||
ChannelOptionsBottomSheet(item.url!!.toID(), item.name)
|
ChannelOptionsBottomSheet(item.url.toID(), item.name)
|
||||||
.show((root.context as BaseActivity).supportFragmentManager)
|
.show((root.context as BaseActivity).supportFragmentManager)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.searchSubButton.setupSubscriptionButton(item.url?.toID(), item.name?.toID())
|
binding.searchSubButton.setupSubscriptionButton(item.url.toID(), item.name?.toID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ class SearchAdapter(
|
|||||||
) {
|
) {
|
||||||
binding.apply {
|
binding.apply {
|
||||||
ImageHelper.loadImage(item.thumbnail, playlistThumbnail)
|
ImageHelper.loadImage(item.thumbnail, playlistThumbnail)
|
||||||
if (item.videos?.toInt() != -1) videoCount.text = item.videos.toString()
|
if (item.videos != -1L) videoCount.text = item.videos.toString()
|
||||||
playlistTitle.text = item.name
|
playlistTitle.text = item.name
|
||||||
playlistDescription.text = item.uploaderName
|
playlistDescription.text = item.uploaderName
|
||||||
root.setOnClickListener {
|
root.setOnClickListener {
|
||||||
@ -158,7 +158,7 @@ class SearchAdapter(
|
|||||||
}
|
}
|
||||||
deletePlaylist.visibility = View.GONE
|
deletePlaylist.visibility = View.GONE
|
||||||
root.setOnLongClickListener {
|
root.setOnLongClickListener {
|
||||||
val playlistId = item.url!!.toID()
|
val playlistId = item.url.toID()
|
||||||
val playlistName = item.name!!
|
val playlistName = item.name!!
|
||||||
PlaylistOptionsBottomSheet(playlistId, playlistName, PlaylistType.PUBLIC)
|
PlaylistOptionsBottomSheet(playlistId, playlistName, PlaylistType.PUBLIC)
|
||||||
.show(
|
.show(
|
||||||
|
@ -11,6 +11,8 @@ import com.github.libretube.obj.BackupFile
|
|||||||
import com.github.libretube.obj.PreferenceItem
|
import com.github.libretube.obj.PreferenceItem
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.util.PreferenceHelper
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.serialization.json.JsonNull
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
|
||||||
class BackupDialog(
|
class BackupDialog(
|
||||||
private val createBackupFile: (BackupFile) -> Unit
|
private val createBackupFile: (BackupFile) -> Unit
|
||||||
@ -45,8 +47,14 @@ class BackupDialog(
|
|||||||
})
|
})
|
||||||
|
|
||||||
object Preferences : BackupOption(R.string.preferences, onSelected = { file ->
|
object Preferences : BackupOption(R.string.preferences, onSelected = { file ->
|
||||||
file.preferences = PreferenceHelper.settings.all.map {
|
file.preferences = PreferenceHelper.settings.all.map { (key, value) ->
|
||||||
PreferenceItem(it.key, it.value)
|
val jsonValue = when (value) {
|
||||||
|
is Number -> JsonPrimitive(value)
|
||||||
|
is Boolean -> JsonPrimitive(value)
|
||||||
|
is String -> JsonPrimitive(value)
|
||||||
|
else -> JsonNull
|
||||||
|
}
|
||||||
|
PreferenceItem(key, jsonValue)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import androidx.fragment.app.DialogFragment
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.api.RetrofitInstance
|
import com.github.libretube.api.RetrofitInstance
|
||||||
|
import com.github.libretube.api.obj.DeleteUserRequest
|
||||||
import com.github.libretube.databinding.DialogDeleteAccountBinding
|
import com.github.libretube.databinding.DialogDeleteAccountBinding
|
||||||
import com.github.libretube.extensions.TAG
|
import com.github.libretube.extensions.TAG
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.util.PreferenceHelper
|
||||||
@ -41,10 +42,7 @@ class DeleteAccountDialog : DialogFragment() {
|
|||||||
val token = PreferenceHelper.getToken()
|
val token = PreferenceHelper.getToken()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RetrofitInstance.authApi.deleteAccount(
|
RetrofitInstance.authApi.deleteAccount(token, DeleteUserRequest(password))
|
||||||
token,
|
|
||||||
com.github.libretube.api.obj.DeleteUserRequest(password)
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG(), e.toString())
|
Log.e(TAG(), e.toString())
|
||||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||||
|
@ -74,7 +74,7 @@ class DownloadDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initDownloadOptions(streams: Streams) {
|
private fun initDownloadOptions(streams: Streams) {
|
||||||
binding.fileName.setText(streams.title.toString())
|
binding.fileName.setText(streams.title)
|
||||||
|
|
||||||
val vidName = arrayListOf<String>()
|
val vidName = arrayListOf<String>()
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ class DownloadDialog(
|
|||||||
vidName.add(getString(R.string.no_video))
|
vidName.add(getString(R.string.no_video))
|
||||||
|
|
||||||
// add all available video streams
|
// add all available video streams
|
||||||
for (vid in streams.videoStreams!!) {
|
for (vid in streams.videoStreams) {
|
||||||
if (vid.url != null) {
|
if (vid.url != null) {
|
||||||
val name = vid.quality + " " + vid.format
|
val name = vid.quality + " " + vid.format
|
||||||
vidName.add(name)
|
vidName.add(name)
|
||||||
@ -95,7 +95,7 @@ class DownloadDialog(
|
|||||||
audioName.add(getString(R.string.no_audio))
|
audioName.add(getString(R.string.no_audio))
|
||||||
|
|
||||||
// add all available audio streams
|
// add all available audio streams
|
||||||
for (audio in streams.audioStreams!!) {
|
for (audio in streams.audioStreams) {
|
||||||
if (audio.url != null) {
|
if (audio.url != null) {
|
||||||
val name = audio.quality + " " + audio.format
|
val name = audio.quality + " " + audio.format
|
||||||
audioName.add(name)
|
audioName.add(name)
|
||||||
@ -108,7 +108,7 @@ class DownloadDialog(
|
|||||||
subtitleName.add(getString(R.string.no_subtitle))
|
subtitleName.add(getString(R.string.no_subtitle))
|
||||||
|
|
||||||
// add all available subtitles
|
// add all available subtitles
|
||||||
for (subtitle in streams.subtitles!!) {
|
for (subtitle in streams.subtitles) {
|
||||||
if (subtitle.url != null) {
|
if (subtitle.url != null) {
|
||||||
subtitleName.add(subtitle.name.toString())
|
subtitleName.add(subtitle.name.toString())
|
||||||
}
|
}
|
||||||
|
@ -92,8 +92,8 @@ class LoginDialog : DialogFragment() {
|
|||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
|
|
||||||
PreferenceHelper.setToken(response.token!!)
|
PreferenceHelper.setToken(response.token)
|
||||||
PreferenceHelper.setUsername(login.username!!)
|
PreferenceHelper.setUsername(login.username)
|
||||||
|
|
||||||
dialog?.dismiss()
|
dialog?.dismiss()
|
||||||
activity?.recreate()
|
activity?.recreate()
|
||||||
|
@ -29,7 +29,7 @@ class UpdateDialog(
|
|||||||
intent.putExtra("downloadUrl", downloadUrl)
|
intent.putExtra("downloadUrl", downloadUrl)
|
||||||
context?.startService(intent)
|
context?.startService(intent)
|
||||||
} else {
|
} else {
|
||||||
val uri = Uri.parse(updateInfo.html_url)
|
val uri = Uri.parse(updateInfo.htmlUrl)
|
||||||
val intent = Intent(Intent.ACTION_VIEW).setData(uri)
|
val intent = Intent(Intent.ACTION_VIEW).setData(uri)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
@ -40,8 +40,10 @@ class UpdateDialog(
|
|||||||
private fun getDownloadUrl(updateInfo: UpdateInfo): String? {
|
private fun getDownloadUrl(updateInfo: UpdateInfo): String? {
|
||||||
val supportedArchitectures = Build.SUPPORTED_ABIS
|
val supportedArchitectures = Build.SUPPORTED_ABIS
|
||||||
supportedArchitectures.forEach { arch ->
|
supportedArchitectures.forEach { arch ->
|
||||||
updateInfo.assets?.forEach { asset ->
|
updateInfo.assets.forEach { asset ->
|
||||||
if (asset.name?.contains(arch) == true) return asset.browser_download_url
|
if (asset.name.contains(arch)) {
|
||||||
|
return asset.browserDownloadUrl
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
@ -142,7 +142,7 @@ class ChannelFragment : BaseFragment() {
|
|||||||
|
|
||||||
binding.channelShare.setOnClickListener {
|
binding.channelShare.setOnClickListener {
|
||||||
val shareDialog = ShareDialog(
|
val shareDialog = ShareDialog(
|
||||||
response.id!!.toID(),
|
response.id.toID(),
|
||||||
ShareObjectType.CHANNEL,
|
ShareObjectType.CHANNEL,
|
||||||
shareData
|
shareData
|
||||||
)
|
)
|
||||||
@ -169,10 +169,10 @@ class ChannelFragment : BaseFragment() {
|
|||||||
R.string.subscribers,
|
R.string.subscribers,
|
||||||
response.subscriberCount.formatShort()
|
response.subscriberCount.formatShort()
|
||||||
)
|
)
|
||||||
if (response.description?.trim() == "") {
|
if (response.description.isBlank()) {
|
||||||
binding.channelDescription.visibility = View.GONE
|
binding.channelDescription.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
binding.channelDescription.text = response.description?.trim()
|
binding.channelDescription.text = response.description.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.channelDescription.setOnClickListener {
|
binding.channelDescription.setOnClickListener {
|
||||||
@ -186,13 +186,13 @@ class ChannelFragment : BaseFragment() {
|
|||||||
|
|
||||||
// recyclerview of the videos by the channel
|
// recyclerview of the videos by the channel
|
||||||
channelAdapter = VideosAdapter(
|
channelAdapter = VideosAdapter(
|
||||||
response.relatedStreams.orEmpty().toMutableList(),
|
response.relatedStreams.toMutableList(),
|
||||||
forceMode = VideosAdapter.Companion.ForceMode.CHANNEL
|
forceMode = VideosAdapter.Companion.ForceMode.CHANNEL
|
||||||
)
|
)
|
||||||
binding.channelRecView.adapter = channelAdapter
|
binding.channelRecView.adapter = channelAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
response.tabs?.let { setupTabs(it) }
|
setupTabs(response.tabs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,16 +230,13 @@ class ChannelFragment : BaseFragment() {
|
|||||||
|
|
||||||
private fun loadTab(tab: ChannelTab) {
|
private fun loadTab(tab: ChannelTab) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
tab.data ?: return@launch
|
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.getChannelTab(tab.data)
|
RetrofitInstance.api.getChannelTab(tab.data)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
val adapter = SearchAdapter(
|
val adapter = SearchAdapter(response.content.toMutableList())
|
||||||
response.content.toMutableList()
|
|
||||||
)
|
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
binding.channelRecView.adapter = adapter
|
binding.channelRecView.adapter = adapter
|
||||||
@ -275,7 +272,7 @@ class ChannelFragment : BaseFragment() {
|
|||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
}
|
}
|
||||||
nextPage = response.nextpage
|
nextPage = response.nextpage
|
||||||
channelAdapter?.insertItems(response.relatedStreams.orEmpty())
|
channelAdapter?.insertItems(response.relatedStreams)
|
||||||
isLoading = false
|
isLoading = false
|
||||||
binding.channelRefresh.isRefreshing = false
|
binding.channelRefresh.isRefreshing = false
|
||||||
}
|
}
|
||||||
@ -291,9 +288,9 @@ class ChannelFragment : BaseFragment() {
|
|||||||
) {
|
) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val newContent = try {
|
val newContent = try {
|
||||||
RetrofitInstance.api.getChannelTab(tab.data ?: "", nextPage)
|
RetrofitInstance.api.getChannelTab(tab.data, nextPage)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
Log.e(TAG(), "Exception: $e")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
onNewNextPage.invoke(newContent?.nextpage)
|
onNewNextPage.invoke(newContent?.nextpage)
|
||||||
|
@ -37,9 +37,9 @@ import androidx.fragment.app.activityViewModels
|
|||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.api.CronetHelper
|
import com.github.libretube.api.CronetHelper
|
||||||
|
import com.github.libretube.api.JsonHelper
|
||||||
import com.github.libretube.api.RetrofitInstance
|
import com.github.libretube.api.RetrofitInstance
|
||||||
import com.github.libretube.api.obj.ChapterSegment
|
import com.github.libretube.api.obj.ChapterSegment
|
||||||
import com.github.libretube.api.obj.PipedStream
|
import com.github.libretube.api.obj.PipedStream
|
||||||
@ -119,6 +119,8 @@ import kotlin.math.abs
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
import org.chromium.net.CronetEngine
|
import org.chromium.net.CronetEngine
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
|
|
||||||
@ -685,9 +687,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
} else {
|
} else {
|
||||||
PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!))
|
PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!))
|
||||||
if (PlayerHelper.autoInsertRelatedVideos) {
|
if (PlayerHelper.autoInsertRelatedVideos) {
|
||||||
PlayingQueue.add(
|
PlayingQueue.add(*streams.relatedStreams.toTypedArray())
|
||||||
*streams.relatedStreams.orEmpty().toTypedArray()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -748,7 +748,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
segmentData =
|
segmentData =
|
||||||
RetrofitInstance.api.getSegments(
|
RetrofitInstance.api.getSegments(
|
||||||
videoId!!,
|
videoId!!,
|
||||||
ObjectMapper().writeValueAsString(categories)
|
JsonHelper.json.encodeToString(categories)
|
||||||
)
|
)
|
||||||
if (segmentData.segments.isEmpty()) return@runCatching
|
if (segmentData.segments.isEmpty()) return@runCatching
|
||||||
playerBinding.exoProgress.setSegments(segmentData.segments)
|
playerBinding.exoProgress.setSegments(segmentData.segments)
|
||||||
@ -776,8 +776,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
playerBinding.liveDiff.text = "-$diffText"
|
playerBinding.liveDiff.text = "-$diffText"
|
||||||
}
|
}
|
||||||
// call the function again after 100ms
|
// call the function again after 100ms
|
||||||
handler
|
handler.postDelayed(this@PlayerFragment::refreshLiveStatus, 100)
|
||||||
.postDelayed(this@PlayerFragment::refreshLiveStatus, 100)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// seek to saved watch position if available
|
// seek to saved watch position if available
|
||||||
@ -797,7 +796,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// position is almost the end of the video => don't seek, start from beginning
|
// position is almost the end of the video => don't seek, start from beginning
|
||||||
if (position != null && position < streams.duration!! * 1000 * 0.9) {
|
if (position != null && position < streams.duration * 1000 * 0.9) {
|
||||||
exoPlayer.seekTo(position)
|
exoPlayer.seekTo(position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -826,7 +825,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
playerBinding.exoProgress.setPlayer(exoPlayer)
|
playerBinding.exoProgress.setPlayer(exoPlayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun localizedDate(date: String?): String? {
|
private fun localizedDate(date: LocalDate): String {
|
||||||
val locale = ConfigurationCompat.getLocales(resources.configuration)[0]!!
|
val locale = ConfigurationCompat.getLocales(resources.configuration)[0]!!
|
||||||
return TextUtils.localizeDate(date, locale)
|
return TextUtils.localizeDate(date, locale)
|
||||||
}
|
}
|
||||||
@ -870,12 +869,12 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
|
|
||||||
playerChannelSubCount.text = context?.getString(
|
playerChannelSubCount.text = context?.getString(
|
||||||
R.string.subscribers,
|
R.string.subscribers,
|
||||||
streams.uploaderSubscriberCount?.formatShort()
|
streams.uploaderSubscriberCount.formatShort()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// duration that's not greater than 0 indicates that the video is live
|
// duration that's not greater than 0 indicates that the video is live
|
||||||
if (streams.duration!! <= 0) {
|
if (streams.duration <= 0) {
|
||||||
isLive = true
|
isLive = true
|
||||||
handleLiveVideo()
|
handleLiveVideo()
|
||||||
}
|
}
|
||||||
@ -883,10 +882,8 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
playerBinding.exoTitle.text = streams.title
|
playerBinding.exoTitle.text = streams.title
|
||||||
|
|
||||||
// init the chapters recyclerview
|
// init the chapters recyclerview
|
||||||
if (streams.chapters != null) {
|
chapters = streams.chapters
|
||||||
chapters = streams.chapters.orEmpty()
|
initializeChapters()
|
||||||
initializeChapters()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listener for play and pause icon change
|
// Listener for play and pause icon change
|
||||||
exoPlayer.addListener(object : Player.Listener {
|
exoPlayer.addListener(object : Player.Listener {
|
||||||
@ -972,7 +969,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
})
|
})
|
||||||
|
|
||||||
binding.relPlayerDownload.setOnClickListener {
|
binding.relPlayerDownload.setOnClickListener {
|
||||||
if (streams.duration!! <= 0) {
|
if (streams.duration <= 0) {
|
||||||
Toast.makeText(context, R.string.cannotDownload, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.cannotDownload, Toast.LENGTH_SHORT).show()
|
||||||
} else if (!DownloadService.IS_DOWNLOAD_RUNNING) {
|
} else if (!DownloadService.IS_DOWNLOAD_RUNNING) {
|
||||||
val newFragment = DownloadDialog(videoId!!)
|
val newFragment = DownloadDialog(videoId!!)
|
||||||
@ -995,7 +992,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
}
|
}
|
||||||
initializeRelatedVideos(streams.relatedStreams)
|
initializeRelatedVideos(streams.relatedStreams)
|
||||||
// set video description
|
// set video description
|
||||||
val description = streams.description!!
|
val description = streams.description
|
||||||
|
|
||||||
setupDescription(binding.playerDescription, description)
|
setupDescription(binding.playerDescription, description)
|
||||||
|
|
||||||
@ -1009,7 +1006,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
|
|
||||||
// update the subscribed state
|
// update the subscribed state
|
||||||
binding.playerSubscribe.setupSubscriptionButton(
|
binding.playerSubscribe.setupSubscriptionButton(
|
||||||
this.streams.uploaderUrl?.toID(),
|
this.streams.uploaderUrl.toID(),
|
||||||
this.streams.uploader
|
this.streams.uploader
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1268,9 +1265,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
private fun setResolutionAndSubtitles() {
|
private fun setResolutionAndSubtitles() {
|
||||||
// create a list of subtitles
|
// create a list of subtitles
|
||||||
subtitles = mutableListOf()
|
subtitles = mutableListOf()
|
||||||
val subtitlesNamesList = mutableListOf(context?.getString(R.string.none)!!)
|
val subtitlesNamesList = mutableListOf(getString(R.string.none))
|
||||||
val subtitleCodesList = mutableListOf("")
|
val subtitleCodesList = mutableListOf("")
|
||||||
streams.subtitles.orEmpty().forEach {
|
streams.subtitles.forEach {
|
||||||
subtitles.add(
|
subtitles.add(
|
||||||
SubtitleConfiguration.Builder(it.url!!.toUri())
|
SubtitleConfiguration.Builder(it.url!!.toUri())
|
||||||
.setMimeType(it.mimeType!!) // The correct MIME type (required).
|
.setMimeType(it.mimeType!!) // The correct MIME type (required).
|
||||||
@ -1300,7 +1297,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
if (defaultResolution != "") setPlayerResolution(defaultResolution.toInt())
|
if (defaultResolution != "") setPlayerResolution(defaultResolution.toInt())
|
||||||
|
|
||||||
if (!PreferenceHelper.getBoolean(PreferenceKeys.USE_HLS_OVER_DASH, false) &&
|
if (!PreferenceHelper.getBoolean(PreferenceKeys.USE_HLS_OVER_DASH, false) &&
|
||||||
streams.videoStreams.orEmpty().isNotEmpty()
|
streams.videoStreams.isNotEmpty()
|
||||||
) {
|
) {
|
||||||
val uri = let {
|
val uri = let {
|
||||||
streams.dash?.toUri()
|
streams.dash?.toUri()
|
||||||
@ -1377,16 +1374,14 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCaptionsClicked() {
|
override fun onCaptionsClicked() {
|
||||||
if (!this@PlayerFragment::streams.isInitialized ||
|
if (!this@PlayerFragment::streams.isInitialized || streams.subtitles.isEmpty()) {
|
||||||
streams.subtitles.isNullOrEmpty()
|
|
||||||
) {
|
|
||||||
Toast.makeText(context, R.string.no_subtitles_available, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.no_subtitles_available, Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val subtitlesNamesList = mutableListOf(context?.getString(R.string.none)!!)
|
val subtitlesNamesList = mutableListOf(getString(R.string.none))
|
||||||
val subtitleCodesList = mutableListOf("")
|
val subtitleCodesList = mutableListOf("")
|
||||||
streams.subtitles!!.forEach {
|
streams.subtitles.forEach {
|
||||||
subtitlesNamesList += it.name!!
|
subtitlesNamesList += it.name!!
|
||||||
subtitleCodesList += it.code!!
|
subtitleCodesList += it.code!!
|
||||||
}
|
}
|
||||||
@ -1515,9 +1510,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
playerBinding.seekbarPreview.visibility = View.GONE
|
playerBinding.seekbarPreview.visibility = View.GONE
|
||||||
playerBinding.exoProgress.addListener(
|
playerBinding.exoProgress.addListener(
|
||||||
SeekbarPreviewListener(
|
SeekbarPreviewListener(
|
||||||
streams.previewFrames.orEmpty(),
|
streams.previewFrames,
|
||||||
playerBinding.seekbarPreview,
|
playerBinding.seekbarPreview,
|
||||||
streams.duration!! * 1000
|
streams.duration * 1000
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ class PlaylistFragment : BaseFragment() {
|
|||||||
Log.e(TAG(), e.toString())
|
Log.e(TAG(), e.toString())
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
}
|
}
|
||||||
playlistFeed = response.relatedStreams.orEmpty().toMutableList()
|
playlistFeed = response.relatedStreams.toMutableList()
|
||||||
binding.playlistScrollview.visibility = View.VISIBLE
|
binding.playlistScrollview.visibility = View.VISIBLE
|
||||||
nextPage = response.nextpage
|
nextPage = response.nextpage
|
||||||
playlistName = response.name
|
playlistName = response.name
|
||||||
@ -140,7 +140,7 @@ class PlaylistFragment : BaseFragment() {
|
|||||||
if (playlistFeed.isEmpty()) return@setOnClickListener
|
if (playlistFeed.isEmpty()) return@setOnClickListener
|
||||||
NavigationHelper.navigateVideo(
|
NavigationHelper.navigateVideo(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
response.relatedStreams!!.first().url?.toID(),
|
response.relatedStreams.first().url?.toID(),
|
||||||
playlistId
|
playlistId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -279,15 +279,9 @@ class PlaylistFragment : BaseFragment() {
|
|||||||
val response = try {
|
val response = try {
|
||||||
// load locally stored playlists with the auth api
|
// load locally stored playlists with the auth api
|
||||||
if (playlistType == PlaylistType.PRIVATE) {
|
if (playlistType == PlaylistType.PRIVATE) {
|
||||||
RetrofitInstance.authApi.getPlaylistNextPage(
|
RetrofitInstance.authApi.getPlaylistNextPage(playlistId!!, nextPage!!)
|
||||||
playlistId!!,
|
|
||||||
nextPage!!
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
RetrofitInstance.api.getPlaylistNextPage(
|
RetrofitInstance.api.getPlaylistNextPage(playlistId!!, nextPage!!)
|
||||||
playlistId!!,
|
|
||||||
nextPage!!
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG(), e.toString())
|
Log.e(TAG(), e.toString())
|
||||||
@ -295,7 +289,7 @@ class PlaylistFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nextPage = response.nextpage
|
nextPage = response.nextpage
|
||||||
playlistAdapter?.updateItems(response.relatedStreams!!)
|
playlistAdapter?.updateItems(response.relatedStreams)
|
||||||
isLoading = false
|
isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
@ -94,13 +95,9 @@ class SearchResultFragment : BaseFragment() {
|
|||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
}
|
}
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
searchAdapter = SearchAdapter(response.items.orEmpty().toMutableList())
|
searchAdapter = SearchAdapter(response.items.toMutableList())
|
||||||
binding.searchRecycler.adapter = searchAdapter
|
binding.searchRecycler.adapter = searchAdapter
|
||||||
binding.noSearchResult.visibility = if (response.items.orEmpty().isEmpty()) {
|
binding.noSearchResult.isVisible = response.items.isEmpty()
|
||||||
View.VISIBLE
|
|
||||||
} else {
|
|
||||||
View.GONE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
nextPage = response.nextpage
|
nextPage = response.nextpage
|
||||||
}
|
}
|
||||||
@ -124,8 +121,8 @@ class SearchResultFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
nextPage = response.nextpage!!
|
nextPage = response.nextpage!!
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
if (response.items?.isNotEmpty() == true) {
|
if (response.items.isNotEmpty()) {
|
||||||
searchAdapter.updateItems(response.items.toMutableList())
|
searchAdapter.updateItems(response.items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ class CommentsViewModel : ViewModel() {
|
|||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val updatedPage = commentsPage.value?.apply {
|
val updatedPage = commentsPage.value?.apply {
|
||||||
comments = comments.plus(response.comments).toMutableList()
|
comments += response.comments
|
||||||
}
|
}
|
||||||
nextPage = response.nextpage
|
nextPage = response.nextpage
|
||||||
commentsPage.postValue(updatedPage)
|
commentsPage.postValue(updatedPage)
|
||||||
|
@ -68,7 +68,7 @@ class BackupRestoreSettings : BasePreferenceFragment() {
|
|||||||
createBackupFile = registerForActivityResult(
|
createBackupFile = registerForActivityResult(
|
||||||
CreateDocument("application/json")
|
CreateDocument("application/json")
|
||||||
) { uri: Uri? ->
|
) { uri: Uri? ->
|
||||||
BackupHelper(requireContext()).advancedBackup(uri, backupFile)
|
BackupHelper(requireContext()).createAdvancedBackup(uri, backupFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -137,8 +137,8 @@ class InstanceSettings : BasePreferenceFragment() {
|
|||||||
|
|
||||||
response?.sortBy { it.name }
|
response?.sortBy { it.name }
|
||||||
|
|
||||||
instanceNames.addAll(response.orEmpty().map { it.name ?: "" })
|
instanceNames.addAll(response.orEmpty().map { it.name })
|
||||||
instanceValues.addAll(response.orEmpty().map { it.api_url ?: "" })
|
instanceValues.addAll(response.orEmpty().map { it.apiUrl })
|
||||||
|
|
||||||
customInstances.forEach { instance ->
|
customInstances.forEach { instance ->
|
||||||
instanceNames += instance.name
|
instanceNames += instance.name
|
||||||
|
@ -20,7 +20,7 @@ import kotlinx.coroutines.runBlocking
|
|||||||
*/
|
*/
|
||||||
class ChannelOptionsBottomSheet(
|
class ChannelOptionsBottomSheet(
|
||||||
private val channelId: String,
|
private val channelId: String,
|
||||||
private val channelName: String?
|
channelName: String?
|
||||||
) : BaseBottomSheet() {
|
) : BaseBottomSheet() {
|
||||||
private val shareData = ShareData(currentChannel = channelName)
|
private val shareData = ShareData(currentChannel = channelName)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -44,7 +44,7 @@ class ChannelOptionsBottomSheet(
|
|||||||
val channel = runBlocking {
|
val channel = runBlocking {
|
||||||
RetrofitInstance.api.getChannel(channelId)
|
RetrofitInstance.api.getChannel(channelId)
|
||||||
}
|
}
|
||||||
channel.relatedStreams?.firstOrNull()?.url?.toID()?.let {
|
channel.relatedStreams.firstOrNull()?.url?.toID()?.let {
|
||||||
NavigationHelper.navigateVideo(
|
NavigationHelper.navigateVideo(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
it,
|
it,
|
||||||
@ -60,7 +60,7 @@ class ChannelOptionsBottomSheet(
|
|||||||
val channel = runBlocking {
|
val channel = runBlocking {
|
||||||
RetrofitInstance.api.getChannel(channelId)
|
RetrofitInstance.api.getChannel(channelId)
|
||||||
}
|
}
|
||||||
channel.relatedStreams?.firstOrNull()?.url?.toID()?.let {
|
channel.relatedStreams.firstOrNull()?.url?.toID()?.let {
|
||||||
BackgroundHelper.playOnBackground(
|
BackgroundHelper.playOnBackground(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
videoId = it,
|
videoId = it,
|
||||||
|
@ -2,15 +2,22 @@ package com.github.libretube.util
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.github.libretube.api.JsonHelper
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
import com.github.libretube.db.DatabaseHolder.Companion.Database
|
import com.github.libretube.db.DatabaseHolder.Companion.Database
|
||||||
|
import com.github.libretube.extensions.TAG
|
||||||
import com.github.libretube.extensions.query
|
import com.github.libretube.extensions.query
|
||||||
import com.github.libretube.obj.BackupFile
|
import com.github.libretube.obj.BackupFile
|
||||||
import com.github.libretube.obj.PreferenceItem
|
import com.github.libretube.obj.PreferenceItem
|
||||||
import java.io.FileOutputStream
|
import kotlinx.serialization.json.booleanOrNull
|
||||||
|
import kotlinx.serialization.json.decodeFromStream
|
||||||
|
import kotlinx.serialization.json.encodeToStream
|
||||||
|
import kotlinx.serialization.json.floatOrNull
|
||||||
|
import kotlinx.serialization.json.intOrNull
|
||||||
|
import kotlinx.serialization.json.longOrNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Backup and restore the preferences
|
* Backup and restore the preferences
|
||||||
@ -19,18 +26,15 @@ class BackupHelper(private val context: Context) {
|
|||||||
/**
|
/**
|
||||||
* Write a [BackupFile] containing the database content as well as the preferences
|
* Write a [BackupFile] containing the database content as well as the preferences
|
||||||
*/
|
*/
|
||||||
fun advancedBackup(uri: Uri?, backupFile: BackupFile) {
|
fun createAdvancedBackup(uri: Uri?, backupFile: BackupFile) {
|
||||||
if (uri == null) return
|
uri?.let {
|
||||||
try {
|
try {
|
||||||
context.contentResolver.openFileDescriptor(uri, "w")?.use {
|
context.contentResolver.openOutputStream(it)?.use { outputStream ->
|
||||||
FileOutputStream(it.fileDescriptor).use { fileOutputStream ->
|
JsonHelper.json.encodeToStream(backupFile, outputStream)
|
||||||
fileOutputStream.write(
|
|
||||||
ObjectMapper().writeValueAsBytes(backupFile)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG(), "Error while writing backup: $e")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,36 +42,33 @@ class BackupHelper(private val context: Context) {
|
|||||||
* Restore data from a [BackupFile]
|
* Restore data from a [BackupFile]
|
||||||
*/
|
*/
|
||||||
fun restoreAdvancedBackup(uri: Uri?) {
|
fun restoreAdvancedBackup(uri: Uri?) {
|
||||||
if (uri == null) return
|
val backupFile = uri?.let {
|
||||||
|
context.contentResolver.openInputStream(it)?.use { inputStream ->
|
||||||
val mapper = ObjectMapper()
|
JsonHelper.json.decodeFromStream<BackupFile>(inputStream)
|
||||||
val json = context.contentResolver.openInputStream(uri)?.use {
|
}
|
||||||
it.bufferedReader().use { reader -> reader.readText() }
|
} ?: return
|
||||||
}.orEmpty()
|
|
||||||
|
|
||||||
val backupFile = mapper.readValue(json, BackupFile::class.java)
|
|
||||||
|
|
||||||
query {
|
query {
|
||||||
Database.watchHistoryDao().insertAll(
|
Database.watchHistoryDao().insertAll(
|
||||||
*backupFile.watchHistory.orEmpty().toTypedArray()
|
*backupFile.watchHistory.toTypedArray()
|
||||||
)
|
)
|
||||||
Database.searchHistoryDao().insertAll(
|
Database.searchHistoryDao().insertAll(
|
||||||
*backupFile.searchHistory.orEmpty().toTypedArray()
|
*backupFile.searchHistory.toTypedArray()
|
||||||
)
|
)
|
||||||
Database.watchPositionDao().insertAll(
|
Database.watchPositionDao().insertAll(
|
||||||
*backupFile.watchPositions.orEmpty().toTypedArray()
|
*backupFile.watchPositions.toTypedArray()
|
||||||
)
|
)
|
||||||
Database.localSubscriptionDao().insertAll(
|
Database.localSubscriptionDao().insertAll(
|
||||||
*backupFile.localSubscriptions.orEmpty().toTypedArray()
|
*backupFile.localSubscriptions.toTypedArray()
|
||||||
)
|
)
|
||||||
Database.customInstanceDao().insertAll(
|
Database.customInstanceDao().insertAll(
|
||||||
*backupFile.customInstances.orEmpty().toTypedArray()
|
*backupFile.customInstances.toTypedArray()
|
||||||
)
|
)
|
||||||
Database.playlistBookmarkDao().insertAll(
|
Database.playlistBookmarkDao().insertAll(
|
||||||
*backupFile.playlistBookmarks.orEmpty().toTypedArray()
|
*backupFile.playlistBookmarks.toTypedArray()
|
||||||
)
|
)
|
||||||
|
|
||||||
backupFile.localPlaylists?.forEach {
|
backupFile.localPlaylists.forEach {
|
||||||
Database.localPlaylistsDao().createPlaylist(it.playlist)
|
Database.localPlaylistsDao().createPlaylist(it.playlist)
|
||||||
val playlistId = Database.localPlaylistsDao().getAll().last().playlist.id
|
val playlistId = Database.localPlaylistsDao().getAll().last().playlist.id
|
||||||
it.videos.forEach {
|
it.videos.forEach {
|
||||||
@ -90,18 +91,26 @@ class BackupHelper(private val context: Context) {
|
|||||||
clear()
|
clear()
|
||||||
|
|
||||||
// decide for each preference which type it is and save it to the preferences
|
// decide for each preference which type it is and save it to the preferences
|
||||||
preferences.forEach {
|
preferences.forEach { (key, jsonValue) ->
|
||||||
when (it.value) {
|
val value = if (jsonValue.isString) {
|
||||||
is Boolean -> putBoolean(it.key, it.value)
|
jsonValue.content
|
||||||
is Float -> putFloat(it.key, it.value)
|
} else {
|
||||||
is Long -> putLong(it.key, it.value)
|
jsonValue.booleanOrNull
|
||||||
|
?: jsonValue.intOrNull
|
||||||
|
?: jsonValue.longOrNull
|
||||||
|
?: jsonValue.floatOrNull
|
||||||
|
}
|
||||||
|
when (value) {
|
||||||
|
is Boolean -> putBoolean(key, value)
|
||||||
|
is Float -> putFloat(key, value)
|
||||||
|
is Long -> putLong(key, value)
|
||||||
is Int -> {
|
is Int -> {
|
||||||
when (it.key) {
|
when (key) {
|
||||||
PreferenceKeys.START_FRAGMENT -> putInt(it.key, it.value)
|
PreferenceKeys.START_FRAGMENT -> putInt(key, value)
|
||||||
else -> putLong(it.key, it.value.toLong())
|
else -> putLong(key, value.toLong())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is String -> putString(it.key, it.value)
|
is String -> putString(key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ import android.app.Activity
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.api.JsonHelper
|
||||||
import com.github.libretube.api.PlaylistsHelper
|
import com.github.libretube.api.PlaylistsHelper
|
||||||
import com.github.libretube.api.RetrofitInstance
|
import com.github.libretube.api.RetrofitInstance
|
||||||
import com.github.libretube.api.SubscriptionHelper
|
import com.github.libretube.api.SubscriptionHelper
|
||||||
@ -15,11 +15,13 @@ import com.github.libretube.obj.ImportPlaylist
|
|||||||
import com.github.libretube.obj.ImportPlaylistFile
|
import com.github.libretube.obj.ImportPlaylistFile
|
||||||
import com.github.libretube.obj.NewPipeSubscription
|
import com.github.libretube.obj.NewPipeSubscription
|
||||||
import com.github.libretube.obj.NewPipeSubscriptions
|
import com.github.libretube.obj.NewPipeSubscriptions
|
||||||
import java.io.FileOutputStream
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.serialization.json.decodeFromStream
|
||||||
|
import kotlinx.serialization.json.encodeToStream
|
||||||
|
import okio.use
|
||||||
|
|
||||||
class ImportHelper(
|
class ImportHelper(
|
||||||
private val activity: Activity
|
private val activity: Activity
|
||||||
@ -56,12 +58,11 @@ class ImportHelper(
|
|||||||
return when (val fileType = activity.contentResolver.getType(uri)) {
|
return when (val fileType = activity.contentResolver.getType(uri)) {
|
||||||
"application/json", "application/*", "application/octet-stream" -> {
|
"application/json", "application/*", "application/octet-stream" -> {
|
||||||
// NewPipe subscriptions format
|
// NewPipe subscriptions format
|
||||||
val subscriptions = ObjectMapper().readValue(
|
val subscriptions = activity.contentResolver.openInputStream(uri)?.use {
|
||||||
uri.readText(),
|
JsonHelper.json.decodeFromStream<NewPipeSubscriptions>(it)
|
||||||
NewPipeSubscriptions::class.java
|
}
|
||||||
)
|
subscriptions?.subscriptions.orEmpty().map {
|
||||||
subscriptions.subscriptions.orEmpty().map {
|
it.url.replace("https://www.youtube.com/channel/", "")
|
||||||
it.url!!.replace("https://www.youtube.com/channel/", "")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"text/csv", "text/comma-separated-values" -> {
|
"text/csv", "text/comma-separated-values" -> {
|
||||||
@ -91,20 +92,14 @@ class ImportHelper(
|
|||||||
SubscriptionHelper.getFormattedLocalSubscriptions()
|
SubscriptionHelper.getFormattedLocalSubscriptions()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val newPipeChannels = mutableListOf<NewPipeSubscription>()
|
val newPipeChannels = subs.map {
|
||||||
subs.forEach {
|
NewPipeSubscription(it.name, 0, "https://www.youtube.com${it.url}")
|
||||||
newPipeChannels += NewPipeSubscription(
|
|
||||||
name = it.name,
|
|
||||||
service_id = 0,
|
|
||||||
url = "https://www.youtube.com" + it.url
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
val newPipeSubscriptions = NewPipeSubscriptions(subscriptions = newPipeChannels)
|
||||||
|
|
||||||
val newPipeSubscriptions = NewPipeSubscriptions(
|
activity.contentResolver.openOutputStream(uri)?.use {
|
||||||
subscriptions = newPipeChannels
|
JsonHelper.json.encodeToStream(newPipeSubscriptions, it)
|
||||||
)
|
}
|
||||||
|
|
||||||
uri.write(newPipeSubscriptions)
|
|
||||||
|
|
||||||
activity.toastFromMainThread(R.string.exportsuccess)
|
activity.toastFromMainThread(R.string.exportsuccess)
|
||||||
}
|
}
|
||||||
@ -134,11 +129,10 @@ class ImportHelper(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"application/json", "application/*", "application/octet-stream" -> {
|
"application/json", "application/*", "application/octet-stream" -> {
|
||||||
val playlistFile = ObjectMapper().readValue(
|
val playlistFile = activity.contentResolver.openInputStream(uri)?.use {
|
||||||
uri.readText(),
|
JsonHelper.json.decodeFromStream<ImportPlaylistFile>(it)
|
||||||
ImportPlaylistFile::class.java
|
}
|
||||||
)
|
importPlaylists.addAll(playlistFile?.playlists.orEmpty())
|
||||||
importPlaylists.addAll(playlistFile.playlists.orEmpty())
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
activity.applicationContext.toastFromMainThread("Unsupported file type $fileType")
|
activity.applicationContext.toastFromMainThread("Unsupported file type $fileType")
|
||||||
@ -167,31 +161,13 @@ class ImportHelper(
|
|||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val playlists = PlaylistsHelper.exportPlaylists()
|
val playlists = PlaylistsHelper.exportPlaylists()
|
||||||
val playlistFile = ImportPlaylistFile(
|
val playlistFile = ImportPlaylistFile("Piped", 1, playlists)
|
||||||
format = "Piped",
|
|
||||||
version = 1,
|
|
||||||
playlists = playlists
|
|
||||||
)
|
|
||||||
|
|
||||||
uri.write(playlistFile)
|
activity.contentResolver.openOutputStream(uri)?.use {
|
||||||
|
JsonHelper.json.encodeToStream(playlistFile, it)
|
||||||
|
}
|
||||||
|
|
||||||
activity.toastFromMainThread(R.string.exportsuccess)
|
activity.toastFromMainThread(R.string.exportsuccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Uri.readText(): String {
|
|
||||||
return activity.contentResolver.openInputStream(this)?.use {
|
|
||||||
it.bufferedReader().use { reader -> reader.readText() }
|
|
||||||
}.orEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Uri.write(text: Any) {
|
|
||||||
activity.contentResolver.openFileDescriptor(this, "w")?.use {
|
|
||||||
FileOutputStream(it.fileDescriptor).use { fileOutputStream ->
|
|
||||||
fileOutputStream.write(
|
|
||||||
ObjectMapper().writeValueAsBytes(text)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
package com.github.libretube.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.github.libretube.api.obj.Streams
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
|
|
||||||
class MetadataHelper(
|
|
||||||
private val context: Context
|
|
||||||
) {
|
|
||||||
private val mapper = ObjectMapper()
|
|
||||||
private val metadataDir = DownloadHelper.getDownloadDir(context, DownloadHelper.METADATA_DIR)
|
|
||||||
|
|
||||||
fun createMetadata(fileName: String, streams: Streams) {
|
|
||||||
val targetFile = File(metadataDir, fileName)
|
|
||||||
targetFile.createNewFile()
|
|
||||||
|
|
||||||
context.contentResolver.openFileDescriptor(
|
|
||||||
Uri.fromFile(targetFile),
|
|
||||||
"w"
|
|
||||||
)?.use {
|
|
||||||
FileOutputStream(it.fileDescriptor).use { fileOutputStream ->
|
|
||||||
fileOutputStream.write(
|
|
||||||
mapper.writeValueAsBytes(
|
|
||||||
streams
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getMetadata(fileName: String): Streams? {
|
|
||||||
val sourceFile = File(metadataDir, fileName)
|
|
||||||
|
|
||||||
return try {
|
|
||||||
val json = context.contentResolver.openInputStream(
|
|
||||||
Uri.fromFile(sourceFile)
|
|
||||||
)?.use {
|
|
||||||
it.bufferedReader().use { reader -> reader.readText() }
|
|
||||||
}
|
|
||||||
mapper.readValue(json, Streams::class.java)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -147,7 +147,7 @@ object PlayingQueue {
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
while (channelNextPage != null) {
|
while (channelNextPage != null) {
|
||||||
RetrofitInstance.api.getChannelNextPage(channelId, nextPage!!).apply {
|
RetrofitInstance.api.getChannelNextPage(channelId, nextPage!!).apply {
|
||||||
add(*relatedStreams.orEmpty().toTypedArray())
|
add(*relatedStreams.toTypedArray())
|
||||||
channelNextPage = this.nextpage
|
channelNextPage = this.nextpage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +158,7 @@ object PlayingQueue {
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
val channel = RetrofitInstance.api.getChannel(channelId)
|
val channel = RetrofitInstance.api.getChannel(channelId)
|
||||||
add(*channel.relatedStreams.orEmpty().toTypedArray())
|
add(*channel.relatedStreams.toTypedArray())
|
||||||
updateCurrent(newCurrentStream)
|
updateCurrent(newCurrentStream)
|
||||||
if (channel.nextpage == null) return@launch
|
if (channel.nextpage == null) return@launch
|
||||||
fetchMoreFromChannel(channelId, channel.nextpage)
|
fetchMoreFromChannel(channelId, channel.nextpage)
|
||||||
|
@ -2,10 +2,11 @@ package com.github.libretube.util
|
|||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.time.LocalDate
|
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.time.format.FormatStyle
|
import java.time.format.FormatStyle
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.datetime.toJavaLocalDate
|
||||||
|
|
||||||
object TextUtils {
|
object TextUtils {
|
||||||
/**
|
/**
|
||||||
@ -42,16 +43,9 @@ object TextUtils {
|
|||||||
* @param locale The locale to use, otherwise uses system default
|
* @param locale The locale to use, otherwise uses system default
|
||||||
* return Localized date string
|
* return Localized date string
|
||||||
*/
|
*/
|
||||||
fun localizeDate(date: String?, locale: Locale): String? {
|
fun localizeDate(date: LocalDate, locale: Locale): String {
|
||||||
date ?: return null
|
|
||||||
|
|
||||||
// relative time span
|
|
||||||
if (!date.contains("-")) return date
|
|
||||||
|
|
||||||
val dateObj = LocalDate.parse(date)
|
|
||||||
|
|
||||||
val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(locale)
|
val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(locale)
|
||||||
return dateObj.format(formatter)
|
return date.toJavaLocalDate().format(formatter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,13 +5,15 @@
|
|||||||
|
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.7.22'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.4.0'
|
classpath 'com.android.tools.build:gradle:7.4.0'
|
||||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.22'
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
@ -11,13 +11,15 @@ espresso = "3.5.1"
|
|||||||
workRuntime = "2.7.1"
|
workRuntime = "2.7.1"
|
||||||
exoplayer = "2.18.2"
|
exoplayer = "2.18.2"
|
||||||
retrofit = "2.9.0"
|
retrofit = "2.9.0"
|
||||||
jacksonAnnotations = "2.13.4"
|
|
||||||
desugaring = "2.0.0"
|
desugaring = "2.0.0"
|
||||||
cronetEmbedded = "108.5359.79"
|
cronetEmbedded = "108.5359.79"
|
||||||
cronetOkHttp = "0.1.0"
|
cronetOkHttp = "0.1.0"
|
||||||
coil = "2.2.2"
|
coil = "2.2.2"
|
||||||
leakcanary = "2.10"
|
leakcanary = "2.10"
|
||||||
room = "2.5.0"
|
room = "2.5.0"
|
||||||
|
kotlinxSerialization = "1.4.1"
|
||||||
|
kotlinxDatetime = "0.4.0"
|
||||||
|
kotlinxRetrofit = "0.8.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||||
@ -33,8 +35,6 @@ androidx-work-runtime = { group = "androidx.work", name="work-runtime-ktx", vers
|
|||||||
exoplayer = { group = "com.google.android.exoplayer", name = "exoplayer", version.ref = "exoplayer" }
|
exoplayer = { group = "com.google.android.exoplayer", name = "exoplayer", version.ref = "exoplayer" }
|
||||||
exoplayer-extension-mediasession = { group = "com.google.android.exoplayer", name = "extension-mediasession", version.ref = "exoplayer" }
|
exoplayer-extension-mediasession = { group = "com.google.android.exoplayer", name = "extension-mediasession", version.ref = "exoplayer" }
|
||||||
square-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
|
square-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
|
||||||
square-retrofit-converterJackson = { group = "com.squareup.retrofit2", name = "converter-jackson", version.ref = "retrofit" }
|
|
||||||
jacksonAnnotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations", version.ref = "jacksonAnnotations" }
|
|
||||||
desugaring = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugaring" }
|
desugaring = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugaring" }
|
||||||
exoplayer-extension-cronet = { group = "com.google.android.exoplayer", name = "extension-cronet", version.ref = "exoplayer" }
|
exoplayer-extension-cronet = { group = "com.google.android.exoplayer", name = "extension-cronet", version.ref = "exoplayer" }
|
||||||
exoplayer-dash = { group = "com.google.android.exoplayer", name = "exoplayer-dash", version.ref = "exoplayer" }
|
exoplayer-dash = { group = "com.google.android.exoplayer", name = "exoplayer-dash", version.ref = "exoplayer" }
|
||||||
@ -47,3 +47,6 @@ lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-kt
|
|||||||
lifecycle-livedata = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
|
lifecycle-livedata = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
|
||||||
room = { group = "androidx.room", name="room-runtime", version.ref = "room" }
|
room = { group = "androidx.room", name="room-runtime", version.ref = "room" }
|
||||||
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
||||||
|
kotlinx-serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
|
||||||
|
kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" }
|
||||||
|
kotlinx-serialization-retrofit = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "kotlinxRetrofit" }
|
Loading…
x
Reference in New Issue
Block a user