From bef783f61d7bffdde0f1d9007f23dc0218a3fe65 Mon Sep 17 00:00:00 2001 From: LagradOst <11805592+LagradOst@users.noreply.github.com> Date: Sun, 3 Apr 2022 19:11:18 +0200 Subject: [PATCH] fixed anilist + added actors --- .../cloudstream3/syncproviders/SyncAPI.kt | 14 +- .../syncproviders/providers/AniListApi.kt | 128 ++++++++++++++++-- .../syncproviders/providers/MALApi.kt | 1 - .../cloudstream3/ui/result/ResultFragment.kt | 14 +- .../cloudstream3/ui/result/ResultViewModel.kt | 9 +- .../cloudstream3/ui/result/SyncViewModel.kt | 45 ++++-- .../lagradost/cloudstream3/utils/SyncUtil.kt | 2 +- 7 files changed, 165 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt index 1d3ee545..bdfff9c1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.syncproviders +import com.lagradost.cloudstream3.ActorData import com.lagradost.cloudstream3.ShowStatus interface SyncAPI : OAuth2API { @@ -36,16 +37,6 @@ interface SyncAPI : OAuth2API { val unixTime: Long, ) - data class SyncActor( - val name: String, - val posterUrl: String?, - ) - - data class SyncCharacter( - val name: String, - val posterUrl: String?, - ) - data class SyncStatus( val status: Int, /** 1-10 */ @@ -84,7 +75,6 @@ interface SyncAPI : OAuth2API { var recommendations: List? = null, var nextSeason: SyncSearchResult? = null, var prevSeason: SyncSearchResult? = null, - var actors: List? = null, - var characters: List? = null, + var actors: List? = null, ) } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt index 003b8a51..803ac5f8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt @@ -5,13 +5,11 @@ import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.readValue +import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.setKey -import com.lagradost.cloudstream3.ErrorLoadingException -import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.OAuth2API @@ -56,7 +54,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { openBrowser(request) } - override suspend fun handleRedirect(url: String) : Boolean { + override suspend fun handleRedirect(url: String): Boolean { val sanitizer = splitQuery(URL(url.replace(appString, "https").replace("/#", "?"))) // FIX ERROR val token = sanitizer["access_token"]!! @@ -87,7 +85,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { override suspend fun getResult(id: String): SyncAPI.SyncResult? { val internalId = id.toIntOrNull() ?: return null - val season = getSeason(internalId).data?.Media ?: throw ErrorLoadingException("No media") + val season = getSeason(internalId).data.Media return SyncAPI.SyncResult( season.id.toString(), @@ -102,7 +100,30 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { isAdult = season.isAdult, totalEpisodes = season.episodes, synopsis = season.description, - + actors = season.characters?.edges?.mapNotNull { edge -> + val node = edge.node ?: return@mapNotNull null + ActorData( + actor = Actor( + name = node.name?.userPreferred ?: node.name?.full ?: node.name?.native + ?: return@mapNotNull null, + image = node.image?.large ?: node.image?.medium + ), + role = when (edge.role) { + "MAIN" -> ActorRole.Main + "SUPPORTING" -> ActorRole.Supporting + "BACKGROUND" -> ActorRole.Background + else -> null + }, + voiceActor = edge.voiceActors?.firstNotNullOfOrNull { staff -> + Actor( + name = staff.name?.userPreferred ?: staff.name?.full + ?: staff.name?.native + ?: return@mapNotNull null, + image = staff.image?.large ?: staff.image?.medium + ) + } + ) + } //TODO REST ) } @@ -126,7 +147,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { fromIntToAnimeStatus(status.status), status.score, status.watchedEpisodes - ) ?: return false + ) } companion object { @@ -317,6 +338,23 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { averageScore isAdult description(asHtml: false) + characters(sort: ROLE page: 1 perPage: 20) { + edges { + role + node { + name { + userPreferred + full + native + } + age + image { + large + medium + } + } + } + } trailer { id site @@ -673,7 +711,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { suspend fun getSeasonRecursive(id: Int) { val season = getSeason(id) seasons.add(season) - if (season.data?.Media?.format?.startsWith("TV") == true) { + if (season.data.Media.format?.startsWith("TV") == true) { season.data.Media.relations?.edges?.forEach { if (it.node?.format != null) { if (it.relationType == "SEQUEL" && it.node.format.startsWith("TV")) { @@ -689,11 +727,11 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } data class SeasonResponse( - @JsonProperty("data") val data: SeasonData?, + @JsonProperty("data") val data: SeasonData, ) data class SeasonData( - @JsonProperty("Media") val Media: SeasonMedia?, + @JsonProperty("Media") val Media: SeasonMedia, ) data class SeasonMedia( @@ -711,6 +749,76 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("isAdult") val isAdult: Boolean?, @JsonProperty("trailer") val trailer: MediaTrailer?, @JsonProperty("description") val description: String?, + @JsonProperty("characters") val characters: CharacterConnection?, + ) + + data class CharacterName( + @JsonProperty("name") val first: String?, + @JsonProperty("middle") val middle: String?, + @JsonProperty("last") val last: String?, + @JsonProperty("full") val full: String?, + @JsonProperty("native") val native: String?, + @JsonProperty("alternative") val alternative: List?, + @JsonProperty("alternativeSpoiler") val alternativeSpoiler: List?, + @JsonProperty("userPreferred") val userPreferred: String?, + ) + + data class CharacterImage( + @JsonProperty("large") val large: String?, + @JsonProperty("medium") val medium: String?, + ) + + data class Character( + @JsonProperty("name") val name: CharacterName?, + @JsonProperty("age") val age: String?, + @JsonProperty("image") val image: CharacterImage?, + ) + + data class CharacterEdge( + @JsonProperty("id") val id: Int?, + /** + MAIN + A primary character role in the media + + SUPPORTING + A supporting character role in the media + + BACKGROUND + A background character in the media + */ + @JsonProperty("role") val role: String?, + @JsonProperty("name") val name: String?, + @JsonProperty("voiceActors") val voiceActors: List?, + @JsonProperty("favouriteOrder") val favouriteOrder: Int?, + @JsonProperty("media") val media: List?, + @JsonProperty("node") val node: Character?, + ) + + data class StaffImage( + @JsonProperty("large") val large: String?, + @JsonProperty("medium") val medium: String?, + ) + + data class StaffName( + @JsonProperty("name") val first: String?, + @JsonProperty("middle") val middle: String?, + @JsonProperty("last") val last: String?, + @JsonProperty("full") val full: String?, + @JsonProperty("native") val native: String?, + @JsonProperty("alternative") val alternative: List?, + @JsonProperty("userPreferred") val userPreferred: String?, + ) + + data class Staff( + @JsonProperty("image") val image: StaffImage?, + @JsonProperty("name") val name: StaffName?, + @JsonProperty("age") val age: Int?, + ) + + data class CharacterConnection( + @JsonProperty("edges") val edges: List?, + @JsonProperty("nodes") val nodes: List?, + //@JsonProperty("pageInfo") pageInfo: PageInfo ) data class MediaTrailer( diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt index 86b69867..7f810780 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt @@ -219,7 +219,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return@firstOrNull it.relationType == "prequel" }?.let { toSearchResult(it.node) }, actors = null, - characters = null, ) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 5d112303..80e69125 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -519,13 +519,11 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio } private fun setMalSync(id: Int?): Boolean { - syncModel.setMalId(id?.toString() ?: return false) - return true + return syncModel.setMalId(id?.toString()) } private fun setAniListSync(id: Int?): Boolean { - syncModel.setAniListId(id?.toString() ?: return false) - return true + return syncModel.setAniListId(id?.toString()) } private fun setActors(actors: List?) { @@ -1547,12 +1545,16 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio if (SettingsFragment.accountEnabled) if (d is AnimeLoadResponse) { + // don't inline these variables as it will cause them to not be called + val addedMal = setMalSync(d.malId) + val addedAniList = setAniListSync(d.anilistId) if ( - setMalSync(d.malId) + addedMal || - setAniListSync(d.anilistId) + addedAniList ) { syncModel.updateMetaAndUser() + syncModel.updateSynced() } else { syncModel.addFromUrl(d.url) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt index 8f03ba43..dcf43769 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt @@ -125,14 +125,7 @@ class ResultViewModel : ViewModel() { plot = if (plot.isNullOrBlank()) meta.synopsis else plot trailerUrl = trailerUrl ?: meta.trailerUrl posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl - actors = actors ?: meta.actors?.map { - ActorData( - Actor( - name = it.name, - image = it.posterUrl - ) - ) - } + actors = actors ?: meta.actors } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt index a01d7635..d1c897d4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.ui.result +import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -18,10 +19,14 @@ data class CurrentSynced( val idPrefix: String, val isSynced: Boolean, val hasAccount: Boolean, - val icon : Int, + val icon: Int, ) class SyncViewModel : ViewModel() { + companion object { + const val TAG = "SYNCVM" + } + private val repos = SyncApis private val _metaResponse: MutableLiveData> = @@ -55,36 +60,47 @@ class SyncViewModel : ViewModel() { } } - private fun updateSynced() { + fun updateSynced() { + Log.i(TAG, "updateSynced") _currentSynced.postValue(getMissing()) } - fun setMalId(id: String?) { - syncIds[malApi.idPrefix] = id ?: return - updateSynced() + fun setMalId(id: String?) : Boolean { + if(syncIds[malApi.idPrefix] == id ?: return false) return false + syncIds[malApi.idPrefix] = id + Log.i(TAG, "setMalId = $id") + return true } - fun setAniListId(id: String?) { - syncIds[aniListApi.idPrefix] = id ?: return - updateSynced() + fun setAniListId(id: String?) : Boolean { + if(syncIds[aniListApi.idPrefix] == id ?: return false) return false + syncIds[aniListApi.idPrefix] = id + Log.i(TAG, "setAniListId = $id") + return true } - var hasAddedFromUrl : HashSet = hashSetOf() + var hasAddedFromUrl: HashSet = hashSetOf() fun addFromUrl(url: String?) = viewModelScope.launch { - if(url == null || hasAddedFromUrl.contains(url)) return@launch + Log.i(TAG, "addFromUrl = $url") + + if (url == null || hasAddedFromUrl.contains(url)) return@launch SyncUtil.getIdsFromUrl(url)?.let { (malId, aniListId) -> hasAddedFromUrl.add(url) setMalId(malId) setAniListId(aniListId) + updateSynced() if (malId != null || aniListId != null) { + Log.i(TAG, "addFromUrl->updateMetaAndUser $malId $aniListId") updateMetaAndUser() } } } fun setEpisodesDelta(delta: Int) { + Log.i(TAG, "setEpisodesDelta = $delta") + val user = userData.value if (user is Resource.Success) { user.value.watchedEpisodes?.plus( @@ -96,6 +112,8 @@ class SyncViewModel : ViewModel() { } fun setEpisodes(episodes: Int) { + Log.i(TAG, "setEpisodes = $episodes") + if (episodes < 0) return val meta = metadata.value if (meta is Resource.Success) { @@ -114,6 +132,7 @@ class SyncViewModel : ViewModel() { } fun setScore(score: Int) { + Log.i(TAG, "setScore = $score") val user = userData.value if (user is Resource.Success) { _userDataResponse.postValue(Resource.Success(user.value.copy(score = score))) @@ -121,6 +140,7 @@ class SyncViewModel : ViewModel() { } fun setStatus(which: Int) { + Log.i(TAG, "setStatus = $which") if (which < -1 || which > 5) return // validate input val user = userData.value if (user is Resource.Success) { @@ -129,6 +149,7 @@ class SyncViewModel : ViewModel() { } fun publishUserData() = viewModelScope.launch { + Log.i(TAG, "publishUserData") val user = userData.value if (user is Resource.Success) { for ((prefix, id) in syncIds) { @@ -139,6 +160,7 @@ class SyncViewModel : ViewModel() { } private fun updateUserData() = viewModelScope.launch { + Log.i(TAG, "updateUserData") _userDataResponse.postValue(Resource.Loading()) var lastError: Resource = Resource.Failure(false, null, null, "No data") for ((prefix, id) in syncIds) { @@ -156,6 +178,8 @@ class SyncViewModel : ViewModel() { } private fun updateMetadata() = viewModelScope.launch { + Log.i(TAG, "updateMetadata") + _metaResponse.postValue(Resource.Loading()) var lastError: Resource = Resource.Failure(false, null, null, "No data") for ((prefix, id) in syncIds) { @@ -174,6 +198,7 @@ class SyncViewModel : ViewModel() { } fun updateMetaAndUser() { + Log.i(TAG, "updateMetaAndUser") updateMetadata() updateUserData() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt index 1ebef068..03bb82b8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt @@ -15,7 +15,7 @@ object SyncUtil { Regex("""(twist\.moe)/a/([^/?]*)"""), ) - private const val TAG = "SNC" + private const val TAG = "SYNCUTIL" private const val GOGOANIME = "Gogoanime" private const val NINE_ANIME = "9anime"