forked from recloudstream/cloudstream
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
59b5732925
97 changed files with 570 additions and 237 deletions
|
@ -376,6 +376,32 @@ data class ProvidersInfoJson(
|
|||
@JsonProperty("status") var status: Int,
|
||||
)
|
||||
|
||||
|
||||
data class MainPageData(
|
||||
val name: String,
|
||||
val data: String,
|
||||
)
|
||||
|
||||
/** return list of MainPageData with url to name, make for more readable code */
|
||||
fun mainPageOf(vararg elements: Pair<String, String>): List<MainPageData> {
|
||||
return elements.map { (url, name) -> MainPageData(name = name, data = url) }
|
||||
}
|
||||
|
||||
fun newHomePageResponse(
|
||||
name: String,
|
||||
list: List<SearchResponse>,
|
||||
hasNext: Boolean? = null
|
||||
): HomePageResponse {
|
||||
return HomePageResponse(
|
||||
listOf(HomePageList(name, list)),
|
||||
hasNext = hasNext ?: list.isNotEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
fun newHomePageResponse(list: HomePageList, hasNext: Boolean? = null): HomePageResponse {
|
||||
return HomePageResponse(listOf(list), hasNext = hasNext ?: list.list.isNotEmpty())
|
||||
}
|
||||
|
||||
/**Every provider will **not** have try catch built in, so handle exceptions when calling these functions*/
|
||||
abstract class MainAPI {
|
||||
companion object {
|
||||
|
@ -431,8 +457,14 @@ abstract class MainAPI {
|
|||
open val vpnStatus = VPNStatus.None
|
||||
open val providerType = ProviderType.DirectProvider
|
||||
|
||||
open val mainPage = listOf(MainPageData("", ""))
|
||||
|
||||
@WorkerThread
|
||||
open suspend fun getMainPage(): HomePageResponse? {
|
||||
open suspend fun getMainPage(
|
||||
page: Int,
|
||||
categoryName: String,
|
||||
categoryData: String
|
||||
): HomePageResponse? {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
|
@ -632,7 +664,8 @@ fun TvType.isAnimeOp(): Boolean {
|
|||
data class SubtitleFile(val lang: String, val url: String)
|
||||
|
||||
data class HomePageResponse(
|
||||
val items: List<HomePageList>
|
||||
val items: List<HomePageList>,
|
||||
val hasNext: Boolean = false
|
||||
)
|
||||
|
||||
data class HomePageList(
|
||||
|
|
|
@ -104,7 +104,7 @@ class AllAnimeProvider : MainAPI() {
|
|||
@JsonProperty("__typename") val _typename: String? = null
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
// Pair(
|
||||
|
|
|
@ -114,7 +114,7 @@ class AniPlayProvider : MainAPI() {
|
|||
@JsonProperty("videoUrl") val url: String
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val response = app.get("$mainUrl/api/home/latest-episodes?page=0").parsed<List<ApiMainPageAnime>>()
|
||||
|
||||
val results = response.map{
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.lagradost.cloudstream3.animeproviders
|
|||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||
import java.net.URLDecoder
|
||||
|
@ -45,7 +44,7 @@ class AniflixProvider : MainAPI() {
|
|||
}
|
||||
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val soup = app.get(mainUrl).document
|
||||
val elements = listOf(
|
||||
|
@ -75,7 +74,6 @@ class AniflixProvider : MainAPI() {
|
|||
val token = getToken()
|
||||
val url = "$mainUrl/_next/data/$token/search.json?keyword=$query"
|
||||
val response = app.get(url)
|
||||
println("resp: $url ===> ${response.text}")
|
||||
val searchResponse =
|
||||
response.parsedSafe<Search>()
|
||||
?: throw ErrorLoadingException("No Media")
|
||||
|
|
|
@ -62,7 +62,7 @@ class AnimeIndoProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = request(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -58,7 +58,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
TvType.OVA
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
data class Data(
|
||||
@JsonProperty("id") val id: Int,
|
||||
@JsonProperty("anime_id") val animeId: Int,
|
||||
|
|
|
@ -47,7 +47,7 @@ class AnimeSailProvider : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = request(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -64,7 +64,7 @@ class AnimeSaturnProvider : MainAPI() {
|
|||
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
val list = ArrayList<HomePageList>()
|
||||
document.select("div.container:has(span.badge-saturn)").forEach {
|
||||
|
|
|
@ -130,7 +130,7 @@ class AnimeWorldProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = request(mainUrl).document
|
||||
val list = ArrayList<HomePageList>()
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ class AnimefenixProvider:MainAPI() {
|
|||
else DubStatus.Subbed
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/", "Animes"),
|
||||
Pair("$mainUrl/animes?type[]=movie&order=default", "Peliculas", ),
|
||||
|
|
|
@ -22,7 +22,7 @@ class AnimeflvIOProvider:MainAPI() {
|
|||
TvType.OVA,
|
||||
TvType.Anime,
|
||||
)
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/series", "Series actualizadas",),
|
||||
|
|
|
@ -34,7 +34,7 @@ class AnimeflvnetProvider : MainAPI() {
|
|||
TvType.Anime,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/browse?type[]=movie&order=updated", "Películas"),
|
||||
Pair("$mainUrl/browse?status[]=2&order=default", "Animes"),
|
||||
|
|
|
@ -26,7 +26,7 @@ class AnimekisaProvider : MainAPI() {
|
|||
@JsonProperty("html") val html: String
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/ajax/list/views?type=all", "All animes"),
|
||||
Pair("$mainUrl/ajax/list/views?type=day", "Trending now"),
|
||||
|
|
|
@ -96,7 +96,7 @@ class DubbedAnimeProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val trendingUrl = "$mainUrl/xz/trending.php?_=$unixTimeMS"
|
||||
val lastEpisodeUrl = "$mainUrl/xz/epgrid.php?p=1&_=$unixTimeMS"
|
||||
val recentlyAddedUrl = "$mainUrl/xz/gridgrabrecent.php?p=1&_=$unixTimeMS"
|
||||
|
|
|
@ -178,7 +178,7 @@ class GogoanimeProvider : MainAPI() {
|
|||
TvType.OVA
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val headers = mapOf(
|
||||
"authority" to "ajax.gogo-load.com",
|
||||
"sec-ch-ua" to "\"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"",
|
||||
|
|
|
@ -46,7 +46,7 @@ class GomunimeProvider : MainAPI() {
|
|||
@JsonProperty("html") val html: String
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair("e", "Episode Baru"),
|
||||
Pair("c", "Completed"),
|
||||
|
|
|
@ -32,7 +32,7 @@ class JKAnimeProvider : MainAPI() {
|
|||
TvType.Anime,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair(
|
||||
"$mainUrl/directorio/?filtro=fecha&tipo=TV&estado=1&fecha=none&temporada=none&orden=desc",
|
||||
|
|
|
@ -14,7 +14,7 @@ class KawaiifuProvider : MainAPI() {
|
|||
|
||||
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val resp = app.get(mainUrl).text
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class KimCartoonProvider : MainAPI() {
|
|||
return if (url.startsWith("/")) mainUrl + url else url
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val doc = app.get(mainUrl).document.select("#container")
|
||||
val response = mutableListOf(
|
||||
HomePageList(
|
||||
|
|
|
@ -38,7 +38,7 @@ class KuramanimeProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -39,7 +39,7 @@ class KuronimeProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -34,7 +34,7 @@ class MonoschinosProvider : MainAPI() {
|
|||
TvType.Anime,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/emision", "En emisión"),
|
||||
Pair(
|
||||
|
|
|
@ -23,7 +23,7 @@ class MundoDonghuaProvider : MainAPI() {
|
|||
TvType.Anime,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/lista-donghuas", "Donghuas"),
|
||||
)
|
||||
|
|
|
@ -40,7 +40,7 @@ class NeonimeProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -137,44 +137,45 @@ class NineAnimeProvider : MainAPI() {
|
|||
private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8")
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
val items = listOf(
|
||||
"$mainUrl/ajax/home/widget/trending?page=1" to "Trending",
|
||||
"$mainUrl/ajax/home/widget/updated-all?page=1" to "All",
|
||||
"$mainUrl/ajax/home/widget/updated-sub?page=1" to "Recently Updated (SUB)",
|
||||
"$mainUrl/ajax/home/widget/updated-dub?page=1" to
|
||||
"Recently Updated (DUB)",
|
||||
"$mainUrl/ajax/home/widget/updated-china?page=1" to
|
||||
"Recently Updated (Chinese)",
|
||||
"$mainUrl/ajax/home/widget/random?page=1" to "Random",
|
||||
).apmap { (url, name) ->
|
||||
val home = Jsoup.parse(
|
||||
app.get(
|
||||
url
|
||||
).parsed<Response>().html
|
||||
).select("div.item").mapNotNull { element ->
|
||||
val title = element.selectFirst(".info > .name") ?: return@mapNotNull null
|
||||
val link = title.attr("href")
|
||||
val poster = element.selectFirst(".poster > a > img")?.attr("src")
|
||||
val meta = element.selectFirst(".poster > a > .meta > .inner > .left")
|
||||
val subbedEpisodes = meta?.selectFirst(".sub")?.text()?.toIntOrNull()
|
||||
val dubbedEpisodes = meta?.selectFirst(".dub")?.text()?.toIntOrNull()
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/ajax/home/widget/trending?page=" to "Trending",
|
||||
"$mainUrl/ajax/home/widget/updated-all?page=" to "All",
|
||||
"$mainUrl/ajax/home/widget/updated-sub?page=" to "Recently Updated (SUB)",
|
||||
"$mainUrl/ajax/home/widget/updated-dub?page=" to "Recently Updated (DUB)",
|
||||
"$mainUrl/ajax/home/widget/updated-china?page=" to "Recently Updated (Chinese)",
|
||||
"$mainUrl/ajax/home/widget/random?page=" to "Random",
|
||||
)
|
||||
|
||||
newAnimeSearchResponse(title.text() ?: return@mapNotNull null, link) {
|
||||
this.posterUrl = poster
|
||||
addDubStatus(
|
||||
dubbedEpisodes != null,
|
||||
subbedEpisodes != null,
|
||||
dubbedEpisodes,
|
||||
subbedEpisodes
|
||||
)
|
||||
}
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
categoryName: String,
|
||||
categoryData: String
|
||||
): HomePageResponse {
|
||||
val url = categoryData + page
|
||||
val home = Jsoup.parse(
|
||||
app.get(
|
||||
url
|
||||
).parsed<Response>().html
|
||||
).select("div.item").mapNotNull { element ->
|
||||
val title = element.selectFirst(".info > .name") ?: return@mapNotNull null
|
||||
val link = title.attr("href")
|
||||
val poster = element.selectFirst(".poster > a > img")?.attr("src")
|
||||
val meta = element.selectFirst(".poster > a > .meta > .inner > .left")
|
||||
val subbedEpisodes = meta?.selectFirst(".sub")?.text()?.toIntOrNull()
|
||||
val dubbedEpisodes = meta?.selectFirst(".dub")?.text()?.toIntOrNull()
|
||||
|
||||
newAnimeSearchResponse(title.text() ?: return@mapNotNull null, link) {
|
||||
this.posterUrl = poster
|
||||
addDubStatus(
|
||||
dubbedEpisodes != null,
|
||||
subbedEpisodes != null,
|
||||
dubbedEpisodes,
|
||||
subbedEpisodes
|
||||
)
|
||||
}
|
||||
|
||||
HomePageList(name, home)
|
||||
}
|
||||
|
||||
return HomePageResponse(items)
|
||||
return newHomePageResponse(categoryName, home)
|
||||
}
|
||||
|
||||
data class Response(
|
||||
|
|
|
@ -40,7 +40,7 @@ class NontonAnimeIDProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -40,7 +40,7 @@ class OploverzProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -38,7 +38,7 @@ class OtakudesuProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -44,7 +44,7 @@ class TenshiProvider : MainAPI() {
|
|||
}
|
||||
}*/
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val soup = app.get(mainUrl, interceptor = ddosGuardKiller).document
|
||||
for (section in soup.select("#content > section")) {
|
||||
|
|
|
@ -39,7 +39,7 @@ class TocanimeProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -30,7 +30,7 @@ class WcoProvider : MainAPI() {
|
|||
TvType.OVA
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/ajax/list/recently_updated?type=tv", "Recently Updated Anime"),
|
||||
Pair("$mainUrl/ajax/list/recently_updated?type=movie", "Recently Updated Movies"),
|
||||
|
|
|
@ -77,7 +77,7 @@ class ZoroProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val html = app.get("$mainUrl/home").text
|
||||
val document = Jsoup.parse(html)
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class EjaTv : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
// Maybe this based on app language or as setting?
|
||||
val language = "English"
|
||||
val dataMap = mapOf(
|
||||
|
|
|
@ -196,7 +196,7 @@ open class TmdbProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
|
||||
// SAME AS DISCOVER IT SEEMS
|
||||
// val popularSeries = tmdb.tvService().popular(1, "en-US").execute().body()?.results?.map {
|
||||
|
|
|
@ -34,7 +34,7 @@ class AkwamProvider : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
// Title, Url
|
||||
val moviesUrl = listOf(
|
||||
"Movies" to "$mainUrl/movies",
|
||||
|
|
|
@ -27,7 +27,7 @@ class AllMoviesForYouProvider : MainAPI() {
|
|||
TvType.TvSeries
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val soup = app.get(mainUrl).document
|
||||
val urls = listOf(
|
||||
|
|
|
@ -18,7 +18,7 @@ class AltadefinizioneProvider : MainAPI() {
|
|||
TvType.Movie
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/azione/", "Azione"),
|
||||
|
|
|
@ -110,7 +110,7 @@ class AsiaFlixProvider : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val headers = mapOf("X-Requested-By" to "asiaflix-web")
|
||||
val response = app.get("$apiUrl/dashboard", headers = headers).text
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ open class BflixProvider : MainAPI() {
|
|||
|
||||
//override val uniqueId: Int by lazy { "BflixProvider".hashCode() }
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val soup = app.get("$mainUrl/home").document
|
||||
val testa = listOf(
|
||||
|
|
|
@ -41,7 +41,7 @@ class CimaNowProvider : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
|
||||
val doc = app.get("$mainUrl/home", headers = mapOf("user-agent" to "MONKE")).document
|
||||
val pages = doc.select("section").not("section:contains(أختر وجهتك المفضلة)").not("section:contains(تم اضافته حديثاً)").apmap {
|
||||
|
|
|
@ -17,7 +17,7 @@ class CineblogProvider : MainAPI() {
|
|||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/genere/azione/", "Azione"),
|
||||
|
|
|
@ -19,7 +19,7 @@ class CinecalidadProvider : MainAPI() {
|
|||
)
|
||||
override val vpnStatus = VPNStatus.MightBeNeeded //Due to evoload sometimes not loading
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/ver-serie/", "Series"),
|
||||
|
|
|
@ -19,7 +19,7 @@ class CuevanaProvider : MainAPI() {
|
|||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair(mainUrl, "Recientemente actualizadas"),
|
||||
|
|
|
@ -31,7 +31,7 @@ class DoramasYTProvider : MainAPI() {
|
|||
TvType.AsianDrama,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/emision", "En emisión"),
|
||||
Pair(
|
||||
|
|
|
@ -14,7 +14,7 @@ class DramaSeeProvider : MainAPI() {
|
|||
override val hasDownloadSupport = true
|
||||
override val supportedTypes = setOf(TvType.AsianDrama)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val headers = mapOf("X-Requested-By" to mainUrl)
|
||||
val document = app.get(mainUrl, headers = headers).document
|
||||
val mainbody = document.getElementsByTag("body")
|
||||
|
|
|
@ -30,7 +30,7 @@ class DramaidProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -41,7 +41,7 @@ class EgyBestProvider : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
// url, title
|
||||
val doc = app.get(mainUrl).document
|
||||
val pages = arrayListOf<HomePageList>()
|
||||
|
|
|
@ -15,7 +15,7 @@ class ElifilmsProvider : MainAPI() {
|
|||
TvType.Movie,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val newest = app.get(mainUrl).document.selectFirst("a.fav_link.premiera")?.attr("href")
|
||||
val urls = listOf(
|
||||
|
|
|
@ -18,7 +18,7 @@ class EntrepeliculasyseriesProvider:MainAPI() {
|
|||
)
|
||||
override val vpnStatus = VPNStatus.MightBeNeeded //Due to evoload sometimes not loading
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/series/", "Series"),
|
||||
|
|
|
@ -28,7 +28,7 @@ class EstrenosDoramasProvider : MainAPI() {
|
|||
TvType.AsianDrama,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair(mainUrl, "Últimas series"),
|
||||
Pair("$mainUrl/category/peliculas", "Películas"),
|
||||
|
|
|
@ -35,7 +35,7 @@ class FaselHDProvider : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
// Title, Url
|
||||
val moviesUrl = listOf(
|
||||
Pair("Movies", "$mainUrl/all-movies/page/"+(0..10).random()),
|
||||
|
|
|
@ -18,7 +18,7 @@ class FilmanProvider : MainAPI() {
|
|||
TvType.TvSeries
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
val lists = document.select("#item-list,#series-list")
|
||||
val categories = ArrayList<HomePageList>()
|
||||
|
|
|
@ -23,7 +23,7 @@ class FilmpertuttiProvider : MainAPI() {
|
|||
TvType.TvSeries
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/category/serie-tv/", "Serie Tv"),
|
||||
|
|
|
@ -232,7 +232,7 @@ class FrenchStreamProvider : MainAPI() {
|
|||
}
|
||||
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse? {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse? {
|
||||
val document = app.get(mainUrl).document
|
||||
val docs = document.select("div.sect")
|
||||
val returnList = docs.mapNotNull {
|
||||
|
|
|
@ -69,7 +69,7 @@ class HDMProvider : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val html = app.get(mainUrl, timeout = 25).text
|
||||
val document = Jsoup.parse(html)
|
||||
val all = ArrayList<HomePageList>()
|
||||
|
|
|
@ -19,7 +19,7 @@ class HDMovie5 : MainAPI() {
|
|||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val doc = app.get(mainUrl).document.select("div.content")
|
||||
val list = mapOf(
|
||||
"Featured Movies" to "featured",
|
||||
|
|
|
@ -28,7 +28,7 @@ class HDrezkaProvider : MainAPI() {
|
|||
TvType.AsianDrama
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
|
||||
val items = ArrayList<HomePageList>()
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ class IHaveNoTvProvider : MainAPI() {
|
|||
|
||||
override val supportedTypes = setOf(TvType.Documentary)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
// Uhh, I am too lazy to scrape the "latest documentaries" and "recommended documentaries",
|
||||
// so I am just scraping 3 random categories
|
||||
val allCategories = listOf(
|
||||
|
|
|
@ -22,7 +22,7 @@ class IdlixProvider : MainAPI() {
|
|||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -26,7 +26,7 @@ class KdramaHoodProvider : MainAPI() {
|
|||
@JsonProperty("file") val file: String
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val doc = app.get("$mainUrl/home2").document
|
||||
val home = ArrayList<HomePageList>()
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ class LayarKacaProvider : MainAPI() {
|
|||
TvType.AsianDrama
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -22,7 +22,7 @@ class MultiplexProvider : MainAPI() {
|
|||
TvType.AsianDrama
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -44,7 +44,7 @@ class MyCimaProvider : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
// Title, Url
|
||||
val moviesUrl = listOf(
|
||||
"Movies" to "$mainUrl/movies/page/" + (0..25).random(),
|
||||
|
|
|
@ -212,7 +212,7 @@ class NginxProvider : MainAPI() {
|
|||
}
|
||||
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val authHeader =
|
||||
getAuthHeader() // call again because it isn't reloaded if in main class and storedCredentials loads after
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class PeliSmartProvider: MainAPI() {
|
|||
)
|
||||
override val vpnStatus = VPNStatus.MightBeNeeded //Due to evoload sometimes not loading
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/peliculas/", "Peliculas"),
|
||||
|
|
|
@ -18,7 +18,7 @@ class PelisflixProvider : MainAPI() {
|
|||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/ver-peliculas-online-gratis-fullhdc3/", "Películas"),
|
||||
|
|
|
@ -16,7 +16,7 @@ class PelisplusHDProvider:MainAPI() {
|
|||
TvType.Movie,
|
||||
TvType.TvSeries,
|
||||
)
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val document = app.get(mainUrl).document
|
||||
val map = mapOf(
|
||||
|
|
|
@ -142,7 +142,7 @@ open class PelisplusProviderTemplate : MainAPI() {
|
|||
|
||||
// This loads the homepage, which is basically a collection of search results with labels.
|
||||
// Optional function, but make sure to enable hasMainPage if you program this.
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = homePageUrlList
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
// .pmap {} is used to fetch the different pages in parallel
|
||||
|
|
|
@ -23,7 +23,7 @@ class PhimmoichillProvider : MainAPI() {
|
|||
TvType.AsianDrama
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -15,7 +15,7 @@ class PinoyHDXyzProvider : MainAPI() {
|
|||
override val hasMainPage = true
|
||||
override val hasQuickSearch = false
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val all = ArrayList<HomePageList>()
|
||||
val document = app.get(mainUrl, referer = mainUrl).document
|
||||
val mainbody = document.getElementsByTag("body")
|
||||
|
|
|
@ -16,7 +16,7 @@ class PinoyMoviePediaProvider : MainAPI() {
|
|||
override val hasMainPage = true
|
||||
override val hasQuickSearch = false
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val all = ArrayList<HomePageList>()
|
||||
val document = app.get(mainUrl).document
|
||||
val mainbody = document.getElementsByTag("body")
|
||||
|
|
|
@ -90,7 +90,7 @@ class PinoyMoviesEsProvider : MainAPI() {
|
|||
return all
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val all = ArrayList<HomePageList>()
|
||||
val document = app.get(mainUrl).document
|
||||
val mainbody = document.getElementsByTag("body")
|
||||
|
|
|
@ -27,7 +27,7 @@ class RebahinProvider : MainAPI() {
|
|||
TvType.AsianDrama
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair("Featured", "xtab1"),
|
||||
Pair("Film Terbaru", "xtab2"),
|
||||
|
|
|
@ -18,7 +18,7 @@ class SeriesflixProvider : MainAPI() {
|
|||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/ver-series-online/", "Series"),
|
||||
|
|
|
@ -43,7 +43,7 @@ open class SflixProvider : MainAPI() {
|
|||
)
|
||||
override val vpnStatus = VPNStatus.None
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val html = app.get("$mainUrl/home").text
|
||||
val document = Jsoup.parse(html)
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class SoaptwoDayProvider : MainAPI() {
|
|||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/movielist/", "Movies"),
|
||||
|
|
|
@ -174,7 +174,7 @@ class StreamingcommunityProvider : MainAPI() {
|
|||
val posterMap = hashMapOf<String, String>()
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val document = app.get(mainUrl).document
|
||||
document.select("slider-title").subList(0, 6).map { it ->
|
||||
|
|
|
@ -18,7 +18,7 @@ class TantifilmProvider : MainAPI() {
|
|||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/watch-genre/serie-tv/", "Serie Tv"),
|
||||
|
|
|
@ -143,7 +143,7 @@ class TheFlixToProvider : MainAPI() {
|
|||
}
|
||||
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val doc = app.get(mainUrl).document
|
||||
val scriptText = doc.selectFirst("script[type=application/json]")!!.data()
|
||||
|
|
|
@ -23,7 +23,7 @@ class UakinoProvider : MainAPI() {
|
|||
TvType.Anime
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -210,7 +210,7 @@ open class VidstreamProviderTemplate : MainAPI() {
|
|||
|
||||
// This loads the homepage, which is basically a collection of search results with labels.
|
||||
// Optional function, but make sure to enable hasMainPage if you program this.
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val urls = homePageUrlList
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
// .pmap {} is used to fetch the different pages in parallel
|
||||
|
|
|
@ -18,7 +18,7 @@ class WatchAsianProvider : MainAPI() {
|
|||
override val hasDownloadSupport = true
|
||||
override val supportedTypes = setOf(TvType.AsianDrama)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val headers = mapOf("X-Requested-By" to mainUrl)
|
||||
val doc = app.get(mainUrl, headers = headers).document
|
||||
val rowPair = mutableListOf<Pair<String, String>>()
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.lagradost.cloudstream3.movieproviders
|
|||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import org.jsoup.nodes.Element
|
||||
|
@ -41,7 +40,7 @@ class XcineProvider : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
val sections = document.select("div.group-film")
|
||||
return HomePageResponse(sections.mapNotNull { section ->
|
||||
|
|
|
@ -19,7 +19,7 @@ class YomoviesProvider : MainAPI() {
|
|||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(): HomePageResponse {
|
||||
override suspend fun getMainPage(page: Int, categoryName: String, categoryData: String): HomePageResponse {
|
||||
val document = app.get(mainUrl).document
|
||||
|
||||
val homePageList = ArrayList<HomePageList>()
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.util.Log
|
|||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.bumptech.glide.load.HttpException
|
||||
import com.lagradost.cloudstream3.BuildConfig
|
||||
import com.lagradost.cloudstream3.ErrorLoadingException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -11,6 +12,34 @@ import java.net.SocketTimeoutException
|
|||
import java.net.UnknownHostException
|
||||
import javax.net.ssl.SSLHandshakeException
|
||||
|
||||
const val DEBUG_EXCEPTION = "THIS IS A DEBUG EXCEPTION!"
|
||||
|
||||
class DebugException(message: String) : Exception("$DEBUG_EXCEPTION\n$message")
|
||||
|
||||
inline fun debugException(message: () -> String) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
throw DebugException(message.invoke())
|
||||
}
|
||||
}
|
||||
|
||||
inline fun debugWarning(message: () -> String) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
logError(DebugException(message.invoke()))
|
||||
}
|
||||
}
|
||||
|
||||
inline fun debugAssert(assert: () -> Boolean, message: () -> String) {
|
||||
if (BuildConfig.DEBUG && assert.invoke()) {
|
||||
throw DebugException(message.invoke())
|
||||
}
|
||||
}
|
||||
|
||||
inline fun debugWarning(assert: () -> Boolean, message: () -> String) {
|
||||
if (BuildConfig.DEBUG && assert.invoke()) {
|
||||
logError(DebugException(message.invoke()))
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) {
|
||||
liveData.observe(this) { it?.let { t -> action(t) } }
|
||||
}
|
||||
|
@ -61,11 +90,12 @@ suspend fun <T> suspendSafeApiCall(apiCall: suspend () -> T): T? {
|
|||
}
|
||||
|
||||
fun <T> safeFail(throwable: Throwable): Resource<T> {
|
||||
val stackTraceMsg = (throwable.localizedMessage ?: "") + "\n\n" + throwable.stackTrace.joinToString(
|
||||
separator = "\n"
|
||||
) {
|
||||
"${it.fileName} ${it.lineNumber}"
|
||||
}
|
||||
val stackTraceMsg =
|
||||
(throwable.localizedMessage ?: "") + "\n\n" + throwable.stackTrace.joinToString(
|
||||
separator = "\n"
|
||||
) {
|
||||
"${it.fileName} ${it.lineNumber}"
|
||||
}
|
||||
return Resource.Failure(false, null, null, stackTraceMsg)
|
||||
}
|
||||
|
||||
|
@ -92,16 +122,31 @@ suspend fun <T> safeApiCall(
|
|||
safeFail(throwable)
|
||||
}
|
||||
is SocketTimeoutException -> {
|
||||
Resource.Failure(true, null, null, "Connection Timeout\nPlease try again later.")
|
||||
Resource.Failure(
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
"Connection Timeout\nPlease try again later."
|
||||
)
|
||||
}
|
||||
is HttpException -> {
|
||||
Resource.Failure(false, throwable.statusCode, null, throwable.message ?: "HttpException")
|
||||
Resource.Failure(
|
||||
false,
|
||||
throwable.statusCode,
|
||||
null,
|
||||
throwable.message ?: "HttpException"
|
||||
)
|
||||
}
|
||||
is UnknownHostException -> {
|
||||
Resource.Failure(true, null, null, "Cannot connect to server, try again later.")
|
||||
}
|
||||
is ErrorLoadingException -> {
|
||||
Resource.Failure(true, null, null, throwable.message ?: "Error loading, try again later.")
|
||||
Resource.Failure(
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
throwable.message ?: "Error loading, try again later."
|
||||
)
|
||||
}
|
||||
is NotImplementedError -> {
|
||||
Resource.Failure(false, null, null, "This operation is not implemented.")
|
||||
|
|
|
@ -29,6 +29,7 @@ class APIRepository(val api: MainAPI) {
|
|||
val hasMainPage = api.hasMainPage
|
||||
val name = api.name
|
||||
val mainUrl = api.mainUrl
|
||||
val mainPage = api.mainPage
|
||||
val hasQuickSearch = api.hasQuickSearch
|
||||
|
||||
suspend fun load(url: String): Resource<LoadResponse> {
|
||||
|
@ -59,9 +60,13 @@ class APIRepository(val api: MainAPI) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getMainPage(): Resource<HomePageResponse?> {
|
||||
suspend fun getMainPage(page: Int, nameIndex: Int? = null): Resource<List<HomePageResponse?>> {
|
||||
return safeApiCall {
|
||||
api.getMainPage() ?: throw ErrorLoadingException()
|
||||
nameIndex?.let { api.mainPage.getOrNull(it) }?.let { data ->
|
||||
listOf(api.getMainPage(page, data.name, data.data))
|
||||
} ?: api.mainPage.apmap { data ->
|
||||
api.getMainPage(page, data.name, data.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ class HomeChildItemAdapter(
|
|||
) :
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
var isHorizontal: Boolean = false
|
||||
var hasNext : Boolean = false
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val layout = overrideLayout
|
||||
|
|
|
@ -20,7 +20,6 @@ import androidx.core.widget.NestedScrollView
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.LinearSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -44,11 +43,12 @@ import com.lagradost.cloudstream3.ui.WatchType
|
|||
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
|
||||
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
|
||||
import com.lagradost.cloudstream3.ui.search.*
|
||||
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
|
||||
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.setMaxViewPoolSize
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||
|
@ -122,11 +122,27 @@ class HomeFragment : Fragment() {
|
|||
|
||||
val errorProfilePic = errorProfilePics.random()
|
||||
|
||||
fun Activity.loadHomepageList(item: HomePageList, deleteCallback: (() -> Unit)? = null) {
|
||||
fun Activity.loadHomepageList(
|
||||
item: HomePageList,
|
||||
deleteCallback: (() -> Unit)? = null,
|
||||
) {
|
||||
loadHomepageList(
|
||||
expand = HomeViewModel.ExpandableHomepageList(item, 1, false),
|
||||
deleteCallback = deleteCallback,
|
||||
expandCallback = null
|
||||
)
|
||||
}
|
||||
|
||||
fun Activity.loadHomepageList(
|
||||
expand: HomeViewModel.ExpandableHomepageList,
|
||||
deleteCallback: (() -> Unit)? = null,
|
||||
expandCallback: (suspend (String) -> HomeViewModel.ExpandableHomepageList?)? = null
|
||||
) {
|
||||
val context = this
|
||||
val bottomSheetDialogBuilder = BottomSheetDialog(context)
|
||||
bottomSheetDialogBuilder.setContentView(R.layout.home_episodes_expanded)
|
||||
val title = bottomSheetDialogBuilder.findViewById<TextView>(R.id.home_expanded_text)!!
|
||||
val item = expand.list
|
||||
title.text = item.name
|
||||
val recycle =
|
||||
bottomSheetDialogBuilder.findViewById<AutofitRecyclerView>(R.id.home_expanded_recycler)!!
|
||||
|
@ -179,8 +195,36 @@ class HomeFragment : Fragment() {
|
|||
if (callback.action == SEARCH_ACTION_LOAD || callback.action == SEARCH_ACTION_PLAY_FILE) {
|
||||
bottomSheetDialogBuilder.dismissSafe(this)
|
||||
}
|
||||
}.apply {
|
||||
hasNext = expand.hasNext
|
||||
}
|
||||
|
||||
recycle.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
var expandCount = 0
|
||||
val name = expand.list.name
|
||||
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
|
||||
val adapter = recyclerView.adapter
|
||||
if (adapter !is SearchAdapter) return
|
||||
|
||||
val count = adapter.itemCount
|
||||
val currentHasNext = adapter.hasNext
|
||||
if (!recyclerView.canScrollVertically(1) && currentHasNext && expandCount != count) {
|
||||
expandCount = count
|
||||
ioSafe {
|
||||
expandCallback?.invoke(name)?.let { newExpand ->
|
||||
(recyclerView.adapter as? SearchAdapter?)?.apply {
|
||||
hasNext = newExpand.hasNext
|
||||
updateList(newExpand.list.list)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
val spanListener = { span: Int ->
|
||||
recycle.spanCount = span
|
||||
//(recycle.adapter as SearchAdapter).notifyDataSetChanged()
|
||||
|
@ -349,8 +393,6 @@ class HomeFragment : Fragment() {
|
|||
return inflater.inflate(layout, container, false)
|
||||
}
|
||||
|
||||
private var currentHomePage: HomePageResponse? = null
|
||||
|
||||
private fun toggleMainVisibility(visible: Boolean) {
|
||||
home_main_holder?.isVisible = visible
|
||||
home_main_poster_recyclerview?.isVisible = visible
|
||||
|
@ -541,18 +583,11 @@ class HomeFragment : Fragment() {
|
|||
val d = data.value
|
||||
listHomepageItems.clear()
|
||||
|
||||
currentHomePage = d
|
||||
// println("ITEMCOUNT: ${d.values.size} ${home_master_recycler?.adapter?.itemCount}")
|
||||
(home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList(
|
||||
d?.items?.mapNotNull {
|
||||
try {
|
||||
val filter = it.list.filterSearchResponse()
|
||||
listHomepageItems.addAll(filter)
|
||||
it.copy(list = filter)
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
null
|
||||
}
|
||||
} ?: listOf())
|
||||
d.values.toMutableList(),
|
||||
home_master_recycler
|
||||
)
|
||||
|
||||
home_loading?.isVisible = false
|
||||
home_loading_error?.isVisible = false
|
||||
|
@ -603,13 +638,6 @@ class HomeFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
|
||||
ParentItemAdapter(mutableListOf(), { callback ->
|
||||
homeHandleSearch(callback)
|
||||
}, { item ->
|
||||
activity?.loadHomepageList(item)
|
||||
})
|
||||
|
||||
val toggleList = listOf(
|
||||
Pair(home_type_watching_btt, WatchType.WATCHING),
|
||||
Pair(home_type_completed_btt, WatchType.COMPLETED),
|
||||
|
@ -861,9 +889,22 @@ class HomeFragment : Fragment() {
|
|||
context?.fixPaddingStatusbarView(home_statusbar)
|
||||
context?.fixPaddingStatusbar(home_loading_statusbar)
|
||||
|
||||
|
||||
home_master_recycler.adapter = adapter
|
||||
home_master_recycler.layoutManager = GridLayoutManager(context, 1)
|
||||
home_master_recycler.adapter =
|
||||
ParentItemAdapter(mutableListOf(), { callback ->
|
||||
homeHandleSearch(callback)
|
||||
}, { item ->
|
||||
activity?.loadHomepageList(item, expandCallback = {
|
||||
homeViewModel.expandAndReturn(it)
|
||||
})
|
||||
}, { name ->
|
||||
homeViewModel.expand(name)
|
||||
})
|
||||
home_master_recycler?.setMaxViewPoolSize(0, Int.MAX_VALUE)
|
||||
home_master_recycler.layoutManager = object : LinearLayoutManager(context) {
|
||||
override fun supportsPredictiveItemAnimations(): Boolean {
|
||||
return false
|
||||
}
|
||||
} // GridLayoutManager(context, 1).also { it.supportsPredictiveItemAnimations() }
|
||||
|
||||
if (context?.isTvSettings() == false) {
|
||||
LinearSnapHelper().attachToRecyclerView(home_main_poster_recyclerview) // snap
|
||||
|
|
|
@ -6,29 +6,38 @@ import android.view.ViewGroup
|
|||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListUpdateCallback
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lagradost.cloudstream3.HomePageList
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||
import kotlinx.android.synthetic.main.homepage_parent.view.*
|
||||
|
||||
|
||||
class ParentItemAdapter(
|
||||
private var items: MutableList<HomePageList>,
|
||||
private var items: MutableList<HomeViewModel.ExpandableHomepageList>,
|
||||
private val clickCallback: (SearchClickCallback) -> Unit,
|
||||
private val moreInfoClickCallback: (HomePageList) -> Unit,
|
||||
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit,
|
||||
private val expandCallback: ((String) -> Unit)? = null,
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, i: Int): ParentViewHolder {
|
||||
//println("onCreateViewHolder $i")
|
||||
val layout =
|
||||
if (parent.context.isTvSettings()) R.layout.homepage_parent_tv else R.layout.homepage_parent
|
||||
return ParentViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(layout, parent, false),
|
||||
clickCallback,
|
||||
moreInfoClickCallback
|
||||
moreInfoClickCallback,
|
||||
expandCallback
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
//println("onBindViewHolder $position")
|
||||
|
||||
when (holder) {
|
||||
is ParentViewHolder -> {
|
||||
holder.bind(items[position])
|
||||
|
@ -41,44 +50,113 @@ class ParentItemAdapter(
|
|||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return items[position].name.hashCode().toLong()
|
||||
return items[position].list.name.hashCode().toLong()
|
||||
}
|
||||
|
||||
@JvmName("updateListHomePageList")
|
||||
fun updateList(newList: List<HomePageList>) {
|
||||
// this moves all bad results to the bottom
|
||||
val endList = mutableListOf<HomePageList>()
|
||||
val newFilteredList = mutableListOf<HomePageList>()
|
||||
for (item in newList) {
|
||||
if (item.list.isEmpty()) {
|
||||
endList.add(item)
|
||||
} else {
|
||||
newFilteredList.add(item)
|
||||
}
|
||||
}
|
||||
newFilteredList.addAll(endList)
|
||||
updateList(newList.map { HomeViewModel.ExpandableHomepageList(it, 1, false) }
|
||||
.toMutableList())
|
||||
}
|
||||
|
||||
@JvmName("updateListExpandableHomepageList")
|
||||
fun updateList(
|
||||
newList: MutableList<HomeViewModel.ExpandableHomepageList>,
|
||||
recyclerView: RecyclerView? = null
|
||||
) {
|
||||
// this
|
||||
// 1. prevents deep copy that makes this.items == newList
|
||||
// 2. filters out undesirable results
|
||||
// 3. moves empty results to the bottom (sortedBy is a stable sort)
|
||||
val new =
|
||||
newList.map { it.copy(list = it.list.copy(list = it.list.list.filterSearchResponse())) }
|
||||
.sortedBy { it.list.list.isEmpty() }
|
||||
|
||||
val diffResult = DiffUtil.calculateDiff(
|
||||
SearchDiffCallback(this.items, newFilteredList)
|
||||
SearchDiffCallback(items, new)
|
||||
)
|
||||
|
||||
items.clear()
|
||||
items.addAll(newFilteredList)
|
||||
items.addAll(new)
|
||||
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
val mAdapter = this
|
||||
diffResult.dispatchUpdatesTo(object : ListUpdateCallback {
|
||||
override fun onInserted(position: Int, count: Int) {
|
||||
mAdapter.notifyItemRangeInserted(position, count)
|
||||
}
|
||||
|
||||
override fun onRemoved(position: Int, count: Int) {
|
||||
mAdapter.notifyItemRangeRemoved(position, count)
|
||||
}
|
||||
|
||||
override fun onMoved(fromPosition: Int, toPosition: Int) {
|
||||
mAdapter.notifyItemMoved(fromPosition, toPosition)
|
||||
}
|
||||
|
||||
override fun onChanged(position: Int, count: Int, payload: Any?) {
|
||||
// I know kinda messy, what this does is using the update or bind instead of onCreateViewHolder -> bind
|
||||
recyclerView?.apply {
|
||||
// this loops every viewHolder in the recycle view and checks the position to see if it is within the update range
|
||||
val missingUpdates = (position until (position + count)).toMutableSet()
|
||||
for (i in 0 until mAdapter.itemCount) {
|
||||
val viewHolder = getChildViewHolder(getChildAt(i))
|
||||
val absolutePosition = viewHolder.absoluteAdapterPosition
|
||||
if (absolutePosition >= position && absolutePosition < position + count) {
|
||||
val expand = items.getOrNull(absolutePosition) ?: continue
|
||||
if (viewHolder is ParentViewHolder) {
|
||||
missingUpdates -= absolutePosition
|
||||
if (viewHolder.title.text == expand.list.name) {
|
||||
viewHolder.update(expand)
|
||||
} else {
|
||||
viewHolder.bind(expand)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// just in case some item did not get updated
|
||||
for (i in missingUpdates) {
|
||||
mAdapter.notifyItemChanged(i, payload)
|
||||
}
|
||||
} ?: run { // in case we don't have a nice
|
||||
mAdapter.notifyItemRangeChanged(position, count, payload)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
class ParentViewHolder
|
||||
constructor(
|
||||
itemView: View,
|
||||
private val clickCallback: (SearchClickCallback) -> Unit,
|
||||
private val moreInfoClickCallback: (HomePageList) -> Unit
|
||||
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit,
|
||||
private val expandCallback: ((String) -> Unit)? = null,
|
||||
) :
|
||||
RecyclerView.ViewHolder(itemView) {
|
||||
val title: TextView = itemView.home_parent_item_title
|
||||
val recyclerView: RecyclerView = itemView.home_child_recyclerview
|
||||
private val moreInfo: FrameLayout? = itemView.home_child_more_info
|
||||
fun bind(info: HomePageList) {
|
||||
title.text = info.name
|
||||
|
||||
fun update(expand: HomeViewModel.ExpandableHomepageList) {
|
||||
val info = expand.list
|
||||
(recyclerView.adapter as? HomeChildItemAdapter?)?.apply {
|
||||
updateList(info.list.toMutableList())
|
||||
hasNext = expand.hasNext
|
||||
} ?: run {
|
||||
recyclerView.adapter = HomeChildItemAdapter(
|
||||
info.list.toMutableList(),
|
||||
clickCallback = clickCallback,
|
||||
nextFocusUp = recyclerView.nextFocusUpId,
|
||||
nextFocusDown = recyclerView.nextFocusDownId,
|
||||
).apply {
|
||||
isHorizontal = info.isHorizontalImages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(expand: HomeViewModel.ExpandableHomepageList) {
|
||||
val info = expand.list
|
||||
recyclerView.adapter = HomeChildItemAdapter(
|
||||
info.list.toMutableList(),
|
||||
clickCallback = clickCallback,
|
||||
|
@ -86,29 +164,56 @@ class ParentItemAdapter(
|
|||
nextFocusDown = recyclerView.nextFocusDownId,
|
||||
).apply {
|
||||
isHorizontal = info.isHorizontalImages
|
||||
hasNext = expand.hasNext
|
||||
}
|
||||
|
||||
title.text = info.name
|
||||
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
var expandCount = 0
|
||||
val name = expand.list.name
|
||||
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
|
||||
val adapter = recyclerView.adapter
|
||||
if (adapter !is HomeChildItemAdapter) return
|
||||
|
||||
val count = adapter.itemCount
|
||||
val hasNext = adapter.hasNext
|
||||
if (!recyclerView.canScrollHorizontally(1) && hasNext && expandCount != count) {
|
||||
expandCount = count
|
||||
expandCallback?.invoke(name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//(recyclerView.adapter as HomeChildItemAdapter).notifyDataSetChanged()
|
||||
|
||||
moreInfo?.setOnClickListener {
|
||||
moreInfoClickCallback.invoke(info)
|
||||
moreInfoClickCallback.invoke(expand)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SearchDiffCallback(
|
||||
private val oldList: List<HomePageList>,
|
||||
private val newList: List<HomePageList>
|
||||
private val oldList: List<HomeViewModel.ExpandableHomepageList>,
|
||||
private val newList: List<HomeViewModel.ExpandableHomepageList>
|
||||
) :
|
||||
DiffUtil.Callback() {
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||
oldList[oldItemPosition].name == newList[newItemPosition].name
|
||||
oldList[oldItemPosition].list.name == newList[newItemPosition].list.name
|
||||
|
||||
override fun getOldListSize() = oldList.size
|
||||
|
||||
override fun getNewListSize() = newList.size
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
|
||||
oldList[oldItemPosition] == newList[newItemPosition]
|
||||
//{
|
||||
// val ret = oldList[oldItemPosition].list.list.size == newList[newItemPosition].list.list.size
|
||||
// println(">>>>>>>>>>>>>>>> $ret ${oldList[oldItemPosition].list.list.size} == ${newList[newItemPosition].list.list.size}")
|
||||
// return ret
|
||||
//}
|
||||
}
|
|
@ -10,10 +10,12 @@ import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
|||
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||
import com.lagradost.cloudstream3.HomePageResponse
|
||||
import com.lagradost.cloudstream3.HomePageList
|
||||
import com.lagradost.cloudstream3.MainAPI
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.debugAssert
|
||||
import com.lagradost.cloudstream3.mvvm.debugWarning
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.ui.APIRepository
|
||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
|
||||
|
@ -34,6 +36,7 @@ import kotlinx.coroutines.Job
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
import kotlin.collections.set
|
||||
|
||||
class HomeViewModel : ViewModel() {
|
||||
private var repo: APIRepository? = null
|
||||
|
@ -41,9 +44,6 @@ class HomeViewModel : ViewModel() {
|
|||
private val _apiName = MutableLiveData<String>()
|
||||
val apiName: LiveData<String> = _apiName
|
||||
|
||||
private val _page = MutableLiveData<Resource<HomePageResponse?>>()
|
||||
val page: LiveData<Resource<HomePageResponse?>> = _page
|
||||
|
||||
private val _randomItems = MutableLiveData<List<SearchResponse>?>(null)
|
||||
val randomItems: LiveData<List<SearchResponse>?> = _randomItems
|
||||
|
||||
|
@ -51,8 +51,10 @@ class HomeViewModel : ViewModel() {
|
|||
return APIRepository(apis.first { it.hasMainPage })
|
||||
}
|
||||
|
||||
private val _availableWatchStatusTypes = MutableLiveData<Pair<EnumSet<WatchType>, EnumSet<WatchType>>>()
|
||||
val availableWatchStatusTypes: LiveData<Pair<EnumSet<WatchType>, EnumSet<WatchType>>> = _availableWatchStatusTypes
|
||||
private val _availableWatchStatusTypes =
|
||||
MutableLiveData<Pair<EnumSet<WatchType>, EnumSet<WatchType>>>()
|
||||
val availableWatchStatusTypes: LiveData<Pair<EnumSet<WatchType>, EnumSet<WatchType>>> =
|
||||
_availableWatchStatusTypes
|
||||
private val _bookmarks = MutableLiveData<Pair<Boolean, List<SearchResponse>>>()
|
||||
val bookmarks: LiveData<Pair<Boolean, List<SearchResponse>>> = _bookmarks
|
||||
|
||||
|
@ -143,6 +145,67 @@ class HomeViewModel : ViewModel() {
|
|||
onGoingLoad = load(api)
|
||||
}
|
||||
|
||||
data class ExpandableHomepageList(
|
||||
var list: HomePageList,
|
||||
var currentPage: Int,
|
||||
var hasNext: Boolean,
|
||||
)
|
||||
|
||||
private val expandable: MutableMap<String, ExpandableHomepageList> = mutableMapOf()
|
||||
private val _page =
|
||||
MutableLiveData<Resource<Map<String, ExpandableHomepageList>>>(Resource.Loading())
|
||||
val page: LiveData<Resource<Map<String, ExpandableHomepageList>>> = _page
|
||||
|
||||
val lock: MutableSet<String> = mutableSetOf()
|
||||
|
||||
suspend fun expandAndReturn(name: String) : ExpandableHomepageList? {
|
||||
if (lock.contains(name)) return null
|
||||
lock += name
|
||||
|
||||
repo?.apply {
|
||||
expandable[name]?.let { current ->
|
||||
debugAssert({ !current.hasNext }) {
|
||||
"Expand called when not needed"
|
||||
}
|
||||
|
||||
val nextPage = current.currentPage + 1
|
||||
val next = getMainPage(nextPage, mainPage.indexOfFirst { it.name == name })
|
||||
if (next is Resource.Success) {
|
||||
next.value.filterNotNull().forEach { main ->
|
||||
main.items.forEach { newList ->
|
||||
val key = newList.name
|
||||
expandable[key]?.apply {
|
||||
hasNext = main.hasNext
|
||||
currentPage = nextPage
|
||||
|
||||
debugWarning({ newList.list.any { outer -> this.list.list.any { it.url == outer.url } } }) {
|
||||
"Expanded contained an item that was previously already in the list\n${list.name} = ${this.list.list}\n${newList.name} = ${newList.list}"
|
||||
}
|
||||
|
||||
this.list.list += newList.list
|
||||
this.list.list.distinctBy { it.url } // just to be sure we are not adding the same shit for some reason
|
||||
} ?: debugWarning {
|
||||
"Expanded an item not in main load named $key, current list is ${expandable.keys}"
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current.hasNext = false
|
||||
}
|
||||
}
|
||||
_page.postValue(Resource.Success(expandable))
|
||||
}
|
||||
|
||||
lock -= name
|
||||
|
||||
return expandable[name]
|
||||
}
|
||||
|
||||
// this is soo over engineered, but idk how I can make it clean without making the main api harder to use :pensive:
|
||||
fun expand(name: String) = viewModelScope.launch {
|
||||
expandAndReturn(name)
|
||||
}
|
||||
|
||||
private fun load(api: MainAPI?) = viewModelScope.launch {
|
||||
repo = if (api != null) {
|
||||
APIRepository(api)
|
||||
|
@ -156,35 +219,43 @@ class HomeViewModel : ViewModel() {
|
|||
if (repo?.hasMainPage == true) {
|
||||
_page.postValue(Resource.Loading())
|
||||
|
||||
val data = repo?.getMainPage()
|
||||
when (data) {
|
||||
when (val data = repo?.getMainPage(1, null)) {
|
||||
is Resource.Success -> {
|
||||
try {
|
||||
val home = data.value
|
||||
if (home?.items?.isNullOrEmpty() == false) {
|
||||
expandable.clear()
|
||||
data.value.forEach { home ->
|
||||
home?.items?.forEach { list ->
|
||||
expandable[list.name] =
|
||||
ExpandableHomepageList(list, 1, home.hasNext)
|
||||
}
|
||||
}
|
||||
_page.postValue(Resource.Success(expandable))
|
||||
val items = data.value.mapNotNull { it?.items }.flatten()
|
||||
|
||||
//val home = data.value
|
||||
if (items.isNotEmpty()) {
|
||||
val currentList =
|
||||
home.items.shuffled().filter { !it.list.isNullOrEmpty() }.flatMap { it.list }
|
||||
items.shuffled().filter { it.list.isNotEmpty() }
|
||||
.flatMap { it.list }
|
||||
.distinctBy { it.url }
|
||||
.toList()
|
||||
|
||||
if (!currentList.isNullOrEmpty()) {
|
||||
if (currentList.isNotEmpty()) {
|
||||
val randomItems = currentList.shuffled()
|
||||
|
||||
_randomItems.postValue(randomItems)
|
||||
}
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
} catch (e: Exception) {
|
||||
_randomItems.postValue(emptyList())
|
||||
logError(e)
|
||||
}
|
||||
}
|
||||
is Resource.Failure -> {
|
||||
_page.postValue(data!!)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
data?.let {
|
||||
_page.postValue(it)
|
||||
}
|
||||
} else {
|
||||
_page.postValue(Resource.Success(HomePageResponse(emptyList())))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,7 +265,7 @@ class HomeViewModel : ViewModel() {
|
|||
loadAndCancel(noneApi)
|
||||
else if (preferredApiName == randomApi.name || api == null) {
|
||||
val validAPIs = context?.filterProviderByPreferredMedia()
|
||||
if(validAPIs.isNullOrEmpty()) {
|
||||
if (validAPIs.isNullOrEmpty()) {
|
||||
loadAndCancel(noneApi)
|
||||
} else {
|
||||
val apiRandom = validAPIs.random()
|
||||
|
|
|
@ -27,7 +27,6 @@ import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageLis
|
|||
import com.lagradost.cloudstream3.ui.home.ParentItemAdapter
|
||||
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
|
||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||
import com.lagradost.cloudstream3.ui.search.SearchViewModel
|
||||
import com.lagradost.cloudstream3.utils.UIHelper
|
||||
|
@ -173,7 +172,7 @@ class QuickSearchFragment : Fragment() {
|
|||
updateList(list.map { ongoing ->
|
||||
val ongoingList = HomePageList(
|
||||
ongoing.apiName,
|
||||
if (ongoing.data is Resource.Success) ongoing.data.value.filterSearchResponse() else ArrayList()
|
||||
if (ongoing.data is Resource.Success) ongoing.data.value else ArrayList()
|
||||
)
|
||||
ongoingList
|
||||
})
|
||||
|
|
|
@ -27,6 +27,7 @@ class SearchAdapter(
|
|||
private val resView: AutofitRecyclerView,
|
||||
private val clickCallback: (SearchClickCallback) -> Unit,
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
var hasNext : Boolean = false
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val layout = if(parent.context.IsBottomLayout()) R.layout.search_result_grid_expanded else R.layout.search_result_grid
|
||||
|
|
|
@ -426,7 +426,7 @@ class SearchFragment : Fragment() {
|
|||
val newItems = list.map { ongoing ->
|
||||
val ongoingList = HomePageList(
|
||||
ongoing.apiName,
|
||||
if (ongoing.data is Resource.Success) ongoing.data.value.filterSearchResponse() else ArrayList()
|
||||
if (ongoing.data is Resource.Success) ongoing.data.value else ArrayList()
|
||||
)
|
||||
ongoingList
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ class SearchViewModel : ViewModel() {
|
|||
_currentSearch.postValue(emptyList())
|
||||
}
|
||||
|
||||
private var currentSearchIndex = 0
|
||||
private var onGoingSearch: Job? = null
|
||||
fun searchAndCancel(
|
||||
query: String,
|
||||
|
@ -50,6 +51,7 @@ class SearchViewModel : ViewModel() {
|
|||
ignoreSettings: Boolean = false,
|
||||
isQuickSearch: Boolean = false,
|
||||
) {
|
||||
currentSearchIndex++
|
||||
onGoingSearch?.cancel()
|
||||
onGoingSearch = search(query, providersActive, ignoreSettings, isQuickSearch)
|
||||
}
|
||||
|
@ -70,6 +72,7 @@ class SearchViewModel : ViewModel() {
|
|||
isQuickSearch: Boolean = false,
|
||||
) =
|
||||
viewModelScope.launch {
|
||||
val currentIndex = currentSearchIndex
|
||||
if (query.length <= 1) {
|
||||
clearSearch()
|
||||
return@launch
|
||||
|
@ -91,40 +94,44 @@ class SearchViewModel : ViewModel() {
|
|||
|
||||
_searchResponse.postValue(Resource.Loading())
|
||||
|
||||
val currentList = ArrayList<OnGoingSearch>()
|
||||
|
||||
_currentSearch.postValue(ArrayList())
|
||||
|
||||
withContext(Dispatchers.IO) { // This interrupts UI otherwise
|
||||
val currentList = ArrayList<OnGoingSearch>()
|
||||
|
||||
repos.filter { a ->
|
||||
(ignoreSettings || (providersActive.isEmpty() || providersActive.contains(a.name))) && (!isQuickSearch || a.hasQuickSearch)
|
||||
}.apmap { a -> // Parallel
|
||||
val search = if (isQuickSearch) a.quickSearch(query) else a.search(query)
|
||||
if(currentSearchIndex != currentIndex) return@apmap
|
||||
currentList.add(OnGoingSearch(a.name, search))
|
||||
_currentSearch.postValue(currentList)
|
||||
}
|
||||
}
|
||||
_currentSearch.postValue(currentList)
|
||||
|
||||
val list = ArrayList<SearchResponse>()
|
||||
val nestedList =
|
||||
currentList.map { it.data }
|
||||
.filterIsInstance<Resource.Success<List<SearchResponse>>>().map { it.value }
|
||||
if(currentSearchIndex != currentIndex) return@withContext // this should prevent rewrite of existing data bug
|
||||
|
||||
// I do it this way to move the relevant search results to the top
|
||||
var index = 0
|
||||
while (true) {
|
||||
var added = 0
|
||||
for (sublist in nestedList) {
|
||||
if (sublist.size > index) {
|
||||
list.add(sublist[index])
|
||||
added++
|
||||
_currentSearch.postValue(currentList)
|
||||
val list = ArrayList<SearchResponse>()
|
||||
val nestedList =
|
||||
currentList.map { it.data }
|
||||
.filterIsInstance<Resource.Success<List<SearchResponse>>>().map { it.value }
|
||||
|
||||
// I do it this way to move the relevant search results to the top
|
||||
var index = 0
|
||||
while (true) {
|
||||
var added = 0
|
||||
for (sublist in nestedList) {
|
||||
if (sublist.size > index) {
|
||||
list.add(sublist[index])
|
||||
added++
|
||||
}
|
||||
}
|
||||
if (added == 0) break
|
||||
index++
|
||||
}
|
||||
if (added == 0) break
|
||||
index++
|
||||
}
|
||||
|
||||
_searchResponse.postValue(Resource.Success(list))
|
||||
_searchResponse.postValue(Resource.Success(list))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.text.toSpanned
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.tvprovider.media.tv.PreviewChannelHelper
|
||||
import androidx.tvprovider.media.tv.TvContractCompat
|
||||
import androidx.tvprovider.media.tv.WatchNextProgram
|
||||
|
@ -52,6 +53,11 @@ import java.net.URL
|
|||
import java.net.URLDecoder
|
||||
|
||||
object AppUtils {
|
||||
fun RecyclerView.setMaxViewPoolSize(maxViewTypeId: Int, maxPoolSize: Int) {
|
||||
for (i in 0..maxViewTypeId)
|
||||
recycledViewPool.setMaxRecycledViews(i, maxPoolSize)
|
||||
}
|
||||
|
||||
//fun Context.deleteFavorite(data: SearchResponse) {
|
||||
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
// normalSafeApiCall {
|
||||
|
|
|
@ -367,8 +367,8 @@
|
|||
<string name="actor_background">Pomocniczy</string>
|
||||
|
||||
<string name="home_source">Źródło</string>
|
||||
|
||||
<string name="home_random">Losowy</string>
|
||||
|
||||
<string name="coming_soon">Już wkrótce…</string>
|
||||
<string name="quality_cam">Cam</string>
|
||||
<string name="quality_cam_rip">Cam</string>
|
||||
|
@ -391,4 +391,11 @@
|
|||
<string name="error">Błąd</string>
|
||||
<string name="subtitles_remove_captions">Usuń informacje dla niesłyszących z napisów</string>
|
||||
<string name="subtitles_remove_bloat">Usuń nadmiarowe informacje z napisów</string>
|
||||
<string name="next">Dalej</string>
|
||||
<string name="provider_languages_tip">Wyświetlaj filmy w wybranych językach</string>
|
||||
<string name="previous">Cofnij</string>
|
||||
<string name="skip_setup">Pomiń</string>
|
||||
<string name="app_layout_subtext">Dostosuj wygląd aplikacji do urządzenia</string>
|
||||
<string name="preferred_media_subtext">Preferowany rodzaj filmów</string>
|
||||
<string name="setup_done">Gotowe</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
|
||||
<resources>
|
||||
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
|
||||
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
|
||||
<string name="storage_size_format" translatable="false" formatted="true">%s • %sGB</string>
|
||||
<string name="download_size_format" translatable="false" formatted="true">%sMB / %sMB</string>
|
||||
<string name="mb_format" translatable="false" formatted="true">%dMB</string>
|
||||
<string name="episode_name_format" translatable="false" formatted="true">%s %s</string>
|
||||
<string name="ffw_text_format" translatable="false" formatted="true">+%d</string>
|
||||
<string name="rew_text_format" translatable="false" formatted="true">-%d</string>
|
||||
<string name="ffw_text_regular_format" translatable="false" formatted="true">%d</string>
|
||||
<string name="rew_text_regular_format" translatable="false" formatted="true">%d</string>
|
||||
<string name="rating_format" translatable="false" formatted="true">%.1f/10.0</string>
|
||||
<string name="year_format" translatable="false" formatted="true">%d</string>
|
||||
<string name="extra_info_format" formatted="true" translatable="false">%d %s | %sMB</string>
|
||||
<string name="storage_size_format" formatted="true" translatable="false">%s • %sGB</string>
|
||||
<string name="download_size_format" formatted="true" translatable="false">%sMB / %sMB</string>
|
||||
<string name="mb_format" formatted="true" translatable="false">%dMB</string>
|
||||
<string name="episode_name_format" formatted="true" translatable="false">%s %s</string>
|
||||
<string name="ffw_text_format" formatted="true" translatable="false">+%d</string>
|
||||
<string name="rew_text_format" formatted="true" translatable="false">-%d</string>
|
||||
<string name="ffw_text_regular_format" formatted="true" translatable="false">%d</string>
|
||||
<string name="rew_text_regular_format" formatted="true" translatable="false">%d</string>
|
||||
<string name="rating_format" formatted="true" translatable="false">%.1f/10.0</string>
|
||||
<string name="year_format" formatted="true" translatable="false">%d</string>
|
||||
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
|
||||
<string name="cast_format" formatted="true">Cast: %s</string>
|
||||
<string name="next_episode_format" formatted="true">Bölüm %d şu tarihte yayınlanacak: </string>
|
||||
|
@ -39,6 +39,7 @@
|
|||
<string name="duration_format" formatted="true">%d dakika</string>
|
||||
|
||||
<string name="app_name">CloudStream</string>
|
||||
<string name="play_with_app_name">CloudStream ile oynat</string>
|
||||
<string name="title_home">Ana sayfa</string>
|
||||
<string name="title_search">Arama</string>
|
||||
<string name="title_downloads">İndirilenler</string>
|
||||
|
@ -66,6 +67,7 @@
|
|||
<string name="type_re_watching">Yeniden izleniyor</string>
|
||||
|
||||
<string name="play_movie_button">Filmi oynat</string>
|
||||
<string name="play_livestream_button">Canlı yayını oynat</string>
|
||||
<string name="play_torrent_button">Torrent oynat</string>
|
||||
<string name="pick_source">Kaynaklar</string>
|
||||
<string name="pick_subtitle">Alt yazılar</string>
|
||||
|
@ -268,6 +270,7 @@
|
|||
<string name="documentaries">Belgeseller</string>
|
||||
<string name="ova">OVA</string>
|
||||
<string name="asian_drama">Asya dramaları</string>
|
||||
<string name="livestreams">Canlı yayınlar</string>
|
||||
|
||||
<!--singular-->
|
||||
<string name="movies_singular">Film</string>
|
||||
|
@ -278,6 +281,7 @@
|
|||
<string name="torrent_singular">Torrent</string>
|
||||
<string name="documentaries_singular">Belgesel</string>
|
||||
<string name="asian_drama_singular">Asya draması</string>
|
||||
<string name="live_singular">Canlı yayın</string>
|
||||
|
||||
<string name="source_error">Kaynak hatası</string>
|
||||
<string name="remote_error">Sunucu hatası</string>
|
||||
|
@ -495,5 +499,15 @@
|
|||
<string name="trailer">Fragman</string>
|
||||
<string name="network_adress_example">Yayına bağlan</string>
|
||||
<string name="referer">Yönlendiren</string>
|
||||
<string name="next">İleri</string>
|
||||
<string name="provider_languages_tip">Videoları bu dillerde izle</string>
|
||||
<string name="previous">Geri</string>
|
||||
<string name="skip_setup">Kurulumu atla</string>
|
||||
<!-- TODO: Remove or change this placeholder text -->
|
||||
<string name="hello_blank_fragment">Merhaba boş parça</string>
|
||||
<string name="app_layout_subtext">Cihazınıza uygun görünümü seçin</string>
|
||||
<string name="crash_reporting_title">Çökme raporları</string>
|
||||
<string name="preferred_media_subtext">Ne izlemek istiyorsunuz?</string>
|
||||
<string name="setup_done">Bitti</string>
|
||||
</resources>
|
||||
|
||||
|
|
Loading…
Reference in a new issue