mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 16:00:31 +05:30
Use Kotlinx Serialization with stream data.
This commit is contained in:
parent
b04cef0e88
commit
219a7d7cfe
@ -3,40 +3,51 @@ package com.github.libretube.api
|
||||
import com.github.libretube.constants.PIPED_API_URL
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.jackson.JacksonConverterFactory
|
||||
import retrofit2.create
|
||||
|
||||
object RetrofitInstance {
|
||||
lateinit var url: String
|
||||
lateinit var authUrl: String
|
||||
val lazyMgr = resettableManager()
|
||||
val jacksonConverterFactory = JacksonConverterFactory.create()
|
||||
private val jacksonConverterFactory = JacksonConverterFactory.create()
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
private val kotlinxConverterFactory = json.asConverterFactory("application/json".toMediaType())
|
||||
|
||||
val api: PipedApi by resettableLazy(lazyMgr) {
|
||||
val api by resettableLazy(lazyMgr) {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(url)
|
||||
.callFactory(CronetHelper.callFactory)
|
||||
.addConverterFactory(kotlinxConverterFactory)
|
||||
.addConverterFactory(jacksonConverterFactory)
|
||||
.build()
|
||||
.create(PipedApi::class.java)
|
||||
.create<PipedApi>()
|
||||
}
|
||||
|
||||
val authApi: PipedApi by resettableLazy(lazyMgr) {
|
||||
val authApi by resettableLazy(lazyMgr) {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(authUrl)
|
||||
.callFactory(CronetHelper.callFactory)
|
||||
.addConverterFactory(kotlinxConverterFactory)
|
||||
.addConverterFactory(jacksonConverterFactory)
|
||||
.build()
|
||||
.create(PipedApi::class.java)
|
||||
.create<PipedApi>()
|
||||
}
|
||||
|
||||
val externalApi: ExternalApi by resettableLazy(lazyMgr) {
|
||||
val externalApi by resettableLazy(lazyMgr) {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(url)
|
||||
.callFactory(CronetHelper.callFactory)
|
||||
.addConverterFactory(kotlinxConverterFactory)
|
||||
.addConverterFactory(jacksonConverterFactory)
|
||||
.build()
|
||||
.create(ExternalApi::class.java)
|
||||
.create<ExternalApi>()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.github.libretube.api.obj
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Serializable
|
||||
data class ChapterSegment(
|
||||
var title: String? = null,
|
||||
var image: String? = null,
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.github.libretube.api.obj
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Serializable
|
||||
data class PipedStream(
|
||||
var url: String? = null,
|
||||
var format: String? = null,
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.github.libretube.api.obj
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Serializable
|
||||
data class PreviewFrames(
|
||||
val urls: List<String>? = null,
|
||||
val frameWidth: Int? = null,
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.github.libretube.api.obj
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Serializable
|
||||
data class StreamItem(
|
||||
var url: String? = null,
|
||||
val type: String? = null,
|
||||
|
@ -1,31 +1,32 @@
|
||||
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(
|
||||
val title: String? = null,
|
||||
val description: String? = null,
|
||||
val uploadDate: String? = null,
|
||||
val uploader: String? = null,
|
||||
val uploaderUrl: String? = null,
|
||||
val uploaderAvatar: String? = null,
|
||||
val thumbnailUrl: String? = null,
|
||||
val title: String,
|
||||
val description: String,
|
||||
val uploadDate: LocalDate,
|
||||
val uploader: String,
|
||||
val uploaderUrl: String,
|
||||
val uploaderAvatar: String,
|
||||
val thumbnailUrl: String,
|
||||
val hls: String? = null,
|
||||
val dash: String? = null,
|
||||
val lbryId: String? = null,
|
||||
val uploaderVerified: Boolean? = null,
|
||||
val duration: Long? = null,
|
||||
val views: Long? = null,
|
||||
val likes: Long? = null,
|
||||
val dislikes: Long? = null,
|
||||
val audioStreams: List<PipedStream>? = null,
|
||||
val videoStreams: List<PipedStream>? = null,
|
||||
val relatedStreams: List<StreamItem>? = null,
|
||||
val subtitles: List<Subtitle>? = null,
|
||||
val livestream: Boolean? = null,
|
||||
val uploaderVerified: Boolean,
|
||||
val duration: Long,
|
||||
val views: Long = 0,
|
||||
val likes: Long = 0,
|
||||
val dislikes: Long = 0,
|
||||
val audioStreams: List<PipedStream> = emptyList(),
|
||||
val videoStreams: List<PipedStream> = emptyList(),
|
||||
val relatedStreams: List<StreamItem> = emptyList(),
|
||||
val subtitles: List<Subtitle> = emptyList(),
|
||||
val livestream: Boolean = false,
|
||||
val proxyUrl: String? = null,
|
||||
val chapters: List<ChapterSegment>? = null,
|
||||
val uploaderSubscriberCount: Long? = null,
|
||||
val previewFrames: List<PreviewFrames>? = null
|
||||
val chapters: List<ChapterSegment> = emptyList(),
|
||||
val uploaderSubscriberCount: Long = 0,
|
||||
val previewFrames: List<PreviewFrames> = emptyList()
|
||||
)
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.github.libretube.api.obj
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Serializable
|
||||
data class Subtitle(
|
||||
val url: String? = null,
|
||||
val mimeType: String? = null,
|
||||
|
@ -16,9 +16,9 @@ object DatabaseHelper {
|
||||
val watchHistoryItem = WatchHistoryItem(
|
||||
videoId,
|
||||
streams.title,
|
||||
streams.uploadDate,
|
||||
streams.uploadDate.toString(),
|
||||
streams.uploader,
|
||||
streams.uploaderUrl!!.toID(),
|
||||
streams.uploaderUrl.toID(),
|
||||
streams.uploaderAvatar,
|
||||
streams.thumbnailUrl,
|
||||
streams.duration
|
||||
|
@ -13,7 +13,7 @@ fun Streams.toStreamItem(videoId: String): StreamItem {
|
||||
uploaderName = uploader,
|
||||
uploaderUrl = uploaderUrl,
|
||||
uploaderAvatar = uploaderAvatar,
|
||||
uploadedDate = uploadDate,
|
||||
uploadedDate = uploadDate.toString(),
|
||||
uploaded = null,
|
||||
duration = duration,
|
||||
views = views,
|
||||
|
@ -102,21 +102,19 @@ class DownloadService : Service() {
|
||||
Database.downloadDao().insertDownload(
|
||||
Download(
|
||||
videoId = videoId,
|
||||
title = streams.title ?: "",
|
||||
title = streams.title,
|
||||
thumbnailPath = thumbnailTargetFile.absolutePath,
|
||||
description = streams.description ?: "",
|
||||
uploadDate = streams.uploadDate,
|
||||
uploader = streams.uploader ?: ""
|
||||
description = streams.description,
|
||||
uploadDate = streams.uploadDate.toString(),
|
||||
uploader = streams.uploader
|
||||
)
|
||||
)
|
||||
}
|
||||
streams.thumbnailUrl?.let { url ->
|
||||
ImageHelper.downloadImage(
|
||||
this@DownloadService,
|
||||
url,
|
||||
streams.thumbnailUrl,
|
||||
thumbnailTargetFile.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
val downloadItems = streams.toDownloadItems(
|
||||
videoId,
|
||||
|
@ -74,7 +74,7 @@ class DownloadDialog(
|
||||
}
|
||||
|
||||
private fun initDownloadOptions(streams: Streams) {
|
||||
binding.fileName.setText(streams.title.toString())
|
||||
binding.fileName.setText(streams.title)
|
||||
|
||||
val vidName = arrayListOf<String>()
|
||||
|
||||
@ -82,7 +82,7 @@ class DownloadDialog(
|
||||
vidName.add(getString(R.string.no_video))
|
||||
|
||||
// add all available video streams
|
||||
for (vid in streams.videoStreams!!) {
|
||||
for (vid in streams.videoStreams) {
|
||||
if (vid.url != null) {
|
||||
val name = vid.quality + " " + vid.format
|
||||
vidName.add(name)
|
||||
@ -95,7 +95,7 @@ class DownloadDialog(
|
||||
audioName.add(getString(R.string.no_audio))
|
||||
|
||||
// add all available audio streams
|
||||
for (audio in streams.audioStreams!!) {
|
||||
for (audio in streams.audioStreams) {
|
||||
if (audio.url != null) {
|
||||
val name = audio.quality + " " + audio.format
|
||||
audioName.add(name)
|
||||
@ -108,7 +108,7 @@ class DownloadDialog(
|
||||
subtitleName.add(getString(R.string.no_subtitle))
|
||||
|
||||
// add all available subtitles
|
||||
for (subtitle in streams.subtitles!!) {
|
||||
for (subtitle in streams.subtitles) {
|
||||
if (subtitle.url != null) {
|
||||
subtitleName.add(subtitle.name.toString())
|
||||
}
|
||||
|
@ -116,6 +116,7 @@ import kotlin.math.abs
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.LocalDate
|
||||
import org.chromium.net.CronetEngine
|
||||
import retrofit2.HttpException
|
||||
|
||||
@ -682,9 +683,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
} else {
|
||||
PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!))
|
||||
if (PlayerHelper.autoInsertRelatedVideos) {
|
||||
PlayingQueue.add(
|
||||
*streams.relatedStreams.orEmpty().toTypedArray()
|
||||
)
|
||||
PlayingQueue.add(*streams.relatedStreams.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -773,8 +772,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
playerBinding.liveDiff.text = "-$diffText"
|
||||
}
|
||||
// call the function again after 100ms
|
||||
handler
|
||||
.postDelayed(this@PlayerFragment::refreshLiveStatus, 100)
|
||||
handler.postDelayed(this@PlayerFragment::refreshLiveStatus, 100)
|
||||
}
|
||||
|
||||
// seek to saved watch position if available
|
||||
@ -794,7 +792,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
return
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@ -828,7 +826,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
playerBinding.exoProgress.setPlayer(exoPlayer)
|
||||
}
|
||||
|
||||
private fun localizedDate(date: String?): String? {
|
||||
private fun localizedDate(date: LocalDate): String {
|
||||
val locale = ConfigurationCompat.getLocales(resources.configuration)[0]!!
|
||||
return TextUtils.localizeDate(date, locale)
|
||||
}
|
||||
@ -870,12 +868,12 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
|
||||
playerChannelSubCount.text = context?.getString(
|
||||
R.string.subscribers,
|
||||
streams.uploaderSubscriberCount?.formatShort()
|
||||
streams.uploaderSubscriberCount.formatShort()
|
||||
)
|
||||
}
|
||||
|
||||
// duration that's not greater than 0 indicates that the video is live
|
||||
if (streams.duration!! <= 0) {
|
||||
if (streams.duration <= 0) {
|
||||
isLive = true
|
||||
handleLiveVideo()
|
||||
}
|
||||
@ -883,10 +881,8 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
playerBinding.exoTitle.text = streams.title
|
||||
|
||||
// init the chapters recyclerview
|
||||
if (streams.chapters != null) {
|
||||
chapters = streams.chapters.orEmpty()
|
||||
chapters = streams.chapters
|
||||
initializeChapters()
|
||||
}
|
||||
|
||||
// Listener for play and pause icon change
|
||||
exoPlayer.addListener(object : Player.Listener {
|
||||
@ -972,7 +968,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
})
|
||||
|
||||
binding.relPlayerDownload.setOnClickListener {
|
||||
if (streams.duration!! <= 0) {
|
||||
if (streams.duration <= 0) {
|
||||
Toast.makeText(context, R.string.cannotDownload, Toast.LENGTH_SHORT).show()
|
||||
} else if (!DownloadService.IS_DOWNLOAD_RUNNING) {
|
||||
val newFragment = DownloadDialog(videoId!!)
|
||||
@ -995,7 +991,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
}
|
||||
initializeRelatedVideos(streams.relatedStreams)
|
||||
// set video description
|
||||
val description = streams.description!!
|
||||
val description = streams.description
|
||||
|
||||
// detect whether the description is html formatted
|
||||
if (description.contains("<") && description.contains(">")) {
|
||||
@ -1016,7 +1012,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
|
||||
// update the subscribed state
|
||||
binding.playerSubscribe.setupSubscriptionButton(
|
||||
this.streams.uploaderUrl?.toID(),
|
||||
this.streams.uploaderUrl.toID(),
|
||||
this.streams.uploader
|
||||
)
|
||||
|
||||
@ -1228,9 +1224,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
private fun setResolutionAndSubtitles() {
|
||||
// create a list of subtitles
|
||||
subtitles = mutableListOf()
|
||||
val subtitlesNamesList = mutableListOf(context?.getString(R.string.none)!!)
|
||||
val subtitlesNamesList = mutableListOf(getString(R.string.none))
|
||||
val subtitleCodesList = mutableListOf("")
|
||||
streams.subtitles.orEmpty().forEach {
|
||||
streams.subtitles.forEach {
|
||||
subtitles.add(
|
||||
SubtitleConfiguration.Builder(it.url!!.toUri())
|
||||
.setMimeType(it.mimeType!!) // The correct MIME type (required).
|
||||
@ -1260,7 +1256,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
if (defaultResolution != "") setPlayerResolution(defaultResolution.toInt())
|
||||
|
||||
if (!PreferenceHelper.getBoolean(PreferenceKeys.USE_HLS_OVER_DASH, false) &&
|
||||
streams.videoStreams.orEmpty().isNotEmpty()
|
||||
streams.videoStreams.isNotEmpty()
|
||||
) {
|
||||
val uri = let {
|
||||
streams.dash?.toUri()
|
||||
@ -1355,16 +1351,14 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
}
|
||||
|
||||
override fun onCaptionsClicked() {
|
||||
if (!this@PlayerFragment::streams.isInitialized ||
|
||||
streams.subtitles.isNullOrEmpty()
|
||||
) {
|
||||
if (!this@PlayerFragment::streams.isInitialized || streams.subtitles.isEmpty()) {
|
||||
Toast.makeText(context, R.string.no_subtitles_available, Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
val subtitlesNamesList = mutableListOf(context?.getString(R.string.none)!!)
|
||||
val subtitlesNamesList = mutableListOf(getString(R.string.none))
|
||||
val subtitleCodesList = mutableListOf("")
|
||||
streams.subtitles!!.forEach {
|
||||
streams.subtitles.forEach {
|
||||
subtitlesNamesList += it.name!!
|
||||
subtitleCodesList += it.code!!
|
||||
}
|
||||
@ -1493,9 +1487,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
playerBinding.seekbarPreview.visibility = View.GONE
|
||||
playerBinding.exoProgress.addListener(
|
||||
SeekbarPreviewListener(
|
||||
streams.previewFrames.orEmpty(),
|
||||
streams.previewFrames,
|
||||
playerBinding.seekbarPreview,
|
||||
streams.duration!! * 1000
|
||||
streams.duration * 1000
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import java.net.URL
|
||||
import java.time.LocalDate
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import java.util.*
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.toJavaLocalDate
|
||||
|
||||
object TextUtils {
|
||||
/**
|
||||
@ -41,15 +42,8 @@ object TextUtils {
|
||||
* @param locale The locale to use, otherwise uses system default
|
||||
* return Localized date string
|
||||
*/
|
||||
fun localizeDate(date: String?, locale: Locale): String? {
|
||||
date ?: return null
|
||||
|
||||
// relative time span
|
||||
if (!date.contains("-")) return date
|
||||
|
||||
val dateObj = LocalDate.parse(date)
|
||||
|
||||
fun localizeDate(date: LocalDate, locale: Locale): String {
|
||||
val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(locale)
|
||||
return dateObj.format(formatter)
|
||||
return date.toJavaLocalDate().format(formatter)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user