351 lines
13 KiB
Kotlin
351 lines
13 KiB
Kotlin
package com.lagradost.cloudstream3.utils
|
|
|
|
import com.fasterxml.jackson.annotation.JsonProperty
|
|
import com.lagradost.cloudstream3.*
|
|
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
|
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
|
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
|
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
|
import com.lagradost.cloudstream3.ui.WatchType
|
|
import com.lagradost.cloudstream3.ui.result.VideoWatchState
|
|
|
|
const val VIDEO_POS_DUR = "video_pos_dur"
|
|
const val VIDEO_WATCH_STATE = "video_watch_state"
|
|
const val RESULT_WATCH_STATE = "result_watch_state"
|
|
const val RESULT_WATCH_STATE_DATA = "result_watch_state_data"
|
|
const val RESULT_SUBSCRIBED_STATE_DATA = "result_subscribed_state_data"
|
|
const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes
|
|
const val RESULT_RESUME_WATCHING_OLD = "result_resume_watching"
|
|
const val RESULT_RESUME_WATCHING_HAS_MIGRATED = "result_resume_watching_migrated"
|
|
const val RESULT_EPISODE = "result_episode"
|
|
const val RESULT_SEASON = "result_season"
|
|
const val RESULT_DUB = "result_dub"
|
|
|
|
object DataStoreHelper {
|
|
data class PosDur(
|
|
@JsonProperty("position") val position: Long,
|
|
@JsonProperty("duration") val duration: Long
|
|
)
|
|
|
|
fun PosDur.fixVisual(): PosDur {
|
|
if (duration <= 0) return PosDur(0, duration)
|
|
val percentage = position * 100 / duration
|
|
if (percentage <= 1) return PosDur(0, duration)
|
|
if (percentage <= 5) return PosDur(5 * duration / 100, duration)
|
|
if (percentage >= 95) return PosDur(duration, duration)
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Used to display notifications on new episodes and posters in library.
|
|
**/
|
|
data class SubscribedData(
|
|
@JsonProperty("id") override var id: Int?,
|
|
@JsonProperty("subscribedTime") val bookmarkedTime: Long,
|
|
@JsonProperty("latestUpdatedTime") val latestUpdatedTime: Long,
|
|
@JsonProperty("lastSeenEpisodeCount") val lastSeenEpisodeCount: Map<DubStatus, Int?>,
|
|
@JsonProperty("name") override val name: String,
|
|
@JsonProperty("url") override val url: String,
|
|
@JsonProperty("apiName") override val apiName: String,
|
|
@JsonProperty("type") override var type: TvType? = null,
|
|
@JsonProperty("posterUrl") override var posterUrl: String?,
|
|
@JsonProperty("year") val year: Int?,
|
|
@JsonProperty("quality") override var quality: SearchQuality? = null,
|
|
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>? = null,
|
|
) : SearchResponse {
|
|
fun toLibraryItem(): SyncAPI.LibraryItem? {
|
|
return SyncAPI.LibraryItem(
|
|
name,
|
|
url,
|
|
id?.toString() ?: return null,
|
|
null,
|
|
null,
|
|
null,
|
|
latestUpdatedTime,
|
|
apiName, type, posterUrl, posterHeaders, quality, this.id
|
|
)
|
|
}
|
|
}
|
|
|
|
data class BookmarkedData(
|
|
@JsonProperty("id") override var id: Int?,
|
|
@JsonProperty("bookmarkedTime") val bookmarkedTime: Long,
|
|
@JsonProperty("latestUpdatedTime") val latestUpdatedTime: Long,
|
|
@JsonProperty("name") override val name: String,
|
|
@JsonProperty("url") override val url: String,
|
|
@JsonProperty("apiName") override val apiName: String,
|
|
@JsonProperty("type") override var type: TvType? = null,
|
|
@JsonProperty("posterUrl") override var posterUrl: String?,
|
|
@JsonProperty("year") val year: Int?,
|
|
@JsonProperty("quality") override var quality: SearchQuality? = null,
|
|
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>? = null,
|
|
) : SearchResponse {
|
|
fun toLibraryItem(id: String): SyncAPI.LibraryItem {
|
|
return SyncAPI.LibraryItem(
|
|
name,
|
|
url,
|
|
id,
|
|
null,
|
|
null,
|
|
null,
|
|
latestUpdatedTime,
|
|
apiName, type, posterUrl, posterHeaders, quality, this.id
|
|
)
|
|
}
|
|
}
|
|
|
|
data class ResumeWatchingResult(
|
|
@JsonProperty("name") override val name: String,
|
|
@JsonProperty("url") override val url: String,
|
|
@JsonProperty("apiName") override val apiName: String,
|
|
@JsonProperty("type") override var type: TvType? = null,
|
|
@JsonProperty("posterUrl") override var posterUrl: String?,
|
|
@JsonProperty("watchPos") val watchPos: PosDur?,
|
|
@JsonProperty("id") override var id: Int?,
|
|
@JsonProperty("parentId") val parentId: Int?,
|
|
@JsonProperty("episode") val episode: Int?,
|
|
@JsonProperty("season") val season: Int?,
|
|
@JsonProperty("isFromDownload") val isFromDownload: Boolean,
|
|
@JsonProperty("quality") override var quality: SearchQuality? = null,
|
|
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>? = null,
|
|
) : SearchResponse
|
|
|
|
/**
|
|
* A datastore wide account for future implementations of a multiple account system
|
|
**/
|
|
var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION
|
|
|
|
fun getAllWatchStateIds(): List<Int>? {
|
|
val folder = "$currentAccount/$RESULT_WATCH_STATE"
|
|
return getKeys(folder)?.mapNotNull {
|
|
it.removePrefix("$folder/").toIntOrNull()
|
|
}
|
|
}
|
|
|
|
fun deleteAllResumeStateIds() {
|
|
val folder = "$currentAccount/$RESULT_RESUME_WATCHING"
|
|
removeKeys(folder)
|
|
}
|
|
|
|
fun deleteAllBookmarkedData() {
|
|
val folder1 = "$currentAccount/$RESULT_WATCH_STATE"
|
|
val folder2 = "$currentAccount/$RESULT_WATCH_STATE_DATA"
|
|
removeKeys(folder1)
|
|
removeKeys(folder2)
|
|
}
|
|
|
|
fun getAllResumeStateIds(): List<Int>? {
|
|
val folder = "$currentAccount/$RESULT_RESUME_WATCHING"
|
|
return getKeys(folder)?.mapNotNull {
|
|
it.removePrefix("$folder/").toIntOrNull()
|
|
}
|
|
}
|
|
|
|
private fun getAllResumeStateIdsOld(): List<Int>? {
|
|
val folder = "$currentAccount/$RESULT_RESUME_WATCHING_OLD"
|
|
return getKeys(folder)?.mapNotNull {
|
|
it.removePrefix("$folder/").toIntOrNull()
|
|
}
|
|
}
|
|
|
|
fun migrateResumeWatching() {
|
|
// if (getKey(RESULT_RESUME_WATCHING_HAS_MIGRATED, false) != true) {
|
|
setKey(RESULT_RESUME_WATCHING_HAS_MIGRATED, true)
|
|
getAllResumeStateIdsOld()?.forEach { id ->
|
|
getLastWatchedOld(id)?.let {
|
|
setLastWatched(
|
|
it.parentId,
|
|
null,
|
|
it.episode,
|
|
it.season,
|
|
it.isFromDownload,
|
|
it.updateTime
|
|
)
|
|
removeLastWatchedOld(it.parentId)
|
|
}
|
|
}
|
|
//}
|
|
}
|
|
|
|
fun setLastWatched(
|
|
parentId: Int?,
|
|
episodeId: Int?,
|
|
episode: Int?,
|
|
season: Int?,
|
|
isFromDownload: Boolean = false,
|
|
updateTime: Long? = null,
|
|
) {
|
|
if (parentId == null) return
|
|
setKey(
|
|
"$currentAccount/$RESULT_RESUME_WATCHING",
|
|
parentId.toString(),
|
|
VideoDownloadHelper.ResumeWatching(
|
|
parentId,
|
|
episodeId,
|
|
episode,
|
|
season,
|
|
updateTime ?: System.currentTimeMillis(),
|
|
isFromDownload
|
|
)
|
|
)
|
|
}
|
|
|
|
private fun removeLastWatchedOld(parentId: Int?) {
|
|
if (parentId == null) return
|
|
removeKey("$currentAccount/$RESULT_RESUME_WATCHING_OLD", parentId.toString())
|
|
}
|
|
|
|
fun removeLastWatched(parentId: Int?) {
|
|
if (parentId == null) return
|
|
removeKey("$currentAccount/$RESULT_RESUME_WATCHING", parentId.toString())
|
|
}
|
|
|
|
fun getLastWatched(id: Int?): VideoDownloadHelper.ResumeWatching? {
|
|
if (id == null) return null
|
|
return getKey(
|
|
"$currentAccount/$RESULT_RESUME_WATCHING",
|
|
id.toString(),
|
|
)
|
|
}
|
|
|
|
private fun getLastWatchedOld(id: Int?): VideoDownloadHelper.ResumeWatching? {
|
|
if (id == null) return null
|
|
return getKey(
|
|
"$currentAccount/$RESULT_RESUME_WATCHING_OLD",
|
|
id.toString(),
|
|
)
|
|
}
|
|
|
|
fun setBookmarkedData(id: Int?, data: BookmarkedData) {
|
|
if (id == null) return
|
|
setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data)
|
|
AccountManager.localListApi.requireLibraryRefresh = true
|
|
}
|
|
|
|
fun getBookmarkedData(id: Int?): BookmarkedData? {
|
|
if (id == null) return null
|
|
return getKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
|
|
}
|
|
|
|
fun getAllSubscriptions(): List<SubscribedData> {
|
|
return getKeys("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA")?.mapNotNull {
|
|
getKey(it)
|
|
} ?: emptyList()
|
|
}
|
|
|
|
fun removeSubscribedData(id: Int?) {
|
|
if (id == null) return
|
|
AccountManager.localListApi.requireLibraryRefresh = true
|
|
removeKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString())
|
|
}
|
|
|
|
/**
|
|
* Set new seen episodes and update time
|
|
**/
|
|
fun updateSubscribedData(id: Int?, data: SubscribedData?, episodeResponse: EpisodeResponse?) {
|
|
if (id == null || data == null || episodeResponse == null) return
|
|
val newData = data.copy(
|
|
latestUpdatedTime = unixTimeMS,
|
|
lastSeenEpisodeCount = episodeResponse.getLatestEpisodes()
|
|
)
|
|
setKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString(), newData)
|
|
}
|
|
|
|
fun setSubscribedData(id: Int?, data: SubscribedData) {
|
|
if (id == null) return
|
|
setKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString(), data)
|
|
AccountManager.localListApi.requireLibraryRefresh = true
|
|
}
|
|
|
|
fun getSubscribedData(id: Int?): SubscribedData? {
|
|
if (id == null) return null
|
|
return getKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString())
|
|
}
|
|
|
|
fun setViewPos(id: Int?, pos: Long, dur: Long) {
|
|
if (id == null) return
|
|
if (dur < 30_000) return // too short
|
|
setKey("$currentAccount/$VIDEO_POS_DUR", id.toString(), PosDur(pos, dur))
|
|
}
|
|
|
|
fun getViewPos(id: Int?): PosDur? {
|
|
if (id == null) return null
|
|
return getKey("$currentAccount/$VIDEO_POS_DUR", id.toString(), null)
|
|
}
|
|
|
|
fun getVideoWatchState(id: Int?): VideoWatchState? {
|
|
if (id == null) return null
|
|
return getKey("$currentAccount/$VIDEO_WATCH_STATE", id.toString(), null)
|
|
}
|
|
|
|
fun setVideoWatchState(id: Int?, watchState: VideoWatchState) {
|
|
if (id == null) return
|
|
|
|
// None == No key
|
|
if (watchState == VideoWatchState.None) {
|
|
removeKey("$currentAccount/$VIDEO_WATCH_STATE", id.toString())
|
|
} else {
|
|
setKey("$currentAccount/$VIDEO_WATCH_STATE", id.toString(), watchState)
|
|
}
|
|
}
|
|
|
|
fun getDub(id: Int): DubStatus? {
|
|
return DubStatus.values()
|
|
.getOrNull(getKey("$currentAccount/$RESULT_DUB", id.toString(), -1) ?: -1)
|
|
}
|
|
|
|
fun setDub(id: Int, status: DubStatus) {
|
|
setKey("$currentAccount/$RESULT_DUB", id.toString(), status.ordinal)
|
|
}
|
|
|
|
fun setResultWatchState(id: Int?, status: Int) {
|
|
if (id == null) return
|
|
val folder = "$currentAccount/$RESULT_WATCH_STATE"
|
|
if (status == WatchType.NONE.internalId) {
|
|
removeKey(folder, id.toString())
|
|
removeKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
|
|
} else {
|
|
setKey(folder, id.toString(), status)
|
|
}
|
|
}
|
|
|
|
fun getResultWatchState(id: Int): WatchType {
|
|
return WatchType.fromInternalId(
|
|
getKey<Int>(
|
|
"$currentAccount/$RESULT_WATCH_STATE",
|
|
id.toString(),
|
|
null
|
|
)
|
|
)
|
|
}
|
|
|
|
fun getResultSeason(id: Int): Int? {
|
|
return getKey("$currentAccount/$RESULT_SEASON", id.toString(), null)
|
|
}
|
|
|
|
fun setResultSeason(id: Int, value: Int?) {
|
|
setKey("$currentAccount/$RESULT_SEASON", id.toString(), value)
|
|
}
|
|
|
|
fun getResultEpisode(id: Int): Int? {
|
|
return getKey("$currentAccount/$RESULT_EPISODE", id.toString(), null)
|
|
}
|
|
|
|
fun setResultEpisode(id: Int, value: Int?) {
|
|
setKey("$currentAccount/$RESULT_EPISODE", id.toString(), value)
|
|
}
|
|
|
|
fun addSync(id: Int, idPrefix: String, url: String) {
|
|
setKey("${idPrefix}_sync", id.toString(), url)
|
|
}
|
|
|
|
fun getSync(id: Int, idPrefixes: List<String>): List<String?> {
|
|
return idPrefixes.map { idPrefix ->
|
|
getKey("${idPrefix}_sync", id.toString())
|
|
}
|
|
}
|
|
} |