@@ -242,42 +242,57 @@ class AnimeKhorProvider : MainAPI() {
242242 return true
243243 }
244244
245+ // track whether any extractor link was actually emitted, so a native extractor
246+ // that matches the domain but yields nothing doesn't swallow the server silently
247+ var emitted = false
248+ val trackedCallback: (ExtractorLink ) -> Unit = { link ->
249+ emitted = true
250+ callback(link)
251+ }
252+
245253 if (host.contains(" dailymotion.com" ) || host.contains(" dai.ly" )) {
246- val vidId = Regex (""" (?:video/|video=|embed/|/video/)([a-zA-Z0-9_]+)""" ).find(clean)?.groupValues?.get(1 )
247- if (vidId != null && loadExtractor(" https://www.dailymotion.com/video/$vidId " , referer, subtitleCallback, callback)) return true
254+ try {
255+ loadExtractor(clean, referer, subtitleCallback, trackedCallback)
256+ if (emitted) return true
257+ } catch (e: Exception ) { }
248258 }
249259
250260 if (host.contains(" ok.ru" ) || host.contains(" odnoklassniki" )) {
251- if (loadExtractor(clean, referer, subtitleCallback, callback)) return true
261+ if (resolveOkru(clean, label, referer, subtitleCallback, trackedCallback)) return true
262+ try {
263+ loadExtractor(clean, referer, subtitleCallback, trackedCallback)
264+ if (emitted) return true
265+ } catch (e: Exception ) { }
252266 }
253267
254268 if (host.contains(" rumble.com" )) {
255- if (resolveRumble(clean, label, subtitleCallback, callback )) return true
269+ if (resolveRumble(clean, label, subtitleCallback, trackedCallback )) return true
256270 }
257271
258272 if (host.contains(" d.tube" )) {
259- if (resolveDtube(clean, label, subtitleCallback, callback )) return true
273+ if (resolveDtube(clean, label, subtitleCallback, trackedCallback )) return true
260274 }
261275
262276 if (host.contains(" turbovid" ) || host.contains(" emturbovid" ) || host.contains(" turboviplay" )) {
263- if (resolveTurbovid(clean, label, referer, subtitleCallback, callback )) return true
277+ if (resolveTurbovid(clean, label, referer, subtitleCallback, trackedCallback )) return true
264278 }
265279
266280 if (host.contains(" upns.live" ) || host.contains(" p2pstream.vip" )) {
267- if (resolveUpns(clean, label, subtitleCallback, callback )) return true
281+ if (resolveUpns(clean, label, subtitleCallback, trackedCallback )) return true
268282 }
269283
270284 if (host.contains(" bysekoze" ) || host.contains(" byse" )) {
271- if (resolveByse(clean, label, subtitleCallback, callback )) return true
285+ if (resolveByse(clean, label, subtitleCallback, trackedCallback )) return true
272286 }
273287
274288 if (host.contains(" abyssplayer" ) || host.contains(" abyss" )) {
275- if (resolveAbyss(clean, label, referer, subtitleCallback, callback )) return true
289+ if (resolveAbyss(clean, label, referer, subtitleCallback, trackedCallback )) return true
276290 }
277291
278292 // generic: native cloudstream extractors (turbovid, vidhide, streamwish, ...)
279293 try {
280- if (loadExtractor(clean, referer, subtitleCallback, callback)) return true
294+ loadExtractor(clean, referer, subtitleCallback, trackedCallback)
295+ if (emitted) return true
281296 } catch (e: Exception ) { }
282297
283298 // generic: scrape the embed page for a direct stream
@@ -304,6 +319,30 @@ class AnimeKhorProvider : MainAPI() {
304319 return false
305320 }
306321
322+ // ok.ru's modern pages put the stream info in a data-options attribute as escaped JSON;
323+ // extract the ondemandHls m3u8 directly (the built-in Odnoklassniki extractor no longer matches)
324+ private suspend fun resolveOkru (
325+ embedUrl : String ,
326+ label : String ,
327+ referer : String ,
328+ subtitleCallback : (SubtitleFile ) -> Unit ,
329+ callback : (ExtractorLink ) -> Unit
330+ ): Boolean {
331+ val clean = embedUrl.replace(" \\ /" , " /" )
332+ val html = try {
333+ app.get(clean, headers = mapOf (" Referer" to referer, " User-Agent" to USER_AGENT )).text
334+ } catch (e: Exception ) { return false }
335+ val hls = Regex (""" ondemandHls\\":\\"(.*?)\\"""" ).find(html)?.groupValues?.get(1 )
336+ ?.replace(""" \u0026""" , " &" )
337+ ?.replace(""" \/""" , " /" )
338+ ?.trim()
339+ if (hls == null || ! hls.startsWith(" http" )) return false
340+ val links = M3u8Helper .generateM3u8(label, hls, clean)
341+ if (links.isEmpty()) return false
342+ links.forEach { callback(it) }
343+ return true
344+ }
345+
307346 private suspend fun resolveRumble (
308347 embedUrl : String ,
309348 label : String ,
0 commit comments