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
|
||||||
|
@ -23,7 +21,7 @@ class AnimekisaProvider : MainAPI() {
|
||||||
TvType.Anime,
|
TvType.Anime,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Response (
|
data class Response(
|
||||||
@JsonProperty("html") val html: String
|
@JsonProperty("html") val html: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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,19 +1191,24 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 -> {
|
||||||
val d = meta.value
|
val d = meta.value
|
||||||
result_sync_episodes?.max = (d.totalEpisodes ?: 0)*1000
|
result_sync_episodes?.max = (d.totalEpisodes ?: 0) * 1000
|
||||||
normalSafeApiCall {
|
normalSafeApiCall {
|
||||||
val ctx = result_sync_max_episodes?.context
|
val ctx = result_sync_max_episodes?.context
|
||||||
result_sync_max_episodes?.text =
|
result_sync_max_episodes?.text =
|
||||||
|
|
|
@ -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,19 +36,42 @@ 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 {
|
||||||
SyncUtil.getIdsFromUrl(url)?.let { (malId, aniListId) ->
|
SyncUtil.getIdsFromUrl(url)?.let { (malId, aniListId) ->
|
||||||
setMalId(malId)
|
setMalId(malId)
|
||||||
setAniListId(aniListId)
|
setAniListId(aniListId)
|
||||||
if(malId != null || aniListId != null) {
|
if (malId != null || aniListId != null) {
|
||||||
updateMetaAndUser()
|
updateMetaAndUser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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…
Reference in a new issue