[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:
parent
a5c2427e70
commit
62f45e094d
@ -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
|
||||
|
@ -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") {
|
||||
|
@ -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)
|
||||
|
@ -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" }
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
18
gradlew
vendored
@ -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
15
gradlew.bat
vendored
@ -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
|
||||
|
@ -12,10 +12,6 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.kotlin.stdlib)
|
||||
compileOnly(libs.okhttp)
|
||||
|
@ -12,13 +12,9 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.kotlin.stdlib)
|
||||
compileOnly(libs.okhttp)
|
||||
compileOnly(libs.aniyomi.lib)
|
||||
}
|
||||
// BUMPS: 0
|
||||
// BUMPS: 0
|
||||
|
@ -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) {
|
||||
|
@ -13,14 +13,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.kotlin.stdlib)
|
||||
compileOnly(libs.kotlin.json)
|
||||
compileOnly(libs.okhttp)
|
||||
compileOnly(libs.aniyomi.lib)
|
||||
}
|
||||
// BUMPS: 0
|
||||
// BUMPS: 0
|
||||
|
@ -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()) }
|
||||
|
@ -12,14 +12,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.kotlin.stdlib)
|
||||
compileOnly(libs.okhttp)
|
||||
compileOnly(libs.aniyomi.lib)
|
||||
compileOnly(libs.jsoup)
|
||||
}
|
||||
// BUMPS: 0
|
||||
// BUMPS: 0
|
||||
|
@ -12,14 +12,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.kotlin.stdlib)
|
||||
compileOnly(libs.kotlin.json)
|
||||
compileOnly(libs.okhttp)
|
||||
compileOnly(libs.aniyomi.lib)
|
||||
}
|
||||
// BUMPS: 0
|
||||
// BUMPS: 0
|
||||
|
@ -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=")
|
||||
|
@ -12,14 +12,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.kotlin.stdlib)
|
||||
compileOnly(libs.okhttp)
|
||||
compileOnly(libs.aniyomi.lib)
|
||||
compileOnly(libs.jsoup)
|
||||
}
|
||||
// BUMPS: 0
|
||||
// BUMPS: 0
|
||||
|
@ -12,14 +12,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.kotlin.stdlib)
|
||||
compileOnly(libs.okhttp)
|
||||
compileOnly(libs.aniyomi.lib)
|
||||
compileOnly(libs.jsoup)
|
||||
}
|
||||
// BUMPS: 0
|
||||
// BUMPS: 0
|
||||
|
@ -13,10 +13,6 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.kotlin.stdlib)
|
||||
compileOnly(libs.okhttp)
|
||||
|
@ -1,3 +1,19 @@
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
maven(url = "https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
include(":core")
|
||||
|
||||
File(rootDir, "lib").eachDir {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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",
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -11,7 +11,6 @@ ext {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly libs.bundles.coroutines
|
||||
implementation(project(':lib-okru-extractor'))
|
||||
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -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>()
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
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(
|
||||
Video(
|
||||
fileURL,
|
||||
label + qualitySuffix,
|
||||
fileURL,
|
||||
headers = videoHeaders
|
||||
)
|
||||
)
|
||||
else videoList.add(Video(fileURL, label + qualitySuffix, fileURL, headers = videoHeaders))
|
||||
} 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(
|
||||
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")
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
@ -10,8 +10,4 @@ ext {
|
||||
libVersion = '13'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly libs.bundles.coroutines
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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î",
|
||||
)
|
||||
}
|
||||
|
@ -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>()
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,4 @@ ext {
|
||||
libVersion = '13'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly libs.bundles.coroutines
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -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
|
||||
|
@ -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? {
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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¶ms=TV,SETI,MINISERIES,VARIETY,TALK,DOCUMENTARY&size=30"),
|
||||
Pair("Popular Anime", "/category?area=&category=1&order=count¶ms=COMIC&size=30"),
|
||||
Pair("Recent Anime", "/category?area=&category=1&order=up¶ms=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()
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,4 @@ ext {
|
||||
containsNsfw = false
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly libs.bundles.coroutines
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -72,7 +72,7 @@ open class Onepace(override val lang: String, override val name: String) : Confi
|
||||
}
|
||||
}
|
||||
},
|
||||
false
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ class OnepaceFactory : AnimeSourceFactory {
|
||||
override fun createSources(): List<AnimeSource> = listOf(
|
||||
OnepaceEspa(),
|
||||
OnepaceFr(),
|
||||
OnepaceEn()
|
||||
OnepaceEn(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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>>) :
|
||||
|
@ -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"),
|
||||
|
||||
)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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>()
|
||||
|
@ -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>()
|
||||
|
@ -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
|
||||
|
@ -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") ||
|
||||
|
@ -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>()
|
||||
|
@ -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")
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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>()
|
||||
|
@ -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"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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>()
|
||||
|
@ -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>()
|
||||
|
@ -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/"),
|
||||
|
||||
)
|
||||
|
||||
|
@ -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,8 +104,9 @@ 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"),
|
||||
|
||||
)
|
||||
|
||||
|
@ -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>()
|
||||
|
@ -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>>) :
|
||||
|
@ -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)) {
|
||||
|
@ -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"),
|
||||
)
|
||||
}
|
||||
|
@ -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,11 +147,12 @@ 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 =
|
||||
element.select("a div.BlockImageItem img").attr("data-src")
|
||||
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 {
|
||||
title
|
||||
}
|
||||
} else if (title.contains("فيلم")) {
|
||||
if (TitleRegex.ARABIC_MOVIE.containsMatchIn(title)) // افلام عربى
|
||||
if (TitleRegex.ARABIC_MOVIE.containsMatchIn(title)) {
|
||||
// افلام عربى
|
||||
TitleRegex.ARABIC_MOVIE.find(title)!!.groupValues[1].trim() + if (details) " ($movie)" else ""
|
||||
else if (TitleRegex.MOVIES.containsMatchIn(title))
|
||||
} 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("انمي"))
|
||||
} else {
|
||||
title
|
||||
}
|
||||
} else if (title.contains("انمي")) {
|
||||
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].removeSurrounding(" ال") + if (details) " (برنامج)" else ""
|
||||
else
|
||||
} else {
|
||||
title
|
||||
}
|
||||
return finalTitle.trim()
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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("\"}],")
|
||||
|
@ -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)
|
||||
|
@ -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("اختر", ""),
|
||||
|
@ -60,26 +60,30 @@ 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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,13 +76,16 @@ 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,22 +105,26 @@ 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
|
||||
|
@ -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("\"}],")
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,8 +136,9 @@ 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)
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
@ -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>>) :
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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}"
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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.")
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user