@@ -14,7 +14,7 @@ import org.jsoup.nodes.Element
1414import java.net.URI
1515
1616class KuramanimeProvider : MainAPI () {
17- override var mainUrl = " https://v5 .kuramanime.blog "
17+ override var mainUrl = " https://v9 .kuramanime.tel "
1818 override var name = " Kuramanime"
1919 override val hasQuickSearch = false
2020 override val hasMainPage = true
@@ -84,7 +84,6 @@ class KuramanimeProvider : MainAPI() {
8484 this .posterUrl = posterUrl
8585 addSub(episode)
8686 }
87-
8887 }
8988
9089 override suspend fun search (query : String ): List <SearchResponse > {
@@ -159,7 +158,6 @@ class KuramanimeProvider : MainAPI() {
159158 addMalId(tracker?.malId)
160159 addAniListId(tracker?.aniId?.toIntOrNull())
161160 }
162-
163161 }
164162
165163 private suspend fun invokeLocalSource (
@@ -169,11 +167,16 @@ class KuramanimeProvider : MainAPI() {
169167 subtitleCallback : (SubtitleFile ) -> Unit ,
170168 callback : (ExtractorLink ) -> Unit
171169 ) {
172- val document = app.get(
170+ val response = app.get(
173171 url,
174172 headers = headers,
175173 cookies = cookies
176- ).document
174+ )
175+
176+ // Handle error response gracefuly
177+ if (response.code != 200 ) return
178+
179+ val document = response.document
177180 document.select(" video#player > source" ).map {
178181 val link = fixUrl(it.attr(" src" ))
179182 val quality = it.attr(" size" ).toIntOrNull()
@@ -185,15 +188,15 @@ class KuramanimeProvider : MainAPI() {
185188 INFER_TYPE
186189 ) {
187190 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" ,
191+ " Referer" to url, // Important for streaming
192+ " User-Agent" to " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
192193 )
193194 this .quality = quality ? : Qualities .Unknown .value
194195 }
195196 )
196197 }
198+
199+ // Fallback for download links if video tag fails
197200 if (server == " kuramadrive" ) {
198201 document.select(" div#animeDownloadLink a" ).amap {
199202 loadExtractor(it.attr(" href" ), " $mainUrl /" , subtitleCallback, callback)
@@ -212,72 +215,86 @@ class KuramanimeProvider : MainAPI() {
212215 val res = req.document
213216 cookies = req.cookies
214217
215- 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
218+ // Get CSRF Token
219+ val token = res.selectFirst(" meta[name=csrf-token]" )?.attr(" content" )
220+ ? : return false
221+
222+ // Get JS Asset Key (support data-kk or data-kps)
223+ val scriptData = res.selectFirst(" div[data-kk]" )?.attr(" data-kk" )
224+ ? : res.selectFirst(" div[data-kps]" )?.attr(" data-kps" )
225+ ? : return false
217226
218- val assets = getAssets(dataKps )
227+ val assets = getAssets(scriptData )
219228
220- var headers = mapOf (
229+ // Base Headers
230+ val baseHeaders = mapOf (
221231 " X-CSRF-TOKEN" to token,
232+ " X-Requested-With" to " XMLHttpRequest" ,
233+ " Referer" to data
234+ )
235+
236+ // Auth headers for the token request
237+ val authHeaders = baseHeaders + mapOf (
222238 " X-Fuck-ID" to " ${assets.MIX_AUTH_KEY } :${assets.MIX_AUTH_TOKEN } " ,
223239 " X-Request-ID" to randomId(),
224240 " X-Request-Index" to " 0" ,
225- " X-Requested-With" to " XMLHttpRequest" ,
226241 )
227242
243+ val tokenKeyUrl = " $mainUrl /${assets.MIX_PREFIX_AUTH_ROUTE_PARAM }${assets.MIX_AUTH_ROUTE_PARAM } "
244+
245+ // Get the dynamic key
228246 val tokenKey = app.get(
229- " $mainUrl / ${assets. MIX_PREFIX_AUTH_ROUTE_PARAM }${assets. MIX_AUTH_ROUTE_PARAM } " ,
230- headers = headers ,
247+ tokenKeyUrl ,
248+ headers = authHeaders ,
231249 cookies = cookies
232250 ).text
233251
234- headers = mapOf (
235- " Alt-Used" to URI (mainUrl).host,
236- " Authorization" to " Bearer 39F25KMTgDv0EQCqwRF9kBWxcSrHOGKc" ,
237- " X-CSRF-TOKEN" to token,
238- " X-Requested-With" to " XMLHttpRequest" ,
252+ // Stream headers (DO NOT include hardcoded Bearer token)
253+ val streamHeaders = baseHeaders + mapOf (
254+ " Alt-Used" to URI (mainUrl).host
239255 )
240256
241257 res.select(" select#changeServer option" ).amap { source ->
242258 val server = source.attr(" value" )
243- val link =
244- " $data ? ${assets. MIX_PAGE_TOKEN_KEY } = $tokenKey & ${assets. MIX_STREAM_SERVER_KEY } = $server "
259+ val link = " $data ? ${assets. MIX_PAGE_TOKEN_KEY } = $tokenKey & ${assets. MIX_STREAM_SERVER_KEY } = $server "
260+
245261 if (server.contains(Regex (" (?i)kuramadrive|archive" ))) {
246- invokeLocalSource(link, server, headers , subtitleCallback, callback)
262+ invokeLocalSource(link, server, streamHeaders , subtitleCallback, callback)
247263 } else {
248- app.get(
264+ val iframeReq = app.get(
249265 link,
250266 referer = data,
251- headers = headers ,
267+ headers = streamHeaders ,
252268 cookies = cookies
253- ).document.select(" div.iframe-container iframe" ).attr(" src" ).let { videoUrl ->
254- loadExtractor(fixUrl(videoUrl), " $mainUrl /" , subtitleCallback, callback)
269+ )
270+
271+ // Check if it's an iframe source
272+ val iframeSrc = iframeReq.document.select(" div.iframe-container iframe" ).attr(" src" )
273+ if (iframeSrc.isNotEmpty()) {
274+ loadExtractor(fixUrl(iframeSrc), " $mainUrl /" , subtitleCallback, callback)
255275 }
256276 }
257277 }
258278
259-
260279 return true
261280 }
262281
263282 private suspend fun getAssets (bpjs : String? ): Assets {
264283 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(" '," )
284+
285+ // Robust Regex extraction
286+ fun extract (key : String ): String {
287+ val regex = Regex (""" $key \s*:\s*['"]([^'"]+)['"]""" )
288+ return regex.find(env)?.groupValues?.get(1 ) ? : " "
289+ }
290+
274291 return Assets (
275- MIX_PREFIX_AUTH_ROUTE_PARAM ,
276- MIX_AUTH_ROUTE_PARAM ,
277- MIX_AUTH_KEY ,
278- MIX_AUTH_TOKEN ,
279- MIX_PAGE_TOKEN_KEY ,
280- MIX_STREAM_SERVER_KEY
292+ MIX_PREFIX_AUTH_ROUTE_PARAM = extract( " MIX_PREFIX_AUTH_ROUTE_PARAM " ) ,
293+ MIX_AUTH_ROUTE_PARAM = extract( " MIX_AUTH_ROUTE_PARAM " ) ,
294+ MIX_AUTH_KEY = extract( " MIX_AUTH_KEY " ) ,
295+ MIX_AUTH_TOKEN = extract( " MIX_AUTH_TOKEN " ) ,
296+ MIX_PAGE_TOKEN_KEY = extract( " MIX_PAGE_TOKEN_KEY " ) ,
297+ MIX_STREAM_SERVER_KEY = extract( " MIX_STREAM_SERVER_KEY " )
281298 )
282299 }
283300
@@ -289,12 +306,11 @@ class KuramanimeProvider : MainAPI() {
289306 }
290307
291308 data class Assets (
292- val MIX_PREFIX_AUTH_ROUTE_PARAM : String? ,
293- val MIX_AUTH_ROUTE_PARAM : String? ,
294- val MIX_AUTH_KEY : String? ,
295- val MIX_AUTH_TOKEN : String? ,
296- val MIX_PAGE_TOKEN_KEY : String? ,
297- val MIX_STREAM_SERVER_KEY : String? ,
309+ val MIX_PREFIX_AUTH_ROUTE_PARAM : String ,
310+ val MIX_AUTH_ROUTE_PARAM : String ,
311+ val MIX_AUTH_KEY : String ,
312+ val MIX_AUTH_TOKEN : String ,
313+ val MIX_PAGE_TOKEN_KEY : String ,
314+ val MIX_STREAM_SERVER_KEY : String ,
298315 )
299-
300- }
316+ }
0 commit comments