Skip to content

Commit 96aa2a1

Browse files
Last Sync: 2025-12-24 04:42 (Mobile)
1 parent e2f4ea7 commit 96aa2a1

1 file changed

Lines changed: 77 additions & 52 deletions

File tree

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

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import org.jsoup.nodes.Element
1414
import java.net.URI
1515

1616
class KuramanimeProvider : MainAPI() {
17-
override var mainUrl = "https://v8.kuramanime.tel"
17+
// Pastikan domain ini masih aktif, jika redirect, ganti ke domain baru
18+
override var mainUrl = "https://v8.kuramanime.tel"
1819
override var name = "Kuramanime"
1920
override val hasQuickSearch = false
2021
override val hasMainPage = true
@@ -174,29 +175,34 @@ class KuramanimeProvider : MainAPI() {
174175
headers = headers,
175176
cookies = cookies
176177
).document
177-
document.select("video#player > source").map {
178-
val link = fixUrl(it.attr("src"))
179-
val quality = it.attr("size").toIntOrNull()
180-
callback.invoke(
181-
newExtractorLink(
182-
fixTitle(server),
183-
fixTitle(server),
184-
link,
185-
INFER_TYPE
186-
) {
187-
this.headers = mapOf(
188-
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
189-
"Range" to "bytes=0-",
190-
"Sec-Fetch-Dest" to "video",
191-
"Sec-Fetch-Mode" to "no-cors",
192-
)
193-
this.quality = quality ?: Qualities.Unknown.value
194-
}
195-
)
196-
}
197-
if (server == "kuramadrive") {
178+
179+
// Memperbaiki selector video agar lebih general
180+
val sources = document.select("video source")
181+
if (sources.isNotEmpty()) {
182+
sources.map {
183+
val link = fixUrl(it.attr("src"))
184+
val quality = it.attr("size").toIntOrNull()
185+
callback.invoke(
186+
newExtractorLink(
187+
fixTitle(server),
188+
fixTitle(server),
189+
link,
190+
INFER_TYPE
191+
) {
192+
this.headers = mapOf(
193+
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
194+
"Range" to "bytes=0-",
195+
"Sec-Fetch-Dest" to "video",
196+
"Sec-Fetch-Mode" to "no-cors",
197+
)
198+
this.quality = quality ?: Qualities.Unknown.value
199+
}
200+
)
201+
}
202+
} else {
203+
// Fallback jika video tag tidak langsung ditemukan (mungkin didalam script)
198204
document.select("div#animeDownloadLink a").amap {
199-
loadExtractor(it.attr("href"), "$mainUrl/", subtitleCallback, callback)
205+
loadExtractor(it.attr("href"), "$mainUrl/", subtitleCallback, callback)
200206
}
201207
}
202208
}
@@ -213,9 +219,14 @@ class KuramanimeProvider : MainAPI() {
213219
cookies = req.cookies
214220

215221
val token = res.selectFirst("meta[name=csrf-token]")?.attr("content") ?: return false
216-
val dataKps = res.selectFirst("div.col-lg-12.mt-3")?.attr("data-kk") ?: return false
222+
val dataKps = res.selectFirst("div[data-kk]")?.attr("data-kk") ?: return false
217223

218-
val assets = getAssets(dataKps)
224+
// Menggunakan try-catch saat mengambil assets agar tidak crash jika pattern berubah
225+
val assets = try {
226+
getAssets(dataKps)
227+
} catch (e: Exception) {
228+
return false
229+
}
219230

220231
var headers = mapOf(
221232
"X-CSRF-TOKEN" to token,
@@ -225,11 +236,13 @@ class KuramanimeProvider : MainAPI() {
225236
"X-Requested-With" to "XMLHttpRequest",
226237
)
227238

228-
val tokenKey = app.get(
239+
val tokenKeyResponse = app.get(
229240
"$mainUrl/${assets.MIX_PREFIX_AUTH_ROUTE_PARAM}${assets.MIX_AUTH_ROUTE_PARAM}",
230241
headers = headers,
231242
cookies = cookies
232-
).text
243+
)
244+
245+
val tokenKey = tokenKeyResponse.text
233246

234247
headers = mapOf(
235248
"Alt-Used" to URI(mainUrl).host,
@@ -239,38 +252,51 @@ class KuramanimeProvider : MainAPI() {
239252
)
240253

241254
res.select("select#changeServer option").amap { source ->
242-
val server = source.attr("value")
243-
val link =
244-
"$data?${assets.MIX_PAGE_TOKEN_KEY}=$tokenKey&${assets.MIX_STREAM_SERVER_KEY}=$server"
245-
if (server.contains(Regex("(?i)kuramadrive|archive"))) {
246-
invokeLocalSource(link, server, headers, subtitleCallback, callback)
247-
} else {
248-
app.get(
249-
link,
250-
referer = data,
251-
headers = headers,
252-
cookies = cookies
253-
).document.select("div.iframe-container iframe").attr("src").let { videoUrl ->
254-
loadExtractor(fixUrl(videoUrl), "$mainUrl/", subtitleCallback, callback)
255+
try {
256+
val server = source.attr("value")
257+
val link = "$data?${assets.MIX_PAGE_TOKEN_KEY}=$tokenKey&${assets.MIX_STREAM_SERVER_KEY}=$server"
258+
259+
if (server.contains(Regex("(?i)kuramadrive|archive"))) {
260+
invokeLocalSource(link, server, headers, subtitleCallback, callback)
261+
} else {
262+
val iframeDoc = app.get(
263+
link,
264+
referer = data,
265+
headers = headers,
266+
cookies = cookies
267+
).document
268+
269+
// Selector iframe yang lebih kuat
270+
val videoUrl = iframeDoc.select("iframe").attr("src")
271+
272+
if (videoUrl.isNotBlank()) {
273+
loadExtractor(fixUrl(videoUrl), "$mainUrl/", subtitleCallback, callback)
274+
}
255275
}
276+
} catch (e: Exception) {
277+
// Ignore error pada satu server, lanjut ke server lain
256278
}
257279
}
258280

259-
260281
return true
261282
}
262283

284+
// Fungsi getAssets yang diperbarui menggunakan Regex
285+
// Ini menangani kutip satu ('), kutip dua ("), dan variasi spasi
263286
private suspend fun getAssets(bpjs: String?): Assets {
264287
val env = app.get("$mainUrl/assets/js/$bpjs.js").text
265-
val MIX_PREFIX_AUTH_ROUTE_PARAM =
266-
env.substringAfter("MIX_PREFIX_AUTH_ROUTE_PARAM: '").substringBefore("',")
267-
val MIX_AUTH_ROUTE_PARAM =
268-
env.substringAfter("MIX_AUTH_ROUTE_PARAM: '").substringBefore("',")
269-
val MIX_AUTH_KEY = env.substringAfter("MIX_AUTH_KEY: '").substringBefore("',")
270-
val MIX_AUTH_TOKEN = env.substringAfter("MIX_AUTH_TOKEN: '").substringBefore("',")
271-
val MIX_PAGE_TOKEN_KEY = env.substringAfter("MIX_PAGE_TOKEN_KEY: '").substringBefore("',")
272-
val MIX_STREAM_SERVER_KEY =
273-
env.substringAfter("MIX_STREAM_SERVER_KEY: '").substringBefore("',")
288+
289+
fun extractVar(name: String): String {
290+
return Regex("""$name\s*:\s*['"](.*?)['"]""").find(env)?.groupValues?.get(1) ?: ""
291+
}
292+
293+
val MIX_PREFIX_AUTH_ROUTE_PARAM = extractVar("MIX_PREFIX_AUTH_ROUTE_PARAM")
294+
val MIX_AUTH_ROUTE_PARAM = extractVar("MIX_AUTH_ROUTE_PARAM")
295+
val MIX_AUTH_KEY = extractVar("MIX_AUTH_KEY")
296+
val MIX_AUTH_TOKEN = extractVar("MIX_AUTH_TOKEN")
297+
val MIX_PAGE_TOKEN_KEY = extractVar("MIX_PAGE_TOKEN_KEY")
298+
val MIX_STREAM_SERVER_KEY = extractVar("MIX_STREAM_SERVER_KEY")
299+
274300
return Assets(
275301
MIX_PREFIX_AUTH_ROUTE_PARAM,
276302
MIX_AUTH_ROUTE_PARAM,
@@ -296,5 +322,4 @@ class KuramanimeProvider : MainAPI() {
296322
val MIX_PAGE_TOKEN_KEY: String?,
297323
val MIX_STREAM_SERVER_KEY: String?,
298324
)
299-
300-
}
325+
}

0 commit comments

Comments
 (0)