mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	fixing anilist stuff
This commit is contained in:
		
							parent
							
								
									2a663ccef1
								
							
						
					
					
						commit
						a933aa8493
					
				
					 8 changed files with 192 additions and 105 deletions
				
			
		| 
						 | 
				
			
			@ -7,11 +7,9 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
 | 
			
		|||
import com.lagradost.cloudstream3.utils.loadExtractor
 | 
			
		||||
import org.jsoup.Jsoup
 | 
			
		||||
import java.util.*
 | 
			
		||||
import kotlin.collections.ArrayList
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AnimekisaProvider : MainAPI() {
 | 
			
		||||
 | 
			
		||||
    override var mainUrl = "https://animekisa.in"
 | 
			
		||||
    override var name = "Animekisa"
 | 
			
		||||
    override val hasMainPage = true
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +21,7 @@ class AnimekisaProvider : MainAPI() {
 | 
			
		|||
        TvType.Anime,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class Response (
 | 
			
		||||
    data class Response(
 | 
			
		||||
        @JsonProperty("html") val html: String
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -72,11 +70,15 @@ class AnimekisaProvider : MainAPI() {
 | 
			
		|||
        if (items.size <= 0) throw ErrorLoadingException()
 | 
			
		||||
        return HomePageResponse(items)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun search(query: String): List<SearchResponse> {
 | 
			
		||||
        return app.get("$mainUrl/search/?keyword=$query").document.select("div.flw-item").map {
 | 
			
		||||
            val title = it.selectFirst("h3 a").text()
 | 
			
		||||
            val url = it.selectFirst("a.film-poster-ahref").attr("href")
 | 
			
		||||
                .replace("watch/","anime/").replace(Regex("(-episode-(\\d+)\\/\$|-episode-(\\d+)\$|-episode-full|-episode-.*-.(\\/|))"),"")
 | 
			
		||||
                .replace("watch/", "anime/").replace(
 | 
			
		||||
                    Regex("(-episode-(\\d+)\\/\$|-episode-(\\d+)\$|-episode-full|-episode-.*-.(\\/|))"),
 | 
			
		||||
                    ""
 | 
			
		||||
                )
 | 
			
		||||
            val poster = it.selectFirst(".film-poster img").attr("data-src")
 | 
			
		||||
            AnimeSearchResponse(
 | 
			
		||||
                title,
 | 
			
		||||
| 
						 | 
				
			
			@ -91,18 +93,24 @@ class AnimekisaProvider : MainAPI() {
 | 
			
		|||
            )
 | 
			
		||||
        }.toList()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun load(url: String): LoadResponse {
 | 
			
		||||
        val doc = app.get(url, timeout = 120).document
 | 
			
		||||
        val poster = doc.selectFirst(".mb-2 img").attr("src") ?: doc.selectFirst("head meta[property=og:image]").attr("content")
 | 
			
		||||
        val poster = doc.selectFirst(".mb-2 img").attr("src")
 | 
			
		||||
            ?: doc.selectFirst("head meta[property=og:image]").attr("content")
 | 
			
		||||
        val title = doc.selectFirst("h1.heading-name a").text()
 | 
			
		||||
        val description = doc.selectFirst("div.description p").text().trim()
 | 
			
		||||
        val genres = doc.select("div.row-line a").map { it.text() }
 | 
			
		||||
        val test = if (doc.selectFirst("div.dp-i-c-right").toString().contains("Airing")) ShowStatus.Ongoing else ShowStatus.Completed
 | 
			
		||||
        val test = if (doc.selectFirst("div.dp-i-c-right").toString()
 | 
			
		||||
                .contains("Airing")
 | 
			
		||||
        ) ShowStatus.Ongoing else ShowStatus.Completed
 | 
			
		||||
        val episodes = doc.select("div.tab-content ul li.nav-item").map {
 | 
			
		||||
            val link = it.selectFirst("a").attr("href")
 | 
			
		||||
            AnimeEpisode(link)
 | 
			
		||||
        }
 | 
			
		||||
        val type = if (doc.selectFirst(".dp-i-stats").toString().contains("Movies")) TvType.AnimeMovie else TvType.Anime
 | 
			
		||||
        val type = if (doc.selectFirst(".dp-i-stats").toString()
 | 
			
		||||
                .contains("Movies")
 | 
			
		||||
        ) TvType.AnimeMovie else TvType.Anime
 | 
			
		||||
        return newAnimeLoadResponse(title, url, type) {
 | 
			
		||||
            posterUrl = poster
 | 
			
		||||
            addEpisodes(DubStatus.Subbed, episodes)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -70,7 +70,9 @@ interface SyncAPI : OAuth2API {
 | 
			
		|||
        var nextAiring: SyncNextAiring? = null,
 | 
			
		||||
        var studio: List<String>? = null,
 | 
			
		||||
        var genres: List<String>? = null,
 | 
			
		||||
        var synonyms: List<String>? = null,
 | 
			
		||||
        var trailerUrl: String? = null,
 | 
			
		||||
        var isAdult : Boolean? = null,
 | 
			
		||||
 | 
			
		||||
        /** In unixtime */
 | 
			
		||||
        var startDate: Long? = null,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,10 +2,12 @@ package com.lagradost.cloudstream3.syncproviders
 | 
			
		|||
 | 
			
		||||
import com.lagradost.cloudstream3.ErrorLoadingException
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.Resource
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
 | 
			
		||||
 | 
			
		||||
class SyncRepo(private val repo: SyncAPI) {
 | 
			
		||||
    val idPrefix get() = repo.idPrefix
 | 
			
		||||
    val idPrefix = repo.idPrefix
 | 
			
		||||
    val name = repo.name
 | 
			
		||||
 | 
			
		||||
    suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource<Boolean> {
 | 
			
		||||
        return safeApiCall { repo.score(id, status) }
 | 
			
		||||
| 
						 | 
				
			
			@ -22,4 +24,8 @@ class SyncRepo(private val repo: SyncAPI) {
 | 
			
		|||
    suspend fun search(query : String) : Resource<List<SyncAPI.SyncSearchResult>> {
 | 
			
		||||
        return safeApiCall { repo.search(query) ?: throw ErrorLoadingException() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun hasAccount() : Boolean {
 | 
			
		||||
        return normalSafeApiCall { repo.loginInfo() != null } ?: false
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -73,9 +73,9 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
 | 
			
		||||
    override suspend fun search(name: String): List<SyncAPI.SyncSearchResult>? {
 | 
			
		||||
        val data = searchShows(name) ?: return null
 | 
			
		||||
        return data.data.Page.media.map {
 | 
			
		||||
        return data.data?.Page?.media?.map {
 | 
			
		||||
            SyncAPI.SyncSearchResult(
 | 
			
		||||
                it.title.romaji,
 | 
			
		||||
                it.title.romaji ?: return null,
 | 
			
		||||
                this.name,
 | 
			
		||||
                it.id.toString(),
 | 
			
		||||
                "$mainUrl/anime/${it.id}",
 | 
			
		||||
| 
						 | 
				
			
			@ -92,10 +92,15 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
            season.id.toString(),
 | 
			
		||||
            nextAiring = season.nextAiringEpisode?.let {
 | 
			
		||||
                SyncAPI.SyncNextAiring(
 | 
			
		||||
                    it.episode,
 | 
			
		||||
                    it.timeUntilAiring + unixTime
 | 
			
		||||
                    it.episode ?: return@let null,
 | 
			
		||||
                    (it.timeUntilAiring ?: return@let null) + unixTime
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            genres = season.genres,
 | 
			
		||||
            synonyms = season.synonyms,
 | 
			
		||||
            isAdult = season.isAdult,
 | 
			
		||||
            totalEpisodes = season.episodes,
 | 
			
		||||
            //synopsis = season.
 | 
			
		||||
            //TODO REST
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +112,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
        return SyncAPI.SyncStatus(
 | 
			
		||||
            score = data.score,
 | 
			
		||||
            watchedEpisodes = data.episodes,
 | 
			
		||||
            status = data.type.value,
 | 
			
		||||
            status = data.type?.value ?: return null,
 | 
			
		||||
            isFavorite = data.isFavourite,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +259,9 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
                            )
 | 
			
		||||
                }
 | 
			
		||||
            filtered?.forEach {
 | 
			
		||||
                if (fixName(it.title.romaji) == fixName(name)) return it
 | 
			
		||||
                it.title.romaji?.let { romaji ->
 | 
			
		||||
                    if (fixName(romaji) == fixName(name)) return it
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return filtered?.firstOrNull()
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +274,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
            Paused(2),
 | 
			
		||||
            Dropped(3),
 | 
			
		||||
            Planning(4),
 | 
			
		||||
            Rewatching(5),
 | 
			
		||||
            ReWatching(5),
 | 
			
		||||
            None(-1)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -279,7 +286,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
                2 -> AniListStatusType.Paused
 | 
			
		||||
                3 -> AniListStatusType.Dropped
 | 
			
		||||
                4 -> AniListStatusType.Planning
 | 
			
		||||
                5 -> AniListStatusType.Rewatching
 | 
			
		||||
                5 -> AniListStatusType.ReWatching
 | 
			
		||||
                else -> AniListStatusType.None
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -290,22 +297,26 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
        private suspend fun getSeason(id: Int): SeasonResponse? {
 | 
			
		||||
            val q: String = """
 | 
			
		||||
            val q = """
 | 
			
		||||
               query (${'$'}id: Int = $id) {
 | 
			
		||||
                   Media (id: ${'$'}id, type: ANIME) {
 | 
			
		||||
                       id
 | 
			
		||||
                       idMal
 | 
			
		||||
                       coverImage
 | 
			
		||||
                       duration
 | 
			
		||||
                       episodes
 | 
			
		||||
                       genres
 | 
			
		||||
                       synonyms
 | 
			
		||||
                       averageScore
 | 
			
		||||
                       isAdult
 | 
			
		||||
                       trailer
 | 
			
		||||
                       relations {
 | 
			
		||||
                            edges {
 | 
			
		||||
                                 id
 | 
			
		||||
                                 relationType(version: 2)
 | 
			
		||||
                                 node {
 | 
			
		||||
                                      id
 | 
			
		||||
                                      format
 | 
			
		||||
                                      nextAiringEpisode {
 | 
			
		||||
                                           timeUntilAiring
 | 
			
		||||
                                           episode
 | 
			
		||||
                                      }
 | 
			
		||||
                                      coverImage
 | 
			
		||||
                                 }
 | 
			
		||||
                            }
 | 
			
		||||
                       }
 | 
			
		||||
| 
						 | 
				
			
			@ -369,8 +380,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
        val data = postApi(q, true)
 | 
			
		||||
        val d = mapper.readValue<GetDataRoot>(data ?: return null)
 | 
			
		||||
 | 
			
		||||
        val main = d.data.Media
 | 
			
		||||
        if (main.mediaListEntry != null) {
 | 
			
		||||
        val main = d.data?.Media
 | 
			
		||||
        if (main?.mediaListEntry != null) {
 | 
			
		||||
            return AniListTitleHolder(
 | 
			
		||||
                title = main.title,
 | 
			
		||||
                id = id,
 | 
			
		||||
| 
						 | 
				
			
			@ -382,11 +393,11 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
            )
 | 
			
		||||
        } else {
 | 
			
		||||
            return AniListTitleHolder(
 | 
			
		||||
                title = main.title,
 | 
			
		||||
                title = main?.title,
 | 
			
		||||
                id = id,
 | 
			
		||||
                isFavourite = main.isFavourite,
 | 
			
		||||
                isFavourite = main?.isFavourite,
 | 
			
		||||
                progress = 0,
 | 
			
		||||
                episodes = main.episodes,
 | 
			
		||||
                episodes = main?.episodes,
 | 
			
		||||
                score = 0,
 | 
			
		||||
                type = AniListStatusType.None,
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			@ -420,14 +431,14 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
 | 
			
		||||
    data class MediaRecommendation(
 | 
			
		||||
        @JsonProperty("id") val id: Int,
 | 
			
		||||
        @JsonProperty("title") val title: Title,
 | 
			
		||||
        @JsonProperty("title") val title: Title?,
 | 
			
		||||
        @JsonProperty("idMal") val idMal: Int?,
 | 
			
		||||
        @JsonProperty("coverImage") val coverImage: CoverImage,
 | 
			
		||||
        @JsonProperty("coverImage") val coverImage: CoverImage?,
 | 
			
		||||
        @JsonProperty("averageScore") val averageScore: Int?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class FullAnilistList(
 | 
			
		||||
        @JsonProperty("data") val data: Data
 | 
			
		||||
        @JsonProperty("data") val data: Data?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class CompletedAt(
 | 
			
		||||
| 
						 | 
				
			
			@ -448,7 +459,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
    )
 | 
			
		||||
 | 
			
		||||
    data class CoverImage(
 | 
			
		||||
        @JsonProperty("medium") val medium: String,
 | 
			
		||||
        @JsonProperty("medium") val medium: String?,
 | 
			
		||||
        @JsonProperty("large") val large: String?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -629,11 +640,11 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
        val data = postApi(q)
 | 
			
		||||
        if (data == "") return null
 | 
			
		||||
        val userData = mapper.readValue<AniListRoot>(data ?: return null)
 | 
			
		||||
        val u = userData.data.Viewer
 | 
			
		||||
        val u = userData.data?.Viewer
 | 
			
		||||
        val user = AniListUser(
 | 
			
		||||
            u.id,
 | 
			
		||||
            u.name,
 | 
			
		||||
            u.avatar.large,
 | 
			
		||||
            u?.id,
 | 
			
		||||
            u?.name,
 | 
			
		||||
            u?.avatar?.large,
 | 
			
		||||
        )
 | 
			
		||||
        if (setSettings) {
 | 
			
		||||
            setKey(accountId, ANILIST_USER_KEY, user)
 | 
			
		||||
| 
						 | 
				
			
			@ -652,9 +663,9 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
            val season = getSeason(id)
 | 
			
		||||
            if (season != null) {
 | 
			
		||||
                seasons.add(season)
 | 
			
		||||
                if (season.data.Media.format?.startsWith("TV") == true) {
 | 
			
		||||
                    season.data.Media.relations.edges.forEach {
 | 
			
		||||
                        if (it.node.format != null) {
 | 
			
		||||
                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")) {
 | 
			
		||||
                                getSeasonRecursive(it.node.id)
 | 
			
		||||
                                return@forEach
 | 
			
		||||
| 
						 | 
				
			
			@ -669,34 +680,56 @@ 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(
 | 
			
		||||
        @JsonProperty("id") val id: Int,
 | 
			
		||||
        @JsonProperty("id") val id: Int?,
 | 
			
		||||
        @JsonProperty("idMal") val idMal: Int?,
 | 
			
		||||
        @JsonProperty("format") val format: String?,
 | 
			
		||||
        @JsonProperty("nextAiringEpisode") val nextAiringEpisode: SeasonNextAiringEpisode?,
 | 
			
		||||
        @JsonProperty("relations") val relations: SeasonEdges,
 | 
			
		||||
        @JsonProperty("relations") val relations: SeasonEdges?,
 | 
			
		||||
        @JsonProperty("coverImage") val coverImage: MediaCoverImage?,
 | 
			
		||||
        @JsonProperty("duration") val duration: Int?,
 | 
			
		||||
        @JsonProperty("episodes") val episodes: Int?,
 | 
			
		||||
        @JsonProperty("genres") val genres: List<String>?,
 | 
			
		||||
        @JsonProperty("synonyms") val synonyms: List<String>?,
 | 
			
		||||
        @JsonProperty("averageScore") val averageScore: Int?,
 | 
			
		||||
        @JsonProperty("isAdult") val isAdult: Boolean?,
 | 
			
		||||
        @JsonProperty("trailer") val trailer: MediaTrailer?,
 | 
			
		||||
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    data class MediaTrailer(
 | 
			
		||||
        @JsonProperty("id") val id: String?,
 | 
			
		||||
        @JsonProperty("site") val site: String?,
 | 
			
		||||
        @JsonProperty("thumbnail") val thumbnail: String?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class MediaCoverImage(
 | 
			
		||||
        @JsonProperty("extraLarge") val extraLarge: String?,
 | 
			
		||||
        @JsonProperty("large") val large: String?,
 | 
			
		||||
        @JsonProperty("medium") val medium: String?,
 | 
			
		||||
        @JsonProperty("color") val color: String?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class SeasonNextAiringEpisode(
 | 
			
		||||
        @JsonProperty("episode") val episode: Int,
 | 
			
		||||
        @JsonProperty("timeUntilAiring") val timeUntilAiring: Int,
 | 
			
		||||
        @JsonProperty("episode") val episode: Int?,
 | 
			
		||||
        @JsonProperty("timeUntilAiring") val timeUntilAiring: Int?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class SeasonEdges(
 | 
			
		||||
        @JsonProperty("edges") val edges: List<SeasonEdge>,
 | 
			
		||||
        @JsonProperty("edges") val edges: List<SeasonEdge>?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class SeasonEdge(
 | 
			
		||||
        @JsonProperty("id") val id: Int,
 | 
			
		||||
        @JsonProperty("relationType") val relationType: String,
 | 
			
		||||
        @JsonProperty("node") val node: SeasonNode,
 | 
			
		||||
        @JsonProperty("id") val id: Int?,
 | 
			
		||||
        @JsonProperty("relationType") val relationType: String?,
 | 
			
		||||
        @JsonProperty("node") val node: SeasonNode?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class AniListFavoritesMediaConnection(
 | 
			
		||||
| 
						 | 
				
			
			@ -710,121 +743,121 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
    data class SeasonNode(
 | 
			
		||||
        @JsonProperty("id") val id: Int,
 | 
			
		||||
        @JsonProperty("format") val format: String?,
 | 
			
		||||
        @JsonProperty("title") val title: Title,
 | 
			
		||||
        @JsonProperty("title") val title: Title?,
 | 
			
		||||
        @JsonProperty("idMal") val idMal: Int?,
 | 
			
		||||
        @JsonProperty("coverImage") val coverImage: CoverImage,
 | 
			
		||||
        @JsonProperty("coverImage") val coverImage: CoverImage?,
 | 
			
		||||
        @JsonProperty("averageScore") val averageScore: Int?
 | 
			
		||||
//        @JsonProperty("nextAiringEpisode") val nextAiringEpisode: SeasonNextAiringEpisode?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class AniListAvatar(
 | 
			
		||||
        @JsonProperty("large") val large: String,
 | 
			
		||||
        @JsonProperty("large") val large: String?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class AniListViewer(
 | 
			
		||||
        @JsonProperty("id") val id: Int,
 | 
			
		||||
        @JsonProperty("name") val name: String,
 | 
			
		||||
        @JsonProperty("avatar") val avatar: AniListAvatar,
 | 
			
		||||
        @JsonProperty("favourites") val favourites: AniListFavourites,
 | 
			
		||||
        @JsonProperty("id") val id: Int?,
 | 
			
		||||
        @JsonProperty("name") val name: String?,
 | 
			
		||||
        @JsonProperty("avatar") val avatar: AniListAvatar?,
 | 
			
		||||
        @JsonProperty("favourites") val favourites: AniListFavourites?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class AniListData(
 | 
			
		||||
        @JsonProperty("Viewer") val Viewer: AniListViewer,
 | 
			
		||||
        @JsonProperty("Viewer") val Viewer: AniListViewer?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class AniListRoot(
 | 
			
		||||
        @JsonProperty("data") val data: AniListData,
 | 
			
		||||
        @JsonProperty("data") val data: AniListData?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class AniListUser(
 | 
			
		||||
        @JsonProperty("id") val id: Int,
 | 
			
		||||
        @JsonProperty("name") val name: String,
 | 
			
		||||
        @JsonProperty("picture") val picture: String,
 | 
			
		||||
        @JsonProperty("id") val id: Int?,
 | 
			
		||||
        @JsonProperty("name") val name: String?,
 | 
			
		||||
        @JsonProperty("picture") val picture: String?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LikeNode(
 | 
			
		||||
        @JsonProperty("id") val id: Int,
 | 
			
		||||
        @JsonProperty("id") val id: Int?,
 | 
			
		||||
        //@JsonProperty("idMal") public int idMal;
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LikePageInfo(
 | 
			
		||||
        @JsonProperty("total") val total: Int,
 | 
			
		||||
        @JsonProperty("currentPage") val currentPage: Int,
 | 
			
		||||
        @JsonProperty("lastPage") val lastPage: Int,
 | 
			
		||||
        @JsonProperty("perPage") val perPage: Int,
 | 
			
		||||
        @JsonProperty("hasNextPage") val hasNextPage: Boolean,
 | 
			
		||||
        @JsonProperty("total") val total: Int?,
 | 
			
		||||
        @JsonProperty("currentPage") val currentPage: Int?,
 | 
			
		||||
        @JsonProperty("lastPage") val lastPage: Int?,
 | 
			
		||||
        @JsonProperty("perPage") val perPage: Int?,
 | 
			
		||||
        @JsonProperty("hasNextPage") val hasNextPage: Boolean?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LikeAnime(
 | 
			
		||||
        @JsonProperty("nodes") val nodes: List<LikeNode>,
 | 
			
		||||
        @JsonProperty("pageInfo") val pageInfo: LikePageInfo,
 | 
			
		||||
        @JsonProperty("nodes") val nodes: List<LikeNode>?,
 | 
			
		||||
        @JsonProperty("pageInfo") val pageInfo: LikePageInfo?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LikeFavourites(
 | 
			
		||||
        @JsonProperty("anime") val anime: LikeAnime,
 | 
			
		||||
        @JsonProperty("anime") val anime: LikeAnime?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LikeViewer(
 | 
			
		||||
        @JsonProperty("favourites") val favourites: LikeFavourites,
 | 
			
		||||
        @JsonProperty("favourites") val favourites: LikeFavourites?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LikeData(
 | 
			
		||||
        @JsonProperty("Viewer") val Viewer: LikeViewer,
 | 
			
		||||
        @JsonProperty("Viewer") val Viewer: LikeViewer?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LikeRoot(
 | 
			
		||||
        @JsonProperty("data") val data: LikeData,
 | 
			
		||||
        @JsonProperty("data") val data: LikeData?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class Recommendation(
 | 
			
		||||
        @JsonProperty("title") val title: String,
 | 
			
		||||
        @JsonProperty("idMal") val idMal: Int,
 | 
			
		||||
        @JsonProperty("poster") val poster: String,
 | 
			
		||||
        @JsonProperty("title") val title: String?,
 | 
			
		||||
        @JsonProperty("idMal") val idMal: Int?,
 | 
			
		||||
        @JsonProperty("poster") val poster: String?,
 | 
			
		||||
        @JsonProperty("averageScore") val averageScore: Int?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class AniListTitleHolder(
 | 
			
		||||
        @JsonProperty("title") val title: Title,
 | 
			
		||||
        @JsonProperty("isFavourite") val isFavourite: Boolean,
 | 
			
		||||
        @JsonProperty("id") val id: Int,
 | 
			
		||||
        @JsonProperty("progress") val progress: Int,
 | 
			
		||||
        @JsonProperty("episodes") val episodes: Int,
 | 
			
		||||
        @JsonProperty("score") val score: Int,
 | 
			
		||||
        @JsonProperty("type") val type: AniListStatusType,
 | 
			
		||||
        @JsonProperty("title") val title: Title?,
 | 
			
		||||
        @JsonProperty("isFavourite") val isFavourite: Boolean?,
 | 
			
		||||
        @JsonProperty("id") val id: Int?,
 | 
			
		||||
        @JsonProperty("progress") val progress: Int?,
 | 
			
		||||
        @JsonProperty("episodes") val episodes: Int?,
 | 
			
		||||
        @JsonProperty("score") val score: Int?,
 | 
			
		||||
        @JsonProperty("type") val type: AniListStatusType?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class GetDataMediaListEntry(
 | 
			
		||||
        @JsonProperty("progress") val progress: Int,
 | 
			
		||||
        @JsonProperty("status") val status: String,
 | 
			
		||||
        @JsonProperty("score") val score: Int,
 | 
			
		||||
        @JsonProperty("progress") val progress: Int?,
 | 
			
		||||
        @JsonProperty("status") val status: String?,
 | 
			
		||||
        @JsonProperty("score") val score: Int?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class Nodes(
 | 
			
		||||
        @JsonProperty("id") val id: Int,
 | 
			
		||||
        @JsonProperty("id") val id: Int?,
 | 
			
		||||
        @JsonProperty("mediaRecommendation") val mediaRecommendation: MediaRecommendation?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class GetDataMedia(
 | 
			
		||||
        @JsonProperty("isFavourite") val isFavourite: Boolean,
 | 
			
		||||
        @JsonProperty("episodes") val episodes: Int,
 | 
			
		||||
        @JsonProperty("title") val title: Title,
 | 
			
		||||
        @JsonProperty("isFavourite") val isFavourite: Boolean?,
 | 
			
		||||
        @JsonProperty("episodes") val episodes: Int?,
 | 
			
		||||
        @JsonProperty("title") val title: Title?,
 | 
			
		||||
        @JsonProperty("mediaListEntry") val mediaListEntry: GetDataMediaListEntry?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class Recommendations(
 | 
			
		||||
        @JsonProperty("nodes") val nodes: List<Nodes>
 | 
			
		||||
        @JsonProperty("nodes") val nodes: List<Nodes>?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class GetDataData(
 | 
			
		||||
        @JsonProperty("Media") val Media: GetDataMedia,
 | 
			
		||||
        @JsonProperty("Media") val Media: GetDataMedia?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class GetDataRoot(
 | 
			
		||||
        @JsonProperty("data") val data: GetDataData,
 | 
			
		||||
        @JsonProperty("data") val data: GetDataData?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class GetSearchTitle(
 | 
			
		||||
        @JsonProperty("romaji") val romaji: String,
 | 
			
		||||
        @JsonProperty("romaji") val romaji: String?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class TrailerObject(
 | 
			
		||||
| 
						 | 
				
			
			@ -845,18 +878,18 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
        @JsonProperty("trailer") val trailer: TrailerObject?,
 | 
			
		||||
        @JsonProperty("nextAiringEpisode") val nextAiringEpisode: SeasonNextAiringEpisode?,
 | 
			
		||||
        @JsonProperty("recommendations") val recommendations: Recommendations?,
 | 
			
		||||
        @JsonProperty("relations") val relations: SeasonEdges
 | 
			
		||||
        @JsonProperty("relations") val relations: SeasonEdges?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class GetSearchPage(
 | 
			
		||||
        @JsonProperty("Page") val Page: GetSearchData,
 | 
			
		||||
        @JsonProperty("Page") val Page: GetSearchData?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class GetSearchData(
 | 
			
		||||
        @JsonProperty("media") val media: List<GetSearchMedia>,
 | 
			
		||||
        @JsonProperty("media") val media: List<GetSearchMedia>?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class GetSearchRoot(
 | 
			
		||||
        @JsonProperty("data") val data: GetSearchPage,
 | 
			
		||||
        @JsonProperty("data") val data: GetSearchPage?,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1191,19 +1191,24 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
 | 
			
		|||
                syncModel.setEpisodesDelta(-1)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            result_sync_current_episodes?.doOnTextChanged { text, start, before, count ->
 | 
			
		||||
                if(count == before) return@doOnTextChanged
 | 
			
		||||
            result_sync_current_episodes?.doOnTextChanged { text, _, before, count ->
 | 
			
		||||
                if (count == before) return@doOnTextChanged
 | 
			
		||||
                text?.toString()?.toIntOrNull()?.let { ep ->
 | 
			
		||||
                    syncModel.setEpisodes(ep)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        observe(syncModel.synced) { list ->
 | 
			
		||||
            result_sync_names?.text =
 | 
			
		||||
                list.filter { it.isSynced && it.hasAccount }.joinToString { it.name }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        observe(syncModel.metadata) { meta ->
 | 
			
		||||
            when (meta) {
 | 
			
		||||
                is Resource.Success -> {
 | 
			
		||||
                    val d = meta.value
 | 
			
		||||
                    result_sync_episodes?.max = (d.totalEpisodes ?: 0)*1000
 | 
			
		||||
                    result_sync_episodes?.max = (d.totalEpisodes ?: 0) * 1000
 | 
			
		||||
                    normalSafeApiCall {
 | 
			
		||||
                        val ctx = result_sync_max_episodes?.context
 | 
			
		||||
                        result_sync_max_episodes?.text =
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,13 @@ import com.lagradost.cloudstream3.utils.SyncUtil
 | 
			
		|||
import kotlinx.coroutines.launch
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
data class CurrentSynced(
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val idPrefix: String,
 | 
			
		||||
    val isSynced: Boolean,
 | 
			
		||||
    val hasAccount: Boolean,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
class SyncViewModel : ViewModel() {
 | 
			
		||||
    private val repos = SyncApis
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -29,19 +36,42 @@ class SyncViewModel : ViewModel() {
 | 
			
		|||
    // prefix, id
 | 
			
		||||
    private val syncIds = hashMapOf<String, String>()
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                syncIds.containsKey(it.idPrefix),
 | 
			
		||||
                it.hasAccount()
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun updateSynced() {
 | 
			
		||||
        _currentSynced.postValue(getMissing())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setMalId(id: String?) {
 | 
			
		||||
        syncIds[malApi.idPrefix] = id ?: return
 | 
			
		||||
        updateSynced()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setAniListId(id: String?) {
 | 
			
		||||
        syncIds[aniListApi.idPrefix] = id ?: return
 | 
			
		||||
        updateSynced()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun addFromUrl(url : String?) = viewModelScope.launch {
 | 
			
		||||
    fun addFromUrl(url: String?) = viewModelScope.launch {
 | 
			
		||||
        SyncUtil.getIdsFromUrl(url)?.let { (malId, aniListId) ->
 | 
			
		||||
            setMalId(malId)
 | 
			
		||||
            setAniListId(aniListId)
 | 
			
		||||
            if(malId != null || aniListId != null) {
 | 
			
		||||
            if (malId != null || aniListId != null) {
 | 
			
		||||
                updateMetaAndUser()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -101,7 +131,7 @@ class SyncViewModel : ViewModel() {
 | 
			
		|||
        updateUserData()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateUserData() = viewModelScope.launch {
 | 
			
		||||
    private fun updateUserData() = viewModelScope.launch {
 | 
			
		||||
        _userDataResponse.postValue(Resource.Loading())
 | 
			
		||||
        var lastError: Resource<SyncAPI.SyncStatus> = Resource.Failure(false, null, null, "No data")
 | 
			
		||||
        for ((prefix, id) in syncIds) {
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +148,7 @@ class SyncViewModel : ViewModel() {
 | 
			
		|||
        _userDataResponse.postValue(lastError)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateMetadata() = viewModelScope.launch {
 | 
			
		||||
    private fun updateMetadata() = viewModelScope.launch {
 | 
			
		||||
        _metaResponse.postValue(Resource.Loading())
 | 
			
		||||
        var lastError: Resource<SyncAPI.SyncResult> = Resource.Failure(false, null, null, "No data")
 | 
			
		||||
        for ((prefix, id) in syncIds) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,9 +3,12 @@ package com.lagradost.cloudstream3.ui.search
 | 
			
		|||
import com.lagradost.cloudstream3.SearchQuality
 | 
			
		||||
import com.lagradost.cloudstream3.SearchResponse
 | 
			
		||||
import com.lagradost.cloudstream3.TvType
 | 
			
		||||
import com.lagradost.cloudstream3.syncproviders.OAuth2API
 | 
			
		||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
 | 
			
		||||
 | 
			
		||||
class SyncSearchViewModel {
 | 
			
		||||
    private val repos = OAuth2API.SyncApis
 | 
			
		||||
 | 
			
		||||
    data class SyncSearchResultSearchResponse(
 | 
			
		||||
        override val name: String,
 | 
			
		||||
        override val url: String,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@
 | 
			
		|||
                android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
            <TextView
 | 
			
		||||
                    android:visibility="gone"
 | 
			
		||||
                    android:id="@+id/result_sync_names"
 | 
			
		||||
                    android:textStyle="bold"
 | 
			
		||||
                    android:textSize="16sp"
 | 
			
		||||
                    android:layout_marginBottom="10dp"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue