fixed anilist + added actors

This commit is contained in:
LagradOst 2022-04-03 19:11:18 +02:00
parent ca1c9eb938
commit bef783f61d
7 changed files with 165 additions and 48 deletions

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.syncproviders package com.lagradost.cloudstream3.syncproviders
import com.lagradost.cloudstream3.ActorData
import com.lagradost.cloudstream3.ShowStatus import com.lagradost.cloudstream3.ShowStatus
interface SyncAPI : OAuth2API { interface SyncAPI : OAuth2API {
@ -36,16 +37,6 @@ interface SyncAPI : OAuth2API {
val unixTime: Long, val unixTime: Long,
) )
data class SyncActor(
val name: String,
val posterUrl: String?,
)
data class SyncCharacter(
val name: String,
val posterUrl: String?,
)
data class SyncStatus( data class SyncStatus(
val status: Int, val status: Int,
/** 1-10 */ /** 1-10 */
@ -84,7 +75,6 @@ interface SyncAPI : OAuth2API {
var recommendations: List<SyncSearchResult>? = null, var recommendations: List<SyncSearchResult>? = null,
var nextSeason: SyncSearchResult? = null, var nextSeason: SyncSearchResult? = null,
var prevSeason: SyncSearchResult? = null, var prevSeason: SyncSearchResult? = null,
var actors: List<SyncActor>? = null, var actors: List<ActorData>? = null,
var characters: List<SyncCharacter>? = null,
) )
} }

View file

