[skip ci] Update dependencies (#1343)

* Bump harmless dependencies

Note that bumping the android plugin will make compilation show lots of
"hey bro use namespace instead of AndroidManifest.xml" warnings

* Remove duktape dependency

Zero extensions are using it, so its safe to remove. For executing js in
extensions we can use quickjs instead of ducktape.

* Upgrade gradle to 7.6

* Update kotlin

* Update OkHttp

* Update JSoup

Jesus Christ this was boring asf

* Update KtLint

* [skip ci] refactor on some build.gradle.kts files

* Expose coroutines to all extension by default
This commit is contained in:
Claudemirovsky 2023-02-28 10:12:46 -03:00 committed by GitHub
parent a5c2427e70
commit 62f45e094d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
387 changed files with 3380 additions and 2909 deletions

View File

@ -8,3 +8,5 @@ indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true

View File

@ -1,23 +1,8 @@
buildscript {
repositories {
mavenCentral()
google()
maven(url = "https://plugins.gradle.org/m2/")
}
dependencies {
classpath(libs.gradle.agp)
classpath(libs.gradle.kotlin)
classpath(libs.gradle.serialization)
classpath(libs.gradle.kotlinter)
}
}
allprojects {
repositories {
mavenCentral()
google()
maven(url = "https://jitpack.io")
}
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.kotlinter) apply false
}
tasks.register<Delete>("clean") {

View File

@ -79,12 +79,16 @@ android {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
freeCompilerArgs += "-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi"
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
}
}
repositories {
mavenCentral()
kotlinter {
experimentalRules = true
disabledRules = [
"experimental:argument-list-wrapping", // Doesn't play well with Android Studio
"experimental:comment-wrapping",
]
}
}
dependencies {
@ -92,5 +96,15 @@ dependencies {
compileOnly(libs.bundles.common)
}
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'org.jetbrains.kotlin' && details.requested.name == 'kotlin-stdlib-jdk8' && details.requested.version == '1.7.0') {
details.useVersion '1.7.21'
details.because 'Fix problems with dev.datlag JsUnpacker'
}
}
}
preBuild.dependsOn(lintKotlin)
lintKotlin.dependsOn(formatKotlin)

View File

