@@ -3,7 +3,9 @@ package com.kuramanime
33import com.lagradost.cloudstream3.*
44import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
55import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
6+ import com.lagradost.cloudstream3.amap
67import com.lagradost.cloudstream3.utils.ExtractorLink
8+ import com.lagradost.cloudstream3.utils.INFER_TYPE
79import com.lagradost.cloudstream3.utils.Qualities
810import com.lagradost.cloudstream3.utils.loadExtractor
911import com.lagradost.cloudstream3.utils.newExtractorLink
@@ -12,7 +14,7 @@ import org.jsoup.nodes.Element
1214import java.net.URI
1315
1416class KuramanimeProvider : MainAPI () {
15- override var mainUrl = " https://v15 .kuramanime.tel"
17+ override var mainUrl = " https://v9 .kuramanime.tel"
1618 override var name = " Kuramanime"
1719 override val hasQuickSearch = false
1820 override val hasMainPage = true
@@ -27,7 +29,6 @@ class KuramanimeProvider : MainAPI() {
2729
2830 companion object {
2931 private var cookies: Map <String , String > = mapOf ()
30- private const val USER_AGENT = " Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36"
3132
3233 fun getType (t : String , s : Int ): TvType {
3334 return if (t.contains(" OVA" , true ) || t.contains(" Special" )) TvType .OVA
@@ -55,7 +56,7 @@ class KuramanimeProvider : MainAPI() {
5556 page : Int ,
5657 request : MainPageRequest
5758 ): HomePageResponse {
58- val document = app.get(request.data + page, headers = mapOf ( " User-Agent " to USER_AGENT ) ).document
59+ val document = app.get(request.data + page).document
5960 val home = document.select(" div#animeList div.product__item" ).mapNotNull {
6061 it.toSearchResult()
6162 }
@@ -83,30 +84,30 @@ class KuramanimeProvider : MainAPI() {
8384 this .posterUrl = posterUrl
8485 addSub(episode)
8586 }
87+
8688 }
8789
8890 override suspend fun search (query : String ): List <SearchResponse > {
8991 return app.get(
90- " $mainUrl /anime?search=$query &order_by=latest" ,
91- headers = mapOf (" User-Agent" to USER_AGENT )
92+ " $mainUrl /anime?search=$query &order_by=latest"
9293 ).document.select(" div#animeList div.product__item" ).mapNotNull {
9394 it.toSearchResult()
9495 }
9596 }
9697
9798 override suspend fun load (url : String ): LoadResponse {
98- val document = app.get(url, headers = mapOf ( " User-Agent " to USER_AGENT ) ).document
99+ val document = app.get(url).document
99100
100101 val title = document.selectFirst(" .anime__details__title > h3" )!! .text().trim()
101102 val poster = document.selectFirst(" .anime__details__pic" )?.attr(" data-setbg" )
102- val tags = document.select(" div.anime__details__widget > div > div:nth-child(2) > ul > li:nth-child(1)" )
103- .text().trim().replace(" Genre: " , " " ).split(" , " )
103+ val tags =
104+ document.select(" div.anime__details__widget > div > div:nth-child(2) > ul > li:nth-child(1)" )
105+ .text().trim().replace(" Genre: " , " " ).split(" , " )
104106
105107 val year = Regex (" \\ D" ).replace(
106108 document.select(" div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(5)" )
107109 .text().trim().replace(" Musim: " , " " ), " "
108110 ).toIntOrNull()
109-
110111 val status = getStatus(
111112 document.select(" div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(3)" )
112113 .text().trim().replace(" Status: " , " " )
@@ -116,21 +117,22 @@ class KuramanimeProvider : MainAPI() {
116117 val episodes = mutableListOf<Episode >()
117118
118119 for (i in 1 .. 30 ) {
119- val doc = app.get(" $url ?page=$i " , headers = mapOf ( " User-Agent " to USER_AGENT ) ).document
120+ val doc = app.get(" $url ?page=$i " ).document
120121 val eps = Jsoup .parse(doc.select(" #episodeLists" ).attr(" data-content" ))
121122 .select(" a.btn.btn-sm.btn-danger" )
122123 .mapNotNull {
123124 val name = it.text().trim()
124- val episode = Regex (" (\\ d+[.,]?\\ d*)" ).find(name)?.groupValues?.getOrNull(0 )?.toIntOrNull()
125+ val episode = Regex (" (\\ d+[.,]?\\ d*)" ).find(name)?.groupValues?.getOrNull(0 )
126+ ?.toIntOrNull()
125127 val link = it.attr(" href" )
126128 newEpisode(link) { this .episode = episode }
127129 }
128130 if (eps.isEmpty()) break else episodes.addAll(eps)
129131 }
130132
131133 val type = getType(
132- document.selectFirst(" div.col-lg-6.col-md-6 ul li:contains(Tipe:) a" )?.text()?.lowercase() ? : " tv " ,
133- episodes.size
134+ document.selectFirst(" div.col-lg-6.col-md-6 ul li:contains(Tipe:) a" )?.text()
135+ ?.lowercase() ? : " tv " , episodes.size
134136 )
135137 val recommendations = document.select(" div#randomList > a" ).mapNotNull {
136138 val epHref = it.attr(" href" )
@@ -157,6 +159,7 @@ class KuramanimeProvider : MainAPI() {
157159 addMalId(tracker?.malId)
158160 addAniListId(tracker?.aniId?.toIntOrNull())
159161 }
162+
160163 }
161164
162165 private suspend fun invokeLocalSource (
@@ -168,72 +171,32 @@ class KuramanimeProvider : MainAPI() {
168171 ) {
169172 val document = app.get(
170173 url,
171- headers = headers + mapOf ( " Referer " to mainUrl) ,
174+ headers = headers,
172175 cookies = cookies
173176 ).document
174-
175- var linkFound = false
176-
177- document.select(" video#player > source, video > source" ).forEach { source ->
178- val link = fixUrl(source.attr(" src" ))
179- val qualityStr = source.attr(" size" ).ifEmpty { source.attr(" res" ) }
180- val quality = qualityStr.toIntOrNull() ? : Qualities .Unknown .value
181- val isM3u8 = link.contains(" .m3u8" )
182-
183- if (link.isNotBlank()) {
184- linkFound = true
185- callback.invoke(
186- newExtractorLink(
187- source = name,
188- name = " Kurama Internal - $server " ,
189- url = link,
190- referer = url,
191- quality = quality,
192- isM3u8 = isM3u8,
193- headers = headers + mapOf (" Origin" to mainUrl, " Referer" to url)
194- )
195- )
196- }
197- }
198-
199- if (! linkFound) {
200- val scripts = document.select(" script" ).map { it.data() }.joinToString(" \n " )
201- val linkRegex = Regex (" [\" '](https?://[^\" ']+(?:\\ .mp4|\\ .m3u8)[^\" ']*)[\" ']" )
202-
203- linkRegex.findAll(scripts).forEach { match ->
204- val link = match.groupValues[1 ].replace(" \\ /" , " /" )
205- val isM3u8 = link.contains(" .m3u8" )
206-
207- if (! link.contains(" popads" ) && ! link.contains(" doubleclick" )) {
208- callback.invoke(
209- newExtractorLink(
210- source = name,
211- name = " Kurama Extracted - $server " ,
212- url = link,
213- referer = url,
214- quality = Qualities .Unknown .value,
215- isM3u8 = isM3u8,
216- headers = headers + mapOf (" Origin" to mainUrl, " Referer" to url)
217- )
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" ,
218192 )
193+ this .quality = quality ? : Qualities .Unknown .value
219194 }
220- }
195+ )
221196 }
222-
223- document.select(" video#player > track, video > track" ).forEach { track ->
224- val subLink = fixUrl(track.attr(" src" ))
225- val lang = track.attr(" label" ).ifEmpty { " Indonesian" }
226- if (subLink.isNotBlank()) {
227- subtitleCallback.invoke(SubtitleFile (lang, subLink))
228- }
229- }
230-
231- if (server.contains(" kuramadrive" , true )) {
232- document.select(" div#animeDownloadLink a, a.btn-download" ).amap {
233- val downloadLink = it.attr(" href" )
234- if (downloadLink.isNotBlank()) {
235- loadExtractor(downloadLink, " $mainUrl /" , subtitleCallback, callback)
236- }
197+ if (server == " kuramadrive" ) {
198+ document.select(" div#animeDownloadLink a" ).amap {
199+ loadExtractor(it.attr(" href" ), " $mainUrl /" , subtitleCallback, callback)
237200 }
238201 }
239202 }
@@ -244,71 +207,70 @@ class KuramanimeProvider : MainAPI() {
244207 subtitleCallback : (SubtitleFile ) -> Unit ,
245208 callback : (ExtractorLink ) -> Unit
246209 ): Boolean {
247- val req = app.get(data, headers = mapOf (" User-Agent" to USER_AGENT ))
210+
211+ val req = app.get(data)
248212 val res = req.document
249213 cookies = req.cookies
250214
251215 val token = res.selectFirst(" meta[name=csrf-token]" )?.attr(" content" ) ? : return false
252- val dataKps = res.selectFirst(" div[data-kk]" )?.attr(" data-kk" )
253- ? : res.selectFirst(" div.col-lg-12.mt-3" )?.attr(" data-kk" ) ? : return false
216+ val dataKps = res.selectFirst(" div.col-lg-12.mt-3" )?.attr(" data-kk" ) ? : return false
254217
255218 val assets = getAssets(dataKps)
256219
257- val baseHeaders = mapOf (
258- " User-Agent" to USER_AGENT ,
220+ var headers = mapOf (
259221 " X-CSRF-TOKEN" to token,
222+ " X-Fuck-ID" to " ${assets.MIX_AUTH_KEY } :${assets.MIX_AUTH_TOKEN } " ,
223+ " X-Request-ID" to randomId(),
224+ " X-Request-Index" to " 0" ,
260225 " X-Requested-With" to " XMLHttpRequest" ,
261- " Referer" to data
262226 )
263227
264- val tokenKeyResponse = app.get(
228+ val tokenKey = app.get(
265229 " $mainUrl /${assets.MIX_PREFIX_AUTH_ROUTE_PARAM }${assets.MIX_AUTH_ROUTE_PARAM } " ,
266- headers = baseHeaders + mapOf (
267- " X-Fuck-ID" to " ${assets.MIX_AUTH_KEY } :${assets.MIX_AUTH_TOKEN } " ,
268- " X-Request-ID" to randomId(),
269- " X-Request-Index" to " 0"
270- ),
230+ headers = headers,
271231 cookies = cookies
232+ ).text
233+
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" ,
272239 )
273-
274- val tokenKey = tokenKeyResponse.text.trim()
275240
276241 res.select(" select#changeServer option" ).amap { source ->
277242 val server = source.attr(" value" )
278- val streamingUrl = " $data ?${assets.MIX_PAGE_TOKEN_KEY } =$tokenKey &${assets.MIX_STREAM_SERVER_KEY } =$server "
279-
280- val streamResponse = app.get(
281- streamingUrl,
282- headers = baseHeaders,
283- cookies = cookies
284- )
285-
286- val streamDoc = streamResponse.document
287-
243+ val link =
244+ " $data ?${assets.MIX_PAGE_TOKEN_KEY } =$tokenKey &${assets.MIX_STREAM_SERVER_KEY } =$server "
288245 if (server.contains(Regex (" (?i)kuramadrive|archive" ))) {
289- invokeLocalSource(streamingUrl , server, baseHeaders , subtitleCallback, callback)
246+ invokeLocalSource(link , server, headers , subtitleCallback, callback)
290247 } else {
291- val iframeSrc = streamDoc.selectFirst(" div.iframe-container iframe" )?.attr(" src" )
292- ? : streamDoc.selectFirst(" iframe" )?.attr(" src" )
293-
294- if (iframeSrc != null ) {
295- loadExtractor(fixUrl(iframeSrc), " $mainUrl /" , subtitleCallback, callback)
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)
296255 }
297256 }
298257 }
299258
259+
300260 return true
301261 }
302262
303263 private suspend fun getAssets (bpjs : String? ): Assets {
304- val env = app.get(" $mainUrl /assets/js/$bpjs .js" , headers = mapOf (" User-Agent" to USER_AGENT )).text
305- val MIX_PREFIX_AUTH_ROUTE_PARAM = env.substringAfter(" MIX_PREFIX_AUTH_ROUTE_PARAM: '" ).substringBefore(" '," )
306- val MIX_AUTH_ROUTE_PARAM = env.substringAfter(" MIX_AUTH_ROUTE_PARAM: '" ).substringBefore(" '," )
264+ 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(" '," )
307269 val MIX_AUTH_KEY = env.substringAfter(" MIX_AUTH_KEY: '" ).substringBefore(" '," )
308270 val MIX_AUTH_TOKEN = env.substringAfter(" MIX_AUTH_TOKEN: '" ).substringBefore(" '," )
309271 val MIX_PAGE_TOKEN_KEY = env.substringAfter(" MIX_PAGE_TOKEN_KEY: '" ).substringBefore(" '," )
310- val MIX_STREAM_SERVER_KEY = env.substringAfter( " MIX_STREAM_SERVER_KEY: ' " ).substringBefore( " ', " )
311-
272+ val MIX_STREAM_SERVER_KEY =
273+ env.substringAfter( " MIX_STREAM_SERVER_KEY: ' " ).substringBefore( " ', " )
312274 return Assets (
313275 MIX_PREFIX_AUTH_ROUTE_PARAM ,
314276 MIX_AUTH_ROUTE_PARAM ,
@@ -334,4 +296,5 @@ class KuramanimeProvider : MainAPI() {
334296 val MIX_PAGE_TOKEN_KEY : String? ,
335297 val MIX_STREAM_SERVER_KEY : String? ,
336298 )
337- }
299+
300+ }
0 commit comments