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