Merge branch 'main' into HentaiZM_Turc
6
.github/workflows/build_push.yml
vendored
|
@ -47,12 +47,13 @@ jobs:
|
|||
passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
git_user_signingkey: true
|
||||
git_commit_gpgsign: true
|
||||
|
||||
|
||||
# This step is going to commit, but this will not trigger another workflow.
|
||||
- name: Bump extensions that uses a modified lib
|
||||
if: steps.modified-libs.outputs.any_changed == 'true'
|
||||
run: |
|
||||
chmod +x ./.github/scripts/bump-versions.py ${{ steps.modified-libs.outputs.all_changed_files }}
|
||||
chmod +x ./.github/scripts/bump-versions.py
|
||||
./.github/scripts/bump-versions.py ${{ steps.modified-libs.outputs.all_changed_files }}
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@a494d935f4b56874c4a5a87d19af7afcf3a163d0 # v2
|
||||
|
@ -174,4 +175,3 @@ jobs:
|
|||
cwd: "./repo"
|
||||
committer_name: Kohi-den-Bot
|
||||
committer_email: 177773202+Kohi-den-Bot@users.noreply.github.com
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ plugins {
|
|||
id("lib-multisrc")
|
||||
}
|
||||
|
||||
baseVersionCode = 3
|
||||
baseVersionCode = 4
|
||||
|
||||
dependencies {
|
||||
api(project(":lib:megacloud-extractor"))
|
||||
|
|
|
@ -48,7 +48,7 @@ abstract class ZoroTheme(
|
|||
.clearOldHosts()
|
||||
}
|
||||
|
||||
private val docHeaders = headers.newBuilder().apply {
|
||||
protected val docHeaders = headers.newBuilder().apply {
|
||||
add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
|
||||
add("Host", baseUrl.toHttpUrl().host)
|
||||
add("Referer", "$baseUrl/")
|
||||
|
@ -217,7 +217,7 @@ abstract class ZoroTheme(
|
|||
|
||||
val serversDoc = response.parseAs<HtmlResponse>().getHtml()
|
||||
|
||||
val embedLinks = listOf("servers-sub", "servers-dub", "servers-mixed").map { type ->
|
||||
val embedLinks = listOf("servers-sub", "servers-dub", "servers-mixed", "servers-raw").map { type ->
|
||||
if (type !in typeSelection) return@map emptyList()
|
||||
|
||||
serversDoc.select("div.$type div.item").parallelMapNotNull {
|
||||
|
@ -236,6 +236,7 @@ abstract class ZoroTheme(
|
|||
}.flatten()
|
||||
|
||||
return embedLinks.parallelCatchingFlatMap(::extractVideo)
|
||||
.sort()
|
||||
}
|
||||
|
||||
abstract fun extractVideo(server: VideoData): List<Video>
|
||||
|
@ -332,8 +333,8 @@ abstract class ZoroTheme(
|
|||
private const val PREF_HOSTER_KEY = "hoster_selection"
|
||||
|
||||
private const val PREF_TYPE_TOGGLE_KEY = "type_selection"
|
||||
private val TYPES_ENTRIES = arrayOf("Sub", "Dub", "Mixed")
|
||||
private val TYPES_ENTRY_VALUES = arrayOf("servers-sub", "servers-dub", "servers-mixed")
|
||||
private val TYPES_ENTRIES = arrayOf("Sub", "Dub", "Mixed", "Raw")
|
||||
private val TYPES_ENTRY_VALUES = arrayOf("servers-sub", "servers-dub", "servers-mixed", "servers-raw")
|
||||
private val PREF_TYPES_TOGGLE_DEFAULT = TYPES_ENTRY_VALUES.toSet()
|
||||
}
|
||||
|
||||
|
|
7
lib/bangumi-scraper/build.gradle.kts
Normal file
|
@ -0,0 +1,7 @@
|
|||
plugins {
|
||||
id("lib-android")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.aniyomi.lib)
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
@file:UseSerializers(BoxItemSerializer::class)
|
||||
package eu.kanade.tachiyomi.lib.bangumiscraper
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.json.JsonDecoder
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
||||
@Serializable
|
||||
internal data class Images(
|
||||
val large: String,
|
||||
val common: String,
|
||||
val medium: String,
|
||||
val small: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
internal data class BoxItem(
|
||||
val key: String,
|
||||
val value: String,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Serializer(forClass = BoxItem::class)
|
||||
internal object BoxItemSerializer : KSerializer<BoxItem> {
|
||||
override fun deserialize(decoder: Decoder): BoxItem {
|
||||
val item = (decoder as JsonDecoder).decodeJsonElement().jsonObject
|
||||
val key = item["key"]!!.jsonPrimitive.content
|
||||
val value = (item["value"] as? JsonPrimitive)?.contentOrNull ?: ""
|
||||
return BoxItem(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
internal data class Subject(
|
||||
val name: String,
|
||||
@SerialName("name_cn")
|
||||
val nameCN: String,
|
||||
val summary: String,
|
||||
val images: Images,
|
||||
@SerialName("meta_tags")
|
||||
val metaTags: List<String>,
|
||||
@SerialName("infobox")
|
||||
val infoBox: List<BoxItem>,
|
||||
) {
|
||||
fun findAuthor(): String? {
|
||||
return findInfo("导演", "原作")
|
||||
}
|
||||
|
||||
fun findArtist(): String? {
|
||||
return findInfo("美术监督", "总作画监督", "动画制作")
|
||||
}
|
||||
|
||||
fun findInfo(vararg keys: String): String? {
|
||||
keys.forEach { key ->
|
||||
return infoBox.find { item ->
|
||||
item.key == key
|
||||
}?.value ?: return@forEach
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
internal data class SearchItem(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
@SerialName("name_cn")
|
||||
val nameCN: String,
|
||||
val summary: String,
|
||||
val images: Images,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
internal data class SearchResponse(val results: Int, val list: List<SearchItem>)
|
|
@ -0,0 +1,126 @@
|
|||
package eu.kanade.tachiyomi.lib.bangumiscraper
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.util.parseAs
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
||||
enum class BangumiSubjectType(val value: Int) {
|
||||
BOOK(1),
|
||||
ANIME(2),
|
||||
MUSIC(3),
|
||||
GAME(4),
|
||||
REAL(6),
|
||||
}
|
||||
|
||||
enum class BangumiFetchType {
|
||||
/**
|
||||
* Give cover and summary info.
|
||||
*/
|
||||
SHORT,
|
||||
|
||||
/**
|
||||
* Give all require info include genre and author info.
|
||||
*/
|
||||
ALL,
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class to fetch anime details from Bangumi
|
||||
*/
|
||||
object BangumiScraper {
|
||||
private const val SEARCH_URL = "https://api.bgm.tv/search/subject"
|
||||
private const val SUBJECTS_URL = "https://api.bgm.tv/v0/subjects"
|
||||
|
||||
/**
|
||||
* Fetch anime details info from Bangumi
|
||||
* @param fetchType check [BangumiFetchType] to get detail
|
||||
* @param subjectType check [BangumiSubjectType] to get detail
|
||||
* @param requestProducer used to custom request
|
||||
*/
|
||||
suspend fun fetchDetail(
|
||||
client: OkHttpClient,
|
||||
keyword: String,
|
||||
fetchType: BangumiFetchType = BangumiFetchType.SHORT,
|
||||
subjectType: BangumiSubjectType = BangumiSubjectType.ANIME,
|
||||
requestProducer: (url: HttpUrl) -> Request = { url -> GET(url) },
|
||||
): SAnime {
|
||||
val httpUrl = SEARCH_URL.toHttpUrl().newBuilder()
|
||||
.addPathSegment(keyword)
|
||||
.addQueryParameter(
|
||||
"responseGroup",
|
||||
if (fetchType == BangumiFetchType.ALL) {
|
||||
"small"
|
||||
} else {
|
||||
"medium"
|
||||
},
|
||||
)
|
||||
.addQueryParameter("type", "${subjectType.value}")
|
||||
.addQueryParameter("start", "0")
|
||||
.addQueryParameter("max_results", "1")
|
||||
.build()
|
||||
val searchResponse = client.newCall(requestProducer(httpUrl)).awaitSuccess()
|
||||
.checkErrorMessage().parseAs<SearchResponse>()
|
||||
return if (searchResponse.list.isEmpty()) {
|
||||
SAnime.create()
|
||||
} else {
|
||||
val item = searchResponse.list[0]
|
||||
if (fetchType == BangumiFetchType.ALL) {
|
||||
fetchSubject(client, "${item.id}", requestProducer)
|
||||
} else {
|
||||
SAnime.create().apply {
|
||||
thumbnail_url = item.images.large
|
||||
description = item.summary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchSubject(
|
||||
client: OkHttpClient,
|
||||
id: String,
|
||||
requestProducer: (url: HttpUrl) -> Request,
|
||||
): SAnime {
|
||||
val httpUrl = SUBJECTS_URL.toHttpUrl().newBuilder().addPathSegment(id).build()
|
||||
val subject = client.newCall(requestProducer(httpUrl)).awaitSuccess()
|
||||
.checkErrorMessage().parseAs<Subject>()
|
||||
return SAnime.create().apply {
|
||||
thumbnail_url = subject.images.large
|
||||
description = subject.summary
|
||||
genre = buildList {
|
||||
addAll(subject.metaTags)
|
||||
subject.findInfo("动画制作")?.let { add(it) }
|
||||
subject.findInfo("放送开始")?.let { add(it) }
|
||||
}.joinToString()
|
||||
author = subject.findAuthor()
|
||||
artist = subject.findArtist()
|
||||
if (subject.findInfo("播放结束") != null) {
|
||||
status = SAnime.COMPLETED
|
||||
} else if (subject.findInfo("放送开始") != null) {
|
||||
status = SAnime.ONGOING
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Response.checkErrorMessage(): String {
|
||||
val responseStr = body.string()
|
||||
val errorMessage =
|
||||
responseStr.parseAs<JsonElement>().jsonObject["error"]?.jsonPrimitive?.contentOrNull
|
||||
if (errorMessage != null) {
|
||||
throw BangumiScraperException(errorMessage)
|
||||
}
|
||||
return responseStr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package eu.kanade.tachiyomi.lib.bangumiscraper
|
||||
|
||||
class BangumiScraperException(message: String) : Exception(message)
|
|
@ -23,13 +23,14 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
|
|||
private val REGEX_SOURCES = Regex("""sources:\s*\[\{"file":"([^"]+)""")
|
||||
private val REGEX_FILE = Regex("""file: ?"([^"]+)"""")
|
||||
private val REGEX_SOURCE = Regex("""source = ?"([^"]+)"""")
|
||||
private val REGEX_SUBS = Regex("""\[(.*?)\](https?://[^\s,]+)""")
|
||||
private val REGEX_SUBS = Regex("""\{"file":"([^"]+)","label":"([^"]+)","kind":"captions","default":\w+\}""")
|
||||
private const val KEY_SOURCE = "https://raw.githubusercontent.com/Rowdy-Avocado/multi-keys/keys/index.html"
|
||||
}
|
||||
|
||||
fun videoFromUrl(url: String, referer: String, prefix: String = "Chillx - "): List<Video> {
|
||||
val newHeaders = headers.newBuilder()
|
||||
.set("Referer", "$referer/")
|
||||
.set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
|
||||
.set("Accept-Language", "en-US,en;q=0.5")
|
||||
.build()
|
||||
|
||||
|
@ -50,7 +51,7 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
|
|||
val subtitleList = buildList {
|
||||
val subtitles = REGEX_SUBS.findAll(decryptedScript)
|
||||
subtitles.forEach {
|
||||
add(Track(it.groupValues[2], decodeUnicodeEscape(it.groupValues[1])))
|
||||
add(Track(it.groupValues[1], decodeUnicodeEscape(it.groupValues[2])))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,5 +86,4 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
|
|||
@SerialName("chillx") val keys: List<String>
|
||||
)
|
||||
}
|
||||
|
||||
class ErrorLoadingException(message: String) : Exception(message)
|
||||
|
|
10
lib/fireplayer-extractor/build.gradle.kts
Normal file
|
@ -0,0 +1,10 @@
|
|||
plugins {
|
||||
id("lib-android")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("dev.datlag.jsunpacker:jsunpacker:1.0.1") {
|
||||
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
|
||||
}
|
||||
implementation(project(":lib:playlist-utils"))
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package eu.kanade.tachiyomi.lib.fireplayerextractor
|
||||
|
||||
import dev.datlag.jsunpacker.JsUnpacker
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class FireplayerExtractor(
|
||||
private val client: OkHttpClient,
|
||||
private val defaultHost: String? = null,
|
||||
) {
|
||||
fun videosFromUrl(
|
||||
url: String,
|
||||
videoNameGen: (String) -> String = { quality -> quality },
|
||||
videoHost: String? = null,
|
||||
): List<Video> {
|
||||
val host = videoHost ?: defaultHost ?: "https://${url.toHttpUrl().host}"
|
||||
|
||||
val headers = Headers.Builder()
|
||||
.set("X-Requested-With", "XMLHttpRequest")
|
||||
.set("Referer", host)
|
||||
.set("Origin", "https://${host.toHttpUrl().host}")
|
||||
.set("X-Requested-With", "XMLHttpRequest")
|
||||
.build()
|
||||
|
||||
var id = url.substringAfterLast("/")
|
||||
|
||||
if (id.length < 32) {
|
||||
val doc = client.newCall(GET(url, headers)).execute().asJsoup()
|
||||
|
||||
val script =
|
||||
doc.selectFirst("script:containsData(eval):containsData(p,a,c,k,e,d)")?.data()
|
||||
?.replace(Regex("[\\u00E0-\\u00FC]"), "-") // Fix a bug in JsUnpacker with accents
|
||||
?.let(JsUnpacker::unpackAndCombine)
|
||||
?: doc.selectFirst("script:containsData(FirePlayer)")?.data()
|
||||
|
||||
if (script?.contains("FirePlayer(") == true) {
|
||||
id = script.substringAfter("FirePlayer(\"").substringBefore('"')
|
||||
}
|
||||
}
|
||||
|
||||
val postUrl = "$host/player/index.php?data=$id&do=getVideo"
|
||||
val body = FormBody.Builder()
|
||||
.add("hash", id)
|
||||
.add("r", "")
|
||||
.build()
|
||||
|
||||
val masterUrl = client.newCall(POST(postUrl, headers, body = body)).execute()
|
||||
.body.string()
|
||||
.substringAfter("securedLink\":\"")
|
||||
.substringBefore('"')
|
||||
.replace("\\", "")
|
||||
|
||||
val playlistUtils = PlaylistUtils(client, headers)
|
||||
|
||||
return playlistUtils.extractFromHls(masterUrl, videoNameGen = videoNameGen)
|
||||
}
|
||||
}
|
1
lib/megacloud-extractor/src/main/assets/crypto-js.js
Normal file
606
lib/megacloud-extractor/src/main/assets/megacloud.decodedpng.js
Normal file
|
@ -0,0 +1,606 @@
|
|||
"use strict";
|
||||
// https://megacloud.tv/images/image.png?v=0.1.0
|
||||
window.decoded_png = new Uint8ClampedArray([
|
||||
246, 246, 246, 255, 226, 234, 236, 255, 113, 170, 187, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 0, 255, 255, 1, 60, 139, 163, 192, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254,
|
||||
130, 180, 196, 254, 242, 243, 244, 254, 246, 246, 246, 254, 243, 244, 245, 254, 105, 165, 184, 254, 60, 140,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60,
|
||||
140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164,
|
||||
254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140,
|
||||
164, 255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60,
|
||||
140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164,
|
||||
254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140,
|
||||
164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60,
|
||||
140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164,
|
||||
254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140,
|
||||
164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60,
|
||||
140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 132, 181, 196, 254, 243, 245, 245,
|
||||
254, 188, 212, 220, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 139, 164,
|
||||
255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60,
|
||||
140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164,
|
||||
254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140,
|
||||
164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164,
|
||||
254, 60, 139, 164, 254, 63, 142, 165, 254, 217, 230, 233, 254, 132, 181, 196, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 255, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 177,
|
||||
206, 216, 254, 119, 174, 190, 255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 164, 198, 210, 255, 119, 174, 190, 254, 60, 140, 164, 254,
|
||||
60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164,
|
||||
254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140,
|
||||
165, 254, 60, 140, 164, 254, 60, 140, 164, 255, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60,
|
||||
140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164,
|
||||
254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140,
|
||||
165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60,
|
||||
140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164,
|
||||
254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140,
|
||||
165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60,
|
||||
140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164,
|
||||
254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164, 255, 163,
|
||||
198, 210, 254, 119, 174, 190, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60,
|
||||
139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
|
||||
164, 254, 60, 139, 164, 255, 60, 139, 164, 254, 163, 198, 210, 254, 119, 174, 190, 254, 60, 139, 164, 254,
|
||||
60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 255, 60, 140, 164, 254, 60, 139, 164,
|
||||
254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140,
|
||||
164, 255, 60, 139, 164, 254, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163,
|
||||
198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 131, 180, 195,
|
||||
255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255,
|
||||
233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233,
|
||||
239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239,
|
||||
240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240,
|
||||
255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255,
|
||||
233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233,
|
||||
239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 218, 230, 234, 255, 143, 187,
|
||||
200, 255, 66, 143, 167, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60,
|
||||
140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164,
|
||||
255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139,
|
||||
164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255,
|
||||
163, 198, 210, 255, 119, 173, 190, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 139, 185,
|
||||
199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 223, 233, 236, 255, 136, 183, 197, 255, 69, 145, 168, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164,
|
||||
255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139,
|
||||
184, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 242, 243, 244, 255, 219, 231, 235, 255, 217,
|
||||
229, 233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229,
|
||||
233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229, 233,
|
||||
255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 97, 160, 180, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 164,
|
||||
255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190, 255, 60,
|
||||
140, 164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 102, 164, 183, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 163, 198, 210,
|
||||
255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164,
|
||||
255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60,
|
||||
140, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102,
|
||||
164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190,
|
||||
255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163,
|
||||
198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 184, 199,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60,
|
||||
139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119,
|
||||
174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 241,
|
||||
243, 244, 255, 227, 235, 238, 255, 243, 245, 245, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 230, 237, 239, 255, 239, 242, 243, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 233, 239, 241, 255, 235, 239, 241, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 239, 242, 243, 255, 230, 236, 239,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 139,
|
||||
185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 216, 230, 233, 255, 94, 160, 179, 255, 66, 143, 166, 255, 99, 161, 182, 255, 221, 232, 236, 255, 246,
|
||||
246, 246, 255, 245, 245, 245, 255, 127, 178, 194, 255, 68, 144, 168, 255, 79, 150, 173, 255, 187, 213, 220,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 154, 193, 205, 255, 73, 147, 170, 255, 72, 146, 169,
|
||||
255, 156, 194, 206, 255, 246, 246, 246, 255, 246, 246, 246, 255, 208, 223, 229, 255, 85, 154, 176, 255, 65,
|
||||
143, 166, 255, 112, 169, 187, 255, 236, 240, 242, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
140, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164,
|
||||
255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 137, 184, 197, 255, 60, 140, 165, 255, 60, 140, 164, 255, 60, 140, 164, 255, 88, 156, 177,
|
||||
255, 210, 226, 230, 255, 133, 181, 196, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 85,
|
||||
154, 175, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 239, 242, 243, 255, 64, 143, 166, 255, 60, 140, 165, 255, 60,
|
||||
140, 164, 255, 60, 140, 164, 255, 146, 189, 202, 255, 203, 221, 227, 255, 70, 145, 168, 255, 60, 140, 164,
|
||||
255, 60, 140, 165, 255, 60, 140, 164, 255, 165, 200, 210, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 140, 164, 255, 60, 140, 164, 255,
|
||||
60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 137, 183, 198, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 67, 144, 167, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 86,
|
||||
154, 175, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 237, 241, 242, 255, 64, 142, 166, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 64, 141, 166, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 164, 200, 211, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 163, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255,
|
||||
60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 139, 184, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 218, 230, 234, 255, 83, 153, 174, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 67, 144, 167, 255,
|
||||
174, 206, 215, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 153, 193, 205, 255, 60, 139, 164, 255,
|
||||
60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 101, 163, 182, 255, 226, 235, 238, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255,
|
||||
60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139,
|
||||
164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 213, 227, 231, 255, 85, 154, 175, 255, 60, 140, 164, 255, 60, 140,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 64, 142, 165, 255, 178, 208, 216, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 242, 243, 244, 255, 148, 189, 203,
|
||||
255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140,
|
||||
164, 255, 97, 160, 180, 255, 231, 238, 240, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 244, 245, 245, 255, 112, 169, 187, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 67, 143, 167, 255, 229, 236, 239,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 200, 220,
|
||||
226, 255, 61, 141, 165, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 140, 186, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 235, 240, 242, 255, 133, 181, 196, 255, 60, 139, 164, 255, 60, 139, 164, 255,
|
||||
60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 86, 155, 176,
|
||||
255, 221, 232, 236, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 203, 221, 227, 255, 70, 145, 168,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 150, 191, 204, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60,
|
||||
139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 165, 199, 210, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 115, 171, 188, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 245, 245, 245, 255, 83, 153, 174, 255, 60, 140, 164,
|
||||
255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140,
|
||||
164, 255, 60, 140, 164, 255, 61, 141, 165, 255, 192, 215, 222, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60,
|
||||
140, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 131, 181, 196, 255, 60, 140, 165, 255, 60, 140, 164, 255, 60, 140, 164, 255, 67,
|
||||
143, 168, 255, 140, 185, 200, 255, 83, 153, 174, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164,
|
||||
255, 79, 151, 173, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 231, 238, 240, 255, 60, 140, 164, 255, 60, 140, 165, 255,
|
||||
60, 140, 164, 255, 60, 140, 164, 255, 89, 156, 177, 255, 124, 176, 192, 255, 60, 140, 164, 255, 60, 140,
|
||||
164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 158, 195, 207, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 140, 164, 255, 60, 140, 164,
|
||||
255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 181, 208, 217, 255, 68, 144, 167, 255, 60, 139, 164, 255, 64, 141, 166, 255, 174,
|
||||
205, 214, 255, 245, 246, 246, 255, 209, 224, 229, 255, 81, 151, 173, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 129, 179, 194, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 245, 246, 246, 255, 104, 165, 184, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 96, 160, 180, 255, 231, 237, 239, 255, 242, 244, 244, 255, 140, 185, 200, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 68, 144, 168, 255, 205, 222, 228, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 163, 183, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255,
|
||||
60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 192, 215, 222, 255, 155, 194, 206, 255, 194, 216, 223, 255,
|
||||
245, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 220, 231, 235, 255, 160, 197, 208, 255, 177,
|
||||
207, 215, 255, 238, 242, 242, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 227, 236, 237, 255,
|
||||
169, 202, 212, 255, 169, 202, 212, 255, 226, 234, 237, 255, 246, 246, 246, 255, 246, 246, 246, 255, 242,
|
||||
244, 244, 255, 183, 210, 219, 255, 155, 194, 206, 255, 205, 222, 228, 255, 245, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164,
|
||||
255, 60, 140, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119,
|
||||
173, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139,
|
||||
184, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 164,
|
||||
255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 215, 229, 233, 255,
|
||||
222, 233, 236, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 223, 233, 236, 255, 213, 227, 232, 255, 245, 245, 245, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60,
|
||||
140, 164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 242, 244, 244, 255, 111, 169,
|
||||
186, 255, 60, 140, 165, 255, 64, 142, 166, 255, 132, 181, 196, 255, 216, 229, 233, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 222, 233, 236, 255, 164, 199, 210, 255, 97, 161, 180, 255, 73, 147, 170, 255, 106, 166,
|
||||
184, 255, 178, 207, 216, 255, 235, 239, 242, 255, 246, 246, 246, 255, 241, 243, 244, 255, 201, 221, 226,
|
||||
255, 117, 172, 189, 255, 61, 141, 165, 255, 61, 140, 164, 255, 138, 185, 198, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
102, 164, 183, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173,
|
||||
190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 196, 217, 224,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 74, 147, 171, 255, 132,
|
||||
181, 195, 255, 150, 191, 204, 255, 77, 149, 172, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 95, 160, 180, 255, 158, 196, 207, 255, 120, 173, 191, 255, 61,
|
||||
140, 165, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 63, 141, 165, 255, 230, 237, 239,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 102, 163, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255,
|
||||
119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 202,
|
||||
221, 226, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 67, 144, 167, 255, 236, 241, 242,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255,
|
||||
119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 139, 185, 199, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 136, 183, 198, 255, 64, 142, 166, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 165,
|
||||
255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 60, 140,
|
||||
164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60,
|
||||
140, 164, 255, 60, 140, 164, 255, 60, 140, 165, 255, 71, 145, 168, 255, 153, 192, 205, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 102, 164, 183, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255,
|
||||
119, 173, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 206, 224, 229, 255, 118, 172, 190, 255, 64, 141, 165, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 95, 160, 180, 255, 143,
|
||||
187, 200, 255, 87, 155, 176, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 63, 141, 165, 255, 134, 181, 196, 255, 225, 235, 237, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 102, 163, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198,
|
||||
210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 184, 199, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 212, 227, 231, 255,
|
||||
129, 179, 194, 255, 89, 157, 177, 255, 85, 154, 175, 255, 111, 169, 187, 255, 183, 210, 219, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 243, 245, 245, 255, 172, 203, 213, 255, 106, 165, 184, 255, 83, 153, 174, 255,
|
||||
93, 159, 179, 255, 142, 187, 201, 255, 221, 231, 235, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 139,
|
||||
185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 244, 245, 246, 255, 244, 245, 245, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 243, 244, 245, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 140, 164,
|
||||
255, 60, 140, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183,
|
||||
255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 102, 163, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210,
|
||||
255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
|
||||
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
|
||||
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
|
||||
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
|
||||
255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 163, 198, 210, 255, 129, 180, 195, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164,
|
||||
255, 93, 158, 178, 255, 158, 196, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159,
|
||||
197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197,
|
||||
207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207,
|
||||
255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255,
|
||||
159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159,
|
||||
197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197,
|
||||
207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207,
|
||||
255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255,
|
||||
159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159,
|
||||
197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197,
|
||||
207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 156, 194, 207, 255, 69, 144, 168, 255, 60, 140, 164, 255,
|
||||
60, 140, 164, 255, 60, 140, 164, 255, 169, 202, 212, 255, 176, 206, 215, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 203, 221, 227,
|
||||
255, 234, 239, 240, 255, 84, 153, 175, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 107, 167, 185, 255, 241, 243, 244, 255, 246, 246, 246, 255, 203, 221, 227, 255, 85,
|
||||
154, 176, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164,
|
||||
255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140,
|
||||
164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139,
|
||||
164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60,
|
||||
140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164,
|
||||
255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140,
|
||||
164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
|
||||
140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164,
|
||||
255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 92, 157, 179, 255, 222, 232, 236, 255, 246,
|
||||
246, 246, 255,
|
||||
]);
|
714
lib/megacloud-extractor/src/main/assets/megacloud.getsrcs.js
Normal file
|
@ -29,6 +29,7 @@ class MegaCloudExtractor(
|
|||
private val json: Json by injectLazy()
|
||||
|
||||
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
|
||||
private val webViewResolver by lazy { WebViewResolver(headers) }
|
||||
|
||||
private val cacheControl = CacheControl.Builder().noStore().build()
|
||||
private val noCacheClient = client.newBuilder()
|
||||
|
@ -141,10 +142,16 @@ class MegaCloudExtractor(
|
|||
|
||||
private fun getVideoDto(url: String): VideoDto {
|
||||
val type = if (url.startsWith("https://megacloud.tv")) 0 else 1
|
||||
|
||||
val keyType = SOURCES_KEY[type]
|
||||
|
||||
val id = url.substringAfter(SOURCES_SPLITTER[type], "")
|
||||
.substringBefore("?", "").ifEmpty { throw Exception("I HATE THE ANTICHRIST") }
|
||||
|
||||
if (type == 0) {
|
||||
return webViewResolver.getSources(id)!!
|
||||
}
|
||||
|
||||
val srcRes = client.newCall(GET(SERVER_URL[type] + SOURCES_URL[type] + id))
|
||||
.execute()
|
||||
.body.string()
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package eu.kanade.tachiyomi.lib.megacloudextractor
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.webkit.ConsoleMessage
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import eu.kanade.tachiyomi.lib.megacloudextractor.MegaCloudExtractor.VideoDto
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Headers
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class WebViewResolver(private val globalHeaders: Headers) {
|
||||
private val context: Application by injectLazy()
|
||||
private val handler by lazy { Handler(Looper.getMainLooper()) }
|
||||
private val json: Json by injectLazy()
|
||||
private val tag by lazy { javaClass.simpleName }
|
||||
|
||||
class JsInterface(private val latch: CountDownLatch) {
|
||||
var result: String? = null
|
||||
|
||||
@JavascriptInterface
|
||||
fun setResponse(response: String) {
|
||||
Log.d("WebViewResolver", "script result: $response")
|
||||
result = response
|
||||
latch.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
fun getJsContent(file: String): String {
|
||||
return javaClass.getResource(file)!!.readText()
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
fun getSources(xrax: String): VideoDto? {
|
||||
val latch = CountDownLatch(1)
|
||||
var webView: WebView? = null
|
||||
val jsi = JsInterface(latch)
|
||||
|
||||
handler.post {
|
||||
val webview = WebView(context)
|
||||
webView = webview
|
||||
with(webview.settings) {
|
||||
javaScriptEnabled = true
|
||||
domStorageEnabled = true
|
||||
databaseEnabled = true
|
||||
useWideViewPort = false
|
||||
loadWithOverviewMode = false
|
||||
userAgentString = globalHeaders["User-Agent"]
|
||||
}
|
||||
|
||||
webview.addJavascriptInterface(jsi, "jsinterface")
|
||||
|
||||
webview.webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
Log.d(tag, "onPageFinished $url")
|
||||
super.onPageFinished(view, url)
|
||||
|
||||
Log.d(tag, "injecting scripts")
|
||||
view?.evaluateJavascript(getJsContent("/assets/crypto-js.js")) {}
|
||||
view?.evaluateJavascript(getJsContent("/assets/megacloud.decodedpng.js")) {}
|
||||
view?.evaluateJavascript(getJsContent("/assets/megacloud.getsrcs.js")) {}
|
||||
|
||||
Log.d(tag, "running script")
|
||||
view?.evaluateJavascript(
|
||||
"getSources(\"${xrax}\")" +
|
||||
".then( s => jsinterface.setResponse( JSON.stringify(s) ) )",
|
||||
) {}
|
||||
}
|
||||
}
|
||||
|
||||
webview.webChromeClient = object : WebChromeClient() {
|
||||
override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
|
||||
Log.d(
|
||||
tag,
|
||||
"Chrome: [${consoleMessage?.messageLevel()}]" +
|
||||
"${consoleMessage?.message()}" +
|
||||
" at ${consoleMessage?.lineNumber()}" +
|
||||
" in ${consoleMessage?.sourceId()}",
|
||||
)
|
||||
return super.onConsoleMessage(consoleMessage)
|
||||
}
|
||||
}
|
||||
|
||||
val headers = mapOf("X-Requested-With" to "org.lineageos.jelly")
|
||||
|
||||
webView?.loadUrl("https://megacloud.tv/about", headers)
|
||||
}
|
||||
|
||||
latch.await(TIMEOUT_SEC, TimeUnit.SECONDS)
|
||||
|
||||
handler.post {
|
||||
webView?.stopLoading()
|
||||
webView?.destroy()
|
||||
webView = null
|
||||
}
|
||||
|
||||
return jsi.result?.let { json.decodeFromString<VideoDto>(it) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TIMEOUT_SEC: Long = 30
|
||||
}
|
||||
}
|
|
@ -17,7 +17,12 @@ class MixDropExtractor(private val client: OkHttpClient) {
|
|||
externalSubs: List<Track> = emptyList(),
|
||||
referer: String = DEFAULT_REFERER,
|
||||
): List<Video> {
|
||||
val headers = Headers.headersOf("Referer", referer)
|
||||
val headers = Headers.headersOf(
|
||||
"Referer",
|
||||
referer,
|
||||
"User-Agent",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
|
||||
)
|
||||
val doc = client.newCall(GET(url, headers)).execute().asJsoup()
|
||||
val unpacked = doc.selectFirst("script:containsData(eval):containsData(MDCore)")
|
||||
?.data()
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
ext {
|
||||
extName = 'Hikari'
|
||||
extClass = '.Hikari'
|
||||
extVersionCode = 8
|
||||
extVersionCode = 12
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:filemoon-extractor'))
|
||||
implementation(project(':lib:vidhide-extractor'))
|
||||
implementation(project(':lib:chillx-extractor'))
|
||||
implementation(project(':lib:filemoon-extractor'))
|
||||
implementation(project(':lib:streamwish-extractor'))
|
||||
implementation(project(':lib:vidhide-extractor'))
|
||||
}
|
|
@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.animesource.model.Video
|
|||
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
||||
import eu.kanade.tachiyomi.lib.chillxextractor.ChillxExtractor
|
||||
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
||||
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
||||
import eu.kanade.tachiyomi.lib.vidhideextractor.VidHideExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
|
@ -220,6 +221,7 @@ class Hikari : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
|
||||
private val vidHideExtractor by lazy { VidHideExtractor(client, headers) }
|
||||
private val chillxExtractor by lazy { ChillxExtractor(client, headers) }
|
||||
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
|
||||
private val embedRegex = Regex("""getEmbed\(\s*(\d+)\s*,\s*(\d+)\s*,\s*'(\d+)'""")
|
||||
|
||||
override fun videoListRequest(episode: SEpisode): Request {
|
||||
|
@ -328,17 +330,19 @@ class Hikari : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
}.filter { it.first.isNotEmpty() }
|
||||
}
|
||||
|
||||
val embedUrls = sdEmbedUrls.ifEmpty {
|
||||
subEmbedUrls + dubEmbedUrls
|
||||
}
|
||||
return embedUrls.parallelCatchingFlatMapBlocking {
|
||||
return sdEmbedUrls.parallelCatchingFlatMapBlocking {
|
||||
getVideosFromEmbed(it.first, it.second)
|
||||
}.ifEmpty {
|
||||
(subEmbedUrls + dubEmbedUrls).parallelCatchingFlatMapBlocking {
|
||||
getVideosFromEmbed(it.first, it.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getVideosFromEmbed(embedUrl: String, name: String): List<Video> = when {
|
||||
name.contains("vidhide", true) -> vidHideExtractor.videosFromUrl(embedUrl, videoNameGen = { s -> "$name - $s" })
|
||||
embedUrl.contains("filemoon", true) -> filemoonExtractor.videosFromUrl(embedUrl, prefix = "$name - ", headers = headers)
|
||||
name.contains("streamwish", true) -> streamwishExtractor.videosFromUrl(embedUrl, prefix = "$name - ")
|
||||
else -> chillxExtractor.videoFromUrl(embedUrl, referer = baseUrl, prefix = "$name - ")
|
||||
}
|
||||
|
||||
|
|
8
src/all/jable/build.gradle
Normal file
|
@ -0,0 +1,8 @@
|
|||
ext {
|
||||
extName = 'Jable'
|
||||
extClass = '.JableFactory'
|
||||
extVersionCode = 2
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
BIN
src/all/jable/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
src/all/jable/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/all/jable/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
src/all/jable/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
src/all/jable/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
|
@ -0,0 +1,253 @@
|
|||
package eu.kanade.tachiyomi.animeextension.all.jable
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeUpdateStrategy
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class Jable(override val lang: String) : AnimeHttpSource() {
|
||||
override val baseUrl: String
|
||||
get() = "https://jable.tv"
|
||||
override val name: String
|
||||
get() = "Jable"
|
||||
override val supportsLatest: Boolean
|
||||
get() = true
|
||||
|
||||
private val preferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
private val json by injectLazy<Json>()
|
||||
private var tagsUpdated = false
|
||||
|
||||
override fun animeDetailsRequest(anime: SAnime): Request {
|
||||
return GET("$baseUrl${anime.url}?lang=${lang.toRequestLang()}", headers)
|
||||
}
|
||||
|
||||
override fun animeDetailsParse(response: Response): SAnime {
|
||||
val doc = response.asJsoup()
|
||||
return SAnime.create().apply {
|
||||
val info = doc.select(".info-header")
|
||||
title = info.select(".header-left h4").text()
|
||||
author = info.select(".header-left .model")
|
||||
.joinToString { it.select("span[title]").attr("title") }
|
||||
genre = doc.select(".tags a").joinToString { it.text() }
|
||||
update_strategy = AnimeUpdateStrategy.ONLY_FETCH_ONCE
|
||||
status = SAnime.COMPLETED
|
||||
description = info.select(".header-right").text()
|
||||
}
|
||||
}
|
||||
|
||||
override fun episodeListParse(response: Response) = throw UnsupportedOperationException()
|
||||
|
||||
override suspend fun getEpisodeList(anime: SAnime): List<SEpisode> {
|
||||
return listOf(
|
||||
SEpisode.create().apply {
|
||||
name = "Episode"
|
||||
url = anime.url
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val doc = response.asJsoup()
|
||||
val videoUrl = doc.selectFirst("script:containsData(var hlsUrl)")!!.data()
|
||||
.substringAfter("var hlsUrl = '").substringBefore("'")
|
||||
return listOf(Video(videoUrl, "Default", videoUrl))
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response): AnimesPage {
|
||||
val doc = response.asJsoup()
|
||||
if (!tagsUpdated) {
|
||||
tagsUpdated = preferences.saveTags(
|
||||
doc.select("a.tag").associate {
|
||||
it.ownText() to it.attr("href").substringAfter(baseUrl).removePrefix("/")
|
||||
.removeSuffix("/")
|
||||
},
|
||||
)
|
||||
}
|
||||
return AnimesPage(
|
||||
doc.select(".container .video-img-box").map {
|
||||
SAnime.create().apply {
|
||||
setUrlWithoutDomain(it.select(".img-box a").attr("href"))
|
||||
thumbnail_url = it.select(".img-box img").attr("data-src")
|
||||
title = it.select(".detail .title").text()
|
||||
}
|
||||
},
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) =
|
||||
searchRequest("latest-updates", page, latestFilter)
|
||||
|
||||
override fun popularAnimeParse(response: Response): AnimesPage = latestUpdatesParse(response)
|
||||
|
||||
override fun popularAnimeRequest(page: Int) =
|
||||
searchRequest("hot", page, popularFilter)
|
||||
|
||||
override fun searchAnimeParse(response: Response) = latestUpdatesParse(response)
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
return if (query.isNotEmpty()) {
|
||||
searchRequest(
|
||||
"search/$query",
|
||||
page,
|
||||
AnimeFilterList(filters.list + defaultSearchFunctionFilter),
|
||||
query = query,
|
||||
)
|
||||
} else {
|
||||
val path = filters.list.filterIsInstance<TagFilter>()
|
||||
.firstOrNull()?.selected?.second?.takeUnless { it.isEmpty() } ?: "hot"
|
||||
searchRequest(path, page, AnimeFilterList(filters.list + commonVideoListFuncFilter))
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchRequest(
|
||||
path: String,
|
||||
page: Int,
|
||||
filters: AnimeFilterList = AnimeFilterList(),
|
||||
query: String = "",
|
||||
): Request {
|
||||
val urlBuilder = baseUrl.toHttpUrl().newBuilder()
|
||||
.addPathSegments("$path/")
|
||||
.addQueryParameter("lang", lang.toRequestLang())
|
||||
if (tagsUpdated) {
|
||||
// load whole page for update filter tags info
|
||||
urlBuilder.addQueryParameter("mode", "async")
|
||||
}
|
||||
filters.list.forEach {
|
||||
when (it) {
|
||||
is BlockFunctionFilter -> {
|
||||
urlBuilder.addQueryParameter("function", it.selected.functionName)
|
||||
.addQueryParameter("block_id", it.selected.blockId)
|
||||
}
|
||||
|
||||
is SortFilter -> {
|
||||
if (it.selected.second.isNotEmpty()) {
|
||||
urlBuilder.addQueryParameter("sort_by", it.selected.second)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
if (query.isNotEmpty()) {
|
||||
urlBuilder.addQueryParameter("q", query)
|
||||
}
|
||||
urlBuilder.addQueryParameter("from", "%02d".format(page))
|
||||
.addQueryParameter("_", System.currentTimeMillis().toString())
|
||||
return GET(urlBuilder.build())
|
||||
}
|
||||
|
||||
override fun getFilterList(): AnimeFilterList {
|
||||
return AnimeFilterList(
|
||||
SortFilter(
|
||||
intl.filterPopularSortTitle,
|
||||
arrayOf(
|
||||
"" to "",
|
||||
intl.hotMonth to "video_viewed_month",
|
||||
intl.hotWeek to "video_viewed_week",
|
||||
intl.hotDay to "video_viewed_today",
|
||||
intl.hotAll to "video_viewed",
|
||||
),
|
||||
),
|
||||
TagFilter(
|
||||
intl.filterTagTitle,
|
||||
buildList {
|
||||
add("" to "")
|
||||
preferences.getTags()?.forEach {
|
||||
add(it.key to it.value)
|
||||
}
|
||||
}.toTypedArray(),
|
||||
),
|
||||
SortFilter(
|
||||
intl.filterTagsSortTitle,
|
||||
arrayOf(
|
||||
"" to "",
|
||||
intl.sortLatestUpdate to "post_date",
|
||||
intl.sortMostView to "video_viewed",
|
||||
intl.sortMostFavorite to "most_favourited",
|
||||
intl.sortRecentBest to "post_date_and_popularity",
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun SharedPreferences.getTags(): Map<String, String>? {
|
||||
val savedStr = getString("${lang}_$PREF_KEY_TAGS", null)
|
||||
if (savedStr.isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
return json.decodeFromString<Map<String, String>>(savedStr)
|
||||
}
|
||||
|
||||
private fun SharedPreferences.saveTags(tags: Map<String, String>): Boolean {
|
||||
if (tags.isNotEmpty()) {
|
||||
edit().putString("${lang}_$PREF_KEY_TAGS", json.encodeToString(tags)).apply()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun String.toRequestLang(): String {
|
||||
if (this == "ja") return "jp"
|
||||
return this
|
||||
}
|
||||
|
||||
private val intl by lazy {
|
||||
JableIntl(lang)
|
||||
}
|
||||
|
||||
private val commonVideoListFuncFilter by lazy {
|
||||
BlockFunctionFilter(
|
||||
intl.popular,
|
||||
arrayOf(BlockFunction(intl.popular, "list_videos_common_videos_list")),
|
||||
)
|
||||
}
|
||||
|
||||
private val defaultSearchFunctionFilter by lazy {
|
||||
BlockFunctionFilter("", arrayOf(BlockFunction("", "list_videos_videos_list_search_result")))
|
||||
}
|
||||
|
||||
private val popularFilter by lazy {
|
||||
AnimeFilterList(
|
||||
commonVideoListFuncFilter,
|
||||
SortFilter(
|
||||
intl.hotWeek,
|
||||
arrayOf(intl.hotWeek to "video_viewed_week"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private val latestFilter by lazy {
|
||||
AnimeFilterList(
|
||||
BlockFunctionFilter(
|
||||
intl.latestUpdate,
|
||||
arrayOf(BlockFunction(intl.latestUpdate, "list_videos_latest_videos_list")),
|
||||
),
|
||||
SortFilter(
|
||||
intl.sortLatestUpdate,
|
||||
arrayOf(intl.sortLatestUpdate to "post_date"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PREF_KEY_TAGS = "pref_key_tags"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package eu.kanade.tachiyomi.animeextension.all.jable
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.AnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.AnimeSourceFactory
|
||||
|
||||
class JableFactory : AnimeSourceFactory {
|
||||
override fun createSources(): List<AnimeSource> {
|
||||
return listOf(
|
||||
Jable("zh"),
|
||||
Jable("en"),
|
||||
Jable("ja"),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package eu.kanade.tachiyomi.animeextension.all.jable
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
|
||||
data class BlockFunction(
|
||||
val name: String,
|
||||
val blockId: String,
|
||||
val functionName: String = "get_block",
|
||||
)
|
||||
|
||||
class BlockFunctionFilter(name: String, private val functions: Array<BlockFunction>) :
|
||||
AnimeFilter.Select<String>(name, functions.map { it.name }.toTypedArray()) {
|
||||
val selected
|
||||
get() = functions[state]
|
||||
}
|
||||
|
||||
open class UriPartFilter(name: String, private val pairs: Array<Pair<String, String>>) :
|
||||
AnimeFilter.Select<String>(name, pairs.map { it.first }.toTypedArray()) {
|
||||
val selected
|
||||
get() = pairs[state]
|
||||
}
|
||||
|
||||
class SortFilter(name: String, pairs: Array<Pair<String, String>>) : UriPartFilter(name, pairs)
|
||||
|
||||
class TagFilter(name: String, pairs: Array<Pair<String, String>>) : UriPartFilter(name, pairs)
|
|
@ -0,0 +1,76 @@
|
|||
package eu.kanade.tachiyomi.animeextension.all.jable
|
||||
|
||||
internal interface Intl {
|
||||
val popular: String
|
||||
val latestUpdate: String
|
||||
val sortLatestUpdate: String
|
||||
val sortMostView: String
|
||||
val sortMostFavorite: String
|
||||
val sortRecentBest: String
|
||||
val hotDay: String
|
||||
val hotWeek: String
|
||||
val hotMonth: String
|
||||
val hotAll: String
|
||||
val filterPopularSortTitle: String
|
||||
val filterTagsSortTitle: String
|
||||
val filterTagTitle: String
|
||||
}
|
||||
|
||||
internal class JableIntl private constructor(delegate: Intl) : Intl by delegate {
|
||||
constructor(lang: String) : this(
|
||||
when (lang) {
|
||||
"zh" -> ZH()
|
||||
"ja" -> JA()
|
||||
"en" -> EN()
|
||||
else -> ZH()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
internal class ZH : Intl {
|
||||
override val popular: String = "熱度優先"
|
||||
override val latestUpdate: String = "新片優先"
|
||||
override val sortLatestUpdate: String = "最近更新"
|
||||
override val sortMostView: String = "最多觀看"
|
||||
override val sortMostFavorite: String = "最高收藏"
|
||||
override val sortRecentBest: String = "近期最佳"
|
||||
override val hotDay: String = "今日熱門"
|
||||
override val hotWeek: String = "本周熱門"
|
||||
override val hotMonth: String = "本月熱門"
|
||||
override val hotAll: String = "所有時間"
|
||||
override val filterPopularSortTitle: String = "熱門排序"
|
||||
override val filterTagsSortTitle: String = "通用排序"
|
||||
override val filterTagTitle: String = "標籤"
|
||||
}
|
||||
|
||||
internal class JA : Intl {
|
||||
override val popular: String = "人気優先"
|
||||
override val latestUpdate: String = "新作優先"
|
||||
override val sortLatestUpdate: String = "最近更新"
|
||||
override val sortMostView: String = "最も見ら"
|
||||
override val sortMostFavorite: String = "最もお気に入"
|
||||
override val sortRecentBest: String = "最近ベスト"
|
||||
override val hotDay: String = "今日のヒット"
|
||||
override val hotWeek: String = "今週のヒット"
|
||||
override val hotMonth: String = "今月のヒット"
|
||||
override val hotAll: String = "全ての時間"
|
||||
override val filterPopularSortTitle: String = "人気ソート"
|
||||
override val filterTagsSortTitle: String = "一般ソート"
|
||||
override val filterTagTitle: String = "タグ"
|
||||
}
|
||||
|
||||
internal class EN : Intl {
|
||||
override val popular: String = "Hot"
|
||||
override val latestUpdate: String = "Newest"
|
||||
override val sortLatestUpdate: String = "Recent Update"
|
||||
override val sortMostView: String = "Most Viewed"
|
||||
override val sortMostFavorite: String = "Most Favorite"
|
||||
override val sortRecentBest: String = "Best Recently"
|
||||
override val hotDay: String = "Today"
|
||||
override val hotWeek: String = "This Week"
|
||||
override val hotMonth: String = "This Month"
|
||||
override val hotAll: String = "All Time"
|
||||
override val filterPopularSortTitle: String = "Popular Sorting"
|
||||
override val filterTagsSortTitle: String = "General Sorting"
|
||||
override val filterTagTitle: String = "Tag"
|
||||
}
|
16
src/all/javgg/build.gradle
Normal file
|
@ -0,0 +1,16 @@
|
|||
ext {
|
||||
extName = 'JavGG'
|
||||
extClass = '.Javgg'
|
||||
extVersionCode = 1
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:okru-extractor'))
|
||||
implementation(project(':lib:streamwish-extractor'))
|
||||
implementation(project(':lib:streamhidevid-extractor'))
|
||||
implementation(project(':lib:voe-extractor'))
|
||||
implementation(project(':lib:yourupload-extractor'))
|
||||
}
|
BIN
src/all/javgg/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
src/all/javgg/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
src/all/javgg/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
src/all/javgg/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
src/all/javgg/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 29 KiB |
|
@ -0,0 +1,240 @@
|
|||
package eu.kanade.tachiyomi.animeextension.all.javgg
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
||||
import eu.kanade.tachiyomi.lib.streamhidevidextractor.StreamHideVidExtractor
|
||||
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
||||
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
|
||||
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class Javgg : ConfigurableAnimeSource, AnimeHttpSource() {
|
||||
|
||||
override val name = "JavGG"
|
||||
|
||||
override val baseUrl = "https://javgg.net"
|
||||
|
||||
override val lang = "all"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREF_QUALITY_KEY = "preferred_quality"
|
||||
private const val PREF_QUALITY_DEFAULT = "1080"
|
||||
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
|
||||
|
||||
private const val PREF_SERVER_KEY = "preferred_server"
|
||||
private const val PREF_SERVER_DEFAULT = "StreamWish"
|
||||
private val SERVER_LIST = arrayOf(
|
||||
"StreamWish",
|
||||
"Voe",
|
||||
"Okru",
|
||||
"YourUpload",
|
||||
"FileLions",
|
||||
"StreamHideVid",
|
||||
"TurboPlay",
|
||||
)
|
||||
}
|
||||
|
||||
override fun animeDetailsParse(response: Response): SAnime {
|
||||
val document = response.asJsoup()
|
||||
val animeDetails = SAnime.create().apply {
|
||||
status = SAnime.COMPLETED
|
||||
description = document.selectFirst("#cover")?.text()
|
||||
}
|
||||
|
||||
document.select(".data .boxye2").forEach { element ->
|
||||
val category = element.select("[id*=owye2]").text().trim()
|
||||
val tags = element.select(".sgeneros3 a").joinToString { it.text() }
|
||||
when {
|
||||
category.contains("Genres:") -> animeDetails.genre = tags
|
||||
category.contains("Cast:") -> animeDetails.artist = tags
|
||||
category.contains("Maker:") -> animeDetails.author = tags
|
||||
}
|
||||
}
|
||||
|
||||
return animeDetails
|
||||
}
|
||||
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/trending/page/$page", headers)
|
||||
|
||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val elements = document.select("article[id*=post-]")
|
||||
val nextPage = document.select("#nextpagination").any()
|
||||
val animeList = elements.map { element ->
|
||||
SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href"))
|
||||
title = element.selectFirst(".data h3")!!.text()
|
||||
thumbnail_url = element.selectFirst(".poster")?.getImageUrl()
|
||||
}
|
||||
}
|
||||
return AnimesPage(animeList, nextPage)
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response) = popularAnimeParse(response)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/new-post/page/$page", headers)
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
return when {
|
||||
query.isNotBlank() -> GET("$baseUrl/jav/page/$page?s=$query", headers)
|
||||
else -> popularAnimeRequest(page)
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val elements = document.select(".result-item article")
|
||||
val nextPage = document.select("#nextpagination").any()
|
||||
val animeList = elements.map { element ->
|
||||
SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href"))
|
||||
title = element.selectFirst(".details .title")!!.text()
|
||||
thumbnail_url = element.selectFirst(".image")?.getImageUrl()
|
||||
}
|
||||
}
|
||||
return AnimesPage(animeList, nextPage)
|
||||
}
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val document = response.asJsoup()
|
||||
return if (document.select(".dooplay_player_option").any()) {
|
||||
listOf(
|
||||
SEpisode.create().apply {
|
||||
name = "Episode 1"
|
||||
episode_number = 1F
|
||||
setUrlWithoutDomain(document.location())
|
||||
},
|
||||
)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
return document.select("[id*=source-player] iframe").parallelCatchingFlatMapBlocking {
|
||||
val numOpt = it.closest(".source-box")?.attr("id")?.replace("source-player-", "")
|
||||
val serverName = document.select("[data-nume=\"$numOpt\"] .server").text()
|
||||
serverVideoResolver(serverName, it.attr("src"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun serverVideoResolver(server: String, url: String): List<Video> {
|
||||
val embedUrl = server.lowercase()
|
||||
return when {
|
||||
embedUrl.contains("ok.ru") || embedUrl.contains("okru") -> OkruExtractor(client).videosFromUrl(url)
|
||||
embedUrl.contains("filelions") || embedUrl.contains("lion") -> StreamWishExtractor(client, headers).videosFromUrl(url, videoNameGen = { "FileLions:$it" })
|
||||
embedUrl.contains("wishembed") || embedUrl.contains("streamwish") || embedUrl.contains("strwish") || embedUrl.contains("wish") -> {
|
||||
val docHeaders = headers.newBuilder()
|
||||
.add("Origin", "https://streamwish.to")
|
||||
.add("Referer", "https://streamwish.to/")
|
||||
.build()
|
||||
StreamWishExtractor(client, docHeaders).videosFromUrl(url, videoNameGen = { "StreamWish:$it" })
|
||||
}
|
||||
embedUrl.contains("vidhide") || embedUrl.contains("streamhide") ||
|
||||
embedUrl.contains("guccihide") || embedUrl.contains("streamvid") -> StreamHideVidExtractor(client).videosFromUrl(url)
|
||||
embedUrl.contains("voe") -> VoeExtractor(client).videosFromUrl(url)
|
||||
embedUrl.contains("yourupload") || embedUrl.contains("upload") -> YourUploadExtractor(client).videoFromUrl(url, headers = headers)
|
||||
embedUrl.contains("turboplay") -> {
|
||||
val turboDocument = client.newCall(GET(url)).execute().asJsoup()
|
||||
val masterUrl = turboDocument.select("#video_player").attr("data-hash")
|
||||
val customHeaders = headers.newBuilder().apply {
|
||||
add("Accept", "*/*")
|
||||
add("Origin", "https://${turboDocument.location().toHttpUrl().host}")
|
||||
add("Referer", "https://${turboDocument.location().toHttpUrl().host}/")
|
||||
}.build()
|
||||
|
||||
listOf(Video(masterUrl, "TurboPlay", masterUrl, customHeaders))
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun org.jsoup.nodes.Element.getImageUrl(): String? {
|
||||
val imageLinkRegex = """https?://[^\s]+\.(jpg|png)""".toRegex()
|
||||
|
||||
for (link in this.select("[href], [src]")) {
|
||||
val href = link.attr("href")
|
||||
val src = link.attr("src")
|
||||
if (imageLinkRegex.matches(href)) {
|
||||
return href
|
||||
}
|
||||
if (imageLinkRegex.matches(src)) {
|
||||
return src
|
||||
}
|
||||
}
|
||||
|
||||
val textMatches = imageLinkRegex.find(this.text())
|
||||
val htmlMatches = imageLinkRegex.find(this.outerHtml())
|
||||
return textMatches?.value ?: htmlMatches?.value
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
|
||||
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
|
||||
return this.sortedWith(
|
||||
compareBy(
|
||||
{ it.quality.contains(server, true) },
|
||||
{ it.quality.contains(quality) },
|
||||
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
|
||||
),
|
||||
).reversed()
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_SERVER_KEY
|
||||
title = "Preferred server"
|
||||
entries = SERVER_LIST
|
||||
entryValues = SERVER_LIST
|
||||
setDefaultValue(PREF_SERVER_DEFAULT)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_QUALITY_KEY
|
||||
title = "Preferred quality"
|
||||
entries = QUALITY_LIST
|
||||
entryValues = QUALITY_LIST
|
||||
setDefaultValue(PREF_QUALITY_DEFAULT)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Jav Guru'
|
||||
extClass = '.JavGuru'
|
||||
extVersionCode = 16
|
||||
extVersionCode = 17
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Sudatchi'
|
||||
extClass = '.Sudatchi'
|
||||
extVersionCode = 4
|
||||
extVersionCode = 5
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class Sudatchi : AnimeHttpSource(), ConfigurableAnimeSource {
|
|||
|
||||
override val baseUrl = "https://sudatchi.com"
|
||||
|
||||
private val ipfsUrl = "https://gboesk298le91ct41kibaonc7o.ingress.akashprovid.com"
|
||||
private val ipfsUrl = "https://ipfs.sudatchi.com"
|
||||
|
||||
override val lang = "all"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Torrentio (Torrent / Debrid)'
|
||||
extClass = '.Torrentio'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
containsNsfw = false
|
||||
}
|
||||
|
||||
|
|
|
@ -602,6 +602,7 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
"AllDebrid",
|
||||
"DebridLink",
|
||||
"Offcloud",
|
||||
"TorBox",
|
||||
)
|
||||
private val PREF_DEBRID_VALUES = arrayOf(
|
||||
"none",
|
||||
|
@ -610,6 +611,7 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
"alldebrid",
|
||||
"debridlink",
|
||||
"offcloud",
|
||||
"torbox",
|
||||
)
|
||||
|
||||
// Sort
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Torrentio Anime (Torrent / Debrid)'
|
||||
extClass = '.Torrentio'
|
||||
extVersionCode = 9
|
||||
extVersionCode = 11
|
||||
containsNsfw = false
|
||||
}
|
||||
|
||||
|
|
|
@ -375,6 +375,7 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
?.let { videos ->
|
||||
if (preferences.getBoolean(UPCOMING_EP_KEY, UPCOMING_EP_DEFAULT)) { videos } else { videos.filter { video -> (video.released?.let { parseDate(it) } ?: 0L) <= System.currentTimeMillis() } }
|
||||
}
|
||||
?.filter { it.thumbnail != null }
|
||||
?.map { video ->
|
||||
SEpisode.create().apply {
|
||||
episode_number = video.episode?.toFloat() ?: 0.0F
|
||||
|
@ -663,6 +664,7 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
"AllDebrid",
|
||||
"DebridLink",
|
||||
"Offcloud",
|
||||
"TorBox",
|
||||
)
|
||||
private val PREF_DEBRID_VALUES = arrayOf(
|
||||
"none",
|
||||
|
@ -671,6 +673,7 @@ class Torrentio : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
"alldebrid",
|
||||
"debridlink",
|
||||
"offcloud",
|
||||
"torbox",
|
||||
)
|
||||
|
||||
// Sort
|
||||
|
|
|
@ -150,4 +150,5 @@ data class EpisodeVideo(
|
|||
val episode: Int? = null,
|
||||
val released: String? = null,
|
||||
val title: String? = null,
|
||||
val thumbnail: String? = null,
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Anime4up'
|
||||
extClass = '.Anime4Up'
|
||||
extVersionCode = 58
|
||||
extVersionCode = 59
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -38,7 +38,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
|
||||
override val name = "Anime4Up"
|
||||
|
||||
override val baseUrl = "https://anime4up.cam"
|
||||
override val baseUrl = "https://anime4up.rest"
|
||||
|
||||
override val lang = "ar"
|
||||
|
||||
|
@ -136,7 +136,6 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
episode_number = name.substringAfterLast(" ").toFloatOrNull() ?: 0F
|
||||
}
|
||||
|
||||
// ============================ Video Links =============================
|
||||
@Serializable
|
||||
data class Qualities(
|
||||
val fhd: Map<String, String> = emptyMap(),
|
||||
|
@ -144,14 +143,47 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
val sd: Map<String, String> = emptyMap(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class WatchServerData(
|
||||
val name: String,
|
||||
val link: String,
|
||||
val order: String,
|
||||
val icon: Boolean,
|
||||
)
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val base64 = response.asJsoup().selectFirst("input[name=wl]")
|
||||
val document = response.asJsoup()
|
||||
|
||||
// Decode base64 for each quality level
|
||||
val base64Fhd = document.selectFirst(".WatchServersEmbed form input[name='watch_fhd']")
|
||||
?.attr("value")
|
||||
?.let { String(Base64.decode(it, Base64.DEFAULT)) }
|
||||
?: return emptyList()
|
||||
?: "[]"
|
||||
|
||||
val parsedData = json.decodeFromString<Qualities>(base64)
|
||||
val streamLinks = with(parsedData) { fhd + hd + sd }
|
||||
val base64Hd = document.selectFirst(".WatchServersEmbed form input[name='watch_hd']")
|
||||
?.attr("value")
|
||||
?.let { String(Base64.decode(it, Base64.DEFAULT)) }
|
||||
?: "[]"
|
||||
|
||||
val base64Sd = document.selectFirst(".WatchServersEmbed form input[name='watch_SD']")
|
||||
?.attr("value")
|
||||
?.let { String(Base64.decode(it, Base64.DEFAULT)) }
|
||||
?: "[]"
|
||||
|
||||
// Parse the base64 decoded strings into lists of WatchServerData
|
||||
val parsedFhd = json.decodeFromString<List<WatchServerData>>(base64Fhd)
|
||||
val parsedHd = json.decodeFromString<List<WatchServerData>>(base64Hd)
|
||||
val parsedSd = json.decodeFromString<List<WatchServerData>>(base64Sd)
|
||||
|
||||
// Convert to the old Qualities structure
|
||||
val qualities = Qualities(
|
||||
fhd = parsedFhd.associate { it.name to it.link },
|
||||
hd = parsedHd.associate { it.name to it.link },
|
||||
sd = parsedSd.associate { it.name to it.link },
|
||||
)
|
||||
|
||||
// Use the same logic as the old implementation
|
||||
val streamLinks = with(qualities) { fhd + hd + sd }
|
||||
|
||||
return streamLinks.values.distinct().flatMap(::extractVideos)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Egy Dead'
|
||||
extClass = '.EgyDead'
|
||||
extVersionCode = 13
|
||||
extVersionCode = 14
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'CineClix'
|
||||
extClass = '.CineClix'
|
||||
extVersionCode = 15
|
||||
extVersionCode = 16
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Einfach'
|
||||
extClass = '.Einfach'
|
||||
extVersionCode = 11
|
||||
extVersionCode = 12
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Movie2k'
|
||||
extClass = '.Movie2k'
|
||||
extVersionCode = 6
|
||||
extVersionCode = 7
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Serienstream'
|
||||
extClass = '.Serienstream'
|
||||
extVersionCode = 19
|
||||
extVersionCode = 20
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -10,4 +10,4 @@ dependencies {
|
|||
implementation(project(':lib:voe-extractor'))
|
||||
implementation(project(':lib:streamtape-extractor'))
|
||||
implementation(project(':lib:dood-extractor'))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class Serienstream : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
|
||||
override val name = "Serienstream"
|
||||
|
||||
override val baseUrl = "http://186.2.175.5"
|
||||
override val baseUrl = "https://s.to"
|
||||
|
||||
override val lang = "de"
|
||||
|
||||
|
@ -91,7 +91,7 @@ class Serienstream : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val headers = Headers.Builder()
|
||||
.add("Referer", "http://186.2.175.5/search")
|
||||
.add("Referer", "https://s.to/search")
|
||||
.add("origin", baseUrl)
|
||||
.add("connection", "keep-alive")
|
||||
.add("user-agent", "Mozilla/5.0 (Linux; Android 12; Pixel 5 Build/SP2A.220405.004; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.127 Safari/537.36")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'StreamCloud'
|
||||
extClass = '.StreamCloud'
|
||||
extVersionCode = 8
|
||||
extVersionCode = 9
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -2,7 +2,7 @@ ext {
|
|||
extName = 'AniPlay'
|
||||
extClass = '.AniPlay'
|
||||
themePkg = 'anilist'
|
||||
overrideVersionCode = 3
|
||||
overrideVersionCode = 7
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.animesource.model.Video
|
|||
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
|
||||
import eu.kanade.tachiyomi.multisrc.anilist.AniListAnimeHttpSource
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.util.parallelFlatMapBlocking
|
||||
import eu.kanade.tachiyomi.util.parallelFlatMap
|
||||
import eu.kanade.tachiyomi.util.parseAs
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.encodeToString
|
||||
|
@ -31,6 +31,7 @@ import java.io.IOException
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
@Suppress("unused")
|
||||
class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
||||
override val name = "AniPlay"
|
||||
override val lang = "en"
|
||||
|
@ -67,6 +68,8 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
}
|
||||
}
|
||||
|
||||
private val baseHost: String get() = "${preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)}"
|
||||
|
||||
/* ====================================== Episode List ====================================== */
|
||||
|
||||
override fun episodeListRequest(anime: SAnime): Request {
|
||||
|
@ -79,7 +82,7 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
val headersWithAction =
|
||||
headers.newBuilder()
|
||||
// next.js stuff I guess
|
||||
.add("Next-Action", HEADER_NEXT_ACTION_EPISODE_LIST_VALUE)
|
||||
.add("Next-Action", getHeaderValue(baseHost, NEXT_ACTION_EPISODE_LIST))
|
||||
.build()
|
||||
|
||||
return POST(url = "$baseUrl/anime/info/$animeId", headersWithAction, requestBody)
|
||||
|
@ -91,7 +94,11 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
val animeId = episodeListUrl.pathSegments[2]
|
||||
|
||||
val responseString = response.body.string()
|
||||
val episodesArrayString = responseString.split("1:").last()
|
||||
val episodesArrayString = extractEpisodeList(responseString)
|
||||
if (episodesArrayString == null) {
|
||||
Log.e("AniPlay", "Episode list not found - ${response.request}\nbody:${response.request.body}\n${responseString.substring(0,200)}")
|
||||
throw Exception("Episode list not found")
|
||||
}
|
||||
|
||||
val providers = episodesArrayString.parseAs<List<EpisodeListResponse>>()
|
||||
val episodes = mutableMapOf<Int, EpisodeListResponse.Episode>()
|
||||
|
@ -108,7 +115,7 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
source = provider.providerId,
|
||||
episodeId = episode.id,
|
||||
episodeNum = episode.number,
|
||||
hasDub = episode.hasDub,
|
||||
hasDub = episode.hasDub ?: false,
|
||||
)
|
||||
episodeExtras[episodeNumber] = existingEpisodeExtras + listOf(episodeExtra)
|
||||
}
|
||||
|
@ -136,7 +143,7 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
else -> ""
|
||||
}
|
||||
val filler = when {
|
||||
episode.isFiller && isMarkFiller -> " • Filler Episode"
|
||||
episode.isFiller == true && isMarkFiller -> " • Filler Episode"
|
||||
else -> ""
|
||||
}
|
||||
val scanlator = "Sub$dub$filler"
|
||||
|
@ -156,7 +163,6 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
override suspend fun getVideoList(episode: SEpisode): List<Video> {
|
||||
val episodeUrl = episode.url.toHttpUrl()
|
||||
val animeId = episodeUrl.queryParameter("id") ?: return emptyList()
|
||||
// val episodeNum = episodeUrl.queryParameter("ep") ?: return emptyList()
|
||||
val extras = episodeUrl.queryParameter("extras")
|
||||
?.let {
|
||||
try {
|
||||
|
@ -170,92 +176,158 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
try {
|
||||
json.decodeFromString<List<EpisodeExtra>>(it)
|
||||
} catch (e: SerializationException) {
|
||||
Log.e("AniPlay", "Error parsing JSON", e)
|
||||
Log.e("AniPlay", "Error parsing JSON extras", e)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
?: emptyList()
|
||||
|
||||
val headersWithAction =
|
||||
headers.newBuilder()
|
||||
// next.js stuff I guess
|
||||
.add("Next-Action", HEADER_NEXT_ACTION_SOURCES_LIST_VALUE)
|
||||
.build()
|
||||
|
||||
val episodeDataList = extras.parallelFlatMapBlocking { extra ->
|
||||
var timeouts = 0
|
||||
var maxTimeout = 0
|
||||
val videos = extras.parallelFlatMap { extra ->
|
||||
val languages = mutableListOf("sub").apply {
|
||||
if (extra.hasDub) add("dub")
|
||||
}
|
||||
languages.map { language ->
|
||||
languages.parallelFlatMap { language ->
|
||||
val epNum = if (extra.episodeNum == extra.episodeNum.toInt().toFloat()) {
|
||||
extra.episodeNum.toInt().toString() // If it has no fractional part, convert it to an integer
|
||||
extra.episodeNum.toInt().toString()
|
||||
} else {
|
||||
extra.episodeNum.toString() // If it has a fractional part, leave it as a float
|
||||
extra.episodeNum.toString()
|
||||
}
|
||||
|
||||
val requestBody = "[\"$animeId\",\"${extra.source}\",\"${extra.episodeId}\",\"$epNum\",\"$language\"]"
|
||||
.toRequestBody("application/json".toMediaType())
|
||||
|
||||
val params = mapOf(
|
||||
"id" to animeId,
|
||||
"host" to extra.source,
|
||||
"ep" to epNum,
|
||||
"type" to language,
|
||||
)
|
||||
val builder = Uri.parse("$baseUrl/anime/watch").buildUpon()
|
||||
|
||||
val builder = Uri.parse("$baseUrl/anime/watch/$animeId").buildUpon()
|
||||
params.map { (k, v) -> builder.appendQueryParameter(k, v); }
|
||||
val url = builder.build().toString()
|
||||
Log.i("AniPlay", "Url: $url")
|
||||
val url = builder.build()
|
||||
|
||||
val headersWithAction =
|
||||
headers.newBuilder()
|
||||
.add("Next-Action", getHeaderValue(baseHost, NEXT_ACTION_SOURCES_LIST))
|
||||
.build()
|
||||
|
||||
val requestBody = "[\"$animeId\",\"${extra.source}\",\"${extra.episodeId}\",\"$epNum\",\"$language\"]"
|
||||
.toRequestBody("application/json".toMediaType())
|
||||
|
||||
val request = POST(url.toString(), headersWithAction, requestBody)
|
||||
|
||||
maxTimeout += 1
|
||||
try {
|
||||
val request = POST(url, headersWithAction, requestBody)
|
||||
val response = client.newCall(request).execute()
|
||||
getVideos(extra, language, request)
|
||||
} catch (e: java.net.SocketTimeoutException) {
|
||||
Log.e("AniPlay", "VideoList $url SocketTimeoutException", e)
|
||||
timeouts++
|
||||
emptyList()
|
||||
} catch (e: IOException) {
|
||||
Log.e("AniPlay", "VideoList $url IOException", e)
|
||||
emptyList()
|
||||
} catch (e: Exception) {
|
||||
Log.e("AniPlay", "VideoList $url Exception", e)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val responseString = response.body.string()
|
||||
val sourcesString = responseString.split("1:").last()
|
||||
if (sourcesString.startsWith("null")) return@map null
|
||||
val data = sourcesString.parseAs<VideoSourceResponse>()
|
||||
if (videos.isEmpty() && timeouts != 0 && maxTimeout == timeouts) {
|
||||
throw Exception("Timed out")
|
||||
}
|
||||
|
||||
return videos.sort()
|
||||
}
|
||||
|
||||
private fun getVideos(extra: EpisodeExtra, language: String, request: Request): List<Video> {
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
val responseString = response.body.string()
|
||||
val sourcesString = extractSourcesList(responseString) ?: return emptyList()
|
||||
Log.i("AniPlay", "${extra.source} $language -> $sourcesString")
|
||||
|
||||
when (extra.source.lowercase()) {
|
||||
"yuki" -> {
|
||||
val data = sourcesString.parseAs<VideoSourceResponseYuki>()
|
||||
return processEpisodeDataYuki(
|
||||
EpisodeDataYuki(
|
||||
source = extra.source,
|
||||
language = language,
|
||||
response = data,
|
||||
),
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val data = sourcesString.parseAs<VideoSourceResponse>()
|
||||
return processEpisodeData(
|
||||
EpisodeData(
|
||||
source = extra.source,
|
||||
language = language,
|
||||
response = data,
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
Log.w("AniPlay", "VideoList $url IOException", e)
|
||||
null // Return null to be filtered out
|
||||
} catch (e: Exception) {
|
||||
Log.w("AniPlay", "VideoList $url Exception", e)
|
||||
null // Return null to be filtered out
|
||||
}
|
||||
}.filterNotNull() // Filter out null values due to errors
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processEpisodeDataYuki(episodeData: EpisodeDataYuki): List<Video> {
|
||||
val defaultSource = episodeData.response.sources?.firstOrNull()
|
||||
|
||||
if (defaultSource == null) {
|
||||
Log.e("AniPlay", "defaultSource is null (${episodeData.response})")
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val videos = episodeDataList.flatMap { episodeData ->
|
||||
val defaultSource = episodeData.response.sources?.firstOrNull {
|
||||
it.quality in listOf("default", "auto")
|
||||
} ?: return@flatMap emptyList()
|
||||
val subtitles = episodeData.response.tracks
|
||||
?.filter { it.kind?.lowercase() == "captions" }
|
||||
?.map { Track(it.file, it.label ?: "Unknown") }
|
||||
?: emptyList()
|
||||
|
||||
val subtitles = episodeData.response.subtitles
|
||||
?.filter { it.lang != "Thumbnails" }
|
||||
?.map { Track(it.url, it.lang) }
|
||||
?: emptyList()
|
||||
val serverName = getServerName(episodeData.source)
|
||||
val typeName = getTypeName(episodeData.language).let {
|
||||
if (it == "Sub" && subtitles.isNotEmpty()) "SoftSub" else it
|
||||
}
|
||||
|
||||
playlistUtils.extractFromHls(
|
||||
try {
|
||||
return playlistUtils.extractFromHls(
|
||||
playlistUrl = defaultSource.url,
|
||||
videoNameGen = { quality ->
|
||||
val serverName = getServerName(episodeData.source)
|
||||
val typeName = when {
|
||||
subtitles.isNotEmpty() -> "SoftSub"
|
||||
else -> getTypeName(episodeData.language)
|
||||
}
|
||||
|
||||
"$serverName - $quality - $typeName"
|
||||
},
|
||||
videoNameGen = { quality -> "$serverName - $quality - $typeName" },
|
||||
subtitleList = subtitles,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e("AniPlay", "processEpisodeDataYuki extractFromHls Error (\"$serverName - $typeName\"): $e")
|
||||
}
|
||||
|
||||
return videos.sort()
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private fun processEpisodeData(episodeData: EpisodeData): List<Video> {
|
||||
val defaultSource = episodeData.response.sources?.firstOrNull {
|
||||
it.quality in listOf("default", "auto")
|
||||
} ?: return emptyList()
|
||||
|
||||
val subtitles = episodeData.response.subtitles
|
||||
?.filter { it.lang?.lowercase() != "thumbnails" }
|
||||
?.map { Track(it.url, it.lang ?: "Unk") }
|
||||
?: emptyList()
|
||||
|
||||
val serverName = getServerName(episodeData.source)
|
||||
val typeName = when {
|
||||
subtitles.isNotEmpty() -> "SoftSub"
|
||||
else -> getTypeName(episodeData.language)
|
||||
}
|
||||
|
||||
try {
|
||||
return playlistUtils.extractFromHls(
|
||||
playlistUrl = defaultSource.url,
|
||||
videoNameGen = { quality -> "$serverName - $quality - $typeName" },
|
||||
subtitleList = subtitles,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e("AniPlay", "processEpisodeData extractFromHls Error (\"$serverName - $typeName\"): $e")
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
|
@ -270,6 +342,34 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
)
|
||||
}
|
||||
|
||||
private fun extractEpisodeList(input: String): String? {
|
||||
return extractList(input, '[', ']')
|
||||
}
|
||||
|
||||
private fun extractSourcesList(input: String): String? {
|
||||
return extractList(input, '{', '}')
|
||||
}
|
||||
|
||||
private fun extractList(input: String, bracket1: Char, bracket2: Char): String? {
|
||||
val startMarker = "1:$bracket1"
|
||||
val list1Index = input.indexOf(startMarker)
|
||||
if (list1Index == -1) return null
|
||||
|
||||
val startIndex = list1Index + startMarker.length
|
||||
var endIndex = startIndex
|
||||
var bracketCount = 1
|
||||
|
||||
while (endIndex < input.length && bracketCount > 0) {
|
||||
when (input[endIndex]) {
|
||||
bracket1 -> bracketCount++
|
||||
bracket2 -> bracketCount--
|
||||
}
|
||||
endIndex++
|
||||
}
|
||||
|
||||
return if (bracketCount == 0) input.substring(startIndex - 1, endIndex) else null
|
||||
}
|
||||
|
||||
/* ====================================== Preferences ====================================== */
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
|
@ -376,11 +476,17 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
}
|
||||
private fun getServerName(value: String): String {
|
||||
val index = PREF_SERVER_ENTRY_VALUES.indexOf(value)
|
||||
if (index == -1) {
|
||||
return "Other"
|
||||
}
|
||||
return PREF_SERVER_ENTRIES[index]
|
||||
}
|
||||
|
||||
private fun getTypeName(value: String): String {
|
||||
val index = PREF_TYPE_ENTRY_VALUES.indexOf(value)
|
||||
val index = PREF_TYPE_ENTRY_VALUES.indexOf(value.lowercase())
|
||||
if (index == -1) {
|
||||
return "Other"
|
||||
}
|
||||
return PREF_TYPE_ENTRIES[index]
|
||||
}
|
||||
|
||||
|
@ -391,6 +497,10 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
} ?: 0L
|
||||
}
|
||||
|
||||
private fun getHeaderValue(serverHost: String, key: String): String {
|
||||
return HEADER_NEXT_ACTION[serverHost]?.get(key) ?: throw Exception("Bad host/key")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREF_DOMAIN_KEY = "domain"
|
||||
private val PREF_DOMAIN_ENTRIES = arrayOf("aniplaynow.live (default)", "aniplay.lol (backup)")
|
||||
|
@ -398,8 +508,8 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
private const val PREF_DOMAIN_DEFAULT = "aniplaynow.live"
|
||||
|
||||
private const val PREF_SERVER_KEY = "server"
|
||||
private val PREF_SERVER_ENTRIES = arrayOf("Kuro (Gogoanime)", "Yuki (HiAnime)", "Yuno (Yugenanime)")
|
||||
private val PREF_SERVER_ENTRY_VALUES = arrayOf("kuro", "yuki", "yuno")
|
||||
private val PREF_SERVER_ENTRIES = arrayOf("Kuro", "Anya", "Yuki")
|
||||
private val PREF_SERVER_ENTRY_VALUES = arrayOf("kuro", "anya", "yuki")
|
||||
private const val PREF_SERVER_DEFAULT = "kuro"
|
||||
|
||||
private const val PREF_QUALITY_KEY = "quality"
|
||||
|
@ -421,8 +531,19 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
private const val PREF_MARK_FILLER_EPISODE_DEFAULT = true
|
||||
|
||||
// These values has probably something to do with Next.js server and hydration
|
||||
private const val HEADER_NEXT_ACTION_EPISODE_LIST_VALUE = "f3422af67c84852f5e63d50e1f51718f1c0225c4"
|
||||
private const val HEADER_NEXT_ACTION_SOURCES_LIST_VALUE = "5dbcd21c7c276c4d15f8de29d9ef27aef5ea4a5e"
|
||||
private const val NEXT_ACTION_EPISODE_LIST = "NEXT_ACTION_EPISODE_LIST"
|
||||
private const val NEXT_ACTION_SOURCES_LIST = "NEXT_ACTION_SOURCES_LIST"
|
||||
|
||||
private val HEADER_NEXT_ACTION = mapOf(
|
||||
PREF_DOMAIN_ENTRY_VALUES[0] to mapOf(
|
||||
"NEXT_ACTION_EPISODE_LIST" to "f3422af67c84852f5e63d50e1f51718f1c0225c4",
|
||||
"NEXT_ACTION_SOURCES_LIST" to "5dbcd21c7c276c4d15f8de29d9ef27aef5ea4a5e",
|
||||
),
|
||||
PREF_DOMAIN_ENTRY_VALUES[1] to mapOf(
|
||||
"NEXT_ACTION_EPISODE_LIST" to "56e4151352ded056cbe226d2376c7436cffc9a37",
|
||||
"NEXT_ACTION_SOURCES_LIST" to "8a76af451978c817dde2364326a5e4e45eb43db1",
|
||||
),
|
||||
)
|
||||
|
||||
private val DATE_FORMATTER = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package eu.kanade.tachiyomi.animeextension.en.aniplay
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
@ -13,29 +12,15 @@ data class EpisodeListResponse(
|
|||
data class Episode(
|
||||
val id: String,
|
||||
val number: Float,
|
||||
val title: String,
|
||||
val hasDub: Boolean,
|
||||
val isFiller: Boolean,
|
||||
val title: String?,
|
||||
val hasDub: Boolean?,
|
||||
val isFiller: Boolean?,
|
||||
val img: String?,
|
||||
val description: String?,
|
||||
val createdAt: String?,
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class VideoSourceRequest(
|
||||
val source: String,
|
||||
|
||||
@SerialName("episodeid")
|
||||
val episodeId: String,
|
||||
|
||||
@SerialName("episodenum")
|
||||
val episodeNum: String,
|
||||
|
||||
@SerialName("subtype")
|
||||
val subType: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class VideoSourceResponse(
|
||||
val sources: List<Source>?,
|
||||
|
@ -44,13 +29,35 @@ data class VideoSourceResponse(
|
|||
@Serializable
|
||||
data class Source(
|
||||
val url: String,
|
||||
val quality: String,
|
||||
val quality: String?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Subtitle(
|
||||
val url: String,
|
||||
val lang: String,
|
||||
val lang: String?,
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class VideoSourceResponseYuki(
|
||||
val sources: List<Source>?,
|
||||
val tracks: List<Subtitle>?,
|
||||
val anilistID: Int?,
|
||||
val malID: Int?,
|
||||
) {
|
||||
@Serializable
|
||||
data class Source(
|
||||
val url: String,
|
||||
val type: String?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Subtitle(
|
||||
val file: String,
|
||||
val label: String?,
|
||||
val kind: String?,
|
||||
val default: Boolean?,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -68,3 +75,10 @@ data class EpisodeData(
|
|||
val language: String,
|
||||
val response: VideoSourceResponse,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class EpisodeDataYuki(
|
||||
val source: String,
|
||||
val language: String,
|
||||
val response: VideoSourceResponseYuki,
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AsiaFlix'
|
||||
extClass = '.AsiaFlix'
|
||||
extVersionCode = 11
|
||||
extVersionCode = 12
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AsianLoad'
|
||||
extClass = '.AsianLoad'
|
||||
extVersionCode = 40
|
||||
extVersionCode = 41
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -2,8 +2,8 @@ ext {
|
|||
extName = 'DonghuaStream'
|
||||
extClass = '.DonghuaStream'
|
||||
themePkg = 'animestream'
|
||||
baseUrl = 'https://donghuastream.co.in'
|
||||
overrideVersionCode = 6
|
||||
baseUrl = 'https://donghuastream.org'
|
||||
overrideVersionCode = 7
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
|
|||
class DonghuaStream : AnimeStream(
|
||||
"en",
|
||||
"DonghuaStream",
|
||||
"https://donghuastream.co.in",
|
||||
"https://donghuastream.org",
|
||||
) {
|
||||
override val fetchFilters: Boolean
|
||||
get() = false
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'DramaCool'
|
||||
extClass = '.DramaCool'
|
||||
extVersionCode = 50
|
||||
extVersionCode = 51
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -29,7 +29,7 @@ class DramaCool : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
|
||||
// TODO: Check frequency of url changes to potentially
|
||||
// add back overridable baseurl preference
|
||||
override val baseUrl = "https://asianc.sh/"
|
||||
override val baseUrl = "https://asianc.co/"
|
||||
|
||||
override val lang = "en"
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Kaido'
|
||||
themePkg = 'zorotheme'
|
||||
baseUrl = 'https://kaido.to'
|
||||
overrideVersionCode = 6
|
||||
overrideVersionCode = 7
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Multimovies'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://multimovies.art'
|
||||
overrideVersionCode = 18
|
||||
overrideVersionCode = 19
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'My Running Man'
|
||||
extClass = '.MyRunningMan'
|
||||
extVersionCode = 5
|
||||
extVersionCode = 6
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.PinoyMoviePedia'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://pinoymoviepedia.ru'
|
||||
overrideVersionCode = 0
|
||||
overrideVersionCode = 1
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Rule34Video'
|
||||
extClass = '.Rule34Video'
|
||||
extVersionCode = 8
|
||||
extVersionCode = 9
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package eu.kanade.tachiyomi.animeextension.en.rule34video
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
|
@ -41,7 +43,21 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/latest-updates/$page/")
|
||||
override fun popularAnimeRequest(page: Int): Request {
|
||||
return if (preferences.getBoolean(PREF_UPLOADER_FILTER_ENABLED_KEY, false)) {
|
||||
val uploaderId = preferences.getString(PREF_UPLOADER_ID_KEY, "") ?: ""
|
||||
if (uploaderId.isNotBlank()) {
|
||||
val url = "$baseUrl/members/$uploaderId/videos/?mode=async&function=get_block&block_id=list_videos_uploaded_videos&sort_by=&from_videos=$page"
|
||||
Log.e("Rule34Video", "Loading popular videos from uploader ID: $uploaderId, page: $page, URL: $url")
|
||||
GET(url)
|
||||
} else {
|
||||
Log.e("Rule34Video", "Uploader filter enabled but ID is blank, loading latest updates.")
|
||||
GET("$baseUrl/latest-updates/$page/")
|
||||
}
|
||||
} else {
|
||||
GET("$baseUrl/latest-updates/$page/")
|
||||
}
|
||||
}
|
||||
|
||||
override fun popularAnimeSelector() = "div.item.thumb"
|
||||
|
||||
|
@ -102,21 +118,60 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
|
||||
// =========================== Anime Details ============================
|
||||
override fun animeDetailsParse(document: Document) = SAnime.create().apply {
|
||||
title = document.selectFirst("h1.title_video")!!.text()
|
||||
val info = document.selectFirst("#tab_video_info")!!
|
||||
author = info.select("div.label:contains(Artist:) + a").eachText().joinToString()
|
||||
title = document.selectFirst("h1.title_video")?.text().toString()
|
||||
|
||||
val infoRow = document.selectFirst("div.info.row")
|
||||
val detailRows = document.select("div.row")
|
||||
|
||||
val artistElement = detailRows.select("div.col:has(div.label:contains(Artist)) a.item span.name").firstOrNull()
|
||||
author = artistElement?.text().orEmpty()
|
||||
|
||||
description = buildString {
|
||||
info.selectFirst("div.label:contains(Description:) > em")?.text()?.also { append("$it\n") }
|
||||
info.selectFirst("i.icon-eye + span")?.text()?.also { append("\nViews : ${it.replace(" ", ",")}") }
|
||||
info.selectFirst("i.icon-clock + span")?.text()?.also { append("\nDuration : $it") }
|
||||
document.select("div.label:contains(Download) ~ a.tag_item")
|
||||
detailRows.select("div.row:has(div.label > em) > div.label > em").html()
|
||||
.replace("<br>", "\n") // Ensure single <br> tags are followed by a newline
|
||||
.let { text ->
|
||||
append(text)
|
||||
}
|
||||
append("\n\n") // Add extra spacing
|
||||
|
||||
infoRow?.selectFirst("div.item_info:nth-child(1) > span")?.text()?.let {
|
||||
append("Uploaded: $it\n")
|
||||
}
|
||||
|
||||
val artist = detailRows.select("div.col:has(div.label:contains(Artist)) a.item span.name")
|
||||
.eachText()
|
||||
.joinToString()
|
||||
if (artist.isNotEmpty()) {
|
||||
append("Artists: $artist\n")
|
||||
}
|
||||
|
||||
val categories = detailRows.select("div.col:has(div.label:contains(Categories)) a.item span")
|
||||
.eachText()
|
||||
.joinToString()
|
||||
if (categories.isNotEmpty()) {
|
||||
append("Categories: $categories\n")
|
||||
}
|
||||
|
||||
val uploader = detailRows.select("div.col:has(div.label:contains(Uploaded by)) a.item").text()
|
||||
if (uploader.isNotEmpty()) {
|
||||
append("Uploader: $uploader\n")
|
||||
}
|
||||
|
||||
infoRow?.select("div.item_info:nth-child(2) > span")?.text()?.let {
|
||||
val views = it.substringBefore(" ").replace(",", "")
|
||||
append("Views: $views\n")
|
||||
}
|
||||
infoRow?.select("div.item_info:nth-child(3) > span")?.text()?.let { append("Duration: $it\n") }
|
||||
document.select("div.row:has(div.label:contains(Download)) a.tag_item")
|
||||
.eachText()
|
||||
.joinToString { it.substringAfter(" ") }
|
||||
.also { append("\nQuality : $it") }
|
||||
.also { append("Quality: $it") }
|
||||
}
|
||||
genre = document.select("div.label:contains(Tags) ~ a.tag_item:not(:contains(Suggest))")
|
||||
|
||||
genre = document.select("div.row_spacer:has(div.label:contains(Tags)) a.tag_item:not(:contains(Suggest))")
|
||||
.eachText()
|
||||
.joinToString()
|
||||
|
||||
status = SAnime.COMPLETED
|
||||
}
|
||||
|
||||
|
@ -186,6 +241,24 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
|
||||
// ============================== Settings ==============================
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
SwitchPreferenceCompat(screen.context).apply {
|
||||
key = PREF_UPLOADER_FILTER_ENABLED_KEY
|
||||
title = "Filter by Uploader"
|
||||
summary = "Load videos only from the specified uploader ID."
|
||||
setDefaultValue(false)
|
||||
}.also(screen::addPreference)
|
||||
|
||||
EditTextPreference(screen.context).apply {
|
||||
key = PREF_UPLOADER_ID_KEY
|
||||
title = "Uploader ID"
|
||||
summary = "Enter the ID of the uploader (e.g., 98965). Requires \"Filter by Uploader\" to be enabled."
|
||||
dialogTitle = "Enter Uploader ID"
|
||||
var dependency = PREF_UPLOADER_FILTER_ENABLED_KEY
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
newValue?.toString().isNullOrBlank().not()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_QUALITY_KEY
|
||||
title = PREF_QUALITY_TITLE
|
||||
|
@ -218,14 +291,20 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
return tagList.toTypedArray()
|
||||
}
|
||||
|
||||
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
|
||||
OrderFilter(),
|
||||
CategoryBy(),
|
||||
AnimeFilter.Separator(),
|
||||
AnimeFilter.Header("Entered a \"tag\", click on \"filter\" then Click \"reset\" to load tags."),
|
||||
TagFilter(),
|
||||
TagSearch(tagsResults(tagDocument)),
|
||||
)
|
||||
override fun getFilterList(): AnimeFilterList = if (preferences.getBoolean(PREF_UPLOADER_FILTER_ENABLED_KEY, false) &&
|
||||
preferences.getString(PREF_UPLOADER_ID_KEY, "")?.isNotBlank() == true
|
||||
) {
|
||||
AnimeFilterList() // If uploader filter is enabled and ID is set, show no other filters
|
||||
} else {
|
||||
AnimeFilterList(
|
||||
OrderFilter(),
|
||||
CategoryBy(),
|
||||
AnimeFilter.Separator(),
|
||||
AnimeFilter.Header("Entered a \"tag\", click on \"filter\" then Click \"reset\" to load tags."),
|
||||
TagFilter(),
|
||||
TagSearch(tagsResults(tagDocument)),
|
||||
)
|
||||
}
|
||||
|
||||
private class TagFilter : AnimeFilter.Text("Click \"reset\" without any text to load all A-Z tags.", "")
|
||||
|
||||
|
@ -267,5 +346,8 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
private const val PREF_QUALITY_TITLE = "Preferred quality"
|
||||
private const val PREF_QUALITY_DEFAULT = "1080p"
|
||||
private val PREF_QUALITY_ENTRIES = arrayOf("2160p", "1080p", "720p", "480p", "360p")
|
||||
|
||||
private const val PREF_UPLOADER_FILTER_ENABLED_KEY = "uploader_filter_enabled"
|
||||
private const val PREF_UPLOADER_ID_KEY = "uploader_id"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Tokuzilla'
|
||||
extClass = '.Tokuzilla'
|
||||
extVersionCode = 17
|
||||
extVersionCode = 20
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.HiAnime'
|
||||
themePkg = 'zorotheme'
|
||||
baseUrl = 'https://hianime.to'
|
||||
overrideVersionCode = 40
|
||||
overrideVersionCode = 44
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
|
@ -4,6 +4,8 @@ import eu.kanade.tachiyomi.animesource.model.Video
|
|||
import eu.kanade.tachiyomi.lib.megacloudextractor.MegaCloudExtractor
|
||||
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
||||
import eu.kanade.tachiyomi.multisrc.zorotheme.ZoroTheme
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import okhttp3.Request
|
||||
|
||||
class HiAnime : ZoroTheme(
|
||||
"en",
|
||||
|
@ -22,6 +24,8 @@ class HiAnime : ZoroTheme(
|
|||
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
|
||||
private val megaCloudExtractor by lazy { MegaCloudExtractor(client, headers, preferences) }
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/recently-updated?page=$page", docHeaders)
|
||||
|
||||
override fun extractVideo(server: VideoData): List<Video> {
|
||||
return when (server.name) {
|
||||
"StreamTape" -> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Animefenix'
|
||||
extClass = '.Animefenix'
|
||||
extVersionCode = 40
|
||||
extVersionCode = 42
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -78,7 +78,7 @@ object AnimeFenixFilters {
|
|||
|
||||
class SortFilter : QueryPartFilter("Orden", AnimeFenixFiltersData.SORT)
|
||||
|
||||
private object AnimeFenixFiltersData {
|
||||
object AnimeFenixFiltersData {
|
||||
val YEARS = (1990..Calendar.getInstance().get(Calendar.YEAR)).map { Pair("$it", "$it") }.reversed().toTypedArray()
|
||||
|
||||
val TYPES = arrayOf(
|
||||
|
|
|
@ -46,6 +46,23 @@ class Animefenix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
|
||||
private val preferences: SharedPreferences by lazy { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) }
|
||||
|
||||
private object CSSQuery {
|
||||
object AnimeList {
|
||||
const val GRID = "main div.container div.grid"
|
||||
const val ELEMENT = "a"
|
||||
const val ELEMENT_URL = "a"
|
||||
const val ELEMENT_TITLE = "div div h3"
|
||||
const val ELEMENT_THUMBNAIL_URL = "img"
|
||||
const val ELEMENT_STATUS = "div div span.bg-zinc-700"
|
||||
const val NEXT = "a:has(span.sr-only:contains(Next))"
|
||||
}
|
||||
|
||||
object EpisodeList {
|
||||
const val EPISODE = "div.container div.bg-zinc-800 ul li"
|
||||
const val NUMBER = "a span span"
|
||||
const val URL = "a"
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
private const val PREF_QUALITY_KEY = "preferred_quality"
|
||||
private const val PREF_QUALITY_DEFAULT = "1080"
|
||||
|
@ -65,14 +82,17 @@ class Animefenix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
|
||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val elements = document.select("article.serie-card")
|
||||
val nextPage = document.select("ul.pagination-list li a.pagination-link:contains(Siguiente)").any()
|
||||
|
||||
val grid = document.select(CSSQuery.AnimeList.GRID)[1]
|
||||
|
||||
val elements = grid.select(CSSQuery.AnimeList.ELEMENT)
|
||||
val nextPage = document.select(CSSQuery.AnimeList.NEXT).any()
|
||||
val animeList = elements.map { element ->
|
||||
SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.select("figure.image a").attr("abs:href"))
|
||||
title = element.select("div.title h3 a").text()
|
||||
thumbnail_url = element.select("figure.image a img").attr("abs:src")
|
||||
description = element.select("div.serie-card__information p").text()
|
||||
setUrlWithoutDomain(element.select(CSSQuery.AnimeList.ELEMENT_URL).attr("abs:href"))
|
||||
title = element.select(CSSQuery.AnimeList.ELEMENT_TITLE).text()
|
||||
thumbnail_url = element.select(CSSQuery.AnimeList.ELEMENT_THUMBNAIL_URL).attr("abs:src")
|
||||
status = parseStatus(element.select(CSSQuery.AnimeList.ELEMENT_STATUS).text())
|
||||
}
|
||||
}
|
||||
return AnimesPage(animeList, nextPage)
|
||||
|
@ -96,12 +116,12 @@ class Animefenix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val document = response.asJsoup()
|
||||
return document.select("ul.anime-page__episode-list.is-size-6 li").map { it ->
|
||||
val epNum = it.select("a span").text().replace("Episodio", "")
|
||||
return document.select(CSSQuery.EpisodeList.EPISODE).map { it ->
|
||||
val epNum = it.select(CSSQuery.EpisodeList.NUMBER).text().replace("Episodio", "")
|
||||
SEpisode.create().apply {
|
||||
episode_number = epNum.toFloat()
|
||||
name = "Episodio $epNum"
|
||||
setUrlWithoutDomain(it.select("a").attr("abs:href"))
|
||||
setUrlWithoutDomain(it.select(CSSQuery.EpisodeList.URL).attr("abs:href"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +130,9 @@ class Animefenix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
val document = response.asJsoup()
|
||||
val videoList = mutableListOf<Video>()
|
||||
val servers = document.selectFirst("script:containsData(var tabsArray)")!!.data()
|
||||
.split("tabsArray").map { it.substringAfter("src='").substringBefore("'").replace("amp;", "") }
|
||||
.split("\n")
|
||||
.filter { it.contains("tabsArray[") }
|
||||
.map { it.split(" = ").last().trim().trim(';').trim('"') }
|
||||
.filter { it.contains("https") }
|
||||
|
||||
servers.forEach { server ->
|
||||
|
@ -216,12 +238,13 @@ class Animefenix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
).reversed()
|
||||
}
|
||||
|
||||
override fun animeDetailsParse(response: Response): SAnime {
|
||||
override fun animeDetailsParse(response: Response) = SAnime.create().apply {
|
||||
val document = response.asJsoup()
|
||||
return SAnime.create().apply {
|
||||
title = document.select("h1.title.has-text-orange").text()
|
||||
genre = document.select("a.button.is-small.is-orange.is-outlined.is-roundedX").joinToString { it.text() }
|
||||
status = parseStatus(document.select("div.column.is-12-mobile.xis-3-tablet.xis-3-desktop.xhas-background-danger.is-narrow-tablet.is-narrow-desktop a").text())
|
||||
with(document.selectFirst("main > div.relative > div.container > div.flex")!!) {
|
||||
title = selectFirst("h1.font-bold")!!.ownText()
|
||||
genre = select("div:has(h2:containsOwn(Géneros)) > div.flex > a").joinToString { it.text() }
|
||||
status = parseStatus(selectFirst("li:has(> span:containsOwn(Estado))")!!.ownText())
|
||||
description = select("div:has(h2:containsOwn(Sinopsis)) > p").text()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.AnimeOnlineNinja'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://ww3.animeonline.ninja'
|
||||
overrideVersionCode = 39
|
||||
overrideVersionCode = 41
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -14,4 +14,7 @@ dependencies {
|
|||
implementation(project(':lib:filemoon-extractor'))
|
||||
implementation(project(':lib:mixdrop-extractor'))
|
||||
implementation(project(':lib:uqload-extractor'))
|
||||
implementation(project(':lib:vidhide-extractor'))
|
||||
implementation(project(':lib:streamwish-extractor'))
|
||||
implementation(project(':lib:mp4upload-extractor'))
|
||||
}
|
||||
|
|
|
@ -4,20 +4,24 @@ import androidx.preference.CheckBoxPreference
|
|||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
|
||||
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
||||
import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
|
||||
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
|
||||
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
||||
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
||||
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
|
||||
import eu.kanade.tachiyomi.lib.vidhideextractor.VidHideExtractor
|
||||
import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class AnimeOnlineNinja : DooPlay(
|
||||
"es",
|
||||
|
@ -91,7 +95,7 @@ class AnimeOnlineNinja : DooPlay(
|
|||
} else if (path.startsWith("/letra") || path.startsWith("/tendencias")) {
|
||||
val before = path.substringBeforeLast("/")
|
||||
val after = path.substringAfterLast("/")
|
||||
GET(baseUrl + before + "/page/$page/" + after)
|
||||
GET("$baseUrl$before/page/$page/$after")
|
||||
} else {
|
||||
GET("$baseUrl$path/page/$page")
|
||||
}
|
||||
|
@ -100,11 +104,29 @@ class AnimeOnlineNinja : DooPlay(
|
|||
// ============================== Episodes ==============================
|
||||
override val episodeMovieText = "Película"
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val doc = getRealAnimeDoc(response.asJsoup())
|
||||
val seasonList = doc.select(seasonListSelector)
|
||||
return if (seasonList.isEmpty()) {
|
||||
listOf(
|
||||
SEpisode.create().apply {
|
||||
setUrlWithoutDomain(doc.location())
|
||||
episode_number = 1F
|
||||
name = episodeMovieText
|
||||
},
|
||||
)
|
||||
} else {
|
||||
seasonList.reversed().flatMap { seasonElement ->
|
||||
getSeasonEpisodes(seasonElement).reversed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ Video Links =============================
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
val players = document.select("ul#playeroptionsul li")
|
||||
return players.flatMap { player ->
|
||||
return players.parallelCatchingFlatMapBlocking { player ->
|
||||
val name = player.selectFirst("span.title")!!.text()
|
||||
val url = getPlayerUrl(player)
|
||||
extractVideos(url, name)
|
||||
|
@ -116,37 +138,41 @@ class AnimeOnlineNinja : DooPlay(
|
|||
private val streamTapeExtractor by lazy { StreamTapeExtractor(client) }
|
||||
private val mixDropExtractor by lazy { MixDropExtractor(client) }
|
||||
private val uqloadExtractor by lazy { UqloadExtractor(client) }
|
||||
private val vidHideExtractor by lazy { VidHideExtractor(client, headers) }
|
||||
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
|
||||
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
|
||||
|
||||
private fun extractVideos(url: String, lang: String): List<Video> {
|
||||
return when {
|
||||
"saidochesto.top" in url || "MULTISERVER" in lang.uppercase() ->
|
||||
extractFromMulti(url)
|
||||
"filemoon" in url ->
|
||||
filemoonExtractor.videosFromUrl(url, "$lang Filemoon - ", headers)
|
||||
"dood" in url ->
|
||||
doodExtractor.videoFromUrl(url, "$lang DoodStream", false)
|
||||
?.let(::listOf)
|
||||
"streamtape" in url ->
|
||||
streamTapeExtractor.videoFromUrl(url, "$lang StreamTape")
|
||||
?.let(::listOf)
|
||||
"mixdrop" in url ->
|
||||
mixDropExtractor.videoFromUrl(url, lang)
|
||||
"uqload" in url ->
|
||||
uqloadExtractor.videosFromUrl(url)
|
||||
"wolfstream" in url -> {
|
||||
client.newCall(GET(url, headers)).execute()
|
||||
.asJsoup()
|
||||
.selectFirst("script:containsData(sources)")
|
||||
?.data()
|
||||
?.let { jsData ->
|
||||
val videoUrl = jsData.substringAfter("{file:\"").substringBefore("\"")
|
||||
listOf(Video(videoUrl, "$lang WolfStream", videoUrl, headers = headers))
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
} ?: emptyList<Video>()
|
||||
try {
|
||||
return when {
|
||||
arrayOf("saidochesto.top").any(url) || "MULTISERVER" in lang.uppercase() -> extractFromMulti(url)
|
||||
arrayOf("filemoon", "filemooon", "moon").any(url) -> filemoonExtractor.videosFromUrl(url, "$lang Filemoon:", headers)
|
||||
arrayOf("doodstream", "dood.", "ds2play", "doods.").any(url) -> doodExtractor.videosFromUrl(url, "$lang DoodStream", false)
|
||||
arrayOf("streamtape", "stp", "stape").any(url) -> streamTapeExtractor.videosFromUrl(url, "$lang StreamTape")
|
||||
arrayOf("mixdrop").any(url) -> mixDropExtractor.videoFromUrl(url, prefix = "$lang ")
|
||||
arrayOf("uqload").any(url) -> uqloadExtractor.videosFromUrl(url, prefix = lang)
|
||||
"wolfstream" in url -> {
|
||||
client.newCall(GET(url, headers)).execute()
|
||||
.asJsoup()
|
||||
.selectFirst("script:containsData(sources)")
|
||||
?.data()
|
||||
?.let { jsData ->
|
||||
val videoUrl = jsData.substringAfter("{file:\"").substringBefore("\"")
|
||||
listOf(Video(videoUrl, "$lang WolfStream", videoUrl, headers = headers))
|
||||
}
|
||||
}
|
||||
arrayOf("mp4upload").any(url) -> mp4uploadExtractor.videosFromUrl(url, headers, prefix = "$lang ")
|
||||
arrayOf("vidhide", "filelions.top", "vid.").any(url) -> vidHideExtractor.videosFromUrl(url) { "$lang VidHide:$it" }
|
||||
arrayOf("wishembed", "streamwish", "strwish", "wish").any(url) -> streamWishExtractor.videosFromUrl(url, videoNameGen = { "$lang StreamWish:$it" })
|
||||
else -> null
|
||||
} ?: emptyList()
|
||||
} catch (e: Exception) {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Array<String>.any(url: String): Boolean = this.any { url.contains(it, ignoreCase = true) }
|
||||
|
||||
private fun extractFromMulti(url: String): List<Video> {
|
||||
val document = client.newCall(GET(url)).execute().asJsoup()
|
||||
val prefLang = preferences.getString(PREF_LANG_KEY, PREF_LANG_DEFAULT)!!
|
||||
|
@ -267,6 +293,7 @@ class AnimeOnlineNinja : DooPlay(
|
|||
|
||||
override val prefQualityValues = arrayOf("480p", "720p", "1080p")
|
||||
override val prefQualityEntries = prefQualityValues
|
||||
override val episodeNumberRegex by lazy { Regex("""(\d+(?:\.\d+)?)$""") }
|
||||
|
||||
companion object {
|
||||
private const val PREF_LANG_KEY = "preferred_lang"
|
||||
|
@ -276,7 +303,7 @@ class AnimeOnlineNinja : DooPlay(
|
|||
private const val PREF_SERVER_DEFAULT = "Uqload"
|
||||
private val PREF_LANG_ENTRIES = arrayOf("SUB", "All", "ES", "LAT")
|
||||
private val PREF_LANG_VALUES = arrayOf("SUB", "", "ES", "LAT")
|
||||
private val SERVER_LIST = arrayOf("Filemoon", "DoodStream", "StreamTape", "MixDrop", "Uqload", "WolfStream", "saidochesto.top")
|
||||
private val SERVER_LIST = arrayOf("Filemoon", "DoodStream", "StreamTape", "MixDrop", "Uqload", "WolfStream", "saidochesto.top", "VidHide", "StreamWish", "Mp4Upload")
|
||||
|
||||
private const val PREF_VRF_INTERCEPT_KEY = "vrf_intercept"
|
||||
private const val PREF_VRF_INTERCEPT_TITLE = "Intercept VRF links (Requiere Reiniciar)"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'CineCalidad'
|
||||
extClass = '.CineCalidad'
|
||||
extVersionCode = 2
|
||||
extVersionCode = 3
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Cuevana'
|
||||
extClass = '.CuevanaFactory'
|
||||
extVersionCode = 33
|
||||
extVersionCode = 34
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Doramasflix'
|
||||
extClass = '.Doramasflix'
|
||||
extVersionCode = 22
|
||||
extVersionCode = 23
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Doramasyt'
|
||||
extClass = '.Doramasyt'
|
||||
extVersionCode = 14
|
||||
extVersionCode = 15
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'HentaiLA'
|
||||
extClass = '.Hentaila'
|
||||
extVersionCode = 25
|
||||
extVersionCode = 26
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class Hentaila : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
|
||||
override val name = "Hentaila"
|
||||
|
||||
override val baseUrl = "https://www4.hentaila.com"
|
||||
override val baseUrl = "https://www5.hentaila.com"
|
||||
|
||||
override val lang = "es"
|
||||
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
ext {
|
||||
extName = 'HomeCine'
|
||||
extClass = '.HomeCine'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:burstcloud-extractor'))
|
||||
implementation(project(':lib:mp4upload-extractor'))
|
||||
implementation(project(':lib:streamwish-extractor'))
|
||||
implementation(project(':lib:voe-extractor'))
|
||||
implementation(project(':lib:yourupload-extractor'))
|
||||
implementation(project(':lib:fastream-extractor'))
|
||||
implementation(project(':lib:upstream-extractor'))
|
||||
implementation(project(':lib:filemoon-extractor'))
|
||||
}
|
|
@ -5,19 +5,27 @@ import android.content.SharedPreferences
|
|||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.lib.burstcloudextractor.BurstCloudExtractor
|
||||
import eu.kanade.tachiyomi.lib.fastreamextractor.FastreamExtractor
|
||||
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
||||
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
|
||||
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
||||
import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor
|
||||
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
|
||||
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
|
@ -25,7 +33,7 @@ class HomeCine : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
|
||||
override val name = "HomeCine"
|
||||
|
||||
override val baseUrl = "https://www3.homecine.tv"
|
||||
override val baseUrl = "https://homecine.cc"
|
||||
|
||||
override val lang = "es"
|
||||
|
||||
|
@ -45,116 +53,175 @@ class HomeCine : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
|
||||
|
||||
private const val PREF_SERVER_KEY = "preferred_server"
|
||||
private const val PREF_SERVER_DEFAULT = "Fastream"
|
||||
private val SERVER_LIST = arrayOf("Fastream")
|
||||
private const val PREF_SERVER_DEFAULT = "YourUpload"
|
||||
private val SERVER_LIST = arrayOf(
|
||||
"YourUpload",
|
||||
"BurstCloud",
|
||||
"Voe",
|
||||
"StreamWish",
|
||||
"Mp4Upload",
|
||||
"Fastream",
|
||||
"Upstream",
|
||||
"Filemoon",
|
||||
)
|
||||
}
|
||||
|
||||
override fun animeDetailsParse(response: Response): SAnime {
|
||||
val document = response.asJsoup()
|
||||
val animeDetails = SAnime.create().apply {
|
||||
title = document.selectFirst(".mvic-desc h1")?.text()?.trim() ?: ""
|
||||
status = if (document.location().contains("/series/")) SAnime.UNKNOWN else SAnime.COMPLETED
|
||||
description = document.selectFirst(".mvic-desc .f-desc")?.ownText()
|
||||
genre = document.select(".mvic-info [rel='category tag']").joinToString { it.text() }
|
||||
thumbnail_url = document.selectFirst("[itemprop=image]")?.attr("abs:src")?.replace("/w185/", "/w500/")
|
||||
document.select(".mvici-left p").map { it.text() }.map { textContent ->
|
||||
when {
|
||||
"Director" in textContent -> author = textContent.substringAfter("Director:").trim().split(", ").firstOrNull()
|
||||
"Actors" in textContent -> artist = textContent.substringAfter("Actors:").trim().split(", ").firstOrNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
return animeDetails
|
||||
}
|
||||
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/peliculas/page/$page", headers)
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/cartelera-series/page/$page", headers)
|
||||
|
||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val elements = document.select("[data-movie-id] > a")
|
||||
val nextPage = document.select(".pagination li a:contains(Last)").any()
|
||||
val elements = document.select(".post")
|
||||
val nextPage = document.select(".nav-links .current ~ a").any()
|
||||
val animeList = elements.map { element ->
|
||||
SAnime.create().apply {
|
||||
title = element.selectFirst(".mli-info")?.text()?.trim() ?: ""
|
||||
thumbnail_url = element.selectFirst("img")!!.attr("abs:data-original")
|
||||
setUrlWithoutDomain(element.attr("abs:href"))
|
||||
setUrlWithoutDomain(element.selectFirst(".lnk-blk")?.attr("abs:href") ?: "")
|
||||
title = element.selectFirst(".entry-header .entry-title")?.text() ?: ""
|
||||
description = element.select(".entry-content p").text() ?: ""
|
||||
thumbnail_url = element.selectFirst(".post-thumbnail figure img")?.let { getImageUrl(it) }
|
||||
}
|
||||
}
|
||||
return AnimesPage(animeList, nextPage)
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response) = popularAnimeParse(response)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = popularAnimeRequest(page)
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
|
||||
override fun latestUpdatesParse(response: Response) = popularAnimeParse(response)
|
||||
|
||||
return when {
|
||||
query.isNotBlank() -> GET("$baseUrl/page/$page/?s=$query", headers)
|
||||
genreFilter.state != 0 -> GET("$baseUrl/${genreFilter.toUriPart()}/page/$page", headers)
|
||||
else -> popularAnimeRequest(page)
|
||||
}
|
||||
}
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = GET("$baseUrl/?s=$query", headers)
|
||||
|
||||
override fun searchAnimeParse(response: Response) = popularAnimeParse(response)
|
||||
|
||||
override fun animeDetailsParse(response: Response): SAnime {
|
||||
val document = response.asJsoup()
|
||||
return SAnime.create().apply {
|
||||
title = document.selectFirst("aside .entry-header .entry-title")?.text() ?: ""
|
||||
description = document.select("aside .description p:not([class])").joinToString { it.text() }
|
||||
thumbnail_url = document.selectFirst(".post-thumbnail img")?.let { getImageUrl(it)?.replace("/w185/", "/w500/") }
|
||||
genre = document.select(".genres a").joinToString { it.text() }
|
||||
status = if (document.location().contains("pelicula")) SAnime.COMPLETED else SAnime.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
private fun getImageUrl(element: Element): String? {
|
||||
return when {
|
||||
element.hasAttr("data-src") -> element.attr("abs:data-src")
|
||||
element.hasAttr("src") -> element.attr("abs:src")
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val document = response.asJsoup()
|
||||
return if (document.location().contains("/series/")) {
|
||||
var episodeCounter = 1F
|
||||
document.select(".tvseason").flatMap { season ->
|
||||
val noSeason = season.select(".les-title strong").text().substringAfter("Temporada").trim()
|
||||
season.select(".les-content a").map { ep ->
|
||||
SEpisode.create().apply {
|
||||
episode_number = episodeCounter++
|
||||
name = "T$noSeason - ${ep.text().trim()}"
|
||||
setUrlWithoutDomain(ep.attr("abs:href"))
|
||||
}
|
||||
}
|
||||
}.reversed()
|
||||
} else {
|
||||
val referer = response.request.url.toString()
|
||||
return if (referer.contains("pelicula")) {
|
||||
listOf(
|
||||
SEpisode.create().apply {
|
||||
episode_number = 1f
|
||||
name = "PELÍCULA"
|
||||
setUrlWithoutDomain(document.location())
|
||||
name = "Película"
|
||||
setUrlWithoutDomain(referer)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
val chunkSize = Runtime.getRuntime().availableProcessors()
|
||||
document.select(".sel-temp a")
|
||||
.sortedByDescending { it.attr("data-season") }
|
||||
.chunked(chunkSize).flatMap { chunk ->
|
||||
chunk.parallelCatchingFlatMapBlocking { season ->
|
||||
getDetailSeason(season, referer)
|
||||
}
|
||||
}.sortedByDescending {
|
||||
it.name.substringBeforeLast("-")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDetailSeason(element: Element, referer: String): List<SEpisode> {
|
||||
return try {
|
||||
val post = element.attr("data-post")
|
||||
val season = element.attr("data-season")
|
||||
val formBody = FormBody.Builder()
|
||||
.add("action", "action_select_season")
|
||||
.add("season", season)
|
||||
.add("post", post)
|
||||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url("$baseUrl/wp-admin/admin-ajax.php")
|
||||
.post(formBody)
|
||||
.header("Origin", baseUrl)
|
||||
.header("Referer", referer)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.build()
|
||||
val detail = client.newCall(request).execute().asJsoup()
|
||||
|
||||
detail.select(".post").reversed().mapIndexed { idx, it ->
|
||||
val epNumber = try {
|
||||
it.select(".entry-header .num-epi").text().substringAfter("x").substringBefore("–").trim()
|
||||
} catch (_: Exception) { "${idx + 1}" }
|
||||
|
||||
SEpisode.create().apply {
|
||||
setUrlWithoutDomain(it.select("a").attr("abs:href"))
|
||||
name = "T$season - Episodio $epNumber"
|
||||
episode_number = epNumber.toFloat()
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
|
||||
return document.select(".movieplay iframe").parallelCatchingFlatMapBlocking {
|
||||
val link = it.attr("abs:src")
|
||||
val videoList = mutableListOf<Video>()
|
||||
document.select(".aa-tbs-video a").forEach {
|
||||
val prefix = runCatching {
|
||||
val tabElement = it.closest("[id*=tab]") ?: return@runCatching ""
|
||||
val tabName = tabElement.attr("id")
|
||||
val lang = document.selectFirst("[href=\"#$tabName\"]")?.ownText()?.trim() ?: ""
|
||||
val lang = it.select(".server").text().lowercase()
|
||||
when {
|
||||
lang.lowercase().contains("latino") -> "[LAT]"
|
||||
lang.lowercase().contains("castellano") -> "[CAST]"
|
||||
lang.lowercase().contains("subtitulado") -> "[SUB]"
|
||||
lang.contains("latino") -> "[LAT]"
|
||||
lang.contains("castellano") -> "[CAST]"
|
||||
lang.contains("sub") || lang.contains("vose") -> "[SUB]"
|
||||
else -> ""
|
||||
}
|
||||
}.getOrDefault("")
|
||||
|
||||
serverVideoResolver(link, prefix)
|
||||
}
|
||||
}
|
||||
val ide = it.attr("href")
|
||||
var src = document.select("$ide iframe").attr("data-src").replace("#038;", "&").replace("&", "")
|
||||
try {
|
||||
if (src.contains("home")) {
|
||||
src = client.newCall(GET(src)).execute().asJsoup().selectFirst("iframe")?.attr("src") ?: ""
|
||||
}
|
||||
|
||||
private fun serverVideoResolver(url: String, prefix: String = ""): List<Video> {
|
||||
val embedUrl = url.lowercase()
|
||||
return when {
|
||||
embedUrl.contains("fastream") -> {
|
||||
val link = if (url.contains("emb.html")) "https://fastream.to/embed-${url.split("/").last()}.html" else url
|
||||
FastreamExtractor(client, headers).videosFromUrl(link, prefix = "$prefix Fastream:")
|
||||
}
|
||||
else -> emptyList()
|
||||
if (src.contains("fastream")) {
|
||||
if (src.contains("emb.html")) {
|
||||
val key = src.split("/").last()
|
||||
src = "https://fastream.to/embed-$key.html"
|
||||
}
|
||||
FastreamExtractor(client, headers).videosFromUrl(src, needsSleep = false, prefix = "$prefix Fastream:").also(videoList::addAll)
|
||||
}
|
||||
if (src.contains("upstream")) {
|
||||
UpstreamExtractor(client).videosFromUrl(src, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("yourupload")) {
|
||||
YourUploadExtractor(client).videoFromUrl(src, headers, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("voe")) {
|
||||
VoeExtractor(client).videosFromUrl(src, prefix = "$prefix ").also(videoList::addAll)
|
||||
}
|
||||
if (src.contains("wishembed") || src.contains("streamwish") || src.contains("wish")) {
|
||||
StreamWishExtractor(client, headers).videosFromUrl(src) { "$prefix StreamWish:$it" }.also(videoList::addAll)
|
||||
}
|
||||
if (src.contains("mp4upload")) {
|
||||
Mp4uploadExtractor(client).videosFromUrl(src, headers, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("burst")) {
|
||||
BurstCloudExtractor(client).videoFromUrl(src, headers = headers, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("filemoon") || src.contains("moonplayer")) {
|
||||
FilemoonExtractor(client).videosFromUrl(src, headers = headers, prefix = "$prefix Filemoon:").let { videoList.addAll(it) }
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
return videoList
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
|
@ -171,25 +238,6 @@ class HomeCine : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
).reversed()
|
||||
}
|
||||
|
||||
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
|
||||
AnimeFilter.Header("La busqueda por texto ignora el filtro"),
|
||||
GenreFilter(),
|
||||
)
|
||||
|
||||
private class GenreFilter : UriPartFilter(
|
||||
"Género",
|
||||
arrayOf(
|
||||
Pair("<Seleccionar>", ""),
|
||||
Pair("Películas", "peliculas"),
|
||||
Pair("Series", "series"),
|
||||
),
|
||||
)
|
||||
|
||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_LANGUAGE_KEY
|
||||
|
@ -207,22 +255,6 @@ class HomeCine : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
}
|
||||
}.also(screen::addPreference)
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_SERVER_KEY
|
||||
title = "Preferred server"
|
||||
entries = SERVER_LIST
|
||||
entryValues = SERVER_LIST
|
||||
setDefaultValue(PREF_SERVER_DEFAULT)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_QUALITY_KEY
|
||||
title = "Preferred quality"
|
||||
|
@ -238,5 +270,21 @@ class HomeCine : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_SERVER_KEY
|
||||
title = "Preferred server"
|
||||
entries = SERVER_LIST
|
||||
entryValues = SERVER_LIST
|
||||
setDefaultValue(PREF_SERVER_DEFAULT)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Jkanime'
|
||||
extClass = '.Jkanime'
|
||||
extVersionCode = 24
|
||||
extVersionCode = 25
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'MetroSeries'
|
||||
extClass = '.MetroSeries'
|
||||
extVersionCode = 11
|
||||
extVersionCode = 12
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -20,25 +20,20 @@ import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor
|
|||
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
|
||||
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import eu.kanade.tachiyomi.util.parallelMapBlocking
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class MetroSeries : ConfigurableAnimeSource, AnimeHttpSource() {
|
||||
|
||||
override val name = "MetroSeries"
|
||||
|
||||
override val baseUrl = "https://metroseries.net"
|
||||
override val baseUrl = "https://www3.seriesmetro.net"
|
||||
|
||||
override val lang = "es"
|
||||
|
||||
|
@ -71,11 +66,11 @@ class MetroSeries : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
)
|
||||
}
|
||||
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/series/page/$page", headers)
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/cartelera-series/page/$page", headers)
|
||||
|
||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val elements = document.select(".post-list, .results-post > .post")
|
||||
val elements = document.select(".post")
|
||||
val nextPage = document.select(".nav-links .current ~ a").any()
|
||||
val animeList = elements.map { element ->
|
||||
SAnime.create().apply {
|
||||
|
@ -99,11 +94,11 @@ class MetroSeries : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
override fun animeDetailsParse(response: Response): SAnime {
|
||||
val document = response.asJsoup()
|
||||
return SAnime.create().apply {
|
||||
title = document.selectFirst("main .entry-header .entry-title")?.text() ?: ""
|
||||
description = document.select("main .entry-content p").joinToString { it.text() }
|
||||
thumbnail_url = document.selectFirst("main .post-thumbnail img")?.let { getImageUrl(it) }
|
||||
genre = document.select("main .entry-content .tagcloud a").joinToString { it.text() }
|
||||
status = SAnime.UNKNOWN
|
||||
title = document.selectFirst("aside .entry-header .entry-title")?.text() ?: ""
|
||||
description = document.select("aside .description p:not([class])").joinToString { it.text() }
|
||||
thumbnail_url = document.selectFirst(".post-thumbnail img")?.let { getImageUrl(it)?.replace("/w185/", "/w500/") }
|
||||
genre = document.select(".genres a").joinToString { it.text() }
|
||||
status = if (document.location().contains("pelicula")) SAnime.COMPLETED else SAnime.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,34 +113,40 @@ class MetroSeries : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val document = response.asJsoup()
|
||||
val referer = response.request.url.toString()
|
||||
val chunkSize = Runtime.getRuntime().availableProcessors()
|
||||
val objectNumber = document.select("#aa-season").attr("data-object")
|
||||
val episodes = document.select(".season-list li a")
|
||||
.sortedByDescending { it.attr("data-season") }
|
||||
.chunked(chunkSize).flatMap { chunk ->
|
||||
chunk.parallelCatchingFlatMapBlocking { season ->
|
||||
val pages = getDetailSeason(season, objectNumber, referer)
|
||||
getPageEpisodeList(pages, referer, objectNumber, season.attr("data-season"))
|
||||
return if (referer.contains("pelicula")) {
|
||||
listOf(
|
||||
SEpisode.create().apply {
|
||||
episode_number = 1f
|
||||
name = "Película"
|
||||
setUrlWithoutDomain(referer)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
val chunkSize = Runtime.getRuntime().availableProcessors()
|
||||
document.select(".sel-temp a")
|
||||
.sortedByDescending { it.attr("data-season") }
|
||||
.chunked(chunkSize).flatMap { chunk ->
|
||||
chunk.parallelCatchingFlatMapBlocking { season ->
|
||||
getDetailSeason(season, referer)
|
||||
}
|
||||
}.sortedByDescending {
|
||||
it.name.substringBeforeLast("-")
|
||||
}
|
||||
}.sortedByDescending {
|
||||
it.name.substringBeforeLast("-")
|
||||
}
|
||||
return episodes
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDetailSeason(element: Element, objectNumber: String, referer: String): IntRange {
|
||||
try {
|
||||
private fun getDetailSeason(element: Element, referer: String): List<SEpisode> {
|
||||
return try {
|
||||
val post = element.attr("data-post")
|
||||
val season = element.attr("data-season")
|
||||
val formBody = FormBody.Builder()
|
||||
.add("action", "action_select_season")
|
||||
.add("season", season)
|
||||
.add("post", post)
|
||||
.add("object", objectNumber)
|
||||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url("https://metroseries.net/wp-admin/admin-ajax.php")
|
||||
.url("$baseUrl/wp-admin/admin-ajax.php")
|
||||
.post(formBody)
|
||||
.header("Origin", baseUrl)
|
||||
.header("Referer", referer)
|
||||
|
@ -153,110 +154,72 @@ class MetroSeries : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
.build()
|
||||
val detail = client.newCall(request).execute().asJsoup()
|
||||
|
||||
val firstPage = try { detail.selectFirst("#aa-season > nav > span.page-numbers")?.text()?.toInt() ?: 1 } catch (_: Exception) { 1 }
|
||||
val lastPage = try { detail.select(".pagination a.page-numbers:not(.next)").last()?.text()?.toInt() ?: firstPage } catch (_: Exception) { firstPage }
|
||||
detail.select(".post").reversed().mapIndexed { idx, it ->
|
||||
val epNumber = try {
|
||||
it.select(".entry-header .num-epi").text().substringAfter("x").substringBefore("–").trim()
|
||||
} catch (_: Exception) { "${idx + 1}" }
|
||||
|
||||
return firstPage.rangeTo(lastPage)
|
||||
} catch (_: Exception) {
|
||||
return 1..1
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPageEpisodeList(pages: IntRange, referer: String, objectNumber: String, season: String): List<SEpisode> {
|
||||
val episodes = mutableListOf<SEpisode>()
|
||||
try {
|
||||
pages.parallelMapBlocking {
|
||||
val formBody = FormBody.Builder()
|
||||
.add("action", "action_pagination_ep")
|
||||
.add("page", "$it")
|
||||
.add("object", objectNumber)
|
||||
.add("season", season)
|
||||
.build()
|
||||
|
||||
val requestPage = Request.Builder()
|
||||
.url("https://metroseries.net/wp-admin/admin-ajax.php")
|
||||
.post(formBody)
|
||||
.header("authority", baseUrl.toHttpUrl().host)
|
||||
.header("Origin", "https://${baseUrl.toHttpUrl().host}")
|
||||
.header("Referer", referer)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.build()
|
||||
|
||||
client.newCall(requestPage).await().parseAsEpisodeList().also(episodes::addAll)
|
||||
SEpisode.create().apply {
|
||||
setUrlWithoutDomain(it.select("a").attr("abs:href"))
|
||||
name = "T$season - Episodio $epNumber"
|
||||
episode_number = epNumber.toFloat()
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) { }
|
||||
return episodes
|
||||
} catch (_: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
val videoList = mutableListOf<Video>()
|
||||
val termId = document.select("#option-players").attr("data-term")
|
||||
document.select(".player-options-list li a").forEach {
|
||||
document.select(".aa-tbs-video a").forEach {
|
||||
val prefix = runCatching {
|
||||
val lang = it.select(".option").text().lowercase()
|
||||
val lang = it.select(".server").text().lowercase()
|
||||
when {
|
||||
lang.contains("latino") -> "[LAT]"
|
||||
lang.contains("castellano") -> "[CAST]"
|
||||
lang.contains("sub") -> "[SUB]"
|
||||
lang.contains("sub") || lang.contains("vose") -> "[SUB]"
|
||||
else -> ""
|
||||
}
|
||||
}.getOrDefault("")
|
||||
|
||||
val ide = it.attr("data-opt")
|
||||
val formBody = FormBody.Builder()
|
||||
.add("action", "action_player_series")
|
||||
.add("ide", ide)
|
||||
.add("term_id", termId)
|
||||
.build()
|
||||
val ide = it.attr("href")
|
||||
var src = document.select("$ide iframe").attr("data-src").replace("#038;", "&").replace("&", "")
|
||||
try {
|
||||
if (src.contains("metro")) {
|
||||
src = client.newCall(GET(src)).execute().asJsoup().selectFirst("iframe")?.attr("src") ?: ""
|
||||
}
|
||||
|
||||
val postRequest = Request.Builder()
|
||||
.url("https://metroseries.net/wp-admin/admin-ajax.php")
|
||||
.post(formBody)
|
||||
.header("Origin", baseUrl)
|
||||
.header("Referer", response.request.url.toString())
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.build()
|
||||
|
||||
val playerDocument = client.newCall(postRequest).execute().asJsoup()
|
||||
playerDocument.select("iframe").forEach {
|
||||
var src = it.attr("src").replace("#038;", "&").replace("&", "")
|
||||
try {
|
||||
if (src.contains("metroseries")) {
|
||||
src = client.newCall(GET(src)).execute().asJsoup().selectFirst("iframe")?.attr("src") ?: ""
|
||||
if (src.contains("fastream")) {
|
||||
if (src.contains("emb.html")) {
|
||||
val key = src.split("/").last()
|
||||
src = "https://fastream.to/embed-$key.html"
|
||||
}
|
||||
|
||||
if (src.contains("fastream")) {
|
||||
if (src.contains("emb.html")) {
|
||||
val key = src.split("/").last()
|
||||
src = "https://fastream.to/embed-$key.html"
|
||||
}
|
||||
FastreamExtractor(client, headers).videosFromUrl(src, needsSleep = false, prefix = "$prefix Fastream:").also(videoList::addAll)
|
||||
}
|
||||
|
||||
if (src.contains("upstream")) {
|
||||
UpstreamExtractor(client).videosFromUrl(src, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("yourupload")) {
|
||||
YourUploadExtractor(client).videoFromUrl(src, headers, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("voe")) {
|
||||
VoeExtractor(client).videosFromUrl(src, prefix = "$prefix ").also(videoList::addAll)
|
||||
}
|
||||
if (src.contains("wishembed") || src.contains("streamwish") || src.contains("wish")) {
|
||||
StreamWishExtractor(client, headers).videosFromUrl(src) { "$prefix StreamWish:$it" }.also(videoList::addAll)
|
||||
}
|
||||
if (src.contains("mp4upload")) {
|
||||
Mp4uploadExtractor(client).videosFromUrl(src, headers, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("burst")) {
|
||||
BurstCloudExtractor(client).videoFromUrl(src, headers = headers, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("filemoon") || src.contains("moonplayer")) {
|
||||
FilemoonExtractor(client).videosFromUrl(src, headers = headers, prefix = "$prefix Filemoon:").let { videoList.addAll(it) }
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
FastreamExtractor(client, headers).videosFromUrl(src, needsSleep = false, prefix = "$prefix Fastream:").also(videoList::addAll)
|
||||
}
|
||||
if (src.contains("upstream")) {
|
||||
UpstreamExtractor(client).videosFromUrl(src, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("yourupload")) {
|
||||
YourUploadExtractor(client).videoFromUrl(src, headers, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("voe")) {
|
||||
VoeExtractor(client).videosFromUrl(src, prefix = "$prefix ").also(videoList::addAll)
|
||||
}
|
||||
if (src.contains("wishembed") || src.contains("streamwish") || src.contains("wish")) {
|
||||
StreamWishExtractor(client, headers).videosFromUrl(src) { "$prefix StreamWish:$it" }.also(videoList::addAll)
|
||||
}
|
||||
if (src.contains("mp4upload")) {
|
||||
Mp4uploadExtractor(client).videosFromUrl(src, headers, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("burst")) {
|
||||
BurstCloudExtractor(client).videoFromUrl(src, headers = headers, prefix = "$prefix ").let { videoList.addAll(it) }
|
||||
}
|
||||
if (src.contains("filemoon") || src.contains("moonplayer")) {
|
||||
FilemoonExtractor(client).videosFromUrl(src, headers = headers, prefix = "$prefix Filemoon:").let { videoList.addAll(it) }
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
return videoList
|
||||
}
|
||||
|
@ -324,21 +287,4 @@ class MetroSeries : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
|
||||
private fun Response.parseAsEpisodeList(): List<SEpisode> {
|
||||
return asJsoup().select(".episodes-list li a").reversed().mapIndexed { idx, it ->
|
||||
val epNumber = try { it.ownText().substringAfter("x").substringBefore("–").trim() } catch (_: Exception) { "${idx + 1}" }
|
||||
val season = it.ownText().substringBefore("x").trim()
|
||||
SEpisode.create().apply {
|
||||
setUrlWithoutDomain(it.attr("abs:href"))
|
||||
name = "T$season - E$epNumber - ${it.ownText().substringAfter("–").trim()}"
|
||||
episode_number = epNumber.toFloat()
|
||||
date_upload = try {
|
||||
SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH).parse(it.select("span").text()).time
|
||||
} catch (_: Exception) {
|
||||
System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'MonosChinos'
|
||||
extClass = '.MonosChinos'
|
||||
extVersionCode = 28
|
||||
extVersionCode = 29
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
12
src/es/pandrama/build.gradle
Normal file
|
@ -0,0 +1,12 @@
|
|||
ext {
|
||||
extName = 'Pandrama'
|
||||
extClass = '.Pandrama'
|
||||
extVersionCode = 1
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:okru-extractor'))
|
||||
implementation(project(':lib:vk-extractor'))
|
||||
}
|
BIN
src/es/pandrama/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
src/es/pandrama/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
src/es/pandrama/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6 KiB |
BIN
src/es/pandrama/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
src/es/pandrama/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,299 @@
|
|||
package eu.kanade.tachiyomi.animeextension.es.pandrama
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
||||
import eu.kanade.tachiyomi.lib.vkextractor.VkExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.net.URLDecoder
|
||||
|
||||
class Pandrama : ConfigurableAnimeSource, AnimeHttpSource() {
|
||||
|
||||
override val name = "Pandrama"
|
||||
|
||||
override val baseUrl = "https://pandra.ma"
|
||||
|
||||
override val lang = "es"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
companion object {
|
||||
private const val PREF_QUALITY_KEY = "preferred_quality"
|
||||
private const val PREF_QUALITY_DEFAULT = "1080"
|
||||
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
|
||||
|
||||
private const val PREF_SERVER_KEY = "preferred_server"
|
||||
private const val PREF_SERVER_DEFAULT = "Vk"
|
||||
private val SERVER_LIST = arrayOf("Vk", "Okru")
|
||||
}
|
||||
|
||||
override fun animeDetailsParse(response: Response): SAnime {
|
||||
val document = response.asJsoup()
|
||||
val details = SAnime.create()
|
||||
|
||||
for (element in document.select(".hl-full-box ul li")) {
|
||||
val title = element.select("em").text()
|
||||
val content = element.select("span")
|
||||
|
||||
when {
|
||||
title.contains("Director:") -> details.author = element.ownText().trim()
|
||||
title.contains("Estado:") -> details.status = parseStatus(content.text())
|
||||
title.contains("Protagonistas:") -> details.artist = element.selectFirst("a")?.text()
|
||||
title.contains("Género:") -> details.genre = element.select("a").joinToString { it.text() }
|
||||
title.contains("Sinopsis:") -> details.description = element.ownText().trim()
|
||||
}
|
||||
}
|
||||
|
||||
return details
|
||||
}
|
||||
|
||||
private fun parseStatus(status: String?) = when (status.orEmpty()) {
|
||||
"En Emisión" -> SAnime.ONGOING
|
||||
"Finalizado" -> SAnime.COMPLETED
|
||||
else -> SAnime.UNKNOWN
|
||||
}
|
||||
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/vodshow/Dramas--------$page---/", headers)
|
||||
|
||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val elements = document.select(".hl-vod-list li > a")
|
||||
val nextPage = document.select(".hl-page-wrap a:not(.hl-disad) span:contains(siguiente)").any()
|
||||
val animeList = elements.map { element ->
|
||||
val langTag = element.select(".hl-pic-tag").text().trim()
|
||||
val prefix = when {
|
||||
langTag.contains("Español LAT") -> "\uD83C\uDDF2\uD83C\uDDFD "
|
||||
langTag.contains("Español ES") -> "\uD83C\uDDEA\uD83C\uDDF8 "
|
||||
else -> ""
|
||||
}
|
||||
SAnime.create().apply {
|
||||
title = """$prefix ${element.attr("title")}""".trim()
|
||||
thumbnail_url = element.attr("abs:data-original")
|
||||
setUrlWithoutDomain(element.attr("abs:href"))
|
||||
}
|
||||
}
|
||||
return AnimesPage(animeList, nextPage)
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response) = popularAnimeParse(response)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/vodshow/Dramas--hits------$page---/", headers)
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val url = when {
|
||||
query.isNotBlank() -> "$baseUrl/vodsearch/$query----------$page---/"
|
||||
else -> "$baseUrl/vodshow/Dramas--------$page---/"
|
||||
}
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
if (!document.location().contains("vodsearch")) return popularAnimeParse(response)
|
||||
val elements = document.select(".hl-one-list .hl-item-thumb")
|
||||
val nextPage = document.select(".hl-page-wrap a:not(.hl-disad) span:contains(siguiente)").any()
|
||||
val animeList = elements.map { element ->
|
||||
SAnime.create().apply {
|
||||
title = element.attr("title")
|
||||
thumbnail_url = element.attr("abs:data-original")
|
||||
setUrlWithoutDomain(element.attr("abs:href"))
|
||||
}
|
||||
}
|
||||
return AnimesPage(animeList, nextPage)
|
||||
}
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val document = response.asJsoup()
|
||||
return document.select(".hl-plays-list li > a").groupBy { it.text().trim() }.map {
|
||||
val urlList = json.encodeToString(it.value.map { it.attr("abs:href") })
|
||||
SEpisode.create().apply {
|
||||
name = it.key
|
||||
episode_number = it.key.substringAfter("Ep.").trim().toFloatOrNull() ?: 0F
|
||||
url = urlList
|
||||
}
|
||||
}.reversed()
|
||||
}
|
||||
|
||||
override suspend fun getVideoList(episode: SEpisode): List<Video> {
|
||||
val serverData = json.decodeFromString<List<String>>(episode.url)
|
||||
return serverData.parallelCatchingFlatMapBlocking {
|
||||
val page = client.newCall(GET(it)).execute().asJsoup()
|
||||
val jsonData = page.selectFirst("script:containsData(var player_aaaa)")
|
||||
?.data()?.substringAfter("var player_aaaa=")?.trim()
|
||||
?: return@parallelCatchingFlatMapBlocking emptyList()
|
||||
val player = json.decodeFromString<PlayerDto>(jsonData)
|
||||
val url = if (player.encrypt == 2) {
|
||||
URLDecoder.decode(base64decode(player.url ?: ""), "UTF-8")
|
||||
} else {
|
||||
URLDecoder.decode(player.url ?: "", "UTF-8")
|
||||
}
|
||||
serverVideoResolver(url)
|
||||
}
|
||||
}
|
||||
|
||||
private val okruExtractor by lazy { OkruExtractor(client) }
|
||||
private val vkExtractor by lazy { VkExtractor(client, headers) }
|
||||
|
||||
private fun serverVideoResolver(url: String): List<Video> {
|
||||
val embedUrl = url.lowercase()
|
||||
return when {
|
||||
embedUrl.contains("ok.ru") || embedUrl.contains("okru") -> okruExtractor.videosFromUrl(url)
|
||||
embedUrl.contains("vk.") -> vkExtractor.videosFromUrl(url)
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
|
||||
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
|
||||
return this.sortedWith(
|
||||
compareBy(
|
||||
{ it.quality.contains(server, true) },
|
||||
{ it.quality.contains(quality) },
|
||||
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
|
||||
),
|
||||
).reversed()
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_SERVER_KEY
|
||||
title = "Preferred server"
|
||||
entries = SERVER_LIST
|
||||
entryValues = SERVER_LIST
|
||||
setDefaultValue(PREF_SERVER_DEFAULT)
|
||||
summary = "%d"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_QUALITY_KEY
|
||||
title = "Preferred quality"
|
||||
entries = QUALITY_LIST
|
||||
entryValues = QUALITY_LIST
|
||||
setDefaultValue(PREF_QUALITY_DEFAULT)
|
||||
summary = "%d"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
|
||||
private val base64DecodeChars = intArrayOf(
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26,
|
||||
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
|
||||
43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
|
||||
)
|
||||
|
||||
private fun base64decode(str: String): String {
|
||||
var c1: Int
|
||||
var c2: Int
|
||||
var c3: Int
|
||||
var c4: Int
|
||||
var i = 0
|
||||
val len = str.length
|
||||
val out = StringBuilder()
|
||||
while (i < len) {
|
||||
do {
|
||||
c1 = base64DecodeChars[str[i].toInt() and 255]
|
||||
i++
|
||||
} while (i < len && c1 == -1)
|
||||
if (c1 == -1) break
|
||||
do {
|
||||
c2 = base64DecodeChars[str[i].toInt() and 255]
|
||||
i++
|
||||
} while (i < len && c2 == -1)
|
||||
if (c2 == -1) break
|
||||
out.append(((c1 shl 2) or ((c2 and 48) shr 4)).toChar())
|
||||
do {
|
||||
c3 = str[i].toInt() and 255
|
||||
if (c3 == 61) return out.toString()
|
||||
c3 = base64DecodeChars[c3]
|
||||
i++
|
||||
} while (i < len && c3 == -1)
|
||||
if (c3 == -1) break
|
||||
out.append((((c2 and 15) shl 4) or ((c3 and 60) shr 2)).toChar())
|
||||
do {
|
||||
c4 = str[i].toInt() and 255
|
||||
if (c4 == 61) return out.toString()
|
||||
c4 = base64DecodeChars[c4]
|
||||
i++
|
||||
} while (i < len && c4 == -1)
|
||||
if (c4 == -1) break
|
||||
out.append((((c3 and 3) shl 6) or c4).toChar())
|
||||
}
|
||||
return out.toString()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class PlayerDto(
|
||||
@SerialName("flag") var flag: String? = null,
|
||||
@SerialName("encrypt") var encrypt: Int? = null,
|
||||
@SerialName("trysee") var trysee: Int? = null,
|
||||
@SerialName("points") var points: Int? = null,
|
||||
@SerialName("link") var link: String? = null,
|
||||
@SerialName("poster") var poster: String? = null,
|
||||
@SerialName("doblado") var doblado: String? = null,
|
||||
@SerialName("vod_en_py") var vodEnPy: String? = null,
|
||||
@SerialName("link_next") var linkNext: String? = null,
|
||||
@SerialName("link_pre") var linkPre: String? = null,
|
||||
@SerialName("vod_data") var vodData: VodData? = VodData(),
|
||||
@SerialName("url") var url: String? = null,
|
||||
@SerialName("url_next") var urlNext: String? = null,
|
||||
@SerialName("from") var from: String? = null,
|
||||
@SerialName("server") var server: String? = null,
|
||||
@SerialName("note") var note: String? = null,
|
||||
@SerialName("id") var id: String? = null,
|
||||
@SerialName("sid") var sid: Int? = null,
|
||||
@SerialName("nid") var nid: Int? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class VodData(
|
||||
@SerialName("vod_name") var vodName: String? = null,
|
||||
@SerialName("vod_actor") var vodActor: String? = null,
|
||||
@SerialName("vod_director") var vodDirector: String? = null,
|
||||
@SerialName("vod_class") var vodClass: String? = null,
|
||||
)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'PelisForte'
|
||||
extClass = '.PelisForte'
|
||||
extVersionCode = 16
|
||||
extVersionCode = 17
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -21,4 +21,5 @@ dependencies {
|
|||
implementation(project(':lib:playlist-utils'))
|
||||
implementation(project(':lib:streamlare-extractor'))
|
||||
implementation(project(':lib:okru-extractor'))
|
||||
implementation(project(':lib:vidguard-extractor'))
|
||||
}
|
|
@ -23,10 +23,12 @@ import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
|||
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
||||
import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor
|
||||
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
|
||||
import eu.kanade.tachiyomi.lib.vidguardextractor.VidGuardExtractor
|
||||
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
|
||||
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
@ -62,6 +64,7 @@ open class PelisForte : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
"YourUpload", "Voe", "Mp4Upload", "Doodstream",
|
||||
"Upload", "BurstCloud", "Upstream", "StreamTape",
|
||||
"Fastream", "Filemoon", "StreamWish", "Okru",
|
||||
"VidGuard",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -149,83 +152,70 @@ open class PelisForte : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
val videoList = mutableListOf<Video>()
|
||||
document.select(".video-player iframe").forEach {
|
||||
try {
|
||||
val id = it.parent()?.attr("id")
|
||||
val idTab = document.selectFirst("[href=\"#$id\"]")?.closest(".lrt")?.attr("id")
|
||||
val lang = document.select("[tab=$idTab]").text()
|
||||
val src = it.attr("src").ifEmpty { it.attr("data-src") }
|
||||
val key = src.substringAfter("/?h=")
|
||||
val player = "https://${src.toHttpUrl().host}/r.php?h=$key"
|
||||
val prefix = when {
|
||||
lang.contains("Latino", true) -> "[LAT]"
|
||||
lang.contains("Subtitulado", true) -> "[SUB]"
|
||||
lang.contains("Castellano", true) -> "[CAST]"
|
||||
else -> ""
|
||||
}
|
||||
val locationsDdh = client.newCall(GET(player, headers = headers.newBuilder().add("referer", src).build()))
|
||||
.execute().networkResponse.toString()
|
||||
return document.select(".video-player iframe").parallelCatchingFlatMapBlocking {
|
||||
val id = it.parent()?.attr("id")
|
||||
val idTab = document.selectFirst("[href=\"#$id\"]")?.closest(".lrt")?.attr("id")
|
||||
val lang = document.select("[tab=$idTab]").text()
|
||||
val src = it.attr("src").ifEmpty { it.attr("data-src") }
|
||||
val key = src.substringAfter("/?h=")
|
||||
val player = "https://${src.toHttpUrl().host}/r.php?h=$key"
|
||||
|
||||
fetchUrls(locationsDdh).forEach {
|
||||
serverVideoResolver(it, prefix, src).also(videoList::addAll)
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
val prefix = when {
|
||||
lang.contains("Latino", true) -> "[LAT]"
|
||||
lang.contains("Subtitulado", true) -> "[SUB]"
|
||||
lang.contains("Castellano", true) -> "[CAST]"
|
||||
else -> ""
|
||||
}
|
||||
|
||||
val locationsDdh = client.newCall(
|
||||
GET(player, headers = headers.newBuilder().add("referer", src).build()),
|
||||
).execute().networkResponse.toString()
|
||||
|
||||
fetchUrls(locationsDdh).flatMap { serverVideoResolver(it, prefix) }
|
||||
}
|
||||
return videoList
|
||||
}
|
||||
|
||||
private fun serverVideoResolver(url: String, prefix: String = "", referer: String = ""): List<Video> {
|
||||
val videoList = mutableListOf<Video>()
|
||||
val embedUrl = url.lowercase()
|
||||
try {
|
||||
if (embedUrl.contains("voe")) {
|
||||
VoeExtractor(client).videosFromUrl(url, prefix).also(videoList::addAll)
|
||||
/*--------------------------------Video extractors------------------------------------*/
|
||||
private val voeExtractor by lazy { VoeExtractor(client) }
|
||||
private val okruExtractor by lazy { OkruExtractor(client) }
|
||||
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
|
||||
private val uqloadExtractor by lazy { UqloadExtractor(client) }
|
||||
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
|
||||
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
|
||||
private val doodExtractor by lazy { DoodExtractor(client) }
|
||||
private val streamlareExtractor by lazy { StreamlareExtractor(client) }
|
||||
private val yourUploadExtractor by lazy { YourUploadExtractor(client) }
|
||||
private val burstCloudExtractor by lazy { BurstCloudExtractor(client) }
|
||||
private val fastreamExtractor by lazy { FastreamExtractor(client, headers) }
|
||||
private val upstreamExtractor by lazy { UpstreamExtractor(client) }
|
||||
private val streamTapeExtractor by lazy { StreamTapeExtractor(client) }
|
||||
private val vidGuardExtractor by lazy { VidGuardExtractor(client) }
|
||||
|
||||
private fun serverVideoResolver(url: String, prefix: String = ""): List<Video> {
|
||||
return runCatching {
|
||||
when {
|
||||
arrayOf("voe").any(url) -> voeExtractor.videosFromUrl(url, "$prefix ")
|
||||
arrayOf("ok.ru", "okru").any(url) -> okruExtractor.videosFromUrl(url, prefix)
|
||||
arrayOf("filemoon", "moonplayer").any(url) -> filemoonExtractor.videosFromUrl(url, prefix = "$prefix Filemoon:")
|
||||
arrayOf("uqload").any(url) -> uqloadExtractor.videosFromUrl(url, prefix)
|
||||
arrayOf("mp4upload").any(url) -> mp4uploadExtractor.videosFromUrl(url, headers, prefix = "$prefix ")
|
||||
arrayOf("wishembed", "streamwish", "strwish", "wish").any(url) -> {
|
||||
streamWishExtractor.videosFromUrl(url, videoNameGen = { "$prefix StreamWish:$it" })
|
||||
}
|
||||
arrayOf("doodstream", "dood.", "ds2play", "doods.").any(url) -> {
|
||||
val url2 = url.replace("https://doodstream.com/e/", "https://d0000d.com/e/")
|
||||
doodExtractor.videosFromUrl(url2, "$prefix DoodStream")
|
||||
}
|
||||
arrayOf("streamlare").any(url) -> streamlareExtractor.videosFromUrl(url, prefix)
|
||||
arrayOf("yourupload", "upload").any(url) -> yourUploadExtractor.videoFromUrl(url, headers = headers, prefix = "$prefix ")
|
||||
arrayOf("burstcloud", "burst").any(url) -> burstCloudExtractor.videoFromUrl(url, headers = headers, prefix = "$prefix ")
|
||||
arrayOf("fastream").any(url) -> fastreamExtractor.videosFromUrl(url, prefix = "$prefix Fastream:")
|
||||
arrayOf("upstream").any(url) -> upstreamExtractor.videosFromUrl(url, prefix = "$prefix ")
|
||||
arrayOf("streamtape", "stp", "stape").any(url) -> streamTapeExtractor.videosFromUrl(url, quality = "$prefix StreamTape")
|
||||
arrayOf("vembed", "guard", "listeamed", "bembed", "vgfplay").any(url) -> vidGuardExtractor.videosFromUrl(url, prefix = "$prefix ")
|
||||
else -> emptyList()
|
||||
}
|
||||
if (embedUrl.contains("ok.ru") || embedUrl.contains("okru")) {
|
||||
OkruExtractor(client).videosFromUrl(url, prefix).also(videoList::addAll)
|
||||
}
|
||||
if (embedUrl.contains("filemoon") || embedUrl.contains("moonplayer")) {
|
||||
FilemoonExtractor(client).videosFromUrl(url, prefix = "${prefix}Filemoon").also(videoList::addAll)
|
||||
}
|
||||
if (embedUrl.contains("uqload")) {
|
||||
UqloadExtractor(client).videosFromUrl(url, prefix = prefix).also(videoList::addAll)
|
||||
}
|
||||
if (embedUrl.contains("mp4upload")) {
|
||||
Mp4uploadExtractor(client).videosFromUrl(url, headers, prefix = prefix).let { videoList.addAll(it) }
|
||||
}
|
||||
if (embedUrl.contains("wishembed") || embedUrl.contains("streamwish") ||
|
||||
embedUrl.contains("strwish") || embedUrl.contains("wish")
|
||||
) {
|
||||
val docHeaders = headers.newBuilder()
|
||||
.add("Referer", referer)
|
||||
.build()
|
||||
StreamWishExtractor(client, docHeaders).videosFromUrl(url, videoNameGen = { "${prefix}StreamWish:$it" }).also(videoList::addAll)
|
||||
}
|
||||
if (embedUrl.contains("doodstream") || embedUrl.contains("dood.")) {
|
||||
DoodExtractor(client).videoFromUrl(url, "${prefix}DoodStream", false)?.let { videoList.add(it) }
|
||||
}
|
||||
if (embedUrl.contains("streamlare")) {
|
||||
StreamlareExtractor(client).videosFromUrl(url, prefix = prefix).let { videoList.addAll(it) }
|
||||
}
|
||||
if (embedUrl.contains("yourupload")) {
|
||||
YourUploadExtractor(client).videoFromUrl(url, headers = headers, prefix = prefix).let { videoList.addAll(it) }
|
||||
}
|
||||
if (embedUrl.contains("burstcloud") || embedUrl.contains("burst")) {
|
||||
BurstCloudExtractor(client).videoFromUrl(url, headers = headers, prefix = prefix).let { videoList.addAll(it) }
|
||||
}
|
||||
if (embedUrl.contains("fastream")) {
|
||||
FastreamExtractor(client, headers).videosFromUrl(url, prefix = "${prefix}Fastream:").also(videoList::addAll)
|
||||
}
|
||||
if (embedUrl.contains("upstream")) {
|
||||
UpstreamExtractor(client).videosFromUrl(url, prefix = prefix).let { videoList.addAll(it) }
|
||||
}
|
||||
if (embedUrl.contains("streamtape")) {
|
||||
StreamTapeExtractor(client).videoFromUrl(url, quality = "${prefix}StreamTape")?.let { videoList.add(it) }
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
return videoList
|
||||
}.getOrNull() ?: emptyList()
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
|
@ -278,6 +268,8 @@ open class PelisForte : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
private fun Array<String>.any(url: String): Boolean = this.any { url.contains(it, ignoreCase = true) }
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_LANGUAGE_KEY
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Pelisplushd'
|
||||
extClass = '.PelisplushdFactory'
|
||||
extVersionCode = 58
|
||||
extVersionCode = 59
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'TioanimeH'
|
||||
extClass = '.TioanimeHFactory'
|
||||
extVersionCode = 19
|
||||
extVersionCode = 20
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Tiodonghua'
|
||||
themePkg = 'animestream'
|
||||
baseUrl = 'https://anime.tiodonghua.com'
|
||||
overrideVersionCode = 4
|
||||
overrideVersionCode = 5
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.AnimeSAGA'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://www.animesaga.in'
|
||||
overrideVersionCode = 11
|
||||
overrideVersionCode = 14
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|