@ -1,14 +1,10 @@
[versions]
kotlin_version = "1.6.21"
coroutines_version = "1.6.0"
serialization_version = "1.3.2"
agp_version = "7.4.1"
coroutines_version = "1.6.4"
kotlin_version = "1.7.21"
serialization_version = "1.4.0"
[libraries]
gradle-agp = { module = "com.android.tools.build:gradle", version = "7.2.1" }
gradle-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin_version" }
gradle-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin_version" }
gradle-kotlinter = { module = "org.jmailen.gradle:kotlinter-gradle", version = "3.6.0" }
aniyomi-lib = { module = "com.github.jmir1:extensions-lib", version = "a2f1874" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin_version" }
@ -19,14 +15,19 @@ coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", ve
coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines_version" }
injekt-core = { module = "com.github.inorichi.injekt:injekt-core", version = "65b0440" }
jsoup = { module = "org.jsoup:jsoup", version = "1.13.1" }
duktape = { module = "com.squareup.duktape:duktape-android", version = "1.4.0" }
quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version = "4.9.1" }
rxandroid = { module = "io.reactivex:rxandroid", version = "1.2.1" }
rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" }
jsoup = { module = "org.jsoup:jsoup", version = "1.15.3" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.11" }
quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" }
[bundles]
common = ["kotlin-stdlib", "injekt-core", "rxjava", "kotlin-protobuf", "kotlin-json", "jsoup", "okhttp", "aniyomi-lib", "duktape", "quickjs"]
coroutines = ["coroutines-core", "coroutines-android"]
common = ["kotlin-stdlib", "injekt-core", "rxjava", "kotlin-protobuf", "kotlin-json", "jsoup", "okhttp", "aniyomi-lib", "quickjs", "coroutines-core", "coroutines-android"]
reactivex = ["rxandroid"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp_version" }
android-library = { id = "com.android.library", version.ref = "agp_version" }
kotlinter = { id = "org.jmailen.kotlinter", version = "3.13.0" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin_version" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin_version" }

Binary file not shown.

View File

@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

18
gradlew vendored
View File

@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -80,10 +80,10 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
@ -143,12 +143,16 @@ fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@ -205,6 +209,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

15
gradlew.bat vendored
View File

@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

@ -12,10 +12,6 @@ android {
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(libs.kotlin.stdlib)
compileOnly(libs.okhttp)

View File

@ -12,10 +12,6 @@ android {
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(libs.kotlin.stdlib)
compileOnly(libs.okhttp)

View File

@ -19,7 +19,7 @@ class DoodExtractor(private val client: OkHttpClient) {
val newUrl = if(redirect) response.request.url.toString() else url
val doodTld = newUrl.substringAfter("https://dood.").substringBefore("/")
val content = response.body!!.string()
val content = response.body.string()
if (!content.contains("'/pass_md5/")) return null
val md5 = content.substringAfter("'/pass_md5/").substringBefore("',")
val token = md5.substringAfterLast("/")
@ -30,7 +30,7 @@ class DoodExtractor(private val client: OkHttpClient) {
"https://dood.$doodTld/pass_md5/$md5",
Headers.headersOf("referer", newUrl)
)
).execute().body!!.string()
).execute().body.string()
val videoUrl = "$videoUrlStart$randomString?token=$token&expiry=$expiry"
Video(newUrl, newQuality, videoUrl, headers = doodHeaders(doodTld))
} catch (e: Exception) {

View File

@ -13,10 +13,6 @@ android {
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(libs.kotlin.stdlib)
compileOnly(libs.kotlin.json)

View File

@ -16,7 +16,7 @@ class FembedExtractor(private val client: OkHttpClient) {
url.replace("/v/", "/api/source/")
}
val body = runCatching {
client.newCall(POST(videoApi)).execute().body?.string().orEmpty()
client.newCall(POST(videoApi)).execute().body.string()
}.getOrNull() ?: return emptyList<Video>()
val jsonResponse = try{ Json { ignoreUnknownKeys = true }.decodeFromString<FembedResponse>(body) } catch (e: Exception) { FembedResponse(false, emptyList()) }

View File

@ -12,10 +12,6 @@ android {
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(libs.kotlin.stdlib)
compileOnly(libs.okhttp)

View File

@ -12,10 +12,6 @@ android {
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(libs.kotlin.stdlib)
compileOnly(libs.kotlin.json)

View File

@ -52,12 +52,12 @@ class StreamSBExtractor(private val client: OkHttpClient) {
val master = fixUrl(url, common)
val json = Json.decodeFromString<JsonObject>(
client.newCall(GET(master, newHeaders))
.execute().body!!.string()
.execute().body.string()
)
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
val masterPlaylist = client.newCall(GET(masterUrl, newHeaders))
.execute()
.body!!.string()
.body.string()
val separator = "#EXT-X-STREAM-INF"
masterPlaylist.substringAfter(separator).split(separator).map {
val resolution = it.substringAfter("RESOLUTION=")
@ -81,9 +81,9 @@ class StreamSBExtractor(private val client: OkHttpClient) {
fun videosFromDecryptedUrl(realUrl: String, headers: Headers, prefix: String = "", suffix: String = ""): List<Video> {
return try {
val json = Json.decodeFromString<JsonObject>(client.newCall(GET(realUrl, headers)).execute().body!!.string())
val json = Json.decodeFromString<JsonObject>(client.newCall(GET(realUrl, headers)).execute().body.string())
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
val masterPlaylist = client.newCall(GET(masterUrl, headers)).execute().body!!.string()
val masterPlaylist = client.newCall(GET(masterUrl, headers)).execute().body.string()
val separator = "#EXT-X-STREAM-INF"
masterPlaylist.substringAfter(separator).split(separator).map {
val resolution = it.substringAfter("RESOLUTION=")

View File

@ -12,10 +12,6 @@ android {
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(libs.kotlin.stdlib)
compileOnly(libs.okhttp)

View File

@ -12,10 +12,6 @@ android {
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(libs.kotlin.stdlib)
compileOnly(libs.okhttp)

View File

@ -13,10 +13,6 @@ android {
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(libs.kotlin.stdlib)
compileOnly(libs.okhttp)

View File

@ -1,3 +1,19 @@
dependencyResolutionManagement {
repositories {
mavenCentral()
google()
maven(url = "https://jitpack.io")
}
}
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
include(":core")
File(rootDir, "lib").eachDir {

View File

@ -33,8 +33,8 @@ class AOAPIInterceptor(client: OkHttpClient) : Interceptor {
"https://auth.animeonsen.xyz/oauth/token",
headers,
body,
)
).execute().body!!.string()
),
).execute().body.string()
val tokenObject = Json.decodeFromString<JsonObject>(tokenResponse)

View File

@ -70,7 +70,7 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
GET("$apiUrl/content/index?start=${(page - 1) * 20}&limit=20")
override fun popularAnimeParse(response: Response): AnimesPage {
val responseJson = json.decodeFromString<AnimeListResponse>(response.body!!.string())
val responseJson = json.decodeFromString<AnimeListResponse>(response.body.string())
val animes = responseJson.content.map {
it.toSAnime()
}
@ -82,7 +82,7 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
override fun episodeListParse(response: Response): List<SEpisode> {
val contentId = response.request.url.toString().substringBeforeLast("/episodes")
.substringAfterLast("/")
val responseJson = json.decodeFromString<JsonObject>(response.body!!.string())
val responseJson = json.decodeFromString<JsonObject>(response.body.string())
return responseJson.keys.map { epNum ->
SEpisode.create().apply {
url = "$contentId/video/$epNum"
@ -100,12 +100,14 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val videoData = json.decodeFromString<VideoData>(response.body!!.string())
val videoData = json.decodeFromString<VideoData>(response.body.string())
val videoUrl = videoData.uri.stream
val subtitleLangs = videoData.metadata.subtitles
val headers = Headers.headersOf(
"referer", baseUrl,
"user-agent", AO_USER_AGENT,
"referer",
baseUrl,
"user-agent",
AO_USER_AGENT,
)
val video = try {
val subtitles = videoData.uri.subtitles.keys.toList().sortSubs().map {
@ -126,7 +128,7 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
// =============================== Search ===============================
override fun searchAnimeParse(response: Response): AnimesPage {
val searchResult = json.decodeFromString<SearchResponse>(response.body!!.string()).result
val searchResult = json.decodeFromString<SearchResponse>(response.body.string()).result
val results = searchResult.map {
it.toSAnime()
}
@ -137,7 +139,7 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
// =========================== Anime Details ============================
override fun animeDetailsParse(response: Response): SAnime {
val details = json.decodeFromString<AnimeDetails>(response.body!!.string())
val details = json.decodeFromString<AnimeDetails>(response.body.string())
val anime = SAnime.create().apply {
url = details.content_id
title = details.content_title ?: details.content_title_en!!
@ -223,10 +225,10 @@ private const val PREF_SUB_TITLE = "Preferred sub language"
private val PREF_SUB_ENTRIES = arrayOf(
"العربية", "Deutsch", "English", "Español (Spain)",
"Español (Latin)", "Français", "Italiano",
"Português (Brasil)", "Русский"
"Português (Brasil)", "Русский",
)
private val PREF_SUB_VALUES = arrayOf(
"ar-ME", "de-DE", "en-US", "es-ES",
"es-LA", "fr-FR", "it-IT",
"pt-BR", "ru-RU"
"pt-BR", "ru-RU",
)

View File

@ -7,7 +7,7 @@ import kotlinx.serialization.json.JsonObject
@Serializable
data class AnimeListResponse(
val content: List<AnimeListItem>,
val cursor: AnimeListCursor
val cursor: AnimeListCursor,
)
@Serializable
@ -19,7 +19,7 @@ data class AnimeListItem(
@Serializable
data class AnimeListCursor(
val next: JsonArray
val next: JsonArray,
)
@Serializable

View File

@ -11,7 +11,6 @@ ext {
}
dependencies {
compileOnly libs.bundles.coroutines
implementation(project(':lib-okru-extractor'))
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
}

View File

@ -72,9 +72,9 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun popularAnimeFromElement(element: Element): SAnime {
return SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a.series").attr("href").toHttpUrl().encodedPath)
thumbnail_url = element.selectFirst("img").attr("src").substringBefore("?resize")
title = element.selectFirst("a.series:not(:has(img))").text()
setUrlWithoutDomain(element.selectFirst("a.series")!!.attr("href").toHttpUrl().encodedPath)
thumbnail_url = element.selectFirst("img")!!.attr("src").substringBefore("?resize")
title = element.selectFirst("a.series:not(:has(img))")!!.text()
}
}
@ -120,9 +120,9 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun searchAnimeFromElement(element: Element): SAnime {
return SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a").attr("href").toHttpUrl().encodedPath)
thumbnail_url = element.selectFirst("img").attr("src").substringBefore("?resize")
title = element.selectFirst("div.tt").text()
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href").toHttpUrl().encodedPath)
thumbnail_url = element.selectFirst("img")!!.attr("src").substringBefore("?resize")
title = element.selectFirst("div.tt")!!.text()
}
}
@ -132,8 +132,8 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun animeDetailsParse(document: Document): SAnime {
return SAnime.create().apply {
title = document.selectFirst("h1.entry-title").text()
thumbnail_url = document.selectFirst("div.thumb > img").attr("src").substringBefore("?resize")
title = document.selectFirst("h1.entry-title")!!.text()
thumbnail_url = document.selectFirst("div.thumb > img")!!.attr("src").substringBefore("?resize")
status = SAnime.COMPLETED
description = document.select("div[itemprop=description] p")?.let {
it.joinToString("\n\n") { t -> t.text() } +
@ -151,7 +151,7 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val document = response.asJsoup()
return document.select("div.eplister > ul > li").map { episodeElement ->
val numberText = episodeElement.selectFirst("div.epl-num").text()
val numberText = episodeElement.selectFirst("div.epl-num")!!.text()
val numberString = numberText.substringBefore(" ")
val episodeNumber = if (numberText.contains("part 2", true)) {
numberString.toFloatOrNull()?.plus(0.5F) ?: 0F
@ -163,7 +163,7 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
episode_number = episodeNumber
name = numberText
date_upload = parseDate(episodeElement.selectFirst("div.epl-date")?.text() ?: "")
setUrlWithoutDomain(episodeElement.selectFirst("a").attr("href").toHttpUrl().encodedPath)
setUrlWithoutDomain(episodeElement.selectFirst("a")!!.attr("href").toHttpUrl().encodedPath)
}
}
}
@ -182,7 +182,7 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
document.select("select.mirror > option[value~=.]").parallelMap { source ->
runCatching {
var decoded = Jsoup.parse(
String(Base64.decode(source.attr("value"), Base64.DEFAULT))
String(Base64.decode(source.attr("value"), Base64.DEFAULT)),
).select("iframe[src~=.]").attr("src")
if (!decoded.startsWith("http")) decoded = "https:$decoded"
val prefix = "${source.text()} - "
@ -246,7 +246,7 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
else -> null
}
}.getOrNull()
}.filterNotNull().flatten()
}.filterNotNull().flatten(),
)
return videoList.sort()
@ -268,7 +268,7 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
compareBy(
{ it.quality.contains(quality) },
{ it.quality.contains(language, true) },
)
),
).reversed()
}

View File

@ -7,10 +7,10 @@ object AnimeXinFilters {
open class QueryPartFilter(
displayName: String,
val vals: Array<Pair<String, String>>
val vals: Array<Pair<String, String>>,
) : AnimeFilter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray()
vals.map { it.first }.toTypedArray(),
) {
fun toQueryPart() = vals[state].second
}
@ -33,28 +33,33 @@ object AnimeXinFilters {
): String {
return (this.getFirst<R>() as CheckBoxFilterList).state
.mapNotNull { checkbox ->
if (checkbox.state)
if (checkbox.state) {
options.find { it.first == checkbox.name }!!.second
else null
} else {
null
}
}.joinToString("&$name[]=").let {
if (it.isBlank()) ""
else "$name[]=$it"
if (it.isBlank()) {
""
} else {
"$name[]=$it"
}
}
}
class GenresFilter : CheckBoxFilterList(
"Genres",
AnimeXinFiltersData.genres.map { CheckBoxVal(it.first, false) }
AnimeXinFiltersData.genres.map { CheckBoxVal(it.first, false) },
)
class SeasonsFilter : CheckBoxFilterList(
"Seasons",
AnimeXinFiltersData.seasons.map { CheckBoxVal(it.first, false) }
AnimeXinFiltersData.seasons.map { CheckBoxVal(it.first, false) },
)
class StudiosFilter : CheckBoxFilterList(
"Studios",
AnimeXinFiltersData.studios.map { CheckBoxVal(it.first, false) }
AnimeXinFiltersData.studios.map { CheckBoxVal(it.first, false) },
)
class StatusFilter : QueryPartFilter("Status", AnimeXinFiltersData.status)
@ -80,7 +85,7 @@ object AnimeXinFilters {
val status: String = "",
val type: String = "",
val sub: String = "",
val order: String = ""
val order: String = "",
)
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
@ -119,7 +124,7 @@ object AnimeXinFilters {
Pair("School", "school"),
Pair("Sci-fi", "sci-fi"),
Pair("Supernatural", "supernatural"),
Pair("War", "war")
Pair("War", "war"),
)
val seasons = arrayOf(
@ -133,7 +138,7 @@ object AnimeXinFilters {
Pair("Season 7", "season-7"),
Pair("Season 8", "season-8"),
Pair("season1", "season1"),
Pair("Winter 2023", "winter-2023")
Pair("Winter 2023", "winter-2023"),
)
val studios = arrayOf(
@ -178,7 +183,7 @@ object AnimeXinFilters {
Pair("Wonder Cat Animation", "wonder-cat-animation"),
Pair("Xing Yi Kai Chen", "xing-yi-kai-chen"),
Pair("Xuan Yuan", "xuan-yuan"),
Pair("Year Young Culture", "year-young-culture")
Pair("Year Young Culture", "year-young-culture"),
)
val status = arrayOf(
@ -198,7 +203,7 @@ object AnimeXinFilters {
Pair("Special", "special"),
Pair("BD", "bd"),
Pair("ONA", "ona"),
Pair("Music", "music")
Pair("Music", "music"),
)
val sub = arrayOf(

View File

@ -13,27 +13,27 @@ import uy.kohesive.injekt.injectLazy
@Serializable
data class DailyQuality(
val qualities: Auto,
val subtitles: Subtitle? = null
val subtitles: Subtitle? = null,
) {
@Serializable
data class Auto(
val auto: List<Item>
val auto: List<Item>,
) {
@Serializable
data class Item(
val type: String,
val url: String
val url: String,
)
}
@Serializable
data class Subtitle(
val data: Map<String, SubtitleObject>
val data: Map<String, SubtitleObject>,
) {
@Serializable
data class SubtitleObject(
val label: String,
val urls: List<String>
val urls: List<String>,
)
}
}
@ -44,7 +44,7 @@ class DailymotionExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, prefix: String): List<Video> {
val videoList = mutableListOf<Video>()
val htmlString = client.newCall(GET(url)).execute().body!!.string()
val htmlString = client.newCall(GET(url)).execute().body.string()
val internalData = htmlString.substringAfter("\"dmInternalData\":").substringBefore("</script>")
val ts = internalData.substringAfter("\"ts\":").substringBefore(",")
@ -53,7 +53,7 @@ class DailymotionExtractor(private val client: OkHttpClient) {
val jsonUrl = "https://www.dailymotion.com/player/metadata/video/${url.toHttpUrl().encodedPath}?locale=en-US&dmV1st=$v1st&dmTs=$ts&is_native_app=0"
val parsed = json.decodeFromString<DailyQuality>(
client.newCall(GET(jsonUrl))
.execute().body!!.string()
.execute().body.string(),
)
val subtitleList = mutableListOf<Track>()
@ -63,16 +63,16 @@ class DailymotionExtractor(private val client: OkHttpClient) {
parsed.subtitles.data.map { k ->
Track(
k.value.urls.first(),
k.value.label
k.value.label,
)
}
},
)
} catch (a: Exception) { }
}
val masterUrl = parsed.qualities.auto.first().url
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body!!.string()
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body.string()
val separator = "#EXT-X-STREAM-INF"
masterPlaylist.substringAfter(separator).split(separator).map {

View File

@ -11,7 +11,7 @@ class DoodExtractor(private val client: OkHttpClient) {
fun videoFromUrl(
url: String,
quality: String? = null,
redirect: Boolean = true
redirect: Boolean = true,
): Video? {
val newQuality = quality ?: "Doodstream" + if (redirect) " mirror" else ""
@ -20,7 +20,7 @@ class DoodExtractor(private val client: OkHttpClient) {
val newUrl = if (redirect) response.request.url.toString() else url
val doodTld = newUrl.substringAfter("https://dood.").substringBefore("/")
val content = response.body!!.string()
val content = response.body.string()
val subtitleList = mutableListOf<Track>()
val subtitleRegex = """src:'//(srt[^']*?)',\s*label:'([^']*?)'""".toRegex()
@ -29,9 +29,9 @@ class DoodExtractor(private val client: OkHttpClient) {
subtitleRegex.findAll(content).map {
Track(
"https://" + it.groupValues[1],
it.groupValues[2]
it.groupValues[2],
)
}
},
)
} catch (a: Exception) { }
@ -43,9 +43,9 @@ class DoodExtractor(private val client: OkHttpClient) {
val videoUrlStart = client.newCall(
GET(
"https://dood.$doodTld/pass_md5/$md5",
Headers.headersOf("referer", newUrl)
)
).execute().body!!.string()
Headers.headersOf("referer", newUrl),
),
).execute().body.string()
val videoUrl = "$videoUrlStart$randomString?token=$token&expiry=$expiry"
try {
Video(newUrl, newQuality, videoUrl, headers = doodHeaders(doodTld), subtitleTracks = subtitleList)
@ -60,7 +60,7 @@ class DoodExtractor(private val client: OkHttpClient) {
fun videosFromUrl(
url: String,
quality: String? = null,
redirect: Boolean = true
redirect: Boolean = true,
): List<Video> {
val video = videoFromUrl(url, quality, redirect)
return video?.let { listOf(it) } ?: emptyList<Video>()

View File

@ -15,12 +15,12 @@ import okhttp3.OkHttpClient
data class FembedResponse(
val success: Boolean,
val data: List<FembedVideo> = emptyList(),
val captions: List<Caption> = emptyList()
val captions: List<Caption> = emptyList(),
) {
@Serializable
data class FembedVideo(
val file: String,
val label: String
val label: String,
)
@Serializable
@ -45,11 +45,11 @@ class FembedExtractor(private val client: OkHttpClient) {
url.replace("/v/", "/api/source/")
}
val body = runCatching {
client.newCall(POST(videoApi)).execute().body?.string().orEmpty()
client.newCall(POST(videoApi)).execute().body.string()
}.getOrNull() ?: return emptyList()
val userId = client.newCall(GET(url)).execute().asJsoup()
.selectFirst("script:containsData(USER_ID)")
.selectFirst("script:containsData(USER_ID)")!!
.data()
.substringAfter("USER_ID")
.substringAfter("'")
@ -64,16 +64,19 @@ class FembedExtractor(private val client: OkHttpClient) {
jsonResponse.captions.map {
Track(
"https://${url.toHttpUrl().host}/asset/userdata/$userId/caption/${it.hash}/${it.id}.${it.extension}",
it.language
it.language,
)
}
},
)
} catch (a: Exception) { }
jsonResponse.data.map {
val quality = ("Fembed:${it.label}").let {
if (prefix.isNotBlank()) "$prefix $it"
else it
if (prefix.isNotBlank()) {
"$prefix $it"
} else {
it
}
}
try {
Video(it.file, quality, it.file, subtitleTracks = subtitleList)

View File

@ -22,14 +22,18 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, name: String): List<Video> {
val headers = Headers.headersOf(
"Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Host", "gdriveplayer.to",
"Referer", "https://animexin.vip/",
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0"
"Accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Host",
"gdriveplayer.to",
"Referer",
"https://animexin.vip/",
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0",
)
val body = client.newCall(GET(url.replace(".me", ".to"), headers = headers)).execute()
.body!!.string()
.body.string()
val subtitleUrl = Jsoup.parse(body).selectFirst("div:contains(\\.srt)")
val subtitleList = mutableListOf<Track>()
@ -38,8 +42,8 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
subtitleList.add(
Track(
"https://gdriveplayer.to/?subtitle=" + subtitleUrl.text(),
"Subtitles"
)
"Subtitles",
),
)
} catch (a: Exception) { }
}
@ -89,9 +93,8 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
hashAlgorithm: String = "MD5",
keyLength: Int = 32,
ivLength: Int = 16,
iterations: Int = 1
iterations: Int = 1,
): List<ByteArray>? {
val md = MessageDigest.getInstance(hashAlgorithm)
val digestLength = md.getDigestLength()
val targetKeySize = keyLength + ivLength
@ -103,12 +106,13 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
md.reset()
while (generatedLength < targetKeySize) {
if (generatedLength > 0)
if (generatedLength > 0) {
md.update(
generatedData,
generatedLength - digestLength,
digestLength
digestLength,
)
}
md.update(password)
md.update(salt, 0, 8)
@ -123,7 +127,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
}
val result = listOf(
generatedData.copyOfRange(0, keyLength),
generatedData.copyOfRange(keyLength, targetKeySize)
generatedData.copyOfRange(keyLength, targetKeySize),
)
return result
} catch (e: DigestException) {

View File

@ -50,7 +50,7 @@ class StreamSBExtractor(private val client: OkHttpClient) {
val master = fixUrl(url, common)
val json = Json.decodeFromString<JsonObject>(
client.newCall(GET(master, newHeaders))
.execute().body!!.string()
.execute().body.string(),
)
val subtitleList = mutableListOf<Track>()
val subsList = json["stream_data"]!!.jsonObject["subs"]
@ -62,7 +62,7 @@ class StreamSBExtractor(private val client: OkHttpClient) {
it.jsonObject["file"]!!.jsonPrimitive.content,
it.jsonObject["label"]!!.jsonPrimitive.content,
)
}
},
)
} catch (a: Exception) { }
}
@ -70,7 +70,7 @@ class StreamSBExtractor(private val client: OkHttpClient) {
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
val masterPlaylist = client.newCall(GET(masterUrl, newHeaders))
.execute()
.body!!.string()
.body.string()
val separator = "#EXT-X-STREAM-INF"
masterPlaylist.substringAfter(separator).split(separator).map {
val resolution = it.substringAfter("RESOLUTION=")
@ -78,11 +78,17 @@ class StreamSBExtractor(private val client: OkHttpClient) {
.substringAfter("x")
.substringBefore(",") + "p"
val quality = ("StreamSB:" + resolution).let {
if (prefix.isNotBlank()) "$prefix $it"
else it
if (prefix.isNotBlank()) {
"$prefix $it"
} else {
it
}
}.let {
if (suffix.isNotBlank()) "$it $suffix"
else it
if (suffix.isNotBlank()) {
"$it $suffix"
} else {
it
}
}
val videoUrl = it.substringAfter("\n").substringBefore("\n")
try {
@ -98,9 +104,9 @@ class StreamSBExtractor(private val client: OkHttpClient) {
fun videosFromDecryptedUrl(realUrl: String, headers: Headers, prefix: String = "", suffix: String = ""): List<Video> {
return try {
val json = Json.decodeFromString<JsonObject>(client.newCall(GET(realUrl, headers)).execute().body!!.string())
val json = Json.decodeFromString<JsonObject>(client.newCall(GET(realUrl, headers)).execute().body.string())
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
val masterPlaylist = client.newCall(GET(masterUrl, headers)).execute().body!!.string()
val masterPlaylist = client.newCall(GET(masterUrl, headers)).execute().body.string()
val separator = "#EXT-X-STREAM-INF"
masterPlaylist.substringAfter(separator).split(separator).map {
val resolution = it.substringAfter("RESOLUTION=")
@ -108,11 +114,17 @@ class StreamSBExtractor(private val client: OkHttpClient) {
.substringAfter("x")
.substringBefore(",") + "p"
val quality = ("StreamSB:$resolution").let {
if (prefix.isNotBlank()) "$prefix $it"
else it
if (prefix.isNotBlank()) {
"$prefix $it"
} else {
it
}
}.let {
if (suffix.isNotBlank()) "$it $suffix"
else it
if (suffix.isNotBlank()) {
"$it $suffix"
} else {
it
}
}
val videoUrl = it.substringAfter("\n").substringBefore("\n")
Video(videoUrl, quality, videoUrl, headers = headers)

View File

@ -41,7 +41,9 @@ class VidstreamingExtractor(private val client: OkHttpClient) {
val encryptAjaxParams = cryptoHandler(
document.select("script[data-value]")
.attr("data-value"),
iv, secretKey, false
iv,
secretKey,
false,
).substringAfter("&")
val httpUrl = serverUrl.toHttpUrl()
@ -55,10 +57,11 @@ class VidstreamingExtractor(private val client: OkHttpClient) {
GET(
"${host}encrypt-ajax.php?id=$encryptedId&$encryptAjaxParams&alias=$id",
Headers.headersOf(
"X-Requested-With", "XMLHttpRequest"
)
)
).execute().body!!.string()
"X-Requested-With",
"XMLHttpRequest",
),
),
).execute().body.string()
val data = json.decodeFromString<JsonObject>(jsonResponse)["data"]!!.jsonPrimitive.content
val decryptedData = cryptoHandler(data, iv, decryptionKey, false)
val videoList = mutableListOf<Video>()
@ -66,7 +69,7 @@ class VidstreamingExtractor(private val client: OkHttpClient) {
val array = json.decodeFromString<JsonObject>(decryptedData)["source"]!!.jsonArray
if (array.size == 1 && array[0].jsonObject["type"]!!.jsonPrimitive.content == "hls") {
val fileURL = array[0].jsonObject["file"].toString().trim('"')
val masterPlaylist = client.newCall(GET(fileURL)).execute().body!!.string()
val masterPlaylist = client.newCall(GET(fileURL)).execute().body.string()
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:").forEach {
val quality = it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",").substringBefore("\n") + "p"
@ -76,20 +79,25 @@ class VidstreamingExtractor(private val client: OkHttpClient) {
}
videoList.add(Video(videoUrl, prefix + quality + qualitySuffix, videoUrl))
}
} else array.forEach {
} else {
array.forEach {
val label = it.jsonObject["label"].toString().lowercase(Locale.ROOT)
.trim('"').replace(" ", "")
val fileURL = it.jsonObject["file"].toString().trim('"')
val videoHeaders = Headers.headersOf("Referer", serverUrl)
if (label == "auto") autoList.add(
if (label == "auto") {
autoList.add(
Video(
fileURL,
label + qualitySuffix,
fileURL,
headers = videoHeaders
headers = videoHeaders,
),
)
)
else videoList.add(Video(fileURL, label + qualitySuffix, fileURL, headers = videoHeaders))
} else {
videoList.add(Video(fileURL, label + qualitySuffix, fileURL, headers = videoHeaders))
}
}
}
return videoList.sortedByDescending {
it.quality.substringBefore(qualitySuffix).substringBefore("p").toIntOrNull() ?: -1
@ -103,7 +111,7 @@ class VidstreamingExtractor(private val client: OkHttpClient) {
string: String,
iv: ByteArray,
secretKeyString: ByteArray,
encrypt: Boolean = true
encrypt: Boolean = true,
): String {
val ivParameterSpec = IvParameterSpec(iv)
val secretKey = SecretKeySpec(secretKeyString, "AES")

View File

@ -34,7 +34,7 @@ class YouTubeExtractor(private val client: OkHttpClient) {
val videoId = url.substringAfter("/embed/")
val document = client.newCall(
GET(url.replace("/embed/", "/watch?v="))
GET(url.replace("/embed/", "/watch?v=")),
).execute().asJsoup()
for (element in document.select("script")) {
@ -78,14 +78,14 @@ class YouTubeExtractor(private val client: OkHttpClient) {
"X-YouTube-Client-Version", "17.31.35",
"Origin", "https://www.youtube.com",
"User-Agent", "com.google.android.youtube/17.31.35 (Linux; U; Android 11) gzip",
"content-type", "application/json"
"content-type", "application/json",
)
val postResponse = client.newCall(
POST(playerUrl, headers = headers, body = body)
POST(playerUrl, headers = headers, body = body),
).execute()
val responseObject = json.decodeFromString<JsonObject>(postResponse.body!!.string())
val responseObject = json.decodeFromString<JsonObject>(postResponse.body.string())
val videoList = mutableListOf<Video>()
val formats = responseObject["streamingData"]!!
@ -103,8 +103,8 @@ class YouTubeExtractor(private val client: OkHttpClient) {
Track(
format.jsonObject["url"]!!.jsonPrimitive.content,
format.jsonObject["audioQuality"]!!.jsonPrimitive.content +
" (${formatBits(format.jsonObject["averageBitrate"]!!.jsonPrimitive.long)}ps)"
)
" (${formatBits(format.jsonObject["averageBitrate"]!!.jsonPrimitive.long)}ps)",
),
)
} catch (a: Exception) { }
}
@ -124,8 +124,8 @@ class YouTubeExtractor(private val client: OkHttpClient) {
Track(
// TODO: Would replacing srv3 with vtt work for every video?
captionJson["baseUrl"]!!.jsonPrimitive.content.replace("srv3", "vtt"),
captionJson["name"]!!.jsonObject["runs"]!!.jsonArray[0].jsonObject["text"]!!.jsonPrimitive.content
)
captionJson["name"]!!.jsonObject["runs"]!!.jsonArray[0].jsonObject["text"]!!.jsonPrimitive.content,
),
)
} catch (a: Exception) { }
}
@ -143,16 +143,16 @@ class YouTubeExtractor(private val client: OkHttpClient) {
" (${mimeType.substringAfter("codecs=\"").substringBefore("\"")})",
format.jsonObject["url"]!!.jsonPrimitive.content,
audioTracks = audioTracks,
subtitleTracks = subtitleTracks
subtitleTracks = subtitleTracks,
)
} catch (a: Exception) {
Video(
format.jsonObject["url"]!!.jsonPrimitive.content,
prefix + format.jsonObject["qualityLabel"]!!.jsonPrimitive.content +
" (${mimeType.substringAfter("codecs=\"").substringBefore("\"")})",
format.jsonObject["url"]!!.jsonPrimitive.content
format.jsonObject["url"]!!.jsonPrimitive.content,
)
}
},
)
}

View File

@ -650,7 +650,7 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun popularAnimeParse(response: Response): AnimesPage {
val parsed = json.decodeFromString<AnilistResponse>(response.body!!.string())
val parsed = json.decodeFromString<AnilistResponse>(response.body.string())
val animeList = parsed.data.Page.media.map { ani ->
SAnime.create().apply {
@ -659,7 +659,7 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
author = ani.studios.nodes.firstOrNull()?.name ?: ""
genre = ani.genres.joinToString(", ")
description = Jsoup.parse(
ani.description.replace("<br>", "br2n")
ani.description.replace("<br>", "br2n"),
).text().replace("br2n", "\n")
status = parseStatus(ani.status)
setUrlWithoutDomain(ani.id.toString())
@ -734,7 +734,7 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun episodeListParse(response: Response): List<SEpisode> {
val parsed = json.decodeFromString<EpisodeResponse>(response.body!!.string())
val parsed = json.decodeFromString<EpisodeResponse>(response.body.string())
val episodeList = mutableListOf<SEpisode>()
if (parsed.episodes != null) {
@ -745,7 +745,7 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
episode_number = it.episodeNumber
setUrlWithoutDomain("/server/source?episode_id=${it.sourceEpisodeId}&source_media_id=${it.sourceMediaId}&source_id=${it.sourceId}")
}
}
},
)
}
@ -759,19 +759,19 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
val subtitleList = mutableListOf<Track>()
val audioList = mutableListOf<Track>()
val sources = json.decodeFromString<SourcesResponse>(
client.newCall(GET(baseUrl + episode.url)).execute().body!!.string()
client.newCall(GET(baseUrl + episode.url)).execute().body.string(),
)
subtitleList.addAll(
sources.subtitles.map {
Track(it.file, "${it.language} - ${it.lang}")
}
},
)
sources.sources.forEach { source ->
if (source.type == "dash") {
// Parsing dash with Jsoup :YEP:
val document = client.newCall(
GET(source.file)
GET(source.file),
).execute().asJsoup()
document.select("Representation[mimetype~=audio]").forEach { audioSrc ->
audioList.add(Track(audioSrc.text(), formatBits(audioSrc.attr("bandwidth").toLongOrNull() ?: 0L) ?: "audio"))
@ -783,8 +783,8 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
"${videoSrc.attr("height")}p - ${formatBits(videoSrc.attr("bandwidth").toLongOrNull() ?: 0L)}",
videoSrc.text(),
audioTracks = audioList,
subtitleTracks = subtitleList
)
subtitleTracks = subtitleList,
),
)
}
}
@ -826,7 +826,7 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
compareBy(
{ it.quality.contains(quality) },
{ it.quality.substringBefore("p ").toIntOrNull() ?: 0 },
)
),
).reversed()
}

View File

@ -4,20 +4,20 @@ import kotlinx.serialization.Serializable
@Serializable
data class AnilistResponse(
val data: DataObject
val data: DataObject,
) {
@Serializable
data class DataObject(
val Page: PageObject
val Page: PageObject,
) {
@Serializable
data class PageObject(
val pageInfo: PageInfoObject,
val media: List<AnimeMedia>
val media: List<AnimeMedia>,
) {
@Serializable
data class PageInfoObject(
val hasNextPage: Boolean
val hasNextPage: Boolean,
)
@Serializable
@ -28,25 +28,25 @@ data class AnilistResponse(
val studios: StudioNode,
val genres: List<String>,
val description: String,
val status: String
val status: String,
) {
@Serializable
data class TitleObject(
val romaji: String
val romaji: String,
)
@Serializable
data class ImageObject(
val extraLarge: String
val extraLarge: String,
)
@Serializable
data class StudioNode(
val nodes: List<Node>
val nodes: List<Node>,
) {
@Serializable
data class Node(
val name: String
val name: String,
)
}
}
@ -56,7 +56,7 @@ data class AnilistResponse(
@Serializable
data class EpisodeResponse(
val episodes: List<EpisodeObject>? = null
val episodes: List<EpisodeObject>? = null,
) {
@Serializable
data class EpisodeObject(
@ -70,18 +70,18 @@ data class EpisodeResponse(
@Serializable
data class SourcesResponse(
val sources: List<SourceObject>,
val subtitles: List<SubtitleObject>
val subtitles: List<SubtitleObject>,
) {
@Serializable
data class SourceObject(
val file: String,
val type: String
val type: String,
)
@Serializable
data class SubtitleObject(
val file: String,
val lang: String,
val language: String
val language: String,
)
}

View File

@ -10,8 +10,4 @@ ext {
libVersion = '13'
}
dependencies {
compileOnly libs.bundles.coroutines
}
apply from: "$rootDir/common.gradle"

View File

@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable
@Serializable
data class ItemsResponse(
val TotalRecordCount: Int,
val Items: List<Item>
val Items: List<Item>,
) {
@Serializable
data class Item(
@ -24,7 +24,7 @@ data class ItemsResponse(
) {
@Serializable
data class ImageObject(
val Primary: String? = null
val Primary: String? = null,
)
}
}
@ -36,7 +36,7 @@ data class SessionResponse(
) {
@Serializable
data class MediaObject(
val MediaStreams: List<MediaStream>
val MediaStreams: List<MediaStream>,
) {
@Serializable
data class MediaStream(
@ -47,7 +47,7 @@ data class SessionResponse(
val Language: String? = null,
val DisplayTitle: String? = null,
val Height: Int? = null,
val Width: Int? = null
val Width: Int? = null,
)
}
}

View File

@ -17,22 +17,28 @@ object JFConstants {
const val HOSTURL_DEFAULT = "http://127.0.0.1:8096"
fun getPrefApiKey(preferences: SharedPreferences): String? = preferences.getString(
APIKEY_KEY, null
APIKEY_KEY,
null,
)
fun getPrefUserId(preferences: SharedPreferences): String? = preferences.getString(
USERID_KEY, null
USERID_KEY,
null,
)
fun getPrefHostUrl(preferences: SharedPreferences): String = preferences.getString(
HOSTURL_KEY, HOSTURL_DEFAULT
HOSTURL_KEY,
HOSTURL_DEFAULT,
)!!
fun getPrefUsername(preferences: SharedPreferences): String = preferences.getString(
USERNAME_KEY, ""
USERNAME_KEY,
"",
)!!
fun getPrefPassword(preferences: SharedPreferences): String = preferences.getString(
PASSWORD_KEY, ""
PASSWORD_KEY,
"",
)!!
fun getPrefParentId(preferences: SharedPreferences): String = preferences.getString(
MEDIALIB_KEY, ""
MEDIALIB_KEY,
"",
)!!
const val PREF_AUDIO_KEY = "preferred_audioLang"
@ -54,7 +60,7 @@ object JFConstants {
Quality(1920, 1080, 39808000, 192000, "1080p - 40 Mbps"),
Quality(1920, 1080, 59808000, 192000, "1080p - 60 Mbps"),
Quality(3840, 2160, 80000000, 192000, "4K - 80 Mbps"),
Quality(3840, 2160, 120000000, 192000, "4K - 120 Mbps")
Quality(3840, 2160, 120000000, 192000, "4K - 120 Mbps"),
)
data class Quality(
@ -91,7 +97,7 @@ object JFConstants {
"tem", "ter", "tet", "tgk", "tgl", "tha", "tig", "tir", "tiv", "tkl", "tlh", "tli", "tmh", "tog", "ton", "tpi", "tsi",
"tsn", "tso", "tuk", "tum", "tup", "tur", "tvl", "twi", "tyv", "udm", "uga", "uig", "ukr", "umb", "urd", "uzb", "vai",
"ven", "vie", "vol", "vot", "wal", "war", "was", "wen", "wln", "wol", "xal", "xho", "yao", "yap", "yid", "yor", "zap",
"zbl", "zen", "zgh", "zha", "zho", "zul", "zun", "zza"
"zbl", "zen", "zgh", "zha", "zho", "zul", "zun", "zza",
)
val PREF_ENTRIES = arrayOf(
@ -163,6 +169,6 @@ object JFConstants {
"ꕙꔤ", "Tshivenḓa", "Tiếng Việt", "Volapük", "vađđa ceeli", "Wolaitta; Wolaytta", "Winaray; Samareño; Lineyte-Samarnon; Binisayâ nga Winaray; Binisayâ nga Samar-Leyte; “Binisayâ nga Waray”",
"wá:šiw ʔítlu", "Serbsce / Serbski", "Walon", "Wolof", "Хальмг келн / Xaľmg keln", "isiXhosa", "Yao", "Yapese",
"ייִדיש; יידיש; אידיש Yidiš", "èdè Yorùbá", "Diidxazá/Dizhsa", "Blissymbols; Blissymbolics; Bliss", "Tuḍḍungiyya",
"ⵜⴰⵎⴰⵣⵉⵖⵜ ⵜⴰⵏⴰⵡⴰⵢⵜ", "Vahcuengh / 話僮", "中文 Zhōngwén; 汉语; 漢語 Hànyǔ", "isiZulu", "Shiwi'ma", "kirmanckî; dimilkî; kirdkî; zazakî"
"ⵜⴰⵎⴰⵣⵉⵖⵜ ⵜⴰⵏⴰⵡⴰⵢⵜ", "Vahcuengh / 話僮", "中文 Zhōngwén; 汉语; 漢語 Hànyǔ", "isiZulu", "Shiwi'ma", "kirmanckî; dimilkî; kirdkî; zazakî",
)
}

View File

@ -203,12 +203,12 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
url.addQueryParameter("SearchTerm", query)
val response = client.newCall(
GET(url.build().toString(), headers = headers)
GET(url.build().toString(), headers = headers),
).execute()
val items = json.decodeFromString<ItemsResponse>(response.body!!.string())
val items = json.decodeFromString<ItemsResponse>(response.body.string())
items.Items.forEach {
animeList.addAll(
getAnimeFromId(it.Id)
getAnimeFromId(it.Id),
)
}
@ -228,7 +228,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
url.addQueryParameter("ParentId", id)
val response = client.newCall(
GET(url.build().toString())
GET(url.build().toString()),
).execute()
return animeParse(response, 0).animes
}
@ -253,7 +253,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun animeDetailsParse(response: Response): SAnime {
val info = json.decodeFromString<ItemsResponse.Item>(response.body!!.string())
val info = json.decodeFromString<ItemsResponse.Item>(response.body.string())
val anime = SAnime.create()
@ -265,7 +265,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
info.Overview
.replace("<br>\n", "br2n")
.replace("<br>", "br2n")
.replace("\n", "br2n")
.replace("\n", "br2n"),
).text().replace("br2n", "\n")
} else {
""
@ -289,14 +289,14 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
override fun episodeListParse(response: Response): List<SEpisode> {
val episodeList = if (response.request.url.toString().startsWith("$baseUrl/Users/")) {
val parsed = json.decodeFromString<ItemsResponse.Item>(response.body!!.string())
val parsed = json.decodeFromString<ItemsResponse.Item>(response.body.string())
val episode = SEpisode.create()
episode.episode_number = 1.0F
episode.name = "Movie ${parsed.Name}"
episode.setUrlWithoutDomain(response.request.url.toString().substringAfter(baseUrl))
listOf(episode)
} else {
val parsed = json.decodeFromString<ItemsResponse>(response.body!!.string())
val parsed = json.decodeFromString<ItemsResponse>(response.body.string())
parsed.Items.map { ep ->
@ -326,12 +326,12 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> {
val videoList = mutableListOf<Video>()
val id = json.decodeFromString<ItemsResponse.Item>(response.body!!.string()).Id
val id = json.decodeFromString<ItemsResponse.Item>(response.body.string()).Id
val sessionResponse = client.newCall(
GET("$baseUrl/Items/$id/PlaybackInfo?userId=$userId&api_key=$apiKey")
GET("$baseUrl/Items/$id/PlaybackInfo?userId=$userId&api_key=$apiKey"),
).execute()
val parsed = json.decodeFromString<SessionResponse>(sessionResponse.body!!.string())
val parsed = json.decodeFromString<SessionResponse>(sessionResponse.body.string())
val subtitleList = mutableListOf<Track>()
@ -401,10 +401,12 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
url.addQueryParameter("VideoCodec", "h264")
url.addQueryParameter("VideoCodec", "h264")
url.addQueryParameter(
"VideoBitrate", quality.videoBitrate.toString()
"VideoBitrate",
quality.videoBitrate.toString(),
)
url.addQueryParameter(
"AudioBitrate", quality.audioBitrate.toString()
"AudioBitrate",
quality.audioBitrate.toString(),
)
url.addQueryParameter("PlaySessionId", parsed.PlaySessionId)
url.addQueryParameter("TranscodingMaxAudioChannels", "6")
@ -434,7 +436,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================= Utilities ==============================
private fun animeParse(response: Response, page: Int): AnimesPage {
val items = json.decodeFromString<ItemsResponse>(response.body!!.string())
val items = json.decodeFromString<ItemsResponse>(response.body.string())
val animesList = mutableListOf<SAnime>()
items.Items.forEach { item ->
@ -446,8 +448,8 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
LinkData(
path = "/Shows/${item.SeriesId}/Episodes?SeasonId=${item.Id}&api_key=$apiKey",
seriesId = item.SeriesId!!,
seasonId = item.Id
).toJsonString()
seasonId = item.Id,
).toJsonString(),
)
// Virtual if show doesn't have any sub-folders, i.e. no seasons
if (item.LocationType == "Virtual") {
@ -471,8 +473,8 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
LinkData(
"/Users/$userId/Items/${item.Id}?api_key=$apiKey",
item.Id,
item.Id
).toJsonString()
item.Id,
).toJsonString(),
)
animesList.add(anime)
}
@ -489,7 +491,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
url.addQueryParameter("EnableImageTypes", "Primary")
val response = client.newCall(
GET(url.build().toString(), headers = headers)
GET(url.build().toString(), headers = headers),
).execute()
animesList.addAll(animeParse(response, page).animes)
}
@ -510,18 +512,36 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
val mediaLibPref = medialibPreference(screen)
screen.addPreference(
screen.editTextPreference(
JFConstants.HOSTURL_KEY, JFConstants.HOSTURL_TITLE, JFConstants.HOSTURL_DEFAULT, baseUrl, false, "", mediaLibPref
)
JFConstants.HOSTURL_KEY,
JFConstants.HOSTURL_TITLE,
JFConstants.HOSTURL_DEFAULT,
baseUrl,
false,
"",
mediaLibPref,
),
)
screen.addPreference(
screen.editTextPreference(
JFConstants.USERNAME_KEY, JFConstants.USERNAME_TITLE, "", username, false, "", mediaLibPref
)
JFConstants.USERNAME_KEY,
JFConstants.USERNAME_TITLE,
"",
username,
false,
"",
mediaLibPref,
),
)
screen.addPreference(
screen.editTextPreference(
JFConstants.PASSWORD_KEY, JFConstants.PASSWORD_TITLE, "", password, true, "••••••••", mediaLibPref
)
JFConstants.PASSWORD_KEY,
JFConstants.PASSWORD_TITLE,
"",
password,
true,
"••••••••",
mediaLibPref,
),
)
screen.addPreference(mediaLibPref)
val subLangPref = ListPreference(screen.context).apply {
@ -587,9 +607,9 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
Thread {
try {
val mediaLibsResponse = client.newCall(
GET("$baseUrl/Users/$userId/Items?api_key=$apiKey")
GET("$baseUrl/Users/$userId/Items?api_key=$apiKey"),
).execute()
val mediaJson = mediaLibsResponse.body?.let { json.decodeFromString<ItemsResponse>(it.string()) }?.Items
val mediaJson = mediaLibsResponse.body.let { json.decodeFromString<ItemsResponse>(it.string()) }?.Items
val entriesArray = mutableListOf<String>()
val entriesValueArray = mutableListOf<String>()

View File

@ -49,7 +49,7 @@ class JellyfinAuthenticator(
""".trimIndent()
.toRequestBody("application/json".toMediaType())
val request = POST("$baseUrl/Users/authenticatebyname", headers = authHeader, body = body)
val response = client.newCall(request).execute().body?.string()
val response = client.newCall(request).execute().body.string()
return response?.let { Json.decodeFromString<JsonObject>(it) }
}
@ -68,11 +68,13 @@ class JellyfinAuthenticator(
}
private fun getPrefDeviceId(): String? = preferences.getString(
DEVICEID_KEY, null
DEVICEID_KEY,
null,
)
private fun setPrefDeviceId(value: String) = preferences.edit().putString(
DEVICEID_KEY, value
DEVICEID_KEY,
value,
).apply()
}

View File

@ -10,8 +10,4 @@ ext {
libVersion = '13'
}
dependencies {
compileOnly libs.bundles.coroutines
}
apply from: "$rootDir/common.gradle"

View File

@ -22,7 +22,7 @@ import java.net.Proxy
class AccessTokenInterceptor(
private val crUrl: String,
private val json: Json,
private val preferences: SharedPreferences
private val preferences: SharedPreferences,
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
@ -42,7 +42,7 @@ class AccessTokenInterceptor(
val refreshedToken = refreshAccessToken()
// Retry the request
return chain.proceed(
newRequestWithAccessToken(chain.request(), refreshedToken)
newRequestWithAccessToken(chain.request(), refreshedToken),
)
}
}
@ -68,8 +68,8 @@ class AccessTokenInterceptor(
.proxy(
Proxy(
Proxy.Type.SOCKS,
InetSocketAddress("cr-unblocker.us.to", 1080)
)
InetSocketAddress("cr-unblocker.us.to", 1080),
),
)
.build()
@ -78,29 +78,31 @@ class AccessTokenInterceptor(
override fun getPasswordAuthentication(): PasswordAuthentication {
return PasswordAuthentication("crunblocker", "crunblocker".toCharArray())
}
}
},
)
// Thanks Stormzy
val refreshTokenResp = client.newCall(GET("https://raw.githubusercontent.com/Samfun75/File-host/main/aniyomi/refreshToken.txt")).execute()
val refreshToken = refreshTokenResp.body!!.string().replace("[\n\r]".toRegex(), "")
val refreshToken = refreshTokenResp.body.string().replace("[\n\r]".toRegex(), "")
val headers = Headers.headersOf(
"Content-Type", "application/x-www-form-urlencoded",
"Authorization", "Basic a3ZvcGlzdXZ6Yy0teG96Y21kMXk6R21JSTExenVPVnRnTjdlSWZrSlpibzVuLTRHTlZ0cU8="
"Content-Type",
"application/x-www-form-urlencoded",
"Authorization",
"Basic a3ZvcGlzdXZ6Yy0teG96Y21kMXk6R21JSTExenVPVnRnTjdlSWZrSlpibzVuLTRHTlZ0cU8=",
)
val postBody = "grant_type=refresh_token&refresh_token=$refreshToken&scope=offline_access".toRequestBody("application/x-www-form-urlencoded".toMediaType())
val response = proxy.newCall(POST("$crUrl/auth/v1/token", headers, postBody)).execute()
val parsedJson = json.decodeFromString<AccessToken>(response.body!!.string())
val parsedJson = json.decodeFromString<AccessToken>(response.body.string())
val policy = proxy.newCall(newRequestWithAccessToken(GET("$crUrl/index/v2"), parsedJson)).execute()
val policyJson = json.decodeFromString<Policy>(policy.body!!.string())
val policyJson = json.decodeFromString<Policy>(policy.body.string())
val allTokens = AccessToken(
parsedJson.access_token,
parsedJson.token_type,
policyJson.cms.policy,
policyJson.cms.signature,
policyJson.cms.key_pair_id,
policyJson.cms.bucket
policyJson.cms.bucket,
)
preferences.edit().putString(TOKEN_PREF_KEY, allTokens.toJsonString()).apply()
return allTokens

View File

@ -11,38 +11,38 @@ data class AccessToken(
val policy: String? = null,
val signature: String? = null,
val key_pair_id: String? = null,
val bucket: String? = null
val bucket: String? = null,
)
@Serializable
data class Policy(
val cms: Tokens
val cms: Tokens,
) {
@Serializable
data class Tokens(
val policy: String,
val signature: String,
val key_pair_id: String,
val bucket: String
val bucket: String,
)
}
@Serializable
data class LinkData(
val id: String,
val media_type: String
val media_type: String,
)
@Serializable
data class Images(
val poster_tall: List<ArrayList<Image>>? = null
val poster_tall: List<ArrayList<Image>>? = null,
) {
@Serializable
data class Image(
val width: Int,
val height: Int,
val type: String,
val source: String
val source: String,
)
}
@ -58,7 +58,7 @@ data class Anime(
val series_metadata: Metadata? = null,
@SerialName("movie_listing_metadata")
val movie_metadata: MovieMeta? = null,
val content_provider: String? = null
val content_provider: String? = null,
) {
@Serializable
data class Metadata(
@ -69,7 +69,7 @@ data class Anime(
val is_dubbed: Boolean,
val is_subbed: Boolean,
@SerialName("tenant_categories")
val genres: ArrayList<String>? = null
val genres: ArrayList<String>? = null,
)
@Serializable
@ -79,14 +79,14 @@ data class Anime(
val maturity_ratings: ArrayList<String>,
val subtitle_locales: ArrayList<String>,
@SerialName("tenant_categories")
val genres: ArrayList<String>? = null
val genres: ArrayList<String>? = null,
)
}
@Serializable
data class AnimeResult(
val total: Int,
val data: ArrayList<Anime>
val data: ArrayList<Anime>,
)
@Serializable
@ -97,28 +97,28 @@ data class SearchAnimeResult(
data class SearchAnime(
val type: String,
val count: Int,
val items: ArrayList<Anime>
val items: ArrayList<Anime>,
)
}
@Serializable
data class SeasonResult(
val total: Int,
val data: ArrayList<Season>
val data: ArrayList<Season>,
) {
@Serializable
data class Season(
val id: String,
val season_number: Int? = null,
@SerialName("premium_available_date")
val date: String? = null
val date: String? = null,
)
}
@Serializable
data class EpisodeResult(
val total: Int,
val data: ArrayList<Episode>
val data: ArrayList<Episode>,
) {
@Serializable
data class Episode(
@ -130,13 +130,13 @@ data class EpisodeResult(
@SerialName("episode_air_date")
val airDate: String? = null,
val versions: ArrayList<Version>? = null,
val streams_link: String
val streams_link: String,
) {
@Serializable
data class Version(
val audio_locale: String,
@SerialName("media_guid")
val mediaId: String
val mediaId: String,
)
}
}
@ -146,19 +146,19 @@ data class TempEpisode(
var name: String,
var episode_number: Float,
var date_upload: Long,
var scanlator: String?
var scanlator: String?,
)
@Serializable
data class EpisodeData(
val ids: List<Pair<String, String>>
val ids: List<Pair<String, String>>,
)
@Serializable
data class VideoStreams(
val streams: Stream,
val subtitles: JsonObject,
val audio_locale: String
val audio_locale: String,
) {
@Serializable
data class Stream(
@ -169,13 +169,13 @@ data class VideoStreams(
@Serializable
data class HlsLinks(
val hardsub_locale: String,
val url: String
val url: String,
)
@Serializable
data class Subtitle(
val locale: String,
val url: String
val url: String,
)
fun <T> List<T>.thirdLast(): T? {

View File

@ -74,7 +74,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun popularAnimeParse(response: Response): AnimesPage {
val parsed = json.decodeFromString<AnimeResult>(response.body!!.string())
val parsed = json.decodeFromString<AnimeResult>(response.body.string())
val animeList = parsed.data.parallelMap { ani ->
runCatching {
ani.toSAnime()
@ -111,7 +111,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun searchAnimeParse(response: Response): AnimesPage {
val bod = response.body!!.string()
val bod = response.body.string()
val total: Int
val animeList = (
if (response.request.url.encodedPath.contains("search")) {
@ -142,10 +142,13 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
val mediaId = json.decodeFromString<LinkData>(anime.url)
val resp = client.newCall(
if (mediaId.media_type == "series") GET("$crUrl/content/v2/cms/series/${mediaId.id}?locale=en-US")
else GET("$crUrl/content/v2/cms/movie_listings/${mediaId.id}?locale=en-US")
if (mediaId.media_type == "series") {
GET("$crUrl/content/v2/cms/series/${mediaId.id}?locale=en-US")
} else {
GET("$crUrl/content/v2/cms/movie_listings/${mediaId.id}?locale=en-US")
},
).execute()
val info = json.decodeFromString<AnimeResult>(resp.body!!.string())
val info = json.decodeFromString<AnimeResult>(resp.body.string())
return Observable.just(
anime.apply {
author = info.data.first().content_provider
@ -153,7 +156,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
if (genre.isNullOrBlank()) {
genre = info.data.first().genres?.joinToString { gen -> gen.replaceFirstChar { it.uppercase() } }
}
}
},
)
}
@ -171,7 +174,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun episodeListParse(response: Response): List<SEpisode> {
val seasons = json.decodeFromString<SeasonResult>(response.body!!.string())
val seasons = json.decodeFromString<SeasonResult>(response.body.string())
val series = response.request.url.encodedPath.contains("series/")
// Why all this? well crunchy sends same season twice with different quality eg. One Piece
// which causes the number of episodes to be higher that what it actually is.
@ -184,7 +187,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
client.newCall(GET("$crUrl/content/v2/cms/seasons/${seasonData.id}/episodes"))
.execute()
val episodes =
json.decodeFromString<EpisodeResult>(episodeResp.body!!.string())
json.decodeFromString<EpisodeResult>(episodeResp.body.string())
episodes.data.sortedBy { it.episode_number }.parallelMap { ep ->
TempEpisode(
epData = EpisodeData(
@ -193,9 +196,9 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
Pair(
ep.streams_link.substringAfter("videos/")
.substringBefore("/streams"),
ep.audio_locale
)
)
ep.audio_locale,
),
),
),
name = if (ep.episode_number > 0 && ep.episode.isNumeric()) {
"Season ${seasonData.season_number} Ep ${df.format(ep.episode_number)}: " + ep.title
@ -206,7 +209,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
date_upload = ep.airDate?.let { parseDate(it) } ?: 0L,
scanlator = ep.versions?.sortedBy { it.audio_locale }
?.joinToString { it.audio_locale.substringBefore("-") }
?: ep.audio_locale.substringBefore("-")
?: ep.audio_locale.substringBefore("-"),
)
}
}.getOrNull()
@ -257,7 +260,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
private fun extractVideo(media: Pair<String, String>, policyJson: AccessToken): List<Video> {
val (mediaId, aud) = media
val response = client.newCall(GET("$crUrl/cms/v2${policyJson.bucket}/videos/$mediaId/streams?Policy=${policyJson.policy}&Signature=${policyJson.signature}&Key-Pair-Id=${policyJson.key_pair_id}")).execute()
val streams = json.decodeFromString<VideoStreams>(response.body!!.string())
val streams = json.decodeFromString<VideoStreams>(response.body.string())
var subsList = emptyList<Track>()
val subLocale = preferences.getString("preferred_sub", "en-US")!!.getLocale()
@ -268,8 +271,8 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
}.sortedWith(
compareBy(
{ it.lang },
{ it.lang.contains(subLocale) }
)
{ it.lang.contains(subLocale) },
),
)
} catch (_: Error) {}
@ -277,7 +280,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
return streams.streams.adaptive_hls.entries.parallelMap { (_, value) ->
val stream = json.decodeFromString<HlsLinks>(value.jsonObject.toString())
runCatching {
val playlist = client.newCall(GET(stream.url)).execute().body!!.string()
val playlist = client.newCall(GET(stream.url)).execute().body.string()
playlist.substringAfter("#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:").map {
val hardsub = stream.hardsub_locale.let { hs ->
@ -294,7 +297,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
videoUrl,
quality,
videoUrl,
subtitleTracks = if (hardsub.isNotBlank()) emptyList() else subsList
subtitleTracks = if (hardsub.isNotBlank()) emptyList() else subsList,
)
} catch (_: Error) {
Video(videoUrl, quality, videoUrl)
@ -341,7 +344,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
Pair("ro-RO", "Romanian"),
Pair("sv-SE", "Swedish"),
Pair("zh-CN", "Chinese (PRC)"),
Pair("zh-HK", "Chinese (Hong Kong)")
Pair("zh-HK", "Chinese (Hong Kong)"),
)
private fun LinkData.toJsonString(): String {
@ -372,12 +375,20 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
if (this@toSAnime.series_metadata?.subtitle_locales?.any() == true ||
this@toSAnime.movie_metadata?.subtitle_locales?.any() == true ||
this@toSAnime.series_metadata?.is_subbed == true
) " Sub" else ""
) {
" Sub"
} else {
""
}
) +
(
if (this@toSAnime.series_metadata?.audio_locales?.any() == true ||
this@toSAnime.movie_metadata?.is_dubbed == true
) " Dub" else ""
) {
" Dub"
} else {
""
}
)
desc += "\nMaturity Ratings: " +
(
@ -410,8 +421,8 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
{ it.quality.contains(quality) },
{ it.quality.contains("Aud: ${dubLocale.getLocale()}") },
{ it.quality.contains("HardSub") == shouldContainHard },
{ it.quality.contains(subLocale) }
)
{ it.quality.contains(subLocale) },
),
).reversed()
}

View File

@ -7,10 +7,10 @@ object YomirollFilters {
open class QueryPartFilter(
displayName: String,
val vals: Array<Pair<String, String>>
val vals: Array<Pair<String, String>>,
) : AnimeFilter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray()
vals.map { it.first }.toTypedArray(),
) {
fun toQueryPart() = vals[state].second
}
@ -28,13 +28,15 @@ object YomirollFilters {
}
private inline fun <reified R> AnimeFilterList.parseCheckbox(
options: Array<Pair<String, String>>
options: Array<Pair<String, String>>,
): String {
return (this.getFirst<R>() as CheckBoxFilterList).state
.mapNotNull { checkbox ->
if (checkbox.state)
if (checkbox.state) {
options.find { it.first == checkbox.name }!!.second
else null
} else {
null
}
}.joinToString("")
}
@ -45,7 +47,7 @@ object YomirollFilters {
class LanguageFilter : CheckBoxFilterList(
"Language",
CrunchyFiltersData.language.map { CheckBoxVal(it.first, false) }
CrunchyFiltersData.language.map { CheckBoxVal(it.first, false) },
)
val filterList = AnimeFilterList(
@ -56,7 +58,7 @@ object YomirollFilters {
CategoryFilter(),
SortFilter(),
MediaFilter(),
LanguageFilter()
LanguageFilter(),
)
data class FilterSearchParams(
@ -64,7 +66,7 @@ object YomirollFilters {
val category: String = "",
val sort: String = "",
val language: String = "",
val media: String = ""
val media: String = "",
)
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
@ -83,7 +85,7 @@ object YomirollFilters {
val searchType = arrayOf(
Pair("Top Results", "top_results"),
Pair("Series", "series"),
Pair("Movies", "movie_listing")
Pair("Movies", "movie_listing"),
)
val categories = arrayOf(
@ -176,24 +178,24 @@ object YomirollFilters {
Pair("Thriller, Drama", "&categories=thriller,drama"),
Pair("Thriller, Fantasy", "&categories=thriller,fantasy"),
Pair("Thriller, Sci-Fi", "&categories=thriller,sci-fi"),
Pair("Thriller, Supernatural", "&categories=thriller,supernatural")
Pair("Thriller, Supernatural", "&categories=thriller,supernatural"),
)
val sortType = arrayOf(
Pair("Popular", "popularity"),
Pair("New", "newly_added"),
Pair("Alphabetical", "alphabetical")
Pair("Alphabetical", "alphabetical"),
)
val language = arrayOf(
Pair("Sub", "&is_subbed=true"),
Pair("Dub", "&is_dubbed=true")
Pair("Dub", "&is_dubbed=true"),
)
val mediaType = arrayOf(
Pair("All", ""),
Pair("Series", "&type=series"),
Pair("Movies", "&type=movie_listing")
Pair("Movies", "&type=movie_listing"),
)
}
}

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable
@Serializable
data class CategoryResponse(
val data: List<CategoryData>
val data: List<CategoryData>,
) {
@Serializable
data class CategoryData(
@ -12,13 +12,13 @@ data class CategoryResponse(
val domainType: Int,
val id: String,
val name: String,
val sort: String
val sort: String,
)
}
@Serializable
data class AnimeInfoResponse(
val data: InfoData
val data: InfoData,
) {
@Serializable
data class InfoData(
@ -33,45 +33,45 @@ data class AnimeInfoResponse(
@Serializable
data class EpisodeInfo(
val id: Int,
val seriesNo: Float
val seriesNo: Float,
)
@Serializable
data class IdInfo(
val name: String
val name: String,
)
}
}
@Serializable
data class SearchResponse(
val data: InfoData
val data: InfoData,
) {
@Serializable
data class InfoData(
val results: List<CategoryResponse.CategoryData>
val results: List<CategoryResponse.CategoryData>,
)
}
@Serializable
data class EpisodeResponse(
val data: EpisodeData
val data: EpisodeData,
) {
@Serializable
data class EpisodeData(
val qualities: List<Quality>,
val subtitles: List<Subtitle>
val subtitles: List<Subtitle>,
) {
@Serializable
data class Quality(
val quality: Int,
val url: String
val url: String,
)
@Serializable
data class Subtitle(
val language: String,
val url: String
val url: String,
)
}
}
@ -80,5 +80,5 @@ data class EpisodeResponse(
data class LinkData(
val category: String,
val id: String,
val episodeId: String? = null
val episodeId: String? = null,
)

View File

@ -55,7 +55,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun popularAnimeParse(response: Response): AnimesPage {
val parsed = json.decodeFromString<CategoryResponse>(response.body!!.string())
val parsed = json.decodeFromString<CategoryResponse>(response.body.string())
if (parsed.data.isEmpty()) {
return AnimesPage(emptyList(), false)
}
@ -67,8 +67,8 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
setUrlWithoutDomain(
LinkData(
ani.domainType.toString(),
ani.id
).toJsonString()
ani.id,
).toJsonString(),
)
}
}
@ -104,7 +104,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
return if (url.startsWith("/api/category")) {
popularAnimeParse(response)
} else {
val parsed = json.decodeFromString<SearchResponse>(response.body!!.string())
val parsed = json.decodeFromString<SearchResponse>(response.body.string())
if (parsed.data.results.isEmpty()) {
return AnimesPage(emptyList(), false)
}
@ -116,8 +116,8 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
setUrlWithoutDomain(
LinkData(
ani.domainType.toString(),
ani.id
).toJsonString()
ani.id,
).toJsonString(),
)
}
}
@ -144,7 +144,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
Pair("Recent TV Series", "/category?area=&category=1&order=up&params=TV,SETI,MINISERIES,VARIETY,TALK,DOCUMENTARY&size=30"),
Pair("Popular Anime", "/category?area=&category=1&order=count&params=COMIC&size=30"),
Pair("Recent Anime", "/category?area=&category=1&order=up&params=COMIC&size=30"),
)
),
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
@ -157,14 +157,14 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
val parsed = json.decodeFromString<LinkData>(anime.url)
val resp = client.newCall(GET("$baseUrl/detail?category=${parsed.category}&id=${parsed.id}")).execute()
val data = json.decodeFromString<AnimeInfoResponse>(resp.body!!.string()).data
val data = json.decodeFromString<AnimeInfoResponse>(resp.body.string()).data
return Observable.just(
anime.apply {
title = data.name
thumbnail_url = data.coverVerticalUrl
description = data.introduction
genre = data.tagList.joinToString(", ") { it.name }
}
},
)
}
@ -175,7 +175,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
val parsed = json.decodeFromString<LinkData>(anime.url)
val resp = client.newCall(GET("$baseUrl/detail?category=${parsed.category}&id=${parsed.id}")).execute()
val data = json.decodeFromString<AnimeInfoResponse>(resp.body!!.string()).data
val data = json.decodeFromString<AnimeInfoResponse>(resp.body.string()).data
val episodeList = data.episodeVo.map { ep ->
val formattedEpNum = if (floor(ep.seriesNo) == ceil(ep.seriesNo)) {
ep.seriesNo.toInt()
@ -188,8 +188,8 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
LinkData(
data.category.toString(),
data.id,
ep.id.toString()
).toJsonString()
ep.id.toString(),
).toJsonString(),
)
name = "Episode $formattedEpNum"
}
@ -204,7 +204,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
val parsed = json.decodeFromString<LinkData>(episode.url)
val resp = client.newCall(GET("$baseUrl/episode?category=${parsed.category}&id=${parsed.id}&episode=${parsed.episodeId!!}")).execute()
val episodeParsed = json.decodeFromString<EpisodeResponse>(resp.body!!.string())
val episodeParsed = json.decodeFromString<EpisodeResponse>(resp.body.string())
val subtitleList = episodeParsed.data.subtitles.map { sub ->
Track(sub.url, sub.language)
}
@ -224,7 +224,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
val quality = preferences.getString("preferred_quality", "1080")!!
return this.sortedWith(
compareBy { it.quality.contains(quality) }
compareBy { it.quality.contains(quality) },
).reversed()
}

View File

@ -10,8 +10,4 @@ ext {
containsNsfw = false
}
dependencies {
compileOnly libs.bundles.coroutines
}
apply from: "$rootDir/common.gradle"

View File

@ -72,7 +72,7 @@ open class Onepace(override val lang: String, override val name: String) : Confi
}
}
},
false
false,
)
}

View File

@ -7,7 +7,7 @@ class OnepaceFactory : AnimeSourceFactory {
override fun createSources(): List<AnimeSource> = listOf(
OnepaceEspa(),
OnepaceFr(),
OnepaceEn()
OnepaceEn(),
)
}

View File

@ -10,7 +10,7 @@ import org.jsoup.Jsoup
class ZippyExtractor {
fun getVideoUrl(url: String, json: Json): String {
val document = Jsoup.connect(url).get()
val jscript = document.selectFirst("script:containsData(dlbutton)").data()
val jscript = document.selectFirst("script:containsData(dlbutton)")!!.data()
.replace("document.getElementById('dlbutton').href", "a")
.replace("document.getElementById('fimage').href", "b")
.replace("document.getElementById('fimage')", "false")

View File

@ -100,7 +100,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val iframe = "https://akwam.im/watch" + document.select("a.link-show").attr("href").substringAfter("watch") + "/" + document.ownerDocument().select("input#page_id").attr("value")
val iframe = "https://akwam.im/watch" + document.select("a.link-show").attr("href").substringAfter("watch") + "/" + document.ownerDocument()!!.select("input#page_id").attr("value")
val referer = response.request.url.toString()
val refererHeaders = Headers.headersOf("referer", referer)
val iframeResponse = client.newCall(GET(iframe, refererHeaders))
@ -229,13 +229,13 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private class RatingSFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("التقييم", vals)
private fun getTypeFilter(): Array<Pair<String?, String>> = arrayOf(
Pair("movies", "افلام"),
Pair("series", "مسلسلات")
Pair("series", "مسلسلات"),
)
private fun getSectionFilter(): Array<Pair<String?, String>> = arrayOf(
Pair("0", "الكل"),
Pair("movie", "افلام"),
Pair("series", "مسلسلات"),
Pair("show", "تلفزيون")
Pair("show", "تلفزيون"),
)
private fun getRatingFilter(): Array<Pair<String?, String>> = arrayOf(
@ -248,7 +248,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Pair("6", "6+"),
Pair("7", "7+"),
Pair("8", "8+"),
Pair("9", "9+")
Pair("9", "9+"),
)
private fun getFormatFilter(): Array<Pair<String?, String>> = arrayOf(
@ -266,7 +266,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Pair("HDTC", "HDTC"),
Pair("BDRIP", "BDRIP"),
Pair("HDRIP", "HDRIP"),
Pair("HC+HDRIP", "HC HDRIP")
Pair("HC+HDRIP", "HC HDRIP"),
)
private fun getQualityFilter(): Array<Pair<String?, String>> = arrayOf(
@ -286,7 +286,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Pair("30", "اجنبي"),
Pair("31", "هندي"),
Pair("32", "تركي"),
Pair("33", "اسيوي")
Pair("33", "اسيوي"),
)
private fun getCategorySFilter(): Array<Pair<String?, String>> = arrayOf(
@ -315,7 +315,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Pair("22", "رعب"),
Pair("21", "جريمة"),
Pair("19", "مغامرة"),
Pair("91", "غربي")
Pair("91", "غربي"),
)
private fun getRatingSFilter(): Array<Pair<String?, String>> = arrayOf(
@ -328,7 +328,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Pair("6", "6+"),
Pair("7", "7+"),
Pair("8", "8+"),
Pair("9", "9+")
Pair("9", "9+"),
)
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String?, String>>) :

View File

@ -107,7 +107,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val newHeaders = Headers.headersOf("referer", baseUrl + referer)
val iframeResponse = client.newCall(GET(iframe, newHeaders))
.execute().asJsoup()
return videosFromElement(iframeResponse.selectFirst(videoListSelector()))
return videosFromElement(iframeResponse.selectFirst(videoListSelector())!!)
} else {
val postUrl = document.select("form[method=post]").attr("action")
val ur = document.select("input[name=ur]").attr("value")
@ -141,7 +141,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val videoList = mutableListOf<Video>()
val elements = element.select(videoListSelector())
for (element in elements) {
val location = element.ownerDocument().location()
val location = element.ownerDocument()!!.location()
val embedUrl = element.attr("data-ep-url")
val qualityy = element.text()
Log.i("embedUrl", "$embedUrl")
@ -297,6 +297,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return GET(typeUrl.toString(), headers)
}
}
else -> {}
}
}
throw Exception("اختر فلتر")
@ -308,7 +309,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = document.select("img.thumbnail").first().attr("src")
anime.thumbnail_url = document.selectFirst("img.thumbnail")!!.attr("src")
anime.title = document.select("h1.anime-details-title").text()
anime.genre = document.select("ul.anime-genres > li > a, div.anime-info > a").joinToString(", ") { it.text() }
anime.description = document.select("p.anime-story").text()
@ -421,7 +422,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Genre("مغامرات", "%d9%85%d8%ba%d8%a7%d9%85%d8%b1%d8%a7%d8%aa/"),
Genre("موسيقي", "%d9%85%d9%88%d8%b3%d9%8a%d9%82%d9%8a/"),
Genre("ميكا", "%d9%85%d9%8a%d9%83%d8%a7/"),
Genre("نفسي", "%d9%86%d9%81%d8%b3%d9%8a/")
Genre("نفسي", "%d9%86%d9%81%d8%b3%d9%8a/"),
)
private fun getTypeList() = listOf(
@ -430,7 +431,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Type("ONA", "ona1"),
Type("OVA", "ova1"),
Type("Special", "special1"),
Type("TV", "tv2")
Type("TV", "tv2"),
)
@ -438,7 +439,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Status("أختر", ""),
Status("لم يعرض بعد", "%d9%84%d9%85-%d9%8a%d8%b9%d8%b1%d8%b6-%d8%a8%d8%b9%d8%af"),
Status("مكتمل", "complete"),
Status("يعرض الان", "%d9%8a%d8%b9%d8%b1%d8%b6-%d8%a7%d9%84%d8%a7%d9%86-1")
Status("يعرض الان", "%d9%8a%d8%b9%d8%b1%d8%b6-%d8%a7%d9%84%d8%a7%d9%86-1"),
)
}

View File

@ -19,7 +19,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val body = client.newCall(GET(url.replace(".me", ".to"))).execute()
.body!!.string()
.body.string()
val eval = JsUnpacker.unpackAndCombine(body)!!.replace("\\", "")
val json = Json.decodeFromString<JsonObject>(REGEX_DATAJSON.getFirst(eval))
val sojson = REGEX_SOJSON.getFirst(eval)
@ -61,9 +61,8 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
hashAlgorithm: String = "MD5",
keyLength: Int = 32,
ivLength: Int = 16,
iterations: Int = 1
iterations: Int = 1,
): List<ByteArray>? {
val md = MessageDigest.getInstance(hashAlgorithm)
val digestLength = md.getDigestLength()
val targetKeySize = keyLength + ivLength
@ -75,12 +74,13 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
md.reset()
while (generatedLength < targetKeySize) {
if (generatedLength > 0)
if (generatedLength > 0) {
md.update(
generatedData,
generatedLength - digestLength,
digestLength
digestLength,
)
}
md.update(password)
md.update(salt, 0, 8)
@ -95,7 +95,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
}
val result = listOf(
generatedData.copyOfRange(0, keyLength),
generatedData.copyOfRange(keyLength, targetKeySize)
generatedData.copyOfRange(keyLength, targetKeySize),
)
return result
} catch (e: DigestException) {

View File

@ -17,7 +17,7 @@ class MoshahdaExtractor(private val client: OkHttpClient) {
val videoList = mutableListOf<Video>()
for (source in sources) {
val masterUrl = source.substringBefore("\"}")
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body!!.string()
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body.string()
val videoList = mutableListOf<Video>()
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:")
.forEach {

View File

@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
class VidBomExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val doc = client.newCall(GET(url)).execute().asJsoup()
val script = doc.selectFirst("script:containsData(sources)")
val script = doc.selectFirst("script:containsData(sources)")!!
val data = script.data().substringAfter("sources: [").substringBefore("],")
val sources = data.split("file:\"").drop(1)
val videoList = mutableListOf<Video>()

View File

@ -7,7 +7,7 @@ import okhttp3.OkHttpClient
class VidYardExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, headers: Headers): List<Video> {
val callPlayer = client.newCall(GET(url)).execute().body!!.string()
val callPlayer = client.newCall(GET(url)).execute().body.string()
val data = callPlayer.substringAfter("hls\":[").substringBefore("]")
val sources = data.split("profile\":\"").drop(1)
val videoList = mutableListOf<Video>()

View File

@ -75,7 +75,7 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(document.location())
episode.episode_number = 1F
episode.name = document.selectFirst("div.name.col-xs-12 span h1").text()
episode.name = document.selectFirst("div.name.col-xs-12 span h1")!!.text()
return listOf(episode)
}
@ -103,7 +103,7 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val iframe = document.selectFirst("iframe").attr("src")
val iframe = document.selectFirst("iframe")!!.attr("src")
val referer = response.request.url.encodedPath
val newHeaders = Headers.headersOf("referer", baseUrl + referer)
val iframeResponse = client.newCall(GET(iframe, newHeaders))
@ -146,7 +146,7 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
anime.thumbnail_url = baseUrl + element.select("img").first().attr("data-original")
anime.thumbnail_url = baseUrl + element.selectFirst("img")!!.attr("data-original")
anime.title = element.select("img").attr("alt").replace(" poster", "")
return anime
}
@ -168,6 +168,7 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return GET(genreUrl.toString(), headers)
}
}
else -> {}
}
}
throw Exception("اختر فلتر")
@ -224,7 +225,7 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Type(" قائمة الأفلام ", "movie-list"),
Type(" قائمة الأوفا ", "ova-list"),
Type(" قائمة الأونا ", "ona-list"),
Type(" قائمة الحلقات خاصة ", "special-list")
Type(" قائمة الحلقات خاصة ", "special-list"),
)
// preferred quality settings

View File

@ -100,7 +100,7 @@ class AnimeLek : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
for (element in elements) {
val url = element.attr("data-ep-url")
val qualityy = element.text()
val location = element.ownerDocument().location()
val location = element.ownerDocument()!!.location()
val videoHeaders = Headers.headersOf("Referer", location)
when {
url.contains("sbembed.com") || url.contains("sbembed1.com") || url.contains("sbplay.org") ||

View File

@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
class VidBomExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val doc = client.newCall(GET(url)).execute().asJsoup()
val script = doc.selectFirst("script:containsData(sources)")
val script = doc.selectFirst("script:containsData(sources)")!!
val data = script.data().substringAfter("sources: [").substringBefore("],")
val sources = data.split("file:\"").drop(1)
val videoList = mutableListOf<Video>()

View File

@ -81,9 +81,9 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
if (type.contains("animes")) {
val seasonsHtml = client.newCall(
GET(
seriesLink
seriesLink,
// headers = Headers.headersOf("Referer", document.location())
)
),
).execute().asJsoup()
val seasonsElements = seasonsHtml.select("ul.chapters-list li a.title")
seasonsElements.reversed().forEach {
@ -107,7 +107,7 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val episodesHtml = client.newCall(
GET(
episodesUrl,
)
),
).execute().asJsoup()
val episodeElements = episodesHtml.select("ul.chapters-list li")
return episodeElements.map { episodeFromElement(it) }
@ -121,8 +121,8 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
else -> 1F
}
// element.select("td > span.Num").text().toFloat()
// val SeasonNum = element.ownerDocument().select("div.Title span").text()
val seasonName = element.ownerDocument().select("div.media-title h1").text()
// val SeasonNum = element.ownerDocument()!!.select("div.Title span").text()
val seasonName = element.ownerDocument()!!.select("div.media-title h1").text()
episode.name = "$seasonName : " + element.select("a.title h3").text()
episode.setUrlWithoutDomain(element.select("a.title").attr("abs:href"))
return episode
@ -145,7 +145,7 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val videoList = mutableListOf<Video>()
val elements = document.select(videoListSelector())
for (element in elements) {
val location = element.ownerDocument().location()
val location = element.ownerDocument()!!.location()
val videoHeaders = Headers.headersOf("Referer", location)
val qualityy = element.text()
val post = element.attr("data-post")

View File

@ -19,7 +19,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val body = client.newCall(GET(url.replace(".me", ".to"))).execute()
.body!!.string()
.body.string()
val eval = JsUnpacker.unpackAndCombine(body)!!.replace("\\", "")
val json = Json.decodeFromString<JsonObject>(REGEX_DATAJSON.getFirst(eval))
val sojson = REGEX_SOJSON.getFirst(eval)
@ -61,9 +61,8 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
hashAlgorithm: String = "MD5",
keyLength: Int = 32,
ivLength: Int = 16,
iterations: Int = 1
iterations: Int = 1,
): List<ByteArray>? {
val md = MessageDigest.getInstance(hashAlgorithm)
val digestLength = md.getDigestLength()
val targetKeySize = keyLength + ivLength
@ -75,12 +74,13 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
md.reset()
while (generatedLength < targetKeySize) {
if (generatedLength > 0)
if (generatedLength > 0) {
md.update(
generatedData,
generatedLength - digestLength,
digestLength
digestLength,
)
}
md.update(password)
md.update(salt, 0, 8)
@ -95,7 +95,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
}
val result = listOf(
generatedData.copyOfRange(0, keyLength),
generatedData.copyOfRange(keyLength, targetKeySize)
generatedData.copyOfRange(keyLength, targetKeySize),
)
return result
} catch (e: DigestException) {

View File

@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
class UQLoadExtractor(private val client: OkHttpClient) {
fun videoFromUrl(url: String, quality: String): Video? {
val document = client.newCall(GET(url)).execute().asJsoup()
val check = document.selectFirst("script:containsData(sources)").data()
val check = document.selectFirst("script:containsData(sources)")!!.data()
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
return if (check.contains("sources")) {
Video(url, quality, videoUrl)

View File

@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
class VidBomExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val doc = client.newCall(GET(url)).execute().asJsoup()
val script = doc.selectFirst("script:containsData(sources)")
val script = doc.selectFirst("script:containsData(sources)")!!
val data = script.data().substringAfter("sources: [").substringBefore("],")
val sources = data.split("file:\"").drop(1)
val videoList = mutableListOf<Video>()

View File

@ -79,12 +79,12 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val epNum = getNumberFromEpsString(element.select(".epl-num").text())
val urlElements = element.select("a")
episode.setUrlWithoutDomain(urlElements.attr("href"))
episode.name = element.select(".epl-title").text().ifBlank { urlElements.first().text() }
episode.name = element.select(".epl-title").text().ifBlank { urlElements.first()!!.text() }
episode.episode_number = when {
(epNum.isNotEmpty()) -> epNum.toFloat()
else -> 1F
}
episode.date_upload = element.select(".epl-date").first()?.text()?.let { parseEpisodeDate(it) } ?: 0L
episode.date_upload = element.selectFirst(".epl-date")?.text()?.let { parseEpisodeDate(it) } ?: 0L
return episode
}
@ -105,7 +105,7 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val videoList = mutableListOf<Video>()
val elements = document.select(videoListSelector())
for (element in elements) {
val location = element.ownerDocument().location()
val location = element.ownerDocument()!!.location()
val videoEncode = element.attr("value")
val qualityy = element.text()
val decoder: Base64.Decoder = Base64.getDecoder()
@ -256,7 +256,7 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return fetchAnimeDetails(
SAnime.create()
.apply { this.url = "$AnimeUrlDirectory/$AnimePath" }
.apply { this.url = "$AnimeUrlDirectory/$AnimePath" },
)
.map {
// Isn't set in returned Anime
@ -371,8 +371,11 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
seriesDetails.selectFirst(seriesTypeSelector)?.ownText().takeIf { it.isNullOrBlank().not() }?.let { genres.add(it) }
genre = genres.map { genre ->
genre.lowercase(Locale.forLanguageTag(lang)).replaceFirstChar { char ->
if (char.isLowerCase()) char.titlecase(Locale.forLanguageTag(lang))
else char.toString()
if (char.isLowerCase()) {
char.titlecase(Locale.forLanguageTag(lang))
} else {
char.toString()
}
}
}
.joinToString { it.trim() }
@ -402,11 +405,11 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
open class SelectFilter(
displayName: String,
val vals: Array<Pair<String, String>>,
defaultValue: String? = null
defaultValue: String? = null,
) : AnimeFilter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray(),
vals.indexOfFirst { it.second == defaultValue }.takeIf { it != -1 } ?: 0
vals.indexOfFirst { it.second == defaultValue }.takeIf { it != -1 } ?: 0,
) {
fun selectedValue() = vals[state].second
}
@ -418,8 +421,8 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Pair("Ongoing", "ongoing"),
Pair("Completed", "completed"),
Pair("Hiatus", "hiatus"),
Pair("UpComing", "upcoming")
)
Pair("UpComing", "upcoming"),
),
)
protected class TypeFilter : SelectFilter(
@ -430,8 +433,8 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Pair("Movie", "movie"),
Pair("OVA", "ova"),
Pair("ONA", "ona"),
Pair("Special", "special")
)
Pair("Special", "special"),
),
)
protected class OrderByFilter(defaultOrder: String? = null) : SelectFilter(
@ -442,9 +445,9 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Pair("Z-A", "titlereverse"),
Pair("Latest Update", "update"),
Pair("Latest Added", "latest"),
Pair("Popular", "popular")
Pair("Popular", "popular"),
),
defaultOrder
defaultOrder,
)
protected class Genre(name: String, val value: String) : AnimeFilter.TriState(name)
@ -532,8 +535,8 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun parseGenres(document: Document): List<Genre>? {
return document.selectFirst("div.filter:contains(التصنيف) ul.scrollz")?.select("li")?.map { li ->
Genre(
li.selectFirst("label").text(),
li.selectFirst("input[type=checkbox]").attr("value")
li.selectFirst("label")!!.text(),
li.selectFirst("input[type=checkbox]")!!.attr("value"),
)
}
}
@ -541,8 +544,8 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun parseSeasons(document: Document): List<Season>? {
return document.selectFirst("div.filter:contains(الموسم) ul.scrollz")?.select("li")?.map { li ->
Season(
li.selectFirst("label").text(),
li.selectFirst("input[type=checkbox]").attr("value")
li.selectFirst("label")!!.text(),
li.selectFirst("input[type=checkbox]")!!.attr("value"),
)
}
}
@ -550,8 +553,8 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun parseStudios(document: Document): List<Studio>? {
return document.selectFirst("div.filter:contains(الاستديو) ul.scrollz")?.select("li")?.map { li ->
Studio(
li.selectFirst("label").text(),
li.selectFirst("input[type=checkbox]").attr("value")
li.selectFirst("label")!!.text(),
li.selectFirst("input[type=checkbox]")!!.attr("value"),
)
}
}

View File

@ -11,7 +11,7 @@ class AnimeTitansExtractor(private val client: OkHttpClient) {
val callPlayer = client.newCall(GET(url)).execute().asJsoup()
val masterUrl = callPlayer.data().substringAfter("source: \"").substringBefore("\",")
val domain = masterUrl.substringBefore("/videowl") // .replace("https", "http")
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body!!.string()
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body.string()
val videoList = mutableListOf<Video>()
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:").forEach {
val quality = "AnimeTitans: " + it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore("\n") + "p"

View File

@ -19,7 +19,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val body = client.newCall(GET(url.replace(".me", ".to"))).execute()
.body!!.string()
.body.string()
val eval = JsUnpacker.unpackAndCombine(body)!!.replace("\\", "")
val json = Json.decodeFromString<JsonObject>(REGEX_DATAJSON.getFirst(eval))
val sojson = REGEX_SOJSON.getFirst(eval)
@ -61,9 +61,8 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
hashAlgorithm: String = "MD5",
keyLength: Int = 32,
ivLength: Int = 16,
iterations: Int = 1
iterations: Int = 1,
): List<ByteArray>? {
val md = MessageDigest.getInstance(hashAlgorithm)
val digestLength = md.getDigestLength()
val targetKeySize = keyLength + ivLength
@ -75,12 +74,13 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
md.reset()
while (generatedLength < targetKeySize) {
if (generatedLength > 0)
if (generatedLength > 0) {
md.update(
generatedData,
generatedLength - digestLength,
digestLength
digestLength,
)
}
md.update(password)
md.update(salt, 0, 8)
@ -95,7 +95,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
}
val result = listOf(
generatedData.copyOfRange(0, keyLength),
generatedData.copyOfRange(keyLength, targetKeySize)
generatedData.copyOfRange(keyLength, targetKeySize),
)
return result
} catch (e: DigestException) {

View File

@ -17,7 +17,7 @@ class Mp4uploadExtractor {
"referer" to url,
"method_free" to "+",
"method_premiun" to "",
)
),
).method(Connection.Method.POST).ignoreContentType(true)
.ignoreHttpErrors(true).execute().url().toString()
Video(videoUrl, "Mp4Upload", videoUrl, headers)

View File

@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
class VidBomExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val doc = client.newCall(GET(url)).execute().asJsoup()
val script = doc.selectFirst("script:containsData(sources)")
val script = doc.selectFirst("script:containsData(sources)")!!
val data = script.data().substringAfter("sources: [").substringBefore("],")
val sources = data.split("file:\"").drop(1)
val videoList = mutableListOf<Video>()

View File

@ -7,7 +7,7 @@ import okhttp3.OkHttpClient
class VidYardExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, headers: Headers): List<Video> {
val callPlayer = client.newCall(GET(url)).execute().body!!.string()
val callPlayer = client.newCall(GET(url)).execute().body.string()
val data = callPlayer.substringAfter("hls\":[").substringBefore("]")
val sources = data.split("profile\":\"").drop(1)
val videoList = mutableListOf<Video>()

View File

@ -153,7 +153,7 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
anime.title = element.select("div.BlockName h4").text()
anime.thumbnail_url = element.select("img").first().attr("data-src")
anime.thumbnail_url = element.selectFirst("img")!!.attr("data-src")
return anime
}
@ -174,6 +174,7 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return GET(typeUrl.toString(), headers)
}
}
else -> {}
}
}
throw Exception("اختر فلتر")
@ -185,7 +186,7 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = document.select("div.Poster img").first().attr("data-src")
anime.thumbnail_url = document.selectFirst("div.Poster img")!!.attr("data-src")
anime.title = document.select("div.BreadCrumbs ol li:last-child a span").text().replace(" مترجم", "").replace("فيلم ", "")
anime.genre = document.select("div.MetaTermsInfo > li:contains(النوع) > a").joinToString(", ") { it.text() }
anime.description = document.select("div.StoryLine p").text()
@ -225,7 +226,7 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Type("مسلسلات رمضان 2019", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2019/"),
Type("مسلسلات رمضان 2020", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2020-hd/"),
Type("مسلسلات رمضان 2021", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2021/"),
Type("مسلسلات Netfilx", "netfilx/%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-netfilz/")
Type("مسلسلات Netfilx", "netfilx/%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-netfilz/"),
)

View File

@ -54,7 +54,7 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
// anime.thumbnail_url = element.select("div.image img").first().attr("data-src")
// anime.thumbnail_url = element.selectFirst("div.image img")!!.attr("data-src")
anime.title = element.attr("title")
return anime
}
@ -80,7 +80,7 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListRequest(episode: SEpisode): Request {
val document = client.newCall(GET(baseUrl + episode.url)).execute().asJsoup()
val link = document.selectFirst("div.loop-episode a.current").attr("href")
val link = document.selectFirst("div.loop-episode a.current")!!.attr("href")
return GET(link)
}
@ -104,10 +104,11 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} else if (url.contains("yodbox")) {
val html = client.newCall(GET(url)).execute().asJsoup()
val videoFromURL = html.select("source").attr("src")
if (videoFromURL.isNotEmpty())
if (videoFromURL.isNotEmpty()) {
videoList.add(Video(videoFromURL, "Yodbox: mirror", videoFromURL))
}
}
}
return videoList
}
@ -142,7 +143,7 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
// anime.thumbnail_url = element.select("div.image img").first().attr("data-src")
// anime.thumbnail_url = element.selectFirst("div.image img")!!.attr("data-src")
anime.title = element.attr("title")
return anime
}
@ -223,14 +224,14 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Type("الدراما اليابانية", "japanese"),
Type("الدراما الصينية والتايوانية", "chinese-taiwanese"),
Type("الدراما التايلاندية", "thai"),
Type("برامج الترفيه", "kshow")
Type("برامج الترفيه", "kshow"),
)
private fun getStatusList() = listOf(
Status("أختر", ""),
Status("يبث حاليا", "status/ongoing-drama"),
Status("الدراما المكتملة", "completed-dramas"),
Status("الدراما القادمة", "status/upcoming-drama")
Status("الدراما القادمة", "status/upcoming-drama"),
)

View File

@ -63,7 +63,7 @@ class Cartoons4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.attr("href"))
episode.name = element.ownerDocument().select("header.Top h1").text()
episode.name = element.ownerDocument()!!.select("header.Top h1").text()
return episode
}
@ -76,14 +76,14 @@ class Cartoons4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val refererHeaders = Headers.headersOf("referer", referer)
val iframeResponse = client.newCall(GET(iframe, refererHeaders))
.execute().asJsoup()
return videosFromElement(iframeResponse.selectFirst(videoListSelector()))
return videosFromElement(iframeResponse.selectFirst(videoListSelector())!!)
}
override fun videoListSelector() = "a.button.is-success"
private fun videosFromElement(element: Element): List<Video> {
val vidURL = element.attr("abs:href")
val apiCall = client.newCall(POST(vidURL.replace("/v/", "/api/source/"))).execute().body!!.string()
val apiCall = client.newCall(POST(vidURL.replace("/v/", "/api/source/"))).execute().body.string()
val data = apiCall.substringAfter("\"data\":[").substringBefore("],")
val sources = data.split("\"file\":\"").drop(1)
val videoList = mutableListOf<Video>()

View File

@ -77,20 +77,20 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return episode
}
fun addEpisodes(document: Document) {
if (document.select(episodeListSelector()).isNullOrEmpty())
if (document.select(episodeListSelector()).isNullOrEmpty()) {
document.select("div.shortLink").map { episodes.add(episodeExtract(it)) }
else {
} else {
document.select(episodeListSelector()).map { episodes.add(episodeFromElement(it)) }
document.select(seasonsNextPageSelector(seasonNumber)).firstOrNull()?.let {
document.selectFirst(seasonsNextPageSelector(seasonNumber))?.let {
seasonNumber++
addEpisodes(
client.newCall(
GET(
"$baseUrl/?p=" + it.select("div.seasonDiv")
.attr("data-href"),
headers
)
).execute().asJsoup()
headers,
),
).execute().asJsoup(),
)
}
}
@ -103,7 +103,7 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.attr("abs:href"))
episode.name = element.ownerDocument().select("div.seasonDiv.active > div.title").text() + " : " + element.text()
episode.name = element.ownerDocument()!!.select("div.seasonDiv.active > div.title").text() + " : " + element.text()
episode.episode_number = element.text().replace("الحلقة ", "").toFloat()
return episode
}
@ -116,9 +116,9 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val document = response.asJsoup()
val getSources = "master.m3u8"
val referer = Headers.headersOf("Referer", "$baseUrl/")
val iframe = document.selectFirst("iframe").attr("src").substringBefore("&img")
val iframe = document.selectFirst("iframe")!!.attr("src").substringBefore("&img")
val webViewIncpec = client.newBuilder().addInterceptor(GetSourcesInterceptor(getSources, client)).build()
val lol = webViewIncpec.newCall(GET(iframe, referer)).execute().body!!.string()
val lol = webViewIncpec.newCall(GET(iframe, referer)).execute().body.string()
val videoList = mutableListOf<Video>()
lol.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:").forEach {
val quality = it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p"
@ -229,7 +229,7 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("NOTE: Ignored if using text search!"),
AnimeFilter.Separator(),
GenreFilter(getGenreList())
GenreFilter(getGenreList()),
)
private class GenreFilter(vals: Array<Pair<String, String>>) : UriPartFilter("تصنيف المسلسلات", vals)
@ -251,7 +251,7 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Pair("المسلسلات القصيرة", "short_series"),
Pair("المسلسلات الاسيوية", "asian-series"),
Pair("المسلسلات الاسيوية الاعلي مشاهدة", "asian_top_views"),
Pair("الانمي الاعلي مشاهدة", "anime_top_views")
Pair("الانمي الاعلي مشاهدة", "anime_top_views"),
)
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :

View File

@ -68,7 +68,7 @@ class GetSourcesInterceptor(private val getSources: String, private val client:
webview.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest
request: WebResourceRequest,
): WebResourceResponse? {
val url = request.url.toString()
if (url.contains(getSources)) {

View File

@ -74,8 +74,8 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val seasonsHtml = client.newCall(
GET(
seasonUrl,
headers = Headers.headersOf("Referer", document.location())
)
headers = Headers.headersOf("Referer", document.location()),
),
).execute().asJsoup()
val seasonsElements = seasonsHtml.select("div.col-6.col-sm-4.col-md-3.col-xl-2 div.card")
seasonsElements.forEach {
@ -101,7 +101,7 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val episodesHtml = client.newCall(
GET(
seasonId,
)
),
).execute().asJsoup()
val episodeElements = episodesHtml.select("div.col-6.col-sm-4.col-md-3.col-xl-2")
return episodeElements.map { episodeFromElement(it, seasonName) }
@ -110,7 +110,7 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun episodeFromElement(element: Element, seasonName: String): SEpisode {
val episode = SEpisode.create()
episode.episode_number = element.select("h3.card__title a").attr("abs:href").substringAfter("episode-").toFloat()
// val SeasonNum = element.ownerDocument().select("div.Title span").text()
// val SeasonNum = element.ownerDocument()!!.select("div.Title span").text()
episode.name = "$seasonName" + ": " + element.select("h3.card__title a").text()
episode.setUrlWithoutDomain(element.select("h3.card__title a").attr("abs:href"))
return episode
@ -120,15 +120,15 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val iframe1 = client.newCall(GET(document.selectFirst("iframe#video").attr("data-src")))
val iframe1 = client.newCall(GET(document.selectFirst("iframe#video")!!.attr("data-src")))
.execute().asJsoup()
val iframe = iframe1.selectFirst("iframe").attr("src")
val iframe = iframe1.selectFirst("iframe")!!.attr("src")
val referer = response.request.url.encodedPath
val newHeaders = Headers.headersOf("referer", baseUrl + referer)
val iframeResponse = client.newCall(GET(iframe, newHeaders))
.execute().asJsoup()
return videosFromElement(iframeResponse.selectFirst(videoListSelector()))
return videosFromElement(iframeResponse.selectFirst(videoListSelector())!!)
}
override fun videoListSelector() = "script:containsData(source)"
@ -217,6 +217,7 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return GET(genreUrl, headers)
}
}
else -> {}
}
}
throw Exception("اختر فلتر")
@ -321,6 +322,6 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Genre("كوميديا", "38"),
Genre("مغامرة", "39"),
Genre("موسيقية", "40"),
Genre("وثائقية", "41")
Genre("وثائقية", "41"),
)
}

View File

@ -73,16 +73,17 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
fun addEpisodeNew(url: String, type: String, title: String = "") {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(url)
if (type == "assembly")
if (type == "assembly") {
episode.name = title.replace("فيلم", "").trim()
else if (type == "movie")
} else if (type == "movie") {
episode.name = "مشاهدة"
else if (TitleRegex.SEASON.containsMatchIn(title))
} else if (TitleRegex.SEASON.containsMatchIn(title)) {
episode.name = TitleRegex.SEASON.find(title)!!.value.replace("مترجمة", "").replace("والاخيرة", "").trim()
else if (TitleRegex.EPISODE.containsMatchIn(title))
} else if (TitleRegex.EPISODE.containsMatchIn(title)) {
episode.name = TitleRegex.EPISODE.find(title)!!.value.replace("مترجمة", "").replace("والاخيرة", "").trim()
else
} else {
episode.name = title
}
episodes.add(episode)
}
@ -146,14 +147,15 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
addEpisodeNew(
episode.select("a").attr("href"),
"series",
document.select("div.SeriesSingle h2 span a").text() + " " + episode.select("a").text()
document.select("div.SeriesSingle h2 span a").text() + " " + episode.select("a").text(),
)
}
if (count == 0)
if (count == 0) {
addEpisodeNew(url, "movie")
}
}
}
}
addEpisodes(response)
@ -222,8 +224,10 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
val title = titleEdit(element.select("a div.BlockImageItem div.BlockTitle").text(), true)
anime.thumbnail_url = element.select("a div.BlockImageItem img").attr("src")
if (anime.thumbnail_url.isNullOrEmpty()) anime.thumbnail_url =
if (anime.thumbnail_url.isNullOrEmpty()) {
anime.thumbnail_url =
element.select("a div.BlockImageItem img").attr("data-src")
}
anime.setUrlWithoutDomain(element.select("a").attr("href"))
anime.title = title
return anime
@ -260,8 +264,11 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
// div.CoverSingle div.CoverSingleContent
anime.genre = document.select("div.SingleDetails li:contains(النوع) a,div.SingleDetails li:contains(الجودة) a").joinToString(", ") { it.text() }
anime.title = if (document.select("h2.postTitle").isNullOrEmpty())
titleEdit(document.select("div.H1Title h1").text()) else titleEdit(document.select("h2.postTitle").text())
anime.title = if (document.select("h2.postTitle").isNullOrEmpty()) {
titleEdit(document.select("div.H1Title h1").text())
} else {
titleEdit(document.select("h2.postTitle").text())
}
anime.author = document.select("div.SingleDetails li:contains(دولة) a").text()
anime.description = document.select("div.ServersEmbeds section.story").text().replace(document.select("meta[property=og:title]").attr("content"), "").replace(":", "").trim()
anime.status = SAnime.COMPLETED
@ -307,7 +314,7 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
CatUnit("مسلسلات نتفلكس", "series/netflix-series"),
CatUnit("مسلسلات انمى", "series/anime-series"),
CatUnit("مسلسلات تركى", "series/turkish-series"),
CatUnit("مسلسلات عربى", "series/arab-series")
CatUnit("مسلسلات عربى", "series/arab-series"),
)
// ============================= title edit =============================
@ -321,19 +328,25 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} else if (TitleRegex.SERIES_EPISODES.containsMatchIn(title)) {
val newTitle = TitleRegex.SERIES_EPISODES.find(title)
newTitle!!.groupValues[1].trim() + if (details) " (${newTitle.groupValues[2].trim()}ح)" else ""
} else title
} else if (title.contains("فيلم")) {
if (TitleRegex.ARABIC_MOVIE.containsMatchIn(title)) // افلام عربى
TitleRegex.ARABIC_MOVIE.find(title)!!.groupValues[1].trim() + if (details) " ($movie)" else ""
else if (TitleRegex.MOVIES.containsMatchIn(title))
TitleRegex.MOVIES.find(title)!!.groupValues[1].trim() + if (details) " (${TitleRegex.MOVIES.find(title)!!.groupValues[2].trim()})($movie)" else ""
else title
} else if (title.contains("انمي"))
Regex(if (title.contains("الموسم"))"انمي(.*)الموسم" else "انمي(.*)الحلقة").find(title)!!.groupValues[1] + if (details) " (انمى)" else ""
else if (title.contains("برنامج"))
Regex(if (title.contains("الموسم"))"برنامج(.*)الموسم" else "برنامج(.*)حلقة").find(title)!!.groupValues[1].removeSurrounding(" ال") + if (details) " (برنامج)" else ""
else
} else {
title
}
} else if (title.contains("فيلم")) {
if (TitleRegex.ARABIC_MOVIE.containsMatchIn(title)) {
// افلام عربى
TitleRegex.ARABIC_MOVIE.find(title)!!.groupValues[1].trim() + if (details) " ($movie)" else ""
} else if (TitleRegex.MOVIES.containsMatchIn(title)) {
TitleRegex.MOVIES.find(title)!!.groupValues[1].trim() + if (details) " (${TitleRegex.MOVIES.find(title)!!.groupValues[2].trim()})($movie)" else ""
} else {
title
}
} else if (title.contains("انمي")) {
Regex(if (title.contains("الموسم"))"انمي(.*)الموسم" else "انمي(.*)الحلقة").find(title)!!.groupValues[1] + if (details) " (انمى)" else ""
} else if (title.contains("برنامج")) {
Regex(if (title.contains("الموسم"))"برنامج(.*)الموسم" else "برنامج(.*)حلقة").find(title)!!.groupValues[1].removeSurrounding(" ال") + if (details) " (برنامج)" else ""
} else {
title
}
return finalTitle.trim()
}

View File

@ -23,8 +23,8 @@ class LinkboxExtractor(private val client: OkHttpClient) {
Video(
it.jsonObject["url"].toString().replace("\"", ""),
"Linkbox: ${it.jsonObject["resolution"].toString().replace("\"", "")}",
it.jsonObject["url"].toString().replace("\"", "")
)
it.jsonObject["url"].toString().replace("\"", ""),
),
)
}
return videoList

View File

@ -9,7 +9,7 @@ import okhttp3.OkHttpClient
class MoshahdaExtractor(private val client: OkHttpClient) {
fun videosFromUrl(link: String, headers: Headers): List<Video> {
val request = client.newCall(GET(link, headers)).execute().asJsoup()
val element = request.selectFirst("script:containsData(sources)")
val element = request.selectFirst("script:containsData(sources)")!!
val videoList = mutableListOf<Video>()
val qualityMap = mapOf("l" to "240p", "n" to "360p", "h" to "480p", "x" to "720p", "o" to "1080p")
val data2 = element.data().substringAfter(", file: \"").substringBefore("\"}],")

View File

@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
class UQLoadExtractor(private val client: OkHttpClient) {
fun videoFromUrl(url: String, quality: String): Video? {
val document = client.newCall(GET(url)).execute().asJsoup()
val check = document.selectFirst("script:containsData(sources)").data()
val check = document.selectFirst("script:containsData(sources)")!!.data()
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
return if (check.contains("sources")) {
Video(url, quality, videoUrl)

View File

@ -73,14 +73,15 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
if (document.select(episodeListSelector()).isNullOrEmpty()) {
if (!document.select("mycima singlerelated.hasdivider ${popularAnimeSelector()}").isNullOrEmpty()) {
document.select("mycima singlerelated.hasdivider ${popularAnimeSelector()}").map { episodes.add(newEpisodeFromElement(it, "mSeries")) }
} else
episodes.add(newEpisodeFromElement(document.select("div.Poster--Single-begin > a").first(), "movie"))
} else {
episodes.add(newEpisodeFromElement(document.selectFirst("div.Poster--Single-begin > a")!!, "movie"))
}
} else {
document.select(episodeListSelector()).map { episodes.add(newEpisodeFromElement(it)) }
document.select(seasonsNextPageSelector(seasonNumber)).firstOrNull()?.let {
document.selectFirst(seasonsNextPageSelector(seasonNumber))?.let {
seasonNumber++
addEpisodes(
client.newCall(GET(it.attr("abs:href"), headers)).execute().asJsoup()
client.newCall(GET(it.attr("abs:href"), headers)).execute().asJsoup(),
)
}
}
@ -93,15 +94,16 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val episode = SEpisode.create()
val epNum = getNumberFromEpsString(element.text())
episode.setUrlWithoutDomain(if (type == "mSeries") element.select("a").attr("href") else element.attr("abs:href"))
if (type == "series")
if (type == "series") {
episode.episode_number = when {
(epNum.isNotEmpty()) -> epNum.toFloat()
else -> 1F
}
}
episode.name = when (type) {
"movie" -> "مشاهدة"
"mSeries" -> element.select("a").attr("title")
else -> element.ownerDocument().select("div.List--Seasons--Episodes a.selected").text() + element.text()
else -> element.ownerDocument()!!.select("div.List--Seasons--Episodes a.selected").text() + element.text()
}
return episode
}
@ -116,13 +118,13 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val iframe = document.selectFirst("iframe").attr("data-lazy-src")
val iframe = document.selectFirst("iframe")!!.attr("data-lazy-src")
val referer = response.request.url.encodedPath
val newHeaderList = mutableMapOf(Pair("referer", baseUrl + referer))
headers.forEach { newHeaderList[it.first] = it.second }
val iframeResponse = client.newCall(GET(iframe, newHeaderList.toHeaders()))
.execute().asJsoup()
return videosFromElement(iframeResponse.selectFirst(videoListSelector()))
return videosFromElement(iframeResponse.selectFirst(videoListSelector())!!)
}
override fun videoListSelector() = "body"
@ -143,7 +145,7 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
return videoList
}
val sourceTag = element.ownerDocument().select("source").firstOrNull()!!
val sourceTag = element.ownerDocument()!!.select("source").firstOrNull()!!
return listOf(Video(sourceTag.attr("src"), "Default", sourceTag.attr("src")))
}
@ -256,7 +258,7 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
SearchCategoryList(searchCategoryNames),
AnimeFilter.Separator(),
AnimeFilter.Header("اقسام الموقع (تعمل فقط اذا كان البحث فارغ)"),
CategoryList(categoryNames)
CategoryList(categoryNames),
)
private class SearchCategoryList(categories: Array<String>) : AnimeFilter.Select<String>("بحث عن", categories)
@ -273,7 +275,7 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
CatUnit("فيلم", "/page/"),
CatUnit("مسلسل", "list/series/?page_number="),
CatUnit("انمى", "list/anime/?page_number="),
CatUnit("برنامج", "list/tv/?page_number=")
CatUnit("برنامج", "list/tv/?page_number="),
)
private fun getCategoryList() = listOf(
CatUnit("اختر", ""),

View File

@ -60,27 +60,31 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
private fun titleEdit(title: String, details: Boolean = false): String {
return if (Regex("فيلم (.*?) مترجم").containsMatchIn(title))
return if (Regex("فيلم (.*?) مترجم").containsMatchIn(title)) {
Regex("فيلم (.*?) مترجم").find(title)!!.groupValues[1] + " (فيلم)" // افلام اجنبيه مترجمه
else if (Regex("فيلم (.*?) مدبلج").containsMatchIn(title))
} else if (Regex("فيلم (.*?) مدبلج").containsMatchIn(title)) {
Regex("فيلم (.*?) مدبلج").find(title)!!.groupValues[1] + " (مدبلج)(فيلم)" // افلام اجنبيه مدبلجه
else if (Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").containsMatchIn(title)) // افلام عربى
} else if (Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").containsMatchIn(title)) {
// افلام عربى
Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").find(title)!!.groupValues[1] + " (فيلم)"
else if (title.contains("مسلسل")) {
} else if (title.contains("مسلسل")) {
if (title.contains("الموسم") and details) {
val newTitle = Regex("مسلسل (.*?) الموسم (.*?) الحلقة ([0-9]+)").find(title)
return "${newTitle!!.groupValues[1]} (م.${newTitle.groupValues[2]})(${newTitle.groupValues[3]}ح)"
} else if (title.contains("الحلقة") and details) {
val newTitle = Regex("مسلسل (.*?) الحلقة ([0-9]+)").find(title)
return "${newTitle!!.groupValues[1]} (${newTitle.groupValues[2]}ح)"
} else Regex(if (title.contains("الموسم")) "مسلسل (.*?) الموسم" else "مسلسل (.*?) الحلقة").find(title)!!.groupValues[1] + " (مسلسل)"
} else if (title.contains("انمي"))
} else {
Regex(if (title.contains("الموسم")) "مسلسل (.*?) الموسم" else "مسلسل (.*?) الحلقة").find(title)!!.groupValues[1] + " (مسلسل)"
}
} else if (title.contains("انمي")) {
return Regex(if (title.contains("الموسم"))"انمي (.*?) الموسم" else "انمي (.*?) الحلقة").find(title)!!.groupValues[1] + " (انمى)"
else if (title.contains("برنامج"))
} else if (title.contains("برنامج")) {
Regex(if (title.contains("الموسم"))"برنامج (.*?) الموسم" else "برنامج (.*?) الحلقة").find(title)!!.groupValues[1] + " (برنامج)"
else
} else {
title
}
}
override fun popularAnimeNextPageSelector(): String = "div.paginate ul.page-numbers li.active + li a"
@ -93,12 +97,13 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
fun addEpisodeNew(url: String, type: String, title: String = "") {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(url)
if (type == "assembly")
if (type == "assembly") {
episode.name = title.replace("فيلم", "").trim()
else if (type == "movie")
} else if (type == "movie") {
episode.name = "مشاهدة"
else
} else {
episode.name = title
}
episodes.add(episode)
}
@ -110,7 +115,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
addEpisodeNew(
movie.select("a.fullClick").attr("href") + "watch/",
"assembly",
movie.select("h3").text()
movie.select("h3").text(),
)
}
return
@ -129,7 +134,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
addEpisodeNew(
episode.attr("href"),
"series",
seasonNum + " " + episode.text()
seasonNum + " " + episode.text(),
)
}
} else {
@ -142,7 +147,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
addEpisodeNew(
episode.attr("href"),
"series",
seasonNum + " " + episode.text()
seasonNum + " " + episode.text(),
)
}
}
@ -165,18 +170,22 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val servers = document.select(videoListSelector())
fun getUrl(v_id: String, i: String): String {
val refererHeaders = Headers.headersOf(
"referer", response.request.url.toString(),
"x-requested-with", "XMLHttpRequest",
"Content-Type", "application/x-www-form-urlencoded; charset=UTF-8",
"user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.42"
"referer",
response.request.url.toString(),
"x-requested-with",
"XMLHttpRequest",
"Content-Type",
"application/x-www-form-urlencoded; charset=UTF-8",
"user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.42",
)
val requestBody = FormBody.Builder().add("id", v_id).add("i", i).build()
val iframe = client.newCall(
POST(
"$baseUrl/wp-content/themes/Shahid4u-WP_HOME/Ajaxat/Single/Server.php",
refererHeaders,
requestBody
)
requestBody,
),
).execute().asJsoup()
return iframe.select("iframe").attr("src")
}
@ -207,7 +216,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun videosFromElement(document: Document): List<Video> {
val videoList = mutableListOf<Video>()
return try {
val scriptSelect = document.select("script:containsData(eval)").first().data()
val scriptSelect = document.selectFirst("script:containsData(eval)")!!.data()
val serverPrefix =
scriptSelect.substringAfter("|net|cdn|amzn|").substringBefore("|rewind|icon|")
val sourceServer = "https://$serverPrefix.e-amzn-cdn.net"
@ -255,10 +264,11 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
var link = element.select("a.fullClick").attr("href")
anime.title = titleEdit(element.select("h3").text(), details = true).trim()
if (link.contains("assemblies"))
if (link.contains("assemblies")) {
anime.thumbnail_url = element.select("a.image img").attr("data-src")
else
} else {
anime.thumbnail_url = element.select("a.image img.imgInit").attr("data-image")
}
if (!link.contains("assemblies")) link += "watch/"
anime.setUrlWithoutDomain(link)
return anime
@ -300,7 +310,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
anime.author = document.select("div.SingleDetails li:contains(دولة) a").text()
anime.description = document.select("div.ServersEmbeds section.story").text().replace(document.select("meta[property=og:title]").attr("content"), "").replace(":", "").trim()
anime.status = SAnime.COMPLETED
if (anime.title.contains("سلسلة")) anime.thumbnail_url = document.select("img.imgInit").first().attr("data-image")
if (anime.title.contains("سلسلة")) anime.thumbnail_url = document.selectFirst("img.imgInit")!!.attr("data-image")
return anime
}
@ -344,7 +354,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
CatUnit("مسلسلات انمى", "category/مسلسلات-انمي-4/"),
CatUnit("مسلسلات تركى", "category/مسلسلات-تركي-3/"),
CatUnit("مسلسلات اسيوى", "category/مسلسلات-اسيوي/"),
CatUnit("مسلسلات هندى", "category/مسلسلات-هندية/")
CatUnit("مسلسلات هندى", "category/مسلسلات-هندية/"),
)
// preferred quality settings

View File

@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
class UQLoadExtractor(private val client: OkHttpClient) {
fun videoFromUrl(url: String, quality: String): Video? {
val document = client.newCall(GET(url)).execute().asJsoup()
val check = document.selectFirst("script:containsData(sources)").data()
val check = document.selectFirst("script:containsData(sources)")!!.data()
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
return if (check.contains("sources")) {
Video(url, quality, videoUrl)

View File

@ -8,15 +8,16 @@ import okhttp3.OkHttpClient
class VidBomExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val doc = client.newCall(GET(url)).execute().asJsoup()
val script = doc.selectFirst("script:containsData(sources)")
val script = doc.selectFirst("script:containsData(sources)")!!
val data = script.data().substringAfter("sources: [").substringBefore("],")
val sources = data.split("file:\"").drop(1)
val videoList = mutableListOf<Video>()
for (source in sources) {
val src = source.substringBefore("\"")
var quality = "Vidbom: " + source.substringAfter("label:\"").substringBefore("\"") // .substringAfter("format: '")
if (quality.length > 15)
if (quality.length > 15) {
quality = "Vidshare: 480p"
}
val video = Video(src, quality, src)
videoList.add(video)
}

View File

@ -59,13 +59,16 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun titleEdit(title: String, details: Boolean = false): String {
return if (title.contains("فيلم")) {
if (Regex("فيلم (.*?) مترجم").containsMatchIn(title))
if (Regex("فيلم (.*?) مترجم").containsMatchIn(title)) {
Regex("فيلم (.*?) مترجم").find(title)!!.groupValues[1] + if (details) " (فيلم)" else "" // افلام اجنبيه مترجمه
else if (Regex("فيلم (.*?) مدبلج").containsMatchIn(title))
} else if (Regex("فيلم (.*?) مدبلج").containsMatchIn(title)) {
Regex("فيلم (.*?) مدبلج").find(title)!!.groupValues[1] + if (details) " (مدبلج)(فيلم)" else "" // افلام اجنبيه مدبلجه
else if (Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").containsMatchIn(title)) // افلام عربى
} else if (Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").containsMatchIn(title)) {
// افلام عربى
Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").find(title)!!.groupValues[1] + if (details) " (فيلم)" else ""
else title
} else {
title
}
} else if (title.contains("مسلسل")) {
if (title.contains("الموسم")) {
val newTitle = Regex("مسلسل (.*?) الموسم (.*?) الحلقة ([0-9]+)").find(title)
@ -73,14 +76,17 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} else if (title.contains("الحلقة")) {
val newTitle = Regex("مسلسل (.*?) الحلقة ([0-9]+)").find(title)
newTitle!!.groupValues[1] + if (details) " (${newTitle.groupValues[2]}ح)" else ""
} else Regex(if (title.contains("الموسم")) "مسلسل (.*?) الموسم" else "مسلسل (.*?) الحلقة").find(title)!!.groupValues[1] + if (details) " (مسلسل)" else ""
} else if (title.contains("انمي"))
} else {
Regex(if (title.contains("الموسم")) "مسلسل (.*?) الموسم" else "مسلسل (.*?) الحلقة").find(title)!!.groupValues[1] + if (details) " (مسلسل)" else ""
}
} else if (title.contains("انمي")) {
return Regex(if (title.contains("الموسم"))"انمي (.*?) الموسم" else "انمي (.*?) الحلقة").find(title)!!.groupValues[1] + if (details) " (انمى)" else ""
else if (title.contains("برنامج"))
} else if (title.contains("برنامج")) {
Regex(if (title.contains("الموسم"))"برنامج (.*?) الموسم" else "برنامج (.*?) الحلقة").find(title)!!.groupValues[1] + if (details) " (برنامج)" else ""
else
} else {
title
}
}
override fun popularAnimeNextPageSelector(): String = "div.pagination ul.page-numbers li a.next"
@ -99,23 +105,27 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
fun addEpisodes(response: Response) {
val document = response.asJsoup()
val url = response.request.url.toString()
if (document.select(seasonsNextPageSelector()).isNullOrEmpty())
if (document.select(seasonsNextPageSelector()).isNullOrEmpty()) {
addEpisodeNew(url, "مشاهدة")
else
} else {
document.select(seasonsNextPageSelector()).reversed().forEach { season ->
val seasonNum = season.select("h3").text()
(
if (seasonNum == document.selectFirst("div#mpbreadcrumbs a span:contains(الموسم)").text())
document else client.newCall(GET(season.selectFirst("a").attr("href"))).execute().asJsoup()
if (seasonNum == document.selectFirst("div#mpbreadcrumbs a span:contains(الموسم)")!!.text()) {
document
} else {
client.newCall(GET(season.selectFirst("a")!!.attr("href"))).execute().asJsoup()
}
)
.select("section.allepcont a").forEach { episode ->
addEpisodeNew(
episode.attr("href") + "watch/",
seasonNum + " : الحلقة " + episode.select("div.epnum").text().filter { it.isDigit() }
seasonNum + " : الحلقة " + episode.select("div.epnum").text().filter { it.isDigit() },
)
}
}
}
}
addEpisodes(response)
return episodes
}
@ -207,7 +217,7 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.title = titleEdit(element.select("h3").text(), true).trim()
anime.thumbnail_url = element.select("img").attr(if (element.ownerDocument().location().contains("?s="))"src" else "data-src")
anime.thumbnail_url = element.select("img").attr(if (element.ownerDocument()!!.location().contains("?s="))"src" else "data-src")
anime.setUrlWithoutDomain(element.select("a").attr("href") + "watch/")
return anime
}
@ -266,7 +276,7 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
CatUnit("مسلسلات انمى", "category/anime-6/انمي-مترجم/"),
CatUnit("مسلسلات تركى", "category/series-9/مسلسلات-تركي/"),
CatUnit("مسلسلات اسيوى", "category/series-9/مسلسلات-أسيوي/"),
CatUnit("مسلسلات هندى", "category/series-9/مسلسلات-هندي/")
CatUnit("مسلسلات هندى", "category/series-9/مسلسلات-هندي/"),
)
// preferred quality settings

View File

@ -9,7 +9,7 @@ import okhttp3.OkHttpClient
class MoshahdaExtractor(private val client: OkHttpClient) {
fun videosFromUrl(link: String, headers: Headers): List<Video> {
val request = client.newCall(GET(link, headers)).execute().asJsoup()
val element = request.selectFirst("script:containsData(sources)")
val element = request.selectFirst("script:containsData(sources)")!!
val videoList = mutableListOf<Video>()
val qualityMap = mapOf("l" to "240p", "n" to "360p", "h" to "480p", "x" to "720p", "o" to "1080p")
val data2 = element.data().substringAfter(", file: \"").substringBefore("\"}],")

View File

@ -8,11 +8,12 @@ import okhttp3.OkHttpClient
class UQLoadExtractor(private val client: OkHttpClient) {
fun videoFromUrl(url: String, quality: String): Video? {
val document = client.newCall(GET(url)).execute().asJsoup()
val check = document.selectFirst("script:containsData(sources)").data()
val check = document.selectFirst("script:containsData(sources)")!!.data()
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
return if (check.contains("sources"))
return if (check.contains("sources")) {
Video(url, quality, videoUrl)
else
} else {
null
}
}
}

View File

@ -8,15 +8,16 @@ import okhttp3.OkHttpClient
class VidBomExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val doc = client.newCall(GET(url)).execute().asJsoup()
val script = doc.selectFirst("script:containsData(sources)")
val script = doc.selectFirst("script:containsData(sources)")!!
val data = script.data().substringAfter("sources: [").substringBefore("],")
val sources = data.split("file:\"").drop(1)
val videoList = mutableListOf<Video>()
for (source in sources) {
val src = source.substringBefore("\"")
var quality = "Vidbom: " + source.substringAfter("label:\"").substringBefore("\"") // .substringAfter("format: '")
if (quality.length > 15)
if (quality.length > 15) {
quality = "Vidshare: 480p"
}
val video = Video(src, quality, src)
videoList.add(video)
}

View File

@ -54,7 +54,7 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("div.anime-card-poster a").attr("href"))
anime.title = element.select("div.anime-card-poster div.ehover6 img").attr("alt")
anime.thumbnail_url = element.select("div.anime-card-poster div.ehover6 img").first().attr("abs:src")
anime.thumbnail_url = element.selectFirst("div.anime-card-poster div.ehover6 img")!!.attr("abs:src")
return anime
}
@ -136,9 +136,10 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
url.contains("dood") -> {
val video = DoodExtractor(client).videoFromUrl(url, "Dood mirror")
if (video != null)
if (video != null) {
videoList.add(video)
}
}
url.contains("4shared") -> {
val video = SharedExtractor(client).videosFromUrl(url)
if (video != null) videoList.add(video)
@ -182,7 +183,7 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("div.anime-card-poster a").attr("href"))
anime.title = element.select("div.anime-card-poster div.ehover6 img").attr("alt")
anime.thumbnail_url = element.select("div.anime-card-poster div.ehover6 img").first().attr("abs:src")
anime.thumbnail_url = element.selectFirst("div.anime-card-poster div.ehover6 img")!!.attr("abs:src")
return anime
}
@ -196,11 +197,12 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
val doc = if (!document.select("div.anime-page-link").isNullOrEmpty())
val doc = if (!document.select("div.anime-page-link").isNullOrEmpty()) {
client.newCall(GET(document.select("div.anime-page-link a").attr("href"), headers)).execute().asJsoup()
else
} else {
document
anime.thumbnail_url = doc.select("img.thumbnail").first().attr("src")
}
anime.thumbnail_url = doc.selectFirst("img.thumbnail")!!.attr("src")
anime.title = doc.select("h1.anime-details-title").text()
anime.genre = doc.select("ul.anime-genres > li > a, div.anime-info > a").joinToString(", ") { it.text() }
anime.description = doc.select("p.anime-story").text()
@ -227,7 +229,7 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("div.anime-card-poster a").attr("href"))
anime.title = element.select("div.anime-card-poster div.ehover6 img").attr("alt")
anime.thumbnail_url = element.select("div.anime-card-poster div.ehover6 img").first().attr("abs:src")
anime.thumbnail_url = element.selectFirst("div.anime-card-poster div.ehover6 img")!!.attr("abs:src")
return anime
}

View File

@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
class SharedExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, quality: String = "mirror"): Video? {
val document = client.newCall(GET(url)).execute().asJsoup()
return document.select("source").firstOrNull()?.let {
return document.selectFirst("source")?.let {
Video(it.attr("src"), "4Shared: $quality", it.attr("src"))
}
}

View File

@ -49,7 +49,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
anime.title = element.attr("title")
anime.thumbnail_url = element.select("div.itemtype_anime_poster img").first().attr("data-src")
anime.thumbnail_url = element.selectFirst("div.itemtype_anime_poster img")!!.attr("data-src")
return anime
}
@ -103,7 +103,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
anime.title = element.attr("title")
anime.thumbnail_url = element.select("div.itemtype_anime_poster img").first().attr("data-src")
anime.thumbnail_url = element.selectFirst("div.itemtype_anime_poster img")!!.attr("data-src")
return anime
}
@ -122,6 +122,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
is GenreFilter -> url.addQueryParameter("genre", filter.toUriPart())
is StatusFilter -> url.addQueryParameter("status", filter.toUriPart())
// is LetterFilter -> url.addQueryParameter("letter", filter.toUriPart())
else -> {}
}
}
GET(url.build().toString(), headers)
@ -132,7 +133,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = document.select("div.inner--image img").first().attr("src")
anime.thumbnail_url = document.selectFirst("div.inner--image img")!!.attr("src")
anime.title = document.select("h1.post--inner-title").text()
anime.genre = document.select("ul.terms--and--metas > li:contains(تصنيفات الأنمي) > a").joinToString(", ") { it.text() }
anime.description = document.select("div.post--content--inner").text()
@ -164,7 +165,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun getStatusFilters(): Array<Pair<String?, String>> = arrayOf(
Pair("", "<اختر>"),
Pair("مستمر", "مستمر"),
Pair("منتهي", "منتهي")
Pair("منتهي", "منتهي"),
)
private fun getGenreFilters(): Array<Pair<String?, String>> = arrayOf(
@ -187,7 +188,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Pair("مدرسي", "مدرسي"),
Pair("مغامرات", "مغامرات"),
Pair("موسيقي", "موسيقي"),
Pair("نفسي", "نفسي")
Pair("نفسي", "نفسي"),
)
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String?, String>>) :

View File

@ -47,7 +47,7 @@ class XsMovie : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
anime.title = element.attr("title")
anime.thumbnail_url = element.select("div.itemtype_anime_poster img").first().attr("data-src")
anime.thumbnail_url = element.selectFirst("div.itemtype_anime_poster img")!!.attr("data-src")
return anime
}
@ -91,7 +91,7 @@ class XsMovie : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
anime.title = element.attr("title")
anime.thumbnail_url = element.select("div.itemtype_anime_poster img").first().attr("data-src")
anime.thumbnail_url = element.selectFirst("div.itemtype_anime_poster img")!!.attr("data-src")
return anime
}
@ -107,7 +107,7 @@ class XsMovie : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = document.select("div.inner--image img").first().attr("src")
anime.thumbnail_url = document.selectFirst("div.inner--image img")!!.attr("src")
anime.title = document.select("h1.post--inner-title").text()
anime.genre = document.select("ul.terms--and--metas > li:contains(تصنيفات الأنمي) > a").joinToString(", ") { it.text() }
anime.description = document.select("div.post--content--inner").text()

View File

@ -72,7 +72,7 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun animeDetailsParse(response: Response): SAnime {
val anime = json.decodeFromString(AnimeDetailsDto.serializer(), response.body!!.string())
val anime = json.decodeFromString(AnimeDetailsDto.serializer(), response.body.string())
val newAnime = SAnime.create().apply {
title = anime.name!!
setUrlWithoutDomain("$baseUrl/api/show/" + anime.url!!)
@ -95,7 +95,7 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
override fun popularAnimeParse(response: Response) = parseAnimePage(response)
private fun parseAnimePage(response: Response, singlePage: Boolean = false): AnimesPage {
val animes = json.decodeFromString(ListSerializer(AnimeDto.serializer()), response.body!!.string())
val animes = json.decodeFromString(ListSerializer(AnimeDto.serializer()), response.body.string())
if (animes.isEmpty()) return AnimesPage(emptyList(), false)
val animeList = mutableListOf<SAnime>()
for (anime in animes) {
@ -124,7 +124,7 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/api/show/airing/${page - 1}", refererHeader)
override fun latestUpdatesParse(response: Response): AnimesPage {
val releases = json.decodeFromString(ListSerializer(Release.serializer()), response.body!!.string()).toMutableList()
val releases = json.decodeFromString(ListSerializer(Release.serializer()), response.body.string()).toMutableList()
if (releases.isEmpty()) return AnimesPage(emptyList(), false)
val animeList = mutableListOf<SAnime>()
val releaseList = mutableListOf<Int>()
@ -153,13 +153,13 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = POST(
url = "$baseUrl/api/show/search",
headers = refererHeader,
body = "{\"search\":\"$query\"}".toRequestBody("application/json".toMediaType())
body = "{\"search\":\"$query\"}".toRequestBody("application/json".toMediaType()),
)
override fun searchAnimeParse(response: Response) = parseAnimePage(response, singlePage = true)
override fun episodeListParse(response: Response): List<SEpisode> {
val anime = json.decodeFromString(AnimeDetailsDto.serializer(), response.body!!.string())
val anime = json.decodeFromString(AnimeDetailsDto.serializer(), response.body.string())
if (anime.seasons.isNullOrEmpty()) return emptyList()
val episodeList = mutableListOf<SEpisode>()
val animeUrl = anime.url!!
@ -170,8 +170,8 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
val seasonPart = json.decodeFromString(
Season.serializer(),
client.newCall(
GET("$baseUrl/api/show/$animeUrl/${season.id!!}/$page")
).execute().body!!.string()
GET("$baseUrl/api/show/$animeUrl/${season.id!!}/$page"),
).execute().body.string(),
)
page++
episodes.addAll(seasonPart.episodes!!)
@ -189,7 +189,7 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun videoListParse(response: Response): List<Video> {
val streams = json.decodeFromString(Episode.serializer(), response.body!!.string()).streams
val streams = json.decodeFromString(Episode.serializer(), response.body.string()).streams
if (streams.isNullOrEmpty()) return emptyList()
val videoList = mutableListOf<Video>()
for (stream in streams) {
@ -236,20 +236,26 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
otherList.add(video)
}
}
} else otherList += this
} else {
otherList += this
}
val newList = mutableListOf<Video>()
var preferred = 0
for (video in hosterList) {
if (video.quality.contains(subPreference)) {
newList.add(preferred, video)
preferred++
} else newList.add(video)
} else {
newList.add(video)
}
}
for (video in otherList) {
if (video.quality.contains(subPreference)) {
newList.add(preferred, video)
preferred++
} else newList.add(video)
} else {
newList.add(video)
}
}
return newList
}

View File

@ -24,7 +24,7 @@ data class AnimeDto(
@SerialName("name")
val name: String? = null,
@SerialName("url")
val url: String? = null
val url: String? = null,
)
object IntSerializer : JsonTransformingSerializer<Int>(Int.serializer()) {
@ -57,7 +57,7 @@ data class AnimeDetailsDto(
@SerialName("url")
val url: String? = null,
@SerialName("seasons")
val seasons: List<Season>? = null
val seasons: List<Season>? = null,
)
@Serializable
@ -69,7 +69,7 @@ data class Season(
@SerialName("id")
val id: Int? = null,
@SerialName("length")
val length: Int? = null
val length: Int? = null,
)
@Serializable
@ -81,19 +81,19 @@ data class Episode(
@SerialName("name")
val name: String? = null,
@SerialName("streams")
val streams: List<Stream>? = null
val streams: List<Stream>? = null,
)
@Serializable
data class Release(
@SerialName("season")
val season: ShortSeason? = null
val season: ShortSeason? = null,
)
@Serializable
data class ShortSeason(
@SerialName("show")
val anime: AnimeDto? = null
val anime: AnimeDto? = null,
)
@Serializable
@ -103,13 +103,13 @@ data class Stream(
@SerialName("lang")
val lang: String? = null,
@SerialName("hoster")
val hoster: Hoster? = null
val hoster: Hoster? = null,
)
@Serializable
data class Hoster(
@SerialName("name")
val name: String? = null
val name: String? = null,
)
@Serializable
@ -117,5 +117,5 @@ data class Genre(
@SerialName("name")
val name: String? = null,
@SerialName("id")
val id: Int? = null
val id: Int? = null,
)

View File

@ -16,10 +16,10 @@ class StreamlareExtractor(private val client: OkHttpClient) {
POST(
"https://slwatch.co/api/video/stream/get",
body = "{\"id\":\"$id\"}"
.toRequestBody("application/json".toMediaType())
.toRequestBody("application/json".toMediaType()),
),
)
)
.execute().body!!.string()
.execute().body.string()
playlist.substringAfter("\"label\":\"").split("\"label\":\"").forEach {
val quality = it.substringAfter("\"label\":\"").substringBefore("\",") + ", ${stream.lang}"

View File

@ -70,8 +70,8 @@ class Aniking : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
GET(
"$baseUrl${anime.url}",
headers =
Headers.headersOf("user-agent", "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36")
)
Headers.headersOf("user-agent", "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36"),
),
)
.execute().request.headers
return GET("$baseUrl${anime.url}", headers = headers)
@ -283,8 +283,8 @@ class Aniking : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
GET(
"$baseUrl${anime.url}",
headers =
Headers.headersOf("user-agent", "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36")
)
Headers.headersOf("user-agent", "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36"),
),
)
.execute().request.headers
return GET("$baseUrl${anime.url}", headers = headers)

View File

@ -13,8 +13,8 @@ class StreamZExtractor(private val client: OkHttpClient) {
val videoUrl = client.newCall(
GET(
"https://get.streamz.tw/getlink-$dllpart.dll",
headers = Headers.headersOf("referer", "https://streamz.ws/", "accept", "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5", "range", "bytes=0-")
)
headers = Headers.headersOf("referer", "https://streamz.ws/", "accept", "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5", "range", "bytes=0-"),
),
)
.execute().request.url.toString()
return Video(url, quality, videoUrl)

View File

@ -73,7 +73,7 @@ class AnimeBase : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} else {
"data-dubbed=\"0\""
}
}][data-hoster=\"1\"], div.tab-content #specials div.panel button[data-dubbed=\"0\"][data-hoster=\"1\"]"
}][data-hoster=\"1\"], div.tab-content #specials div.panel button[data-dubbed=\"0\"][data-hoster=\"1\"]",
)
episodeElement.forEach {
val episode = episodeFromElement(it)

View File

@ -137,9 +137,16 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
"$baseUrl/files/captcha",
body = "cID=0&rT=1".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15"
)
)
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"application/json, text/javascript, */*; q=0.01",
"cache-control",
"max-age=15",
),
),
).execute().asJsoup()
val hashes = capfiles.toString().substringAfter("[").substringBefore("]").split(",")
@ -156,11 +163,15 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
GET(
"$baseUrl/files/captcha?cid=0&hash=$hash",
headers = Headers.headersOf(
"Referer", url.replace("#$id$epnum", ""),
"Accept", "image/avif,image/webp,*/*", "cache-control", "max-age=15"
)
)
).execute().body?.byteString()
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"image/avif,image/webp,*/*",
"cache-control",
"max-age=15",
),
),
).execute().body.byteString()
val size = png.toString()
.substringAfter("[size=").substringBefore(" hex")
pnglist.add("$size | $hash")
@ -194,9 +205,9 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
"$baseUrl/files/captcha",
body = "cID=0&pC=$maxhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*", "cache-control", "max-age=15"
)
)
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*", "cache-control", "max-age=15",
),
),
).execute()
val maxdoc = client.newCall(
POST(
@ -204,9 +215,9 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$maxhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
"Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15"
)
)
"Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15",
),
),
).execute().asJsoup().toString()
if (maxdoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
@ -277,19 +288,32 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
"$baseUrl/files/captcha",
body = "cID=0&pC=$minhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*"
)
)
"Origin",
baseUrl,
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"*/*",
),
),
).execute()
val mindoc = client.newCall(
POST(
"$baseUrl/ajax/captcha",
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$minhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
"Accept", "application/json, text/javascript, */*; q=0.01"
)
)
"Origin",
baseUrl,
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"application/json, text/javascript, */*; q=0.01",
),
),
).execute().asJsoup().toString()
if (mindoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
@ -367,9 +391,16 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
"$baseUrl/files/captcha",
body = "cID=0&rT=1".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15"
)
)
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"application/json, text/javascript, */*; q=0.01",
"cache-control",
"max-age=15",
),
),
).execute().asJsoup()
val hashes = capfiles.toString().substringAfter("[").substringBefore("]").split(",")
@ -386,11 +417,15 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
GET(
"$baseUrl/files/captcha?cid=0&hash=$hash",
headers = Headers.headersOf(
"Referer", url.replace("#$id$epnum", ""),
"Accept", "image/avif,image/webp,*/*", "cache-control", "max-age=15"
)
)
).execute().body?.byteString()
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"image/avif,image/webp,*/*",
"cache-control",
"max-age=15",
),
),
).execute().body.byteString()
val size = png.toString()
.substringAfter("[size=").substringBefore(" hex")
pnglist.add("$size | $hash")
@ -424,9 +459,9 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
"$baseUrl/files/captcha",
body = "cID=0&pC=$maxhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*", "cache-control", "max-age=15"
)
)
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*", "cache-control", "max-age=15",
),
),
).execute()
val maxdoc = client.newCall(
POST(
@ -434,9 +469,9 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$maxhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
"Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15"
)
)
"Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15",
),
),
).execute().asJsoup().toString()
if (maxdoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
@ -507,19 +542,32 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
"$baseUrl/files/captcha",
body = "cID=0&pC=$minhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*"
)
)
"Origin",
baseUrl,
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"*/*",
),
),
).execute()
val mindoc = client.newCall(
POST(
"$baseUrl/ajax/captcha",
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$minhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
"Accept", "application/json, text/javascript, */*; q=0.01"
)
)
"Origin",
baseUrl,
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"application/json, text/javascript, */*; q=0.01",
),
),
).execute().asJsoup().toString()
if (mindoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")

View File

@ -58,7 +58,7 @@ class DdosGuardInterceptor(private val client: OkHttpClient) : Interceptor {
return ddg2Cookie
}
val wellKnown = client.newCall(GET("https://check.ddos-guard.net/check.js"))
.execute().body!!.string()
.execute().body.string()
.substringAfter("'", "")
.substringBefore("'", "")
val checkUrl = "${url.scheme}://${url.host + wellKnown}"

Some files were not shown because too many files have changed in this diff Show More