fixing anilist stuff

This commit is contained in:
LagradOst 2022-04-03 00:06:35 +02:00
parent 2a663ccef1
commit a933aa8493
8 changed files with 192 additions and 105 deletions

View file

@ -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)

View file

@ -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,

View file

@ -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
}
} }

View file

@ -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?,
) )
} }

View file

@ -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 =

View file

@ -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) {

View file

@ -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,

View file

@ -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"