Skip to content

Commit 92f3a06

Browse files
Try to fix
1 parent 548b4da commit 92f3a06

1 file changed

Lines changed: 160 additions & 115 deletions

File tree

AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt

Lines changed: 160 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@ package com.hexated
33
import com.lagradost.cloudstream3.*
44
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
55
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
6+
import com.lagradost.cloudstream3.mvvm.safeApiCall
67
import com.lagradost.cloudstream3.utils.*
78
import com.lagradost.nicehttp.NiceResponse
9+
import kotlinx.coroutines.CoroutineScope
10+
import kotlinx.coroutines.Dispatchers
11+
import kotlinx.coroutines.launch
12+
import kotlinx.coroutines.coroutineScope
813
import kotlinx.coroutines.async
914
import kotlinx.coroutines.awaitAll
10-
import kotlinx.coroutines.coroutineScope
1115
import org.jsoup.Jsoup
1216
import org.jsoup.nodes.Element
17+
import com.lagradost.cloudstream3.SubtitleFile
18+
import com.lagradost.cloudstream3.app
19+
import kotlin.text.Regex
1320

1421
class AnimeSailProvider : MainAPI() {
1522
override var mainUrl = "https://154.26.137.28"
@@ -18,19 +25,12 @@ class AnimeSailProvider : MainAPI() {
1825
override var lang = "id"
1926
override val hasDownloadSupport = true
2027

21-
override val supportedTypes = setOf(
22-
TvType.Anime,
23-
TvType.AnimeMovie,
24-
TvType.OVA
25-
)
28+
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie, TvType.OVA)
2629

2730
companion object {
2831
fun getType(t: String): TvType {
29-
return when {
30-
t.contains("OVA", true) || t.contains("Special") -> TvType.OVA
31-
t.contains("Movie", true) -> TvType.AnimeMovie
32-
else -> TvType.Anime
33-
}
32+
return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA
33+
else if (t.contains("Movie", true)) TvType.AnimeMovie else TvType.Anime
3434
}
3535

3636
fun getStatus(t: String): ShowStatus {
@@ -45,19 +45,22 @@ class AnimeSailProvider : MainAPI() {
4545
private suspend fun request(url: String, ref: String? = null): NiceResponse {
4646
return app.get(
4747
url,
48-
headers = mapOf(
49-
"Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
50-
),
48+
headers =
49+
mapOf(
50+
"Accept" to
51+
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
52+
),
5153
cookies = mapOf("_as_ipin_ct" to "ID"),
5254
referer = ref
5355
)
5456
}
5557

56-
override val mainPage = mainPageOf(
57-
"$mainUrl/rilisan-anime-terbaru/page/" to "Episode Anime Terbaru",
58-
"$mainUrl/rilisan-donghua-terbaru/page/" to "Episode Donghua Terbaru",
59-
"$mainUrl/movie-terbaru/page/" to "Movie Terbaru",
60-
)
58+
override val mainPage =
59+
mainPageOf(
60+
"$mainUrl/rilisan-anime-terbaru/page/" to "Episode Anime Terbaru",
61+
"$mainUrl/rilisan-donghua-terbaru/page/" to "Episode Donghua Terbaru",
62+
"$mainUrl/movie-terbaru/page/" to "Movie Terbaru",
63+
)
6164

6265
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
6366
val document = request(request.data + page).document
@@ -66,25 +69,31 @@ class AnimeSailProvider : MainAPI() {
6669
}
6770

6871
private fun getProperAnimeLink(uri: String): String {
69-
return if (uri.contains("/anime/")) uri else {
72+
return if (uri.contains("/anime/")) {
73+
uri
74+
} else {
7075
var title = uri.substringAfter("$mainUrl/")
71-
title = when {
72-
title.contains("-episode") && !title.contains("-movie") -> title.substringBefore("-episode")
73-
title.contains("-movie") -> title.substringBefore("-movie")
74-
else -> title
75-
}
76+
title =
77+
when {
78+
(title.contains("-episode")) && !(title.contains("-movie")) ->
79+
title.substringBefore("-episode")
80+
81+
(title.contains("-movie")) -> title.substringBefore("-movie")
82+
else -> title
83+
}
84+
7685
"$mainUrl/anime/$title"
7786
}
7887
}
7988

8089
private fun Element.toSearchResult(): AnimeSearchResponse {
81-
val href = getProperAnimeLink(fixUrlNull(selectFirst("a")?.attr("href")).toString())
82-
val title = select(".tt > h2").text().trim()
83-
val posterUrl = fixUrlNull(selectFirst("div.limit img")?.attr("src"))
84-
val epNum = selectFirst(".tt > h2")?.text()?.let {
85-
Regex("Episode\\s?(\\d+)").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull()
86-
}
87-
90+
val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString())
91+
val title = this.select(".tt > h2").text().trim()
92+
val posterUrl = fixUrl(this.selectFirst("div.limit img")?.attr("src") ?: "")
93+
val epNum =
94+
this.selectFirst(".tt > h2")?.text()?.let {
95+
Regex("Episode\\s?(\\d+)").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull()
96+
}
8897
return newAnimeSearchResponse(title, href, TvType.Anime) {
8998
this.posterUrl = posterUrl
9099
addSub(epNum)
@@ -94,24 +103,46 @@ class AnimeSailProvider : MainAPI() {
94103
override suspend fun search(query: String): List<SearchResponse> {
95104
val link = "$mainUrl/?s=$query"
96105
val document = request(link).document
106+
97107
return document.select("div.listupd article").map { it.toSearchResult() }
98108
}
99109

100110
override suspend fun load(url: String): LoadResponse {
101111
val document = request(url).document
102112

103-
val title = document.selectFirst("h1.entry-title")?.text()
104-
?.replace("Subtitle Indonesia", "")?.trim().orEmpty()
113+
val title =
114+
document.selectFirst("h1.entry-title")
115+
?.text()
116+
.toString()
117+
.replace("Subtitle Indonesia", "")
118+
.trim()
105119
val poster = document.selectFirst("div.entry-content > img")?.attr("src")
106120
val type = getType(document.select("tbody th:contains(Tipe)").next().text().lowercase())
107121
val year = document.select("tbody th:contains(Dirilis)").next().text().trim().toIntOrNull()
108122

109-
val episodes = document.select("ul.daftar > li").map {
110-
val link = fixUrl(it.select("a").attr("href"))
111-
val name = it.select("a").text()
112-
val episode = Regex("Episode\\s?(\\d+)").find(name)?.groupValues?.getOrNull(0)?.toIntOrNull()
113-
newEpisode(link) { this.episode = episode }
114-
}.reversed()
123+
// --- Corrected Episode Mapping ---
124+
val episodes =
125+
document.select("ul.daftar > li") // Assuming this is your episode list selector
126+
.mapNotNull { episodeElement -> // Use mapNotNull to safely skip if data is missing
127+
val anchor = episodeElement.selectFirst("a") ?: return@mapNotNull null
128+
val episodeLink = fixUrl(anchor.attr("href"))
129+
val episodeName = anchor.text()
130+
131+
val episodeNumber =
132+
// Renamed from 'episode' to avoid confusion with property name
133+
Regex("Episode\\s?(\\d+)")
134+
.find(episodeName)
135+
?.groupValues
136+
?.getOrNull(1) // IMPORTANT: Group 1 for the number
137+
?.toIntOrNull()
138+
139+
newEpisode(episodeLink) { // 'episodeLink' is the 'data' argument
140+
this.name = episodeName // Set the 'name' property
141+
this.episode = episodeNumber // Set the 'episode' property (the number)
142+
}
143+
}
144+
.reversed()
145+
// --- End Corrected Episode Mapping ---
115146

116147
val tracker = APIHolder.getTracker(listOf(title), TrackerType.getTypes(type), year, true)
117148

@@ -120,9 +151,11 @@ class AnimeSailProvider : MainAPI() {
120151
backgroundPosterUrl = tracker?.cover
121152
this.year = year
122153
addEpisodes(DubStatus.Subbed, episodes)
123-
showStatus = getStatus(document.select("tbody th:contains(Status)").next().text().trim())
154+
showStatus =
155+
getStatus(document.select("tbody th:contains(Status)").next().text().trim())
124156
plot = document.selectFirst("div.entry-content > p")?.text()
125-
this.tags = document.select("tbody th:contains(Genre)").next().select("a").map { it.text() }
157+
this.tags =
158+
document.select("tbody th:contains(Genre)").next().select("a").map { it.text() }
126159
addMalId(tracker?.malId)
127160
addAniListId(tracker?.aniId?.toIntOrNull())
128161
}
@@ -133,99 +166,111 @@ class AnimeSailProvider : MainAPI() {
133166
isCasting: Boolean,
134167
subtitleCallback: (SubtitleFile) -> Unit,
135168
callback: (ExtractorLink) -> Unit
136-
): Boolean = coroutineScope {
169+
): Boolean {
170+
137171
val document = request(data).document
138-
val options = document.select(".mobius > .mirror > option")
139-
140-
options.map { option ->
141-
async {
142-
try {
143-
val iframe = fixUrl(
144-
Jsoup.parse(base64Decode(option.attr("data-em"))).select("iframe").attr("src")
145-
?: throw ErrorLoadingException("No iframe found")
146-
)
147-
val quality = getIndexQuality(option.text())
148-
149-
when {
150-
iframe.startsWith("$mainUrl/utils/player/kodir2") -> {
151-
val text = request(iframe, ref = data).text
152-
val html = text.substringAfter("= `").substringBefore("`;")
153-
val link = Jsoup.parse(html).select("source").last()?.attr("src")
154-
if (link != null) callback(newExtractorLink(name, name, link, INFER_TYPE) {
155-
referer = mainUrl
156-
this.quality = quality
157-
})
158-
}
159172

160-
iframe.contains("/arch/") || iframe.contains("/race/") ||
161-
iframe.contains("/hexupload/") || iframe.contains("/pomf/") -> {
162-
val link = request(iframe, ref = data).document.select("source").attr("src")
163-
val source = when {
164-
iframe.contains("/arch/") -> "Arch"
165-
iframe.contains("/race/") -> "Race"
166-
iframe.contains("/hexupload/") -> "Hexupload"
167-
iframe.contains("/pomf/") -> "Pomf"
168-
else -> name
173+
// Replace blocking apmap with coroutine-friendly concurrent processing
174+
coroutineScope {
175+
val jobs = document.select(".mobius > .mirror > option").map { element ->
176+
async {
177+
safeApiCall {
178+
val iframe =
179+
fixUrl(
180+
Jsoup.parse(base64Decode(element.attr("data-em")))
181+
.select("iframe")
182+
.attr("src")
183+
)
184+
val quality = getIndexQuality(element.text())
185+
when {
186+
iframe.startsWith("$mainUrl/utils/player/arch/") ||
187+
iframe.startsWith("$mainUrl/utils/player/race/") ->
188+
request(iframe, ref = data).document.select("source").attr("src")
189+
.let { link ->
190+
val source =
191+
when {
192+
iframe.contains("/arch/") -> "Arch"
193+
iframe.contains("/race/") -> "Race"
194+
else -> this@AnimeSail.name
195+
}
196+
callback.invoke(
197+
ExtractorLink(
198+
source = source,
199+
name = source,
200+
url = link,
201+
referer = mainUrl,
202+
quality = getIndexQuality(element.text()),
203+
type = ExtractorLinkType.VIDEO
204+
)
205+
)
206+
}
207+
iframe.startsWith("https://aghanim.xyz/tools/redirect/") -> {
208+
val link =
209+
"https://rasa-cintaku-semakin-berantai.xyz/v/${
210+
iframe.substringAfter("id=").substringBefore("&token")
211+
}"
212+
loadFixedExtractor(link, quality, mainUrl, subtitleCallback, callback)
169213
}
170-
callback(newExtractorLink(source, source, link, INFER_TYPE) {
171-
referer = mainUrl
172-
this.quality = quality
173-
})
174-
}
175214

176-
iframe.startsWith("https://aghanim.xyz/tools/redirect/") -> {
177-
val link = "https://rasa-cintaku-semakin-berantai.xyz/v/" +
178-
iframe.substringAfter("id=").substringBefore("&token")
179-
loadFixedExtractor(link, quality, mainUrl, subtitleCallback, callback)
180-
}
181-
182-
iframe.startsWith("$mainUrl/utils/player/framezilla/") ||
183-
iframe.startsWith("https://uservideo.xyz") -> {
184-
val link = request(iframe, ref = data).document.select("iframe").attr("src")
185-
loadFixedExtractor(fixUrl(link), quality, mainUrl, subtitleCallback, callback)
186-
}
215+
iframe.startsWith("$mainUrl/utils/player/framezilla/") ||
216+
iframe.startsWith("https://uservideo.xyz") -> {
217+
request(iframe, ref = data).document.select("iframe").attr("src").let { link
218+
->
219+
loadFixedExtractor(
220+
fixUrl(link),
221+
quality,
222+
mainUrl,
223+
subtitleCallback,
224+
callback
225+
)
226+
}
227+
}
187228

188-
else -> {
189-
loadFixedExtractor(iframe, quality, mainUrl, subtitleCallback, callback)
229+
else -> {
230+
loadFixedExtractor(iframe, quality, mainUrl, subtitleCallback, callback)
231+
}
190232
}
191233
}
192-
} catch (e: Exception) {
193-
logError(e)
194234
}
195235
}
196-
}.awaitAll()
236+
jobs.awaitAll()
237+
}
197238

198-
true
239+
return true
240+
}
241+
242+
private fun getIndexQuality(str: String?): Int {
243+
return Regex("(\\d{3,4})[pP]").find(str ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull()
244+
?: Qualities.Unknown.value
199245
}
200246

201247
private suspend fun loadFixedExtractor(
202248
url: String,
203-
quality: Int?,
249+
quality: Int,
204250
referer: String? = null,
205251
subtitleCallback: (SubtitleFile) -> Unit,
206252
callback: (ExtractorLink) -> Unit
207253
) {
208254
loadExtractor(url, referer, subtitleCallback) { link ->
209-
callback(
210-
newExtractorLink(
211-
source = link.name,
212-
name = link.name,
213-
url = link.url,
214-
INFER_TYPE
215-
) {
216-
this.referer = mainUrl
217-
this.quality = if (link.type == ExtractorLinkType.M3U8) link.quality
218-
else quality ?: Qualities.Unknown.value
219-
this.type = link.type
220-
this.headers = link.headers
221-
this.extractorData = link.extractorData
222-
}
223-
)
255+
CoroutineScope(Dispatchers.IO).launch {
256+
callback.invoke(
257+
ExtractorLink(
258+
source = name,
259+
name = name,
260+
url = link.url,
261+
referer = link.referer,
262+
quality = link.quality,
263+
type = link.type,
264+
extractorData = link.extractorData,
265+
headers = link.headers
266+
)
267+
)
268+
}
224269
}
225-
}
226270

227-
private fun getIndexQuality(str: String): Int {
228-
return Regex("(\\d{3,4})[pP]").find(str)?.groupValues?.getOrNull(1)?.toIntOrNull()
229-
?: Qualities.Unknown.value
271+
fun getIndexQuality(str: String): Int {
272+
return Regex("(\\d{3,4})[pP]").find(str)?.groupValues?.getOrNull(1)?.toIntOrNull()
273+
?: Qualities.Unknown.value
274+
}
230275
}
231276
}

0 commit comments

Comments
 (0)