mirror of
				https://github.com/hexated/cloudstream-extensions-hexated.git
				synced 2024-08-15 00:03:22 +00:00 
			
		
		
		
	sora: fixed Crunchyroll and Zoro
This commit is contained in:
		
							parent
							
								
									92803be98d
								
							
						
					
					
						commit
						93c948bb8e
					
				
					 6 changed files with 298 additions and 461 deletions
				
			
		
							
								
								
									
										4
									
								
								.github/workflows/build.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/build.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -46,12 +46,16 @@ jobs: | ||||||
|           SORAHE: ${{ secrets.SORAHE }} |           SORAHE: ${{ secrets.SORAHE }} | ||||||
|           SORAXA: ${{ secrets.SORAXA }} |           SORAXA: ${{ secrets.SORAXA }} | ||||||
|           SORATED: ${{ secrets.SORATED }} |           SORATED: ${{ secrets.SORATED }} | ||||||
|  |           CRUNCHYROLL_BASIC_TOKEN: ${{ secrets.CRUNCHYROLL_BASIC_TOKEN }} | ||||||
|  |           CRUNCHYROLL_REFRESH_TOKEN: ${{ secrets.CRUNCHYROLL_REFRESH_TOKEN }} | ||||||
|         run: | |         run: | | ||||||
|           cd $GITHUB_WORKSPACE/src |           cd $GITHUB_WORKSPACE/src | ||||||
|           echo SORA_API=$SORA_API >> local.properties |           echo SORA_API=$SORA_API >> local.properties | ||||||
|           echo SORAHE=$SORAHE >> local.properties |           echo SORAHE=$SORAHE >> local.properties | ||||||
|           echo SORAXA=$SORAXA >> local.properties |           echo SORAXA=$SORAXA >> local.properties | ||||||
|           echo SORATED=$SORATED >> local.properties |           echo SORATED=$SORATED >> local.properties | ||||||
|  |           echo CRUNCHYROLL_BASIC_TOKEN=$CRUNCHYROLL_BASIC_TOKEN >> local.properties | ||||||
|  |           echo CRUNCHYROLL_REFRESH_TOKEN=$CRUNCHYROLL_REFRESH_TOKEN >> local.properties | ||||||
| 
 | 
 | ||||||
