AquaStream/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt

281 lines
9.2 KiB
Kotlin
Raw Normal View History

2022-04-01 20:05:34 +00:00
package com.lagradost.cloudstream3.ui.result
2022-04-03 17:11:18 +00:00
import android.util.Log
2022-04-01 20:05:34 +00:00
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
2022-04-08 19:38:19 +00:00
import com.lagradost.cloudstream3.apmap
2022-04-01 20:05:34 +00:00
import com.lagradost.cloudstream3.mvvm.Resource
2022-06-22 01:20:10 +00:00
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
2022-04-01 20:05:34 +00:00
import com.lagradost.cloudstream3.syncproviders.SyncAPI
2022-08-04 01:19:59 +00:00
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
2022-04-02 17:58:56 +00:00
import com.lagradost.cloudstream3.utils.SyncUtil
import java.util.*
2022-04-01 20:05:34 +00:00
2022-04-02 01:38:55 +00:00
2022-04-02 22:06:35 +00:00
data class CurrentSynced(
val name: String,
val idPrefix: String,
val isSynced: Boolean,
val hasAccount: Boolean,
val icon: Int?,
2022-04-02 22:06:35 +00:00
)
2022-04-01 20:05:34 +00:00
class SyncViewModel : ViewModel() {
2022-04-03 17:11:18 +00:00
companion object {
const val TAG = "SYNCVM"
}
2022-04-01 20:05:34 +00:00
private val repos = SyncApis
private val _metaResponse: MutableLiveData<Resource<SyncAPI.SyncResult>> =
MutableLiveData()
val metadata: LiveData<Resource<SyncAPI.SyncResult>> get() = _metaResponse
2022-04-02 01:38:55 +00:00
private val _userDataResponse: MutableLiveData<Resource<SyncAPI.SyncStatus>?> =
2022-04-01 20:05:34 +00:00
MutableLiveData(null)
2022-04-02 01:38:55 +00:00
val userData: LiveData<Resource<SyncAPI.SyncStatus>?> get() = _userDataResponse
2022-04-01 20:05:34 +00:00
// prefix, id
2022-04-08 19:38:19 +00:00
private var syncs = mutableMapOf<String, String>()
2022-08-02 00:43:42 +00:00
//private val _syncIds: MutableLiveData<MutableMap<String, String>> =
// MutableLiveData(mutableMapOf())
//val syncIds: LiveData<MutableMap<String, String>> get() = _syncIds
fun getSyncs() : Map<String,String> {
return syncs
}
2022-04-01 20:05:34 +00:00
2022-04-02 22:06:35 +00:00
private val _currentSynced: MutableLiveData<List<CurrentSynced>> =
MutableLiveData(getMissing())
// pair of name idPrefix isSynced
val synced: LiveData<List<CurrentSynced>> get() = _currentSynced
private fun getMissing(): List<CurrentSynced> {
return repos.map {
CurrentSynced(
it.name,
it.idPrefix,
2022-04-08 19:38:19 +00:00
syncs.containsKey(it.idPrefix),
2022-04-03 01:13:02 +00:00
it.hasAccount(),
it.icon,
2022-04-02 22:06:35 +00:00
)
}
}
2022-04-03 17:11:18 +00:00
fun updateSynced() {
Log.i(TAG, "updateSynced")
2022-04-02 22:06:35 +00:00
_currentSynced.postValue(getMissing())
}
2022-04-08 19:38:19 +00:00
private fun addSync(idPrefix: String, id: String): Boolean {
if (syncs[idPrefix] == id) return false
2022-04-03 21:41:28 +00:00
Log.i(TAG, "addSync $idPrefix = $id")
2022-04-08 19:38:19 +00:00
syncs[idPrefix] = id
2022-08-02 00:43:42 +00:00
//_syncIds.postValue(syncs)
2022-04-03 17:11:18 +00:00
return true
2022-04-01 20:05:34 +00:00
}
2022-04-08 19:38:19 +00:00
fun addSyncs(map: Map<String, String>?): Boolean {
var isValid = false
map?.forEach { (prefix, id) ->
2022-04-18 00:26:13 +00:00
isValid = addSync(prefix, id) || isValid
2022-04-08 19:38:19 +00:00
}
return isValid
2022-04-03 21:41:28 +00:00
}
2022-04-18 00:26:13 +00:00
private fun setMalId(id: String?): Boolean {
2022-04-08 19:38:19 +00:00
return addSync(malApi.idPrefix, id ?: return false)
}
2022-04-18 00:26:13 +00:00
private fun setAniListId(id: String?): Boolean {
2022-04-08 19:38:19 +00:00
return addSync(aniListApi.idPrefix, id ?: return false)
2022-04-02 17:58:56 +00:00
}
2022-04-03 17:11:18 +00:00
var hasAddedFromUrl: HashSet<String> = hashSetOf()
2022-04-03 01:13:02 +00:00
2022-08-04 01:19:59 +00:00
fun addFromUrl(url: String?) = ioSafe {
2022-04-03 17:11:18 +00:00
Log.i(TAG, "addFromUrl = $url")
2022-08-04 01:19:59 +00:00
if (url == null || hasAddedFromUrl.contains(url)) return@ioSafe
2022-08-06 18:36:45 +00:00
if(!url.startsWith("http")) return@ioSafe
2022-04-02 17:58:56 +00:00
SyncUtil.getIdsFromUrl(url)?.let { (malId, aniListId) ->
2022-04-03 01:13:02 +00:00
hasAddedFromUrl.add(url)
2022-04-02 17:58:56 +00:00
setMalId(malId)
setAniListId(aniListId)
2022-04-03 17:11:18 +00:00
updateSynced()
2022-04-02 22:06:35 +00:00
if (malId != null || aniListId != null) {
2022-04-03 17:11:18 +00:00
Log.i(TAG, "addFromUrl->updateMetaAndUser $malId $aniListId")
2022-04-02 17:58:56 +00:00
updateMetaAndUser()
}
}
2022-04-01 20:05:34 +00:00
}
2022-04-02 01:38:55 +00:00
fun setEpisodesDelta(delta: Int) {
2022-04-03 17:11:18 +00:00
Log.i(TAG, "setEpisodesDelta = $delta")
2022-04-02 01:38:55 +00:00
val user = userData.value
if (user is Resource.Success) {
user.value.watchedEpisodes?.plus(
delta
)?.let { episode ->
setEpisodes(episode)
}
}
}
fun setEpisodes(episodes: Int) {
2022-04-03 17:11:18 +00:00
Log.i(TAG, "setEpisodes = $episodes")
2022-04-02 01:38:55 +00:00
if (episodes < 0) return
val meta = metadata.value
if (meta is Resource.Success) {
meta.value.totalEpisodes?.let { max ->
if (episodes > max) {
setEpisodes(max)
return
}
}
2022-04-01 20:05:34 +00:00
}
2022-04-02 01:38:55 +00:00
val user = userData.value
if (user is Resource.Success) {
_userDataResponse.postValue(Resource.Success(user.value.copy(watchedEpisodes = episodes)))
}
}
fun setScore(score: Int) {
2022-04-03 17:11:18 +00:00
Log.i(TAG, "setScore = $score")
2022-04-02 01:38:55 +00:00
val user = userData.value
if (user is Resource.Success) {
_userDataResponse.postValue(Resource.Success(user.value.copy(score = score)))
}
}
fun setStatus(which: Int) {
2022-04-03 17:11:18 +00:00
Log.i(TAG, "setStatus = $which")
2022-04-02 01:38:55 +00:00
if (which < -1 || which > 5) return // validate input
val user = userData.value
if (user is Resource.Success) {
_userDataResponse.postValue(Resource.Success(user.value.copy(status = which)))
}
}
2022-08-04 01:19:59 +00:00
fun publishUserData() = ioSafe {
2022-04-03 17:11:18 +00:00
Log.i(TAG, "publishUserData")
2022-04-02 01:38:55 +00:00
val user = userData.value
if (user is Resource.Success) {
2022-04-08 19:38:19 +00:00
syncs.forEach { (prefix, id) ->
2022-04-02 01:38:55 +00:00
repos.firstOrNull { it.idPrefix == prefix }?.score(id, user.value)
}
}
updateUserData()
2022-04-01 20:05:34 +00:00
}
2022-04-08 19:38:19 +00:00
fun modifyMaxEpisode(episodeNum: Int) {
Log.i(TAG, "modifyMaxEpisode = $episodeNum")
modifyData { status ->
status.copy(
watchedEpisodes = maxOf(
episodeNum,
status.watchedEpisodes ?: return@modifyData null
)
)
2022-04-08 19:38:19 +00:00
}
}
/// modifies the current sync data, return null if you don't want to change it
private fun modifyData(update: ((SyncAPI.SyncStatus) -> (SyncAPI.SyncStatus?))) =
2022-08-04 01:19:59 +00:00
ioSafe {
2022-04-08 19:38:19 +00:00
syncs.apmap { (prefix, id) ->
repos.firstOrNull { it.idPrefix == prefix }?.let { repo ->
if (repo.hasAccount()) {
val result = repo.getStatus(id)
if (result is Resource.Success) {
update(result.value)?.let { newData ->
Log.i(TAG, "modifyData ${repo.name} => $newData")
repo.score(id, newData)
}
} else if (result is Resource.Failure) {
2022-04-10 22:00:03 +00:00
Log.e(TAG, "modifyData getStatus error ${result.errorString}")
2022-04-08 19:38:19 +00:00
}
}
}
}
}
2022-08-04 01:19:59 +00:00
fun updateUserData() = ioSafe {
2022-04-03 17:11:18 +00:00
Log.i(TAG, "updateUserData")
2022-04-02 01:38:55 +00:00
_userDataResponse.postValue(Resource.Loading())
2022-04-01 20:05:34 +00:00
var lastError: Resource<SyncAPI.SyncStatus> = Resource.Failure(false, null, null, "No data")
2022-04-08 19:38:19 +00:00
syncs.forEach { (prefix, id) ->
2022-04-03 21:41:28 +00:00
repos.firstOrNull { it.idPrefix == prefix }?.let { repo ->
2022-04-08 19:38:19 +00:00
if (repo.hasAccount()) {
2022-04-03 21:41:28 +00:00
val result = repo.getStatus(id)
if (result is Resource.Success) {
_userDataResponse.postValue(result)
2022-08-04 01:19:59 +00:00
return@ioSafe
2022-04-03 21:41:28 +00:00
} else if (result is Resource.Failure) {
2022-04-10 22:00:03 +00:00
Log.e(TAG, "updateUserData error ${result.errorString}")
2022-04-03 21:41:28 +00:00
lastError = result
}
2022-04-01 20:05:34 +00:00
}
}
}
2022-04-02 01:38:55 +00:00
_userDataResponse.postValue(lastError)
2022-04-01 20:05:34 +00:00
}
2022-08-04 01:19:59 +00:00
private fun updateMetadata() = ioSafe {
2022-04-03 17:11:18 +00:00
Log.i(TAG, "updateMetadata")
2022-04-01 20:05:34 +00:00
_metaResponse.postValue(Resource.Loading())
var lastError: Resource<SyncAPI.SyncResult> = Resource.Failure(false, null, null, "No data")
2022-06-22 01:20:10 +00:00
val current = ArrayList(syncs.toList())
// shitty way to sort anilist first, as it has trailers while mal does not
if (syncs.containsKey(aniListApi.idPrefix)) {
2022-06-22 01:20:10 +00:00
try { // swap can throw error
Collections.swap(current, current.indexOfFirst { it.first == aniListApi.idPrefix }, 0)
} catch (e : Exception) {
logError(e)
}
}
current.forEach { (prefix, id) ->
2022-04-03 21:41:28 +00:00
repos.firstOrNull { it.idPrefix == prefix }?.let { repo ->
if (!repo.requiresLogin || repo.hasAccount()) {
Log.i(TAG, "updateMetadata loading ${repo.idPrefix}")
2022-04-03 21:41:28 +00:00
val result = repo.getResult(id)
if (result is Resource.Success) {
_metaResponse.postValue(result)
2022-08-04 01:19:59 +00:00
return@ioSafe
2022-04-03 21:41:28 +00:00
} else if (result is Resource.Failure) {
Log.e(
TAG,
"updateMetadata error $id at ${repo.idPrefix} ${result.errorString}"
)
2022-04-03 21:41:28 +00:00
lastError = result
}
2022-04-01 20:05:34 +00:00
}
}
}
_metaResponse.postValue(lastError)
2022-04-02 01:38:55 +00:00
setEpisodesDelta(0)
2022-04-01 20:05:34 +00:00
}
2022-04-02 17:58:56 +00:00
fun updateMetaAndUser() {
2022-04-03 17:11:18 +00:00
Log.i(TAG, "updateMetaAndUser")
2022-04-02 17:58:56 +00:00
updateMetadata()
updateUserData()
}
2022-04-01 20:05:34 +00:00
}