@ -5,13 +5,11 @@ import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey 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.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.OAuth2API import com.lagradost.cloudstream3.syncproviders.OAuth2API
@ -56,7 +54,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
openBrowser(request) openBrowser(request)
} }
override suspend fun handleRedirect(url: String) : Boolean { override suspend fun handleRedirect(url: String): Boolean {
val sanitizer = val sanitizer =
splitQuery(URL(url.replace(appString, "https").replace("/#", "?"))) // FIX ERROR splitQuery(URL(url.replace(appString, "https").replace("/#", "?"))) // FIX ERROR
val token = sanitizer["access_token"]!! val token = sanitizer["access_token"]!!
@ -87,7 +85,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
override suspend fun getResult(id: String): SyncAPI.SyncResult? { override suspend fun getResult(id: String): SyncAPI.SyncResult? {
val internalId = id.toIntOrNull() ?: return null 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( return SyncAPI.SyncResult(
season.id.toString(), season.id.toString(),
@ -102,7 +100,30 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
isAdult = season.isAdult, isAdult = season.isAdult,
totalEpisodes = season.episodes, totalEpisodes = season.episodes,
synopsis = season.description, 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 //TODO REST
) )
} }
@ -126,7 +147,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
fromIntToAnimeStatus(status.status), fromIntToAnimeStatus(status.status),
status.score, status.score,
status.watchedEpisodes status.watchedEpisodes
) ?: return false )
} }
companion object { companion object {
@ -317,6 +338,23 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
averageScore averageScore
isAdult isAdult
description(asHtml: false) description(asHtml: false)
characters(sort: ROLE page: 1 perPage: 20) {
edges {
role
node {
name {
userPreferred
full
native
}
age
image {
large
medium
}
}
}
}
trailer { trailer {
id id
site site
@ -673,7 +711,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
suspend fun getSeasonRecursive(id: Int) { suspend fun getSeasonRecursive(id: Int) {
val season = getSeason(id) val season = getSeason(id)
seasons.add(season) 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 { season.data.Media.relations?.edges?.forEach {
if (it.node?.format != null) { if (it.node?.format != null) {
if (it.relationType == "SEQUEL" && it.node.format.startsWith("TV")) { if (it.relationType == "SEQUEL" && it.node.format.startsWith("TV")) {
@ -689,11 +727,11 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
} }
data class SeasonResponse( data class SeasonResponse(
@JsonProperty("data") val data: SeasonData?, @JsonProperty("data") val data: SeasonData,
) )
data class SeasonData( data class SeasonData(
@JsonProperty("Media") val Media: SeasonMedia?, @JsonProperty("Media") val Media: SeasonMedia,
) )
data class SeasonMedia( data class SeasonMedia(
@ -711,6 +749,76 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
@JsonProperty("isAdult") val isAdult: Boolean?, @JsonProperty("isAdult") val isAdult: Boolean?,
@JsonProperty("trailer") val trailer: MediaTrailer?, @JsonProperty("trailer") val trailer: MediaTrailer?,
@JsonProperty("description") val description: String?, @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<String>?,
@JsonProperty("alternativeSpoiler") val alternativeSpoiler: List<String>?,
@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<Staff>?,
@JsonProperty("favouriteOrder") val favouriteOrder: Int?,
@JsonProperty("media") val media: List<SeasonMedia>?,
@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<String>?,
@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<CharacterEdge>?,
@JsonProperty("nodes") val nodes: List<Character>?,
//@JsonProperty("pageInfo") pageInfo: PageInfo
) )
data class MediaTrailer( data class MediaTrailer(

View file

@ -219,7 +219,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
return@firstOrNull it.relationType == "prequel" return@firstOrNull it.relationType == "prequel"
}?.let { toSearchResult(it.node) }, }?.let { toSearchResult(it.node) },
actors = null, actors = null,
characters = null,
) )
} }
} }

View file

@ -519,13 +519,11 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
} }
private fun setMalSync(id: Int?): Boolean { private fun setMalSync(id: Int?): Boolean {
syncModel.setMalId(id?.toString() ?: return false) return syncModel.setMalId(id?.toString())
return true
} }
private fun setAniListSync(id: Int?): Boolean { private fun setAniListSync(id: Int?): Boolean {
syncModel.setAniListId(id?.toString() ?: return false) return syncModel.setAniListId(id?.toString())
return true
} }
private fun setActors(actors: List<ActorData>?) { private fun setActors(actors: List<ActorData>?) {
@ -1547,12 +1545,16 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
if (SettingsFragment.accountEnabled) if (SettingsFragment.accountEnabled)
if (d is AnimeLoadResponse) { 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 ( if (
setMalSync(d.malId) addedMal
|| ||
setAniListSync(d.anilistId) addedAniList
) { ) {
syncModel.updateMetaAndUser() syncModel.updateMetaAndUser()
syncModel.updateSynced()
} else { } else {
syncModel.addFromUrl(d.url) syncModel.addFromUrl(d.url)
} }

View file

@ -125,14 +125,7 @@ class ResultViewModel : ViewModel() {
plot = if (plot.isNullOrBlank()) meta.synopsis else plot plot = if (plot.isNullOrBlank()) meta.synopsis else plot
trailerUrl = trailerUrl ?: meta.trailerUrl trailerUrl = trailerUrl ?: meta.trailerUrl
posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl
actors = actors ?: meta.actors?.map { actors = actors ?: meta.actors
ActorData(
Actor(
name = it.name,
image = it.posterUrl
)
)
}
} }
} }

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.ui.result package com.lagradost.cloudstream3.ui.result
import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@ -18,10 +19,14 @@ data class CurrentSynced(
val idPrefix: String, val idPrefix: String,
val isSynced: Boolean, val isSynced: Boolean,
val hasAccount: Boolean, val hasAccount: Boolean,
val icon : Int, val icon: Int,
) )
class SyncViewModel : ViewModel() { class SyncViewModel : ViewModel() {
companion object {
const val TAG = "SYNCVM"
}
private val repos = SyncApis private val repos = SyncApis
private val _metaResponse: MutableLiveData<Resource<SyncAPI.SyncResult>> = private val _metaResponse: MutableLiveData<Resource<SyncAPI.SyncResult>> =
@ -55,36 +60,47 @@ class SyncViewModel : ViewModel() {
} }
} }
private fun updateSynced() { fun updateSynced() {
Log.i(TAG, "updateSynced")
_currentSynced.postValue(getMissing()) _currentSynced.postValue(getMissing())
} }
fun setMalId(id: String?) { fun setMalId(id: String?) : Boolean {
syncIds[malApi.idPrefix] = id ?: return if(syncIds[malApi.idPrefix] == id ?: return false) return false
updateSynced() syncIds[malApi.idPrefix] = id
Log.i(TAG, "setMalId = $id")
return true
} }
fun setAniListId(id: String?) { fun setAniListId(id: String?) : Boolean {
syncIds[aniListApi.idPrefix] = id ?: return if(syncIds[aniListApi.idPrefix] == id ?: return false) return false
updateSynced() syncIds[aniListApi.idPrefix] = id
Log.i(TAG, "setAniListId = $id")
return true
} }
var hasAddedFromUrl : HashSet<String> = hashSetOf() var hasAddedFromUrl: HashSet<String> = hashSetOf()
fun addFromUrl(url: String?) = viewModelScope.launch { 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) -> SyncUtil.getIdsFromUrl(url)?.let { (malId, aniListId) ->
hasAddedFromUrl.add(url) hasAddedFromUrl.add(url)
setMalId(malId) setMalId(malId)
setAniListId(aniListId) setAniListId(aniListId)
updateSynced()
if (malId != null || aniListId != null) { if (malId != null || aniListId != null) {
Log.i(TAG, "addFromUrl->updateMetaAndUser $malId $aniListId")
updateMetaAndUser() updateMetaAndUser()
} }
} }
} }
fun setEpisodesDelta(delta: Int) { fun setEpisodesDelta(delta: Int) {
Log.i(TAG, "setEpisodesDelta = $delta")
val user = userData.value val user = userData.value
if (user is Resource.Success) { if (user is Resource.Success) {
user.value.watchedEpisodes?.plus( user.value.watchedEpisodes?.plus(
@ -96,6 +112,8 @@ class SyncViewModel : ViewModel() {
} }
fun setEpisodes(episodes: Int) { fun setEpisodes(episodes: Int) {
Log.i(TAG, "setEpisodes = $episodes")
if (episodes < 0) return if (episodes < 0) return
val meta = metadata.value val meta = metadata.value
if (meta is Resource.Success) { if (meta is Resource.Success) {
@ -114,6 +132,7 @@ class SyncViewModel : ViewModel() {
} }
fun setScore(score: Int) { fun setScore(score: Int) {
Log.i(TAG, "setScore = $score")
val user = userData.value val user = userData.value
if (user is Resource.Success) { if (user is Resource.Success) {
_userDataResponse.postValue(Resource.Success(user.value.copy(score = score))) _userDataResponse.postValue(Resource.Success(user.value.copy(score = score)))
@ -121,6 +140,7 @@ class SyncViewModel : ViewModel() {
} }
fun setStatus(which: Int) { fun setStatus(which: Int) {
Log.i(TAG, "setStatus = $which")
if (which < -1 || which > 5) return // validate input if (which < -1 || which > 5) return // validate input
val user = userData.value val user = userData.value
if (user is Resource.Success) { if (user is Resource.Success) {
@ -129,6 +149,7 @@ class SyncViewModel : ViewModel() {
} }
fun publishUserData() = viewModelScope.launch { fun publishUserData() = viewModelScope.launch {
Log.i(TAG, "publishUserData")
val user = userData.value val user = userData.value
if (user is Resource.Success) { if (user is Resource.Success) {
for ((prefix, id) in syncIds) { for ((prefix, id) in syncIds) {
@ -139,6 +160,7 @@ class SyncViewModel : ViewModel() {
} }
private fun updateUserData() = viewModelScope.launch { private fun updateUserData() = viewModelScope.launch {
Log.i(TAG, "updateUserData")
_userDataResponse.postValue(Resource.Loading()) _userDataResponse.postValue(Resource.Loading())
var lastError: Resource<SyncAPI.SyncStatus> = Resource.Failure(false, null, null, "No data") var lastError: Resource<SyncAPI.SyncStatus> = Resource.Failure(false, null, null, "No data")
for ((prefix, id) in syncIds) { for ((prefix, id) in syncIds) {
@ -156,6 +178,8 @@ class SyncViewModel : ViewModel() {
} }
private fun updateMetadata() = viewModelScope.launch { private fun updateMetadata() = viewModelScope.launch {
Log.i(TAG, "updateMetadata")
_metaResponse.postValue(Resource.Loading()) _metaResponse.postValue(Resource.Loading())
var lastError: Resource<SyncAPI.SyncResult> = Resource.Failure(false, null, null, "No data") var lastError: Resource<SyncAPI.SyncResult> = Resource.Failure(false, null, null, "No data")
for ((prefix, id) in syncIds) { for ((prefix, id) in syncIds) {
@ -174,6 +198,7 @@ class SyncViewModel : ViewModel() {
} }
fun updateMetaAndUser() { fun updateMetaAndUser() {
Log.i(TAG, "updateMetaAndUser")
updateMetadata() updateMetadata()
updateUserData() updateUserData()
} }

View file

@ -15,7 +15,7 @@ object SyncUtil {
Regex("""(twist\.moe)/a/([^/?]*)"""), Regex("""(twist\.moe)/a/([^/?]*)"""),
) )
private const val TAG = "SNC" private const val TAG = "SYNCUTIL"
private const val GOGOANIME = "Gogoanime" private const val GOGOANIME = "Gogoanime"
private const val NINE_ANIME = "9anime" private const val NINE_ANIME = "9anime"