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.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 kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.jackson.JacksonConverterFactory
|
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 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()
|
Retrofit.Builder()
|
||||||
.baseUrl(url)
|
.baseUrl(url)
|
||||||
.callFactory(CronetHelper.callFactory)
|
.callFactory(CronetHelper.callFactory)
|
||||||
|
.addConverterFactory(kotlinxConverterFactory)
|
||||||
.addConverterFactory(jacksonConverterFactory)
|
.addConverterFactory(jacksonConverterFactory)
|
||||||
.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(kotlinxConverterFactory)
|
||||||
.addConverterFactory(jacksonConverterFactory)
|
.addConverterFactory(jacksonConverterFactory)
|
||||||
.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(kotlinxConverterFactory)
|
||||||
.addConverterFactory(jacksonConverterFactory)
|
.addConverterFactory(jacksonConverterFactory)
|
||||||
.build()
|
.build()
|
||||||
.create(ExternalApi::class.java)
|
.create<ExternalApi>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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 ChapterSegment(
|
data class ChapterSegment(
|
||||||
var title: String? = null,
|
var title: String? = null,
|
||||||
var image: String? = null,
|
var image: 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 PipedStream(
|
data class PipedStream(
|
||||||
var url: String? = null,
|
var url: String? = null,
|
||||||
var format: String? = null,
|
var format: 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 PreviewFrames(
|
data class PreviewFrames(
|
||||||
val urls: List<String>? = null,
|
val urls: List<String>? = null,
|
||||||
val frameWidth: Int? = null,
|
val frameWidth: Int? = 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 StreamItem(
|
data class StreamItem(
|
||||||
var url: String? = null,
|
var url: String? = null,
|
||||||
val type: String? = null,
|
val type: String? = null,
|
||||||
|
@ -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,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,
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
url,
|
streams.thumbnailUrl,
|
||||||
thumbnailTargetFile.absolutePath
|
thumbnailTargetFile.absolutePath
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
val downloadItems = streams.toDownloadItems(
|
val downloadItems = streams.toDownloadItems(
|
||||||
videoId,
|
videoId,
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,7 @@ 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 org.chromium.net.CronetEngine
|
import org.chromium.net.CronetEngine
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
|
|
||||||
@ -682,9 +683,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()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -773,8 +772,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
|
||||||
@ -794,7 +792,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -828,7 +826,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 +868,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 +881,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 +968,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 +991,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
|
||||||
|
|
||||||
// detect whether the description is html formatted
|
// detect whether the description is html formatted
|
||||||
if (description.contains("<") && description.contains(">")) {
|
if (description.contains("<") && description.contains(">")) {
|
||||||
@ -1016,7 +1012,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
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1228,9 +1224,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).
|
||||||
@ -1260,7 +1256,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()
|
||||||
@ -1355,16 +1351,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!!
|
||||||
}
|
}
|
||||||
@ -1493,9 +1487,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
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package com.github.libretube.util
|
package com.github.libretube.util
|
||||||
|
|
||||||
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 {
|
||||||
/**
|
/**
|
||||||
@ -41,15 +42,8 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user