diff --git a/.github/site-list.py b/.github/site-list.py deleted file mode 100644 index 362aa157..00000000 --- a/.github/site-list.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/python3 - -from glob import glob -from re import findall, compile, DOTALL -from json import dump, load -from typing import List, Dict - -# Globals -JSON_PATH = "docs/providers.json" -GLOB_ANIME = "app/src/main/java/com/lagradost/cloudstream3/animeproviders/*Provider.kt" -GLOB_MOVIE = "app/src/main/java/com/lagradost/cloudstream3/movieproviders/*Provider.kt" -URL_REGEX = compile("override\sva[lr]\smainUrl[^\"']+[\"'](https?://[a-zA-Z0-9\.-/]+)[\"']") -FILENAME_REGEX = compile("([A-Za-z0-9]+)(?:.kt)$") -PROVIDER_CLASSNAME_REGEX = compile("(?<=class\s)([a-zA-Z]+)(?=\s:\sMainAPI\(\))") -NAME_REGEX = compile("override\sva[lr]\sname[^\"']+[\"']([a-zA-Z-.\s]+)") -LANG_REGEX = compile("override\sva[lr]\slang[^\"']+[\"']([a-zA-Z]+)") - -old_sites: Dict[str, Dict] = load(open(JSON_PATH, "r", encoding="utf-8")) -sites: Dict[str, Dict] = {} - -animelist = glob(GLOB_ANIME) -movielist = glob(GLOB_MOVIE) -allProvidersList = animelist + movielist - -# parse all *Provider.kt files -for path in allProvidersList: - with open(path, "r", encoding='utf-8') as file: - try: - site_text: str = file.read() - filename: str = findall(FILENAME_REGEX, path)[0] - name: str = [*findall(PROVIDER_CLASSNAME_REGEX, site_text), filename][0] - provider_url: str = [*findall(URL_REGEX, site_text), ""][0] - lang: str = [*findall(LANG_REGEX, site_text), "en"][0] - - if name in old_sites.keys(): # if already in previous list use old status and name - sites[name] = { - "name": old_sites[name]['name'], - "url": provider_url if provider_url else old_sites[name]['url'], - "status": old_sites[name]['status'], - "language": lang - } - else: # if not in previous list add with new data - display_name: str = [*findall(NAME_REGEX, site_text), name][0] - if display_name.endswith("Provider"): - display_name = display_name[:-len("Provider")] - sites[name] = { - "name": display_name, - "url": provider_url if provider_url else "", - "status": 1, - "language": lang - } - - except Exception as ex: - print("Error => {0}: {1}".format(path, ex)) - -# add sites from old_sites that are missing in new list -for name in old_sites.keys(): - if name not in sites.keys(): - sites[name] = { - "name": old_sites[name]['name'], - "url": old_sites[name]['url'], - "status": old_sites[name]['status'] - } - -dump(sites, open(JSON_PATH, "w+", encoding="utf-8"), indent=4, sort_keys=True) diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 94207a1c..19df6708 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -31,25 +31,34 @@ jobs: run: | COMMIT_HASH="$(git log -1 --format='%H')" sed -i "s/unknown_prerelease<\/string>/$COMMIT_HASH<\/string>/g" app/src/main/res/values/strings.xml - - name: Decode Keystore - env: - ENCODED_STRING: ${{ secrets.KEYSTORE }} - run: | - TMP_KEYSTORE_FILE_PATH="${RUNNER_TEMP}"/keystore - mkdir -p "${TMP_KEYSTORE_FILE_PATH}" - echo $ENCODED_STRING | base64 -di > "${TMP_KEYSTORE_FILE_PATH}"/prerelease_keystore.keystore + # - name: Decode Keystore + # env: + # ENCODED_STRING: ${{ secrets.KEYSTORE }} + # run: | + # TMP_KEYSTORE_FILE_PATH="${RUNNER_TEMP}"/keystore + # mkdir -p "${TMP_KEYSTORE_FILE_PATH}" + # echo $ENCODED_STRING | base64 -di > "${TMP_KEYSTORE_FILE_PATH}"/prerelease_keystore.keystore - name: Run Gradle - run: ./gradlew app:assemblePrerelease - env: - SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }} - SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }} - SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }} - #- name: Create pre-release - # uses: "marvinpinto/action-automatic-releases@latest" - # with: - # repo_token: "${{ secrets.GITHUB_TOKEN }}" - # automatic_release_tag: "pre-release" - # prerelease: true - # title: "Pre-release Build" - # files: | - # app/build/outputs/apk/prerelease/*.apk + run: | + ./gradlew assembleDebug + ./gradlew androidSourcesJar + ./gradlew makeJar + # env: + # SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }} + # SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }} + # SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }} + - name: Display tree + run: | + sudo apt install tree -y 2>&1 > /dev/null + tree app/build + - name: Create pre-release + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "pre-release" + prerelease: true + title: "Pre-release Build" + files: | + app/build/outputs/apk/debug/*.apk + app/build/libs/app-sources.jar + app/build/classes.jar diff --git a/.github/workflows/site_list.yml b/.github/workflows/site_list.yml deleted file mode 100644 index ab2d30dd..00000000 --- a/.github/workflows/site_list.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Create site list - -on: - push: - branches: [ master ] - paths: - - 'app/src/main/java/com/lagradost/cloudstream3/*providers/*Provider.kt' - - '.github/workflows/site_list.yml' - - '.github/site-list.py' - - 'docs/providers.json' - - 'app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt' - -concurrency: - group: "site-list" - cancel-in-progress: true - -jobs: - create: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Edit providers.json - run: | - python3 .github/site-list.py - - name: Commit to the repo - run: | - git config user.name "GitHub Actions" - git config user.email "actions@github.com" - git add . - # "echo" returns true so the build succeeds, even if no changed files - git commit -m 'chore(docs): update list of sites' || echo - git push diff --git a/app/build.gradle b/app/build.gradle index 1ffdc812..875361ed 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,22 +51,22 @@ android { } buildTypes { - release { - debuggable false - minifyEnabled false - shrinkResources false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - prerelease { - applicationIdSuffix ".prerelease" - buildConfigField("boolean", "BETA", "true") - signingConfig signingConfigs.prerelease - versionNameSuffix '-PRE' - debuggable false - minifyEnabled false - shrinkResources false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } + // release { + // debuggable false + // minifyEnabled false + // shrinkResources false + // proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + // } + // prerelease { + // applicationIdSuffix ".prerelease" + // buildConfigField("boolean", "BETA", "true") + // signingConfig signingConfigs.prerelease + // versionNameSuffix '-PRE' + // debuggable false + // minifyEnabled false + // shrinkResources false + // proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + // } debug { debuggable true applicationIdSuffix ".debug" @@ -83,6 +83,10 @@ android { jvmTarget = '1.8' freeCompilerArgs = ['-Xjvm-default=compatibility'] } + lintOptions { + checkReleaseBuilds false + abortOnError false + } } repositories { @@ -183,4 +187,16 @@ dependencies { implementation 'com.github.TeamNewPipe:NewPipeExtractor:master-SNAPSHOT' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' +} + +task androidSourcesJar(type: Jar) { + getArchiveClassifier().set('sources') + from android.sourceSets.main.java.srcDirs//full sources +} + +task makeJar(type: Copy) { + from('build/intermediates/compile_app_classes_jar/debug') // after modifying here, you can export. Jar + into('build') // output location + include('classes.jar') // the classes file of the imported rack package + dependsOn build } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a5b6182c..14ab2c8d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + @@ -75,110 +76,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 7832aa3b..ca1eb3b6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -10,10 +10,6 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.module.kotlin.KotlinModule -import com.lagradost.cloudstream3.animeproviders.* -import com.lagradost.cloudstream3.liveproviders.EjaTv -import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider -import com.lagradost.cloudstream3.movieproviders.* import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi @@ -27,6 +23,7 @@ import okhttp3.Interceptor import java.text.SimpleDateFormat import java.util.* import kotlin.math.absoluteValue +import kotlin.collections.MutableList const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" @@ -43,122 +40,7 @@ object APIHolder { private const val defProvider = 0 - val allProviders = - arrayListOf( - // Movie providers - ElifilmsProvider(), - EstrenosDoramasProvider(), - PelisplusProvider(), - PelisplusHDProvider(), - PeliSmartProvider(), - MeloMovieProvider(), // Captcha for links - DoramasYTProvider(), - CinecalidadProvider(), - CuevanaProvider(), - EntrepeliculasyseriesProvider(), - PelisflixProvider(), - SeriesflixProvider(), - IHaveNoTvProvider(), // Documentaries provider - VMoveeProvider(), - AllMoviesForYouProvider(), - VidEmbedProvider(), - VfFilmProvider(), - VfSerieProvider(), - FrenchStreamProvider(), - AsianLoadProvider(), - AsiaFlixProvider(), // This should be removed in favor of asianembed.io, same source - EjaTv(), - BflixProvider(), - FmoviesToProvider(), - SflixProProvider(), - FilmanProvider(), - SflixProvider(), - DopeboxProvider(), - SolarmovieProvider(), - PinoyMoviePediaProvider(), - PinoyHDXyzProvider(), - PinoyMoviesEsProvider(), - TrailersTwoProvider(), - TwoEmbedProvider(), - DramaSeeProvider(), - WatchAsianProvider(), - DramaidProvider(), - KdramaHoodProvider(), - AkwamProvider(), - MyCimaProvider(), - CimaNowProvider(), - EgyBestProvider(), - FaselHDProvider(), - SoaptwoDayProvider(), - HDMProvider(),// disabled due to cloudflare - TheFlixToProvider(), - StreamingcommunityProvider(), - TantifilmProvider(), - CineblogProvider(), - IlGenioDelloStreamingProvider(), - AltadefinizioneProvider(), - FilmpertuttiProvider(), - HDMovie5(), - RebahinProvider(), - LayarKacaProvider(), - HDTodayProvider(), - OpenVidsProvider(), - IdlixProvider(), - MultiplexProvider(), - VidSrcProvider(), - UakinoProvider(), - PhimmoichillProvider(), - HDrezkaProvider(), - YomoviesProvider(), - DubokuProvider(), - KisskhProvider(), - - // Metadata providers - //TmdbProvider(), - CrossTmdbProvider(), - - // Anime providers - WatchCartoonOnlineProvider(), - GogoanimeProvider(), - AllAnimeProvider(), - AnimekisaProvider(), - //ShiroProvider(), // v2 fucked me - AnimeFlickProvider(), - AnimeflvnetProvider(), - AnimefenixProvider(), - AnimeflvIOProvider(), - JKAnimeProvider(), - TenshiProvider(), - WcoProvider(), - AnimePaheProvider(), - NineAnimeProvider(), - AnimeWorldProvider(), - AnimeSaturnProvider(), - AniPlayProvider(), - ZoroProvider(), - DubbedAnimeProvider(), - MonoschinosProvider(), - MundoDonghuaProvider(), - KawaiifuProvider(), // disabled due to cloudflare - NeonimeProvider(), - KuramanimeProvider(), - OploverzProvider(), - GomunimeProvider(), - NontonAnimeIDProvider(), - KuronimeProvider(), - OtakudesuProvider(), - AnimeIndoProvider(), - AnimeSailProvider(), - TocanimeProvider(), - WcofunProvider(), - //MultiAnimeProvider(), - NginxProvider(), - OlgplyProvider(), - AniflixProvider(), - KimCartoonProvider(), - XcineProvider(), - SuperStream() - ) + val allProviders: MutableList = arrayListOf() fun initAll() { diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 12a39d18..ca3edbdf 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -10,6 +10,7 @@ import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.view.WindowManager +import android.widget.Toast import androidx.annotation.IdRes import androidx.appcompat.app.AppCompatActivity import androidx.core.view.isVisible @@ -39,7 +40,6 @@ import com.lagradost.cloudstream3.CommonActivity.onUserLeaveHint import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.updateLocale import com.lagradost.cloudstream3.mvvm.logError -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.network.initClient import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.OAuth2Apis @@ -52,16 +52,12 @@ import com.lagradost.cloudstream3.ui.result.ResultFragment import com.lagradost.cloudstream3.ui.search.SearchResultBuilder import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings -import com.lagradost.cloudstream3.ui.settings.SettingsGeneral import com.lagradost.cloudstream3.ui.setup.HAS_DONE_SETUP_KEY import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadResult -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup import com.lagradost.cloudstream3.utils.Coroutines.ioSafe -import com.lagradost.cloudstream3.utils.Coroutines.main -import com.lagradost.cloudstream3.utils.DataStore import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.utils.DataStore.setKey @@ -76,18 +72,17 @@ import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.requestRW -import com.lagradost.cloudstream3.utils.USER_PROVIDER_API import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.ResponseParser import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_result_swipe.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import org.schabi.newpipe.extractor.NewPipe import java.io.File import kotlin.concurrent.thread import kotlin.reflect.KClass +import com.lagradost.cloudstream3.plugins.PluginManager +import com.lagradost.cloudstream3.plugins.RepositoryManager +import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData +import com.lagradost.cloudstream3.utils.Coroutines.main const val VLC_PACKAGE = "org.videolan.vlc" @@ -333,7 +328,26 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { val str = intent.dataString loadCache() if (str != null) { - if (str.contains(appString)) { + if (str.startsWith("https://cs.repo")) { + val realUrl = "https://" + str.substringAfter("?") + println("Repository url: $realUrl") + ioSafe { + val repo = RepositoryManager.parseRepository(realUrl) ?: return@ioSafe + RepositoryManager.addRepository( + RepositoryData( + repo.name, + realUrl + ) + ) + main { + showToast( + this, + this.getString(R.string.player_loaded_subtitles, repo.name), + Toast.LENGTH_LONG + ) + } + } + } else if (str.contains(appString)) { for (api in OAuth2Apis) { if (str.contains("/${api.redirectUrl}")) { val activity = this @@ -402,66 +416,22 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } } - fun test() { - /*thread { - val youtubeLink = "https://www.youtube.com/watch?v=Zxem9rqJ5S0" - - val url = YoutubeStreamLinkHandlerFactory.getInstance().fromUrl(youtubeLink) - println("ID:::: ${url.id}") - NewPipe.init(DownloaderTestImpl.getInstance()) - val service = ServiceList.YouTube - val s = object : YoutubeStreamExtractor( - service, - url - ) { - - } - s.fetchPage() - val streams = s.videoStreams - println("STREAMS: ${streams.map { "url = "+ it.url + " extra= " + it.height + "|" + it.isVideoOnly + "\n" }}") - }*/ - /* - runBlocking { - - val query = """ - query { - searchShows(search: "spider", limit: 10) { - id - name - originalName - } - } - """ - val data = - mapOf( - "query" to query, - //"variables" to - // mapOf( - // "name" to name, - // ).toJson() - ) - val txt = app.post( - "http://api.anime-skip.com/graphql", - headers = mapOf( - "X-Client-ID" to "", - "Content-Type" to "application/json", - "Accept" to "application/json", - ), - json = data - ) - println("TEXT: $txt") - }*/ - /*runBlocking { - //https://test.api.anime-skip.com/graphiql - val txt = app.get( - "https://api.anime-skip.com/status", - headers = mapOf("X-Client-ID" to "") - ) - println("TEXT: $txt") - }*/ - } - override fun onCreate(savedInstanceState: Bundle?) { + app.initClient(this) + + PluginManager.loadAllLocalPlugins(applicationContext) + PluginManager.loadAllOnlinePlugins(applicationContext) + +// ioSafe { +// val plugins = +// RepositoryParser.getRepoPlugins("https://raw.githubusercontent.com/recloudstream/TestPlugin/master/repo.json") +// ?: emptyList() +// plugins.map { +// println("Load plugin: ${it.name} ${it.url}") +// RepositoryParser.loadSiteTemp(applicationContext, it.url, it.name) +// } +// } + // init accounts for (api in accountManagers) { api.init() @@ -480,118 +450,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { SearchResultBuilder.updateCache(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) - val downloadFromGithub = try { - settingsManager.getBoolean(getString(R.string.killswitch_key), true) - } catch (e: Exception) { - logError(e) - false - } - - // must give benenes to get beta providers - val hasBenene = try { - val count = settingsManager.getInt(getString(R.string.benene_count), 0) - count > 30 - } catch (e: Exception) { - e.printStackTrace() - false - } - - try { - getKey>(USER_PROVIDER_API)?.let { list -> - list.forEach { custom -> - allProviders.firstOrNull { it.javaClass.simpleName == custom.parentJavaClass } - ?.let { - allProviders.add(it.javaClass.newInstance().apply { - name = custom.name - lang = custom.lang - mainUrl = custom.url.trimEnd('/') - canBeOverridden = false - }) - } - } - } - apis = allProviders - APIHolder.apiMap = null - } catch (e: Exception) { - logError(e) - } - - // this pulls the latest data so ppl don't have to update to simply change provider url - if (downloadFromGithub) { - try { - runBlocking { - withContext(Dispatchers.IO) { - try { - val cacheStr: String? = getKey(PROVIDER_STATUS_KEY) - val cache: HashMap? = - cacheStr?.let { tryParseJson(cacheStr) } - if (cache != null) { - // if cache is found then spin up a new request, but dont wait - main { - try { - val txt = app.get(PROVIDER_STATUS_URL).text - val newCache = - tryParseJson>(txt) - setKey(PROVIDER_STATUS_KEY, txt) - MainAPI.overrideData = newCache // update all new providers - - initAll() - for (api in apis) { // update current providers - newCache?.get(api.javaClass.simpleName) - ?.let { data -> - api.overrideWithNewData(data) - } - } - } catch (e: Exception) { - logError(e) - } - } - cache - } else { - // if it is the first time the user has used the app then wait for a request to update all providers - val txt = app.get(PROVIDER_STATUS_URL).text - setKey(PROVIDER_STATUS_KEY, txt) - val newCache = tryParseJson>(txt) - newCache - }?.let { providersJsonMap -> - MainAPI.overrideData = providersJsonMap - initAll() - val acceptableProviders = - providersJsonMap.filter { it.value.status == PROVIDER_STATUS_OK || it.value.status == PROVIDER_STATUS_SLOW } - .map { it.key }.toSet() - - val restrictedApis = - if (hasBenene) providersJsonMap.filter { it.value.status == PROVIDER_STATUS_BETA_ONLY } - .map { it.key }.toSet() else emptySet() - - apis = allProviders.filter { api -> - val name = api.javaClass.simpleName - // if the provider does not exist in the json file, then it is shown by default - !providersJsonMap.containsKey(name) || acceptableProviders.contains( - name - ) || restrictedApis.contains(name) - } - } - } catch (e: Exception) { - logError(e) - } - } - } - } catch (e: Exception) { - initAll() - apis = allProviders - e.printStackTrace() - logError(e) - } - } else { - initAll() - apis = allProviders - } + initAll() + apis = allProviders loadThemes(this) updateLocale() - app.initClient(this) super.onCreate(savedInstanceState) try { if (isCastApiAvailable()) { @@ -649,7 +513,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } loadCache() - test() updateHasTrailers() /*nav_view.setOnNavigationItemSelectedListener { item -> when (item.itemId) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt deleted file mode 100644 index 836b5f10..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt +++ /dev/null @@ -1,404 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.Qualities -import org.jsoup.Jsoup -import org.mozilla.javascript.Context -import org.mozilla.javascript.Scriptable -import java.net.URI -import java.net.URLDecoder - - -class AllAnimeProvider : MainAPI() { - override var mainUrl = "https://allanime.site" - override var name = "AllAnime" - override val hasQuickSearch = false - override val hasMainPage = true - - private fun getStatus(t: String): ShowStatus { - return when (t) { - "Finished" -> ShowStatus.Completed - "Releasing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - - override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie) - - private data class Data( - @JsonProperty("shows") val shows: Shows - ) - - private data class Shows( - @JsonProperty("pageInfo") val pageInfo: PageInfo, - @JsonProperty("edges") val edges: List, - @JsonProperty("__typename") val _typename: String - ) - - private data class Edges( - @JsonProperty("_id") val Id: String?, - @JsonProperty("name") val name: String, - @JsonProperty("englishName") val englishName: String?, - @JsonProperty("nativeName") val nativeName: String?, - @JsonProperty("thumbnail") val thumbnail: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("season") val season: Season?, - @JsonProperty("score") val score: Double?, - @JsonProperty("airedStart") val airedStart: AiredStart?, - @JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes?, - @JsonProperty("availableEpisodesDetail") val availableEpisodesDetail: AvailableEpisodesDetail?, - @JsonProperty("studios") val studios: List?, - @JsonProperty("description") val description: String?, - @JsonProperty("status") val status: String?, - ) - - private data class AvailableEpisodes( - @JsonProperty("sub") val sub: Int, - @JsonProperty("dub") val dub: Int, - @JsonProperty("raw") val raw: Int - ) - - private data class AiredStart( - @JsonProperty("year") val year: Int, - @JsonProperty("month") val month: Int, - @JsonProperty("date") val date: Int - ) - - private data class Season( - @JsonProperty("quarter") val quarter: String, - @JsonProperty("year") val year: Int - ) - - private data class PageInfo( - @JsonProperty("total") val total: Int, - @JsonProperty("__typename") val _typename: String - ) - - private data class AllAnimeQuery( - @JsonProperty("data") val data: Data - ) - - data class RandomMain( - @JsonProperty("data") var data: DataRan? = DataRan() - ) - - data class DataRan( - @JsonProperty("queryRandomRecommendation") var queryRandomRecommendation: ArrayList = arrayListOf() - ) - - data class QueryRandomRecommendation( - @JsonProperty("_id") val Id: String? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("englishName") val englishName: String? = null, - @JsonProperty("nativeName") val nativeName: String? = null, - @JsonProperty("thumbnail") val thumbnail: String? = null, - @JsonProperty("airedStart") val airedStart: String? = null, - @JsonProperty("availableChapters") val availableChapters: String? = null, - @JsonProperty("availableEpisodes") val availableEpisodes: String? = null, - @JsonProperty("__typename") val _typename: String? = null - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val urls = listOf( -// Pair( -// "Top Anime", -// """$mainUrl/graphql?variables={"type":"anime","size":30,"dateRange":30}&extensions={"persistedQuery":{"version":1,"sha256Hash":"276d52ba09ca48ce2b8beb3affb26d9d673b22f9d1fd4892aaa39524128bc745"}}""" -// ), - // "countryOrigin":"JP" for Japanese only - Pair( - "Recently updated", - """$mainUrl/graphql?variables={"search":{"allowAdult":false,"allowUnknown":false},"limit":30,"page":1,"translationType":"dub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"d2670e3e27ee109630991152c8484fce5ff5e280c523378001f9a23dc1839068"}}""" - ), - ) - - val random = - """$mainUrl/graphql?variables={"format":"anime"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"21ac672633498a3698e8f6a93ce6c2b3722b29a216dcca93363bf012c360cd54"}}""" - val ranlink = app.get(random).text - val jsonran = parseJson(ranlink) - val ranhome = jsonran.data?.queryRandomRecommendation?.map { - newAnimeSearchResponse(it.name!!, "$mainUrl/anime/${it.Id}", fix = false) { - this.posterUrl = it.thumbnail - this.otherName = it.nativeName - } - } - - items.add(HomePageList("Random", ranhome!!)) - - urls.apmap { (HomeName, url) -> - val test = app.get(url).text - val json = parseJson(test) - val home = ArrayList() - val results = json.data.shows.edges.filter { - // filtering in case there is an anime with 0 episodes available on the site. - !(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) - } - results.map { - home.add( - newAnimeSearchResponse(it.name, "$mainUrl/anime/${it.Id}", fix = false) { - this.posterUrl = it.thumbnail - this.year = it.airedStart?.year - this.otherName = it.englishName - addDub(it.availableEpisodes?.dub) - addSub(it.availableEpisodes?.sub) - }) - } - items.add(HomePageList(HomeName, home)) - } - - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - override suspend fun search(query: String): List { - val link = - """ $mainUrl/graphql?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"dub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"d2670e3e27ee109630991152c8484fce5ff5e280c523378001f9a23dc1839068"}}""" - var res = app.get(link).text - if (res.contains("PERSISTED_QUERY_NOT_FOUND")) { - res = app.get(link).text - if (res.contains("PERSISTED_QUERY_NOT_FOUND")) return emptyList() - } - val response = parseJson(res) - - val results = response.data.shows.edges.filter { - // filtering in case there is an anime with 0 episodes available on the site. - !(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) - } - - return results.map { - newAnimeSearchResponse(it.name, "$mainUrl/anime/${it.Id}", fix = false) { - this.posterUrl = it.thumbnail - this.year = it.airedStart?.year - this.otherName = it.englishName - addDub(it.availableEpisodes?.dub) - addSub(it.availableEpisodes?.sub) - } - } - } - - private data class AvailableEpisodesDetail( - @JsonProperty("sub") val sub: List, - @JsonProperty("dub") val dub: List, - @JsonProperty("raw") val raw: List - ) - - - override suspend fun load(url: String): LoadResponse? { - val rhino = Context.enter() - rhino.initStandardObjects() - rhino.optimizationLevel = -1 - val scope: Scriptable = rhino.initStandardObjects() - - val html = app.get(url).text - val soup = Jsoup.parse(html) - - val script = soup.select("script").firstOrNull { - it.html().contains("window.__NUXT__") - } ?: return null - - val js = """ - const window = {} - ${script.html()} - const returnValue = JSON.stringify(window.__NUXT__.fetch[0].show) - """.trimIndent() - - rhino.evaluateString(scope, js, "JavaScript", 1, null) - val jsEval = scope.get("returnValue", scope) ?: return null - val showData = parseJson(jsEval as String) - - val title = showData.name - val description = showData.description - val poster = showData.thumbnail - - val episodes = showData.availableEpisodes.let { - if (it == null) return@let Pair(null, null) - Pair(if (it.sub != 0) ((1..it.sub).map { epNum -> - Episode( - "$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum - ) - }) else null, if (it.dub != 0) ((1..it.dub).map { epNum -> - Episode( - "$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum - ) - }) else null) - } - - val characters = soup.select("div.character > div.card-character-box").mapNotNull { - val img = it?.selectFirst("img")?.attr("src") ?: return@mapNotNull null - val name = it.selectFirst("div > a")?.ownText() ?: return@mapNotNull null - val role = when (it.selectFirst("div > .text-secondary")?.text()?.trim()) { - "Main" -> ActorRole.Main - "Supporting" -> ActorRole.Supporting - "Background" -> ActorRole.Background - else -> null - } - Pair(Actor(name, img), role) - } - - // bruh, they use graphql - //val recommendations = soup.select("#suggesction > div > div.p > .swipercard")?.mapNotNull { - // val recTitle = it?.selectFirst(".showname > a") ?: return@mapNotNull null - // val recName = recTitle.text() ?: return@mapNotNull null - // val href = fixUrlNull(recTitle.attr("href")) ?: return@mapNotNull null - // val img = it.selectFirst(".image > img").attr("src") ?: return@mapNotNull null - // AnimeSearchResponse(recName, href, this.name, TvType.Anime, img) - //} - - return newAnimeLoadResponse(title, url, TvType.Anime) { - posterUrl = poster - year = showData.airedStart?.year - - addEpisodes(DubStatus.Subbed, episodes.first) - addEpisodes(DubStatus.Dubbed, episodes.second) - addActors(characters) - //this.recommendations = recommendations - - showStatus = getStatus(showData.status.toString()) - - plot = description?.replace(Regex("""<(.*?)>"""), "") - } - } - - private val embedBlackList = listOf( - "https://mp4upload.com/", - "https://streamsb.net/", - "https://dood.to/", - "https://videobin.co/", - "https://ok.ru", - "https://streamlare.com", - ) - - private fun embedIsBlacklisted(url: String): Boolean { - embedBlackList.forEach { - if (it.javaClass.name == "kotlin.text.Regex") { - if ((it as Regex).matches(url)) { - return true - } - } else { - if (url.contains(it)) { - return true - } - } - } - return false - } - - private fun String.sanitize(): String { - var out = this - listOf(Pair("\\u002F", "/")).forEach { - out = out.replace(it.first, it.second) - } - return out - } - - private data class Links( - @JsonProperty("link") val link: String, - @JsonProperty("hls") val hls: Boolean?, - @JsonProperty("resolutionStr") val resolutionStr: String, - @JsonProperty("src") val src: String? - ) - - private data class AllAnimeVideoApiResponse( - @JsonProperty("links") val links: List - ) - - private data class ApiEndPoint( - @JsonProperty("episodeIframeHead") val episodeIframeHead: String - ) - - private suspend fun getM3u8Qualities( - m3u8Link: String, - referer: String, - qualityName: String, - ): List { - return M3u8Helper.generateM3u8( - this.name, - m3u8Link, - referer, - name = "${this.name} - $qualityName" - ) - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - var apiEndPoint = - parseJson(app.get("$mainUrl/getVersion").text).episodeIframeHead - if (apiEndPoint.endsWith("/")) apiEndPoint = - apiEndPoint.slice(0 until apiEndPoint.length - 1) - - val html = app.get(data).text - - val sources = Regex("""sourceUrl[:=]"(.+?)"""").findAll(html).toList() - .map { URLDecoder.decode(it.destructured.component1().sanitize(), "UTF-8") } - sources.apmap { - safeApiCall { - var link = it.replace(" ", "%20") - if (URI(link).isAbsolute || link.startsWith("//")) { - if (link.startsWith("//")) link = "https:$it" - - if (Regex("""streaming\.php\?""").matches(link)) { - // for now ignore - } else if (!embedIsBlacklisted(link)) { - if (URI(link).path.contains(".m3u")) { - getM3u8Qualities(link, data, URI(link).host).forEach(callback) - } else { - callback( - ExtractorLink( - "AllAnime - " + URI(link).host, - "", - link, - data, - Qualities.P1080.value, - false - ) - ) - } - } - } else { - link = apiEndPoint + URI(link).path + ".json?" + URI(link).query - val response = app.get(link) - - if (response.code < 400) { - val links = parseJson(response.text).links - links.forEach { server -> - if (server.hls != null && server.hls) { - getM3u8Qualities( - server.link, - "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI( - server.link - ).path), - server.resolutionStr - ).forEach(callback) - } else { - callback( - ExtractorLink( - "AllAnime - " + URI(server.link).host, - server.resolutionStr, - server.link, - "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI( - server.link - ).path), - Qualities.P1080.value, - false - ) - ) - } - } - } - } - } - } - return true - } - -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AniPlayProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AniPlayProvider.kt deleted file mode 100644 index 9edfa813..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AniPlayProvider.kt +++ /dev/null @@ -1,215 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.Qualities - -class AniPlayProvider : MainAPI() { - override var mainUrl = "https://aniplay.it" - override var name = "AniPlay" - override var lang = "it" - override val hasMainPage = true - private val dubIdentifier = " (ITA)" - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getStatus(t: String?): ShowStatus? { - return when (t?.lowercase()) { - "completato" -> ShowStatus.Completed - "in corso" -> ShowStatus.Ongoing - else -> null // "annunciato" - } - } - fun getType(t: String?): TvType { - return when (t?.lowercase()) { - "ona" -> TvType.OVA - "movie" -> TvType.AnimeMovie - else -> TvType.Anime //"serie", "special" - } - } - } - - private fun isDub(title: String): Boolean{ - return title.contains(dubIdentifier) - } - - data class ApiPoster( - @JsonProperty("imageFull") val posterUrl: String - ) - - data class ApiMainPageAnime( - @JsonProperty("animeId") val id: Int, - @JsonProperty("episodeNumber") val episode: String?, - @JsonProperty("animeTitle") val title: String, - @JsonProperty("animeType") val type: String, - @JsonProperty("fullHd") val fullHD: Boolean, - @JsonProperty("animeVerticalImages") val posters: List - ) - - data class ApiSearchResult( - @JsonProperty("id") val id: Int, - @JsonProperty("title") val title: String, - @JsonProperty("status") val status: String, - @JsonProperty("type") val type: String, - @JsonProperty("verticalImages") val posters: List - ) - - data class ApiGenres( - @JsonProperty("description") val name: String - ) - data class ApiWebsite( - @JsonProperty("listWebsiteId") val websiteId: Int, - @JsonProperty("url") val url: String - ) - - data class ApiEpisode( - @JsonProperty("id") val id: Int, - @JsonProperty("title") val title: String?, - @JsonProperty("episodeNumber") val number: String, - ) - - private fun ApiEpisode.toEpisode() : Episode? { - val number = this.number.toIntOrNull() ?: return null - return Episode( - data = "$mainUrl/api/episode/${this.id}", - episode = number, - name = this.title - ) - } - - data class ApiSeason( - @JsonProperty("id") val id: Int, - @JsonProperty("name") val name: String - ) - - private suspend fun ApiSeason.toEpisodeList(url: String) : List { - return app.get("$url/season/${this.id}").parsed>().mapNotNull { it.toEpisode() } - } - - data class ApiAnime( - @JsonProperty("title") val title: String, - @JsonProperty("alternativeTitle") val japTitle: String?, - @JsonProperty("episodeDuration") val duration: Int, - @JsonProperty("storyline") val plot: String, - @JsonProperty("type") val type: String, - @JsonProperty("status") val status: String, - @JsonProperty("genres") val genres: List, - @JsonProperty("verticalImages") val posters: List, - @JsonProperty("listWebsites") val websites: List, - @JsonProperty("episodes") val episodes: List, - @JsonProperty("seasons") val seasons: List? - ) - - data class ApiEpisodeUrl( - @JsonProperty("videoUrl") val url: String - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val response = app.get("$mainUrl/api/home/latest-episodes?page=0").parsed>() - - val results = response.map{ - val isDub = isDub(it.title) - newAnimeSearchResponse( - name = if (isDub) it.title.replace(dubIdentifier, "") else it.title, - url = "$mainUrl/api/anime/${it.id}", - type = getType(it.type), - ){ - addDubStatus(isDub, it.episode?.toIntOrNull()) - this.posterUrl = it.posters.first().posterUrl - this.quality = if (it.fullHD) SearchQuality.HD else null - } - } - return HomePageResponse(listOf(HomePageList("Ultime uscite",results))) - } - - override suspend fun search(query: String): List { - val response = app.get("$mainUrl/api/anime/advanced-search?page=0&size=36&query=$query").parsed>() - - return response.map { - val isDub = isDub(it.title) - - newAnimeSearchResponse( - name = if (isDub) it.title.replace(dubIdentifier, "") else it.title, - url = "$mainUrl/api/anime/${it.id}", - type = getType(it.type), - ){ - addDubStatus(isDub) - this.posterUrl = it.posters.first().posterUrl - } - } - } - - override suspend fun load(url: String): LoadResponse { - - val response = app.get(url).parsed() - - val tags: List = response.genres.map { it.name } - - val malId: Int? = response.websites.find { it.websiteId == 1 }?.url?.removePrefix("https://myanimelist.net/anime/")?.split("/")?.first()?.toIntOrNull() - val aniListId: Int? = response.websites.find { it.websiteId == 4 }?.url?.removePrefix("https://anilist.co/anime/")?.split("/")?.first()?.toIntOrNull() - - val episodes = if (response.seasons.isNullOrEmpty()) response.episodes.mapNotNull { it.toEpisode() } else response.seasons.map{ it.toEpisodeList(url) }.flatten() - val isDub = isDub(response.title) - - return newAnimeLoadResponse(response.title, url, getType(response.type)) { - this.name = if (isDub) response.title.replace(dubIdentifier, "") else response.title - this.japName = response.japTitle - this.plot = response.plot - this.tags = tags - this.showStatus = getStatus(response.status) - addPoster(response.posters.first().posterUrl) - addEpisodes(if (isDub) DubStatus.Dubbed else DubStatus.Subbed, episodes) - addMalId(malId) - addAniListId(aniListId) - addDuration(response.duration.toString()) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val episode = app.get(data).parsed() - - if(episode.url.contains(".m3u8")){ - val m3u8Helper = M3u8Helper() - val streams = m3u8Helper.m3u8Generation(M3u8Helper.M3u8Stream(episode.url,Qualities.Unknown.value), false) - - streams.forEach { - callback.invoke( - ExtractorLink( - name, - name, - it.streamUrl, - referer = mainUrl, - quality = it.quality ?: Qualities.Unknown.value, - isM3u8 = it.streamUrl.contains(".m3u8"))) } - return true - } - - callback.invoke( - ExtractorLink( - name, - name, - episode.url, - referer = mainUrl, - quality = Qualities.Unknown.value, - isM3u8 = false, - ) - ) - return true - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AniflixProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AniflixProvider.kt deleted file mode 100644 index 8142f0bb..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AniflixProvider.kt +++ /dev/null @@ -1,274 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import java.net.URLDecoder - -class AniflixProvider : MainAPI() { - override var mainUrl = "https://aniflix.pro" - override var name = "Aniflix" - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.OVA, - TvType.Anime, - ) - - companion object { - var token: String? = null - } - - private suspend fun getToken(): String { - return token ?: run { - Regex("([^/]*)/_buildManifest\\.js").find(app.get(mainUrl).text)?.groupValues?.getOrNull( - 1 - ) - ?.also { - token = it - } - ?: throw ErrorLoadingException("No token found") - } - } - - private fun Anime.toSearchResponse(): SearchResponse? { - return newAnimeSearchResponse( - title?.english ?: title?.romaji ?: return null, - "$mainUrl/anime/${id ?: return null}" - ) { - posterUrl = coverImage?.large ?: coverImage?.medium - } - } - - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val soup = app.get(mainUrl).document - val elements = listOf( - Pair("Trending Now", "div:nth-child(3) > div a"), - Pair("Popular", "div:nth-child(4) > div a"), - Pair("Top Rated", "div:nth-child(5) > div a"), - ) - - elements.map { (name, element) -> - val home = soup.select(element).map { - val href = it.attr("href") - val title = it.selectFirst("p.mt-2")!!.text() - val image = it.selectFirst("img.rounded-md[sizes]")!!.attr("src").replace("/_next/image?url=","") - .replace(Regex("\\&.*\$"),"") - val realposter = URLDecoder.decode(image, "UTF-8") - newAnimeSearchResponse(title, fixUrl(href)) { - this.posterUrl = realposter - } - } - items.add(HomePageList(name, home)) - } - - return HomePageResponse(items) - } - - override suspend fun search(query: String): List? { - val token = getToken() - val url = "$mainUrl/_next/data/$token/search.json?keyword=$query" - val response = app.get(url) - val searchResponse = - response.parsedSafe() - ?: throw ErrorLoadingException("No Media") - return searchResponse.pageProps?.searchResults?.Page?.media?.mapNotNull { media -> - media.toSearchResponse() - } - } - - override suspend fun load(url: String): LoadResponse { - val token = getToken() - - val id = Regex("$mainUrl/anime/([0-9]*)").find(url)?.groupValues?.getOrNull(1) - ?: throw ErrorLoadingException("Error parsing link for id") - - val res = app.get("https://aniflix.pro/_next/data/$token/anime/$id.json?id=$id") - .parsedSafe()?.pageProps - ?: throw ErrorLoadingException("Invalid Json reponse") - val isMovie = res.anime.format == "MOVIE" - return newAnimeLoadResponse( - res.anime.title?.english ?: res.anime.title?.romaji - ?: throw ErrorLoadingException("Invalid title reponse"), - url, if (isMovie) TvType.AnimeMovie else TvType.Anime - ) { - recommendations = res.recommended.mapNotNull { it.toSearchResponse() } - tags = res.anime.genres - posterUrl = res.anime.coverImage?.large ?: res.anime.coverImage?.medium - plot = res.anime.description - showStatus = when (res.anime.status) { - "FINISHED" -> ShowStatus.Completed - "RELEASING" -> ShowStatus.Ongoing - else -> null - } - addAniListId(id.toIntOrNull()) - - // subbed because they are both subbed and dubbed - if (isMovie) - addEpisodes( - DubStatus.Subbed, - listOf(newEpisode("$mainUrl/api/anime/?id=$id&episode=1")) - ) - else - addEpisodes(DubStatus.Subbed, res.episodes.episodes?.nodes?.mapIndexed { index, node -> - val episodeIndex = node?.number ?: (index + 1) - //"$mainUrl/_next/data/$token/watch/$id.json?episode=${node.number ?: return@mapNotNull null}&id=$id" - newEpisode("$mainUrl/api/anime?id=$id&episode=${episodeIndex}") { - episode = episodeIndex - posterUrl = node?.thumbnail?.original?.url - name = node?.titles?.canonical - } - }) - } - } - - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - return app.get(data).parsed().let { res -> - val dubReferer = res.dub?.Referer ?: "" - res.dub?.sources?.forEach { source -> - callback( - ExtractorLink( - name, - "${source.label ?: name} (DUB)", - source.file ?: return@forEach, - dubReferer, - getQualityFromName(source.label), - source.type == "hls" - ) - ) - } - - val subReferer = res.dub?.Referer ?: "" - res.sub?.sources?.forEach { source -> - callback( - ExtractorLink( - name, - "${source.label ?: name} (SUB)", - source.file ?: return@forEach, - subReferer, - getQualityFromName(source.label), - source.type == "hls" - ) - ) - } - - !res.dub?.sources.isNullOrEmpty() && !res.sub?.sources.isNullOrEmpty() - } - } - - data class AniLoadResponse( - @JsonProperty("sub") val sub: DubSubSource?, - @JsonProperty("dub") val dub: DubSubSource?, - @JsonProperty("episodes") val episodes: Int? - ) - - data class Sources( - @JsonProperty("file") val file: String?, - @JsonProperty("label") val label: String?, - @JsonProperty("type") val type: String? - ) - - data class DubSubSource( - @JsonProperty("Referer") var Referer: String?, - @JsonProperty("sources") var sources: ArrayList = arrayListOf() - ) - - data class PageProps( - @JsonProperty("searchResults") val searchResults: SearchResults? - ) - - data class SearchResults( - @JsonProperty("Page") val Page: Page? - ) - - data class Page( - @JsonProperty("media") val media: ArrayList = arrayListOf() - ) - - data class CoverImage( - @JsonProperty("color") val color: String?, - @JsonProperty("medium") val medium: String?, - @JsonProperty("large") val large: String?, - ) - - data class Title( - @JsonProperty("english") val english: String?, - @JsonProperty("romaji") val romaji: String?, - ) - - data class Search( - @JsonProperty("pageProps") val pageProps: PageProps?, - @JsonProperty("__N_SSP") val _NSSP: Boolean? - ) - - data class Anime( - @JsonProperty("status") val status: String?, - @JsonProperty("id") val id: Int?, - @JsonProperty("title") val title: Title?, - @JsonProperty("coverImage") val coverImage: CoverImage?, - @JsonProperty("format") val format: String?, - @JsonProperty("duration") val duration: Int?, - @JsonProperty("meanScore") val meanScore: Int?, - @JsonProperty("nextAiringEpisode") val nextAiringEpisode: String?, - @JsonProperty("bannerImage") val bannerImage: String?, - @JsonProperty("description") val description: String?, - @JsonProperty("genres") val genres: ArrayList? = null, - @JsonProperty("season") val season: String?, - @JsonProperty("startDate") val startDate: StartDate?, - ) - - data class StartDate( - @JsonProperty("year") val year: Int? - ) - - data class AnimeResponsePage( - @JsonProperty("pageProps") val pageProps: AnimeResponse?, - ) - - data class AnimeResponse( - @JsonProperty("anime") val anime: Anime, - @JsonProperty("recommended") val recommended: ArrayList, - @JsonProperty("episodes") val episodes: EpisodesParent, - ) - - data class EpisodesParent( - @JsonProperty("id") val id: String?, - @JsonProperty("season") val season: String?, - @JsonProperty("startDate") val startDate: String?, - @JsonProperty("episodeCount") val episodeCount: Int?, - @JsonProperty("episodes") val episodes: Episodes?, - ) - - data class Episodes( - @JsonProperty("nodes") val nodes: ArrayList = arrayListOf() - ) - - data class Nodes( - @JsonProperty("number") val number: Int? = null, - @JsonProperty("titles") val titles: Titles?, - @JsonProperty("thumbnail") val thumbnail: Thumbnail?, - ) - - data class Titles( - @JsonProperty("canonical") val canonical: String?, - ) - - data class Original( - @JsonProperty("url") val url: String?, - ) - - data class Thumbnail( - @JsonProperty("original") val original: Original?, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeFlickProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeFlickProvider.kt deleted file mode 100644 index 2f58d1a8..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeFlickProvider.kt +++ /dev/null @@ -1,119 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.extractorApis -import org.jsoup.Jsoup -import java.util.* - -class AnimeFlickProvider : MainAPI() { - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - } - - override var mainUrl = "https://animeflick.net" - override var name = "AnimeFlick" - override val hasQuickSearch = false - override val hasMainPage = false - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.Anime, - TvType.OVA - ) - - override suspend fun search(query: String): List { - val link = "https://animeflick.net/search.php?search=$query" - val html = app.get(link).text - val doc = Jsoup.parse(html) - - return doc.select(".row.mt-2").mapNotNull { - val href = mainUrl + it.selectFirst("a")?.attr("href") - val title = it.selectFirst("h5 > a")?.text() ?: return@mapNotNull null - val poster = mainUrl + it.selectFirst("img")?.attr("src")?.replace("70x110", "225x320") - AnimeSearchResponse( - title, - href, - this.name, - getType(title), - poster, - null, - EnumSet.of(DubStatus.Subbed), - ) - } - } - - override suspend fun load(url: String): LoadResponse { - val html = app.get(url).text - val doc = Jsoup.parse(html) - - val poster = mainUrl + doc.selectFirst("img.rounded")?.attr("src") - val title = doc.selectFirst("h2.title")!!.text() - - val yearText = doc.selectFirst(".trending-year")?.text() - val year = - if (yearText != null) Regex("""(\d{4})""").find(yearText)?.destructured?.component1() - ?.toIntOrNull() else null - val description = doc.selectFirst("p")?.text() - - val genres = doc.select("a[href*=\"genre-\"]").map { it.text() } - - val episodes = doc.select("#collapseOne .block-space > .row > div:nth-child(2)").map { - val name = it.selectFirst("a")?.text() - val link = mainUrl + it.selectFirst("a")?.attr("href") - Episode(link, name) - }.reversed() - - return newAnimeLoadResponse(title, url, getType(title)) { - posterUrl = poster - this.year = year - - addEpisodes(DubStatus.Subbed, episodes) - - plot = description - tags = genres - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val html = app.get(data).text - - val episodeRegex = Regex("""(https://.*?\.mp4)""") - val links = episodeRegex.findAll(html).map { - it.value - }.toList() - for (link in links) { - var alreadyAdded = false - for (extractor in extractorApis) { - if (link.startsWith(extractor.mainUrl)) { - extractor.getSafeUrl(link, data, subtitleCallback, callback) - alreadyAdded = true - break - } - } - if (!alreadyAdded) { - callback( - ExtractorLink( - this.name, - "${this.name} - Auto", - link, - "", - Qualities.P1080.value - ) - ) - } - } - - return true - } -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeIndoProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeIndoProvider.kt deleted file mode 100644 index 295f134a..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeIndoProvider.kt +++ /dev/null @@ -1,192 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.APIHolder.getCaptchaToken -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import com.lagradost.nicehttp.NiceResponse -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class AnimeIndoProvider : MainAPI() { - override var mainUrl = "https://animeindo.sbs" - override var name = "AnimeIndo" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Finished Airing" -> ShowStatus.Completed - "Currently Airing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - - private suspend fun request(url: String): NiceResponse { - val req = app.get( - url, - cookies = mapOf("recaptcha_cookie" to "#Asia/Jakarta#-420#win32#Windows#0,false,false#Google Inc. (Intel)~ANGLE (Intel, Intel(R) HD Graphics 400 Direct3D11 vs_5_0 ps_5_0)") - ) - if (req.isSuccessful) { - return req - } else { - val document = app.get(url).document - val captchaKey = - document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]") - .attr("src").substringAfter("render=").substringBefore("&") - val token = getCaptchaToken(url, captchaKey) - return app.post( - url, - data = mapOf( - "action" to "recaptcha_for_all", - "token" to "$token", - "sitekey" to captchaKey - ) - ) - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/anime-terbaru/page/" to "Anime Terbaru", - "$mainUrl/donghua-terbaru/page/" to "Donghua Terbaru" - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = request(request.data + page).document - val home = document.select("div.post-show > article").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/anime/")) { - uri - } else { - var title = uri.substringAfter("$mainUrl/") - title = when { - (title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find( - title - )?.groupValues?.get(1).toString() - (title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get( - 1 - ).toString() - else -> title - } - "$mainUrl/anime/$title" - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val title = this.selectFirst("div.title")?.text()?.trim() ?: return null - val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) - val posterUrl = this.select("img[itemprop=image]").attr("src").toString() - val type = getType(this.select("div.type").text().trim()) - val epNum = - this.selectFirst("span.episode")?.ownText()?.replace(Regex("[^0-9]"), "")?.trim() - ?.toIntOrNull() - return newAnimeSearchResponse(title, href, type) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = request(link).document - - return document.select(".site-main.relat > article").map { - val title = it.selectFirst("div.title > h2")!!.ownText().trim() - val href = it.selectFirst("a")!!.attr("href") - val posterUrl = it.selectFirst("img")!!.attr("src").toString() - val type = getType(it.select("div.type").text().trim()) - newAnimeSearchResponse(title, href, type) { - this.posterUrl = posterUrl - } - } - } - - override suspend fun load(url: String): LoadResponse { - val document = request(url).document - - val title = document.selectFirst("h1.entry-title")?.text().toString().trim() - val poster = document.selectFirst("div.thumb > img[itemprop=image]")?.attr("src") - val tags = document.select("div.genxed > a").map { it.text() } - val type = getType( - document.selectFirst("div.info-content > div.spe > span:nth-child(6)")?.ownText() - .toString() - ) - val year = Regex("\\d, ([0-9]*)").find( - document.select("div.info-content > div.spe > span:nth-child(9) > time").text() - )?.groupValues?.get(1)?.toIntOrNull() - val status = getStatus( - document.selectFirst("div.info-content > div.spe > span:nth-child(1)")!!.ownText() - .trim() - ) - val description = document.select("div[itemprop=description] > p").text() - val trailer = document.selectFirst("div.player-embed iframe")?.attr("src") - val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull { - val header = it.selectFirst("span.lchx > a") ?: return@mapNotNull null - val name = header.text().trim() - val episode = header.text().trim().replace("Episode", "").trim().toIntOrNull() - val link = fixUrl(header.attr("href")) - Episode(link, name = name, episode = episode) - }.reversed() - - return newAnimeLoadResponse(title, url, type) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - addTrailer(trailer) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = request(data).document - document.select("div.itemleft > .mirror > option").mapNotNull { - fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) - }.apmap { - if (it.startsWith("https://uservideo.xyz")) { - app.get(it, referer = "$mainUrl/").document.select("iframe").attr("src") - } else { - it - } - }.apmap { - loadExtractor(it, data, subtitleCallback, callback) - } - - return true - } - - -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimePaheProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimePaheProvider.kt deleted file mode 100644 index 20a485ea..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimePaheProvider.kt +++ /dev/null @@ -1,562 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.module.kotlin.readValue -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.JsUnpacker -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.nicehttp.NiceResponse -import org.jsoup.Jsoup -import kotlin.math.pow - -class AnimePaheProvider : MainAPI() { - // credit to https://github.com/justfoolingaround/animdl/tree/master/animdl/core/codebase/providers/animepahe - companion object { - const val MAIN_URL = "https://animepahe.com" - - var cookies: Map = mapOf() - private fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - - suspend fun generateSession(): Boolean { - if (cookies.isNotEmpty()) return true - return try { - val response = app.get("$MAIN_URL/") - cookies = response.cookies - true - } catch (e: Exception) { - false - } - } - - val YTSM = Regex("ysmm = '([^']+)") - - val KWIK_PARAMS_RE = Regex("""\("(\w+)",\d+,"(\w+)",(\d+),(\d+),\d+\)""") - val KWIK_D_URL = Regex("action=\"([^\"]+)\"") - val KWIK_D_TOKEN = Regex("value=\"([^\"]+)\"") - val YOUTUBE_VIDEO_LINK = - Regex("""(^(?:https?:)?(?://)?(?:www\.)?(?:youtu\.be/|youtube(?:-nocookie)?\.(?:[A-Za-z]{2,4}|[A-Za-z]{2,3}\.[A-Za-z]{2})/)(?:watch|embed/|vi?/)*(?:\?[\w=&]*vi?=)?[^#&?/]{11}.*${'$'})""") - } - - override var mainUrl = MAIN_URL - override var name = "AnimePahe" - override val hasQuickSearch = false - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.Anime, - TvType.OVA - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - data class Data( - @JsonProperty("id") val id: Int, - @JsonProperty("anime_id") val animeId: Int, - @JsonProperty("anime_title") val animeTitle: String, - @JsonProperty("anime_slug") val animeSlug: String, - @JsonProperty("episode") val episode: Int, - @JsonProperty("snapshot") val snapshot: String, - @JsonProperty("created_at") val createdAt: String, - @JsonProperty("anime_session") val animeSession: String, - ) - - data class AnimePaheLatestReleases( - @JsonProperty("total") val total: Int, - @JsonProperty("data") val data: List - ) - - val urls = listOf( - Pair("$mainUrl/api?m=airing&page=1", "Latest Releases"), - ) - - val items = ArrayList() - for (i in urls) { - try { - val response = app.get(i.first).text - val episodes = parseJson(response).data.map { - newAnimeSearchResponse( - it.animeTitle, - "https://pahe.win/a/${it.animeId}?slug=${it.animeTitle}", - fix = false - ) { - this.posterUrl = it.snapshot - addDubStatus(DubStatus.Subbed, it.episode) - } - } - - items.add(HomePageList(i.second, episodes)) - } catch (e: Exception) { - e.printStackTrace() - } - } - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - data class AnimePaheSearchData( - @JsonProperty("id") val id: Int, - @JsonProperty("slug") val slug: String, - @JsonProperty("title") val title: String, - @JsonProperty("type") val type: String, - @JsonProperty("episodes") val episodes: Int, - @JsonProperty("status") val status: String, - @JsonProperty("season") val season: String, - @JsonProperty("year") val year: Int, - @JsonProperty("score") val score: Double, - @JsonProperty("poster") val poster: String, - @JsonProperty("session") val session: String, - @JsonProperty("relevance") val relevance: String - ) - - data class AnimePaheSearch( - @JsonProperty("total") val total: Int, - @JsonProperty("data") val data: List - ) - - private suspend fun getAnimeByIdAndTitle(title: String, animeId: Int): String? { - val url = "$mainUrl/api?m=search&l=8&q=$title" - val headers = mapOf("referer" to "$mainUrl/") - - val req = app.get(url, headers = headers).text - val data = parseJson(req) - for (anime in data.data) { - if (anime.id == animeId) { - return "https://animepahe.com/anime/${anime.session}" - } - } - return null - } - - - override suspend fun search(query: String): List { - val url = "$mainUrl/api?m=search&l=8&q=$query" - val headers = mapOf("referer" to "$mainUrl/") - - val req = app.get(url, headers = headers).text - val data = parseJson(req) - - return data.data.map { - newAnimeSearchResponse( - it.title, - "https://pahe.win/a/${it.id}?slug=${it.title}", - fix = false - ) { - this.posterUrl = it.poster - addDubStatus(DubStatus.Subbed, it.episodes) - } - } - } - - private data class AnimeData( - @JsonProperty("id") val id: Int, - @JsonProperty("anime_id") val animeId: Int, - @JsonProperty("episode") val episode: Int, - @JsonProperty("title") val title: String, - @JsonProperty("snapshot") val snapshot: String, - @JsonProperty("session") val session: String, - @JsonProperty("filler") val filler: Int, - @JsonProperty("created_at") val createdAt: String - ) - - private data class AnimePaheAnimeData( - @JsonProperty("total") val total: Int, - @JsonProperty("per_page") val perPage: Int, - @JsonProperty("current_page") val currentPage: Int, - @JsonProperty("last_page") val lastPage: Int, - @JsonProperty("next_page_url") val nextPageUrl: String?, - @JsonProperty("prev_page_url") val prevPageUrl: String?, - @JsonProperty("from") val from: Int, - @JsonProperty("to") val to: Int, - @JsonProperty("data") val data: List - ) - - private suspend fun generateListOfEpisodes(link: String): ArrayList { - try { - val attrs = link.split('/') - val id = attrs[attrs.size - 1].split("?")[0] - - val uri = "$mainUrl/api?m=release&id=$id&sort=episode_asc&page=1" - val headers = mapOf("referer" to "$mainUrl/") - - val req = app.get(uri, headers = headers).text - val data = parseJson(req) - - val lastPage = data.lastPage - val perPage = data.perPage - val total = data.total - var ep = 1 - val episodes = ArrayList() - - fun getEpisodeTitle(k: AnimeData): String { - return k.title.ifEmpty { - "Episode ${k.episode}" - } - } - - if (lastPage == 1 && perPage > total) { - data.data.forEach { - episodes.add( - newEpisode("$mainUrl/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!") { - addDate(it.createdAt) - this.name = getEpisodeTitle(it) - this.posterUrl = it.snapshot - } - ) - } - } else { - for (page in 0 until lastPage) { - for (i in 0 until perPage) { - if (ep <= total) { - episodes.add( - Episode( - "$mainUrl/api?m=release&id=${id}&sort=episode_asc&page=${page + 1}&ep=${ep}!!FALSE!!" - ) - ) - ++ep - } - } - } - } - return episodes - } catch (e: Exception) { - return ArrayList() - } - } - - override suspend fun load(url: String): LoadResponse? { - return suspendSafeApiCall { - val regex = Regex("""a/(\d+)\?slug=(.+)""") - val (animeId, animeTitle) = regex.find(url)!!.destructured - val link = getAnimeByIdAndTitle(animeTitle, animeId.toInt())!! - - val html = app.get(link).text - val doc = Jsoup.parse(html) - - val japTitle = doc.selectFirst("h2.japanese")?.text() - val poster = doc.selectFirst(".anime-poster a")?.attr("href") - - val tvType = doc.selectFirst("""a[href*="/anime/type/"]""")?.text() - - val trailer: String? = if (html.contains("https://www.youtube.com/watch")) { - YOUTUBE_VIDEO_LINK.find(html)?.destructured?.component1() - } else { - null - } - - val episodes = generateListOfEpisodes(url) - val year = Regex("""Aired:[^,]*, (\d+)""") - .find(html)!!.destructured.component1() - .toIntOrNull() - val status = - when (Regex("""Status:[^a]*a href=["']/anime/(.*?)["']""") - .find(html)!!.destructured.component1()) { - "airing" -> ShowStatus.Ongoing - "completed" -> ShowStatus.Completed - else -> null - } - val synopsis = doc.selectFirst(".anime-synopsis")?.text() - - var anilistId: Int? = null - var malId: Int? = null - - doc.select(".external-links > a").forEach { aTag -> - val split = aTag.attr("href").split("/") - - if (aTag.attr("href").contains("anilist.co")) { - anilistId = split[split.size - 1].toIntOrNull() - } else if (aTag.attr("href").contains("myanimelist.net")) { - malId = split[split.size - 1].toIntOrNull() - } - } - - newAnimeLoadResponse(animeTitle, url, getType(tvType.toString())) { - engName = animeTitle - japName = japTitle - - this.posterUrl = poster - this.year = year - - addEpisodes(DubStatus.Subbed, episodes) - this.showStatus = status - plot = synopsis - tags = if (!doc.select(".anime-genre > ul a").isEmpty()) { - ArrayList(doc.select(".anime-genre > ul a").map { it.text().toString() }) - } else { - null - } - - addMalId(malId) - addAniListId(anilistId) - addTrailer(trailer) - } - } - } - - - private fun isNumber(s: String?): Boolean { - return s?.toIntOrNull() != null - } - - private fun cookieStrToMap(cookie: String): Map { - val cookies = mutableMapOf() - for (string in cookie.split("; ")) { - val split = string.split("=").toMutableList() - val name = split.removeFirst().trim() - val value = if (split.size == 0) { - "true" - } else { - split.joinToString("=") - } - cookies[name] = value - } - return cookies.toMap() - } - - private fun getString(content: String, s1: Int, s2: Int): String { - val characterMap = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/" - - val slice2 = characterMap.slice(0 until s2) - var acc: Long = 0 - - for ((n, i) in content.reversed().withIndex()) { - acc += (when (isNumber("$i")) { - true -> "$i".toLong() - false -> "0".toLong() - }) * s1.toDouble().pow(n.toDouble()).toInt() - } - - var k = "" - - while (acc > 0) { - k = slice2[(acc % s2).toInt()] + k - acc = (acc - (acc % s2)) / s2 - } - - return when (k != "") { - true -> k - false -> "0" - } - } - - private fun decrypt(fullString: String, key: String, v1: Int, v2: Int): String { - var r = "" - var i = 0 - - while (i < fullString.length) { - var s = "" - - while (fullString[i] != key[v2]) { - s += fullString[i] - ++i - } - var j = 0 - - while (j < key.length) { - s = s.replace(key[j].toString(), j.toString()) - ++j - } - r += (getString(s, v2, 10).toInt() - v1).toChar() - ++i - } - return r - } - - private fun zipGen(gen: Sequence>): ArrayList, Pair>> { - val allItems = gen.toList().toMutableList() - val newList = ArrayList, Pair>>() - - while (allItems.size > 1) { - newList.add(Pair(allItems[0], allItems[1])) - allItems.removeAt(0) - allItems.removeAt(0) - } - return newList - } - - private fun decodeAdfly(codedKey: String): String { - var r = "" - var j = "" - - for ((n, l) in codedKey.withIndex()) { - if (n % 2 != 0) { - j = l + j - } else { - r += l - } - } - - val encodedUri = ((r + j).toCharArray().map { it.toString() }).toMutableList() - val numbers = sequence { - for ((i, n) in encodedUri.withIndex()) { - if (isNumber(n)) { - yield(Pair(i, n.toInt())) - } - } - } - - for ((first, second) in zipGen(numbers)) { - val xor = first.second.xor(second.second) - if (xor < 10) { - encodedUri[first.first] = xor.toString() - } - } - var returnValue = String(encodedUri.joinToString("").toByteArray(), Charsets.UTF_8) - returnValue = base64Decode(returnValue) - return returnValue.slice(16..returnValue.length - 17) - } - - private data class VideoQuality( - @JsonProperty("id") val id: Int?, - @JsonProperty("audio") val audio: String?, - @JsonProperty("kwik") val kwik: String?, - @JsonProperty("kwik_pahewin") val kwikPahewin: String - ) - - private data class AnimePaheEpisodeLoadLinks( - @JsonProperty("data") val data: List> - ) - - private suspend fun bypassAdfly(adflyUri: String): String { - if (!generateSession()) { - return bypassAdfly(adflyUri) - } - - var responseCode = 302 - var adflyContent: NiceResponse? = null - var tries = 0 - - while (responseCode != 200 && tries < 20) { - adflyContent = app.get( - app.get(adflyUri, cookies = cookies, allowRedirects = false).url, - cookies = cookies, - allowRedirects = false - ) - cookies = cookies + adflyContent.cookies - responseCode = adflyContent.code - ++tries - } - if (tries > 19) { - throw Exception("Failed to bypass adfly.") - } - return decodeAdfly(YTSM.find(adflyContent?.text.toString())!!.destructured.component1()) - } - - private suspend fun getStreamUrlFromKwik(url: String?): String? { - if (url == null) return null - val response = - app.get( - url, - headers = mapOf("referer" to mainUrl), - cookies = cookies - ).text - Regex("eval((.|\\n)*?)").find(response)?.groupValues?.get(1)?.let { jsEval -> - JsUnpacker("eval$jsEval").unpack()?.let { unPacked -> - Regex("source=\'(.*?)\'").find(unPacked)?.groupValues?.get(1)?.let { link -> - return link - } - } - } - return null - } - - private suspend fun getStreamUrlFromKwikAdfly(adflyUri: String): String { - val fContent = - app.get( - bypassAdfly(adflyUri), - headers = mapOf("referer" to "https://kwik.cx/"), - cookies = cookies - ) - cookies = cookies + fContent.cookies - - val (fullString, key, v1, v2) = KWIK_PARAMS_RE.find(fContent.text)!!.destructured - val decrypted = decrypt(fullString, key, v1.toInt(), v2.toInt()) - val uri = KWIK_D_URL.find(decrypted)!!.destructured.component1() - val tok = KWIK_D_TOKEN.find(decrypted)!!.destructured.component1() - var content: NiceResponse? = null - - var code = 419 - var tries = 0 - - while (code != 302 && tries < 20) { - content = app.post( - uri, - allowRedirects = false, - data = mapOf("_token" to tok), - headers = mapOf("referer" to fContent.url), - cookies = fContent.cookies - ) - code = content.code - ++tries - } - if (tries > 19) { - throw Exception("Failed to extract the stream uri from kwik.") - } - return content?.headers?.values("location").toString() - } - - private suspend fun extractVideoLinks( - episodeLink: String, - callback: (ExtractorLink) -> Unit - ) { - var link = episodeLink - val headers = mapOf("referer" to "$mainUrl/") - - if (link.contains("!!TRUE!!")) { - link = link.replace("!!TRUE!!", "") - } else { - val regex = """&ep=(\d+)!!FALSE!!""".toRegex() - val episodeNum = regex.find(link)?.destructured?.component1()?.toIntOrNull() - link = link.replace(regex, "") - - val req = app.get(link, headers = headers).text - val jsonResponse = parseJson(req) - val ep = ((jsonResponse.data.map { - if (it.episode == episodeNum) { - it - } else { - null - } - }).filterNotNull())[0] - link = "$mainUrl/api?m=links&id=${ep.animeId}&session=${ep.session}&p=kwik" - } - val req = app.get(link, headers = headers).text - val data = mapper.readValue(req) - - data.data.forEach { - it.entries.toList().apmap { quality -> - getStreamUrlFromKwik(quality.value.kwik)?.let { link -> - callback( - ExtractorLink( - "KWIK", - "KWIK - ${quality.key} [${quality.value.audio ?: "jpn"}]", - link, - "https://kwik.cx/", - getQualityFromName(quality.key), - link.contains(".m3u8") - ) - ) - } - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - extractVideoLinks(data, callback) - return true - } -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeSailProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeSailProvider.kt deleted file mode 100644 index 07a93fd7..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeSailProvider.kt +++ /dev/null @@ -1,191 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor -import com.lagradost.nicehttp.NiceResponse -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class AnimeSailProvider : MainAPI() { - override var mainUrl = "https://111.90.143.42" - override var name = "AnimeSail" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - private suspend fun request(url: String, ref: String? = null): NiceResponse { - return app.get( - url, - headers = mapOf("Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"), - cookies = mapOf("_as_ipin_ct" to "ID"), - referer = ref - ) - } - - override val mainPage = mainPageOf( - "$mainUrl/page/" to "Episode Terbaru", - "$mainUrl/movie-terbaru/page/" to "Movie Terbaru", - "$mainUrl/genres/donghua/page/" to "Donghua" - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val document = request(request.data + page).document - val home = document.select("article").map { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/anime/")) { - uri - } else { - var title = uri.substringAfter("$mainUrl/") - title = when { - (title.contains("-episode")) && !(title.contains("-movie")) -> title.substringBefore( - "-episode" - ) - (title.contains("-movie")) -> title.substringBefore("-movie") - else -> title - } - - "$mainUrl/anime/$title" - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse { - val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString()) - val title = this.select(".tt > h2").text().trim() - val posterUrl = fixUrlNull(this.selectFirst("div.limit img")?.attr("src")) - val epNum = this.selectFirst(".tt > h2")?.text()?.let { - Regex("Episode\\s?([0-9]+)").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull() - } - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = request(link).document - - return document.select("div.listupd article").map { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = request(url).document - - val title = document.selectFirst("h1.entry-title")?.text().toString().trim() - val type = getType( - document.select("tbody th:contains(Tipe)").next().text() - ) - val episodes = document.select("ul.daftar > li").map { - val header = it.select("a").text().trim() - val name = - Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header - val link = fixUrl(it.select("a").attr("href")) - Episode(link, name = name) - }.reversed() - - return newAnimeLoadResponse(title, url, type) { - posterUrl = document.selectFirst("div.entry-content > img")?.attr("src") - this.year = - document.select("tbody th:contains(Dirilis)").next().text().trim().toIntOrNull() - addEpisodes(DubStatus.Subbed, episodes) - showStatus = - getStatus(document.select("tbody th:contains(Status)").next().text().trim()) - plot = document.selectFirst("div.entry-content > p")?.text() - this.tags = - document.select("tbody th:contains(Genre)").next().select("a").map { it.text() } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = request(data).document - - document.select(".mobius > .mirror > option").apmap { - safeApiCall { - val iframe = fixUrl( - Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src") - ?: throw ErrorLoadingException("No iframe found") - ) - - when { - iframe.startsWith("$mainUrl/utils/player/arch/") || iframe.startsWith( - "$mainUrl/utils/player/race/" - ) -> request(iframe, ref = data).document.select("source").attr("src") - .let { link -> - val source = - when { - iframe.contains("/arch/") -> "Arch" - iframe.contains("/race/") -> "Race" - else -> this.name - } - val quality = - Regex("\\.([0-9]{3,4})\\.").find(link)?.groupValues?.get(1) - callback.invoke( - ExtractorLink( - source = source, - name = source, - url = link, - referer = mainUrl, - quality = quality?.toIntOrNull() ?: Qualities.Unknown.value - ) - ) - } -// skip for now -// iframe.startsWith("$mainUrl/utils/player/fichan/") -> "" -// iframe.startsWith("$mainUrl/utils/player/blogger/") -> "" - iframe.startsWith("$mainUrl/utils/player/framezilla/") || iframe.startsWith("https://uservideo.xyz") -> { - request(iframe, ref = data).document.select("iframe").attr("src") - .let { link -> - loadExtractor(fixUrl(link), mainUrl, subtitleCallback, callback) - } - } - else -> { - loadExtractor(iframe, mainUrl, subtitleCallback, callback) - } - } - } - } - - return true - } - - -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeSaturnProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeSaturnProvider.kt deleted file mode 100644 index bc7ccff9..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeSaturnProvider.kt +++ /dev/null @@ -1,201 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.LoadResponse.Companion.addRating -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import org.jsoup.nodes.Element - -class AnimeSaturnProvider : MainAPI() { - override var mainUrl = "https://www.animesaturn.cc" - override var name = "AnimeSaturn" - override var lang = "it" - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getStatus(t: String?): ShowStatus? { - return when (t?.lowercase()) { - "finito" -> ShowStatus.Completed - "in corso" -> ShowStatus.Ongoing - else -> null - } - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse { - - var title = this.select("a.badge-archivio").first()!!.text() - var isDubbed = false - - if (title.contains(" (ITA)")){ - title = title.replace(" (ITA)", "") - isDubbed = true - } - - val url = this.select("a.badge-archivio").first()!!.attr("href") - - val posterUrl = this.select("img.locandina-archivio[src]").first()!!.attr("src") - - return newAnimeSearchResponse(title, url, TvType.Anime) { - addDubStatus(isDubbed) - this.posterUrl = posterUrl - } - } - - private fun Element.toEpisode(): Episode? { - var episode = this.text().split(" ")[1] - if(episode.contains(".")) return null - if(episode.contains("-")) - episode = episode.split("-")[0] - - return Episode( - data = this.attr("href"), - episode = episode.toInt() - ) - - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val document = app.get(mainUrl).document - val list = ArrayList() - document.select("div.container:has(span.badge-saturn)").forEach { - val tabName = it.select("span.badge-saturn").first()!!.text() - if (tabName.equals("Ultimi episodi")) return@forEach - val results = ArrayList() - it.select(".main-anime-card").forEach { card -> - var title = card.select("a[title]").first()!!.attr("title") - var isDubbed = false - if(title.contains(" (ITA)")){ - title = title.replace(" (ITA)", "") - isDubbed = true - } - val posterUrl = card.select("img.new-anime").first()!!.attr("src") - val url = card.select("a").first()!!.attr("href") - - results.add(newAnimeSearchResponse(title, url, TvType.Anime){ - addDubStatus(isDubbed) - this.posterUrl = posterUrl - }) - } - list.add(HomePageList(tabName, results)) - } - return HomePageResponse(list) - } - - override suspend fun search(query: String): List { - val document = app.get("$mainUrl/animelist?search=$query").document - return document.select("div.item-archivio").map { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - - val document = app.get(url).document - - val title = document.select("img.cover-anime").first()!!.attr("alt") - val japTitle = document.select("div.box-trasparente-alternativo").first()!!.text() - val posterUrl = document.select("img.cover-anime[src]").first()!!.attr("src") - var malId : Int? = null - var aniListId : Int? = null - - document.select("[rel=\"noopener noreferrer\"]").forEach { - if(it.attr("href").contains("myanimelist")) - malId = it.attr("href").removeSuffix("/").split('/').last().toIntOrNull() - else - aniListId = it.attr("href").removeSuffix("/").split('/').last().toIntOrNull() - } - - val plot = document.select("div#shown-trama").first()?.text() - - val tags = document.select("a.generi-as").map { it.text() } - - val details : List? = document.select("div.container:contains(Stato: )").first()?.text()?.split(" ") - var status : String? = null - var duration : String? = null - var year : String? = null - var score : String? = null - - val isDubbed = document.select("div.anime-title-as").first()!!.text().contains("(ITA)") - - if (!details.isNullOrEmpty()) { - details.forEach { - val index = details.indexOf(it) +1 - when (it) { - "Stato:" -> status = details[index] - "episodi:" -> duration = details[index] - "uscita:" -> year = details[index + 2] - "Voto:" -> score = details[index].split("/")[0] - else -> return@forEach - } - } - } - - val episodes = document.select("a.bottone-ep").mapNotNull{ it.toEpisode() } - - return newAnimeLoadResponse(title, url, TvType.Anime) { - this.engName = title - this.japName = japTitle - this.year = year?.toIntOrNull() - this.plot = plot - this.tags = tags - this.showStatus = getStatus(status) - addPoster(posterUrl) - addRating(score) - addEpisodes(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed, episodes) - addMalId(malId) - addAniListId(aniListId) - addDuration(duration) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val page = app.get(data).document - val episodeLink = page.select("div.card-body > a[href]").find {it1 -> - it1.attr("href").contains("watch?") - }?.attr("href") - - val episodePage = app.get(episodeLink!!).document - val episodeUrl: String? - var isM3U8 = false - - if(episodePage.select("video.afterglow > source").isNotEmpty()) //Old player - episodeUrl = episodePage.select("video.afterglow > source").first()!!.attr("src") - - else{ //New player - val script = episodePage.select("script").find { - it.toString().contains("jwplayer('player_hls').setup({") - }!!.toString() - episodeUrl = script.split(" ").find { it.contains(".m3u8") and !it.contains(".replace") }!!.replace("\"","").replace(",", "") - isM3U8 = true - } - - - callback.invoke( - ExtractorLink( - name, - name, - episodeUrl!!, - isM3u8 = isM3U8, - referer = "https://www.animesaturn.io/", //Some servers need the old host as referer, and the new ones accept it too - quality = Qualities.Unknown.value - ) - ) - return true - } -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeWorldProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeWorldProvider.kt deleted file mode 100644 index 1349fe78..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeWorldProvider.kt +++ /dev/null @@ -1,265 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.LoadResponse.Companion.addRating -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.nicehttp.NiceResponse -import org.jsoup.nodes.Element -import org.mozilla.javascript.ConsString -import org.mozilla.javascript.Context -import org.mozilla.javascript.Scriptable - -class AnimeWorldProvider : MainAPI() { - override var mainUrl = "https://www.animeworld.tv" - override var name = "AnimeWorld" - override var lang = "it" - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - private var cookies = emptyMap() - - // Disabled authentication as site did - private suspend fun request(url: String): NiceResponse { -// if (cookies.isEmpty()) { -// cookies = getCookies(url) -// } - return app.get(url -// , cookies = cookies - ) - } - - private suspend fun getCookies(url: String): Map { - val rhino = Context.enter() - rhino.optimizationLevel = -1 - val scope: Scriptable = rhino.initSafeStandardObjects() - - val slowAes = app.get("https://www.animeworld.tv/aes.min.js").text -// val slowAes = """ -// var _0x2465=["\x72\x6F\x74\x61\x74\x65","\x73\x62\x6F\x78","\x52\x63\x6F\x6E","\x6E\x75\x6D\x62\x65\x72\x4F\x66\x52\x6F\x75\x6E\x64\x73","\x63\x6F\x72\x65","\x53\x49\x5A\x45\x5F\x32\x35\x36","\x6B\x65\x79\x53\x69\x7A\x65","\x72\x73\x62\x6F\x78","\x73\x68\x69\x66\x74\x52\x6F\x77","\x6D\x69\x78\x43\x6F\x6C\x75\x6D\x6E","\x67\x61\x6C\x6F\x69\x73\x5F\x6D\x75\x6C\x74\x69\x70\x6C\x69\x63\x61\x74\x69\x6F\x6E","\x73\x75\x62\x42\x79\x74\x65\x73","\x73\x68\x69\x66\x74\x52\x6F\x77\x73","\x6D\x69\x78\x43\x6F\x6C\x75\x6D\x6E\x73","\x61\x64\x64\x52\x6F\x75\x6E\x64\x4B\x65\x79","\x63\x72\x65\x61\x74\x65\x52\x6F\x75\x6E\x64\x4B\x65\x79","\x72\x6F\x75\x6E\x64","\x69\x6E\x76\x52\x6F\x75\x6E\x64","\x53\x49\x5A\x45\x5F\x31\x32\x38","\x53\x49\x5A\x45\x5F\x31\x39\x32","\x65\x78\x70\x61\x6E\x64\x4B\x65\x79","\x6D\x61\x69\x6E","\x69\x6E\x76\x4D\x61\x69\x6E","\x73\x6C\x69\x63\x65","\x6C\x65\x6E\x67\x74\x68","\x69\x76\x20\x6C\x65\x6E\x67\x74\x68\x20\x6D\x75\x73\x74\x20\x62\x65\x20\x31\x32\x38\x20\x62\x69\x74\x73\x2E","\x43\x42\x43","\x6D\x6F\x64\x65\x4F\x66\x4F\x70\x65\x72\x61\x74\x69\x6F\x6E","\x70\x61\x64\x42\x79\x74\x65\x73\x49\x6E","\x63\x65\x69\x6C","\x67\x65\x74\x42\x6C\x6F\x63\x6B","\x43\x46\x42","\x65\x6E\x63\x72\x79\x70\x74","\x61\x65\x73","\x70\x75\x73\x68","\x4F\x46\x42","\x64\x65\x63\x72\x79\x70\x74","\x75\x6E\x70\x61\x64\x42\x79\x74\x65\x73\x4F\x75\x74","\x73\x70\x6C\x69\x63\x65"];var slowAES={aes:{keySize:{SIZE_128:16,SIZE_192:24,SIZE_256:32},sbox:[0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16],rsbox:[0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d],rotate:function(_0x85f6x2){var _0x85f6x3=_0x85f6x2[0];for(var _0x85f6x4=0;_0x85f6x4< 3;_0x85f6x4++){_0x85f6x2[_0x85f6x4]= _0x85f6x2[_0x85f6x4+ 1]};_0x85f6x2[3]= _0x85f6x3;return _0x85f6x2},Rcon:[0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91,0x39,0x72,0xe4,0xd3,0xbd,0x61,0xc2,0x9f,0x25,0x4a,0x94,0x33,0x66,0xcc,0x83,0x1d,0x3a,0x74,0xe8,0xcb,0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91,0x39,0x72,0xe4,0xd3,0xbd,0x61,0xc2,0x9f,0x25,0x4a,0x94,0x33,0x66,0xcc,0x83,0x1d,0x3a,0x74,0xe8,0xcb,0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91,0x39,0x72,0xe4,0xd3,0xbd,0x61,0xc2,0x9f,0x25,0x4a,0x94,0x33,0x66,0xcc,0x83,0x1d,0x3a,0x74,0xe8,0xcb,0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91,0x39,0x72,0xe4,0xd3,0xbd,0x61,0xc2,0x9f,0x25,0x4a,0x94,0x33,0x66,0xcc,0x83,0x1d,0x3a,0x74,0xe8,0xcb,0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91,0x39,0x72,0xe4,0xd3,0xbd,0x61,0xc2,0x9f,0x25,0x4a,0x94,0x33,0x66,0xcc,0x83,0x1d,0x3a,0x74,0xe8,0xcb],G2X:[0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,0x1b,0x19,0x1f,0x1d,0x13,0x11,0x17,0x15,0x0b,0x09,0x0f,0x0d,0x03,0x01,0x07,0x05,0x3b,0x39,0x3f,0x3d,0x33,0x31,0x37,0x35,0x2b,0x29,0x2f,0x2d,0x23,0x21,0x27,0x25,0x5b,0x59,0x5f,0x5d,0x53,0x51,0x57,0x55,0x4b,0x49,0x4f,0x4d,0x43,0x41,0x47,0x45,0x7b,0x79,0x7f,0x7d,0x73,0x71,0x77,0x75,0x6b,0x69,0x6f,0x6d,0x63,0x61,0x67,0x65,0x9b,0x99,0x9f,0x9d,0x93,0x91,0x97,0x95,0x8b,0x89,0x8f,0x8d,0x83,0x81,0x87,0x85,0xbb,0xb9,0xbf,0xbd,0xb3,0xb1,0xb7,0xb5,0xab,0xa9,0xaf,0xad,0xa3,0xa1,0xa7,0xa5,0xdb,0xd9,0xdf,0xdd,0xd3,0xd1,0xd7,0xd5,0xcb,0xc9,0xcf,0xcd,0xc3,0xc1,0xc7,0xc5,0xfb,0xf9,0xff,0xfd,0xf3,0xf1,0xf7,0xf5,0xeb,0xe9,0xef,0xed,0xe3,0xe1,0xe7,0xe5],G3X:[0x00,0x03,0x06,0x05,0x0c,0x0f,0x0a,0x09,0x18,0x1b,0x1e,0x1d,0x14,0x17,0x12,0x11,0x30,0x33,0x36,0x35,0x3c,0x3f,0x3a,0x39,0x28,0x2b,0x2e,0x2d,0x24,0x27,0x22,0x21,0x60,0x63,0x66,0x65,0x6c,0x6f,0x6a,0x69,0x78,0x7b,0x7e,0x7d,0x74,0x77,0x72,0x71,0x50,0x53,0x56,0x55,0x5c,0x5f,0x5a,0x59,0x48,0x4b,0x4e,0x4d,0x44,0x47,0x42,0x41,0xc0,0xc3,0xc6,0xc5,0xcc,0xcf,0xca,0xc9,0xd8,0xdb,0xde,0xdd,0xd4,0xd7,0xd2,0xd1,0xf0,0xf3,0xf6,0xf5,0xfc,0xff,0xfa,0xf9,0xe8,0xeb,0xee,0xed,0xe4,0xe7,0xe2,0xe1,0xa0,0xa3,0xa6,0xa5,0xac,0xaf,0xaa,0xa9,0xb8,0xbb,0xbe,0xbd,0xb4,0xb7,0xb2,0xb1,0x90,0x93,0x96,0x95,0x9c,0x9f,0x9a,0x99,0x88,0x8b,0x8e,0x8d,0x84,0x87,0x82,0x81,0x9b,0x98,0x9d,0x9e,0x97,0x94,0x91,0x92,0x83,0x80,0x85,0x86,0x8f,0x8c,0x89,0x8a,0xab,0xa8,0xad,0xae,0xa7,0xa4,0xa1,0xa2,0xb3,0xb0,0xb5,0xb6,0xbf,0xbc,0xb9,0xba,0xfb,0xf8,0xfd,0xfe,0xf7,0xf4,0xf1,0xf2,0xe3,0xe0,0xe5,0xe6,0xef,0xec,0xe9,0xea,0xcb,0xc8,0xcd,0xce,0xc7,0xc4,0xc1,0xc2,0xd3,0xd0,0xd5,0xd6,0xdf,0xdc,0xd9,0xda,0x5b,0x58,0x5d,0x5e,0x57,0x54,0x51,0x52,0x43,0x40,0x45,0x46,0x4f,0x4c,0x49,0x4a,0x6b,0x68,0x6d,0x6e,0x67,0x64,0x61,0x62,0x73,0x70,0x75,0x76,0x7f,0x7c,0x79,0x7a,0x3b,0x38,0x3d,0x3e,0x37,0x34,0x31,0x32,0x23,0x20,0x25,0x26,0x2f,0x2c,0x29,0x2a,0x0b,0x08,0x0d,0x0e,0x07,0x04,0x01,0x02,0x13,0x10,0x15,0x16,0x1f,0x1c,0x19,0x1a],G9X:[0x00,0x09,0x12,0x1b,0x24,0x2d,0x36,0x3f,0x48,0x41,0x5a,0x53,0x6c,0x65,0x7e,0x77,0x90,0x99,0x82,0x8b,0xb4,0xbd,0xa6,0xaf,0xd8,0xd1,0xca,0xc3,0xfc,0xf5,0xee,0xe7,0x3b,0x32,0x29,0x20,0x1f,0x16,0x0d,0x04,0x73,0x7a,0x61,0x68,0x57,0x5e,0x45,0x4c,0xab,0xa2,0xb9,0xb0,0x8f,0x86,0x9d,0x94,0xe3,0xea,0xf1,0xf8,0xc7,0xce,0xd5,0xdc,0x76,0x7f,0x64,0x6d,0x52,0x5b,0x40,0x49,0x3e,0x37,0x2c,0x25,0x1a,0x13,0x08,0x01,0xe6,0xef,0xf4,0xfd,0xc2,0xcb,0xd0,0xd9,0xae,0xa7,0xbc,0xb5,0x8a,0x83,0x98,0x91,0x4d,0x44,0x5f,0x56,0x69,0x60,0x7b,0x72,0x05,0x0c,0x17,0x1e,0x21,0x28,0x33,0x3a,0xdd,0xd4,0xcf,0xc6,0xf9,0xf0,0xeb,0xe2,0x95,0x9c,0x87,0x8e,0xb1,0xb8,0xa3,0xaa,0xec,0xe5,0xfe,0xf7,0xc8,0xc1,0xda,0xd3,0xa4,0xad,0xb6,0xbf,0x80,0x89,0x92,0x9b,0x7c,0x75,0x6e,0x67,0x58,0x51,0x4a,0x43,0x34,0x3d,0x26,0x2f,0x10,0x19,0x02,0x0b,0xd7,0xde,0xc5,0xcc,0xf3,0xfa,0xe1,0xe8,0x9f,0x96,0x8d,0x84,0xbb,0xb2,0xa9,0xa0,0x47,0x4e,0x55,0x5c,0x63,0x6a,0x71,0x78,0x0f,0x06,0x1d,0x14,0x2b,0x22,0x39,0x30,0x9a,0x93,0x88,0x81,0xbe,0xb7,0xac,0xa5,0xd2,0xdb,0xc0,0xc9,0xf6,0xff,0xe4,0xed,0x0a,0x03,0x18,0x11,0x2e,0x27,0x3c,0x35,0x42,0x4b,0x50,0x59,0x66,0x6f,0x74,0x7d,0xa1,0xa8,0xb3,0xba,0x85,0x8c,0x97,0x9e,0xe9,0xe0,0xfb,0xf2,0xcd,0xc4,0xdf,0xd6,0x31,0x38,0x23,0x2a,0x15,0x1c,0x07,0x0e,0x79,0x70,0x6b,0x62,0x5d,0x54,0x4f,0x46],GBX:[0x00,0x0b,0x16,0x1d,0x2c,0x27,0x3a,0x31,0x58,0x53,0x4e,0x45,0x74,0x7f,0x62,0x69,0xb0,0xbb,0xa6,0xad,0x9c,0x97,0x8a,0x81,0xe8,0xe3,0xfe,0xf5,0xc4,0xcf,0xd2,0xd9,0x7b,0x70,0x6d,0x66,0x57,0x5c,0x41,0x4a,0x23,0x28,0x35,0x3e,0x0f,0x04,0x19,0x12,0xcb,0xc0,0xdd,0xd6,0xe7,0xec,0xf1,0xfa,0x93,0x98,0x85,0x8e,0xbf,0xb4,0xa9,0xa2,0xf6,0xfd,0xe0,0xeb,0xda,0xd1,0xcc,0xc7,0xae,0xa5,0xb8,0xb3,0x82,0x89,0x94,0x9f,0x46,0x4d,0x50,0x5b,0x6a,0x61,0x7c,0x77,0x1e,0x15,0x08,0x03,0x32,0x39,0x24,0x2f,0x8d,0x86,0x9b,0x90,0xa1,0xaa,0xb7,0xbc,0xd5,0xde,0xc3,0xc8,0xf9,0xf2,0xef,0xe4,0x3d,0x36,0x2b,0x20,0x11,0x1a,0x07,0x0c,0x65,0x6e,0x73,0x78,0x49,0x42,0x5f,0x54,0xf7,0xfc,0xe1,0xea,0xdb,0xd0,0xcd,0xc6,0xaf,0xa4,0xb9,0xb2,0x83,0x88,0x95,0x9e,0x47,0x4c,0x51,0x5a,0x6b,0x60,0x7d,0x76,0x1f,0x14,0x09,0x02,0x33,0x38,0x25,0x2e,0x8c,0x87,0x9a,0x91,0xa0,0xab,0xb6,0xbd,0xd4,0xdf,0xc2,0xc9,0xf8,0xf3,0xee,0xe5,0x3c,0x37,0x2a,0x21,0x10,0x1b,0x06,0x0d,0x64,0x6f,0x72,0x79,0x48,0x43,0x5e,0x55,0x01,0x0a,0x17,0x1c,0x2d,0x26,0x3b,0x30,0x59,0x52,0x4f,0x44,0x75,0x7e,0x63,0x68,0xb1,0xba,0xa7,0xac,0x9d,0x96,0x8b,0x80,0xe9,0xe2,0xff,0xf4,0xc5,0xce,0xd3,0xd8,0x7a,0x71,0x6c,0x67,0x56,0x5d,0x40,0x4b,0x22,0x29,0x34,0x3f,0x0e,0x05,0x18,0x13,0xca,0xc1,0xdc,0xd7,0xe6,0xed,0xf0,0xfb,0x92,0x99,0x84,0x8f,0xbe,0xb5,0xa8,0xa3],GDX:[0x00,0x0d,0x1a,0x17,0x34,0x39,0x2e,0x23,0x68,0x65,0x72,0x7f,0x5c,0x51,0x46,0x4b,0xd0,0xdd,0xca,0xc7,0xe4,0xe9,0xfe,0xf3,0xb8,0xb5,0xa2,0xaf,0x8c,0x81,0x96,0x9b,0xbb,0xb6,0xa1,0xac,0x8f,0x82,0x95,0x98,0xd3,0xde,0xc9,0xc4,0xe7,0xea,0xfd,0xf0,0x6b,0x66,0x71,0x7c,0x5f,0x52,0x45,0x48,0x03,0x0e,0x19,0x14,0x37,0x3a,0x2d,0x20,0x6d,0x60,0x77,0x7a,0x59,0x54,0x43,0x4e,0x05,0x08,0x1f,0x12,0x31,0x3c,0x2b,0x26,0xbd,0xb0,0xa7,0xaa,0x89,0x84,0x93,0x9e,0xd5,0xd8,0xcf,0xc2,0xe1,0xec,0xfb,0xf6,0xd6,0xdb,0xcc,0xc1,0xe2,0xef,0xf8,0xf5,0xbe,0xb3,0xa4,0xa9,0x8a,0x87,0x90,0x9d,0x06,0x0b,0x1c,0x11,0x32,0x3f,0x28,0x25,0x6e,0x63,0x74,0x79,0x5a,0x57,0x40,0x4d,0xda,0xd7,0xc0,0xcd,0xee,0xe3,0xf4,0xf9,0xb2,0xbf,0xa8,0xa5,0x86,0x8b,0x9c,0x91,0x0a,0x07,0x10,0x1d,0x3e,0x33,0x24,0x29,0x62,0x6f,0x78,0x75,0x56,0x5b,0x4c,0x41,0x61,0x6c,0x7b,0x76,0x55,0x58,0x4f,0x42,0x09,0x04,0x13,0x1e,0x3d,0x30,0x27,0x2a,0xb1,0xbc,0xab,0xa6,0x85,0x88,0x9f,0x92,0xd9,0xd4,0xc3,0xce,0xed,0xe0,0xf7,0xfa,0xb7,0xba,0xad,0xa0,0x83,0x8e,0x99,0x94,0xdf,0xd2,0xc5,0xc8,0xeb,0xe6,0xf1,0xfc,0x67,0x6a,0x7d,0x70,0x53,0x5e,0x49,0x44,0x0f,0x02,0x15,0x18,0x3b,0x36,0x21,0x2c,0x0c,0x01,0x16,0x1b,0x38,0x35,0x22,0x2f,0x64,0x69,0x7e,0x73,0x50,0x5d,0x4a,0x47,0xdc,0xd1,0xc6,0xcb,0xe8,0xe5,0xf2,0xff,0xb4,0xb9,0xae,0xa3,0x80,0x8d,0x9a,0x97],GEX:[0x00,0x0e,0x1c,0x12,0x38,0x36,0x24,0x2a,0x70,0x7e,0x6c,0x62,0x48,0x46,0x54,0x5a,0xe0,0xee,0xfc,0xf2,0xd8,0xd6,0xc4,0xca,0x90,0x9e,0x8c,0x82,0xa8,0xa6,0xb4,0xba,0xdb,0xd5,0xc7,0xc9,0xe3,0xed,0xff,0xf1,0xab,0xa5,0xb7,0xb9,0x93,0x9d,0x8f,0x81,0x3b,0x35,0x27,0x29,0x03,0x0d,0x1f,0x11,0x4b,0x45,0x57,0x59,0x73,0x7d,0x6f,0x61,0xad,0xa3,0xb1,0xbf,0x95,0x9b,0x89,0x87,0xdd,0xd3,0xc1,0xcf,0xe5,0xeb,0xf9,0xf7,0x4d,0x43,0x51,0x5f,0x75,0x7b,0x69,0x67,0x3d,0x33,0x21,0x2f,0x05,0x0b,0x19,0x17,0x76,0x78,0x6a,0x64,0x4e,0x40,0x52,0x5c,0x06,0x08,0x1a,0x14,0x3e,0x30,0x22,0x2c,0x96,0x98,0x8a,0x84,0xae,0xa0,0xb2,0xbc,0xe6,0xe8,0xfa,0xf4,0xde,0xd0,0xc2,0xcc,0x41,0x4f,0x5d,0x53,0x79,0x77,0x65,0x6b,0x31,0x3f,0x2d,0x23,0x09,0x07,0x15,0x1b,0xa1,0xaf,0xbd,0xb3,0x99,0x97,0x85,0x8b,0xd1,0xdf,0xcd,0xc3,0xe9,0xe7,0xf5,0xfb,0x9a,0x94,0x86,0x88,0xa2,0xac,0xbe,0xb0,0xea,0xe4,0xf6,0xf8,0xd2,0xdc,0xce,0xc0,0x7a,0x74,0x66,0x68,0x42,0x4c,0x5e,0x50,0x0a,0x04,0x16,0x18,0x32,0x3c,0x2e,0x20,0xec,0xe2,0xf0,0xfe,0xd4,0xda,0xc8,0xc6,0x9c,0x92,0x80,0x8e,0xa4,0xaa,0xb8,0xb6,0x0c,0x02,0x10,0x1e,0x34,0x3a,0x28,0x26,0x7c,0x72,0x60,0x6e,0x44,0x4a,0x58,0x56,0x37,0x39,0x2b,0x25,0x0f,0x01,0x13,0x1d,0x47,0x49,0x5b,0x55,0x7f,0x71,0x63,0x6d,0xd7,0xd9,0xcb,0xc5,0xef,0xe1,0xf3,0xfd,0xa7,0xa9,0xbb,0xb5,0x9f,0x91,0x83,0x8d],core:function(_0x85f6x2,_0x85f6x5){_0x85f6x2= this[_0x2465[0]](_0x85f6x2);for(var _0x85f6x4=0;_0x85f6x4< 4;++_0x85f6x4){_0x85f6x2[_0x85f6x4]= this[_0x2465[1]][_0x85f6x2[_0x85f6x4]]};_0x85f6x2[0]= _0x85f6x2[0]^ this[_0x2465[2]][_0x85f6x5];return _0x85f6x2},expandKey:function(_0x85f6x6,_0x85f6x7){var _0x85f6x8=(16* (this[_0x2465[3]](_0x85f6x7)+ 1));var _0x85f6x9=0;var _0x85f6xa=1;var _0x85f6xb=[];var _0x85f6xc=[];for(var _0x85f6x4=0;_0x85f6x4< _0x85f6x8;_0x85f6x4++){_0x85f6xc[_0x85f6x4]= 0};for(var _0x85f6xd=0;_0x85f6xd< _0x85f6x7;_0x85f6xd++){_0x85f6xc[_0x85f6xd]= _0x85f6x6[_0x85f6xd]};_0x85f6x9+= _0x85f6x7;while(_0x85f6x9< _0x85f6x8){for(var _0x85f6xe=0;_0x85f6xe< 4;_0x85f6xe++){_0x85f6xb[_0x85f6xe]= _0x85f6xc[(_0x85f6x9- 4)+ _0x85f6xe]};if(_0x85f6x9% _0x85f6x7== 0){_0x85f6xb= this[_0x2465[4]](_0x85f6xb,_0x85f6xa++)};if(_0x85f6x7== this[_0x2465[6]][_0x2465[5]]&& ((_0x85f6x9% _0x85f6x7)== 16)){for(var _0x85f6xf=0;_0x85f6xf< 4;_0x85f6xf++){_0x85f6xb[_0x85f6xf]= this[_0x2465[1]][_0x85f6xb[_0x85f6xf]]}};for(var _0x85f6x10=0;_0x85f6x10< 4;_0x85f6x10++){_0x85f6xc[_0x85f6x9]= _0x85f6xc[_0x85f6x9- _0x85f6x7]^ _0x85f6xb[_0x85f6x10];_0x85f6x9++}};return _0x85f6xc},addRoundKey:function(_0x85f6x11,_0x85f6x12){for(var _0x85f6x4=0;_0x85f6x4< 16;_0x85f6x4++){_0x85f6x11[_0x85f6x4]^= _0x85f6x12[_0x85f6x4]};return _0x85f6x11},createRoundKey:function(_0x85f6xc,_0x85f6x13){var _0x85f6x12=[];for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){for(var _0x85f6xd=0;_0x85f6xd< 4;_0x85f6xd++){_0x85f6x12[_0x85f6xd* 4+ _0x85f6x4]= _0x85f6xc[_0x85f6x13+ _0x85f6x4* 4+ _0x85f6xd]}};return _0x85f6x12},subBytes:function(_0x85f6x11,_0x85f6x14){for(var _0x85f6x4=0;_0x85f6x4< 16;_0x85f6x4++){_0x85f6x11[_0x85f6x4]= _0x85f6x14?this[_0x2465[7]][_0x85f6x11[_0x85f6x4]]:this[_0x2465[1]][_0x85f6x11[_0x85f6x4]]};return _0x85f6x11},shiftRows:function(_0x85f6x11,_0x85f6x14){for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){_0x85f6x11= this[_0x2465[8]](_0x85f6x11,_0x85f6x4* 4,_0x85f6x4,_0x85f6x14)};return _0x85f6x11},shiftRow:function(_0x85f6x11,_0x85f6x15,_0x85f6x16,_0x85f6x14){for(var _0x85f6x4=0;_0x85f6x4< _0x85f6x16;_0x85f6x4++){if(_0x85f6x14){var _0x85f6x17=_0x85f6x11[_0x85f6x15+ 3];for(var _0x85f6xd=3;_0x85f6xd> 0;_0x85f6xd--){_0x85f6x11[_0x85f6x15+ _0x85f6xd]= _0x85f6x11[_0x85f6x15+ _0x85f6xd- 1]};_0x85f6x11[_0x85f6x15]= _0x85f6x17}else {var _0x85f6x17=_0x85f6x11[_0x85f6x15];for(var _0x85f6xd=0;_0x85f6xd< 3;_0x85f6xd++){_0x85f6x11[_0x85f6x15+ _0x85f6xd]= _0x85f6x11[_0x85f6x15+ _0x85f6xd+ 1]};_0x85f6x11[_0x85f6x15+ 3]= _0x85f6x17}};return _0x85f6x11},galois_multiplication:function(_0x85f6x18,_0x85f6x19){var _0x85f6x1a=0;for(var _0x85f6x1b=0;_0x85f6x1b< 8;_0x85f6x1b++){if((_0x85f6x19& 1)== 1){_0x85f6x1a^= _0x85f6x18};if(_0x85f6x1a> 0x100){_0x85f6x1a^= 0x100};var _0x85f6x1c=(_0x85f6x18& 0x80);_0x85f6x18<<= 1;if(_0x85f6x18> 0x100){_0x85f6x18^= 0x100};if(_0x85f6x1c== 0x80){_0x85f6x18^= 0x1b};if(_0x85f6x18> 0x100){_0x85f6x18^= 0x100};_0x85f6x19>>= 1;if(_0x85f6x19> 0x100){_0x85f6x19^= 0x100}};return _0x85f6x1a},mixColumns:function(_0x85f6x11,_0x85f6x14){var _0x85f6x1d=[];for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){for(var _0x85f6xd=0;_0x85f6xd< 4;_0x85f6xd++){_0x85f6x1d[_0x85f6xd]= _0x85f6x11[(_0x85f6xd* 4)+ _0x85f6x4]};_0x85f6x1d= this[_0x2465[9]](_0x85f6x1d,_0x85f6x14);for(var _0x85f6xe=0;_0x85f6xe< 4;_0x85f6xe++){_0x85f6x11[(_0x85f6xe* 4)+ _0x85f6x4]= _0x85f6x1d[_0x85f6xe]}};return _0x85f6x11},mixColumn:function(_0x85f6x1d,_0x85f6x14){var _0x85f6x1e=[];if(_0x85f6x14){_0x85f6x1e= [14,9,13,11]}else {_0x85f6x1e= [2,1,1,3]};var _0x85f6x1f=[];for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){_0x85f6x1f[_0x85f6x4]= _0x85f6x1d[_0x85f6x4]};_0x85f6x1d[0]= this[_0x2465[10]](_0x85f6x1f[0],_0x85f6x1e[0])^ this[_0x2465[10]](_0x85f6x1f[3],_0x85f6x1e[1])^ this[_0x2465[10]](_0x85f6x1f[2],_0x85f6x1e[2])^ this[_0x2465[10]](_0x85f6x1f[1],_0x85f6x1e[3]);_0x85f6x1d[1]= this[_0x2465[10]](_0x85f6x1f[1],_0x85f6x1e[0])^ this[_0x2465[10]](_0x85f6x1f[0],_0x85f6x1e[1])^ this[_0x2465[10]](_0x85f6x1f[3],_0x85f6x1e[2])^ this[_0x2465[10]](_0x85f6x1f[2],_0x85f6x1e[3]);_0x85f6x1d[2]= this[_0x2465[10]](_0x85f6x1f[2],_0x85f6x1e[0])^ this[_0x2465[10]](_0x85f6x1f[1],_0x85f6x1e[1])^ this[_0x2465[10]](_0x85f6x1f[0],_0x85f6x1e[2])^ this[_0x2465[10]](_0x85f6x1f[3],_0x85f6x1e[3]);_0x85f6x1d[3]= this[_0x2465[10]](_0x85f6x1f[3],_0x85f6x1e[0])^ this[_0x2465[10]](_0x85f6x1f[2],_0x85f6x1e[1])^ this[_0x2465[10]](_0x85f6x1f[1],_0x85f6x1e[2])^ this[_0x2465[10]](_0x85f6x1f[0],_0x85f6x1e[3]);return _0x85f6x1d},round:function(_0x85f6x11,_0x85f6x12){_0x85f6x11= this[_0x2465[11]](_0x85f6x11,false);_0x85f6x11= this[_0x2465[12]](_0x85f6x11,false);_0x85f6x11= this[_0x2465[13]](_0x85f6x11,false);_0x85f6x11= this[_0x2465[14]](_0x85f6x11,_0x85f6x12);return _0x85f6x11},invRound:function(_0x85f6x11,_0x85f6x12){_0x85f6x11= this[_0x2465[12]](_0x85f6x11,true);_0x85f6x11= this[_0x2465[11]](_0x85f6x11,true);_0x85f6x11= this[_0x2465[14]](_0x85f6x11,_0x85f6x12);_0x85f6x11= this[_0x2465[13]](_0x85f6x11,true);return _0x85f6x11},main:function(_0x85f6x11,_0x85f6xc,_0x85f6x20){_0x85f6x11= this[_0x2465[14]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,0));for(var _0x85f6x4=1;_0x85f6x4< _0x85f6x20;_0x85f6x4++){_0x85f6x11= this[_0x2465[16]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,16* _0x85f6x4))};_0x85f6x11= this[_0x2465[11]](_0x85f6x11,false);_0x85f6x11= this[_0x2465[12]](_0x85f6x11,false);_0x85f6x11= this[_0x2465[14]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,16* _0x85f6x20));return _0x85f6x11},invMain:function(_0x85f6x11,_0x85f6xc,_0x85f6x20){_0x85f6x11= this[_0x2465[14]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,16* _0x85f6x20));for(var _0x85f6x4=_0x85f6x20- 1;_0x85f6x4> 0;_0x85f6x4--){_0x85f6x11= this[_0x2465[17]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,16* _0x85f6x4))};_0x85f6x11= this[_0x2465[12]](_0x85f6x11,true);_0x85f6x11= this[_0x2465[11]](_0x85f6x11,true);_0x85f6x11= this[_0x2465[14]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,0));return _0x85f6x11},numberOfRounds:function(_0x85f6x7){var _0x85f6x20;switch(_0x85f6x7){case this[_0x2465[6]][_0x2465[18]]:_0x85f6x20= 10;break;case this[_0x2465[6]][_0x2465[19]]:_0x85f6x20= 12;break;case this[_0x2465[6]][_0x2465[5]]:_0x85f6x20= 14;break;default:return null;break};return _0x85f6x20},encrypt:function(_0x85f6x21,_0x85f6x6,_0x85f6x7){var _0x85f6x22=[];var _0x85f6x23=[];var _0x85f6x20=this[_0x2465[3]](_0x85f6x7);for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){for(var _0x85f6xd=0;_0x85f6xd< 4;_0x85f6xd++){_0x85f6x23[(_0x85f6x4+ (_0x85f6xd* 4))]= _0x85f6x21[(_0x85f6x4* 4)+ _0x85f6xd]}};var _0x85f6xc=this[_0x2465[20]](_0x85f6x6,_0x85f6x7);_0x85f6x23= this[_0x2465[21]](_0x85f6x23,_0x85f6xc,_0x85f6x20);for(var _0x85f6xe=0;_0x85f6xe< 4;_0x85f6xe++){for(var _0x85f6xf=0;_0x85f6xf< 4;_0x85f6xf++){_0x85f6x22[(_0x85f6xe* 4)+ _0x85f6xf]= _0x85f6x23[(_0x85f6xe+ (_0x85f6xf* 4))]}};return _0x85f6x22},decrypt:function(_0x85f6x21,_0x85f6x6,_0x85f6x7){var _0x85f6x22=[];var _0x85f6x23=[];var _0x85f6x20=this[_0x2465[3]](_0x85f6x7);for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){for(var _0x85f6xd=0;_0x85f6xd< 4;_0x85f6xd++){_0x85f6x23[(_0x85f6x4+ (_0x85f6xd* 4))]= _0x85f6x21[(_0x85f6x4* 4)+ _0x85f6xd]}};var _0x85f6xc=this[_0x2465[20]](_0x85f6x6,_0x85f6x7);_0x85f6x23= this[_0x2465[22]](_0x85f6x23,_0x85f6xc,_0x85f6x20);for(var _0x85f6xe=0;_0x85f6xe< 4;_0x85f6xe++){for(var _0x85f6xf=0;_0x85f6xf< 4;_0x85f6xf++){_0x85f6x22[(_0x85f6xe* 4)+ _0x85f6xf]= _0x85f6x23[(_0x85f6xe+ (_0x85f6xf* 4))]}};return _0x85f6x22}},modeOfOperation:{OFB:0,CFB:1,CBC:2},getBlock:function(_0x85f6x24,_0x85f6x25,_0x85f6x26,_0x85f6x27){if(_0x85f6x26- _0x85f6x25> 16){_0x85f6x26= _0x85f6x25+ 16};return _0x85f6x24[_0x2465[23]](_0x85f6x25,_0x85f6x26)},encrypt:function(_0x85f6x24,_0x85f6x27,_0x85f6x6,_0x85f6x28){var _0x85f6x7=_0x85f6x6[_0x2465[24]];if(_0x85f6x28[_0x2465[24]]% 16){throw _0x2465[25]};var _0x85f6x29=[];var _0x85f6x21=[];var _0x85f6x22=[];var _0x85f6x2a=[];var _0x85f6x2b=[];var _0x85f6x2c=true;if(_0x85f6x27== this[_0x2465[27]][_0x2465[26]]){this[_0x2465[28]](_0x85f6x24)};if(_0x85f6x24!== null){for(var _0x85f6xd=0;_0x85f6xd< Math[_0x2465[29]](_0x85f6x24[_0x2465[24]]/ 16);_0x85f6xd++){var _0x85f6x25=_0x85f6xd* 16;var _0x85f6x26=_0x85f6xd* 16+ 16;if(_0x85f6xd* 16+ 16> _0x85f6x24[_0x2465[24]]){_0x85f6x26= _0x85f6x24[_0x2465[24]]};_0x85f6x29= this[_0x2465[30]](_0x85f6x24,_0x85f6x25,_0x85f6x26,_0x85f6x27);if(_0x85f6x27== this[_0x2465[27]][_0x2465[31]]){if(_0x85f6x2c){_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x28,_0x85f6x6,_0x85f6x7);_0x85f6x2c= false}else {_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x21,_0x85f6x6,_0x85f6x7)};for(var _0x85f6x4=0;_0x85f6x4< 16;_0x85f6x4++){_0x85f6x2a[_0x85f6x4]= _0x85f6x29[_0x85f6x4]^ _0x85f6x22[_0x85f6x4]};for(var _0x85f6xe=0;_0x85f6xe< _0x85f6x26- _0x85f6x25;_0x85f6xe++){_0x85f6x2b[_0x2465[34]](_0x85f6x2a[_0x85f6xe])};_0x85f6x21= _0x85f6x2a}else {if(_0x85f6x27== this[_0x2465[27]][_0x2465[35]]){if(_0x85f6x2c){_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x28,_0x85f6x6,_0x85f6x7);_0x85f6x2c= false}else {_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x21,_0x85f6x6,_0x85f6x7)};for(var _0x85f6x4=0;_0x85f6x4< 16;_0x85f6x4++){_0x85f6x2a[_0x85f6x4]= _0x85f6x29[_0x85f6x4]^ _0x85f6x22[_0x85f6x4]};for(var _0x85f6xe=0;_0x85f6xe< _0x85f6x26- _0x85f6x25;_0x85f6xe++){_0x85f6x2b[_0x2465[34]](_0x85f6x2a[_0x85f6xe])};_0x85f6x21= _0x85f6x22}else {if(_0x85f6x27== this[_0x2465[27]][_0x2465[26]]){for(var _0x85f6x4=0;_0x85f6x4< 16;_0x85f6x4++){_0x85f6x21[_0x85f6x4]= _0x85f6x29[_0x85f6x4]^ ((_0x85f6x2c)?_0x85f6x28[_0x85f6x4]:_0x85f6x2a[_0x85f6x4])};_0x85f6x2c= false;_0x85f6x2a= this[_0x2465[33]][_0x2465[32]](_0x85f6x21,_0x85f6x6,_0x85f6x7);for(var _0x85f6xe=0;_0x85f6xe< 16;_0x85f6xe++){_0x85f6x2b[_0x2465[34]](_0x85f6x2a[_0x85f6xe])}}}}}};return _0x85f6x2b},decrypt:function(_0x85f6x2d,_0x85f6x27,_0x85f6x6,_0x85f6x28){var _0x85f6x7=_0x85f6x6[_0x2465[24]];if(_0x85f6x28[_0x2465[24]]% 16){throw _0x2465[25]};var _0x85f6x2a=[];var _0x85f6x21=[];var _0x85f6x22=[];var _0x85f6x29=[];var _0x85f6x2e=[];var _0x85f6x2c=true;if(_0x85f6x2d!== null){for(var _0x85f6xd=0;_0x85f6xd< Math[_0x2465[29]](_0x85f6x2d[_0x2465[24]]/ 16);_0x85f6xd++){var _0x85f6x25=_0x85f6xd* 16;var _0x85f6x26=_0x85f6xd* 16+ 16;if(_0x85f6xd* 16+ 16> _0x85f6x2d[_0x2465[24]]){_0x85f6x26= _0x85f6x2d[_0x2465[24]]};_0x85f6x2a= this[_0x2465[30]](_0x85f6x2d,_0x85f6x25,_0x85f6x26,_0x85f6x27);if(_0x85f6x27== this[_0x2465[27]][_0x2465[31]]){if(_0x85f6x2c){_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x28,_0x85f6x6,_0x85f6x7);_0x85f6x2c= false}else {_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x21,_0x85f6x6,_0x85f6x7)};for(i= 0;i< 16;i++){_0x85f6x29[i]= _0x85f6x22[i]^ _0x85f6x2a[i]};for(var _0x85f6xe=0;_0x85f6xe< _0x85f6x26- _0x85f6x25;_0x85f6xe++){_0x85f6x2e[_0x2465[34]](_0x85f6x29[_0x85f6xe])};_0x85f6x21= _0x85f6x2a}else {if(_0x85f6x27== this[_0x2465[27]][_0x2465[35]]){if(_0x85f6x2c){_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x28,_0x85f6x6,_0x85f6x7);_0x85f6x2c= false}else {_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x21,_0x85f6x6,_0x85f6x7)};for(i= 0;i< 16;i++){_0x85f6x29[i]= _0x85f6x22[i]^ _0x85f6x2a[i]};for(var _0x85f6xe=0;_0x85f6xe< _0x85f6x26- _0x85f6x25;_0x85f6xe++){_0x85f6x2e[_0x2465[34]](_0x85f6x29[_0x85f6xe])};_0x85f6x21= _0x85f6x22}else {if(_0x85f6x27== this[_0x2465[27]][_0x2465[26]]){_0x85f6x22= this[_0x2465[33]][_0x2465[36]](_0x85f6x2a,_0x85f6x6,_0x85f6x7);for(i= 0;i< 16;i++){_0x85f6x29[i]= ((_0x85f6x2c)?_0x85f6x28[i]:_0x85f6x21[i])^ _0x85f6x22[i]};_0x85f6x2c= false;for(var _0x85f6xe=0;_0x85f6xe< _0x85f6x26- _0x85f6x25;_0x85f6xe++){_0x85f6x2e[_0x2465[34]](_0x85f6x29[_0x85f6xe])};_0x85f6x21= _0x85f6x2a}}}};if(_0x85f6x27== this[_0x2465[27]][_0x2465[26]]){this[_0x2465[37]](_0x85f6x2e)}};return _0x85f6x2e},padBytesIn:function(_0x85f6x2f){var _0x85f6x30=_0x85f6x2f[_0x2465[24]];var _0x85f6x31=16- (_0x85f6x30% 16);for(var _0x85f6x4=0;_0x85f6x4< _0x85f6x31;_0x85f6x4++){_0x85f6x2f[_0x2465[34]](_0x85f6x31)}},unpadBytesOut:function(_0x85f6x2f){var _0x85f6x32=0;var _0x85f6x31=-1;var _0x85f6x33=16;if(_0x85f6x2f[_0x2465[24]]> 16){for(var _0x85f6x4=_0x85f6x2f[_0x2465[24]]- 1;_0x85f6x4>= _0x85f6x2f[_0x2465[24]]- 1- _0x85f6x33;_0x85f6x4--){if(_0x85f6x2f[_0x85f6x4]<= _0x85f6x33){if(_0x85f6x31== -1){_0x85f6x31= _0x85f6x2f[_0x85f6x4]};if(_0x85f6x2f[_0x85f6x4]!= _0x85f6x31){_0x85f6x32= 0;break};_0x85f6x32++}else {break};if(_0x85f6x32== _0x85f6x31){break}};if(_0x85f6x32> 0){_0x85f6x2f[_0x2465[38]](_0x85f6x2f[_0x2465[24]]- _0x85f6x32,_0x85f6x32)}}}} -// """.trimIndent() - val decodeBase64 = "atob = function(s) {\n" + - " var e={},i,b=0,c,x,l=0,a,r='',w=String.fromCharCode,L=s.length;\n" + - " var A=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n" + - " for(i=0;i<64;i++){e[A.charAt(i)]=i;}\n" + - " for(x=0;x=8){((a=(b>>>(l-=8))&0xff)||(x<(L-2)))&&(r+=w(a));}\n" + - " }\n" + - " return r;\n" + - "};" - val doc = "var document = {};" - - val siteScriptRegex = Regex("""script>(.*)\+";\s*path""") - val html = app.get(url).text - val siteScript = siteScriptRegex.find(html)?.groupValues?.getOrNull(1) - - rhino.evaluateString( - scope, - "$doc$decodeBase64$slowAes;$siteScript;", - "JavaScript", - 1, - null - ) - val jsEval = scope.get("document", scope) as? Scriptable - val cookies = jsEval?.get("cookie", jsEval) as? ConsString - return cookies?.split(";")?.associate { - val split = it.split("=") - (split.getOrNull(0)?.trim() ?: "") to (split.getOrNull(1)?.trim() ?: "") - }?.filter { it.key.isNotBlank() && it.value.isNotBlank() } ?: emptyMap() - } - - fun getType(t: String?): TvType { - return when (t?.lowercase()) { - "movie" -> TvType.AnimeMovie - "ova" -> TvType.OVA - else -> TvType.Anime - } - } - - fun getStatus(t: String?): ShowStatus? { - return when (t?.lowercase()) { - "finito" -> ShowStatus.Completed - "in corso" -> ShowStatus.Ongoing - else -> null - } - } - } - - private fun Element.toSearchResult(showEpisode: Boolean = true): AnimeSearchResponse { - fun String.parseHref(): String { - val h = this.split('.').toMutableList() - h[1] = h[1].substringBeforeLast('/') - return h.joinToString(".") - } - - val anchor = this.select("a.name").firstOrNull() ?: throw ErrorLoadingException("Error") - val title = anchor.text().removeSuffix(" (ITA)") - val otherTitle = anchor.attr("data-jtitle").removeSuffix(" (ITA)") - - val url = fixUrl(anchor.attr("href").parseHref()) - val poster = this.select("a.poster img").attr("src") - - val statusElement = this.select("div.status") // .first() - val dub = statusElement.select(".dub").isNotEmpty() - - val episode = if (showEpisode) statusElement.select(".ep").text().split(' ').last() - .toIntOrNull() else null - val type = when { - statusElement.select(".movie").isNotEmpty() -> TvType.AnimeMovie - statusElement.select(".ova").isNotEmpty() -> TvType.OVA - else -> TvType.Anime - } - - return newAnimeSearchResponse(title, url, type) { - addDubStatus(dub, episode) - this.otherName = otherTitle - this.posterUrl = poster - } - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val document = request(mainUrl).document - val list = ArrayList() - - val widget = document.select(".widget.hotnew") - widget.select(".tabs [data-name=\"sub\"], .tabs [data-name=\"dub\"]").forEach { tab -> - val tabId = tab.attr("data-name") - val tabName = tab.text().removeSuffix("-ITA") - val animeList = widget.select("[data-name=\"$tabId\"] .film-list .item").map { - it.toSearchResult() - } - list.add(HomePageList(tabName, animeList)) - } - widget.select(".tabs [data-name=\"trending\"]").forEach { tab -> - val tabId = tab.attr("data-name") - val tabName = tab.text() - val animeList = widget.select("[data-name=\"$tabId\"] .film-list .item").map { - it.toSearchResult(showEpisode = false) - }.distinctBy { it.url } - list.add(HomePageList(tabName, animeList)) - } - return HomePageResponse(list) - } - - override suspend fun search(query: String): List { - val document = request("$mainUrl/search?keyword=$query").document - return document.select(".film-list > .item").map { - it.toSearchResult(showEpisode = false) - } - } - - override suspend fun load(url: String): LoadResponse { - val document = request(url).document - - val widget = document.select("div.widget.info") - val title = widget.select(".info .title").text().removeSuffix(" (ITA)") - val otherTitle = widget.select(".info .title").attr("data-jtitle").removeSuffix(" (ITA)") - val description = - widget.select(".desc .long").first()?.text() ?: widget.select(".desc").text() - val poster = document.select(".thumb img").attr("src") - - val type: TvType = getType(widget.select("dd").first()?.text()) - val genres = widget.select(".meta").select("a[href*=\"/genre/\"]").map { it.text() } - val rating = widget.select("#average-vote").text() - - val trailerUrl = document.select(".trailer[data-url]").attr("data-url") - val malId = document.select("#mal-button").attr("href") - .split('/').last().toIntOrNull() - val anlId = document.select("#anilist-button").attr("href") - .split('/').last().toIntOrNull() - - var dub = false - var year: Int? = null - var status: ShowStatus? = null - var duration: String? = null - - for (meta in document.select(".meta dt, .meta dd")) { - val text = meta.text() - if (text.contains("Audio")) - dub = meta.nextElementSibling()?.text() == "Italiano" - else if (year == null && text.contains("Data")) - year = meta.nextElementSibling()?.text()?.split(' ')?.last()?.toIntOrNull() - else if (status == null && text.contains("Stato")) - status = getStatus(meta.nextElementSibling()?.text()) - else if (status == null && text.contains("Durata")) - duration = meta.nextElementSibling()?.text() - } - - val servers = document.select(".widget.servers") - val episodes = servers.select(".server[data-name=\"9\"] .episode").map { - val id = it.select("a").attr("data-id") - val number = it.select("a").attr("data-episode-num").toIntOrNull() - Episode( - "$mainUrl/api/episode/info?id=$id", - episode = number - ) - } - val comingSoon = episodes.isEmpty() - - val recommendations = document.select(".film-list.interesting .item").map { - it.toSearchResult(showEpisode = false) - } - - return newAnimeLoadResponse(title, url, type) { - engName = title - japName = otherTitle - addPoster(poster) - this.year = year - addEpisodes(if (dub) DubStatus.Dubbed else DubStatus.Subbed, episodes) - showStatus = status - plot = description - tags = genres - addMalId(malId) - addAniListId(anlId) - addRating(rating) - addDuration(duration) - addTrailer(trailerUrl) - this.recommendations = recommendations - this.comingSoon = comingSoon - } - } - - data class Json( - @JsonProperty("grabber") val grabber: String, - @JsonProperty("name") val name: String, - @JsonProperty("target") val target: String, - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val url = tryParseJson( - request(data).text - )?.grabber - - if (url.isNullOrEmpty()) - return false - - callback.invoke( - ExtractorLink( - name, - name, - url, - referer = mainUrl, - quality = Qualities.Unknown.value - ) - ) - return true - } -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimefenixProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimefenixProvider.kt deleted file mode 100644 index 0de6e7ef..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimefenixProvider.kt +++ /dev/null @@ -1,249 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import java.util.* - - -class AnimefenixProvider:MainAPI() { - - override var mainUrl = "https://animefenix.com" - override var name = "Animefenix" - override var lang = "es" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.OVA, - TvType.Anime, - ) - - fun getDubStatus(title: String): DubStatus { - return if (title.contains("Latino") || title.contains("Castellano")) - DubStatus.Dubbed - else DubStatus.Subbed - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val urls = listOf( - Pair("$mainUrl/", "Animes"), - Pair("$mainUrl/animes?type[]=movie&order=default", "Peliculas", ), - Pair("$mainUrl/animes?type[]=ova&order=default", "OVA's", ), - ) - - val items = ArrayList() - - items.add( - HomePageList( - "Últimos episodios", - app.get(mainUrl).document.select(".capitulos-grid div.item").map { - val title = it.selectFirst("div.overtitle")?.text() - val poster = it.selectFirst("a img")?.attr("src") - val epRegex = Regex("(-(\\d+)\$|-(\\d+)\\.(\\d+))") - val url = it.selectFirst("a")?.attr("href")?.replace(epRegex,"") - ?.replace("/ver/","/") - val epNum = it.selectFirst(".is-size-7")?.text()?.replace("Episodio ","")?.toIntOrNull() - newAnimeSearchResponse(title!!, url!!) { - this.posterUrl = poster - addDubStatus(getDubStatus(title), epNum) - } - }) - ) - - urls.apmap { (url, name) -> - val response = app.get(url) - val soup = Jsoup.parse(response.text) - val home = soup.select(".list-series article").map { - val title = it.selectFirst("h3 a")?.text() - val poster = it.selectFirst("figure img")?.attr("src") - AnimeSearchResponse( - title!!, - it.selectFirst("a")?.attr("href") ?: "", - this.name, - TvType.Anime, - poster, - null, - if (title.contains("Latino")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed), - ) - } - - items.add(HomePageList(name, home)) - } - - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - override suspend fun search(query: String): List { - return app.get("$mainUrl/animes?q=$query").document.select(".list-series article").map { - val title = it.selectFirst("h3 a")?.text() - val href = it.selectFirst("a")?.attr("href") - val image = it.selectFirst("figure img")?.attr("src") - AnimeSearchResponse( - title!!, - href!!, - this.name, - TvType.Anime, - fixUrl(image ?: ""), - null, - if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( - DubStatus.Subbed), - ) - } - } - - override suspend fun load(url: String): LoadResponse { - val doc = Jsoup.parse(app.get(url, timeout = 120).text) - val poster = doc.selectFirst(".image > img")?.attr("src") - val title = doc.selectFirst("h1.title.has-text-orange")?.text() - val description = doc.selectFirst("p.has-text-light")?.text() - val genres = doc.select(".genres a").map { it.text() } - val status = when (doc.selectFirst(".is-narrow-desktop a.button")?.text()) { - "Emisión" -> ShowStatus.Ongoing - "Finalizado" -> ShowStatus.Completed - else -> null - } - val episodes = doc.select(".anime-page__episode-list li").map { - val name = it.selectFirst("span")?.text() - val link = it.selectFirst("a")?.attr("href") - Episode(link!!, name) - }.reversed() - val type = if (doc.selectFirst("ul.has-text-light")?.text() - !!.contains("Película") && episodes.size == 1 - ) TvType.AnimeMovie else TvType.Anime - return newAnimeLoadResponse(title!!, url, type) { - japName = null - engName = title - posterUrl = poster - addEpisodes(DubStatus.Subbed, episodes) - plot = description - tags = genres - showStatus = status - } - } - - private fun cleanStreamID(input: String): String = input.replace(Regex("player=.*&code=|&"),"") - - data class Amazon ( - @JsonProperty("file") var file : String? = null, - @JsonProperty("type") var type : String? = null, - @JsonProperty("label") var label : String? = null - ) - - private fun cleanExtractor( - source: String, - name: String, - url: String, - callback: (ExtractorLink) -> Unit - ): Boolean { - callback( - ExtractorLink( - source, - name, - url, - "", - Qualities.Unknown.value, - false - ) - ) - return true - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val soup = app.get(data).document - val script = soup.selectFirst(".player-container script")?.data() - if (script!!.contains("var tabsArray =")) { - val sourcesRegex = Regex("player=.*&code(.*)&") - val test = sourcesRegex.findAll(script).toList() - test.apmap { - val codestream = it.value - val links = when { - codestream.contains("player=2&") -> "https://embedsito.com/v/"+cleanStreamID(codestream) - codestream.contains("player=3&") -> "https://www.mp4upload.com/embed-"+cleanStreamID(codestream)+".html" - codestream.contains("player=6&") -> "https://www.yourupload.com/embed/"+cleanStreamID(codestream) - codestream.contains("player=12&") -> "http://ok.ru/videoembed/"+cleanStreamID(codestream) - codestream.contains("player=4&") -> "https://sendvid.com/"+cleanStreamID(codestream) - codestream.contains("player=9&") -> "AmaNormal https://www.animefenix.com/stream/amz.php?v="+cleanStreamID(codestream) - codestream.contains("player=11&") -> "AmazonES https://www.animefenix.com/stream/amz.php?v="+cleanStreamID(codestream) - codestream.contains("player=22&") -> "Fireload https://www.animefenix.com/stream/fl.php?v="+cleanStreamID(codestream) - - else -> "" - } - loadExtractor(links, data, subtitleCallback, callback) - - argamap({ - if (links.contains("AmaNormal")) { - val doc = app.get(links.replace("AmaNormal ","")).document - doc.select("script").map { script -> - if (script.data().contains("sources: [{\"file\"")) { - val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","") - val json = parseJson(text) - if (json.file != null) { - cleanExtractor( - "Amazon", - "Amazon ${json.label}", - json.file!!, - callback - ) - } - } - } - } - - if (links.contains("AmazonES")) { - val amazonES = links.replace("AmazonES ", "") - val doc = app.get("$amazonES&ext=es").document - doc.select("script").map { script -> - if (script.data().contains("sources: [{\"file\"")) { - val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","") - val json = parseJson(text) - if (json.file != null) { - cleanExtractor( - "AmazonES", - "AmazonES ${json.label}", - json.file!!, - callback - ) - } - } - } - } - if (links.contains("Fireload")) { - val doc = app.get(links.replace("Fireload ", "")).document - doc.select("script").map { script -> - if (script.data().contains("sources: [{\"file\"")) { - val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","") - val json = parseJson(text) - val testurl = if (json.file?.contains("fireload") == true) { - app.get("https://${json.file}").text - } else null - if (testurl?.contains("error") == true) { - // - } else if (json.file?.contains("fireload") == true) { - cleanExtractor( - "Fireload", - "Fireload ${json.label}", - "https://"+json.file!!, - callback - ) - } - } - } - } - }) - } - } - return true - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvIOProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvIOProvider.kt deleted file mode 100644 index b2373295..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvIOProvider.kt +++ /dev/null @@ -1,229 +0,0 @@ -package com.lagradost.cloudstream3.movieproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.cloudstream3.utils.loadExtractor -import java.util.* - -class AnimeflvIOProvider:MainAPI() { - override var mainUrl = "https://animeflv.io" //Also scrapes from animeid.to - override var name = "Animeflv.io" - override var lang = "es" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.OVA, - TvType.Anime, - ) - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val urls = listOf( - Pair("$mainUrl/series", "Series actualizadas",), - Pair("$mainUrl/peliculas", "Peliculas actualizadas"), - ) - items.add(HomePageList("Estrenos", app.get(mainUrl).document.select("div#owl-demo-premiere-movies .pull-left").map{ - val title = it.selectFirst("p")?.text() ?: "" - AnimeSearchResponse( - title, - fixUrl(it.selectFirst("a")?.attr("href") ?: ""), - this.name, - TvType.Anime, - it.selectFirst("img")?.attr("src"), - it.selectFirst("span.year").toString().toIntOrNull(), - EnumSet.of(DubStatus.Subbed), - ) - })) - urls.apmap { (url, name) -> - val soup = app.get(url).document - val home = soup.select("div.item-pelicula").map { - val title = it.selectFirst(".item-detail p")?.text() ?: "" - val poster = it.selectFirst("figure img")?.attr("src") - AnimeSearchResponse( - title, - fixUrl(it.selectFirst("a")?.attr("href") ?: ""), - this.name, - TvType.Anime, - poster, - null, - if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed), - ) - } - - items.add(HomePageList(name, home)) - } - - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - override suspend fun search(query: String): List { - val headers = mapOf( - "Host" to "animeflv.io", - "User-Agent" to USER_AGENT, - "X-Requested-With" to "XMLHttpRequest", - "DNT" to "1", - "Alt-Used" to "animeflv.io", - "Connection" to "keep-alive", - "Referer" to "https://animeflv.io", - ) - val url = "$mainUrl/search.html?keyword=$query" - val document = app.get( - url, - headers = headers - ).document - return document.select(".item-pelicula.pull-left").map { - val title = it.selectFirst("div.item-detail p")?.text() ?: "" - val href = fixUrl(it.selectFirst("a")?.attr("href") ?: "") - var image = it.selectFirst("figure img")?.attr("src") ?: "" - val isMovie = href.contains("/pelicula/") - if (image.contains("/static/img/picture.png")) { image = ""} - if (isMovie) { - MovieSearchResponse( - title, - href, - this.name, - TvType.AnimeMovie, - image, - null - ) - } else { - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - image, - null, - EnumSet.of(DubStatus.Subbed), - ) - } - } - } - - override suspend fun load(url: String): LoadResponse? { - // Gets the url returned from searching. - val soup = app.get(url).document - val title = soup.selectFirst(".info-content h1")?.text() - val description = soup.selectFirst("span.sinopsis")?.text()?.trim() - val poster: String? = soup.selectFirst(".poster img")?.attr("src") - val episodes = soup.select(".item-season-episodes a").map { li -> - val href = fixUrl(li.selectFirst("a")?.attr("href") ?: "") - val name = li.selectFirst("a")?.text() ?: "" - Episode( - href, name, - ) - }.reversed() - - val year = Regex("(\\d*)").find(soup.select(".info-half").text()) - - val tvType = if (url.contains("/pelicula/")) TvType.AnimeMovie else TvType.Anime - val genre = soup.select(".content-type-a a") - .map { it?.text()?.trim().toString().replace(", ","") } - val duration = Regex("""(\d*)""").find( - soup.select("p.info-half:nth-child(4)").text()) - - return when (tvType) { - TvType.Anime -> { - return newAnimeLoadResponse(title ?: "", url, tvType) { - japName = null - engName = title - posterUrl = poster - this.year = null - addEpisodes(DubStatus.Subbed, episodes) - plot = description - tags = genre - - showStatus = null - } - } - TvType.AnimeMovie -> { - MovieLoadResponse( - title ?: "", - url, - this.name, - tvType, - url, - poster, - year.toString().toIntOrNull(), - description, - null, - genre, - duration.toString().toIntOrNull(), - ) - } - else -> null - } - } - - data class MainJson ( - @JsonProperty("source") val source: List, - @JsonProperty("source_bk") val sourceBk: String?, - @JsonProperty("track") val track: List?, - @JsonProperty("advertising") val advertising: List?, - @JsonProperty("linkiframe") val linkiframe: String? - ) - - data class Source ( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String, - @JsonProperty("default") val default: String, - @JsonProperty("type") val type: String - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - app.get(data).document.select("li.tab-video").apmap { - val url = fixUrl(it.attr("data-video")) - if (url.contains("animeid")) { - val ajaxurl = url.replace("streaming.php","ajax.php") - val ajaxurltext = app.get(ajaxurl).text - val json = parseJson(ajaxurltext) - json.source.forEach { source -> - if (source.file.contains("m3u8")) { - generateM3u8( - "Animeflv.io", - source.file, - "https://animeid.to", - headers = mapOf("Referer" to "https://animeid.to") - ).apmap { - callback( - ExtractorLink( - "Animeflv.io", - "Animeflv.io", - it.url, - "https://animeid.to", - getQualityFromName(it.quality.toString()), - it.url.contains("m3u8") - ) - ) - } - } else { - callback( - ExtractorLink( - name, - "$name ${source.label}", - source.file, - "https://animeid.to", - Qualities.Unknown.value, - isM3u8 = source.file.contains("m3u8") - ) - ) - } - } - } - loadExtractor(url, data, subtitleCallback, callback) - } - return true - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvnetProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvnetProvider.kt deleted file mode 100644 index 993a035d..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvnetProvider.kt +++ /dev/null @@ -1,182 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import java.util.* - -class AnimeflvnetProvider : MainAPI() { - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Especial")) TvType.OVA - else if (t.contains("Película")) TvType.AnimeMovie - else TvType.Anime - } - - fun getDubStatus(title: String): DubStatus { - return if (title.contains("Latino") || title.contains("Castellano")) - DubStatus.Dubbed - else DubStatus.Subbed - } - } - - override var mainUrl = "https://www3.animeflv.net" - override var name = "Animeflv.net" - override var lang = "es" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.OVA, - TvType.Anime, - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val urls = listOf( - Pair("$mainUrl/browse?type[]=movie&order=updated", "Películas"), - Pair("$mainUrl/browse?status[]=2&order=default", "Animes"), - Pair("$mainUrl/browse?status[]=1&order=rating", "En emision"), - ) - val items = ArrayList() - items.add( - HomePageList( - "Últimos episodios", - app.get(mainUrl).document.select("main.Main ul.ListEpisodios li").mapNotNull { - val title = it.selectFirst("strong.Title")?.text() ?: return@mapNotNull null - val poster = it.selectFirst("span img")?.attr("src") ?: return@mapNotNull null - val epRegex = Regex("(-(\\d+)\$)") - val url = it.selectFirst("a")?.attr("href")?.replace(epRegex, "") - ?.replace("ver/", "anime/") ?: return@mapNotNull null - val epNum = - it.selectFirst("span.Capi")?.text()?.replace("Episodio ", "")?.toIntOrNull() - newAnimeSearchResponse(title, url) { - this.posterUrl = fixUrl(poster) - addDubStatus(getDubStatus(title), epNum) - } - }) - ) - for ((url, name) in urls) { - try { - val doc = app.get(url).document - val home = doc.select("ul.ListAnimes li article").mapNotNull { - val title = it.selectFirst("h3.Title")?.text() ?: return@mapNotNull null - val poster = it.selectFirst("figure img")?.attr("src") ?: return@mapNotNull null - newAnimeSearchResponse( - title, - fixUrl(it.selectFirst("a")?.attr("href") ?: return@mapNotNull null) - ) { - this.posterUrl = fixUrl(poster) - addDubStatus(MonoschinosProvider.getDubStatus(title)) - } - } - - items.add(HomePageList(name, home)) - } catch (e: Exception) { - e.printStackTrace() - } - } - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - data class SearchObject( - @JsonProperty("id") val id: String, - @JsonProperty("title") val title: String, - @JsonProperty("type") val type: String, - @JsonProperty("last_id") val lastId: String, - @JsonProperty("slug") val slug: String - ) - - override suspend fun search(query: String): List { - val response = app.post( - "https://www3.animeflv.net/api/animes/search", - data = mapOf(Pair("value", query)) - ).text - val json = parseJson>(response) - return json.map { searchr -> - val title = searchr.title - val href = "$mainUrl/anime/${searchr.slug}" - val image = "$mainUrl/uploads/animes/covers/${searchr.id}.jpg" - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - fixUrl(image), - null, - if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( - DubStatus.Subbed - ), - ) - } - } - - override suspend fun load(url: String): LoadResponse { - val doc = app.get(url).document - val episodes = ArrayList() - val title = doc.selectFirst("h1.Title")!!.text() - val poster = doc.selectFirst("div.AnimeCover div.Image figure img")?.attr("src")!! - val description = doc.selectFirst("div.Description p")?.text() - val type = doc.selectFirst("span.Type")?.text() ?: "" - val status = when (doc.selectFirst("p.AnmStts span")?.text()) { - "En emision" -> ShowStatus.Ongoing - "Finalizado" -> ShowStatus.Completed - else -> null - } - val genre = doc.select("nav.Nvgnrs a") - .map { it?.text()?.trim().toString() } - - doc.select("script").map { script -> - if (script.data().contains("var episodes = [")) { - val data = script.data().substringAfter("var episodes = [").substringBefore("];") - data.split("],").forEach { - val epNum = it.removePrefix("[").substringBefore(",") - // val epthumbid = it.removePrefix("[").substringAfter(",").substringBefore("]") - val animeid = doc.selectFirst("div.Strs.RateIt")?.attr("data-id") - val epthumb = "https://cdn.animeflv.net/screenshots/$animeid/$epNum/th_3.jpg" - val link = url.replace("/anime/", "/ver/") + "-$epNum" - episodes.add( - Episode( - link, - null, - posterUrl = epthumb, - episode = epNum.toIntOrNull() - ) - ) - } - } - } - return newAnimeLoadResponse(title, url, getType(type)) { - posterUrl = fixUrl(poster) - addEpisodes(DubStatus.Subbed, episodes.reversed()) - showStatus = status - plot = description - tags = genre - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - app.get(data).document.select("script").apmap { script -> - if (script.data().contains("var videos = {") || script.data() - .contains("var anime_id =") || script.data().contains("server") - ) { - val videos = script.data().replace("\\/", "/") - fetchUrls(videos).map { - it.replace("https://embedsb.com/e/", "https://watchsb.com/e/") - .replace("https://ok.ru", "http://ok.ru") - }.apmap { - loadExtractor(it, data, subtitleCallback, callback) - } - } - } - return true - } -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimekisaProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimekisaProvider.kt deleted file mode 100644 index 83861772..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimekisaProvider.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import java.util.* - - -class AnimekisaProvider : MainAPI() { - override var mainUrl = "https://animekisa.in" - override var name = "Animekisa" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.OVA, - TvType.Anime, - ) - - data class Response( - @JsonProperty("html") val html: String - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val urls = listOf( - Pair("$mainUrl/ajax/list/views?type=all", "All animes"), - Pair("$mainUrl/ajax/list/views?type=day", "Trending now"), - Pair("$mainUrl/ajax/list/views?type=week", "Trending by week"), - Pair("$mainUrl/ajax/list/views?type=month", "Trending by month"), - ) - - val items = urls.mapNotNull { - suspendSafeApiCall { - val home = Jsoup.parse( - parseJson( - app.get( - it.first - ).text - ).html - ).select("div.flw-item").mapNotNull secondMap@ { - val title = it.selectFirst("h3.title a")?.text() ?: return@secondMap null - val link = it.selectFirst("a")?.attr("href") ?: return@secondMap null - val poster = it.selectFirst("img.lazyload")?.attr("data-src") - AnimeSearchResponse( - title, - link, - this.name, - TvType.Anime, - poster, - null, - if (title.contains("(DUB)") || title.contains("(Dub)")) EnumSet.of( - DubStatus.Dubbed - ) else EnumSet.of(DubStatus.Subbed), - ) - } - HomePageList(name, home) - } - } - - if (items.isEmpty()) throw ErrorLoadingException() - return HomePageResponse(items) - } - - override suspend fun search(query: String): List { - return app.get("$mainUrl/search/?keyword=$query").document.select("div.flw-item") - .mapNotNull { - val title = it.selectFirst("h3 a")?.text() ?: "" - val url = it.selectFirst("a.film-poster-ahref")?.attr("href") - ?.replace("watch/", "anime/")?.replace( - Regex("(-episode-(\\d+)/\$|-episode-(\\d+)\$|-episode-full|-episode-.*-.(/|))"), - "" - ) ?: return@mapNotNull null - val poster = it.selectFirst(".film-poster img")?.attr("data-src") - AnimeSearchResponse( - title, - url, - this.name, - TvType.Anime, - poster, - null, - if (title.contains("(DUB)") || title.contains("(Dub)")) EnumSet.of( - DubStatus.Dubbed - ) else EnumSet.of(DubStatus.Subbed), - ) - }.toList() - } - - override suspend fun load(url: String): LoadResponse { - val doc = app.get(url, timeout = 120).document - val poster = doc.selectFirst(".mb-2 img")?.attr("src") - ?: doc.selectFirst("head meta[property=og:image]")?.attr("content") - val title = doc.selectFirst("h1.heading-name a")!!.text() - val description = doc.selectFirst("div.description p")?.text()?.trim() - val genres = doc.select("div.row-line a").map { it.text() } - val test = if (doc.selectFirst("div.dp-i-c-right").toString() - .contains("Airing") - ) ShowStatus.Ongoing else ShowStatus.Completed - val episodes = doc.select("div.tab-content ul li.nav-item").mapNotNull { - val link = it.selectFirst("a")?.attr("href") ?: return@mapNotNull null - Episode(link) - } - val type = if (doc.selectFirst(".dp-i-stats").toString() - .contains("Movies") - ) TvType.AnimeMovie else TvType.Anime - return newAnimeLoadResponse(title, url, type) { - posterUrl = poster - addEpisodes(DubStatus.Subbed, episodes) - showStatus = test - plot = description - tags = genres - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - app.get(data).document.select("#servers-list ul.nav li a").apmap { - val server = it.attr("data-embed") - loadExtractor(server, data, subtitleCallback, callback) - } - return true - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt deleted file mode 100644 index 76fb8f35..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt +++ /dev/null @@ -1,270 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.APIHolder.unixTime -import com.lagradost.cloudstream3.APIHolder.unixTimeMS -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.Jsoup -import java.util.* - -class DubbedAnimeProvider : MainAPI() { - override var mainUrl = "https://bestdubbedanime.com" - override var name = "DubbedAnime" - override val hasQuickSearch = true - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.Anime, - ) - - data class QueryEpisodeResultRoot( - @JsonProperty("result") - val result: QueryEpisodeResult, - ) - - data class QueryEpisodeResult( - @JsonProperty("anime") val anime: List, - @JsonProperty("error") val error: Boolean, - @JsonProperty("errorMSG") val errorMSG: String?, - ) - - data class EpisodeInfo( - @JsonProperty("serversHTML") val serversHTML: String, - @JsonProperty("title") val title: String, - @JsonProperty("preview_img") val previewImg: String?, - @JsonProperty("wideImg") val wideImg: String?, - @JsonProperty("year") val year: String?, - @JsonProperty("desc") val desc: String?, - - /* - @JsonProperty("rowid") val rowid: String, - @JsonProperty("status") val status: String, - @JsonProperty("skips") val skips: String, - @JsonProperty("totalEp") val totalEp: Long, - @JsonProperty("ep") val ep: String, - @JsonProperty("NextEp") val nextEp: Long, - @JsonProperty("slug") val slug: String, - @JsonProperty("showid") val showid: String, - @JsonProperty("Epviews") val epviews: String, - @JsonProperty("TotalViews") val totalViews: String, - @JsonProperty("tags") val tags: String,*/ - ) - - private suspend fun parseDocumentTrending(url: String): List { - val response = app.get(url).text - val document = Jsoup.parse(response) - return document.select("li > a").mapNotNull { - val href = fixUrl(it.attr("href")) - val title = it.selectFirst("> div > div.cittx")?.text() ?: return@mapNotNull null - val poster = fixUrlNull(it.selectFirst("> div > div.imghddde > img")?.attr("src")) - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - poster, - null, - EnumSet.of(DubStatus.Dubbed), - ) - } - } - - private suspend fun parseDocument( - url: String, - trimEpisode: Boolean = false - ): List { - val response = app.get(url).text - val document = Jsoup.parse(response) - return document.select("a.grid__link").mapNotNull { - val href = fixUrl(it.attr("href")) - val title = it.selectFirst("> div.gridtitlek")?.text() ?: return@mapNotNull null - val poster = - fixUrl(it.selectFirst("> img.grid__img")?.attr("src") ?: return@mapNotNull null) - AnimeSearchResponse( - title, - if (trimEpisode) href.removeRange(href.lastIndexOf('/'), href.length) else href, - this.name, - TvType.Anime, - poster, - null, - EnumSet.of(DubStatus.Dubbed), - ) - } - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val trendingUrl = "$mainUrl/xz/trending.php?_=$unixTimeMS" - val lastEpisodeUrl = "$mainUrl/xz/epgrid.php?p=1&_=$unixTimeMS" - val recentlyAddedUrl = "$mainUrl/xz/gridgrabrecent.php?p=1&_=$unixTimeMS" - //val allUrl = "$mainUrl/xz/gridgrab.php?p=1&limit=12&_=$unixTimeMS" - - val listItems = listOf( - HomePageList("Trending", parseDocumentTrending(trendingUrl)), - HomePageList("Recently Added", parseDocument(recentlyAddedUrl)), - HomePageList("Recent Releases", parseDocument(lastEpisodeUrl, true)), - // HomePageList("All", parseDocument(allUrl)) - ) - - return HomePageResponse(listItems) - } - - - private suspend fun getEpisode(slug: String, isMovie: Boolean): EpisodeInfo { - val url = - mainUrl + (if (isMovie) "/movies/jsonMovie" else "/xz/v3/jsonEpi") + ".php?slug=$slug&_=$unixTime" - val response = app.get(url).text - val mapped = parseJson(response) - return mapped.result.anime.first() - } - - - private fun getIsMovie(href: String): Boolean { - return href.contains("movies/") - } - - private fun getSlug(href: String): String { - return href.replace("$mainUrl/", "") - } - - override suspend fun quickSearch(query: String): List { - val url = "$mainUrl/xz/searchgrid.php?p=1&limit=12&s=$query&_=$unixTime" - val response = app.get(url).text - val document = Jsoup.parse(response) - val items = document.select("div.grid__item > a") - if (items.isEmpty()) return emptyList() - return items.mapNotNull { i -> - val href = fixUrl(i.attr("href")) - val title = i.selectFirst("div.gridtitlek")?.text() ?: return@mapNotNull null - val img = fixUrlNull(i.selectFirst("img.grid__img")?.attr("src")) - - if (getIsMovie(href)) { - MovieSearchResponse( - title, href, this.name, TvType.AnimeMovie, img, null - ) - } else { - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - img, - null, - EnumSet.of(DubStatus.Dubbed), - ) - } - } - } - - override suspend fun search(query: String): List { - val url = "$mainUrl/search/$query" - val response = app.get(url).text - val document = Jsoup.parse(response) - val items = document.select("div.resultinner > a.resulta") - if (items.isEmpty()) return ArrayList() - return items.mapNotNull { i -> - val innerDiv = i.selectFirst("> div.result") - val href = fixUrl(i.attr("href")) - val img = fixUrl(innerDiv?.selectFirst("> div.imgkz > img")?.attr("src") ?: return@mapNotNull null) - val title = innerDiv.selectFirst("> div.titleresults")?.text() ?: return@mapNotNull null - - if (getIsMovie(href)) { - MovieSearchResponse( - title, href, this.name, TvType.AnimeMovie, img, null - ) - } else { - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - img, - null, - EnumSet.of(DubStatus.Dubbed), - ) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val serversHTML = (if (data.startsWith(mainUrl)) { // CLASSIC EPISODE - val slug = getSlug(data) - getEpisode(slug, false).serversHTML - } else data).replace("\\", "") - - val hls = ArrayList("hl=\"(.*?)\"".toRegex().findAll(serversHTML).map { - it.groupValues[1] - }.toList()) - for (hl in hls) { - try { - val sources = app.get("$mainUrl/xz/api/playeri.php?url=$hl&_=$unixTime").text - val find = "src=\"(.*?)\".*?label=\"(.*?)\"".toRegex().find(sources) - if (find != null) { - val quality = find.groupValues[2] - callback.invoke( - ExtractorLink( - this.name, - this.name + " " + quality + if (quality.endsWith('p')) "" else 'p', - fixUrl(find.groupValues[1]), - this.mainUrl, - getQualityFromName(quality) - ) - ) - } - } catch (e: Exception) { - //IDK - } - } - return true - } - - override suspend fun load(url: String): LoadResponse { - if (getIsMovie(url)) { - val realSlug = url.replace("movies/", "") - val episode = getEpisode(realSlug, true) - val poster = episode.previewImg ?: episode.wideImg - return MovieLoadResponse( - episode.title, - realSlug, - this.name, - TvType.AnimeMovie, - episode.serversHTML, - if (poster == null) null else fixUrl(poster), - episode.year?.toIntOrNull(), - episode.desc, - null - ) - } else { - val response = app.get(url).text - val document = Jsoup.parse(response) - val title = document.selectFirst("h4")!!.text() - val descriptHeader = document.selectFirst("div.animeDescript") - val descript = descriptHeader?.selectFirst("> p")?.text() - val year = descriptHeader?.selectFirst("> div.distatsx > div.sroverd") - ?.text() - ?.replace("Released: ", "") - ?.toIntOrNull() - - val episodes = document.select("a.epibloks").map { - val epTitle = it.selectFirst("> div.inwel > span.isgrxx")?.text() - Episode(fixUrl(it.attr("href")), epTitle) - } - - val img = fixUrl(document.select("div.fkimgs > img").attr("src")) - return newAnimeLoadResponse(title, url, TvType.Anime) { - posterUrl = img - this.year = year - addEpisodes(DubStatus.Dubbed, episodes) - plot = descript - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt deleted file mode 100644 index 7aaab91d..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt +++ /dev/null @@ -1,412 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.AppUtils -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import org.jsoup.nodes.Document -import java.net.URI -import java.util.* -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -class GogoanimeProvider : MainAPI() { - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - - /** - * @param id base64Decode(show_id) + IV - * @return the encryption key - * */ - private fun getKey(id: String): String? { - return normalSafeApiCall { - id.map { - it.code.toString(16) - }.joinToString("").substring(0, 32) - } - } - - val qualityRegex = Regex("(\\d+)P") - - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt#L60 - // No Licence on the function - private fun cryptoHandler( - string: String, - iv: String, - secretKeyString: String, - encrypt: Boolean = true - ): String { - //println("IV: $iv, Key: $secretKeyString, encrypt: $encrypt, Message: $string") - val ivParameterSpec = IvParameterSpec(iv.toByteArray()) - val secretKey = SecretKeySpec(secretKeyString.toByteArray(), "AES") - val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") - return if (!encrypt) { - cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec) - String(cipher.doFinal(base64DecodeArray(string))) - } else { - cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec) - base64Encode(cipher.doFinal(string.toByteArray())) - } - } - - private fun String.decodeHex(): ByteArray { - check(length % 2 == 0) { "Must have an even length" } - return chunked(2) - .map { it.toInt(16).toByte() } - .toByteArray() - } - - /** - * @param iframeUrl something like https://gogoplay4.com/streaming.php?id=XXXXXX - * @param mainApiName used for ExtractorLink names and source - * @param iv secret iv from site, required non-null if isUsingAdaptiveKeys is off - * @param secretKey secret key for decryption from site, required non-null if isUsingAdaptiveKeys is off - * @param secretDecryptKey secret key to decrypt the response json, required non-null if isUsingAdaptiveKeys is off - * @param isUsingAdaptiveKeys generates keys from IV and ID, see getKey() - * @param isUsingAdaptiveData generate encrypt-ajax data based on $("script[data-name='episode']")[0].dataset.value - * */ - suspend fun extractVidstream( - iframeUrl: String, - mainApiName: String, - callback: (ExtractorLink) -> Unit, - iv: String?, - secretKey: String?, - secretDecryptKey: String?, - // This could be removed, but i prefer it verbose - isUsingAdaptiveKeys: Boolean, - isUsingAdaptiveData: Boolean, - // If you don't want to re-fetch the document - iframeDocument: Document? = null - ) = safeApiCall { - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt - // No Licence on the following code - // Also modified of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/gogoanime/src/eu/kanade/tachiyomi/animeextension/en/gogoanime/extractors/GogoCdnExtractor.kt - // License on the code above https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE - - if ((iv == null || secretKey == null || secretDecryptKey == null) && !isUsingAdaptiveKeys) - return@safeApiCall - - val id = Regex("id=([^&]+)").find(iframeUrl)!!.value.removePrefix("id=") - - var document: Document? = iframeDocument - val foundIv = - iv ?: (document ?: app.get(iframeUrl).document.also { document = it }) - .select("""div.wrapper[class*=container]""") - .attr("class").split("-").lastOrNull() ?: return@safeApiCall - val foundKey = secretKey ?: getKey(base64Decode(id) + foundIv) ?: return@safeApiCall - val foundDecryptKey = secretDecryptKey ?: foundKey - - val uri = URI(iframeUrl) - val mainUrl = "https://" + uri.host - - val encryptedId = cryptoHandler(id, foundIv, foundKey) - val encryptRequestData = if (isUsingAdaptiveData) { - // Only fetch the document if necessary - val realDocument = document ?: app.get(iframeUrl).document - val dataEncrypted = - realDocument.select("script[data-name='episode']").attr("data-value") - val headers = cryptoHandler(dataEncrypted, foundIv, foundKey, false) - "id=$encryptedId&alias=$id&" + headers.substringAfter("&") - } else { - "id=$encryptedId&alias=$id" - } - - val jsonResponse = - app.get( - "$mainUrl/encrypt-ajax.php?$encryptRequestData", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ) - val dataencrypted = - jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}") - val datadecrypted = cryptoHandler(dataencrypted, foundIv, foundDecryptKey, false) - val sources = AppUtils.parseJson(datadecrypted) - - fun invokeGogoSource( - source: GogoSource, - sourceCallback: (ExtractorLink) -> Unit - ) { - sourceCallback.invoke( - ExtractorLink( - mainApiName, - mainApiName, - source.file, - mainUrl, - getQualityFromName(source.label), - isM3u8 = source.type == "hls" || source.label?.contains( - "auto", - ignoreCase = true - ) == true - ) - ) - } - - sources.source?.forEach { - invokeGogoSource(it, callback) - } - sources.sourceBk?.forEach { - invokeGogoSource(it, callback) - } - } - } - - override var mainUrl = "https://gogoanime.lu" - override var name = "GogoAnime" - override val hasQuickSearch = false - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.Anime, - TvType.OVA - ) - - val headers = mapOf( - "authority" to "ajax.gogo-load.com", - "sec-ch-ua" to "\"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"", - "accept" to "text/html, */*; q=0.01", - "dnt" to "1", - "sec-ch-ua-mobile" to "?0", - "user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36", - "origin" to mainUrl, - "sec-fetch-site" to "cross-site", - "sec-fetch-mode" to "cors", - "sec-fetch-dest" to "empty", - "referer" to "$mainUrl/" - ) - val parseRegex = - Regex("""
  • \s*\n.*\n.*\n.*?img src="(.*?)"""") - - override val mainPage = mainPageOf( - Pair("1", "Recent Release - Sub"), - Pair("2", "Recent Release - Dub"), - Pair("3", "Recent Release - Chinese"), - ) - - override suspend fun getMainPage( - page: Int, - request : MainPageRequest - ): HomePageResponse { - val params = mapOf("page" to page.toString(), "type" to request.data) - val html = app.get( - "https://ajax.gogo-load.com/ajax/page-recent-release.html", - headers = headers, - params = params - ) - val isSub = listOf(1, 3).contains(request.data.toInt()) - - val home = parseRegex.findAll(html.text).map { - val (link, epNum, title, poster) = it.destructured - newAnimeSearchResponse(title, link) { - this.posterUrl = poster - addDubStatus(!isSub, epNum.toIntOrNull()) - } - }.toList() - - return newHomePageResponse(request.name, home) - } - - override suspend fun search(query: String): ArrayList { - val link = "$mainUrl/search.html?keyword=$query" - val html = app.get(link).text - val doc = Jsoup.parse(html) - - val episodes = doc.select(""".last_episodes li""").mapNotNull { - AnimeSearchResponse( - it.selectFirst(".name")?.text()?.replace(" (Dub)", "") ?: return@mapNotNull null, - fixUrl(it.selectFirst(".name > a")?.attr("href") ?: return@mapNotNull null), - this.name, - TvType.Anime, - it.selectFirst("img")?.attr("src"), - it.selectFirst(".released")?.text()?.split(":")?.getOrNull(1)?.trim() - ?.toIntOrNull(), - if (it.selectFirst(".name")?.text() - ?.contains("Dub") == true - ) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( - DubStatus.Subbed - ), - ) - } - - return ArrayList(episodes) - } - - private fun getProperAnimeLink(uri: String): String { - if (uri.contains("-episode")) { - val split = uri.split("/") - val slug = split[split.size - 1].split("-episode")[0] - return "$mainUrl/category/$slug" - } - return uri - } - - override suspend fun load(url: String): LoadResponse { - val link = getProperAnimeLink(url) - val episodeloadApi = "https://ajax.gogo-load.com/ajax/load-list-episode" - val doc = app.get(link).document - - val animeBody = doc.selectFirst(".anime_info_body_bg") - val title = animeBody?.selectFirst("h1")!!.text() - val poster = animeBody.selectFirst("img")?.attr("src") - var description: String? = null - val genre = ArrayList() - var year: Int? = null - var status: String? = null - var nativeName: String? = null - var type: String? = null - - animeBody.select("p.type").forEach { pType -> - when (pType.selectFirst("span")?.text()?.trim()) { - "Plot Summary:" -> { - description = pType.text().replace("Plot Summary:", "").trim() - } - "Genre:" -> { - genre.addAll(pType.select("a").map { - it.attr("title") - }) - } - "Released:" -> { - year = pType.text().replace("Released:", "").trim().toIntOrNull() - } - "Status:" -> { - status = pType.text().replace("Status:", "").trim() - } - "Other name:" -> { - nativeName = pType.text().replace("Other name:", "").trim() - } - "Type:" -> { - type = pType.text().replace("type:", "").trim() - } - } - } - - val animeId = doc.selectFirst("#movie_id")!!.attr("value") - val params = mapOf("ep_start" to "0", "ep_end" to "2000", "id" to animeId) - - val episodes = app.get(episodeloadApi, params = params).document.select("a").map { - Episode( - fixUrl(it.attr("href").trim()), - "Episode " + it.selectFirst(".name")?.text()?.replace("EP", "")?.trim() - ) - }.reversed() - - return newAnimeLoadResponse(title, link, getType(type.toString())) { - japName = nativeName - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) // TODO CHECK - plot = description - tags = genre - - showStatus = getStatus(status.toString()) - } - } - - data class GogoSources( - @JsonProperty("source") val source: List?, - @JsonProperty("sourceBk") val sourceBk: List?, - //val track: List, - //val advertising: List, - //val linkiframe: String - ) - - data class GogoSource( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("default") val default: String? = null - ) - - private suspend fun extractVideos( - uri: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val doc = app.get(uri).document - - val iframe = fixUrlNull(doc.selectFirst("div.play-video > iframe")?.attr("src")) ?: return - - argamap( - { - val link = iframe.replace("streaming.php", "download") - val page = app.get(link, headers = mapOf("Referer" to iframe)) - - page.document.select(".dowload > a").apmap { - if (it.hasAttr("download")) { - val qual = if (it.text() - .contains("HDP") - ) "1080" else qualityRegex.find(it.text())?.destructured?.component1() - .toString() - callback( - ExtractorLink( - "Gogoanime", - "Gogoanime", - it.attr("href"), - page.url, - getQualityFromName(qual), - it.attr("href").contains(".m3u8") - ) - ) - } else { - val url = it.attr("href") - loadExtractor(url, null, subtitleCallback, callback) - } - } - }, { - val streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe)) - val streamingDocument = streamingResponse.document - argamap({ - streamingDocument.select(".list-server-items > .linkserver") - .forEach { element -> - val status = element.attr("data-status") ?: return@forEach - if (status != "1") return@forEach - val data = element.attr("data-video") ?: return@forEach - loadExtractor(data, streamingResponse.url, subtitleCallback, callback) - } - }, { - val iv = "3134003223491201" - val secretKey = "37911490979715163134003223491201" - val secretDecryptKey = "54674138327930866480207815084989" - extractVidstream( - iframe, - this.name, - callback, - iv, - secretKey, - secretDecryptKey, - isUsingAdaptiveKeys = false, - isUsingAdaptiveData = true - ) - }) - } - ) - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - extractVideos(data, subtitleCallback, callback) - return true - } -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GomunimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GomunimeProvider.kt deleted file mode 100644 index 66a9ee99..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GomunimeProvider.kt +++ /dev/null @@ -1,232 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup - -class GomunimeProvider : MainAPI() { - override var mainUrl = "https://185.231.223.76" - override var name = "Gomunime" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "e" to "Episode Baru", - "c" to "Completed", - "la" to "Live Action", - "t" to "Trending" - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val home = Jsoup.parse( - (app.post( - url = "$mainUrl/wp-admin/admin-ajax.php/wp-admin/admin-ajax.php", - headers = mapOf("Referer" to mainUrl), - data = mapOf( - "action" to "home_ajax", - "fungsi" to request.data, - "pag" to "$page" - ) - ).parsedSafe()?.html ?: throw ErrorLoadingException("Invalid Json reponse")) - ).select("li").mapNotNull { - val title = it.selectFirst("a.name")?.text()?.trim() ?: return@mapNotNull null - val href = getProperAnimeLink(it.selectFirst("a")!!.attr("href")) - val posterUrl = it.selectFirst("img")?.attr("src") - val type = getType(it.selectFirst(".taglist > span")!!.text().trim()) - val epNum = it.select(".tag.ep").text().replace(Regex("[^0-9]"), "").trim() - .toIntOrNull() - newAnimeSearchResponse(title, href, type) { - this.posterUrl = posterUrl - addSub(epNum) - } - } - - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("-episode")) { - val href = - "$mainUrl/anime/" + Regex("\\w\\d/(.*)-episode.*").find(uri)?.groupValues?.get(1) - .toString() - when { - href.contains("pokemon") -> href.replace(Regex("-[0-9]+"), "") - else -> href - } - } else { - uri - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = app.get(link).document - - return document.select(".anime-list > li").map { - val title = it.selectFirst("a.name")!!.text() - val poster = it.selectFirst("img")!!.attr("src") - val tvType = getType(it.selectFirst(".taglist > span")?.text().toString()) - val href = fixUrl(it.selectFirst("a.name")!!.attr("href")) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addDubStatus(dubExist = false, subExist = true) - } - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst(".entry-title")?.text().toString() - val poster = document.selectFirst(".thumbposter > img")?.attr("data-lazy-src") - val tags = document.select(".genxed > a").map { it.text() } - - val year = Regex("\\d, ([0-9]*)").find( - document.select("time[itemprop = datePublished]").text() - )?.groupValues?.get(1)?.toIntOrNull() - val status = getStatus(document.selectFirst(".spe > span")!!.ownText()) - val description = document.select("div[itemprop = description] > p").text() - val trailer = document.selectFirst("div.embed-responsive noscript iframe")?.attr("src") - val episodes = parseJson>( - Regex("var episodelist = (\\[.*])").find( - document.select(".bixbox.bxcl.epcheck > script").toString().trim() - )?.groupValues?.get(1).toString().replace(Regex("""\\"""), "").trim() - ).map { - val name = - Regex("(Episode\\s?[0-9]+)").find(it.epTitle.toString())?.groupValues?.getOrNull(0) - ?: it.epTitle - val link = it.epLink - Episode(link, name) - }.reversed() - - return newAnimeLoadResponse(title, url, TvType.Anime) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - addTrailer(trailer) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - - val scriptData = document.select("aside.sidebar > script").dataNodes().toString() - val key = scriptData.substringAfter("var a_ray = '").substringBefore("';") - val title = scriptData.substringAfter("var judul_postingan = \"").substringBefore("\";") - - val sources: List> = app.post( - url = "https://path.gomuni.me/app/vapi.php", - data = mapOf("data" to key, "judul" to title, "func" to "mirror") - ).document.select("div.gomunime-server-mirror").map { - Pair( - it.attr("data-vhash"), - it.attr("data-type") - ) - } - - sources.apmap { - safeApiCall { - when { - it.second.contains("frame") -> { - loadExtractor(it.first, data, subtitleCallback, callback) - } - it.second.contains("hls") -> { - app.post( - url = "https://path.gomuni.me/app/vapi.php", - data = mapOf("fid" to it.first, "func" to "hls") - ).text.let { link -> - M3u8Helper.generateM3u8( - this.name, - link, - "$mainUrl/", - headers = mapOf("Origin" to mainUrl) - ).forEach(callback) - } - } - it.second.contains("mp4") -> { - app.post( - url = "https://path.gomuni.me/app/vapi.php", - data = mapOf("data" to it.first, "func" to "blogs") - ).parsed>().map { - callback.invoke( - ExtractorLink( - source = name, - name = "Mobi SD", - url = it.file, - referer = "$mainUrl/", - quality = Qualities.P360.value - ) - ) - } - } - else -> null - } - } - } - - return true - } - - private data class Response( - @JsonProperty("status") val status: Boolean, - @JsonProperty("html") val html: String - ) - - data class MobiSource( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String, - @JsonProperty("type") val type: String - ) - - private data class EpisodeElement( - @JsonProperty("data-index") val dataIndex: Long?, - @JsonProperty("ep-num") val epNum: String?, - @JsonProperty("ep-title") val epTitle: String?, - @JsonProperty("ep-link") val epLink: String, - @JsonProperty("ep-date") val epDate: String? - ) - -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/JKAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/JKAnimeProvider.kt deleted file mode 100644 index fcfc7445..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/JKAnimeProvider.kt +++ /dev/null @@ -1,319 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.cloudstream3.utils.loadExtractor -import java.util.* - - -class JKAnimeProvider : MainAPI() { - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Especial")) TvType.OVA - else if (t.contains("Pelicula")) TvType.AnimeMovie - else TvType.Anime - } - } - - override var mainUrl = "https://jkanime.net" - override var name = "JKAnime" - override var lang = "es" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.OVA, - TvType.Anime, - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val urls = listOf( - Pair( - "$mainUrl/directorio/?filtro=fecha&tipo=TV&estado=1&fecha=none&temporada=none&orden=desc", - "En emisión" - ), - Pair( - "$mainUrl/directorio/?filtro=fecha&tipo=none&estado=none&fecha=none&temporada=none&orden=none", - "Animes" - ), - Pair( - "$mainUrl/directorio/?filtro=fecha&tipo=Movie&estado=none&fecha=none&temporada=none&orden=none", - "Películas" - ), - ) - - val items = ArrayList() - - items.add( - HomePageList( - "Últimos episodios", - app.get(mainUrl).document.select(".listadoanime-home a.bloqq").map { - val title = it.selectFirst("h5")?.text() - val dubstat = if (title!!.contains("Latino") || title.contains("Castellano")) - DubStatus.Dubbed else DubStatus.Subbed - val poster = - it.selectFirst(".anime__sidebar__comment__item__pic img")?.attr("src") ?: "" - val epRegex = Regex("/(\\d+)/|/especial/|/ova/") - val url = it.attr("href").replace(epRegex, "") - val epNum = - it.selectFirst("h6")?.text()?.replace("Episodio ", "")?.toIntOrNull() - newAnimeSearchResponse(title, url) { - this.posterUrl = poster - addDubStatus(dubstat, epNum) - } - }) - ) - urls.apmap { (url, name) -> - val soup = app.get(url).document - val home = soup.select(".g-0").map { - val title = it.selectFirst("h5 a")?.text() - val poster = it.selectFirst("img")?.attr("src") ?: "" - AnimeSearchResponse( - title!!, - fixUrl(it.selectFirst("a")?.attr("href") ?: ""), - this.name, - TvType.Anime, - fixUrl(poster), - null, - if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( - DubStatus.Dubbed - ) else EnumSet.of(DubStatus.Subbed), - ) - } - items.add(HomePageList(name, home)) - } - - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - data class MainSearch( - @JsonProperty("animes") val animes: List, - @JsonProperty("anime_types") val animeTypes: AnimeTypes - ) - - data class Animes( - @JsonProperty("id") val id: String, - @JsonProperty("slug") val slug: String, - @JsonProperty("title") val title: String, - @JsonProperty("image") val image: String, - @JsonProperty("synopsis") val synopsis: String, - @JsonProperty("type") val type: String, - @JsonProperty("status") val status: String, - @JsonProperty("thumbnail") val thumbnail: String - ) - - data class AnimeTypes( - @JsonProperty("TV") val TV: String, - @JsonProperty("OVA") val OVA: String, - @JsonProperty("Movie") val Movie: String, - @JsonProperty("Special") val Special: String, - @JsonProperty("ONA") val ONA: String, - @JsonProperty("Music") val Music: String - ) - - override suspend fun search(query: String): List { - val main = app.get("$mainUrl/ajax/ajax_search/?q=$query").text - val json = parseJson(main) - return json.animes.map { - val title = it.title - val href = "$mainUrl/${it.slug}" - val image = "https://cdn.jkanime.net/assets/images/animes/image/${it.slug}.jpg" - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - image, - null, - if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( - DubStatus.Dubbed - ) else EnumSet.of(DubStatus.Subbed), - ) - } - } - - override suspend fun load(url: String): LoadResponse { - val doc = app.get(url, timeout = 120).document - val poster = doc.selectFirst(".set-bg")?.attr("data-setbg") - val title = doc.selectFirst(".anime__details__title > h3")?.text() - val type = doc.selectFirst(".anime__details__text")?.text() - val description = doc.selectFirst(".anime__details__text > p")?.text() - val genres = doc.select("div.col-lg-6:nth-child(1) > ul:nth-child(1) > li:nth-child(2) > a") - .map { it.text() } - val status = when (doc.selectFirst("span.enemision")?.text()) { - "En emisión" -> ShowStatus.Ongoing - "Concluido" -> ShowStatus.Completed - else -> null - } - val animeID = doc.selectFirst("div.ml-2")?.attr("data-anime")?.toInt() - val animeeps = "$mainUrl/ajax/last_episode/$animeID/" - val jsoneps = app.get(animeeps).text - val lastepnum = - jsoneps.substringAfter("{\"number\":\"").substringBefore("\",\"title\"").toInt() - val episodes = (1..lastepnum).map { - val link = "${url.removeSuffix("/")}/$it" - Episode(link) - } - - return newAnimeLoadResponse(title!!, url, getType(type!!)) { - posterUrl = poster - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - tags = genres - } - } - - data class Nozomi( - @JsonProperty("file") val file: String? - ) - - private fun streamClean( - name: String, - url: String, - referer: String, - quality: String?, - callback: (ExtractorLink) -> Unit, - m3u8: Boolean - ): Boolean { - callback( - ExtractorLink( - name, - name, - url, - referer, - getQualityFromName(quality), - m3u8 - ) - ) - return true - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - app.get(data).document.select("script").apmap { script -> - if (script.data().contains("var video = []")) { - val videos = script.data().replace("\\/", "/") - fetchUrls(videos).map { - it.replace("$mainUrl/jkfembed.php?u=", "https://embedsito.com/v/") - .replace("$mainUrl/jkokru.php?u=", "http://ok.ru/videoembed/") - .replace("$mainUrl/jkvmixdrop.php?u=", "https://mixdrop.co/e/") - .replace("$mainUrl/jk.php?u=", "$mainUrl/") - }.apmap { link -> - loadExtractor(link, data, subtitleCallback, callback) - if (link.contains("um2.php")) { - val doc = app.get(link, referer = data).document - val gsplaykey = doc.select("form input[value]").attr("value") - app.post( - "$mainUrl/gsplay/redirect_post.php", - headers = mapOf( - "Host" to "jkanime.net", - "User-Agent" to USER_AGENT, - "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", - "Accept-Language" to "en-US,en;q=0.5", - "Referer" to link, - "Content-Type" to "application/x-www-form-urlencoded", - "Origin" to "https://jkanime.net", - "DNT" to "1", - "Connection" to "keep-alive", - "Upgrade-Insecure-Requests" to "1", - "Sec-Fetch-Dest" to "iframe", - "Sec-Fetch-Mode" to "navigate", - "Sec-Fetch-Site" to "same-origin", - "TE" to "trailers", - "Pragma" to "no-cache", - "Cache-Control" to "no-cache", - ), - data = mapOf(Pair("data", gsplaykey)), - allowRedirects = false - ).okhttpResponse.headers.values("location").apmap { loc -> - val postkey = loc.replace("/gsplay/player.html#", "") - val nozomitext = app.post( - "$mainUrl/gsplay/api.php", - headers = mapOf( - "Host" to "jkanime.net", - "User-Agent" to USER_AGENT, - "Accept" to "application/json, text/javascript, */*; q=0.01", - "Accept-Language" to "en-US,en;q=0.5", - "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", - "X-Requested-With" to "XMLHttpRequest", - "Origin" to "https://jkanime.net", - "DNT" to "1", - "Connection" to "keep-alive", - "Sec-Fetch-Dest" to "empty", - "Sec-Fetch-Mode" to "cors", - "Sec-Fetch-Site" to "same-origin", - ), - data = mapOf(Pair("v", postkey)), - allowRedirects = false - ).text - val json = parseJson(nozomitext) - val nozomiurl = listOf(json.file) - if (nozomiurl.isEmpty()) null else - nozomiurl.forEach { url -> - val nozominame = "Nozomi" - streamClean( - nozominame, - url!!, - "", - null, - callback, - url.contains(".m3u8") - ) - } - } - } - if (link.contains("um.php")) { - val desutext = app.get(link, referer = data).text - val desuRegex = Regex("((https:|http:)//.*\\.m3u8)") - val file = desuRegex.find(desutext)?.value - val namedesu = "Desu" - generateM3u8( - namedesu, - file!!, - mainUrl, - ).forEach { desurl -> - streamClean( - namedesu, - desurl.url, - mainUrl, - desurl.quality.toString(), - callback, - true - ) - } - } - if (link.contains("jkmedia")) { - app.get( - link, - referer = data, - allowRedirects = false - ).okhttpResponse.headers.values("location").apmap { xtremeurl -> - val namex = "Xtreme S" - streamClean( - namex, - xtremeurl, - "", - null, - callback, - xtremeurl.contains(".m3u8") - ) - } - } - } - } - } - return true - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt deleted file mode 100644 index 6b79b878..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt +++ /dev/null @@ -1,174 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.Jsoup -import java.util.* - -class KawaiifuProvider : MainAPI() { - override var mainUrl = "https://kawaiifu.com" - override var name = "Kawaiifu" - override val hasQuickSearch = false - override val hasMainPage = true - - override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val resp = app.get(mainUrl).text - - val soup = Jsoup.parse(resp) - - items.add(HomePageList("Latest Updates", soup.select(".today-update .item").mapNotNull { - val title = it.selectFirst("img")?.attr("alt") - AnimeSearchResponse( - title ?: return@mapNotNull null, - it.selectFirst("a")?.attr("href") ?: return@mapNotNull null, - this.name, - TvType.Anime, - it.selectFirst("img")?.attr("src"), - it.selectFirst("h4 > a")?.attr("href")?.split("-")?.last()?.toIntOrNull(), - if (title.contains("(DUB)")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( - DubStatus.Subbed - ), - ) - })) - for (section in soup.select(".section")) { - try { - val title = section.selectFirst(".title")!!.text() - val anime = section.select(".list-film > .item").mapNotNull { ani -> - val animTitle = ani.selectFirst("img")?.attr("alt") - AnimeSearchResponse( - animTitle ?: return@mapNotNull null, - ani.selectFirst("a")?.attr("href") ?: return@mapNotNull null, - this.name, - TvType.Anime, - ani.selectFirst("img")?.attr("src"), - ani.selectFirst(".vl-chil-date")?.text()?.toIntOrNull(), - if (animTitle.contains("(DUB)")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( - DubStatus.Subbed - ), - ) - } - items.add(HomePageList(title, anime)) - - } catch (e: Exception) { - e.printStackTrace() - } - } - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - - override suspend fun search(query: String): ArrayList { - val link = "$mainUrl/search-movie?keyword=${query}" - val html = app.get(link).text - val soup = Jsoup.parse(html) - - return ArrayList(soup.select(".item").mapNotNull { - val year = it.selectFirst("h4 > a")?.attr("href")?.split("-")?.last()?.toIntOrNull() - val title = it.selectFirst("img")?.attr("alt") ?: return@mapNotNull null - val poster = it.selectFirst("img")?.attr("src") - val uri = it.selectFirst("a")?.attr("href") ?: return@mapNotNull null - AnimeSearchResponse( - title, - uri, - this.name, - TvType.Anime, - poster, - year, - if (title.contains("(DUB)")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed), - ) - }) - } - - override suspend fun load(url: String): LoadResponse { - val html = app.get(url).text - val soup = Jsoup.parse(html) - - val title = soup.selectFirst(".title")!!.text() - val tags = soup.select(".table a[href*=\"/tag/\"]").map { tag -> tag.text() } - val description = soup.select(".sub-desc p") - .filter { it -> it.select("strong").isEmpty() && it.select("iframe").isEmpty() } - .joinToString("\n") { it.text() } - val year = url.split("/").filter { it.contains("-") }[0].split("-")[1].toIntOrNull() - - val episodesLink = soup.selectFirst("a[href*=\".html-episode\"]")?.attr("href") - ?: throw ErrorLoadingException("Error getting episode list") - val episodes = Jsoup.parse( - app.get(episodesLink).text - ).selectFirst(".list-ep")?.select("li")?.map { - Episode( - it.selectFirst("a")!!.attr("href"), - if (it.text().trim().toIntOrNull() != null) "Episode ${ - it.text().trim() - }" else it.text().trim() - ) - } - val poster = soup.selectFirst("a.thumb > img")?.attr("src") - - return newAnimeLoadResponse(title, url, TvType.Anime) { - this.year = year - posterUrl = poster - addEpisodes(DubStatus.Subbed, episodes) - plot = description - this.tags = tags - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val htmlSource = app.get(data).text - val soupa = Jsoup.parse(htmlSource) - - val episodeNum = - if (data.contains("ep=")) data.split("ep=")[1].split("&")[0].toIntOrNull() else null - - val servers = soupa.select(".list-server").map { - val serverName = it.selectFirst(".server-name")!!.text() - val episodes = it.select(".list-ep > li > a") - .map { episode -> Pair(episode.attr("href"), episode.text()) } - val episode = if (episodeNum == null) episodes[0] else episodes.mapNotNull { ep -> - if ((if (ep.first.contains("ep=")) ep.first.split("ep=")[1].split("&")[0].toIntOrNull() else null) == episodeNum) { - ep - } else null - }[0] - Pair(serverName, episode) - }.map { - if (it.second.first == data) { - val sources = soupa.select("video > source") - .map { source -> Pair(source.attr("src"), source.attr("data-quality")) } - Triple(it.first, sources, it.second.second) - } else { - val html = app.get(it.second.first).text - val soup = Jsoup.parse(html) - - val sources = soup.select("video > source") - .map { source -> Pair(source.attr("src"), source.attr("data-quality")) } - Triple(it.first, sources, it.second.second) - } - } - - servers.forEach { - it.second.forEach { source -> - callback( - ExtractorLink( - "Kawaiifu", - it.first, - source.first, - "", - getQualityFromName(source.second), - source.first.contains(".m3u") - ) - ) - } - } - return true - } -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KimCartoonProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KimCartoonProvider.kt deleted file mode 100644 index 4826f2bc..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KimCartoonProvider.kt +++ /dev/null @@ -1,152 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor - -class KimCartoonProvider : MainAPI() { - - override var mainUrl = "https://kimcartoon.li" - override var name = "Kim Cartoon" - override val hasQuickSearch = true - override val hasMainPage = true - - override val supportedTypes = setOf(TvType.Cartoon) - - private fun fixUrl(url: String): String { - return if (url.startsWith("/")) mainUrl + url else url - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val doc = app.get(mainUrl).document.select("#container") - val response = mutableListOf( - HomePageList( - "Latest Update", - doc.select("div.bigBarContainer div.items > div > a").map { - AnimeSearchResponse( - it.select(".item-title").let { div -> - //Because it doesn't contain Title separately - div.text().replace(div.select("span").text(), "") - }, - mainUrl + it.attr("href"), - mainUrl, - TvType.Cartoon, - fixUrl(it.select("img").let { img -> - img.attr("src").let { src -> - src.ifEmpty { img.attr("srctemp") } - } - }) - ) - } - ) - ) - val list = mapOf( - "Top Day" to "tab-top-day", - "Top Week" to "tab-top-week", - "Top Month" to "tab-top-month", - "New Cartoons" to "tab-newest-series" - ) - response.addAll(list.map { item -> - HomePageList( - item.key, - doc.select("#${item.value} > div").map { - AnimeSearchResponse( - it.select("span.title").text(), - mainUrl + it.select("a")[0].attr("href"), - mainUrl, - TvType.Cartoon, - fixUrl(it.select("a > img").attr("src")) - ) - } - ) - }) - return HomePageResponse(response) - } - - override suspend fun search(query: String): List { - return app.post( - "$mainUrl/Search/Cartoon", - data = mapOf("keyword" to query) - ).document - .select("#leftside > div.bigBarContainer div.list-cartoon > div.item > a") - .map { - AnimeSearchResponse( - it.select("span").text(), - mainUrl + it.attr("href"), - mainUrl, - TvType.Cartoon, - fixUrl(it.select("img").attr("src")) - ) - } - } - - override suspend fun quickSearch(query: String): List { - return app.post( - "$mainUrl/Ajax/SearchSuggest", - data = mapOf("keyword" to query) - ).document.select("a").map { - AnimeSearchResponse( - it.text(), - it.attr("href"), - mainUrl, - TvType.Cartoon, - ) - } - } - - - private fun getStatus(from: String?): ShowStatus? { - return when { - from?.contains("Completed") == true -> ShowStatus.Completed - from?.contains("Ongoing") == true -> ShowStatus.Ongoing - else -> null - } - } - - override suspend fun load(url: String): LoadResponse { - val doc = app.get(url).document.select("#leftside") - val info = doc.select("div.barContent") - val name = info.select("a.bigChar").text() - val eps = doc.select("table.listing > tbody > tr a").reversed().map { - Episode( - fixUrl(it.attr("href")), - it.text().replace(name, "").trim() - ) - } - val infoText = info.text() - fun getData(after: String, before: String): String? { - return if (infoText.contains(after)) - infoText - .substringAfter("$after:") - .substringBefore(before) - .trim() - else null - } - - return newTvSeriesLoadResponse(name, url, TvType.Cartoon, eps) { - posterUrl = fixUrl(info.select("div > img").attr("src")) - showStatus = getStatus(getData("Status", "Views")) - plot = getData("Summary", "Tags:") - tags = getData("Genres", "Date aired")?.split(",") - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val servers = - app.get(data).document.select("#selectServer > option").map { fixUrl(it.attr("value")) } - servers.apmap { - app.get(it).document.select("#my_video_1").attr("src").let { iframe -> - if (iframe.isNotEmpty()) { - loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback) - } - //There are other servers, but they require some work to do - } - } - return true - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuramanimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuramanimeProvider.kt deleted file mode 100644 index 4d2f3d46..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuramanimeProvider.kt +++ /dev/null @@ -1,176 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall -import com.lagradost.cloudstream3.utils.ExtractorLink -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class KuramanimeProvider : MainAPI() { - override var mainUrl = "https://kuramanime.com" - override var name = "Kuramanime" - override val hasQuickSearch = false - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Selesai Tayang" -> ShowStatus.Completed - "Sedang Tayang" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/anime/ongoing?order_by=updated&page=" to "Sedang Tayang", - "$mainUrl/anime/finished?order_by=updated&page=" to "Selesai Tayang", - "$mainUrl/properties/season/summer-2022?order_by=most_viewed&page=" to "Dilihat Terbanyak Musim Ini", - "$mainUrl/anime/movie?order_by=updated&page=" to "Film Layar Lebar", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - - val home = document.select("div.col-lg-4.col-md-6.col-sm-6").mapNotNull { - it.toSearchResult() - } - - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/episode")) { - Regex("(.*)/episode/.+").find(uri)?.groupValues?.get(1).toString() + "/" - } else { - uri - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href"))) - val title = this.selectFirst("h5 a")?.text() ?: return null - val posterUrl = fixUrl(this.select("div.product__item__pic.set-bg").attr("data-setbg")) - val episode = Regex("([0-9*])\\s?/").find( - this.select("div.ep span").text() - )?.groupValues?.getOrNull(1)?.toIntOrNull() - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(episode) - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/anime?search=$query&order_by=oldest" - val document = app.get(link).document - - return document.select(".product__item").mapNotNull { - val title = it.selectFirst("div.product__item__text > h5")!!.text().trim() - val poster = it.selectFirst("a > div")!!.attr("data-setbg") - val tvType = - getType(it.selectFirst(".product__item__text > ul > li")!!.text().toString()) - val href = fixUrl(it.selectFirst("a")!!.attr("href")) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addDubStatus(dubExist = false, subExist = true) - } - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst(".anime__details__title > h3")!!.text().trim() - val poster = document.selectFirst(".anime__details__pic")?.attr("data-setbg") - val tags = - document.select("div.anime__details__widget > div > div:nth-child(2) > ul > li:nth-child(1)") - .text().trim().replace("Genre: ", "").split(", ") - - val year = Regex("[^0-9]").replace( - document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(5)") - .text().trim().replace("Musim: ", ""), "" - ).toIntOrNull() - val status = getStatus( - document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(3)") - .text().trim().replace("Status: ", "") - ) - val description = document.select(".anime__details__text > p").text().trim() - - val episodes = - Jsoup.parse(document.select("#episodeLists").attr("data-content")).select("a").map { - val name = it.text().trim() - val link = it.attr("href") - Episode(link, name) - } - - val recommendations = document.select("div#randomList > a").mapNotNull { - val epHref = it.attr("href") - val epTitle = it.select("h5.sidebar-title-h5.px-2.py-2").text() - val epPoster = it.select(".product__sidebar__view__item.set-bg").attr("data-setbg") - - newAnimeSearchResponse(epTitle, epHref, TvType.Anime) { - this.posterUrl = epPoster - addDubStatus(dubExist = false, subExist = true) - } - } - - return newAnimeLoadResponse(title, url, TvType.Anime) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - this.recommendations = recommendations - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val servers = app.get(data).document - servers.select("video#player > source").map { - suspendSafeApiCall { - val url = it.attr("src") - val quality = it.attr("size").toInt() - callback.invoke( - ExtractorLink( - name, - name, - url, - referer = "$mainUrl/", - quality = quality - ) - ) - } - } - - return true - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt deleted file mode 100644 index 950799e1..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt +++ /dev/null @@ -1,197 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class KuronimeProvider : MainAPI() { - override var mainUrl = "https://45.12.2.2" - override var name = "Kuronime" - override val hasQuickSearch = false - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/page/" to "New Episodes", - "$mainUrl/popular-anime/page/" to "Popular Anime", - "$mainUrl/movies/page/" to "Movies", - "$mainUrl/genres/donghua/page/" to "Donghua", - "$mainUrl/live-action/page/" to "Live Action", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("article").map { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/anime/")) { - uri - } else { - var title = uri.substringAfter("$mainUrl/") - title = when { - (title.contains("-episode")) && !(title.contains("-movie")) -> Regex("nonton-(.+)-episode").find( - title - )?.groupValues?.get(1).toString() - (title.contains("-movie")) -> Regex("nonton-(.+)-movie").find(title)?.groupValues?.get( - 1 - ).toString() - else -> title - } - - "$mainUrl/anime/$title" - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse { - val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString()) - val title = this.select(".bsuxtt, .tt > h4").text().trim() - val posterUrl = fixUrlNull( - this.selectFirst("div.view,div.bt")?.nextElementSibling()?.select("img") - ?.attr("data-src") - ) - val epNum = this.select(".ep").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull() - val tvType = getType(this.selectFirst(".bt > span")?.text().toString()) - return newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = app.get(link).document - - return document.select("article.bs").map { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst(".entry-title")?.text().toString().trim() - val poster = document.selectFirst("div.l[itemprop=image] > img")?.attr("data-src") - val tags = document.select(".infodetail > ul > li:nth-child(2) > a").map { it.text() } - val type = getType( - document.selectFirst(".infodetail > ul > li:nth-child(7)")?.ownText()?.trim().toString() - ) - val trailer = document.selectFirst("div.tply iframe")?.attr("data-lazy-src") - val year = Regex("\\d, ([0-9]*)").find( - document.select(".infodetail > ul > li:nth-child(5)").text() - )?.groupValues?.get(1)?.toIntOrNull() - val status = getStatus( - document.selectFirst(".infodetail > ul > li:nth-child(3)")!!.ownText() - .replace(Regex("\\W"), "") - ) - val description = document.select("span.const > p").text() - - val episodes = document.select("div.bixbox.bxcl > ul > li").map { - val name = it.selectFirst("a")?.text()?.trim() - val episode = - it.selectFirst("a")?.text()?.trim()?.replace("Episode", "")?.trim()?.toIntOrNull() - val link = it.selectFirst("a")!!.attr("href") - Episode(link, name = name, episode = episode) - }.reversed() - - return newAnimeLoadResponse(title, url, type) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - addTrailer(trailer) - this.tags = tags - } - } - - private suspend fun invokeKuroSource( - url: String, - sourceCallback: (ExtractorLink) -> Unit - ) { - val doc = app.get(url, referer = "${mainUrl}/").document - - doc.select("script").map { script -> - if (script.data().contains("function jalankan_jwp() {")) { - val data = script.data() - val doma = data.substringAfter("var doma = \"").substringBefore("\";") - val token = data.substringAfter("var token = \"").substringBefore("\";") - val pat = data.substringAfter("var pat = \"").substringBefore("\";") - val link = "$doma$token$pat/index.m3u8" - val quality = - Regex("\\d{3,4}p").find(doc.select("title").text())?.groupValues?.get(0) - - sourceCallback.invoke( - ExtractorLink( - this.name, - this.name, - link, - referer = "https://animeku.org/", - quality = getQualityFromName(quality), - headers = mapOf("Origin" to "https://animeku.org"), - isM3u8 = true - ) - ) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - val sources = document.select(".mobius > .mirror > option").mapNotNull { - fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("data-src")) - } - - sources.apmap { - safeApiCall { - when { - it.startsWith("https://animeku.org") -> invokeKuroSource(it, callback) - else -> loadExtractor(it, mainUrl, subtitleCallback, callback) - } - } - } - return true - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MonoschinosProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MonoschinosProvider.kt deleted file mode 100644 index 7e063f0c..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MonoschinosProvider.kt +++ /dev/null @@ -1,156 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.FEmbed -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import java.util.* - - -class MonoschinosProvider : MainAPI() { - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Especial")) TvType.OVA - else if (t.contains("Pelicula")) TvType.AnimeMovie - else TvType.Anime - } - - fun getDubStatus(title: String): DubStatus { - return if (title.contains("Latino") || title.contains("Castellano")) - DubStatus.Dubbed - else DubStatus.Subbed - } - } - - override var mainUrl = "https://monoschinos2.com" - override var name = "Monoschinos" - override var lang = "es" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.OVA, - TvType.Anime, - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val urls = listOf( - Pair("$mainUrl/emision", "En emisión"), - Pair( - "$mainUrl/animes?categoria=pelicula&genero=false&fecha=false&letra=false", - "Peliculas" - ), - Pair("$mainUrl/animes", "Animes"), - ) - - val items = ArrayList() - - items.add( - HomePageList( - "Capítulos actualizados", - app.get(mainUrl, timeout = 120).document.select(".col-6").map { - val title = it.selectFirst("p.animetitles")?.text() ?: it.selectFirst(".animetitles")?.text() ?: "" - val poster = it.selectFirst(".animeimghv")!!.attr("data-src") - val epRegex = Regex("episodio-(\\d+)") - val url = it.selectFirst("a")?.attr("href")!!.replace("ver/", "anime/") - .replace(epRegex, "sub-espanol") - val epNum = (it.selectFirst(".positioning h5")?.text() ?: it.selectFirst("div.positioning p")?.text())?.toIntOrNull() - newAnimeSearchResponse(title, url) { - this.posterUrl = fixUrl(poster) - addDubStatus(getDubStatus(title), epNum) - } - }) - ) - - for (i in urls) { - try { - val home = app.get(i.first, timeout = 120).document.select(".col-6").map { - val title = it.selectFirst(".seristitles")!!.text() - val poster = it.selectFirst("img.animemainimg")!!.attr("src") - newAnimeSearchResponse(title, fixUrl(it.selectFirst("a")!!.attr("href"))) { - this.posterUrl = fixUrl(poster) - addDubStatus(getDubStatus(title)) - } - } - - items.add(HomePageList(i.second, home)) - } catch (e: Exception) { - e.printStackTrace() - } - } - - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - override suspend fun search(query: String): ArrayList { - val search = - app.get("$mainUrl/buscar?q=$query", timeout = 120).document.select(".col-6").map { - val title = it.selectFirst(".seristitles")!!.text() - val href = fixUrl(it.selectFirst("a")!!.attr("href")) - val image = it.selectFirst("img.animemainimg")!!.attr("src") - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - fixUrl(image), - null, - if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( - DubStatus.Dubbed - ) else EnumSet.of(DubStatus.Subbed), - ) - } - return ArrayList(search) - } - - override suspend fun load(url: String): LoadResponse { - val doc = app.get(url, timeout = 120).document - val poster = doc.selectFirst(".chapterpic img")!!.attr("src") - val title = doc.selectFirst(".chapterdetails h1")!!.text() - val type = doc.selectFirst("div.chapterdetls2")!!.text() - val description = doc.selectFirst("p.textComplete")!!.text().replace("Ver menos", "") - val genres = doc.select(".breadcrumb-item a").map { it.text() } - val status = when (doc.selectFirst("button.btn1")?.text()) { - "Estreno" -> ShowStatus.Ongoing - "Finalizado" -> ShowStatus.Completed - else -> null - } - val episodes = doc.select("div.col-item").map { - val name = it.selectFirst("p.animetitles")!!.text() - val link = it.selectFirst("a")!!.attr("href") - val epThumb = it.selectFirst(".animeimghv")!!.attr("data-src") - Episode(link, name, posterUrl = epThumb) - } - return newAnimeLoadResponse(title, url, getType(type)) { - posterUrl = poster - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - tags = genres - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - app.get(data).document.select("div.playother p").forEach { - val encodedurl = it.select("p").attr("data-player") - val urlDecoded = base64Decode(encodedurl) - val url = (urlDecoded).replace("https://monoschinos2.com/reproductor?url=", "") - if (url.startsWith("https://www.fembed.com")) { - val extractor = FEmbed() - extractor.getUrl(url).forEach { link -> - callback.invoke(link) - } - } else { - loadExtractor(url, mainUrl, subtitleCallback, callback) - } - } - return true - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MundoDonghuaProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MundoDonghuaProvider.kt deleted file mode 100644 index b32000db..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MundoDonghuaProvider.kt +++ /dev/null @@ -1,217 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 -import com.lagradost.cloudstream3.utils.getAndUnpack -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.cloudstream3.utils.loadExtractor -import java.util.* - - -class MundoDonghuaProvider : MainAPI() { - - override var mainUrl = "https://www.mundodonghua.com" - override var name = "MundoDonghua" - override var lang = "es" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Anime, - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val urls = listOf( - Pair("$mainUrl/lista-donghuas", "Donghuas"), - ) - - val items = ArrayList() - items.add( - HomePageList( - "Últimos episodios", - app.get(mainUrl, timeout = 120).document.select("div.row .col-xs-4").map { - val title = it.selectFirst("h5")?.text() ?: "" - val poster = it.selectFirst(".fit-1 img")?.attr("src") - val epRegex = Regex("(\\/(\\d+)\$)") - val url = it.selectFirst("a")?.attr("href")?.replace(epRegex,"")?.replace("/ver/","/donghua/") - val epnumRegex = Regex("((\\d+)$)") - val epNum = epnumRegex.find(title)?.value?.toIntOrNull() - val dubstat = if (title.contains("Latino") || title.contains("Castellano")) DubStatus.Dubbed else DubStatus.Subbed - newAnimeSearchResponse(title.replace(Regex("Episodio|(\\d+)"),"").trim(), fixUrl(url ?: "")) { - this.posterUrl = fixUrl(poster ?: "") - addDubStatus(dubstat, epNum) - } - }) - ) - - urls.apmap { (url, name) -> - val home = app.get(url, timeout = 120).document.select(".col-xs-4").map { - val title = it.selectFirst(".fs-14")?.text() ?: "" - val poster = it.selectFirst(".fit-1 img")?.attr("src") ?: "" - AnimeSearchResponse( - title, - fixUrl(it.selectFirst("a")?.attr("href") ?: ""), - this.name, - TvType.Anime, - fixUrl(poster), - null, - if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( - DubStatus.Dubbed - ) else EnumSet.of(DubStatus.Subbed), - ) - } - - items.add(HomePageList(name, home)) - } - - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - override suspend fun search(query: String): List { - return app.get("$mainUrl/busquedas/$query", timeout = 120).document.select(".col-xs-4").map { - val title = it.selectFirst(".fs-14")?.text() ?: "" - val href = fixUrl(it.selectFirst("a")?.attr("href") ?: "") - val image = it.selectFirst(".fit-1 img")?.attr("src") - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - fixUrl(image ?: ""), - null, - if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( - DubStatus.Dubbed - ) else EnumSet.of(DubStatus.Subbed), - ) - } - } - - override suspend fun load(url: String): LoadResponse { - val doc = app.get(url, timeout = 120).document - val poster = doc.selectFirst("head meta[property=og:image]")?.attr("content") ?: "" - val title = doc.selectFirst(".ls-title-serie")?.text() ?: "" - val description = doc.selectFirst("p.text-justify.fc-dark")?.text() ?: "" - val genres = doc.select("span.label.label-primary.f-bold").map { it.text() } - val status = when (doc.selectFirst("div.col-md-6.col-xs-6.align-center.bg-white.pt-10.pr-15.pb-0.pl-15 p span.badge.bg-default")?.text()) { - "En Emisión" -> ShowStatus.Ongoing - "Finalizada" -> ShowStatus.Completed - else -> null - } - val episodes = doc.select("ul.donghua-list a").map { - val name = it.selectFirst(".fs-16")?.text() - val link = it.attr("href") - Episode(fixUrl(link), name) - }.reversed() - val typeinfo = doc.select("div.row div.col-md-6.pl-15 p.fc-dark").text() - val tvType = if (typeinfo.contains(Regex("Tipo.*Pel.cula"))) TvType.AnimeMovie else TvType.Anime - return newAnimeLoadResponse(title, url, tvType) { - posterUrl = poster - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - tags = genres - } - } - data class Protea ( - @JsonProperty("source") val source: List, - @JsonProperty("poster") val poster: String? - ) - - data class Source ( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("default") val default: String? - ) - - private fun cleanStream( - name: String, - url: String, - qualityString: String?, - callback: (ExtractorLink) -> Unit, - isM3U8: Boolean - ): Boolean { - callback( - ExtractorLink( - name, - name, - url, - "", - getQualityFromName(qualityString), - isM3U8 - ) - ) - return true - } - - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - app.get(data).document.select("script").apmap { script -> - if (script.data().contains("eval(function(p,a,c,k,e")) { - val packedRegex = Regex("eval\\(function\\(p,a,c,k,e,.*\\)\\)") - packedRegex.findAll(script.data()).map { - it.value - }.toList().apmap { - val unpack = getAndUnpack(it).replace("diasfem","embedsito") - fetchUrls(unpack).apmap { url -> - loadExtractor(url, data, subtitleCallback, callback) - } - if (unpack.contains("protea_tab")) { - val protearegex = Regex("(protea_tab.*slug.*,type)") - val slug = protearegex.findAll(unpack).map { - it.value.replace(Regex("(protea_tab.*slug\":\")"),"").replace("\"},type","") - }.first() - val requestlink = "$mainUrl/api_donghua.php?slug=$slug" - val response = app.get(requestlink, headers = - mapOf("Host" to "www.mundodonghua.com", - "User-Agent" to USER_AGENT, - "Accept" to "*/*", - "Accept-Language" to "en-US,en;q=0.5", - "Referer" to data, - "X-Requested-With" to "XMLHttpRequest", - "DNT" to "1", - "Connection" to "keep-alive", - "Sec-Fetch-Dest" to "empty", - "Sec-Fetch-Mode" to "no-cors", - "Sec-Fetch-Site" to "same-origin", - "TE" to "trailers", - "Pragma" to "no-cache", - "Cache-Control" to "no-cache",) - ).text.removePrefix("[").removeSuffix("]") - val json = parseJson(response) - json.source.forEach { source -> - val protename = "Protea" - cleanStream(protename, fixUrl(source.file), source.label, callback, false) - } - } - if (unpack.contains("asura_player")) { - val asuraRegex = Regex("(asura_player.*type)") - asuraRegex.findAll(unpack).map { - it.value - }.toList().apmap { protea -> - val asuraname = "Asura" - val file = protea.substringAfter("{file:\"").substringBefore("\"") - generateM3u8( - asuraname, - file, - "" - ).forEach { - cleanStream(asuraname, it.url, it.quality.toString(), callback, true) - } - } - } - } - } - } - return true - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NeonimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NeonimeProvider.kt deleted file mode 100644 index 051c4cfd..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NeonimeProvider.kt +++ /dev/null @@ -1,178 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.nodes.Element -import java.util.* - -class NeonimeProvider : MainAPI() { - override var mainUrl = "https://neonime.watch" - override var name = "Neonime" - override val hasQuickSearch = false - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Ended" -> ShowStatus.Completed - "OnGoing" -> ShowStatus.Ongoing - "Ongoing" -> ShowStatus.Ongoing - "In Production" -> ShowStatus.Ongoing - "Returning Series" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/episode/page/" to "Episode Terbaru", - "$mainUrl/tvshows/page/" to "Anime Terbaru", - "$mainUrl/movies/page/" to "Movie", - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("tbody tr,div.item").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return when { - uri.contains("/episode") -> { - val title = uri.substringAfter("$mainUrl/episode/").let { tt -> - val fixTitle = Regex("(.*)-\\d{1,2}x\\d+").find(tt)?.groupValues?.getOrNull(1).toString() - when { - !tt.contains("-season") && !tt.contains(Regex("-1x\\d+")) && !tt.contains("one-piece") -> "$fixTitle-season-${Regex("-(\\d{1,2})x\\d+").find(tt)?.groupValues?.getOrNull(1).toString()}" - tt.contains("-special") -> fixTitle.replace(Regex("-x\\d+"), "") - !fixTitle.contains("-subtitle-indonesia") -> "$fixTitle-subtitle-indonesia" - else -> fixTitle - } - } - -// title = when { -// title.contains("youkoso-jitsuryoku") && !title.contains("-season") -> title.replace("-e-", "-e-tv-") -// else -> title -// } - - "$mainUrl/tvshows/$title" - } - else -> uri - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val title = this.selectFirst("td.bb a")?.ownText() ?: this.selectFirst("h2")?.text() ?: return null - val href = getProperAnimeLink(fixUrl(this.select("a").attr("href"))) - val posterUrl = fixUrl(this.select("img").attr("data-src")) - val epNum = this.selectFirst("td.bb span")?.text()?.let { eps -> - Regex("Episode\\s?([0-9]+)").find(eps)?.groupValues?.getOrNull(1)?.toIntOrNull() - } - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = app.get(link).document - - return document.select("div.item.episode-home").mapNotNull { - val title = it.selectFirst("div.judul-anime > span")!!.text() - val poster = it.select("img").attr("data-src").toString().trim() - val episodes = it.selectFirst("div.fixyear > h2.text-center")!! - .text().replace(Regex("[^0-9]"), "").trim().toIntOrNull() - val tvType = getType(it.selectFirst("span.calidad2.episode")?.text().toString()) - val href = getProperAnimeLink(fixUrl(it.selectFirst("a")!!.attr("href"))) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addSub(episodes) - } - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - if (url.contains("movie") || url.contains("live-action")) { - val mTitle = document.selectFirst(".sbox > .data > h1[itemprop = name]")?.text().toString().trim() - val mTrailer = document.selectFirst("div.youtube_id iframe")?.attr("data-wpfc-original-src")?.substringAfterLast("html#")?.let{ "https://www.youtube.com/embed/$it"} - - return newMovieLoadResponse(name = mTitle, url = url, type = TvType.Movie, dataUrl = url) { - posterUrl = document.selectFirst(".sbox > .imagen > .fix > img[itemprop = image]")?.attr("data-src") - year = document.selectFirst("a[href*=release-year]")!!.text().toIntOrNull() - plot = document.select("div[itemprop = description]").text().trim() - rating = document.select("span[itemprop = ratingValue]").text().toIntOrNull() - tags = document.select("p.meta_dd > a").map { it.text() } - addTrailer(mTrailer) - } - } - else { - val title = document.select("h1[itemprop = name]").text().trim() - val trailer = document.selectFirst("div.youtube_id_tv iframe")?.attr("data-wpfc-original-src")?.substringAfterLast("html#")?.let{ "https://www.youtube.com/embed/$it"} - - val episodes = document.select("ul.episodios > li").mapNotNull { - val header = it.selectFirst(".episodiotitle > a")?.ownText().toString() - val name = Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header - val link = fixUrl(it.selectFirst(".episodiotitle > a")!!.attr("href")) - Episode(link, name) - }.reversed() - - return newAnimeLoadResponse(title, url, TvType.Anime) { - engName = title - posterUrl = document.selectFirst(".imagen > img")?.attr("data-src") - year = document.select("#info a[href*=\"-year/\"]").text().toIntOrNull() - addEpisodes(DubStatus.Subbed, episodes) - showStatus = getStatus(document.select("div.metadatac > span").last()!!.text().trim()) - plot = document.select("div[itemprop = description] > p").text().trim() - tags = document.select("#info a[href*=\"-genre/\"]").map { it.text() } - addTrailer(trailer) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val source = if(data.contains("movie") || data.contains("live-action")) { - app.get(data).document.select("#player2-1 > div[id*=div]").mapNotNull { - fixUrl(it.select("iframe").attr("data-src")) - } - } else { - app.get(data).document.select(".player2 > .embed2 > div[id*=player]").mapNotNull { - fixUrl(it.select("iframe").attr("data-src")) - } - } - - source.apmap { - loadExtractor(it, data, subtitleCallback, callback) - } - - return true - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt deleted file mode 100644 index 05ea8304..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt +++ /dev/null @@ -1,357 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.logError -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup - -class NineAnimeProvider : MainAPI() { - override var mainUrl = "https://9anime.id" - override var name = "9Anime" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf(TvType.Anime) - override val hasQuickSearch = true - - // taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/NineAnime.kt - // GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md - companion object { - private const val nineAnimeKey = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - private const val cipherKey = "kMXzgyNzT3k5dYab" - - fun encodeVrf(text: String, mainKey: String): String { - return encode( - encrypt( - cipher(mainKey, encode(text)), - nineAnimeKey - )//.replace("""=+$""".toRegex(), "") - ) - } - - fun decodeVrf(text: String, mainKey: String): String { - return decode(cipher(mainKey, decrypt(text, nineAnimeKey))) - } - - fun encrypt(input: String, key: String): String { - if (input.any { it.code > 255 }) throw Exception("illegal characters!") - var output = "" - for (i in input.indices step 3) { - val a = intArrayOf(-1, -1, -1, -1) - a[0] = input[i].code shr 2 - a[1] = (3 and input[i].code) shl 4 - if (input.length > i + 1) { - a[1] = a[1] or (input[i + 1].code shr 4) - a[2] = (15 and input[i + 1].code) shl 2 - } - if (input.length > i + 2) { - a[2] = a[2] or (input[i + 2].code shr 6) - a[3] = 63 and input[i + 2].code - } - for (n in a) { - if (n == -1) output += "=" - else { - if (n in 0..63) output += key[n] - } - } - } - return output - } - - fun cipher(key: String, text: String): String { - val arr = IntArray(256) { it } - - var u = 0 - var r: Int - arr.indices.forEach { - u = (u + arr[it] + key[it % key.length].code) % 256 - r = arr[it] - arr[it] = arr[u] - arr[u] = r - } - u = 0 - var c = 0 - - return text.indices.map { j -> - c = (c + 1) % 256 - u = (u + arr[c]) % 256 - r = arr[c] - arr[c] = arr[u] - arr[u] = r - (text[j].code xor arr[(arr[c] + arr[u]) % 256]).toChar() - }.joinToString("") - } - - @Suppress("SameParameterValue") - private fun decrypt(input: String, key: String): String { - val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) { - input.replace("""==?$""".toRegex(), "") - } else input - if (t.length % 4 == 1 || t.contains("""[^+/0-9A-Za-z]""".toRegex())) throw Exception("bad input") - var i: Int - var r = "" - var e = 0 - var u = 0 - for (o in t.indices) { - e = e shl 6 - i = key.indexOf(t[o]) - e = e or i - u += 6 - if (24 == u) { - r += ((16711680 and e) shr 16).toChar() - r += ((65280 and e) shr 8).toChar() - r += (255 and e).toChar() - e = 0 - u = 0 - } - } - return if (12 == u) { - e = e shr 4 - r + e.toChar() - } else { - if (18 == u) { - e = e shr 2 - r += ((65280 and e) shr 8).toChar() - r += (255 and e).toChar() - } - r - } - } - - fun encode(input: String): String = - java.net.URLEncoder.encode(input, "utf-8").replace("+", "%20") - - private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8") - } - - override val mainPage = mainPageOf( - "$mainUrl/ajax/home/widget/trending?page=" to "Trending", - "$mainUrl/ajax/home/widget/updated-all?page=" to "All", - "$mainUrl/ajax/home/widget/updated-sub?page=" to "Recently Updated (SUB)", - "$mainUrl/ajax/home/widget/updated-dub?page=" to "Recently Updated (DUB)", - "$mainUrl/ajax/home/widget/updated-china?page=" to "Recently Updated (Chinese)", - "$mainUrl/ajax/home/widget/random?page=" to "Random", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val url = request.data + page - val home = Jsoup.parse( - app.get( - url - ).parsed().html - ).select("div.item").mapNotNull { element -> - val title = element.selectFirst(".info > .name") ?: return@mapNotNull null - val link = title.attr("href") - val poster = element.selectFirst(".poster > a > img")?.attr("src") - val meta = element.selectFirst(".poster > a > .meta > .inner > .left") - val subbedEpisodes = meta?.selectFirst(".sub")?.text()?.toIntOrNull() - val dubbedEpisodes = meta?.selectFirst(".dub")?.text()?.toIntOrNull() - - newAnimeSearchResponse(title.text() ?: return@mapNotNull null, link) { - this.posterUrl = poster - addDubStatus( - dubbedEpisodes != null, - subbedEpisodes != null, - dubbedEpisodes, - subbedEpisodes - ) - } - } - - return newHomePageResponse(request.name, home) - } - - data class Response( - @JsonProperty("result") val html: String - ) - - data class QuickSearchResponse( - //@JsonProperty("status") val status: Int? = null, - @JsonProperty("result") val result: QuickSearchResult? = null, - //@JsonProperty("message") val message: String? = null, - //@JsonProperty("messages") val messages: ArrayList = arrayListOf() - ) - - data class QuickSearchResult( - @JsonProperty("html") val html: String? = null, - //@JsonProperty("linkMore") val linkMore: String? = null - ) - - override suspend fun quickSearch(query: String): List? { - val vrf = encodeVrf(query, cipherKey) - val url = - "$mainUrl/ajax/anime/search?keyword=$query&vrf=$vrf" - val response = app.get(url).parsedSafe() - val document = Jsoup.parse(response?.result?.html ?: return null) - return document.select(".items > a").mapNotNull { element -> - val link = fixUrl(element?.attr("href") ?: return@mapNotNull null) - val title = element.selectFirst(".info > .name")?.text() ?: return@mapNotNull null - newAnimeSearchResponse(title, link) { - posterUrl = element.selectFirst(".poster > span > img")?.attr("src") - } - } - } - - override suspend fun search(query: String): List { - val vrf = encodeVrf(query, cipherKey) - //?language%5B%5D=${if (selectDub) "dubbed" else "subbed"}& - val url = - "$mainUrl/filter?keyword=${encode(query)}&vrf=${vrf}&page=1" - return app.get(url).document.select("#list-items div.ani.poster.tip > a").mapNotNull { - val link = fixUrl(it.attr("href") ?: return@mapNotNull null) - val img = it.select("img") - val title = img.attr("alt") - newAnimeSearchResponse(title, link) { - posterUrl = img.attr("src") - } - } - } - - override suspend fun load(url: String): LoadResponse { - val validUrl = url.replace("https://9anime.to", mainUrl) - val doc = app.get(validUrl).document - - val meta = doc.selectFirst("#w-info") ?: throw ErrorLoadingException("Could not find info") - val ratingElement = meta.selectFirst(".brating > #w-rating") - val id = ratingElement?.attr("data-id") ?: throw ErrorLoadingException("Could not find id") - val binfo = - meta.selectFirst(".binfo") ?: throw ErrorLoadingException("Could not find binfo") - val info = binfo.selectFirst(".info") ?: throw ErrorLoadingException("Could not find info") - - val title = (info.selectFirst(".title") ?: info.selectFirst(".d-title"))?.text() - ?: throw ErrorLoadingException("Could not find title") - - val vrf = encodeVrf(id, cipherKey) - val episodeListUrl = "$mainUrl/ajax/episode/list/$id?vrf=$vrf" - val body = - app.get(episodeListUrl).parsedSafe()?.html - ?: throw ErrorLoadingException("Could not parse json with cipherKey=$cipherKey id=$id url=\n$episodeListUrl") - - val subEpisodes = ArrayList() - val dubEpisodes = ArrayList() - - //TODO RECOMMENDATIONS - - Jsoup.parse(body).body().select(".episodes > ul > li > a").mapNotNull { element -> - val ids = element.attr("data-ids").split(",", limit = 2) - - val epNum = element.attr("data-num") - .toIntOrNull() // might fuck up on 7.5 ect might use data-slug instead - val epTitle = element.selectFirst("span.d-title")?.text() - //val filler = element.hasClass("filler") - ids.getOrNull(1)?.let { dub -> - dubEpisodes.add( - Episode( - "$mainUrl/ajax/server/list/$dub?vrf=${encodeVrf(dub, cipherKey)}", - epTitle, - episode = epNum - ) - ) - } - ids.getOrNull(0)?.let { sub -> - subEpisodes.add( - Episode( - "$mainUrl/ajax/server/list/$sub?vrf=${encodeVrf(sub, cipherKey)}", - epTitle, - episode = epNum - ) - ) - } - } - - return newAnimeLoadResponse(title, url, TvType.Anime) { - addEpisodes(DubStatus.Dubbed, dubEpisodes) - addEpisodes(DubStatus.Subbed, subEpisodes) - - plot = info.selectFirst(".synopsis > .shorting > .content")?.text() - posterUrl = binfo.selectFirst(".poster > span > img")?.attr("src") - rating = ratingElement.attr("data-score").toFloat().times(1000f).toInt() - - info.select(".bmeta > .meta > div").forEach { element -> - when (element.ownText()) { - "Genre: " -> { - tags = element.select("span > a").mapNotNull { it?.text() } - } - "Duration: " -> { - duration = getDurationFromString(element.selectFirst("span")?.text()) - } - "Type: " -> { - type = when (element.selectFirst("span > a")?.text()) { - "ONA" -> TvType.OVA - else -> { - type - } - } - } - "Status: " -> { - showStatus = when (element.selectFirst("span")?.text()) { - "Releasing" -> ShowStatus.Ongoing - "Completed" -> ShowStatus.Completed - else -> { - showStatus - } - } - } - else -> {} - } - } - } - } - - data class Result( - @JsonProperty("url") - val url: String? = null - ) - - data class Links( - @JsonProperty("result") - val result: Result? = null - ) - - //TODO 9anime outro into {"status":200,"result":{"url":"","skip_data":{"intro_begin":67,"intro_end":154,"outro_begin":1337,"outro_end":1415,"count":3}},"message":"","messages":[]} - private suspend fun getEpisodeLinks(id: String): Links? { - return app.get("$mainUrl/ajax/server/$id?vrf=${encodeVrf(id, cipherKey)}").parsedSafe() - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val body = app.get(data).parsed().html - val document = Jsoup.parse(body) - - document.select("li").apmap { - try { - val name = it.text() - val encodedStreamUrl = - getEpisodeLinks(it.attr("data-link-id"))?.result?.url ?: return@apmap - val url = decodeVrf(encodedStreamUrl, cipherKey) - if (!loadExtractor(url, mainUrl, subtitleCallback, callback)) { - callback( - ExtractorLink( - this.name, - name, - url, - mainUrl, - Qualities.Unknown.value, - url.contains(".m3u8") - ) - ) - } - } catch (e: Exception) { - logError(e) - } - } - - return true - } -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NontonAnimeIDProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NontonAnimeIDProvider.kt deleted file mode 100644 index 9f518c66..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NontonAnimeIDProvider.kt +++ /dev/null @@ -1,257 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class NontonAnimeIDProvider : MainAPI() { - override var mainUrl = "https://75.119.159.228" - override var name = "NontonAnimeID" - override val hasQuickSearch = false - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return when { - t.contains("TV") -> TvType.Anime - t.contains("Movie") -> TvType.AnimeMovie - else -> TvType.OVA - } - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Finished Airing" -> ShowStatus.Completed - "Currently Airing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val document = app.get(mainUrl).document - - val homePageList = ArrayList() - - document.select("section#postbaru").forEach { block -> - val header = block.selectFirst("h2")!!.text().trim() - val animes = block.select("article.animeseries").map { - it.toSearchResult() - } - if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) - } - - document.select("aside#sidebar_right > div:nth-child(4)").forEach { block -> - val header = block.selectFirst("h3")!!.ownText().trim() - val animes = block.select("li.fullwdth").map { - it.toSearchResultPopular() - } - if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) - } - - return HomePageResponse(homePageList) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/anime/")) { - uri - } else { - var title = uri.substringAfter("$mainUrl/") - val fixTitle = Regex("(.*)-episode.*").find(title)?.groupValues?.getOrNull(1).toString() - title = when { - title.contains("utawarerumono-season-3") -> fixTitle.replace( - "season-3", - "futari-no-hakuoro" - ) - title.contains("kingdom-season-4") -> fixTitle.replace("season-4", "4th-season") - title.contains("maou-sama-season-2") -> fixTitle.replace("season-2", "2") - title.contains("overlord-season-4") -> fixTitle.replace("season-4", "iv") - title.contains("kyoushitsu-e-season-2") -> fixTitle.replace( - "kyoushitsu-e-season-2", - "kyoushitsu-e-tv-2nd-season" - ) - title.contains("season-2") -> fixTitle.replace("season-2", "2nd-season") - title.contains("season-3") -> fixTitle.replace("season-3", "3rd-season") - title.contains("movie") -> title.substringBefore("-movie") - else -> fixTitle - } - "$mainUrl/anime/$title" - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse { - val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href"))) - val title = this.selectFirst("h3.title")!!.text() - val posterUrl = fixUrl(this.select("img").attr("data-src")) - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addDubStatus(dubExist = false, subExist = true) - } - - } - - private fun Element.toSearchResultPopular(): AnimeSearchResponse { - val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href"))) - val title = this.select("h4").text().trim() - val posterUrl = fixUrl(this.select("img").attr("data-src")) - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addDubStatus(dubExist = false, subExist = true) - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = app.get(link).document - - return document.select(".result > ul > li").mapNotNull { - val title = it.selectFirst("h2")!!.text().trim() - val poster = it.selectFirst("img")!!.attr("src") - val tvType = getType( - it.selectFirst(".boxinfores > span.typeseries")!!.text().toString() - ) - val href = fixUrl(it.selectFirst("a")!!.attr("href")) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addDubStatus(dubExist = false, subExist = true) - } - } - } - - private data class EpResponse( - @JsonProperty("posts") val posts: String?, - @JsonProperty("max_page") val max_page: Int?, - @JsonProperty("found_posts") val found_posts: Int?, - @JsonProperty("content") val content: String - ) - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst("h1.entry-title.cs")!!.text().trim() - val poster = document.selectFirst(".poster > img")?.attr("data-src") - val tags = document.select(".tagline > a").map { it.text() } - - val year = Regex("\\d, ([0-9]*)").find( - document.select(".bottomtitle > span:nth-child(5)").text() - )?.groupValues?.get(1)?.toIntOrNull() - val status = getStatus( - document.select("span.statusseries").text().trim() - ) - val type = getType(document.select("span.typeseries").text().trim()) - val rating = document.select("span.nilaiseries").text().trim().toIntOrNull() - val description = document.select(".entry-content.seriesdesc > p").text().trim() - val trailer = document.selectFirst("iframe#traileryt")?.attr("data-src") - - val episodes = if (document.select("button.buttfilter").isNotEmpty()) { - val id = document.select("input[name=series_id]").attr("value") - val numEp = - document.selectFirst(".latestepisode > a")?.text()?.replace(Regex("[^0-9]"), "") - .toString() - Jsoup.parse( - app.post( - url = "$mainUrl/wp-admin/admin-ajax.php", - data = mapOf( - "misha_number_of_results" to numEp, - "misha_order_by" to "date-DESC", - "action" to "mishafilter", - "series_id" to id - ) - ).parsed().content - ).select("li").map { - val name = Regex("(Episode\\s?[0-9]+)").find( - it.selectFirst("a")?.text().toString() - )?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text() - val link = fixUrl(it.selectFirst("a")!!.attr("href")) - Episode(link, name) - }.reversed() - } else { - document.select("ul.misha_posts_wrap2 > li").map { - val name = Regex("(Episode\\s?[0-9]+)").find( - it.selectFirst("a")?.text().toString() - )?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text() - val link = it.select("a").attr("href") - Episode(link, name) - }.reversed() - } - - - val recommendations = document.select(".result > li").mapNotNull { - val epHref = it.selectFirst("a")!!.attr("href") - val epTitle = it.selectFirst("h3")!!.text() - val epPoster = it.select(".top > img").attr("data-src") - - newAnimeSearchResponse(epTitle, epHref, TvType.Anime) { - this.posterUrl = epPoster - addDubStatus(dubExist = false, subExist = true) - } - } - - return newAnimeLoadResponse(title, url, type) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - this.rating = rating - plot = description - addTrailer(trailer) - this.tags = tags - this.recommendations = recommendations - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - val sources = ArrayList() - - document.select(".container1 > ul > li:not(.boxtab)").apmap { - val dataPost = it.attr("data-post") - val dataNume = it.attr("data-nume") - val dataType = it.attr("data-type") - - val iframe = app.post( - url = "$mainUrl/wp-admin/admin-ajax.php", - data = mapOf( - "action" to "player_ajax", - "post" to dataPost, - "nume" to dataNume, - "type" to dataType - ), - referer = data, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).document.select("iframe").attr("src") - - sources.add(fixUrl(iframe)) - } - - sources.apmap { - loadExtractor(it, "$mainUrl/", subtitleCallback, callback) - } - - return true - } -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/OploverzProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/OploverzProvider.kt deleted file mode 100644 index 097f9125..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/OploverzProvider.kt +++ /dev/null @@ -1,203 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.* -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import java.util.ArrayList - - -class OploverzProvider : MainAPI() { - override var mainUrl = "https://65.108.132.145" - override var name = "Oploverz" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return when { - t.contains("TV") -> TvType.Anime - t.contains("Movie") -> TvType.AnimeMovie - else -> TvType.OVA - } - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "&status=&type=&order=update" to "Episode Terbaru", - "&status=&type=&order=latest" to "Anime Terbaru", - "&sub=&order=popular" to "Popular Anime", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get("$mainUrl/anime/?page=$page${request.data}").document - val home = document.select("article[itemscope=itemscope]").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - - return if (uri.contains("/anime/")) { - uri - } else { - var title = uri.substringAfter("$mainUrl/") - title = when { - (title.contains("-episode")) && !(title.contains("-ova")) -> Regex("(.+)-episode").find( - title - )?.groupValues?.get(1).toString() - (title.contains("-ova")) -> Regex("(.+)-ova").find(title)?.groupValues?.get(1) - .toString() - (title.contains("-movie")) -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1) - .toString() - else -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1).toString() - .replace(Regex("-\\d+"), "") - } - - when { - title.contains("overlord") -> { - title = title.replace("s", "season-") - } - title.contains("kaguya-sama") -> { - title = title.replace("s3", "ultra-romantic") - } - } - - "$mainUrl/anime/$title" - } - - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val href = getProperAnimeLink(this.selectFirst("a.tip")!!.attr("href")) - val title = this.selectFirst("h2[itemprop=headline]")?.text()?.trim() ?: return null - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) - val type = getType(this.selectFirst(".eggtype, .typez")?.text()?.trim().toString()) - - return newAnimeSearchResponse(title, href, type) { - this.posterUrl = posterUrl - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = app.get(link).document - - return document.select("article[itemscope=itemscope]").map { - val title = it.selectFirst(".tt")?.ownText()?.trim().toString() - val poster = fixUrlNull(it.selectFirst("img")?.attr("src")) - val tvType = getType(it.selectFirst(".typez")?.text().toString()) - val href = fixUrl(it.selectFirst("a.tip")!!.attr("href")) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addDubStatus(dubExist = false, subExist = true) - } - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst("h1.entry-title")!!.text().trim() - val poster = document.select(".thumb > img").attr("src") - val tags = document.select(".genxed > a").map { it.text() } - - val year = Regex("\\d, ([0-9]*)").find( - document.selectFirst(".info-content > .spe > span > time")!!.text().trim() - )?.groupValues?.get(1).toString().toIntOrNull() - val status = getStatus( - document.select(".info-content > .spe > span:nth-child(1)") - .text().trim().replace("Status: ", "") - ) - val typeCheck = - when (document.select(".info-content > .spe > span:nth-child(5), .info-content > .spe > span") - .text().trim()) { - "OVA" -> "OVA" - "Movie" -> "Movie" - else -> "TV" - } - val type = getType(typeCheck) - val description = document.select(".entry-content > p").text().trim() - val trailer = document.selectFirst("a.trailerbutton")?.attr("href") - - val episodes = document.select(".eplister > ul > li").map { - val header = it.select(".epl-title").text() - val name = - Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header - val link = fixUrl(it.select("a").attr("href")) - Episode(link, name) - }.reversed() - - val recommendations = - document.select(".listupd > article[itemscope=itemscope]").mapNotNull { rec -> - val epTitle = rec.selectFirst(".tt")!!.ownText().trim() - val epPoster = rec.selectFirst("img")!!.attr("src") - val epType = getType(rec.selectFirst(".typez")?.text().toString()) - val epHref = fixUrl(rec.selectFirst("a.tip")!!.attr("href")) - - newAnimeSearchResponse(epTitle, epHref, epType) { - this.posterUrl = epPoster - addDubStatus(dubExist = false, subExist = true) - } - } - - return newAnimeLoadResponse(title, url, type) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - this.recommendations = recommendations - addTrailer(trailer) - } - - } - - data class Source( - @JsonProperty("play_url") val play_url: String, - @JsonProperty("format_id") val format_id: Int - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - val sources = document.select(".mobius > .mirror > option").mapNotNull { - fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) - } - - sources.apmap { - loadExtractor(it, data, subtitleCallback, callback) - } - - return true - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/OtakudesuProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/OtakudesuProvider.kt deleted file mode 100644 index 9ba17ca7..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/OtakudesuProvider.kt +++ /dev/null @@ -1,204 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import java.util.ArrayList - -class OtakudesuProvider : MainAPI() { - override var mainUrl = "https://otakudesu.watch" - override var name = "Otakudesu" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/ongoing-anime/page/" to "Anime Ongoing", - "$mainUrl/complete-anime/page/" to "Anime Completed" - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("div.venz > ul > li").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val title = this.selectFirst("h2.jdlflm")?.text()?.trim() ?: return null - val href = this.selectFirst("a")!!.attr("href") - val posterUrl = this.select("div.thumbz > img").attr("src").toString() - val epNum = this.selectFirst("div.epz")?.ownText()?.replace(Regex("[^0-9]"), "")?.trim() - ?.toIntOrNull() - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query&post_type=anime" - val document = app.get(link).document - - return document.select("ul.chivsrc > li").map { - val title = it.selectFirst("h2 > a")!!.ownText().trim() - val href = it.selectFirst("h2 > a")!!.attr("href") - val posterUrl = it.selectFirst("img")!!.attr("src").toString() - newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - } - } - } - - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst("div.infozingle > p:nth-child(1) > span")?.ownText() - ?.replace(":", "")?.trim().toString() - val poster = document.selectFirst("div.fotoanime > img")?.attr("src") - val tags = document.select("div.infozingle > p:nth-child(11) > span > a").map { it.text() } - val type = getType( - document.selectFirst("div.infozingle > p:nth-child(5) > span")?.ownText() - ?.replace(":", "")?.trim().toString() - ) - val year = Regex("\\d, ([0-9]*)").find( - document.select("div.infozingle > p:nth-child(9) > span").text() - )?.groupValues?.get(1)?.toIntOrNull() - val status = getStatus( - document.selectFirst("div.infozingle > p:nth-child(6) > span")!!.ownText() - .replace(":", "") - .trim() - ) - val description = document.select("div.sinopc > p").text() - - val episodes = document.select("div.episodelist")[1].select("ul > li").mapNotNull { - val name = Regex("(Episode\\s?[0-9]+)").find( - it.selectFirst("a")?.text().toString() - )?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text() - val link = fixUrl(it.selectFirst("a")!!.attr("href")) - Episode(link, name) - }.reversed() - - val recommendations = - document.select("div.isi-recommend-anime-series > div.isi-konten").map { - val recName = it.selectFirst("span.judul-anime > a")!!.text() - val recHref = it.selectFirst("a")!!.attr("href") - val recPosterUrl = it.selectFirst("a > img")?.attr("src").toString() - newAnimeSearchResponse(recName, recHref, TvType.Anime) { - this.posterUrl = recPosterUrl - } - } - - return newAnimeLoadResponse(title, url, type) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - this.recommendations = recommendations - } - } - - - data class ResponseSources( - @JsonProperty("id") val id: String, - @JsonProperty("i") val i: String, - @JsonProperty("q") val q: String, - ) - - data class ResponseData( - @JsonProperty("data") val data: String - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - val scriptData = document.select("script").last()?.data() - val token = scriptData?.substringAfter("{action:\"")?.substringBefore("\"}").toString() - - val nonce = app.post("$mainUrl/wp-admin/admin-ajax.php", data = mapOf("action" to token)) - .parsed().data - val action = scriptData?.substringAfter(",action:\"")?.substringBefore("\"}").toString() - - val mirrorData = document.select("div.mirrorstream > ul > li").mapNotNull { - base64Decode(it.select("a").attr("data-content")) - }.toString() - - tryParseJson>(mirrorData)?.apmap { res -> - val id = res.id - val i = res.i - val q = res.q - - var sources = Jsoup.parse( - base64Decode( - app.post( - "${mainUrl}/wp-admin/admin-ajax.php", data = mapOf( - "id" to id, - "i" to i, - "q" to q, - "nonce" to nonce, - "action" to action - ) - ).parsed().data - ) - ).select("iframe").attr("src") - - if (sources.startsWith("https://desustream.me")) { - if (!sources.contains("/arcg/") && !sources.contains("/odchan/") && !sources.contains( - "/desudrive/" - ) - ) { - sources = app.get(sources).document.select("iframe").attr("src") - } - if (sources.startsWith("https://yourupload.com")) { - sources = sources.replace("//", "//www.") - } - } - - loadExtractor(sources, data, subtitleCallback, callback) - - } - - return true - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt deleted file mode 100644 index ba2a5f9e..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt +++ /dev/null @@ -1,352 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import android.annotation.SuppressLint -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.network.DdosGuardKiller -import com.lagradost.cloudstream3.network.getHeaders -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.nodes.Document -import java.net.URI -import java.text.SimpleDateFormat -import java.util.* - -class TenshiProvider : MainAPI() { - companion object { - //var token: String? = null - //var cookie: Map = mapOf() - - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - } - - override var mainUrl = "https://tenshi.moe" - override var name = "Tenshi.moe" - override val hasQuickSearch = false - override val hasMainPage = true - override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie, TvType.OVA) - private var ddosGuardKiller = DdosGuardKiller(true) - - /*private fun loadToken(): Boolean { - return try { - val response = get(mainUrl) - cookie = response.cookies - val document = Jsoup.parse(response.text) - token = document.selectFirst("""meta[name="csrf-token"]""").attr("content") - token != null - } catch (e: Exception) { - false - } - }*/ - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val soup = app.get(mainUrl, interceptor = ddosGuardKiller).document - for (section in soup.select("#content > section")) { - try { - if (section.attr("id") == "toplist-tabs") { - for (top in section.select(".tab-content > [role=\"tabpanel\"]")) { - val title = "Top - " + top.attr("id").split("-")[1].replaceFirstChar { - if (it.isLowerCase()) it.titlecase( - Locale.UK - ) else it.toString() - } - val anime = top.select("li > a").map { - AnimeSearchResponse( - it.selectFirst(".thumb-title")!!.text(), - fixUrl(it.attr("href")), - this.name, - TvType.Anime, - it.selectFirst("img")!!.attr("src"), - null, - EnumSet.of(DubStatus.Subbed), - ) - } - items.add(HomePageList(title, anime)) - } - } else { - val title = section.selectFirst("h2")!!.text() - val anime = section.select("li > a").map { - AnimeSearchResponse( - it.selectFirst(".thumb-title")?.text() ?: "", - fixUrl(it.attr("href")), - this.name, - TvType.Anime, - it.selectFirst("img")!!.attr("src"), - null, - EnumSet.of(DubStatus.Subbed), - ) - } - items.add(HomePageList(title, anime)) - } - } catch (e: Exception) { - e.printStackTrace() - } - } - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - private fun getIsMovie(type: String, id: Boolean = false): Boolean { - if (!id) return type == "Movie" - - val movies = listOf("rrso24fa", "e4hqvtym", "bl5jdbqn", "u4vtznut", "37t6h2r4", "cq4azcrj") - val aniId = type.replace("$mainUrl/anime/", "") - return movies.contains(aniId) - } - - private fun parseSearchPage(soup: Document): List { - val items = soup.select("ul.thumb > li > a") - return items.map { - val href = fixUrl(it.attr("href")) - val img = fixUrl(it.selectFirst("img")!!.attr("src")) - val title = it.attr("title") - if (getIsMovie(href, true)) { - MovieSearchResponse( - title, href, this.name, TvType.Movie, img, null - ) - } else { - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - img, - null, - EnumSet.of(DubStatus.Subbed), - ) - } - } - } - - @SuppressLint("SimpleDateFormat") - private fun dateParser(dateString: String?): Date? { - if (dateString == null) return null - try { - val format = SimpleDateFormat("dd 'of' MMM',' yyyy") - val data = format.parse( - dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ") - .replace("rd ", " ") - ) ?: return null - return data - } catch (e: Exception) { - return null - } - } - -// data class TenshiSearchResponse( -// @JsonProperty("url") var url : String, -// @JsonProperty("title") var title : String, -// @JsonProperty("cover") var cover : String, -// @JsonProperty("genre") var genre : String, -// @JsonProperty("year") var year : Int, -// @JsonProperty("type") var type : String, -// @JsonProperty("eps") var eps : String, -// @JsonProperty("cen") var cen : String -// ) - -// override suspend fun quickSearch(query: String): ArrayList? { -// if (!autoLoadToken()) return quickSearch(query) -// val url = "$mainUrl/anime/search" -// val response = khttp.post( -// url, -// data=mapOf("q" to query), -// headers=mapOf("x-csrf-token" to token, "x-requested-with" to "XMLHttpRequest"), -// cookies = cookie -// -// ) -// -// val items = mapper.readValue>(response.text) -// -// if (items.isEmpty()) return ArrayList() -// -// val returnValue = ArrayList() -// for (i in items) { -// val href = fixUrl(i.url) -// val title = i.title -// val img = fixUrl(i.cover) -// val year = i.year -// -// returnValue.add( -// if (getIsMovie(i.type)) { -// MovieSearchResponse( -// title, href, getSlug(href), this.name, TvType.Movie, img, year -// ) -// } else { -// AnimeSearchResponse( -// title, href, getSlug(href), this.name, -// TvType.Anime, img, year, null, -// EnumSet.of(DubStatus.Subbed), -// null, null -// ) -// } -// ) -// } -// return returnValue -// } - - override suspend fun search(query: String): List { - val url = "$mainUrl/anime" - var document = app.get( - url, - params = mapOf("q" to query), - cookies = mapOf("loop-view" to "thumb"), - interceptor = ddosGuardKiller - ).document - - val returnValue = parseSearchPage(document).toMutableList() - - while (!document.select("""a.page-link[rel="next"]""").isEmpty()) { - val link = document.selectFirst("""a.page-link[rel="next"]""")?.attr("href") - if (!link.isNullOrBlank()) { - document = app.get( - link, - cookies = mapOf("loop-view" to "thumb"), - interceptor = ddosGuardKiller - ).document - returnValue.addAll(parseSearchPage(document)) - } else { - break - } - } - - return returnValue - } - - override suspend fun load(url: String): LoadResponse { - var document = app.get( - url, - cookies = mapOf("loop-view" to "thumb"), - interceptor = ddosGuardKiller - ).document - - val canonicalTitle = document.selectFirst("header.entry-header > h1.mb-3")!!.text().trim() - val episodeNodes = document.select("li[class*=\"episode\"] > a").toMutableList() - val totalEpisodePages = if (document.select(".pagination").size > 0) - document.select(".pagination .page-item a.page-link:not([rel])").last()!!.text() - .toIntOrNull() - else 1 - - if (totalEpisodePages != null && totalEpisodePages > 1) { - for (pageNum in 2..totalEpisodePages) { - document = app.get( - "$url?page=$pageNum", - cookies = mapOf("loop-view" to "thumb"), - interceptor = ddosGuardKiller - ).document - episodeNodes.addAll(document.select("li[class*=\"episode\"] > a")) - } - } - - val episodes = ArrayList(episodeNodes.map { - val title = it.selectFirst(".episode-title")?.text()?.trim() - newEpisode(it.attr("href")) { - this.name = if (title == "No Title") null else title - this.posterUrl = it.selectFirst("img")?.attr("src") - addDate(dateParser(it?.selectFirst(".episode-date")?.text()?.trim())) - this.description = it.attr("data-content").trim() - } - }) - - val similarAnime = document.select("ul.anime-loop > li > a").mapNotNull { element -> - val href = element.attr("href") ?: return@mapNotNull null - val title = - element.selectFirst("> .overlay > .thumb-title")?.text() ?: return@mapNotNull null - val img = element.selectFirst("> img")?.attr("src") - AnimeSearchResponse(title, href, this.name, TvType.Anime, img) - } - - val type = document.selectFirst("a[href*=\"$mainUrl/type/\"]")?.text()?.trim() - - return newAnimeLoadResponse(canonicalTitle, url, getType(type ?: "")) { - recommendations = similarAnime - posterUrl = document.selectFirst("img.cover-image")?.attr("src") - plot = document.selectFirst(".entry-description > .card-body")?.text()?.trim() - tags = - document.select("li.genre.meta-data > span.value") - .map { it?.text()?.trim().toString() } - - synonyms = - document.select("li.synonym.meta-data > div.info-box > span.value") - .map { it?.text()?.trim().toString() } - - engName = - document.selectFirst("span.value > span[title=\"English\"]")?.parent()?.text() - ?.trim() - japName = - document.selectFirst("span.value > span[title=\"Japanese\"]")?.parent()?.text() - ?.trim() - - val pattern = Regex("(\\d{4})") - val yearText = document.selectFirst("li.release-date .value")!!.text() - year = pattern.find(yearText)?.groupValues?.get(1)?.toIntOrNull() - - addEpisodes(DubStatus.Subbed, episodes) - - showStatus = when (document.selectFirst("li.status > .value")?.text()?.trim()) { - "Ongoing" -> ShowStatus.Ongoing - "Completed" -> ShowStatus.Completed - else -> null - } - } - } - - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val soup = app.get(data, interceptor = ddosGuardKiller).document - - data class Quality( - @JsonProperty("src") val src: String, - @JsonProperty("size") val size: Int - ) - - for (source in soup.select("""[aria-labelledby="mirror-dropdown"] > li > a.dropdown-item""")) { - val release = source.text().replace("/", "").trim() - val sourceHTML = app.get( - "https://tenshi.moe/embed?v=${source.attr("href").split("v=")[1].split("&")[0]}", - headers = mapOf("Referer" to data), interceptor = ddosGuardKiller - ).text - - val match = Regex("""sources: (\[(?:.|\s)+?type: ['"]video/.*?['"](?:.|\s)+?])""").find( - sourceHTML - ) - if (match != null) { - val qualities = parseJson>( - match.destructured.component1() - .replace("'", "\"") - .replace(Regex("""(\w+): """), "\"\$1\": ") - .replace(Regex("""\s+"""), "") - .replace(",}", "}") - .replace(",]", "]") - ) - qualities.forEach { - callback.invoke( - ExtractorLink( - this.name, - "${this.name} $release", - fixUrl(it.src), - this.mainUrl, - getQualityFromName("${it.size}"), - headers = getHeaders(emptyMap(), - ddosGuardKiller.savedCookiesMap[URI(this.mainUrl).host] - ?: emptyMap() - ).toMap() - ) - ) - } - } - } - - return true - } -} diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TocanimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TocanimeProvider.kt deleted file mode 100644 index b7f4bca6..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TocanimeProvider.kt +++ /dev/null @@ -1,178 +0,0 @@ -package com.lagradost.cloudstream3.animeproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import org.jsoup.nodes.Element -import java.util.* - -class TocanimeProvider : MainAPI() { - override var mainUrl = "https://tocanime.co" - override var name = "Tocanime" - override val hasMainPage = true - override var lang = "vi" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return when { - t.contains("OVA") || t.contains("Special") -> TvType.OVA - t.contains("Movie") -> TvType.AnimeMovie - else -> TvType.Anime - } - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Đã hoàn thành" -> ShowStatus.Completed - "Chưa hoàn thành" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val document = app.get(mainUrl).document - - val homePageList = ArrayList() - - document.select("div#playlists > div").forEach { block -> - val header = block.selectFirst("h2")?.text()?.trim() ?: "" - val items = block.select("div.col-lg-3.col-md-4.col-6").map { - it.toSearchResult() - } - if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) - } - - return HomePageResponse(homePageList) - } - - private fun Element.toSearchResult(): AnimeSearchResponse { - val title = this.selectFirst("h3 a")?.text()?.trim() ?: "" - val href = fixUrl(this.selectFirst("h3 a")!!.attr("href")) - val posterUrl = fixUrlNull(this.selectFirst("div.card-item-img")?.attr("data-original")) - val epNum = this.selectFirst("div.card-item-badget.rtl")?.text()?.let { eps -> - val num = eps.filter { it.isDigit() }.toIntOrNull() - if(eps.contains("Preview")) { - num?.minus(1) - } else { - num - } - } - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val document = app.get("$mainUrl/content/search?t=kw&q=$query").document - - return document.select("div.col-lg-3.col-md-4.col-6").map { - it.toSearchResult() - } - - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("h1.title")?.text() ?: return null - val type = - if (document.select("div.me-list.scroller a").size == 1) TvType.AnimeMovie else TvType.Anime - val episodes = document.select("div.me-list.scroller a").mapNotNull { - Episode(fixUrl(it.attr("href")), it.text()) - }.reversed() - val trailer = - document.selectFirst("div#trailer script")?.data()?.substringAfter("