|       - name: Build Plugins |       - name: Build Plugins | ||||||
|         run: | |         run: | | ||||||
|  |  | ||||||
|  | @ -12,6 +12,8 @@ android { | ||||||
|         buildConfigField("String", "SORAHE", "\"${properties.getProperty("SORAHE")}\"") |         buildConfigField("String", "SORAHE", "\"${properties.getProperty("SORAHE")}\"") | ||||||
|         buildConfigField("String", "SORAXA", "\"${properties.getProperty("SORAXA")}\"") |         buildConfigField("String", "SORAXA", "\"${properties.getProperty("SORAXA")}\"") | ||||||
|         buildConfigField("String", "SORATED", "\"${properties.getProperty("SORATED")}\"") |         buildConfigField("String", "SORATED", "\"${properties.getProperty("SORATED")}\"") | ||||||
|  |         buildConfigField("String", "CRUNCHYROLL_BASIC_TOKEN", "\"${properties.getProperty("CRUNCHYROLL_BASIC_TOKEN")}\"") | ||||||
|  |         buildConfigField("String", "CRUNCHYROLL_REFRESH_TOKEN", "\"${properties.getProperty("CRUNCHYROLL_REFRESH_TOKEN")}\"") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -45,7 +45,9 @@ object SoraExtractor : SoraStream() { | ||||||
|                 val link = source.link ?: return@let |                 val link = source.link ?: return@let | ||||||
|                 if (link.contains("rabbitstream")) { |                 if (link.contains("rabbitstream")) { | ||||||
|                     extractRabbitStream( |                     extractRabbitStream( | ||||||
|  |                         "Vidcloud", | ||||||
|                         link, |                         link, | ||||||
|  |                         "$twoEmbedAPI/", | ||||||
|                         subtitleCallback, |                         subtitleCallback, | ||||||
|                         callback, |                         callback, | ||||||
|                         false, |                         false, | ||||||
|  | @ -188,21 +190,6 @@ object SoraExtractor : SoraStream() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     suspend fun invokeDatabaseGdrive( |  | ||||||
|         imdbId: String? = null, |  | ||||||
|         season: Int? = null, |  | ||||||
|         episode: Int? = null, |  | ||||||
|         subtitleCallback: (SubtitleFile) -> Unit, |  | ||||||
|         callback: (ExtractorLink) -> Unit |  | ||||||
|     ) { |  | ||||||
|         val url = if (season == null) { |  | ||||||
|             "$databaseGdriveAPI/player.php?imdb=$imdbId" |  | ||||||
|         } else { |  | ||||||
|             "$databaseGdriveAPI/player.php?type=series&imdb=$imdbId&season=$season&episode=$episode" |  | ||||||
|         } |  | ||||||
|         loadExtractor(url, databaseGdriveAPI, subtitleCallback, callback) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     suspend fun invokeHDMovieBox( |     suspend fun invokeHDMovieBox( | ||||||
|         title: String? = null, |         title: String? = null, | ||||||
|         season: Int? = null, |         season: Int? = null, | ||||||
|  | @ -327,14 +314,13 @@ object SoraExtractor : SoraStream() { | ||||||
|         }, { |         }, { | ||||||
|             val iv = "9225679083961858" |             val iv = "9225679083961858" | ||||||
|             val secretKey = "25742532592138496744665879883281" |             val secretKey = "25742532592138496744665879883281" | ||||||
|             val secretDecryptKey = secretKey |  | ||||||
|             GogoHelper.extractVidstream( |             GogoHelper.extractVidstream( | ||||||
|                 iframe.url, |                 iframe.url, | ||||||
|                 "Vidstream", |                 "Vidstream", | ||||||
|                 callback, |                 callback, | ||||||
|                 iv, |                 iv, | ||||||
|                 secretKey, |                 secretKey, | ||||||
|                 secretDecryptKey, |                 secretKey, | ||||||
|                 isUsingAdaptiveKeys = false, |                 isUsingAdaptiveKeys = false, | ||||||
|                 isUsingAdaptiveData = true, |                 isUsingAdaptiveData = true, | ||||||
|                 iframeDocument = iframeDoc |                 iframeDocument = iframeDoc | ||||||
|  | @ -496,13 +482,12 @@ object SoraExtractor : SoraStream() { | ||||||
|         } else { |         } else { | ||||||
|             "${filmxyAPI}/tv/$imdbId" |             "${filmxyAPI}/tv/$imdbId" | ||||||
|         } |         } | ||||||
|         val filmxyCookies = |         val filmxyCookies = getFilmxyCookies(imdbId, season) | ||||||
|             getFilmxyCookies(imdbId, season) ?: throw ErrorLoadingException("No Cookies Found") |  | ||||||
| 
 | 
 | ||||||
|         val cookiesDoc = mapOf( |         val cookiesDoc = mapOf( | ||||||
|             "G_ENABLED_IDPS" to "google", |             "G_ENABLED_IDPS" to "google", | ||||||
|             "wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" to "${filmxyCookies.wLog}", |             "wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" to (filmxyCookies.wLog ?: return), | ||||||
|             "PHPSESSID" to "${filmxyCookies.phpsessid}" |             "PHPSESSID" to (filmxyCookies.phpsessid ?: return) | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         val doc = session.get(url, cookies = cookiesDoc).document |         val doc = session.get(url, cookies = cookiesDoc).document | ||||||
|  | @ -868,85 +853,10 @@ object SoraExtractor : SoraStream() { | ||||||
|             { |             { | ||||||
|                 invokeBiliBili(aniId, episode, subtitleCallback, callback) |                 invokeBiliBili(aniId, episode, subtitleCallback, callback) | ||||||
|             }, |             }, | ||||||
| //            { |             { | ||||||
| //                if (season != null) invokeAllanime(aniId, title, jpTitle, episode, callback) |                 if (season != null) invokeCrunchyroll(aniId, epsTitle, season, episode, subtitleCallback, callback) | ||||||
| //            } |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private suspend fun invokeAllanime( |  | ||||||
|         aniId: String? = null, |  | ||||||
|         title: String? = null, |  | ||||||
|         jpTitle: String? = null, |  | ||||||
|         episode: Int? = null, |  | ||||||
|         callback: (ExtractorLink) -> Unit |  | ||||||
|     ) { |  | ||||||
|         val aniDetail = |  | ||||||
|             app.get("$consumetAnilistAPI/info/$aniId").parsedSafe<ConsumetDetails>() ?: return |  | ||||||
|         val edges = app.get( |  | ||||||
|             allanimeQueries( |  | ||||||
|                 """{"search":{"query":"$title","allowAdult":false,"allowUnknown":false},"limit":26,"page":1,"translationType":"sub","countryOrigin":"ALL"}""", |  | ||||||
|                 allanimeSearchQuery |  | ||||||
|             ) |  | ||||||
|         ) |  | ||||||
|             .parsedSafe<AllanimeResponses>()?.data?.shows?.edges |  | ||||||
|         val id = edges?.let { edge -> |  | ||||||
|             if (edges.size == 1) { |  | ||||||
|                 edge.firstOrNull() |  | ||||||
|             } else { |  | ||||||
|                 edge.find { |  | ||||||
|                     (it.thumbnail == aniDetail.cover || it.thumbnail == aniDetail.image) || ( |  | ||||||
|                             (it.name.equals( |  | ||||||
|                                 aniDetail.title?.romaji, |  | ||||||
|                                 true |  | ||||||
|                             ) || it.name.equals( |  | ||||||
|                                 jpTitle, |  | ||||||
|                                 true |  | ||||||
|                             ) || it.englishName.equals(aniDetail.title?.english, true)) |  | ||||||
|                                     && it.airedStart?.year == aniDetail.releaseDate) |  | ||||||
|                 } ?: edge.find { |  | ||||||
|                     it.name.equals( |  | ||||||
|                         aniDetail.title?.romaji, |  | ||||||
|                         true |  | ||||||
|                     ) || it.name.equals( |  | ||||||
|                         jpTitle, |  | ||||||
|                         true |  | ||||||
|                     ) || it.englishName.equals( |  | ||||||
|                         aniDetail.title?.english, |  | ||||||
|                         true |  | ||||||
|                     ) || it.englishName.equals(title, true) |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         }?._id ?: return |         ) | ||||||
| 
 |  | ||||||
|         listOf( |  | ||||||
|             "sub", |  | ||||||
|             "dub" |  | ||||||
|         ).apmap { tl -> |  | ||||||
|             val server = app.get( |  | ||||||
|                 allanimeQueries( |  | ||||||
|                     """{"showId":"$id","translationType":"$tl","episodeString":"$episode"}""", |  | ||||||
|                     allanimeServerQuery |  | ||||||
|                 ) |  | ||||||
|             ) |  | ||||||
|                 .parsedSafe<AllanimeResponses>()?.data?.episode?.sourceUrls?.find { it.sourceName == "Ac" } |  | ||||||
|             val serverUrl = fixUrl( |  | ||||||
|                 server?.sourceUrl?.replace("/clock", "/clock.json") ?: return@apmap, |  | ||||||
|                 "https://blog.allanime.pro" |  | ||||||
|             ) |  | ||||||
|             app.get(serverUrl) |  | ||||||
|                 .parsedSafe<AllanimeLinks>()?.links?.filter { it.resolutionStr == "RAW" && it.hls == true } |  | ||||||
|                 ?.forEach { source -> |  | ||||||
|                     val translation = if (tl == "sub") "Raw" else "English Dub" |  | ||||||
|                     M3u8Helper.generateM3u8( |  | ||||||
|                         "Vrv [$translation]", |  | ||||||
|                         source.link ?: return@apmap, |  | ||||||
|                         "https://static.crunchyroll.com/", |  | ||||||
|                     ).forEach(callback) |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private suspend fun invokeBiliBili( |     private suspend fun invokeBiliBili( | ||||||
|  | @ -999,41 +909,47 @@ object SoraExtractor : SoraStream() { | ||||||
|         subtitleCallback: (SubtitleFile) -> Unit, |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|         callback: (ExtractorLink) -> Unit |         callback: (ExtractorLink) -> Unit | ||||||
|     ) { |     ) { | ||||||
|         val episodeId = app.get("$consumetAnilistAPI/info/$aniId?provider=zoro") |         val animeId = | ||||||
|             .parsedSafe<ConsumetDetails>()?.episodes?.find { |             app.get("https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/anilist/anime/$aniId.json") | ||||||
|                 it.number == (episode ?: 1) |                 .parsedSafe<MALSyncResponses>()?.pages?.zoro?.keys?.firstOrNull() | ||||||
|             }?.id?.substringBeforeLast("$") ?: return |  | ||||||
| 
 | 
 | ||||||
|         listOf( |         val episodeId = app.get("$zoroAPI/ajax/v2/episode/list/${animeId ?: return}") | ||||||
|             "$episodeId\$sub" to "Raw", |             .parsedSafe<ZoroResponses>()?.html?.let { | ||||||
|             "$episodeId\$dub" to "English Dub", |                 Jsoup.parse(it) | ||||||
|         ).apmap { (id, type) -> |             }?.select("div.ss-list a")?.find { it.attr("data-number") == "$episode" } | ||||||
|             val sources = app.get("$consumetZoroAPI/watch?episodeId=$id") |             ?.attr("data-id") | ||||||
|                 .parsedSafe<ConsumetSourcesResponse>() ?: return@apmap null |  | ||||||
| 
 | 
 | ||||||
|             sources.sources?.map sources@{ |         val servers = app.get("$zoroAPI/ajax/v2/episode/servers?episodeId=${episodeId ?: return}") | ||||||
|                 callback.invoke( |             .parsedSafe<ZoroResponses>()?.html?.let { Jsoup.parse(it) } | ||||||
|                     ExtractorLink( |             ?.select("div.item.server-item")?.map { | ||||||
|                         "Zoro [$type]", |                 Triple( | ||||||
|                         "Zoro [$type]", |                     it.text(), | ||||||
|                         it.url ?: return@sources null, |                     it.attr("data-id"), | ||||||
|                         "", |                     it.attr("data-type"), | ||||||
|                         getQualityFromName(it.quality), |  | ||||||
|                         it.isM3U8 ?: true |  | ||||||
|                     ) |  | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             sources.subtitles?.map subtitles@{ |         servers?.apmap { server -> | ||||||
|                 subtitleCallback.invoke( |             val iframe = app.get("$zoroAPI/ajax/v2/episode/sources?id=${server.second ?: return@apmap}") | ||||||
|                     SubtitleFile( |                 .parsedSafe<ZoroResponses>()?.link ?: return@apmap | ||||||
|                         it.lang ?: "", |             val audio = if(server.third == "sub") "Raw" else "English Dub" | ||||||
|                         it.url ?: return@subtitles null |             if(server.first == "Vidstreaming" || server.first == "Vidcloud") { | ||||||
|                     ) |                 extractRabbitStream( | ||||||
|                 ) |                     "${server.first} [$audio]", | ||||||
|  |                     iframe, | ||||||
|  |                     "$zoroAPI/", | ||||||
|  |                     subtitleCallback, | ||||||
|  |                     callback, | ||||||
|  |                     false, | ||||||
|  |                     decryptKey = RabbitStream.getZoroKey() | ||||||
|  |                 ) { it } | ||||||
|  |             } else { | ||||||
|  |                 loadExtractor(iframe,"$zoroAPI/", subtitleCallback, callback) | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private suspend fun invokeAnimeKaizoku( |     private suspend fun invokeAnimeKaizoku( | ||||||
|  | @ -1551,52 +1467,78 @@ object SoraExtractor : SoraStream() { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     suspend fun invokeCrunchyroll( |     suspend fun invokeCrunchyroll( | ||||||
|         title: String? = null, |         aniId: String? = null, | ||||||
|         epsTitle: String? = null, |         epsTitle: String? = null, | ||||||
|         season: Int? = null, |         season: Int? = null, | ||||||
|         episode: Int? = null, |         episode: Int? = null, | ||||||
|         subtitleCallback: (SubtitleFile) -> Unit, |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|         callback: (ExtractorLink) -> Unit |         callback: (ExtractorLink) -> Unit | ||||||
|     ) { |     ) { | ||||||
|         val id = searchCrunchyrollAnimeId(title ?: return) ?: return |         val id = getCrunchyrollId(aniId) ?: return | ||||||
|         val detail = |         val audioLocal = listOf( | ||||||
|             app.get("$consumetCrunchyrollAPI/info/$id?fetchAllSeasons=true", timeout = 600L).text |             "ja-JP", | ||||||
|         val epsId = tryParseJson<CrunchyrollDetails>(detail)?.findCrunchyrollId( |             "en-US", | ||||||
|             season, |             "zh-CN", | ||||||
|             episode, |  | ||||||
|             epsTitle |  | ||||||
|         ) |         ) | ||||||
|  |         val headers = getCrunchyrollToken() | ||||||
|  |         val seasonIdData = app.get("$crunchyrollAPI/content/v2/cms/series/$id/seasons", headers = headers) | ||||||
|  |             .parsedSafe<CrunchyrollResponses>()?.data?.let { s -> | ||||||
|  |                 if (s.size == 1) { | ||||||
|  |                     s.firstOrNull() | ||||||
|  |                 } else { | ||||||
|  |                     s.find { | ||||||
|  |                         when (epsTitle) { | ||||||
|  |                             "One Piece" -> it.season_number == 13 | ||||||
|  |                             "Hunter x Hunter" -> it.season_number == 5 | ||||||
|  |                             else -> it.season_number == season | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         val seasonId = seasonIdData?.versions?.filter { it.audio_locale in audioLocal } | ||||||
|  |             ?.map { it.guid to it.audio_locale } | ||||||
| 
 | 
 | ||||||
|         epsId?.apmap { |         seasonId?.apmap { (sId, audioL) -> | ||||||
|             delay(2000) |             val streamsLink = | ||||||
|             val json = |  | ||||||
|                 app.get( |                 app.get( | ||||||
|                     "$consumetCrunchyrollAPI/watch/${it?.first?.id ?: return@apmap null}", |                     "$crunchyrollAPI/content/v2/cms/seasons/${sId ?: return@apmap}/episodes", | ||||||
|                     timeout = 600L |                     headers = headers | ||||||
|                 ) |                 ).parsedSafe<CrunchyrollResponses>()?.data?.find { | ||||||
|                     .parsedSafe<ConsumetSourcesResponse>() |                     it.title.equals(epsTitle, true) || it.slug_title.equals( | ||||||
| 
 |                         epsTitle.createSlug(), | ||||||
|             json?.sources?.map source@{ source -> |  | ||||||
|                 callback.invoke( |  | ||||||
|                     ExtractorLink( |  | ||||||
|                         "Crunchyroll", |  | ||||||
|                         "Crunchyroll [${it.second ?: ""}]", |  | ||||||
|                         source.url ?: return@source null, |  | ||||||
|                         "https://static.crunchyroll.com/", |  | ||||||
|                         source.quality?.removeSuffix("p")?.toIntOrNull() ?: return@source null, |  | ||||||
|                         true |                         true | ||||||
|                     ) |                     ) || it.episode_number == episode | ||||||
|                 ) |                 }?.streams_link | ||||||
|  |             val sources = | ||||||
|  |                 app.get(fixUrl(streamsLink ?: return@apmap, crunchyrollAPI), headers = headers) | ||||||
|  |                     .parsedSafe<CrunchyrollSourcesResponses>() | ||||||
|  | 
 | ||||||
|  |             listOf( | ||||||
|  |                 "adaptive_hls", | ||||||
|  |                 "vo_adaptive_hls" | ||||||
|  |             ).map { hls -> | ||||||
|  |                 val name = if (hls == "adaptive_hls") "Crunchyroll" else "Vrv" | ||||||
|  |                 val audio = if (audioL == "en-US") "English Dub" else "Raw" | ||||||
|  |                 val source = sources?.data?.firstOrNull()?.let { | ||||||
|  |                     if (hls == "adaptive_hls") it.adaptive_hls else it.vo_adaptive_hls | ||||||
|  |                 } | ||||||
|  |                 M3u8Helper.generateM3u8( | ||||||
|  |                     "$name [$audio]", | ||||||
|  |                     source?.get("")?.get("url") ?: return@map, | ||||||
|  |                     "https://static.crunchyroll.com/" | ||||||
|  |                 ).forEach(callback) | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             json?.subtitles?.map subtitle@{ sub -> |             sources?.meta?.subtitles?.map { sub -> | ||||||
|                 subtitleCallback.invoke( |                 subtitleCallback.invoke( | ||||||
|                     SubtitleFile( |                     SubtitleFile( | ||||||
|                         "${fixCrunchyrollLang(sub.lang ?: return@subtitle null) ?: sub.lang} [ass]", |                         "${fixCrunchyrollLang(sub.key) ?: sub.key} [ass]", | ||||||
|                         sub.url ?: return@subtitle null |                         sub.value["url"] ?: return@map null | ||||||
|                     ) |                     ) | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1900,6 +1842,7 @@ object SoraExtractor : SoraStream() { | ||||||
|         imdbId: String? = null, |         imdbId: String? = null, | ||||||
|         season: Int? = null, |         season: Int? = null, | ||||||
|         episode: Int? = null, |         episode: Int? = null, | ||||||
|  |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|         callback: (ExtractorLink) -> Unit, |         callback: (ExtractorLink) -> Unit, | ||||||
|     ) { |     ) { | ||||||
|         val url = if (season == null) { |         val url = if (season == null) { | ||||||
|  | @ -1927,6 +1870,9 @@ object SoraExtractor : SoraStream() { | ||||||
|                 it.first.contains("/nflim") -> { |                 it.first.contains("/nflim") -> { | ||||||
|                     invokeSmashyNflim(it.second, it.first, callback) |                     invokeSmashyNflim(it.second, it.first, callback) | ||||||
|                 } |                 } | ||||||
|  |                 it.first.contains("/rip") -> { | ||||||
|  |                     invokeSmashyRip(it.second, it.first, subtitleCallback, callback) | ||||||
|  |                 } | ||||||
|                 else -> return@apmap |                 else -> return@apmap | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -2121,46 +2067,6 @@ object SoraExtractor : SoraStream() { | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     suspend fun invokePapaonMovies1( |  | ||||||
|         apiUrl: String, |  | ||||||
|         api: String, |  | ||||||
|         title: String? = null, |  | ||||||
|         year: Int? = null, |  | ||||||
|         season: Int? = null, |  | ||||||
|         episode: Int? = null, |  | ||||||
|         callback: (ExtractorLink) -> Unit, |  | ||||||
|     ) { |  | ||||||
|         invokeIndex( |  | ||||||
|             apiUrl, |  | ||||||
|             api, |  | ||||||
|             title, |  | ||||||
|             year, |  | ||||||
|             season, |  | ||||||
|             episode, |  | ||||||
|             callback, |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     suspend fun invokePapaonMovies2( |  | ||||||
|         apiUrl: String, |  | ||||||
|         api: String, |  | ||||||
|         title: String? = null, |  | ||||||
|         year: Int? = null, |  | ||||||
|         season: Int? = null, |  | ||||||
|         episode: Int? = null, |  | ||||||
|         callback: (ExtractorLink) -> Unit, |  | ||||||
|     ) { |  | ||||||
|         invokeIndex( |  | ||||||
|             apiUrl, |  | ||||||
|             api, |  | ||||||
|             title, |  | ||||||
|             year, |  | ||||||
|             season, |  | ||||||
|             episode, |  | ||||||
|             callback, |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     suspend fun invokeJmdkhMovies( |     suspend fun invokeJmdkhMovies( | ||||||
|         apiUrl: String, |         apiUrl: String, | ||||||
|         api: String, |         api: String, | ||||||
|  | @ -2700,7 +2606,7 @@ object SoraExtractor : SoraStream() { | ||||||
|         season: Int? = null, |         season: Int? = null, | ||||||
|         callback: (ExtractorLink) -> Unit, |         callback: (ExtractorLink) -> Unit, | ||||||
|     ) { |     ) { | ||||||
|         val monsterMainUrl = "https://freedoze.monster" |         val monsterMainUrl = "https://mobirs.monster" | ||||||
|         val playSlug = if (season == null) { |         val playSlug = if (season == null) { | ||||||
|             "movies/play/$urlSlug" |             "movies/play/$urlSlug" | ||||||
|         } else { |         } else { | ||||||
|  | @ -2713,7 +2619,7 @@ object SoraExtractor : SoraStream() { | ||||||
|         val res = app.get(url).document |         val res = app.get(url).document | ||||||
|         val script = res.selectFirst("script:containsData(window['show_storage'])")?.data() |         val script = res.selectFirst("script:containsData(window['show_storage'])")?.data() | ||||||
|         val hash = Regex("hash:\\s*['\"](\\S+)['\"],").find(script ?: return)?.groupValues?.get(1) |         val hash = Regex("hash:\\s*['\"](\\S+)['\"],").find(script ?: return)?.groupValues?.get(1) | ||||||
|         val expires = Regex("expires:\\s*(\\d+),").find(script ?: return)?.groupValues?.get(1) |         val expires = Regex("expires:\\s*(\\d+),").find(script)?.groupValues?.get(1) | ||||||
| 
 | 
 | ||||||
|         val videoUrl = if (season == null) { |         val videoUrl = if (season == null) { | ||||||
|             "$monsterMainUrl/api/v1/security/movie-access?id_movie=$episodeId&hash=$hash&expires=$expires" |             "$monsterMainUrl/api/v1/security/movie-access?id_movie=$episodeId&hash=$hash&expires=$expires" | ||||||
|  | @ -3051,71 +2957,6 @@ data class Load( | ||||||
|     @JsonProperty("data") val data: MediaDetail? = null, |     @JsonProperty("data") val data: MediaDetail? = null, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| data class ConsumetHeaders( |  | ||||||
|     @JsonProperty("Referer") val referer: String? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class ConsumetSubtitles( |  | ||||||
|     @JsonProperty("url") val url: String? = null, |  | ||||||
|     @JsonProperty("lang") val lang: String? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class ConsumetSources( |  | ||||||
|     @JsonProperty("url") val url: String? = null, |  | ||||||
|     @JsonProperty("quality") val quality: String? = null, |  | ||||||
|     @JsonProperty("isM3U8") val isM3U8: Boolean? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class ConsumetSourcesResponse( |  | ||||||
|     @JsonProperty("headers") val headers: ConsumetHeaders? = null, |  | ||||||
|     @JsonProperty("sources") val sources: ArrayList<ConsumetSources>? = arrayListOf(), |  | ||||||
|     @JsonProperty("subtitles") val subtitles: ArrayList<ConsumetSubtitles>? = arrayListOf(), |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class ConsumetEpisodes( |  | ||||||
|     @JsonProperty("id") val id: String? = null, |  | ||||||
|     @JsonProperty("type") val type: String? = null, |  | ||||||
|     @JsonProperty("title") val title: String? = null, |  | ||||||
|     @JsonProperty("number") val number: Int? = null, |  | ||||||
|     @JsonProperty("season") val season: Int? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class ConsumetTitle( |  | ||||||
|     @JsonProperty("romaji") val romaji: String? = null, |  | ||||||
|     @JsonProperty("english") val english: String? = null |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class ConsumetDetails( |  | ||||||
|     @JsonProperty("episodes") val episodes: ArrayList<ConsumetEpisodes>? = arrayListOf(), |  | ||||||
|     @JsonProperty("image") val image: String? = null, |  | ||||||
|     @JsonProperty("cover") val cover: String? = null, |  | ||||||
|     @JsonProperty("title") val title: ConsumetTitle? = null, |  | ||||||
|     @JsonProperty("releaseDate") val releaseDate: Int? = null |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class CrunchyrollEpisodes( |  | ||||||
|     @JsonProperty("id") val id: String? = null, |  | ||||||
|     @JsonProperty("title") val title: String? = null, |  | ||||||
|     @JsonProperty("episode_number") val episode_number: Int? = null, |  | ||||||
|     @JsonProperty("season_number") val season_number: Int? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class CrunchyrollDetails( |  | ||||||
|     @JsonProperty("episodes") val episodes: HashMap<String, List<CrunchyrollEpisodes>>? = hashMapOf(), |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class ConsumetResults( |  | ||||||
|     @JsonProperty("id") val id: String? = null, |  | ||||||
|     @JsonProperty("title") val title: String? = null, |  | ||||||
|     @JsonProperty("seasons") val seasons: Int? = null, |  | ||||||
|     @JsonProperty("releaseDate") val releaseDate: String? = null, |  | ||||||
|     @JsonProperty("type") val type: String? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class ConsumetSearchResponse( |  | ||||||
|     @JsonProperty("results") val results: ArrayList<ConsumetResults>? = arrayListOf(), |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class KisskhSources( | data class KisskhSources( | ||||||
|     @JsonProperty("Video") val video: String?, |     @JsonProperty("Video") val video: String?, | ||||||
|     @JsonProperty("ThirdParty") val thirdParty: String?, |     @JsonProperty("ThirdParty") val thirdParty: String?, | ||||||
|  | @ -3246,23 +3087,6 @@ data class SorastreamVideos( | ||||||
|     @JsonProperty("currentDefinition") val currentDefinition: String? = null, |     @JsonProperty("currentDefinition") val currentDefinition: String? = null, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| data class SapphireSubtitles( |  | ||||||
|     @JsonProperty("language") val language: String? = null, |  | ||||||
|     @JsonProperty("url") val url: String? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class SapphireStreams( |  | ||||||
|     @JsonProperty("format") val format: String? = null, |  | ||||||
|     @JsonProperty("audio_lang") val audio_lang: String? = null, |  | ||||||
|     @JsonProperty("hardsub_lang") val hardsub_lang: String? = null, |  | ||||||
|     @JsonProperty("url") val url: String? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class SapphireSources( |  | ||||||
|     @JsonProperty("streams") val streams: ArrayList<SapphireStreams>? = arrayListOf(), |  | ||||||
|     @JsonProperty("subtitles") val subtitles: ArrayList<SapphireSubtitles>? = arrayListOf(), |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class BiliBiliEpisodes( | data class BiliBiliEpisodes( | ||||||
|     @JsonProperty("id") val id: Int? = null, |     @JsonProperty("id") val id: Int? = null, | ||||||
|     @JsonProperty("sourceId") val sourceId: String? = null, |     @JsonProperty("sourceId") val sourceId: String? = null, | ||||||
|  | @ -3325,74 +3149,6 @@ data class PutlockerResponses( | ||||||
|     @JsonProperty("backupLink") val backupLink: String? = null, |     @JsonProperty("backupLink") val backupLink: String? = null, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| data class AllanimeStreams( |  | ||||||
|     @JsonProperty("format") val format: String? = null, |  | ||||||
|     @JsonProperty("url") val url: String? = null, |  | ||||||
|     @JsonProperty("audio_lang") val audio_lang: String? = null, |  | ||||||
|     @JsonProperty("hardsub_lang") val hardsub_lang: String? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimePortData( |  | ||||||
|     @JsonProperty("streams") val streams: ArrayList<AllanimeStreams>? = arrayListOf(), |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeLink( |  | ||||||
|     @JsonProperty("portData") val portData: AllanimePortData? = null, |  | ||||||
|     @JsonProperty("resolutionStr") val resolutionStr: String? = null, |  | ||||||
|     @JsonProperty("src") val src: String? = null, |  | ||||||
|     @JsonProperty("link") val link: String? = null, |  | ||||||
|     @JsonProperty("hls") val hls: Boolean? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeLinks( |  | ||||||
|     @JsonProperty("links") val links: ArrayList<AllanimeLink>? = arrayListOf(), |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeSourceUrls( |  | ||||||
|     @JsonProperty("sourceUrl") val sourceUrl: String? = null, |  | ||||||
|     @JsonProperty("sourceName") val sourceName: String? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeEpisode( |  | ||||||
|     @JsonProperty("sourceUrls") val sourceUrls: ArrayList<AllanimeSourceUrls>? = arrayListOf(), |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeAvailableEpisodesDetail( |  | ||||||
|     @JsonProperty("sub") val sub: ArrayList<String>? = arrayListOf(), |  | ||||||
|     @JsonProperty("dub") val dub: ArrayList<String>? = arrayListOf(), |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeDetailShow( |  | ||||||
|     @JsonProperty("availableEpisodesDetail") val availableEpisodesDetail: AllanimeAvailableEpisodesDetail? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeAiredStart( |  | ||||||
|     @JsonProperty("year") val year: Int? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeEdges( |  | ||||||
|     @JsonProperty("_id") val _id: String? = null, |  | ||||||
|     @JsonProperty("name") val name: String? = null, |  | ||||||
|     @JsonProperty("englishName") val englishName: String? = null, |  | ||||||
|     @JsonProperty("thumbnail") val thumbnail: String? = null, |  | ||||||
|     @JsonProperty("type") val type: String? = null, |  | ||||||
|     @JsonProperty("airedStart") val airedStart: AllanimeAiredStart? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeShows( |  | ||||||
|     @JsonProperty("edges") val edges: ArrayList<AllanimeEdges>? = arrayListOf(), |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeData( |  | ||||||
|     @JsonProperty("shows") val shows: AllanimeShows? = null, |  | ||||||
|     @JsonProperty("show") val show: AllanimeDetailShow? = null, |  | ||||||
|     @JsonProperty("episode") val episode: AllanimeEpisode? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class AllanimeResponses( |  | ||||||
|     @JsonProperty("data") val data: AllanimeData? = null, |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| data class ShivamhwSources( | data class ShivamhwSources( | ||||||
|     @JsonProperty("id") val id: String? = null, |     @JsonProperty("id") val id: String? = null, | ||||||
|     @JsonProperty("stream_link") val stream_link: String? = null, |     @JsonProperty("stream_link") val stream_link: String? = null, | ||||||
|  | @ -3448,4 +3204,74 @@ data class VizcloudData( | ||||||
| 
 | 
 | ||||||
| data class VizcloudResponses( | data class VizcloudResponses( | ||||||
|     @JsonProperty("data") val data: VizcloudData? = null, |     @JsonProperty("data") val data: VizcloudData? = null, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class AnilistExternalLinks( | ||||||
|  |     @JsonProperty("id") var id: Int? = null, | ||||||
|  |     @JsonProperty("site") var site: String? = null, | ||||||
|  |     @JsonProperty("url") var url: String? = null, | ||||||
|  |     @JsonProperty("type") var type: String? = null, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class AnilistMedia( | ||||||
|  |     @JsonProperty("externalLinks") var externalLinks: ArrayList<AnilistExternalLinks> = arrayListOf() | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class AnilistData( | ||||||
|  |     @JsonProperty("Media") var Media: AnilistMedia? = AnilistMedia() | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class AnilistResponses( | ||||||
|  |     @JsonProperty("data") var data: AnilistData? = AnilistData() | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class CrunchyrollToken( | ||||||
|  |     @JsonProperty("access_token") val accessToken: String? = null, | ||||||
|  |     @JsonProperty("expires_in") val expiresIn: Int? = null, | ||||||
|  |     @JsonProperty("token_type") val tokenType: String? = null, | ||||||
|  |     @JsonProperty("scope") val scope: String? = null, | ||||||
|  |     @JsonProperty("country") val country: String? = null | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class CrunchyrollVersions( | ||||||
|  |     @JsonProperty("audio_locale") val audio_locale: String? = null, | ||||||
|  |     @JsonProperty("guid") val guid: String? = null, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class CrunchyrollData( | ||||||
|  |     @JsonProperty("id") val id: String? = null, | ||||||
|  |     @JsonProperty("title") val title: String? = null, | ||||||
|  |     @JsonProperty("slug_title") val slug_title: String? = null, | ||||||
|  |     @JsonProperty("season_number") val season_number: Int? = null, | ||||||
|  |     @JsonProperty("episode_number") val episode_number: Int? = null, | ||||||
|  |     @JsonProperty("versions") val versions: ArrayList<CrunchyrollVersions>? = null, | ||||||
|  |     @JsonProperty("streams_link") val streams_link: String? = null, | ||||||
|  |     @JsonProperty("adaptive_hls") val adaptive_hls: HashMap<String, HashMap<String, String>>? = hashMapOf(), | ||||||
|  |     @JsonProperty("vo_adaptive_hls") val vo_adaptive_hls: HashMap<String, HashMap<String, String>>? = hashMapOf(), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class CrunchyrollResponses( | ||||||
|  |     @JsonProperty("data") val data: ArrayList<CrunchyrollData>? = arrayListOf(), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class CrunchyrollMeta( | ||||||
|  |     @JsonProperty("subtitles") val subtitles: HashMap<String, HashMap<String, String>>? = hashMapOf(), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class CrunchyrollSourcesResponses( | ||||||
|  |     @JsonProperty("data") val data: ArrayList<CrunchyrollData>? = arrayListOf(), | ||||||
|  |     @JsonProperty("meta") val meta: CrunchyrollMeta? = null, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class MALSyncPages( | ||||||
|  |     @JsonProperty("Zoro") val zoro: HashMap<String, HashMap<String, String>>? = hashMapOf(), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class MALSyncResponses( | ||||||
|  |     @JsonProperty("Pages") val pages: MALSyncPages? = null, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | data class ZoroResponses( | ||||||
|  |     @JsonProperty("html") val html: String? = null, | ||||||
|  |     @JsonProperty("link") val link: String? = null, | ||||||
| ) | ) | ||||||
|  | @ -6,7 +6,6 @@ import com.hexated.SoraExtractor.invokeAsk4Movies | ||||||
| import com.hexated.SoraExtractor.invokeBlackmovies | import com.hexated.SoraExtractor.invokeBlackmovies | ||||||
| import com.hexated.SoraExtractor.invokeBollyMaza | import com.hexated.SoraExtractor.invokeBollyMaza | ||||||
| import com.hexated.SoraExtractor.invokeCodexmovies | import com.hexated.SoraExtractor.invokeCodexmovies | ||||||
| import com.hexated.SoraExtractor.invokeCrunchyroll |  | ||||||
| import com.hexated.SoraExtractor.invokeCryMovies | import com.hexated.SoraExtractor.invokeCryMovies | ||||||
| import com.hexated.SoraExtractor.invokeDbgo | import com.hexated.SoraExtractor.invokeDbgo | ||||||
| import com.hexated.SoraExtractor.invokeFilmxy | import com.hexated.SoraExtractor.invokeFilmxy | ||||||
|  | @ -17,7 +16,6 @@ import com.hexated.SoraExtractor.invokeMovieHab | ||||||
| import com.hexated.SoraExtractor.invokeNoverse | import com.hexated.SoraExtractor.invokeNoverse | ||||||
| import com.hexated.SoraExtractor.invokeSeries9 | import com.hexated.SoraExtractor.invokeSeries9 | ||||||
| import com.hexated.SoraExtractor.invokeTwoEmbed | import com.hexated.SoraExtractor.invokeTwoEmbed | ||||||
| import com.hexated.SoraExtractor.invokeUniqueStream |  | ||||||
| import com.hexated.SoraExtractor.invokeVidSrc | import com.hexated.SoraExtractor.invokeVidSrc | ||||||
| import com.hexated.SoraExtractor.invokeXmovies | import com.hexated.SoraExtractor.invokeXmovies | ||||||
| import com.lagradost.cloudstream3.* | import com.lagradost.cloudstream3.* | ||||||
|  | @ -41,8 +39,6 @@ import com.hexated.SoraExtractor.invokeMoviesbay | ||||||
| import com.hexated.SoraExtractor.invokeMoviezAdd | import com.hexated.SoraExtractor.invokeMoviezAdd | ||||||
| import com.hexated.SoraExtractor.invokeNinetv | import com.hexated.SoraExtractor.invokeNinetv | ||||||
| import com.hexated.SoraExtractor.invokeNowTv | import com.hexated.SoraExtractor.invokeNowTv | ||||||
| import com.hexated.SoraExtractor.invokePapaonMovies1 |  | ||||||
| import com.hexated.SoraExtractor.invokePapaonMovies2 |  | ||||||
| import com.hexated.SoraExtractor.invokePutlocker | import com.hexated.SoraExtractor.invokePutlocker | ||||||
| import com.hexated.SoraExtractor.invokeRStream | import com.hexated.SoraExtractor.invokeRStream | ||||||
| import com.hexated.SoraExtractor.invokeRinzrymovies | import com.hexated.SoraExtractor.invokeRinzrymovies | ||||||
|  | @ -51,7 +47,6 @@ import com.hexated.SoraExtractor.invokeShinobiMovies | ||||||
| import com.hexated.SoraExtractor.invokeShivamhw | import com.hexated.SoraExtractor.invokeShivamhw | ||||||
| import com.hexated.SoraExtractor.invokeSmashyStream | import com.hexated.SoraExtractor.invokeSmashyStream | ||||||
| import com.hexated.SoraExtractor.invokeSoraStream | import com.hexated.SoraExtractor.invokeSoraStream | ||||||
| import com.hexated.SoraExtractor.invokeTgarMovies |  | ||||||
| import com.hexated.SoraExtractor.invokeTvMovies | import com.hexated.SoraExtractor.invokeTvMovies | ||||||
| import com.hexated.SoraExtractor.invokeUhdmovies | import com.hexated.SoraExtractor.invokeUhdmovies | ||||||
| import com.hexated.SoraExtractor.invokeVitoenMovies | import com.hexated.SoraExtractor.invokeVitoenMovies | ||||||
|  | @ -81,17 +76,14 @@ open class SoraStream : TmdbProvider() { | ||||||
|         private const val tmdbAPI = "https://api.themoviedb.org/3" |         private const val tmdbAPI = "https://api.themoviedb.org/3" | ||||||
|         const val tmdb2anilist = "https://tmdb2anilist.slidemovies.org" |         const val tmdb2anilist = "https://tmdb2anilist.slidemovies.org" | ||||||
|         const val gdbot = "https://gdtot.pro" |         const val gdbot = "https://gdtot.pro" | ||||||
|         const val consumetAnilistAPI = "https://api.consumet.org/meta/anilist" |  | ||||||
| 
 | 
 | ||||||
|         private val apiKey = |         private val apiKey = base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL | ||||||
|             base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL |  | ||||||
| 
 | 
 | ||||||
|         /** ALL SOURCES */ |         /** ALL SOURCES */ | ||||||
|         const val twoEmbedAPI = "https://www.2embed.to" |         const val twoEmbedAPI = "https://www.2embed.to" | ||||||
|         const val vidSrcAPI = "https://v2.vidsrc.me" |         const val vidSrcAPI = "https://v2.vidsrc.me" | ||||||
|         const val dbgoAPI = "https://dbgo.fun" |         const val dbgoAPI = "https://dbgo.fun" | ||||||
|         const val movieHabAPI = "https://moviehab.com" |         const val movieHabAPI = "https://moviehab.com" | ||||||
|         const val databaseGdriveAPI = "https://databasegdriveplayer.co" |  | ||||||
|         const val hdMovieBoxAPI = "https://hdmoviebox.net" |         const val hdMovieBoxAPI = "https://hdmoviebox.net" | ||||||
|         const val series9API = "https://series9.sh" |         const val series9API = "https://series9.sh" | ||||||
|         const val idlixAPI = "https://idlixian.com" |         const val idlixAPI = "https://idlixian.com" | ||||||
|  | @ -100,11 +92,11 @@ open class SoraStream : TmdbProvider() { | ||||||
|         const val filmxyAPI = "https://www.filmxy.vip" |         const val filmxyAPI = "https://www.filmxy.vip" | ||||||
|         const val kimcartoonAPI = "https://kimcartoon.li" |         const val kimcartoonAPI = "https://kimcartoon.li" | ||||||
|         const val xMovieAPI = "https://xemovies.to" |         const val xMovieAPI = "https://xemovies.to" | ||||||
|         const val consumetZoroAPI = "https://api.consumet.org/anime/zoro" |         const val zoroAPI = "https://sanji.to" | ||||||
|         const val allanimeAPI = "https://api.allanime.to" |         const val crunchyrollAPI = "https://beta-api.crunchyroll.com" | ||||||
|         const val kissKhAPI = "https://kisskh.co" |         const val kissKhAPI = "https://kisskh.co" | ||||||
|         const val lingAPI = "https://ling-online.net" |         const val lingAPI = "https://ling-online.net" | ||||||
|         const val uhdmoviesAPI = "https://uhdmovies.one" |         const val uhdmoviesAPI = "https://uhdmovies.bio" | ||||||
|         const val fwatayakoAPI = "https://5100.svetacdn.in" |         const val fwatayakoAPI = "https://5100.svetacdn.in" | ||||||
|         const val gMoviesAPI = "https://gdrivemovies.xyz" |         const val gMoviesAPI = "https://gdrivemovies.xyz" | ||||||
|         const val fdMoviesAPI = "https://freedrivemovie.lol" |         const val fdMoviesAPI = "https://freedrivemovie.lol" | ||||||
|  | @ -600,7 +592,7 @@ open class SoraStream : TmdbProvider() { | ||||||
|                 invokeMovie123Net(res.title, res.season, res.episode, subtitleCallback, callback) |                 invokeMovie123Net(res.title, res.season, res.episode, subtitleCallback, callback) | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 invokeSmashyStream(res.imdbId, res.season, res.episode, callback) |                 invokeSmashyStream(res.imdbId, res.season, res.episode, subtitleCallback, callback) | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 invokeWatchsomuch( |                 invokeWatchsomuch( | ||||||
|  |  | ||||||
|  | @ -167,7 +167,7 @@ class SoraStreamLite : SoraStream() { | ||||||
|                 invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) |                 invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 invokeSmashyStream(res.imdbId, res.season, res.episode, callback) |                 invokeSmashyStream(res.imdbId, res.season, res.episode, subtitleCallback, callback) | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 invokeXmovies( |                 invokeXmovies( | ||||||
|  |  | ||||||
|  | @ -2,10 +2,9 @@ package com.hexated | ||||||
| 
 | 
 | ||||||
| import android.util.Base64 | import android.util.Base64 | ||||||
| import com.fasterxml.jackson.annotation.JsonProperty | import com.fasterxml.jackson.annotation.JsonProperty | ||||||
| import com.hexated.SoraStream.Companion.allanimeAPI |  | ||||||
| import com.hexated.SoraStream.Companion.base64DecodeAPI | import com.hexated.SoraStream.Companion.base64DecodeAPI | ||||||
| import com.hexated.SoraStream.Companion.baymoviesAPI | import com.hexated.SoraStream.Companion.baymoviesAPI | ||||||
| import com.hexated.SoraStream.Companion.consumetCrunchyrollAPI | import com.hexated.SoraStream.Companion.crunchyrollAPI | ||||||
| import com.hexated.SoraStream.Companion.filmxyAPI | import com.hexated.SoraStream.Companion.filmxyAPI | ||||||
| import com.hexated.SoraStream.Companion.gdbot | import com.hexated.SoraStream.Companion.gdbot | ||||||
| import com.hexated.SoraStream.Companion.putlockerAPI | import com.hexated.SoraStream.Companion.putlockerAPI | ||||||
|  | @ -17,10 +16,12 @@ import com.lagradost.cloudstream3.* | ||||||
| import com.lagradost.cloudstream3.APIHolder.getCaptchaToken | import com.lagradost.cloudstream3.APIHolder.getCaptchaToken | ||||||
| import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall | import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall | ||||||
| import com.lagradost.cloudstream3.utils.* | import com.lagradost.cloudstream3.utils.* | ||||||
|  | import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||||
| import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson | import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson | ||||||
| import com.lagradost.cloudstream3.utils.Coroutines.ioSafe | import com.lagradost.cloudstream3.utils.Coroutines.ioSafe | ||||||
| import com.lagradost.nicehttp.NiceResponse | import com.lagradost.nicehttp.NiceResponse | ||||||
| import com.lagradost.nicehttp.RequestBodyTypes | import com.lagradost.nicehttp.RequestBodyTypes | ||||||
|  | import com.lagradost.nicehttp.requestCreator | ||||||
| import kotlinx.coroutines.delay | import kotlinx.coroutines.delay | ||||||
| import okhttp3.FormBody | import okhttp3.FormBody | ||||||
| import okhttp3.Headers | import okhttp3.Headers | ||||||
|  | @ -28,10 +29,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrl | ||||||
| import okhttp3.MediaType.Companion.toMediaTypeOrNull | import okhttp3.MediaType.Companion.toMediaTypeOrNull | ||||||
| import okhttp3.RequestBody.Companion.toRequestBody | import okhttp3.RequestBody.Companion.toRequestBody | ||||||
| import org.jsoup.nodes.Document | import org.jsoup.nodes.Document | ||||||
| import java.net.URI | import java.net.* | ||||||
| import java.net.URL |  | ||||||
| import java.net.URLDecoder |  | ||||||
| import java.net.URLEncoder |  | ||||||
| import java.nio.charset.StandardCharsets | import java.nio.charset.StandardCharsets | ||||||
| import java.security.MessageDigest | import java.security.MessageDigest | ||||||
| import java.security.SecureRandom | import java.security.SecureRandom | ||||||
|  | @ -45,56 +43,13 @@ import kotlin.math.min | ||||||
| val soraAPI = base64DecodeAPI("cA==YXA=cy8=Y20=di8=LnQ=b2s=a2w=bG8=aS4=YXA=ZS0=aWw=b2I=LW0=Z2E=Ly8=czo=dHA=aHQ=") | val soraAPI = base64DecodeAPI("cA==YXA=cy8=Y20=di8=LnQ=b2s=a2w=bG8=aS4=YXA=ZS0=aWw=b2I=LW0=Z2E=Ly8=czo=dHA=aHQ=") | ||||||
| val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=") | val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=") | ||||||
| val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | ||||||
| val kaguyaBaseUrl = "https://kaguya.app/" | const val kaguyaBaseUrl = "https://kaguya.app/" | ||||||
| val soraHeaders = mapOf( | val soraHeaders = mapOf( | ||||||
|     "lang" to "en", |     "lang" to "en", | ||||||
|     "versioncode" to "33", |     "versioncode" to "33", | ||||||
|     "clienttype" to "android_Official", |     "clienttype" to "android_Official", | ||||||
|     "deviceid" to getDeviceId(), |     "deviceid" to getDeviceId(), | ||||||
| ) | ) | ||||||
| val allanimeSearchQuery = """ |  | ||||||
|         query( |  | ||||||
|                 ${'$'}search: SearchInput |  | ||||||
|                 ${'$'}limit: Int |  | ||||||
|                 ${'$'}page: Int |  | ||||||
|                 ${'$'}translationType: VaildTranslationTypeEnumType |  | ||||||
|                 ${'$'}countryOrigin: VaildCountryOriginEnumType |  | ||||||
|             ) { |  | ||||||
|             shows( |  | ||||||
|                 search: ${'$'}search |  | ||||||
|                 limit: ${'$'}limit |  | ||||||
|                 page: ${'$'}page |  | ||||||
|                 translationType: ${'$'}translationType |  | ||||||
|                 countryOrigin: ${'$'}countryOrigin |  | ||||||
|             ) { |  | ||||||
|                 pageInfo { |  | ||||||
|                     total |  | ||||||
|                 } |  | ||||||
|                 edges { |  | ||||||
|                     _id |  | ||||||
|                     name |  | ||||||
|                     thumbnail |  | ||||||
|                     englishName |  | ||||||
|                     nativeName |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     """.trimIndent().trim() |  | ||||||
| val allanimeServerQuery = """ |  | ||||||
|         query( |  | ||||||
|                 ${'$'}showId: String!, |  | ||||||
|                 ${'$'}translationType: VaildTranslationTypeEnumType!, |  | ||||||
|                 ${'$'}episodeString: String! |  | ||||||
|             ) { |  | ||||||
|             episode( |  | ||||||
|                 showId: ${'$'}showId |  | ||||||
|                 translationType: ${'$'}translationType |  | ||||||
|                 episodeString: ${'$'}episodeString |  | ||||||
|             ) { |  | ||||||
|                 sourceUrls |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     """.trimIndent().trim() |  | ||||||
| val encodedIndex = arrayOf( | val encodedIndex = arrayOf( | ||||||
|     "GamMovies", |     "GamMovies", | ||||||
|     "JSMovies", |     "JSMovies", | ||||||
|  | @ -574,6 +529,45 @@ suspend fun invokeSmashyNflim( | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | suspend fun invokeSmashyRip( | ||||||
|  |     name: String, | ||||||
|  |     url: String, | ||||||
|  |     subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|  |     callback: (ExtractorLink) -> Unit, | ||||||
|  | ) { | ||||||
|  |     val script = app.get(url).document.selectFirst("script:containsData(player =)")?.data() ?: return | ||||||
|  | 
 | ||||||
|  |     val source = Regex("file:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1) | ||||||
|  |     val subtitle = Regex("subtitle:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1) | ||||||
|  | 
 | ||||||
|  |     source?.split(",")?.map { links -> | ||||||
|  |         val quality = Regex("\\[(\\d+)]").find(links)?.groupValues?.getOrNull(1)?.trim() | ||||||
|  |         val link = links.removePrefix("[$quality]").substringAfter("dev/").trim() | ||||||
|  |         callback.invoke( | ||||||
|  |             ExtractorLink( | ||||||
|  |                 "Smashy [$name]", | ||||||
|  |                 "Smashy [$name]", | ||||||
|  |                 link, | ||||||
|  |                 "", | ||||||
|  |                 quality?.toIntOrNull() ?: return@map, | ||||||
|  |                 isM3u8 = true, | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     subtitle?.replace("<br>", "")?.split(",")?.map { sub -> | ||||||
|  |         val lang = Regex("\\[(.*?)]").find(sub)?.groupValues?.getOrNull(1)?.trim() | ||||||
|  |         val link = sub.removePrefix("[$lang]") | ||||||
|  |         subtitleCallback.invoke( | ||||||
|  |             SubtitleFile( | ||||||
|  |                 lang.orEmpty().ifEmpty { return@map }, | ||||||
|  |                 link | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| suspend fun getSoraIdAndType(title: String?, year: Int?, season: Int?): Pair<String, String>? { | suspend fun getSoraIdAndType(title: String?, year: Int?, season: Int?): Pair<String, String>? { | ||||||
|     val doc = |     val doc = | ||||||
|         app.get("${base64DecodeAPI("b20=LmM=b2s=a2w=bG8=Ly8=czo=dHA=aHQ=")}/search?keyword=$title").document |         app.get("${base64DecodeAPI("b20=LmM=b2s=a2w=bG8=Ly8=czo=dHA=aHQ=")}/search?keyword=$title").document | ||||||
|  | @ -825,7 +819,7 @@ suspend fun getTvMoviesServer(url: String, season: Int?, episode: Int?): Pair<St | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| suspend fun getFilmxyCookies(imdbId: String? = null, season: Int? = null): FilmxyCookies? { | suspend fun getFilmxyCookies(imdbId: String? = null, season: Int? = null): FilmxyCookies { | ||||||
| 
 | 
 | ||||||
|     val url = if (season == null) { |     val url = if (season == null) { | ||||||
|         "${filmxyAPI}/movie/$imdbId" |         "${filmxyAPI}/movie/$imdbId" | ||||||
|  | @ -905,47 +899,68 @@ suspend fun searchWatchOnline( | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| suspend fun searchCrunchyrollAnimeId(title: String): String? { | //modified code from https://github.com/jmir1/aniyomi-extensions/blob/master/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/AccessTokenInterceptor.kt | ||||||
|     val res = app.get("${consumetCrunchyrollAPI}/search/$title", timeout = 600L) | fun getCrunchyrollToken(): Map<String, String> { | ||||||
|         .parsedSafe<ConsumetSearchResponse>()?.results |     val client = app.baseClient.newBuilder() | ||||||
|     return (if (res?.size == 1) { |         .proxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress("cr-unblocker.us.to", 1080))) | ||||||
|         res.firstOrNull() |         .build() | ||||||
|     } else { | 
 | ||||||
|         res?.find { |     Authenticator.setDefault(object : Authenticator() { | ||||||
|             (it.title?.contains( |         override fun getPasswordAuthentication(): PasswordAuthentication { | ||||||
|                 title, |             return PasswordAuthentication("crunblocker", "crunblocker".toCharArray()) | ||||||
|                 true |  | ||||||
|             ) == true || it.title.createSlug() |  | ||||||
|                 ?.contains("${title.createSlug()}", true) == true) && it.type.equals("series") |  | ||||||
|         } |         } | ||||||
|     })?.id |     }) | ||||||
|  | 
 | ||||||
|  |     val request = requestCreator( | ||||||
|  |         method = "POST", | ||||||
|  |         url = "$crunchyrollAPI/auth/v1/token", | ||||||
|  |         headers = mapOf( | ||||||
|  |             "User-Agent" to "Crunchyroll/3.26.1 Android/11 okhttp/4.9.2", | ||||||
|  |             "Content-Type" to "application/x-www-form-urlencoded", | ||||||
|  |             "Authorization" to "Basic ${BuildConfig.CRUNCHYROLL_BASIC_TOKEN}" | ||||||
|  |         ), | ||||||
|  |         data = mapOf( | ||||||
|  |             "refresh_token" to BuildConfig.CRUNCHYROLL_REFRESH_TOKEN, | ||||||
|  |             "grant_type" to "refresh_token", | ||||||
|  |             "scope" to "offline_access" | ||||||
|  |         ) | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     val response = tryParseJson<CrunchyrollToken>(client.newCall(request).execute().body.string()) | ||||||
|  |     return mapOf("Authorization" to "${response?.tokenType} ${response?.accessToken}") | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun CrunchyrollDetails.findCrunchyrollId( | suspend fun getCrunchyrollId(aniId: String?): String? { | ||||||
|     season: Int?, |     val query = """ | ||||||
|     episode: Int?, |         query media(${'$'}id: Int, ${'$'}type: MediaType, ${'$'}isAdult: Boolean) { | ||||||
|     epsTitle: String? |           Media(id: ${'$'}id, type: ${'$'}type, isAdult: ${'$'}isAdult) { | ||||||
| ): List<Pair<CrunchyrollEpisodes?, String?>?> { |             id | ||||||
|     val sub = this.episodes?.filterKeys { it.contains("subbed") } |             externalLinks { | ||||||
|         .matchingEpisode(epsTitle, season, episode) to "Raw" |               id | ||||||
|     val dub = this.episodes?.filterKeys { it.contains("English Dub") } |               site | ||||||
|         .matchingEpisode(epsTitle, season, episode) to "English Dub" |               url | ||||||
|     return listOf(sub, dub) |               type | ||||||
| } |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |     """.trimIndent().trim() | ||||||
| 
 | 
 | ||||||
| fun Map<String, List<CrunchyrollEpisodes>>?.matchingEpisode( |     val variables = mapOf( | ||||||
|     epsTitle: String?, |         "id" to aniId, | ||||||
|     season: Int?, |         "isAdult" to false, | ||||||
|     episode: Int? |         "type" to "ANIME", | ||||||
| ): CrunchyrollEpisodes? { |     ) | ||||||
|     return this?.mapNotNull { eps -> | 
 | ||||||
|         eps.value.find { |     val data = mapOf( | ||||||
|             (it.episode_number == episode && it.season_number == season) || it.title.equals( |         "query" to query, | ||||||
|                 epsTitle, |         "variables" to variables | ||||||
|                 true |     ).toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) | ||||||
|             ) | 
 | ||||||
|         } ?: eps.value.find { it.episode_number == episode } |     return app.post("https://graphql.anilist.co", requestBody = data) | ||||||
|     }?.firstOrNull() |         .parsedSafe<AnilistResponses>()?.data?.Media?.externalLinks?.find { it.site == "VRV" }?.url?.let { | ||||||
|  |             Regex("series/(\\w+)/?").find(it)?.groupValues?.get(1) | ||||||
|  |         } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| suspend fun extractPutlockerSources(url: String?): NiceResponse? { | suspend fun extractPutlockerSources(url: String?): NiceResponse? { | ||||||
|  | @ -1120,10 +1135,6 @@ fun String.decryptGomoviesJson(key: String = "123"): String { | ||||||
|     return sb.toString() |     return sb.toString() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun allanimeQueries(variables: String, query: String) : String { |  | ||||||
|     return "${allanimeAPI}/allanimeapi?variables=$variables&query=$query" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fun Headers.getGomoviesCookies(cookieKey: String = "set-cookie"): Map<String, String> { | fun Headers.getGomoviesCookies(cookieKey: String = "set-cookie"): Map<String, String> { | ||||||
|     val cookieList = |     val cookieList = | ||||||
|         this.filter { it.first.equals(cookieKey, ignoreCase = true) }.mapNotNull { |         this.filter { it.first.equals(cookieKey, ignoreCase = true) }.mapNotNull { | ||||||
|  | @ -1292,7 +1303,7 @@ private fun decryptVrf(input: String, key: String): String { | ||||||
|     val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) { |     val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) { | ||||||
|         input.replace("""==?$""".toRegex(), "") |         input.replace("""==?$""".toRegex(), "") | ||||||
|     } else input |     } else input | ||||||
|     if (t.length % 4 == 1 || t.contains("""[^+/0-9A-Za-z]""".toRegex())) throw Exception("bad input") |     if (t.length % 4 == 1 || t.contains("""[^+/\dA-Za-z]""".toRegex())) throw Exception("bad input") | ||||||
|     var i: Int |     var i: Int | ||||||
|     var r = "" |     var r = "" | ||||||
|     var e = 0 |     var e = 0 | ||||||
|  | @ -1359,10 +1370,6 @@ fun getBaseUrl(url: String): String { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun String.decodeBase64(): String { |  | ||||||
|     return Base64.decode(this, Base64.DEFAULT).toString(Charsets.UTF_8) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fun decode(input: String): String = URLDecoder.decode(input, "utf-8") | fun decode(input: String): String = URLDecoder.decode(input, "utf-8") | ||||||
| 
 | 
 | ||||||
| fun encode(input: String): String = URLEncoder.encode(input, "utf-8").replace("+", "%20") | fun encode(input: String): String = URLEncoder.encode(input, "utf-8").replace("+", "%20") | ||||||
|  | @ -1599,7 +1606,9 @@ object CryptoAES { | ||||||
| object RabbitStream { | object RabbitStream { | ||||||
| 
 | 
 | ||||||
|     suspend fun MainAPI.extractRabbitStream( |     suspend fun MainAPI.extractRabbitStream( | ||||||
|  |         server: String, | ||||||
|         url: String, |         url: String, | ||||||
|  |         ref: String, | ||||||
|         subtitleCallback: (SubtitleFile) -> Unit, |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|         callback: (ExtractorLink) -> Unit, |         callback: (ExtractorLink) -> Unit, | ||||||
|         useSidAuthentication: Boolean, |         useSidAuthentication: Boolean, | ||||||
|  | @ -1681,8 +1690,8 @@ object RabbitStream { | ||||||
|         list.forEach { subList -> |         list.forEach { subList -> | ||||||
|             subList.first?.forEach { source -> |             subList.first?.forEach { source -> | ||||||
|                 source?.toExtractorLink( |                 source?.toExtractorLink( | ||||||
|                     "Vidcloud", |                     server, | ||||||
|                     "$twoEmbedAPI/", |                     ref, | ||||||
|                     extractorData, |                     extractorData, | ||||||
|                 ) |                 ) | ||||||
|                     ?.forEach { |                     ?.forEach { | ||||||
|  | @ -1792,11 +1801,15 @@ object RabbitStream { | ||||||
|         return code.reversed() |         return code.reversed() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     suspend fun getKey(): String? { |     suspend fun getKey(): String { | ||||||
|         return app.get("https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt") |         return app.get("https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt") | ||||||
|             .text |             .text | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     suspend fun getZoroKey(): String { | ||||||
|  |         return app.get("https://raw.githubusercontent.com/enimax-anime/key/e6/key.txt").text | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private inline fun <reified T> decryptMapped(input: String, key: String): T? { |     private inline fun <reified T> decryptMapped(input: String, key: String): T? { | ||||||
|         return tryParseJson(decrypt(input, key)) |         return tryParseJson(decrypt(input, key)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue