refactor(global): Adapt to extlib v14 (#2759)

This commit is contained in:
Claudemirovsky
2024-01-16 11:18:47 -03:00
committed by GitHub
parent 0ea1468261
commit 50577ef826
386 changed files with 1122 additions and 2640 deletions

View File

@ -238,10 +238,10 @@ apply from: "$rootDir/common.gradle"
| `pkgNameSuffix` | A unique suffix added to `eu.kanade.tachiyomi.animeextension`. The language and the site name should be enough. Remember your extension code implementation must be placed in this package. | | `pkgNameSuffix` | A unique suffix added to `eu.kanade.tachiyomi.animeextension`. The language and the site name should be enough. Remember your extension code implementation must be placed in this package. |
| `extClass` | Points to the class that implements `AnimeSource`. You can use a relative path starting with a dot (the package name is the base path). This is used to find and instantiate the source(s). | | `extClass` | Points to the class that implements `AnimeSource`. You can use a relative path starting with a dot (the package name is the base path). This is used to find and instantiate the source(s). |
| `extVersionCode` | The extension version code. This must be a positive integer and incremented with any change to the code. | | `extVersionCode` | The extension version code. This must be a positive integer and incremented with any change to the code. |
| `libVersion` | (Optional, defaults to `13`) The version of the [extensions library](https://github.com/aniyomiorg/extensions-lib) used. | | `libVersion` | (Optional, defaults to `14`) The version of the [extensions library](https://github.com/aniyomiorg/extensions-lib) used. |
| `containsNsfw` | (Optional, defaults to `false`) Flag to indicate that a source contains NSFW content. | | `containsNsfw` | (Optional, defaults to `false`) Flag to indicate that a source contains NSFW content. |
The extension's version name is generated automatically by concatenating `libVersion` and `extVersionCode`. With the example used above, the version would be `13`. The extension's version name is generated automatically by concatenating `libVersion` and `extVersionCode`. With the example used above, the version would be `14`.
### Core dependencies ### Core dependencies
@ -680,7 +680,7 @@ class AnimeSource : AnimeTheme(
return this return this
} }
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = network.client.newBuilder()
.ignoreAllSSLErrors() .ignoreAllSSLErrors()
.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress("10.0.2.2", 8080))) .proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress("10.0.2.2", 8080)))
.build() .build()

View File

@ -38,7 +38,6 @@ class BurstCloudExtractor(private val client: OkHttpClient) {
} else { } else {
null null
} }
}.getOrNull().orEmpty() }.getOrNull().orEmpty()
} }
} }

View File

@ -100,7 +100,6 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
return bits.joinToString("") { Char(it.toInt(index) - offset).toString() } return bits.joinToString("") { Char(it.toInt(index) - offset).toString() }
} }
@Serializable @Serializable
data class CryptoInfo( data class CryptoInfo(
@SerialName("ct") @SerialName("ct")

View File

@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import kotlinx.serialization.decodeFromString import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.Headers import okhttp3.Headers
@ -13,7 +13,6 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class DailymotionExtractor(private val client: OkHttpClient, private val headers: Headers) { class DailymotionExtractor(private val client: OkHttpClient, private val headers: Headers) {
@ -133,8 +132,4 @@ class DailymotionExtractor(private val client: OkHttpClient, private val headers
videoNameGen = { "$prefix$it" }, videoNameGen = { "$prefix$it" },
) )
} }
private inline fun <reified T> Response.parseAs(): T {
return use { it.body.string() }.let(json::decodeFromString)
}
} }

View File

@ -1,15 +1,15 @@
package eu.kanade.tachiyomi.lib.fastreamextractor package eu.kanade.tachiyomi.lib.fastreamextractor
import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import okhttp3.FormBody
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.FormBody
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.internal.commonEmptyHeaders import okhttp3.internal.commonEmptyHeaders
import dev.datlag.jsunpacker.JsUnpacker
class FastreamExtractor(private val client: OkHttpClient, private val headers: Headers = commonEmptyHeaders) { class FastreamExtractor(private val client: OkHttpClient, private val headers: Headers = commonEmptyHeaders) {
private val videoHeaders by lazy { private val videoHeaders by lazy {
@ -35,8 +35,7 @@ class FastreamExtractor(private val client: OkHttpClient, private val headers: H
}.build() }.build()
val doc = client.newCall(POST(url, videoHeaders, body = form)).execute().use { it.asJsoup() } val doc = client.newCall(POST(url, videoHeaders, body = form)).execute().use { it.asJsoup() }
doc.selectFirst("script:containsData(jwplayer):containsData(vplayer)") ?: return emptyList() doc.selectFirst("script:containsData(jwplayer):containsData(vplayer)") ?: return emptyList()
} } else {
else {
firstDoc.selectFirst("script:containsData(jwplayer):containsData(vplayer)") ?: return emptyList() firstDoc.selectFirst("script:containsData(jwplayer):containsData(vplayer)") ?: return emptyList()
} }

View File

@ -3,11 +3,11 @@ package eu.kanade.tachiyomi.lib.filemoonextractor
import dev.datlag.jsunpacker.JsUnpacker import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Track import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video 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.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient

View File

@ -1,10 +1,10 @@
package eu.kanade.tachiyomi.lib.fusevideoextractor package eu.kanade.tachiyomi.lib.fusevideoextractor
import android.util.Base64
import eu.kanade.tachiyomi.animesource.model.Video 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.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import android.util.Base64
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -28,5 +28,4 @@ class FusevideoExtractor(private val client: OkHttpClient, private val headers:
PlaylistUtils(client, newHeaders).extractFromHls(videoUrl, videoNameGen = { "${prefix}Fusevideo - $it" }) PlaylistUtils(client, newHeaders).extractFromHls(videoUrl, videoNameGen = { "${prefix}Fusevideo - $it" })
}.getOrDefault(emptyList()) }.getOrDefault(emptyList())
} }
} }

View File

@ -2,20 +2,19 @@ package eu.kanade.tachiyomi.lib.gogostreamextractor
import android.util.Base64 import android.util.Base64
import eu.kanade.tachiyomi.animesource.model.Video 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.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Headers import okhttp3.Headers
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.lang.Exception
import java.util.Locale
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.lang.Exception
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
import uy.kohesive.injekt.injectLazy
class GogoStreamExtractor(private val client: OkHttpClient) { class GogoStreamExtractor(private val client: OkHttpClient) {
private val json: Json by injectLazy() private val json: Json by injectLazy()

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.lib.gogostreamextractor package eu.kanade.tachiyomi.lib.gogostreamextractor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable

View File

@ -7,8 +7,8 @@ import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Cookie import okhttp3.Cookie
import okhttp3.CookieJar import okhttp3.CookieJar
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.internal.commonEmptyRequestBody import okhttp3.internal.commonEmptyRequestBody
@ -37,7 +37,7 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
}.build() }.build()
val docResp = noRedirectClient.newCall( val docResp = noRedirectClient.newCall(
GET(itemUrl, headers = docHeaders) GET(itemUrl, headers = docHeaders),
).execute() ).execute()
if (docResp.isRedirect) { if (docResp.isRedirect) {
@ -60,7 +60,7 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
}.build() }.build()
val newUrl = noRedirectClient.newCall( val newUrl = noRedirectClient.newCall(
POST(downloadUrl, headers = postHeaders, body = commonEmptyRequestBody) POST(downloadUrl, headers = postHeaders, body = commonEmptyRequestBody),
).execute().use { it.headers["location"] ?: downloadUrl } ).execute().use { it.headers["location"] ?: downloadUrl }
return videoFromRedirect(newUrl, videoName, itemSize, cookieJar) return videoFromRedirect(newUrl, videoName, itemSize, cookieJar)
@ -70,7 +70,7 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
downloadUrl: String, downloadUrl: String,
videoName: String, videoName: String,
itemSize: String, itemSize: String,
cookieJar: GDriveCookieJar cookieJar: GDriveCookieJar,
): List<Video> { ): List<Video> {
var newUrl = downloadUrl var newUrl = downloadUrl
@ -82,7 +82,7 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
}.build() }.build()
var newResp = noRedirectClient.newCall( var newResp = noRedirectClient.newCall(
GET(newUrl, headers = newHeaders) GET(newUrl, headers = newHeaders),
).execute() ).execute()
var redirectCounter = 1 var redirectCounter = 1
@ -101,7 +101,7 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
}.build() }.build()
newResp = noRedirectClient.newCall( newResp = noRedirectClient.newCall(
GET(newUrl, headers = newHeaders) GET(newUrl, headers = newHeaders),
).execute() ).execute()
redirectCounter += 1 redirectCounter += 1
} }
@ -120,8 +120,8 @@ class GoogleDriveExtractor(private val client: OkHttpClient, private val headers
videoUrl.toString(), videoUrl.toString(),
videoName + itemSize, videoName + itemSize,
videoUrl.toString(), videoUrl.toString(),
headers = videoHeaders headers = videoHeaders,
) ),
) )
} }

View File

@ -2,6 +2,8 @@ package eu.kanade.tachiyomi.lib.javcoverfetcher
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Log import android.util.Log
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
@ -13,13 +15,11 @@ import okhttp3.internal.commonEmptyHeaders
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.IOException import java.io.IOException
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
object JavCoverFetcher { object JavCoverFetcher {
private val CLIENT by lazy { private val CLIENT by lazy {
Injekt.get<NetworkHelper>().cloudflareClient.newBuilder() Injekt.get<NetworkHelper>().client.newBuilder()
.addInterceptor(::amazonAgeVerifyIntercept) .addInterceptor(::amazonAgeVerifyIntercept)
.build() .build()
} }
@ -149,5 +149,4 @@ object JavCoverFetcher {
val SharedPreferences.fetchHDCovers val SharedPreferences.fetchHDCovers
get() = getBoolean("JavCoverFetcherPref", false) get() = getBoolean("JavCoverFetcherPref", false)
} }

View File

@ -24,7 +24,7 @@ import uy.kohesive.injekt.injectLazy
class MegaCloudExtractor( class MegaCloudExtractor(
private val client: OkHttpClient, private val client: OkHttpClient,
private val headers: Headers, private val headers: Headers,
private val preferences: SharedPreferences private val preferences: SharedPreferences,
) { ) {
private val json: Json by injectLazy() private val json: Json by injectLazy()
@ -59,7 +59,7 @@ class MegaCloudExtractor(
shouldUpdateKey = false shouldUpdateKey = false
} }
json.decodeFromString<List<List<Int>>>( json.decodeFromString<List<List<Int>>>(
preferences.getString(PREF_KEY_KEY + type, PREF_KEY_DEFAULT)!! preferences.getString(PREF_KEY_KEY + type, PREF_KEY_DEFAULT)!!,
) )
} }
@ -79,8 +79,8 @@ class MegaCloudExtractor(
val var1 = match.groupValues[1] val var1 = match.groupValues[1]
val var2 = match.groupValues[2] val var2 = match.groupValues[2]
val regexVar1 = Regex(",${var1}=((?:0x)?([0-9a-fA-F]+))") val regexVar1 = Regex(",$var1=((?:0x)?([0-9a-fA-F]+))")
val regexVar2 = Regex(",${var2}=((?:0x)?([0-9a-fA-F]+))") val regexVar2 = Regex(",$var2=((?:0x)?([0-9a-fA-F]+))")
val matchVar1 = regexVar1.find(script)?.groupValues?.get(1)?.removePrefix("0x") val matchVar1 = regexVar1.find(script)?.groupValues?.get(1)?.removePrefix("0x")
val matchVar2 = regexVar2.find(script)?.groupValues?.get(1)?.removePrefix("0x") val matchVar2 = regexVar2.find(script)?.groupValues?.get(1)?.removePrefix("0x")
@ -135,7 +135,7 @@ class MegaCloudExtractor(
masterUrl, masterUrl,
videoNameGen = { "$name - $it - $type" }, videoNameGen = { "$name - $it - $type" },
subtitleList = subs2, subtitleList = subs2,
referer = "https://${url.toHttpUrl().host}/" referer = "https://${url.toHttpUrl().host}/",
) )
} }
@ -159,7 +159,6 @@ class MegaCloudExtractor(
return VideoDto(decrypted, data.tracks) return VideoDto(decrypted, data.tracks)
} }
@Serializable @Serializable
data class VideoDto( data class VideoDto(
val sources: List<VideoLink>, val sources: List<VideoLink>,

View File

@ -15,7 +15,7 @@ class MixDropExtractor(private val client: OkHttpClient) {
lang: String = "", lang: String = "",
prefix: String = "", prefix: String = "",
externalSubs: List<Track> = emptyList(), externalSubs: List<Track> = emptyList(),
referer: String = DEFAULT_REFERER referer: String = DEFAULT_REFERER,
): List<Video> { ): List<Video> {
val headers = Headers.headersOf("Referer", referer) val headers = Headers.headersOf("Referer", referer)
val doc = client.newCall(GET(url, headers)).execute().use { it.asJsoup() } val doc = client.newCall(GET(url, headers)).execute().use { it.asJsoup() }

View File

@ -1,11 +1,11 @@
package eu.kanade.tachiyomi.lib.mp4uploadextractor package eu.kanade.tachiyomi.lib.mp4uploadextractor
import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import dev.datlag.jsunpacker.JsUnpacker
class Mp4uploadExtractor(private val client: OkHttpClient) { class Mp4uploadExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, headers: Headers, prefix: String = "", suffix: String = ""): List<Video> { fun videosFromUrl(url: String, headers: Headers, prefix: String = "", suffix: String = ""): List<Video> {

View File

@ -1,9 +1,9 @@
package eu.kanade.tachiyomi.lib.okruextractor package eu.kanade.tachiyomi.lib.okruextractor
import eu.kanade.tachiyomi.animesource.model.Video 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.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
class OkruExtractor(private val client: OkHttpClient) { class OkruExtractor(private val client: OkHttpClient) {
@ -37,7 +37,6 @@ class OkruExtractor(private val client: OkHttpClient) {
"ondemandDash" in videoString -> { "ondemandDash" in videoString -> {
val playlistUrl = videoString.extractLink("ondemandDash") val playlistUrl = videoString.extractLink("ondemandDash")
playlistUtils.extractFromDash(playlistUrl, videoNameGen = { it -> "Okru:$it".addPrefix(prefix) }) playlistUtils.extractFromDash(playlistUrl, videoNameGen = { it -> "Okru:$it".addPrefix(prefix) })
} }
else -> videosFromJson(videoString, prefix, fixQualities) else -> videosFromJson(videoString, prefix, fixQualities)
} }
@ -53,7 +52,6 @@ class OkruExtractor(private val client: OkHttpClient) {
.substringBefore("\\\"") .substringBefore("\\\"")
.replace("\\\\u0026", "&") .replace("\\\\u0026", "&")
private fun videosFromJson(videoString: String, prefix: String = "", fixQualities: Boolean = true): List<Video> { private fun videosFromJson(videoString: String, prefix: String = "", fixQualities: Boolean = true): List<Video> {
val arrayData = videoString.substringAfter("\\\"videos\\\":[{\\\"name\\\":\\\"") val arrayData = videoString.substringAfter("\\\"videos\\\":[{\\\"name\\\":\\\"")
.substringBefore("]") .substringBefore("]")

View File

@ -43,7 +43,7 @@ class PlaylistUtils(private val client: OkHttpClient, private val headers: Heade
{ _, _, _ -> videoHeaders }, { _, _, _ -> videoHeaders },
videoNameGen, videoNameGen,
subtitleList, subtitleList,
audioList audioList,
) )
} }
@ -90,8 +90,13 @@ class PlaylistUtils(private val client: OkHttpClient, private val headers: Heade
if (PLAYLIST_SEPARATOR !in masterPlaylist) { if (PLAYLIST_SEPARATOR !in masterPlaylist) {
return listOf( return listOf(
Video( Video(
playlistUrl, videoNameGen("Video"), playlistUrl, headers = masterHeaders, subtitleTracks = subtitleList, audioTracks = audioList playlistUrl,
) videoNameGen("Video"),
playlistUrl,
headers = masterHeaders,
subtitleTracks = subtitleList,
audioTracks = audioList,
),
) )
} }
@ -108,7 +113,7 @@ class PlaylistUtils(private val client: OkHttpClient, private val headers: Heade
val subtitleTracks = subtitleList + SUBTITLE_REGEX.findAll(masterPlaylist).mapNotNull { val subtitleTracks = subtitleList + SUBTITLE_REGEX.findAll(masterPlaylist).mapNotNull {
Track( Track(
getAbsoluteUrl(it.groupValues[2], playlistUrl, masterUrlBasePath) ?: return@mapNotNull null, getAbsoluteUrl(it.groupValues[2], playlistUrl, masterUrlBasePath) ?: return@mapNotNull null,
it.groupValues[1] it.groupValues[1],
) )
}.toList() }.toList()
@ -116,7 +121,7 @@ class PlaylistUtils(private val client: OkHttpClient, private val headers: Heade
val audioTracks = audioList + AUDIO_REGEX.findAll(masterPlaylist).mapNotNull { val audioTracks = audioList + AUDIO_REGEX.findAll(masterPlaylist).mapNotNull {
Track( Track(
getAbsoluteUrl(it.groupValues[2], playlistUrl, masterUrlBasePath) ?: return@mapNotNull null, getAbsoluteUrl(it.groupValues[2], playlistUrl, masterUrlBasePath) ?: return@mapNotNull null,
it.groupValues[1] it.groupValues[1],
) )
}.toList() }.toList()
@ -130,12 +135,13 @@ class PlaylistUtils(private val client: OkHttpClient, private val headers: Heade
getAbsoluteUrl(url, playlistUrl, masterUrlBasePath) getAbsoluteUrl(url, playlistUrl, masterUrlBasePath)
} ?: return@mapNotNull null } ?: return@mapNotNull null
Video( Video(
videoUrl, videoNameGen(resolution), videoUrl, videoUrl,
videoNameGen(resolution),
videoUrl,
headers = videoHeadersGen(headers, referer, videoUrl), headers = videoHeadersGen(headers, referer, videoUrl),
subtitleTracks = subtitleTracks, audioTracks = audioTracks subtitleTracks = subtitleTracks,
audioTracks = audioTracks,
) )
} }
} }
@ -195,7 +201,7 @@ class PlaylistUtils(private val client: OkHttpClient, private val headers: Heade
{ _, _ -> mpdHeaders }, { _, _ -> mpdHeaders },
{ _, _, _ -> videoHeaders }, { _, _, _ -> videoHeaders },
subtitleList, subtitleList,
audioList audioList,
) )
} }
@ -242,7 +248,7 @@ class PlaylistUtils(private val client: OkHttpClient, private val headers: Heade
mpdHeadersGen, mpdHeadersGen,
videoHeadersGen, videoHeadersGen,
subtitleList, subtitleList,
audioList audioList,
) )
} }

View File

@ -36,5 +36,4 @@ class SibnetExtractor(private val client: OkHttpClient) {
return videoList return videoList
} }
} }

View File

@ -12,8 +12,8 @@ class StreamHubExtractor(private val client: OkHttpClient) {
val document = client.newCall(GET(url)).execute().use { it.body.string() } val document = client.newCall(GET(url)).execute().use { it.body.string() }
val id = REGEX_ID.find(document)?.groupValues?.get(1) val id = REGEX_ID.find(document)?.groupValues?.get(1)
val sub = REGEX_SUB.find(document)?.groupValues?.get(1) val sub = REGEX_SUB.find(document)?.groupValues?.get(1)
val masterUrl = "https://${sub}.streamhub.ink/hls/,${id},.urlset/master.m3u8" val masterUrl = "https://$sub.streamhub.ink/hls/,$id,.urlset/master.m3u8"
return playlistUtils.extractFromHls(masterUrl, videoNameGen = { "${prefix}StreamHub - (${it})" }) return playlistUtils.extractFromHls(masterUrl, videoNameGen = { "${prefix}StreamHub - ($it)" })
} }
companion object { companion object {

View File

@ -21,7 +21,9 @@ class StreamWishExtractor(private val client: OkHttpClient, private val headers:
?.let { script -> ?.let { script ->
if (script.contains("eval(function(p,a,c")) { if (script.contains("eval(function(p,a,c")) {
JsUnpacker.unpackAndCombine(script) JsUnpacker.unpackAndCombine(script)
} else script } else {
script
}
} }
val masterUrl = scriptBody val masterUrl = scriptBody

View File

@ -5,9 +5,7 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.internal.commonEmptyHeaders
class UpstreamExtractor(private val client: OkHttpClient) { class UpstreamExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> = fun videosFromUrl(url: String, prefix: String = ""): List<Video> =

View File

@ -16,7 +16,7 @@ class VidoExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> { fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val document = client.newCall(GET(url)).execute().use { it.body.string() } val document = client.newCall(GET(url)).execute().use { it.body.string() }
val id = REGEX_ID.find(document)?.groupValues?.get(1) val id = REGEX_ID.find(document)?.groupValues?.get(1)
val masterUrl = "$VIDO_URL/hls/${id}/master.m3u8" val masterUrl = "$VIDO_URL/hls/$id/master.m3u8"
return playlistUtils.extractFromHls(masterUrl, videoNameGen = { "${prefix}Vido - (${it})" }) return playlistUtils.extractFromHls(masterUrl, videoNameGen = { "${prefix}Vido - ($it)" })
} }
} }

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.lib.vkextractor
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
class VkExtractor(private val client: OkHttpClient, private val headers: Headers) { class VkExtractor(private val client: OkHttpClient, private val headers: Headers) {

View File

@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -16,7 +17,6 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class CDAExtractor(private val client: OkHttpClient, private val headers: Headers, private val referer: String) { class CDAExtractor(private val client: OkHttpClient, private val headers: Headers, private val referer: String) {
@ -102,9 +102,4 @@ class CDAExtractor(private val client: OkHttpClient, private val headers: Header
val resp: String, val resp: String,
) )
} }
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
val responseBody = use { transform(it.body.string()) }
return json.decodeFromString(responseBody)
}
} }

View File

@ -5,12 +5,12 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Response
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class StreamPlayExtractor(private val client: OkHttpClient, private val headers: Headers) { class StreamPlayExtractor(private val client: OkHttpClient, private val headers: Headers) {
@ -68,9 +68,4 @@ class StreamPlayExtractor(private val client: OkHttpClient, private val headers:
val label: String, val label: String,
) )
} }
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
val responseBody = use { transform(it.body.string()) }
return json.decodeFromString(responseBody)
}
} }

View File

@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import okhttp3.Response import okhttp3.Response
class LMAnime : AnimeStream( class LMAnime : AnimeStream(
@ -27,11 +28,11 @@ class LMAnime : AnimeStream(
.filter { element -> .filter { element ->
val text = element.text() val text = element.text()
allowed.any { it in text } allowed.any { it in text }
}.parallelMap { }.parallelCatchingFlatMapBlocking {
val language = it.text().substringBefore(" ") val language = it.text().substringBefore(" ")
val url = getHosterUrl(it) val url = getHosterUrl(it)
getVideoList(url, language) getVideoList(url, language)
}.flatten().ifEmpty { throw Exception("Empty video list!") } }
} }
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) } private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }

View File

@ -15,11 +15,11 @@ import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor
import eu.kanade.tachiyomi.multisrc.datalifeengine.DataLifeEngine import eu.kanade.tachiyomi.multisrc.datalifeengine.DataLifeEngine
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMap
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable
import java.lang.Exception import java.lang.Exception
class FrenchAnime : DataLifeEngine( class FrenchAnime : DataLifeEngine(
@ -87,8 +87,7 @@ class FrenchAnime : DataLifeEngine(
override fun episodeFromElement(element: Element): SEpisode = throw Exception("not used") override fun episodeFromElement(element: Element): SEpisode = throw Exception("not used")
// ============================ Video Links ============================= // ============================ Video Links =============================
override suspend fun getVideoList(episode: SEpisode): List<Video> {
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
val list = episode.url.split(",").filter { it.isNotBlank() }.parallelCatchingFlatMap { val list = episode.url.split(",").filter { it.isNotBlank() }.parallelCatchingFlatMap {
with(it) { with(it) {
when { when {
@ -107,8 +106,7 @@ class FrenchAnime : DataLifeEngine(
} }
} }
}.sort() }.sort()
if (list.isEmpty()) throw Exception("no player found") return list
return Observable.just(list)
} }
override fun videoFromElement(element: Element): Video = throw Exception("Not Used") override fun videoFromElement(element: Element): Video = throw Exception("Not Used")

View File

@ -12,11 +12,11 @@ import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor
import eu.kanade.tachiyomi.multisrc.datalifeengine.DataLifeEngine import eu.kanade.tachiyomi.multisrc.datalifeengine.DataLifeEngine
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.parallelCatchingFlatMap
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable
class Wiflix : DataLifeEngine( class Wiflix : DataLifeEngine(
"Wiflix", "Wiflix",
@ -74,8 +74,7 @@ class Wiflix : DataLifeEngine(
} }
// ============================ Video Links ============================= // ============================ Video Links =============================
override suspend fun getVideoList(episode: SEpisode): List<Video> {
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
val list = episode.url.split(",").filter { it.isNotBlank() }.parallelCatchingFlatMap { val list = episode.url.split(",").filter { it.isNotBlank() }.parallelCatchingFlatMap {
with(it) { with(it) {
when { when {
@ -93,7 +92,7 @@ class Wiflix : DataLifeEngine(
} }
}.sort() }.sort()
if (list.isEmpty()) throw Exception("no player found") if (list.isEmpty()) throw Exception("no player found")
return Observable.just(list) return list
} }
override fun videoFromElement(element: Element): Video = throw Exception("Not Used") override fun videoFromElement(element: Element): Video = throw Exception("Not Used")

View File

@ -26,11 +26,11 @@ class AnimeOnlineNinja : DooPlay(
) { ) {
override val client by lazy { override val client by lazy {
if (preferences.getBoolean(PREF_VRF_INTERCEPT_KEY, PREF_VRF_INTERCEPT_DEFAULT)) { if (preferences.getBoolean(PREF_VRF_INTERCEPT_KEY, PREF_VRF_INTERCEPT_DEFAULT)) {
network.cloudflareClient.newBuilder() network.client.newBuilder()
.addInterceptor(VrfInterceptor()) .addInterceptor(VrfInterceptor())
.build() .build()
} else { } else {
network.cloudflareClient network.client
} }
} }

View File

@ -13,7 +13,6 @@ class AnimePlayer : DooPlay(
"AnimePlayer", "AnimePlayer",
"https://animeplayer.com.br", "https://animeplayer.com.br",
) { ) {
override val client = network.cloudflareClient
// ============================== Popular =============================== // ============================== Popular ===============================
override fun popularAnimeSelector() = "div#featured-titles article div.poster" override fun popularAnimeSelector() = "div#featured-titles article div.poster"

View File

@ -13,10 +13,7 @@ import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Response import okhttp3.Response
@ -60,7 +57,7 @@ class AnimesOnline : DooPlay(
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup() val document = response.asJsoup()
val players = document.select("ul#playeroptionsul li") val players = document.select("ul#playeroptionsul li")
return players.parallelMap(::getPlayerVideos).flatten() return players.parallelCatchingFlatMapBlocking(::getPlayerVideos)
} }
override val prefQualityValues = arrayOf("360p", "480p", "720p", "1080p") override val prefQualityValues = arrayOf("360p", "480p", "720p", "1080p")
@ -138,11 +135,6 @@ class AnimesOnline : DooPlay(
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
override fun getRealAnimeDoc(document: Document): Document { override fun getRealAnimeDoc(document: Document): Document {
if (!document.location().contains("/episodio/")) return document if (!document.location().contains("/episodio/")) return document

View File

@ -27,7 +27,7 @@ class AnimesHouse : DooPlay(
// ============================== Popular =============================== // ============================== Popular ===============================
// This source does not have a "popular" animes page, so we're going to // This source does not have a "popular" animes page, so we're going to
// use latest updates page instead. // use latest updates page instead.
override fun fetchPopularAnime(page: Int) = fetchLatestUpdates(page) override suspend fun getPopularAnime(page: Int) = getLatestUpdates(page)
// =============================== Latest =============================== // =============================== Latest ===============================
override fun latestUpdatesNextPageSelector(): String = "div.resppages > a > span.icon-chevron-right" override fun latestUpdatesNextPageSelector(): String = "div.resppages > a > span.icon-chevron-right"

View File

@ -11,11 +11,9 @@ import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -49,21 +47,19 @@ class Cinemathek : DooPlay(
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val players = response.use { it.asJsoup().select("ul#playeroptionsul li") } val players = response.use { it.asJsoup().select("ul#playeroptionsul li") }
val hosterSelection = preferences.getStringSet(PREF_HOSTER_SELECTION_KEY, PREF_HOSTER_SELECTION_DEFAULT)!! val hosterSelection = preferences.getStringSet(PREF_HOSTER_SELECTION_KEY, PREF_HOSTER_SELECTION_DEFAULT)!!
return players.parallelMapNotNull { player -> return players.parallelCatchingFlatMapBlocking { player ->
runCatching { val url = getPlayerUrl(player).takeUnless(String::isEmpty)!!
val url = getPlayerUrl(player).ifEmpty { return@parallelMapNotNull null }
getPlayerVideos(url, hosterSelection) getPlayerVideos(url, hosterSelection)
}.getOrNull() }
}.flatten()
} }
private fun getPlayerUrl(player: Element): String { private suspend fun getPlayerUrl(player: Element): String {
val type = player.attr("data-type") val type = player.attr("data-type")
val id = player.attr("data-post") val id = player.attr("data-post")
val num = player.attr("data-nume") val num = player.attr("data-nume")
if (num == "trailer") return "" if (num == "trailer") return ""
return client.newCall(GET("$baseUrl/wp-json/dooplayer/v2/$id/$type/$num")) return client.newCall(GET("$baseUrl/wp-json/dooplayer/v2/$id/$type/$num"))
.execute() .await()
.use { it.body.string() } .use { it.body.string() }
.substringAfter("\"embed_url\":\"") .substringAfter("\"embed_url\":\"")
.substringBefore("\",") .substringBefore("\",")
@ -76,7 +72,7 @@ class Cinemathek : DooPlay(
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) } private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) } private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
private fun getPlayerVideos(url: String, hosterSelection: Set<String>): List<Video>? { private fun getPlayerVideos(url: String, hosterSelection: Set<String>): List<Video> {
return when { return when {
url.contains("https://streamlare.com") && hosterSelection.contains("slare") -> { url.contains("https://streamlare.com") && hosterSelection.contains("slare") -> {
streamlareExtractor.videosFromUrl(url) streamlareExtractor.videosFromUrl(url)
@ -95,7 +91,7 @@ class Cinemathek : DooPlay(
streamwishExtractor.videosFromUrl(url) streamwishExtractor.videosFromUrl(url)
} }
else -> null else -> null
} }.orEmpty()
} }
// ============================== Settings ============================== // ============================== Settings ==============================
@ -163,11 +159,6 @@ class Cinemathek : DooPlay(
).reversed() ).reversed()
} }
private inline fun <A, B> Iterable<A>.parallelMapNotNull(crossinline f: suspend (A) -> B?): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll().filterNotNull()
}
companion object { companion object {
private const val PREF_HOSTER_KEY = "preferred_hoster" private const val PREF_HOSTER_KEY = "preferred_hoster"
private const val PREF_HOSTER_TITLE = "Standard-Hoster" private const val PREF_HOSTER_TITLE = "Standard-Hoster"

View File

@ -10,11 +10,9 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.bloggerextractor.BloggerExtractor import eu.kanade.tachiyomi.lib.bloggerextractor.BloggerExtractor
import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -76,10 +74,10 @@ class GoAnimes : DooPlay(
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val document = response.use { it.asJsoup() } val document = response.use { it.asJsoup() }
val players = document.select("ul#playeroptionsul li") val players = document.select("ul#playeroptionsul li")
return players.parallelCatchingFlatMap(::getPlayerVideos) return players.parallelCatchingFlatMapBlocking(::getPlayerVideos)
} }
private fun getPlayerVideos(player: Element): List<Video> { private suspend fun getPlayerVideos(player: Element): List<Video> {
val url = getPlayerUrl(player) val url = getPlayerUrl(player)
return when { return when {
"player5.goanimes.net" in url -> goanimesExtractor.videosFromUrl(url) "player5.goanimes.net" in url -> goanimesExtractor.videosFromUrl(url)
@ -88,7 +86,7 @@ class GoAnimes : DooPlay(
.set("referer", url) .set("referer", url)
.build() .build()
val script = client.newCall(GET(url, headers)).execute() val script = client.newCall(GET(url, headers)).await()
.use { it.body.string() } .use { it.body.string() }
.let { JsDecoder.decodeScript(it, false) } .let { JsDecoder.decodeScript(it, false) }
@ -111,7 +109,7 @@ class GoAnimes : DooPlay(
} }
} }
listOf("/bloggerjwplayer", "/m3u8", "/multivideo").any { it in url } -> { listOf("/bloggerjwplayer", "/m3u8", "/multivideo").any { it in url } -> {
val script = client.newCall(GET(url)).execute() val script = client.newCall(GET(url)).await()
.use { it.body.string() } .use { it.body.string() }
.let(JsDecoder::decodeScript) .let(JsDecoder::decodeScript)
when { when {
@ -132,12 +130,12 @@ class GoAnimes : DooPlay(
} }
} }
private fun getPlayerUrl(player: Element): String { private suspend fun getPlayerUrl(player: Element): String {
val type = player.attr("data-type") val type = player.attr("data-type")
val id = player.attr("data-post") val id = player.attr("data-post")
val num = player.attr("data-nume") val num = player.attr("data-nume")
val url = client.newCall(GET("$baseUrl/wp-json/dooplayer/v2/$id/$type/$num")) val url = client.newCall(GET("$baseUrl/wp-json/dooplayer/v2/$id/$type/$num"))
.execute() .await()
.use { it.body.string() } .use { it.body.string() }
.substringAfter("\"embed_url\":\"") .substringAfter("\"embed_url\":\"")
.substringBefore("\",") .substringBefore("\",")
@ -145,24 +143,14 @@ class GoAnimes : DooPlay(
return when { return when {
"/protetorlinks/" in url -> { "/protetorlinks/" in url -> {
val link = client.newCall(GET(url)).execute() val link = client.newCall(GET(url)).await()
.use { it.asJsoup() } .use { it.asJsoup() }
.selectFirst("a[href]")!!.attr("href") .selectFirst("a[href]")!!.attr("href")
client.newCall(GET(link)).execute() client.newCall(GET(link)).await()
.use(linkfunBypasser::getIframeUrl) .use(linkfunBypasser::getIframeUrl)
} }
else -> url else -> url
} }
} }
// ============================= Utilities ==============================
private inline fun <A, B> Iterable<A>.parallelCatchingFlatMap(crossinline f: suspend (A) -> Iterable<B>): List<B> =
runBlocking {
map {
async(Dispatchers.Default) {
runCatching { f(it) }.getOrElse { emptyList() }
}
}.awaitAll().flatten()
}
} }

View File

@ -35,8 +35,6 @@ class Kinoking : DooPlay(
override val videoSortPrefKey = PREF_HOSTER_KEY override val videoSortPrefKey = PREF_HOSTER_KEY
override val videoSortPrefDefault = PREF_HOSTER_DEFAULT override val videoSortPrefDefault = PREF_HOSTER_DEFAULT
override val client = network.cloudflareClient
// ============================== Popular =============================== // ============================== Popular ===============================
override fun popularAnimeSelector(): String = "div#featured-titles div.poster" override fun popularAnimeSelector(): String = "div#featured-titles div.poster"

View File

@ -30,7 +30,6 @@ class Multimovies : DooPlay(
"Multimovies", "Multimovies",
"https://multimovies.live", "https://multimovies.live",
) { ) {
override val client = network.cloudflareClient
private val defaultBaseUrl = "https://multimovies.live" private val defaultBaseUrl = "https://multimovies.live"

View File

@ -689,10 +689,7 @@ import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -733,8 +730,7 @@ class AutoEmbedExtractor(private val client: OkHttpClient) {
} }
// Get video servers from containers // Get video servers from containers
val serverList = containerList.parallelMap { container -> val serverList = containerList.parallelCatchingFlatMapBlocking { container ->
runCatching {
when (container.name) { when (container.name) {
"2embed" -> { "2embed" -> {
getTwoEmbedServers(container.url, headers = headers) getTwoEmbedServers(container.url, headers = headers)
@ -743,16 +739,14 @@ class AutoEmbedExtractor(private val client: OkHttpClient) {
getGomoStreamServers(container.url, headers = headers) getGomoStreamServers(container.url, headers = headers)
} }
else -> null else -> null
}.orEmpty()
} }
}.getOrNull() ?: emptyList()
}.flatten()
val videoHeaders = headers.newBuilder() val videoHeaders = headers.newBuilder()
.add("Referer", "https://www.2embed.to/") .add("Referer", "https://www.2embed.to/")
.build() .build()
return serverList.parallelMap { server -> return serverList.parallelCatchingFlatMapBlocking { server ->
runCatching {
val prefix = server.name val prefix = server.name
val videoUrl = server.url val videoUrl = server.url
@ -775,9 +769,8 @@ class AutoEmbedExtractor(private val client: OkHttpClient) {
?.let(::listOf) ?.let(::listOf)
} }
else -> null else -> null
}.orEmpty()
} }
}.getOrNull() ?: emptyList()
}.flatten()
} }
data class Server( data class Server(
@ -892,10 +885,4 @@ class AutoEmbedExtractor(private val client: OkHttpClient) {
} }
}.getOrNull() }.getOrNull()
} }
// From Dopebox
private fun <A, B> Iterable<A>.parallelMap(f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
} }

View File

@ -3,12 +3,11 @@ package eu.kanade.tachiyomi.animeextension.pt.pifansubs.extractors
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Response
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class BlembedExtractor(private val client: OkHttpClient, private val headers: Headers) { class BlembedExtractor(private val client: OkHttpClient, private val headers: Headers) {
@ -32,10 +31,6 @@ class BlembedExtractor(private val client: OkHttpClient, private val headers: He
return res.sources.map { Video(it.file, "Blembed - ${it.label}", it.file, headers) } return res.sources.map { Video(it.file, "Blembed - ${it.label}", it.file, headers) }
} }
private inline fun <reified T> Response.parseAs(): T = use {
json.decodeFromStream(it.body.byteStream())
}
} }
@Serializable @Serializable

View File

@ -3,11 +3,9 @@ package eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.FormBody import okhttp3.FormBody
@ -26,7 +24,7 @@ class SuperFlixExtractor(
fun videosFromUrl(url: String): List<Video> { fun videosFromUrl(url: String): List<Video> {
val links = linksFromUrl(url) val links = linksFromUrl(url)
val fixedLinks = links.parallelCatchingFlatMap { val fixedLinks = links.parallelCatchingFlatMapBlocking {
val (language, linkUrl) = it val (language, linkUrl) = it
when { when {
linkUrl.contains("?vid=") -> linksFromPlayer(linkUrl, language) linkUrl.contains("?vid=") -> linksFromPlayer(linkUrl, language)
@ -34,10 +32,10 @@ class SuperFlixExtractor(
} }
} }
return fixedLinks.parallelCatchingFlatMap { genericExtractor(it.second, it.first) } return fixedLinks.parallelCatchingFlatMapBlocking { genericExtractor(it.second, it.first) }
} }
private fun linksFromPlayer(url: String, language: String): List<Pair<String, String>> { private suspend fun linksFromPlayer(url: String, language: String): List<Pair<String, String>> {
val httpUrl = url.toHttpUrl() val httpUrl = url.toHttpUrl()
val id = httpUrl.queryParameter("vid")!! val id = httpUrl.queryParameter("vid")!!
val headers = defaultHeaders.newBuilder() val headers = defaultHeaders.newBuilder()
@ -45,7 +43,7 @@ class SuperFlixExtractor(
.set("origin", API_DOMAIN) .set("origin", API_DOMAIN)
.build() .build()
val doc = client.newCall(GET(url, headers)).execute().use { it.asJsoup() } val doc = client.newCall(GET(url, headers)).await().use { it.asJsoup() }
val baseUrl = "https://" + httpUrl.host val baseUrl = "https://" + httpUrl.host
val apiUrl = "$baseUrl/ajax_sources.php" val apiUrl = "$baseUrl/ajax_sources.php"
@ -66,7 +64,7 @@ class SuperFlixExtractor(
.add("ord", order) .add("ord", order)
.build() .build()
val req = client.newCall(POST(apiUrl, apiHeaders, formBody)).execute() val req = client.newCall(POST(apiUrl, apiHeaders, formBody)).await()
.use { it.body.string() } .use { it.body.string() }
runCatching { runCatching {
@ -125,15 +123,6 @@ class SuperFlixExtractor(
@Serializable @Serializable
data class DataDto(val video_url: String? = null) data class DataDto(val video_url: String? = null)
private inline fun <A, B> Iterable<A>.parallelCatchingFlatMap(crossinline f: suspend (A) -> Iterable<B>): List<B> =
runBlocking {
map {
async(Dispatchers.Default) {
runCatching { f(it) }.getOrElse { emptyList() }
}
}.awaitAll().flatten()
}
} }
private const val API_DOMAIN = "https://superflixapi.top" private const val API_DOMAIN = "https://superflixapi.top"

View File

@ -16,7 +16,6 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class UniqueStream : DooPlay( class UniqueStream : DooPlay(
@ -24,7 +23,6 @@ class UniqueStream : DooPlay(
"UniqueStream", "UniqueStream",
"https://uniquestream.net", "https://uniquestream.net",
) { ) {
override val client = network.cloudflareClient
private val json: Json by injectLazy() private val json: Json by injectLazy()
@ -183,7 +181,7 @@ class UniqueStream : DooPlay(
// ============================ Video Links ============================= // ============================ Video Links =============================
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> { override suspend fun getVideoList(episode: SEpisode): List<Video> {
val videoList = mutableListOf<Video>() val videoList = mutableListOf<Video>()
val document = client.newCall( val document = client.newCall(
GET(baseUrl + episode.url, headers = headers), GET(baseUrl + episode.url, headers = headers),
@ -290,7 +288,7 @@ class UniqueStream : DooPlay(
require(videoList.isNotEmpty()) { "Failed to fetch videos" } require(videoList.isNotEmpty()) { "Failed to fetch videos" }
return Observable.just(videoList.sort()) return videoList.sort()
} }
// ============================== Settings ============================== // ============================== Settings ==============================

View File

@ -21,11 +21,10 @@ import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.StudioFilter
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.SubFilter import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.SubFilter
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.TypeFilter import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.TypeFilter
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
@ -34,7 +33,6 @@ import okhttp3.Response
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -48,8 +46,6 @@ abstract class AnimeStream(
override val supportsLatest = true override val supportsLatest = true
override val client = network.cloudflareClient
protected open val preferences by lazy { protected open val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -81,9 +77,9 @@ abstract class AnimeStream(
protected open val animeListUrl = "$baseUrl/anime" protected open val animeListUrl = "$baseUrl/anime"
// ============================== Popular =============================== // ============================== Popular ===============================
override fun fetchPopularAnime(page: Int): Observable<AnimesPage> { override suspend fun getPopularAnime(page: Int): AnimesPage {
fetchFilterList() fetchFilterList()
return super.fetchPopularAnime(page) return super.getPopularAnime(page)
} }
override fun popularAnimeRequest(page: Int) = GET("$animeListUrl/?page=$page&order=popular") override fun popularAnimeRequest(page: Int) = GET("$animeListUrl/?page=$page&order=popular")
@ -95,9 +91,9 @@ abstract class AnimeStream(
override fun popularAnimeNextPageSelector(): String? = searchAnimeNextPageSelector() override fun popularAnimeNextPageSelector(): String? = searchAnimeNextPageSelector()
// =============================== Latest =============================== // =============================== Latest ===============================
override fun fetchLatestUpdates(page: Int): Observable<AnimesPage> { override suspend fun getLatestUpdates(page: Int): AnimesPage {
fetchFilterList() fetchFilterList()
return super.fetchLatestUpdates(page) return super.getLatestUpdates(page)
} }
override fun latestUpdatesRequest(page: Int) = GET("$animeListUrl/?page=$page&order=update") override fun latestUpdatesRequest(page: Int) = GET("$animeListUrl/?page=$page&order=update")
@ -109,14 +105,14 @@ abstract class AnimeStream(
override fun latestUpdatesNextPageSelector(): String? = searchAnimeNextPageSelector() override fun latestUpdatesNextPageSelector(): String? = searchAnimeNextPageSelector()
// =============================== Search =============================== // =============================== Search ===============================
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> { override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
val path = query.removePrefix(PREFIX_SEARCH) val path = query.removePrefix(PREFIX_SEARCH)
client.newCall(GET("$baseUrl/$path")) client.newCall(GET("$baseUrl/$path"))
.asObservableSuccess() .awaitSuccess()
.map(::searchAnimeByPathParse) .use(::searchAnimeByPathParse)
} else { } else {
super.fetchSearchAnime(page, query, filters) super.getSearchAnime(page, query, filters)
} }
} }
@ -320,13 +316,11 @@ abstract class AnimeStream(
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val items = response.use { it.asJsoup() }.select(videoListSelector()) val items = response.use { it.asJsoup() }.select(videoListSelector())
return items.parallelMap { element -> return items.parallelCatchingFlatMapBlocking { element ->
runCatching {
val name = element.text() val name = element.text()
val url = getHosterUrl(element) val url = getHosterUrl(element)
getVideoList(url, name) getVideoList(url, name)
}.onFailure { it.printStackTrace() }.getOrElse { emptyList() } }
}.flatten()
} }
protected open fun getHosterUrl(element: Element): String { protected open fun getHosterUrl(element: Element): String {
@ -422,11 +416,6 @@ abstract class AnimeStream(
} ?: 0L } ?: 0L
} }
protected inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
/** /**
* Tries to get the image url via various possible attributes. * Tries to get the image url via various possible attributes.
* Taken from Tachiyomi's Madara multisrc. * Taken from Tachiyomi's Madara multisrc.

View File

@ -12,21 +12,15 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -38,8 +32,6 @@ abstract class DataLifeEngine(
override val supportsLatest = false override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences by lazy { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) } private val preferences by lazy { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) }
// ============================== Popular =============================== // ============================== Popular ===============================
@ -139,10 +131,10 @@ abstract class DataLifeEngine(
} }
// =========================== Anime Details ============================ // =========================== Anime Details ============================
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> { override suspend fun getAnimeDetails(anime: SAnime): SAnime {
return client.newCall(animeDetailsRequest(anime)) return client.newCall(animeDetailsRequest(anime))
.asObservableSuccess() .awaitSuccess()
.map { response -> .use { response ->
animeDetailsParse(response, anime).apply { initialized = true } animeDetailsParse(response, anime).apply { initialized = true }
} }
} }
@ -183,11 +175,6 @@ abstract class DataLifeEngine(
).reversed() ).reversed()
} }
inline fun <A, B> Iterable<A>.parallelCatchingFlatMap(crossinline f: suspend (A) -> Iterable<B>): List<B> =
runBlocking {
map { async(Dispatchers.Default) { runCatching { f(it) }.getOrElse { emptyList() } } }.awaitAll().flatten()
}
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply { val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality" key = "preferred_quality"

View File

@ -13,14 +13,12 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.lang.Exception import java.lang.Exception
@ -39,8 +37,6 @@ abstract class DooPlay(
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.client
override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl) override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl)
protected open val preferences: SharedPreferences by lazy { protected open val preferences: SharedPreferences by lazy {
@ -185,16 +181,14 @@ abstract class DooPlay(
return AnimesPage(animes, hasNextPage) return AnimesPage(animes, hasNextPage)
} }
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> { override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
return if (query.startsWith(PREFIX_SEARCH)) { return if (query.startsWith(PREFIX_SEARCH)) {
val path = query.removePrefix(PREFIX_SEARCH) val path = query.removePrefix(PREFIX_SEARCH)
client.newCall(GET("$baseUrl/$path", headers)) client.newCall(GET("$baseUrl/$path", headers))
.asObservableSuccess() .awaitSuccess()
.map { response -> .use(::searchAnimeByPathParse)
searchAnimeByPathParse(response)
}
} else { } else {
super.fetchSearchAnime(page, query, filters) super.getSearchAnime(page, query, filters)
} }
} }

View File

@ -17,14 +17,10 @@ import eu.kanade.tachiyomi.multisrc.dopeflix.dto.VideoDto
import eu.kanade.tachiyomi.multisrc.dopeflix.extractors.DopeFlixExtractor import eu.kanade.tachiyomi.multisrc.dopeflix.extractors.DopeFlixExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -45,8 +41,6 @@ abstract class DopeFlix(
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -191,7 +185,7 @@ abstract class DopeFlix(
val doc = response.asJsoup() val doc = response.asJsoup()
val episodeReferer = Headers.headersOf("Referer", response.request.header("referer")!!) val episodeReferer = Headers.headersOf("Referer", response.request.header("referer")!!)
return doc.select("ul.fss-list a.btn-play") return doc.select("ul.fss-list a.btn-play")
.parallelMap { server -> .parallelCatchingFlatMapBlocking { server ->
val name = server.selectFirst("span")!!.text() val name = server.selectFirst("span")!!.text()
val id = server.attr("data-id") val id = server.attr("data-id")
val url = "$baseUrl/ajax/sources/$id" val url = "$baseUrl/ajax/sources/$id"
@ -199,7 +193,6 @@ abstract class DopeFlix(
.use { it.body.string() } .use { it.body.string() }
val sourceUrl = reqBody.substringAfter("\"link\":\"") val sourceUrl = reqBody.substringAfter("\"link\":\"")
.substringBefore("\"") .substringBefore("\"")
runCatching {
when { when {
"DoodStream" in name -> "DoodStream" in name ->
DoodExtractor(client).videoFromUrl(sourceUrl) DoodExtractor(client).videoFromUrl(sourceUrl)
@ -209,9 +202,8 @@ abstract class DopeFlix(
getVideosFromServer(video, name) getVideosFromServer(video, name)
} }
else -> null else -> null
}.orEmpty()
} }
}.getOrNull() ?: emptyList()
}.flatten()
} }
private fun getVideosFromServer(video: VideoDto, name: String): List<Video> { private fun getVideosFromServer(video: VideoDto, name: String): List<Video> {
@ -343,11 +335,6 @@ abstract class DopeFlix(
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
private fun HttpUrl.Builder.addIfNotBlank(query: String, value: String): HttpUrl.Builder { private fun HttpUrl.Builder.addIfNotBlank(query: String, value: String): HttpUrl.Builder {
if (value.isNotBlank()) { if (value.isNotBlank()) {
addQueryParameter(query, value) addQueryParameter(query, value)

View File

@ -16,13 +16,14 @@ import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.multisrc.zorotheme.dto.HtmlResponse import eu.kanade.tachiyomi.multisrc.zorotheme.dto.HtmlResponse
import eu.kanade.tachiyomi.multisrc.zorotheme.dto.SourcesResponse import eu.kanade.tachiyomi.multisrc.zorotheme.dto.SourcesResponse
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.util.parallelCatchingFlatMap import eu.kanade.tachiyomi.util.parallelCatchingFlatMap
import eu.kanade.tachiyomi.util.parallelMapNotNull import eu.kanade.tachiyomi.util.parallelMapNotNull
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -39,8 +40,6 @@ abstract class ZoroTheme(
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.client
private val json: Json by injectLazy() private val json: Json by injectLazy()
val preferences: SharedPreferences by lazy { val preferences: SharedPreferences by lazy {
@ -210,7 +209,7 @@ abstract class ZoroTheme(
) )
override suspend fun getVideoList(episode: SEpisode): List<Video> { override suspend fun getVideoList(episode: SEpisode): List<Video> {
val response = client.newCall(videoListRequest(episode)).execute() val response = client.newCall(videoListRequest(episode)).await()
val episodeReferer = response.request.header("referer")!! val episodeReferer = response.request.header("referer")!!
val typeSelection = preferences.typeToggle val typeSelection = preferences.typeToggle
@ -230,7 +229,7 @@ abstract class ZoroTheme(
val link = client.newCall( val link = client.newCall(
GET("$baseUrl/ajax$ajaxRoute/episode/sources?id=$id", apiHeaders(episodeReferer)), GET("$baseUrl/ajax$ajaxRoute/episode/sources?id=$id", apiHeaders(episodeReferer)),
).execute().parseAs<SourcesResponse>().link ?: "" ).await().parseAs<SourcesResponse>().link ?: ""
VideoData(type, link, name) VideoData(type, link, name)
} }
@ -250,11 +249,6 @@ abstract class ZoroTheme(
override fun videoUrlParse(document: Document) = throw Exception("not used") override fun videoUrlParse(document: Document) = throw Exception("not used")
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <reified T> Response.parseAs(): T {
return use { it.body.string() }.let(json::decodeFromString)
}
private fun Set<String>.contains(s: String, ignoreCase: Boolean): Boolean { private fun Set<String>.contains(s: String, ignoreCase: Boolean): Boolean {
return any { it.equals(s, ignoreCase) } return any { it.equals(s, ignoreCase) }
} }

View File

@ -81,7 +81,6 @@ interface ThemeSourceGenerator {
| extClass = '.${source.className}' | extClass = '.${source.className}'
| extFactory = '$themePkg' | extFactory = '$themePkg'
| extVersionCode = ${baseVersionCode + source.overrideVersionCode + MULTISRC_LIBRARY_VERSION} | extVersionCode = ${baseVersionCode + source.overrideVersionCode + MULTISRC_LIBRARY_VERSION}
| libVersion = '13'
| ${if (source.isNsfw) "containsNsfw = true\n" else ""} | ${if (source.isNsfw) "containsNsfw = true\n" else ""}
|} |}
|$defaultAdditionalGradleText |$defaultAdditionalGradleText

View File

@ -9,7 +9,6 @@ ext {
pkgNameSuffix = 'all.animeonsen' pkgNameSuffix = 'all.animeonsen'
extClass = '.AnimeOnsen' extClass = '.AnimeOnsen'
extVersionCode = 7 extVersionCode = 7
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -18,14 +18,12 @@ import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.boolean import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers import okhttp3.Headers
import okhttp3.Response import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -43,11 +41,9 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
override val supportsLatest = false override val supportsLatest = false
private val cfClient = network.cloudflareClient
override val client by lazy { override val client by lazy {
network.client.newBuilder() network.client.newBuilder()
.addInterceptor(AOAPIInterceptor(cfClient)) .addInterceptor(AOAPIInterceptor(network.client))
.build() .build()
} }
@ -88,15 +84,9 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
} }
// =========================== Anime Details ============================ // =========================== Anime Details ============================
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> { override fun animeDetailsRequest(anime: SAnime) = GET("$apiUrl/content/${anime.url}/extensive")
return client.newCall(GET("$apiUrl/content/${anime.url}/extensive"))
.asObservableSuccess()
.map { response ->
animeDetailsParse(response).apply { initialized = true }
}
}
override fun animeDetailsRequest(anime: SAnime) = GET("$baseUrl/details/${anime.url}") override fun getAnimeUrl(anime: SAnime) = "$baseUrl/details/${anime.url}"
override fun animeDetailsParse(response: Response) = SAnime.create().apply { override fun animeDetailsParse(response: Response) = SAnime.create().apply {
val details = response.parseAs<AnimeDetails>() val details = response.parseAs<AnimeDetails>()
@ -165,10 +155,6 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <reified T> Response.parseAs(): T {
return use { it.body.string() }.let(json::decodeFromString)
}
private fun parseStatus(statusString: String?): Int { private fun parseStatus(statusString: String?): Int {
return when (statusString?.trim()) { return when (statusString?.trim()) {
"finished_airing" -> SAnime.COMPLETED "finished_airing" -> SAnime.COMPLETED

View File

@ -9,7 +9,6 @@ ext {
pkgNameSuffix = 'all.animeworldindia' pkgNameSuffix = 'all.animeworldindia'
extClass = '.AnimeWorldIndiaFactory' extClass = '.AnimeWorldIndiaFactory'
extVersionCode = 7 extVersionCode = 7
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -34,8 +34,6 @@ class AnimeWorldIndia(
override val supportsLatest = true override val supportsLatest = true
override val client = network.cloudflareClient
private val json: Json by injectLazy() private val json: Json by injectLazy()
private val preferences by lazy { private val preferences by lazy {

View File

@ -9,7 +9,6 @@ ext {
pkgNameSuffix = 'all.googledrive' pkgNameSuffix = 'all.googledrive'
extClass = '.GoogleDrive' extClass = '.GoogleDrive'
extVersionCode = 13 extVersionCode = 13
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -22,18 +22,17 @@ import eu.kanade.tachiyomi.lib.googledriveextractor.GoogleDriveExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.ProtocolException import okhttp3.ProtocolException
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import okhttp3.internal.commonEmptyRequestBody import okhttp3.internal.commonEmptyRequestBody
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -63,8 +62,6 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
override val client: OkHttpClient = network.client
// Overriding headersBuilder() seems to cause issues with webview // Overriding headersBuilder() seems to cause issues with webview
private val getHeaders = headers.newBuilder().apply { private val getHeaders = headers.newBuilder().apply {
add("Accept", "*/*") add("Accept", "*/*")
@ -77,8 +74,8 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================== Popular =============================== // ============================== Popular ===============================
override fun fetchPopularAnime(page: Int): Observable<AnimesPage> = override suspend fun getPopularAnime(page: Int): AnimesPage =
Observable.just(parsePage(popularAnimeRequest(page), page)) parsePage(popularAnimeRequest(page), page)
override fun popularAnimeRequest(page: Int): Request { override fun popularAnimeRequest(page: Int): Request {
require(!baseUrlInternal.isNullOrEmpty()) { "Enter drive path(s) in extension settings." } require(!baseUrlInternal.isNullOrEmpty()) { "Enter drive path(s) in extension settings." }
@ -106,11 +103,11 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
override fun searchAnimeParse(response: Response): AnimesPage = throw Exception("Not used") override fun searchAnimeParse(response: Response): AnimesPage = throw Exception("Not used")
override fun fetchSearchAnime( override suspend fun getSearchAnime(
page: Int, page: Int,
query: String, query: String,
filters: AnimeFilterList, filters: AnimeFilterList,
): Observable<AnimesPage> { ): AnimesPage {
val filterList = if (filters.isEmpty()) getFilterList() else filters val filterList = if (filters.isEmpty()) getFilterList() else filters
val urlFilter = filterList.find { it is URLFilter } as URLFilter val urlFilter = filterList.find { it is URLFilter } as URLFilter
@ -118,16 +115,16 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
val req = searchAnimeRequest(page, query, filters) val req = searchAnimeRequest(page, query, filters)
if (query.isEmpty()) { if (query.isEmpty()) {
Observable.just(parsePage(req, page)) parsePage(req, page)
} else { } else {
val parentId = req.url.pathSegments.last() val parentId = req.url.pathSegments.last()
val cleanQuery = URLEncoder.encode(query, "UTF-8") val cleanQuery = URLEncoder.encode(query, "UTF-8")
val genMultiFormReq = searchReq(parentId, cleanQuery) val genMultiFormReq = searchReq(parentId, cleanQuery)
Observable.just(parsePage(req, page, genMultiFormReq)) parsePage(req, page, genMultiFormReq)
} }
} else { } else {
Observable.just(addSinglePage(urlFilter.state)) addSinglePage(urlFilter.state)
} }
} }
@ -187,10 +184,10 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
return GET(parsed.url, headers = getHeaders) return GET(parsed.url, headers = getHeaders)
} }
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> { override suspend fun getAnimeDetails(anime: SAnime): SAnime {
val parsed = json.decodeFromString<LinkData>(anime.url) val parsed = json.decodeFromString<LinkData>(anime.url)
if (parsed.type == "single") return Observable.just(anime) if (parsed.type == "single") return anime
val folderId = DRIVE_FOLDER_REGEX.matchEntire(parsed.url)!!.groups["id"]!!.value val folderId = DRIVE_FOLDER_REGEX.matchEntire(parsed.url)!!.groups["id"]!!.value
@ -198,7 +195,7 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
client.newCall(GET(parsed.url, headers = getHeaders)).execute().asJsoup() client.newCall(GET(parsed.url, headers = getHeaders)).execute().asJsoup()
} catch (a: ProtocolException) { } catch (a: ProtocolException) {
null null
} ?: return Observable.just(anime) } ?: return anime
// Get cover // Get cover
@ -251,20 +248,19 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
} }
} }
return Observable.just(anime) return anime
} }
override fun animeDetailsParse(response: Response): SAnime = throw Exception("Not used") override fun animeDetailsParse(response: Response): SAnime = throw Exception("Not used")
// ============================== Episodes ============================== // ============================== Episodes ==============================
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> { override suspend fun getEpisodeList(anime: SAnime): List<SEpisode> {
val episodeList = mutableListOf<SEpisode>() val episodeList = mutableListOf<SEpisode>()
val parsed = json.decodeFromString<LinkData>(anime.url) val parsed = json.decodeFromString<LinkData>(anime.url)
if (parsed.type == "single") { if (parsed.type == "single") {
return Observable.just( return listOf(
listOf(
SEpisode.create().apply { SEpisode.create().apply {
name = "Video" name = "Video"
scanlator = parsed.info!!.size scanlator = parsed.info!!.size
@ -272,7 +268,6 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
episode_number = 1F episode_number = 1F
date_upload = -1L date_upload = -1L
}, },
),
) )
} }
@ -354,15 +349,15 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
traverseFolder(parsed.url, "") traverseFolder(parsed.url, "")
return Observable.just(episodeList.reversed()) return episodeList.reversed()
} }
override fun episodeListParse(response: Response): List<SEpisode> = throw Exception("Not used") override fun episodeListParse(response: Response): List<SEpisode> = throw Exception("Not used")
// ============================ Video Links ============================= // ============================ Video Links =============================
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> = override suspend fun getVideoList(episode: SEpisode): List<Video> =
Observable.just(GoogleDriveExtractor(client, headers).videosFromUrl(episode.url)) GoogleDriveExtractor(client, headers).videosFromUrl(episode.url)
// ============================= Utilities ============================== // ============================= Utilities ==============================
@ -508,11 +503,6 @@ class GoogleDrive : ConfigurableAnimeSource, AnimeHttpSource() {
return AnimesPage(animeList, nextPageToken != null) return AnimesPage(animeList, nextPageToken != null)
} }
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
val responseBody = use { transform(it.body.string()) }
return json.decodeFromString(responseBody)
}
// https://github.com/yt-dlp/yt-dlp/blob/8f0be90ecb3b8d862397177bb226f17b245ef933/yt_dlp/extractor/youtube.py#L573 // https://github.com/yt-dlp/yt-dlp/blob/8f0be90ecb3b8d862397177bb226f17b245ef933/yt_dlp/extractor/youtube.py#L573
private fun generateSapisidhashHeader( private fun generateSapisidhashHeader(
SAPISID: String, SAPISID: String,

View File

@ -7,7 +7,6 @@ ext {
pkgNameSuffix = 'all.googledriveindex' pkgNameSuffix = 'all.googledriveindex'
extClass = '.GoogleDriveIndex' extClass = '.GoogleDriveIndex'
extVersionCode = 7 extVersionCode = 7
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -21,19 +21,18 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Credentials import okhttp3.Credentials
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -59,7 +58,7 @@ class GoogleDriveIndex : ConfigurableAnimeSource, AnimeHttpSource() {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client = network.client.newBuilder()
.addInterceptor { chain -> .addInterceptor { chain ->
var request = chain.request() var request = chain.request()
@ -119,29 +118,22 @@ class GoogleDriveIndex : ConfigurableAnimeSource, AnimeHttpSource() {
override fun searchAnimeParse(response: Response): AnimesPage = throw Exception("Not used") override fun searchAnimeParse(response: Response): AnimesPage = throw Exception("Not used")
override fun fetchSearchAnime( override suspend fun getSearchAnime(
page: Int, page: Int,
query: String, query: String,
filters: AnimeFilterList, filters: AnimeFilterList,
): Observable<AnimesPage> { ): AnimesPage {
val filterList = if (filters.isEmpty()) getFilterList() else filters val filterList = if (filters.isEmpty()) getFilterList() else filters
val urlFilter = filterList.find { it is URLFilter } as URLFilter val urlFilter = filterList.find { it is URLFilter } as URLFilter
return if (urlFilter.state.isEmpty()) { return if (urlFilter.state.isEmpty()) {
val req = searchAnimeRequest(page, query, filters) val req = searchAnimeRequest(page, query, filters)
Observable.defer { client.newCall(req).awaitSuccess()
try { .use { response ->
client.newCall(req).asObservableSuccess()
} catch (e: NoClassDefFoundError) {
// RxJava doesn't handle Errors, which tends to happen during global searches
// if an old extension using non-existent classes is still around
throw RuntimeException(e)
}
}.map { response ->
searchAnimeParse(response, req.url.toString()) searchAnimeParse(response, req.url.toString())
} }
} else { } else {
Observable.just(addSinglePage(urlFilter.state)) addSinglePage(urlFilter.state)
} }
} }
@ -237,7 +229,7 @@ class GoogleDriveIndex : ConfigurableAnimeSource, AnimeHttpSource() {
// =========================== Anime Details ============================ // =========================== Anime Details ============================
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> { override suspend fun getAnimeDetails(anime: SAnime): SAnime {
val parsed = json.decodeFromString<LinkData>(anime.url) val parsed = json.decodeFromString<LinkData>(anime.url)
val newParsed = if (parsed.type != "search") { val newParsed = if (parsed.type != "search") {
parsed parsed
@ -265,7 +257,7 @@ class GoogleDriveIndex : ConfigurableAnimeSource, AnimeHttpSource() {
} }
if (newParsed.type == "single") { if (newParsed.type == "single") {
return Observable.just(anime) return anime
} }
var newToken: String? = "" var newToken: String? = ""
@ -309,14 +301,14 @@ class GoogleDriveIndex : ConfigurableAnimeSource, AnimeHttpSource() {
newPageIndex += 1 newPageIndex += 1
} }
return Observable.just(anime) return anime
} }
override fun animeDetailsParse(response: Response): SAnime = throw Exception("Not used") override fun animeDetailsParse(response: Response): SAnime = throw Exception("Not used")
// ============================== Episodes ============================== // ============================== Episodes ==============================
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> { override suspend fun getEpisodeList(anime: SAnime): List<SEpisode> {
val episodeList = mutableListOf<SEpisode>() val episodeList = mutableListOf<SEpisode>()
val parsed = json.decodeFromString<LinkData>(anime.url) val parsed = json.decodeFromString<LinkData>(anime.url)
var counter = 1 var counter = 1
@ -446,14 +438,14 @@ class GoogleDriveIndex : ConfigurableAnimeSource, AnimeHttpSource() {
traverseDirectory(newParsed.url) traverseDirectory(newParsed.url)
} }
return Observable.just(episodeList.reversed()) return episodeList.reversed()
} }
override fun episodeListParse(response: Response): List<SEpisode> = throw Exception("Not used") override fun episodeListParse(response: Response): List<SEpisode> = throw Exception("Not used")
// ============================ Video Links ============================= // ============================ Video Links =============================
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> { override suspend fun getVideoList(episode: SEpisode): List<Video> {
val url = episode.url val url = episode.url
val doc = client.newCall( val doc = client.newCall(
@ -462,10 +454,10 @@ class GoogleDriveIndex : ConfigurableAnimeSource, AnimeHttpSource() {
val script = doc.selectFirst("script:containsData(videodomain)")?.data() val script = doc.selectFirst("script:containsData(videodomain)")?.data()
?: doc.selectFirst("script:containsData(downloaddomain)")?.data() ?: doc.selectFirst("script:containsData(downloaddomain)")?.data()
?: return Observable.just(listOf(Video(url, "Video", url))) ?: return listOf(Video(url, "Video", url))
if (script.contains("\"second_domain_for_dl\":false")) { if (script.contains("\"second_domain_for_dl\":false")) {
return Observable.just(listOf(Video(url, "Video", url))) return listOf(Video(url, "Video", url))
} }
val domainUrl = if (script.contains("videodomain", true)) { val domainUrl = if (script.contains("videodomain", true)) {
@ -484,18 +476,10 @@ class GoogleDriveIndex : ConfigurableAnimeSource, AnimeHttpSource() {
domainUrl + url.toHttpUrl().encodedPath domainUrl + url.toHttpUrl().encodedPath
} }
return Observable.just( return listOf(Video(videoUrl, "Video", videoUrl))
listOf(Video(videoUrl, "Video", videoUrl)),
)
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
val responseBody = use { transform(it.body.string()) }
return json.decodeFromString(responseBody)
}
private fun HttpUrl.hostAndCred(): String { private fun HttpUrl.hostAndCred(): String {
return if (this.password.isNotBlank() && this.username.isNotBlank()) { return if (this.password.isNotBlank() && this.username.isNotBlank()) {
"${this.username}:${this.password}@${this.host}" "${this.username}:${this.password}@${this.host}"

View File

@ -8,7 +8,6 @@ ext {
pkgNameSuffix = 'all.javguru' pkgNameSuffix = 'all.javguru'
extClass = '.JavGuru' extClass = '.JavGuru'
extVersionCode = 9 extVersionCode = 9
libVersion = '13'
containsNsfw = true containsNsfw = true
} }

View File

@ -20,20 +20,16 @@ import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.Call import okhttp3.Call
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.select.Elements import org.jsoup.select.Elements
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import kotlin.math.min import kotlin.math.min
@ -48,8 +44,6 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
override val supportsLatest = true override val supportsLatest = true
override val client = network.cloudflareClient
private val noRedirectClient = client.newBuilder() private val noRedirectClient = client.newBuilder()
.followRedirects(false) .followRedirects(false)
.build() .build()
@ -60,13 +54,13 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
private lateinit var popularElements: Elements private lateinit var popularElements: Elements
override fun fetchPopularAnime(page: Int): Observable<AnimesPage> { override suspend fun getPopularAnime(page: Int): AnimesPage {
return if (page == 1) { return if (page == 1) {
client.newCall(popularAnimeRequest(page)) client.newCall(popularAnimeRequest(page))
.asObservableSuccess() .awaitSuccess()
.map(::popularAnimeParse) .use(::popularAnimeParse)
} else { } else {
Observable.just(cachedPopularAnimeParse(page)) cachedPopularAnimeParse(page)
} }
} }
@ -126,22 +120,22 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
return AnimesPage(entries, page < lastPage) return AnimesPage(entries, page < lastPage)
} }
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> { override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
if (query.startsWith(PREFIX_ID)) { if (query.startsWith(PREFIX_ID)) {
val id = query.substringAfter(PREFIX_ID) val id = query.substringAfter(PREFIX_ID)
if (id.toIntOrNull() == null) { if (id.toIntOrNull() == null) {
return Observable.just(AnimesPage(emptyList(), false)) return AnimesPage(emptyList(), false)
} }
val url = "/$id/" val url = "/$id/"
val tempAnime = SAnime.create().apply { this.url = url } val tempAnime = SAnime.create().apply { this.url = url }
return fetchAnimeDetails(tempAnime).map { return getAnimeDetails(tempAnime).let {
val anime = it.apply { this.url = url } val anime = it.apply { this.url = url }
AnimesPage(listOf(anime), false) AnimesPage(listOf(anime), false)
} }
} else if (query.isNotEmpty()) { } else if (query.isNotEmpty()) {
return client.newCall(searchAnimeRequest(page, query, filters)) return client.newCall(searchAnimeRequest(page, query, filters))
.asObservableSuccess() .awaitSuccess()
.map(::searchAnimeParse) .use(::searchAnimeParse)
} else { } else {
filters.forEach { filter -> filters.forEach { filter ->
when (filter) { when (filter) {
@ -152,8 +146,8 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
val url = "$baseUrl${filter.toUrlPart()}" + if (page > 1) "page/$page/" else "" val url = "$baseUrl${filter.toUrlPart()}" + if (page > 1) "page/$page/" else ""
val request = GET(url, headers) val request = GET(url, headers)
return client.newCall(request) return client.newCall(request)
.asObservableSuccess() .awaitSuccess()
.map(::searchAnimeParse) .use(::searchAnimeParse)
} }
} }
is ActressFilter, is ActressFilter,
@ -165,8 +159,8 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
val url = "$baseUrl${filter.toUrlPart()}" + if (page > 1) "page/$page/" else "" val url = "$baseUrl${filter.toUrlPart()}" + if (page > 1) "page/$page/" else ""
val request = GET(url, headers) val request = GET(url, headers)
return client.newCall(request) return client.newCall(request)
.asObservableIgnoreCode(404) .awaitIgnoreCode(404)
.map(::searchAnimeParse) .use(::searchAnimeParse)
} }
} }
else -> { } else -> { }
@ -218,14 +212,12 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
} }
} }
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> { override suspend fun getEpisodeList(anime: SAnime): List<SEpisode> {
return Observable.just( return listOf(
listOf(
SEpisode.create().apply { SEpisode.create().apply {
url = anime.url url = anime.url
name = "Episode" name = "Episode"
}, },
),
) )
} }
@ -242,8 +234,7 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
return iframeUrls return iframeUrls
.mapNotNull(::resolveHosterUrl) .mapNotNull(::resolveHosterUrl)
.parallelMap(::getVideos) .parallelCatchingFlatMapBlocking(::getVideos)
.flatten()
} }
private fun resolveHosterUrl(iframeUrl: String): String? { private fun resolveHosterUrl(iframeUrl: String): String? {
@ -289,8 +280,7 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
private val emTurboExtractor by lazy { EmTurboExtractor(client, headers) } private val emTurboExtractor by lazy { EmTurboExtractor(client, headers) }
private fun getVideos(hosterUrl: String): List<Video> { private fun getVideos(hosterUrl: String): List<Video> {
return runCatching { return when {
when {
hosterUrl.contains("javplaya") -> { hosterUrl.contains("javplaya") -> {
streamWishExtractor.videosFromUrl(hosterUrl) streamWishExtractor.videosFromUrl(hosterUrl)
} }
@ -315,12 +305,9 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
emTurboExtractor.getVideos(hosterUrl) emTurboExtractor.getVideos(hosterUrl)
} }
else -> { else -> emptyList()
emptyList()
} }
} }
}.getOrDefault(emptyList())
}
override fun List<Video>.sort(): List<Video> { override fun List<Video>.sort(): List<Video> {
val quality = preference.getString(PREF_QUALITY, PREF_QUALITY_DEFAULT)!! val quality = preference.getString(PREF_QUALITY, PREF_QUALITY_DEFAULT)!!
@ -330,11 +317,6 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
).reversed() ).reversed()
} }
private fun <A, B> Iterable<A>.parallelMap(f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
private fun getIDFromUrl(element: Elements): String? { private fun getIDFromUrl(element: Elements): String? {
return element.attr("abs:href") return element.attr("abs:href")
.toHttpUrlOrNull() .toHttpUrlOrNull()
@ -353,8 +335,8 @@ class JavGuru : AnimeHttpSource(), ConfigurableAnimeSource {
?.last() ?.last()
?.toIntOrNull() ?.toIntOrNull()
private fun Call.asObservableIgnoreCode(code: Int): Observable<Response> { private suspend fun Call.awaitIgnoreCode(code: Int): Response {
return asObservable().doOnNext { response -> return await().also { response ->
if (!response.isSuccessful && response.code != code) { if (!response.isSuccessful && response.code != code) {
response.close() response.close()
throw Exception("HTTP error ${response.code}") throw Exception("HTTP error ${response.code}")

View File

@ -9,7 +9,6 @@ ext {
pkgNameSuffix = 'all.jellyfin' pkgNameSuffix = 'all.jellyfin'
extClass = '.JellyfinFactory' extClass = '.JellyfinFactory'
extVersionCode = 11 extVersionCode = 11
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -19,7 +19,8 @@ import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -31,7 +32,6 @@ import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.Jsoup import org.jsoup.Jsoup
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -135,10 +135,10 @@ class Jellyfin(private val suffix: String) : ConfigurableAnimeSource, AnimeHttpS
override fun popularAnimeParse(response: Response): AnimesPage = throw Exception("Not used") override fun popularAnimeParse(response: Response): AnimesPage = throw Exception("Not used")
override fun fetchPopularAnime(page: Int): Observable<AnimesPage> { override suspend fun getPopularAnime(page: Int): AnimesPage {
return client.newCall(popularAnimeRequest(page)) return client.newCall(popularAnimeRequest(page))
.asObservableSuccess() .awaitSuccess()
.map { response -> .use { response ->
popularAnimeParsePage(response, page) popularAnimeParsePage(response, page)
} }
} }
@ -175,10 +175,10 @@ class Jellyfin(private val suffix: String) : ConfigurableAnimeSource, AnimeHttpS
override fun latestUpdatesParse(response: Response) = throw Exception("Not used") override fun latestUpdatesParse(response: Response) = throw Exception("Not used")
override fun fetchLatestUpdates(page: Int): Observable<AnimesPage> { override suspend fun getLatestUpdates(page: Int): AnimesPage {
return client.newCall(latestUpdatesRequest(page)) return client.newCall(latestUpdatesRequest(page))
.asObservableSuccess() .awaitSuccess()
.map { response -> .use { response ->
latestUpdatesParsePage(response, page) latestUpdatesParsePage(response, page)
} }
} }
@ -211,7 +211,7 @@ class Jellyfin(private val suffix: String) : ConfigurableAnimeSource, AnimeHttpS
override fun searchAnimeParse(response: Response) = throw Exception("Not used") override fun searchAnimeParse(response: Response) = throw Exception("Not used")
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> { override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
require(parentId.isNotEmpty()) { "Select library in the extension settings." } require(parentId.isNotEmpty()) { "Select library in the extension settings." }
val startIndex = (page - 1) * 5 val startIndex = (page - 1) * 5
@ -240,7 +240,7 @@ class Jellyfin(private val suffix: String) : ConfigurableAnimeSource, AnimeHttpS
getAnimeFromId(it.Id) getAnimeFromId(it.Id)
} }
return Observable.just(AnimesPage(animeList, 5 * page < items.TotalRecordCount)) return AnimesPage(animeList, 5 * page < items.TotalRecordCount)
} }
private fun getAnimeFromMovie(movieList: List<ItemsResponse.Item>): List<SAnime> { private fun getAnimeFromMovie(movieList: List<ItemsResponse.Item>): List<SAnime> {
@ -557,11 +557,6 @@ class Jellyfin(private val suffix: String) : ConfigurableAnimeSource, AnimeHttpS
return json.encodeToString(this) return json.encodeToString(this)
} }
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
val responseBody = use { transform(it.body.string()) }
return json.decodeFromString(responseBody)
}
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val mediaLibPref = medialibPreference(screen) val mediaLibPref = medialibPreference(screen)
screen.addPreference( screen.addPreference(

View File

@ -5,6 +5,7 @@ import android.os.Build
import android.util.Log import android.util.Log
import eu.kanade.tachiyomi.AppInfo import eu.kanade.tachiyomi.AppInfo
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.buildJsonObject
@ -13,7 +14,6 @@ import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class JellyfinAuthenticator( class JellyfinAuthenticator(
@ -84,11 +84,6 @@ class JellyfinAuthenticator(
value, value,
).apply() ).apply()
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
val responseBody = use { transform(it.body.string()) }
return json.decodeFromString(responseBody)
}
companion object { companion object {
private const val DEVICEID_KEY = "device_id" private const val DEVICEID_KEY = "device_id"
private const val CLIENT = "Aniyomi" private const val CLIENT = "Aniyomi"

View File

@ -9,7 +9,6 @@ ext {
pkgNameSuffix = 'all.kamyroll' pkgNameSuffix = 'all.kamyroll'
extClass = '.Yomiroll' extClass = '.Yomiroll'
extVersionCode = 29 extVersionCode = 29
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -16,12 +16,12 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.parallelCatchingFlatMap
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import eu.kanade.tachiyomi.util.parallelMapNotNullBlocking
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
@ -30,7 +30,6 @@ import kotlinx.serialization.json.jsonObject
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -176,7 +175,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
} }
} }
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> { override suspend fun getAnimeDetails(anime: SAnime): SAnime {
val mediaId = json.decodeFromString<LinkData>(anime.url) val mediaId = json.decodeFromString<LinkData>(anime.url)
val resp = client.newCall( val resp = client.newCall(
if (mediaId.media_type == "series") { if (mediaId.media_type == "series") {
@ -186,9 +185,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
}, },
).execute().use { it.body.string() } ).execute().use { it.body.string() }
val info = json.decodeFromString<AnimeResult>(resp) val info = json.decodeFromString<AnimeResult>(resp)
return Observable.just( return info.data.first().toSAnimeOrNull(anime) ?: anime
info.data.first().toSAnimeOrNull(anime) ?: anime,
)
} }
override fun animeDetailsParse(response: Response): SAnime = throw Exception("not used") override fun animeDetailsParse(response: Response): SAnime = throw Exception("not used")
@ -210,11 +207,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
val chunkSize = Runtime.getRuntime().availableProcessors() val chunkSize = Runtime.getRuntime().availableProcessors()
return if (series) { return if (series) {
seasons.data.sortedBy { it.season_number }.chunked(chunkSize).flatMap { chunk -> seasons.data.sortedBy { it.season_number }.chunked(chunkSize).flatMap { chunk ->
chunk.parallelMap { seasonData -> chunk.parallelCatchingFlatMapBlocking(::getEpisodes)
runCatching {
getEpisodes(seasonData)
}.getOrNull()
}.filterNotNull().flatten()
}.reversed() }.reversed()
} else { } else {
seasons.data.mapIndexed { index, movie -> seasons.data.mapIndexed { index, movie ->
@ -263,7 +256,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================ Video Links ============================= // ============================ Video Links =============================
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> { override suspend fun getVideoList(episode: SEpisode): List<Video> {
val urlJson = json.decodeFromString<EpisodeData>(episode.url) val urlJson = json.decodeFromString<EpisodeData>(episode.url)
val dubLocale = preferences.getString(PREF_AUD_KEY, PREF_AUD_DEFAULT)!! val dubLocale = preferences.getString(PREF_AUD_KEY, PREF_AUD_DEFAULT)!!
@ -276,13 +269,9 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
it.second == "en-US" || it.second == "en-US" ||
it.second == "" || it.second == "" ||
if (isUsingLocalToken) it.second == urlJson.ids.first().second else false if (isUsingLocalToken) it.second == urlJson.ids.first().second else false
}.parallelMap { media -> }.parallelCatchingFlatMap(::extractVideo)
runCatching {
extractVideo(media)
}.getOrNull()
}.filterNotNull().flatten()
return Observable.just(videoList.sort()) return videoList.sort()
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
@ -314,11 +303,11 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
audLang: String, audLang: String,
subsList: List<Track>, subsList: List<Track>,
): List<Video> { ): List<Video> {
return streams.streams?.adaptive_hls?.entries?.parallelMap { (_, value) -> return streams.streams?.adaptive_hls?.entries?.parallelMapNotNullBlocking { (_, value) ->
val stream = json.decodeFromString<HlsLinks>(value.jsonObject.toString()) val stream = json.decodeFromString<HlsLinks>(value.jsonObject.toString())
runCatching { runCatching {
val playlist = client.newCall(GET(stream.url)).execute() val playlist = client.newCall(GET(stream.url)).execute()
if (playlist.code != 200) return@parallelMap null if (playlist.code != 200) return@parallelMapNotNullBlocking null
playlist.use { it.body.string() }.substringAfter("#EXT-X-STREAM-INF:") playlist.use { it.body.string() }.substringAfter("#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:").map { .split("#EXT-X-STREAM-INF:").map {
val hardsub = stream.hardsub_locale.let { hs -> val hardsub = stream.hardsub_locale.let { hs ->
@ -342,7 +331,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
} }
} }
}.getOrNull() }.getOrNull()
}?.filterNotNull()?.flatten() ?: emptyList() }?.flatten() ?: emptyList()
} }
private fun getVideoRequest(mediaId: String): Request { private fun getVideoRequest(mediaId: String): Request {
@ -421,7 +410,9 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
val media = json.decodeFromString<LinkData>(anime.url) val media = json.decodeFromString<LinkData>(anime.url)
if (media.media_type == "series") { if (media.media_type == "series") {
fetchStatusByTitle(this@toSAnime.title) fetchStatusByTitle(this@toSAnime.title)
} else SAnime.COMPLETED } else {
SAnime.COMPLETED
}
} ?: SAnime.UNKNOWN } ?: SAnime.UNKNOWN
author = content_provider author = content_provider
description = anime?.description ?: StringBuilder().apply { description = anime?.description ?: StringBuilder().apply {
@ -600,12 +591,6 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
} }
}.apply { reload() } }.apply { reload() }
// From Dopebox
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
private fun getTokenDetail(force: Boolean = false): String { private fun getTokenDetail(force: Boolean = false): String {
return runCatching { return runCatching {
val storedToken = tokenInterceptor.getAccessToken(force) val storedToken = tokenInterceptor.getAccessToken(force)

View File

@ -20,7 +20,6 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -34,8 +33,6 @@ class MissAV : AnimeHttpSource(), ConfigurableAnimeSource {
override val supportsLatest = true override val supportsLatest = true
override val client = network.cloudflareClient
override fun headersBuilder() = super.headersBuilder() override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/") .add("Referer", "$baseUrl/")
@ -136,14 +133,12 @@ class MissAV : AnimeHttpSource(), ConfigurableAnimeSource {
.joinToString() .joinToString()
.takeIf(String::isNotBlank) .takeIf(String::isNotBlank)
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> { override suspend fun getEpisodeList(anime: SAnime): List<SEpisode> {
return Observable.just( return listOf(
listOf(
SEpisode.create().apply { SEpisode.create().apply {
url = anime.url url = anime.url
name = "Episode" name = "Episode"
}, },
),
) )
} }

View File

@ -8,7 +8,6 @@ ext {
pkgNameSuffix = 'all.supjav' pkgNameSuffix = 'all.supjav'
extClass = '.SupJavFactory' extClass = '.SupJavFactory'
extVersionCode = 1 extVersionCode = 1
libVersion = '13'
containsNsfw = true containsNsfw = true
} }

View File

@ -15,18 +15,14 @@ import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.select.Elements import org.jsoup.select.Elements
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -38,8 +34,6 @@ class SupJav(override val lang: String = "en") : ConfigurableAnimeSource, Parsed
override val supportsLatest = false override val supportsLatest = false
override val client = network.cloudflareClient
override fun headersBuilder() = super.headersBuilder() override fun headersBuilder() = super.headersBuilder()
.set("Referer", "$baseUrl/") .set("Referer", "$baseUrl/")
.set("Origin", baseUrl) .set("Origin", baseUrl)
@ -87,14 +81,14 @@ class SupJav(override val lang: String = "en") : ConfigurableAnimeSource, Parsed
} }
// =============================== Search =============================== // =============================== Search ===============================
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> { override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
val id = query.removePrefix(PREFIX_SEARCH) val id = query.removePrefix(PREFIX_SEARCH)
client.newCall(GET("$baseUrl/$id")) client.newCall(GET("$baseUrl/$id"))
.asObservableSuccess() .awaitSuccess()
.map(::searchAnimeByIdParse) .use(::searchAnimeByIdParse)
} else { } else {
super.fetchSearchAnime(page, query, filters) super.getSearchAnime(page, query, filters)
} }
} }
@ -129,14 +123,14 @@ class SupJav(override val lang: String = "en") : ConfigurableAnimeSource, Parsed
private fun Elements.textsOrNull() = eachText().joinToString().takeUnless(String::isEmpty) private fun Elements.textsOrNull() = eachText().joinToString().takeUnless(String::isEmpty)
// ============================== Episodes ============================== // ============================== Episodes ==============================
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> { override suspend fun getEpisodeList(anime: SAnime): List<SEpisode> {
val episode = SEpisode.create().apply { val episode = SEpisode.create().apply {
name = "JAV" name = "JAV"
episode_number = 1F episode_number = 1F
url = anime.url url = anime.url
} }
return Observable.just(listOf(episode)) return listOf(episode)
} }
override fun episodeListSelector(): String { override fun episodeListSelector(): String {
@ -155,7 +149,7 @@ class SupJav(override val lang: String = "en") : ConfigurableAnimeSource, Parsed
.filter { it.text() in SUPPORTED_PLAYERS } .filter { it.text() in SUPPORTED_PLAYERS }
.map { it.text() to it.attr("data-link").reversed() } .map { it.text() to it.attr("data-link").reversed() }
return players.parallelCatchingFlatMap(::videosFromPlayer) return players.parallelCatchingFlatMapBlocking(::videosFromPlayer)
} }
private val playlistUtils by lazy { PlaylistUtils(client, headers) } private val playlistUtils by lazy { PlaylistUtils(client, headers) }
@ -227,15 +221,6 @@ class SupJav(override val lang: String = "en") : ConfigurableAnimeSource, Parsed
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <A, B> Iterable<A>.parallelCatchingFlatMap(crossinline f: suspend (A) -> Iterable<B>): List<B> =
runBlocking {
map {
async(Dispatchers.Default) {
runCatching { f(it) }.getOrElse { emptyList() }
}
}.awaitAll().flatten()
}
override fun List<Video>.sort(): List<Video> { override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!! val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!

View File

@ -6,7 +6,6 @@ ext {
pkgNameSuffix = 'ar.akwam' pkgNameSuffix = 'ar.akwam'
extClass = '.Akwam' extClass = '.Akwam'
extVersionCode = 9 extVersionCode = 9
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -15,7 +15,6 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -34,8 +33,6 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = false override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }

View File

@ -9,7 +9,6 @@ ext {
pkgNameSuffix = 'ar.anime4up' pkgNameSuffix = 'ar.anime4up'
extClass = '.Anime4Up' extClass = '.Anime4Up'
extVersionCode = 52 extVersionCode = 52
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -22,10 +22,7 @@ import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -48,8 +45,6 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = false override val supportsLatest = false
override val client = network.cloudflareClient
private val json: Json by injectLazy() private val json: Json by injectLazy()
private val preferences by lazy { private val preferences by lazy {
@ -159,7 +154,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val parsedData = json.decodeFromString<Qualities>(base64) val parsedData = json.decodeFromString<Qualities>(base64)
val streamLinks = with(parsedData) { fhd + hd + sd } val streamLinks = with(parsedData) { fhd + hd + sd }
return streamLinks.values.distinct().parallelCatchingFlatMap(::extractVideos) return streamLinks.values.distinct().parallelCatchingFlatMapBlocking(::extractVideos)
} }
private val uqloadExtractor by lazy { UqloadExtractor(client) } private val uqloadExtractor by lazy { UqloadExtractor(client) }
@ -216,15 +211,6 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <A, B> Iterable<A>.parallelCatchingFlatMap(crossinline f: suspend (A) -> Iterable<B>): List<B> =
runBlocking {
map {
async(Dispatchers.Default) {
runCatching { f(it) }.getOrElse { emptyList() }
}
}.awaitAll().flatten()
}
override fun List<Video>.sort(): List<Video> { override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!! val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!

View File

@ -8,7 +8,6 @@ ext {
pkgNameSuffix = 'ar.animeblkom' pkgNameSuffix = 'ar.animeblkom'
extClass = '.AnimeBlkom' extClass = '.AnimeBlkom'
extVersionCode = 16 extVersionCode = 16
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -32,8 +32,6 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = false override val supportsLatest = false
override val client = network.cloudflareClient
override fun headersBuilder() = super.headersBuilder() override fun headersBuilder() = super.headersBuilder()
.add("referer", baseUrl) .add("referer", baseUrl)

View File

@ -7,7 +7,6 @@ ext {
pkgNameSuffix = 'ar.animeiat' pkgNameSuffix = 'ar.animeiat'
extClass = '.Animeiat' extClass = '.Animeiat'
extVersionCode = 1 extVersionCode = 1
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -8,7 +8,6 @@ ext {
pkgNameSuffix = 'ar.animelek' pkgNameSuffix = 'ar.animelek'
extClass = '.AnimeLek' extClass = '.AnimeLek'
extVersionCode = 27 extVersionCode = 27
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -17,7 +17,6 @@ import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -36,8 +35,6 @@ class AnimeLek : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }

View File

@ -8,7 +8,6 @@ ext {
pkgNameSuffix = 'ar.animerco' pkgNameSuffix = 'ar.animerco'
extClass = '.Animerco' extClass = '.Animerco'
extVersionCode = 34 extVersionCode = 34
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -23,12 +23,8 @@ import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -47,8 +43,6 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = false override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -160,9 +154,7 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val document = response.use { it.asJsoup() } val document = response.use { it.asJsoup() }
val players = document.select(videoListSelector()) val players = document.select(videoListSelector())
return players.parallelMap { return players.parallelCatchingFlatMapBlocking(::getPlayerVideos)
runCatching { getPlayerVideos(it) }.getOrElse { emptyList() }
}.flatten()
} }
override fun videoListSelector() = "li.dooplay_player_option" // ul#playeroptionsul override fun videoListSelector() = "li.dooplay_player_option" // ul#playeroptionsul
@ -250,11 +242,6 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
companion object { companion object {
private const val PREF_QUALITY_KEY = "preferred_quality" private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Preferred quality" private const val PREF_QUALITY_TITLE = "Preferred quality"

View File

@ -9,7 +9,6 @@ ext {
pkgNameSuffix = 'ar.arabanime' pkgNameSuffix = 'ar.arabanime'
extClass = '.ArabAnime' extClass = '.ArabAnime'
extVersionCode = 2 extVersionCode = 2
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -38,8 +38,6 @@ class ArabAnime : ConfigurableAnimeSource, AnimeHttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client = network.cloudflareClient
private val json: Json by injectLazy() private val json: Json by injectLazy()
private val preferences by lazy { private val preferences by lazy {

View File

@ -8,7 +8,6 @@ ext {
pkgNameSuffix = 'ar.arabseed' pkgNameSuffix = 'ar.arabseed'
extClass = '.ArabSeed' extClass = '.ArabSeed'
extVersionCode = 9 extVersionCode = 9
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -18,10 +18,7 @@ import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -42,8 +39,6 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = false override val supportsLatest = false
override val client = network.cloudflareClient
override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl) override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl)
private val preferences by lazy { private val preferences by lazy {
@ -97,11 +92,11 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListSelector() = "div.containerServers ul li" override fun videoListSelector() = "div.containerServers ul li"
private fun videosFromElement(document: Document): List<Video> { private fun videosFromElement(document: Document): List<Video> {
return document.select(videoListSelector()).parallelMap { element -> return document.select(videoListSelector()).parallelCatchingFlatMapBlocking { element ->
val quality = element.text() val quality = element.text()
val embedUrl = element.attr("data-link") val embedUrl = element.attr("data-link")
runCatching { getVideosFromUrl(embedUrl, quality) }.getOrElse { emptyList() } getVideosFromUrl(embedUrl, quality)
}.flatten() }
} }
private val doodExtractor by lazy { DoodExtractor(client) } private val doodExtractor by lazy { DoodExtractor(client) }
@ -251,11 +246,6 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
companion object { companion object {
// From egydead(ar) // From egydead(ar)
private const val PREF_DOMAIN_KEY = "default_domain_v${BuildConfig.VERSION_NAME}" private const val PREF_DOMAIN_KEY = "default_domain_v${BuildConfig.VERSION_NAME}"

View File

@ -8,7 +8,6 @@ ext {
pkgNameSuffix = 'ar.asia2tv' pkgNameSuffix = 'ar.asia2tv'
extClass = '.Asia2TV' extClass = '.Asia2TV'
extVersionCode = 15 extVersionCode = 15
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -19,12 +19,8 @@ import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -43,8 +39,6 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = false override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -90,7 +84,7 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val document = response.use { it.asJsoup() } val document = response.use { it.asJsoup() }
return document.select(videoListSelector()).parallelCatchingFlatMap { return document.select(videoListSelector()).parallelCatchingFlatMapBlocking {
val url = it.attr("data-server") val url = it.attr("data-server")
getVideosFromUrl(url) getVideosFromUrl(url)
} }
@ -265,15 +259,6 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <A, B> Iterable<A>.parallelCatchingFlatMap(crossinline f: suspend (A) -> Iterable<B>): List<B> =
runBlocking {
map {
async(Dispatchers.Default) {
runCatching { f(it) }.getOrElse { emptyList() }
}
}.awaitAll().flatten()
}
companion object { companion object {
private val STREAM_WISH_DOMAINS by lazy { listOf("wishfast", "fviplions", "filelions", "streamwish", "dwish") } private val STREAM_WISH_DOMAINS by lazy { listOf("wishfast", "fviplions", "filelions", "streamwish", "dwish") }
private val VID_BOM_DOMAINS by lazy { listOf("vidbam", "vadbam", "vidbom", "vidbm") } private val VID_BOM_DOMAINS by lazy { listOf("vidbam", "vadbam", "vidbom", "vidbm") }

View File

@ -6,7 +6,6 @@ ext {
pkgNameSuffix = 'ar.egydead' pkgNameSuffix = 'ar.egydead'
extClass = '.EgyDead' extClass = '.EgyDead'
extVersionCode = 10 extVersionCode = 10
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -19,13 +19,10 @@ import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMap
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -46,8 +43,6 @@ class EgyDead : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -132,19 +127,18 @@ class EgyDead : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// ================================== video urls ================================== // ================================== video urls ==================================
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) } private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
override fun videoListParse(response: Response): List<Video> { override suspend fun getVideoList(episode: SEpisode): List<Video> {
val requestBody = FormBody.Builder().add("View", "1").build() val requestBody = FormBody.Builder().add("View", "1").build()
val document = client.newCall(POST(response.request.url.toString(), body = requestBody)).execute().asJsoup()
return document.select(videoListSelector()).parallelMap { val document = client.newCall(POST(baseUrl + episode.url, body = requestBody))
.await()
.use { it.asJsoup() }
return document.select(videoListSelector()).parallelCatchingFlatMap {
val url = it.attr("data-link") val url = it.attr("data-link")
runCatching { extractVideos(url) }.getOrElse { emptyList() } extractVideos(url)
}.flatten() }
} }
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
private fun extractVideos(url: String): List<Video> { private fun extractVideos(url: String): List<Video> {
return when { return when {
DOOD_REGEX.containsMatchIn(url) -> { DOOD_REGEX.containsMatchIn(url) -> {

View File

@ -6,7 +6,6 @@ ext {
pkgNameSuffix = 'ar.faselhd' pkgNameSuffix = 'ar.faselhd'
extClass = '.FASELHD' extClass = '.FASELHD'
extVersionCode = 14 extVersionCode = 14
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -15,7 +15,6 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -34,8 +33,6 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }

View File

@ -6,7 +6,6 @@ ext {
pkgNameSuffix = 'ar.movies4u' pkgNameSuffix = 'ar.movies4u'
extClass = '.Movies4U' extClass = '.Movies4U'
extVersionCode = 4 extVersionCode = 4
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -14,7 +14,6 @@ import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -34,8 +33,6 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = false override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }

View File

@ -6,7 +6,6 @@ ext {
pkgNameSuffix = 'ar.mycima' pkgNameSuffix = 'ar.mycima'
extClass = '.MyCima' extClass = '.MyCima'
extVersionCode = 20 extVersionCode = 20
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -16,11 +16,7 @@ import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -39,8 +35,6 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -124,9 +118,8 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup() val document = response.asJsoup()
return document.select("ul.WatchServersList li btn").parallelMap { return document.select("ul.WatchServersList li btn").parallelCatchingFlatMapBlocking {
val frameURL = it.attr("data-url") val frameURL = it.attr("data-url")
runCatching {
if (it.parent()?.hasClass("MyCimaServer") == true) { if (it.parent()?.hasClass("MyCimaServer") == true) {
val referer = response.request.url.encodedPath val referer = response.request.url.encodedPath
val newHeader = headers.newBuilder().add("referer", baseUrl + referer).build() val newHeader = headers.newBuilder().add("referer", baseUrl + referer).build()
@ -135,8 +128,7 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} else { } else {
extractVideos(frameURL) extractVideos(frameURL)
} }
}.getOrElse { emptyList() } }
}.flatten()
} }
private fun extractVideos(url: String): List<Video> { private fun extractVideos(url: String): List<Video> {
@ -367,10 +359,6 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun getPrefBaseUrl(): String = preferences.getString(PREF_BASE_URL_KEY, PREF_BASE_URL_DEFAULT)!! private fun getPrefBaseUrl(): String = preferences.getString(PREF_BASE_URL_KEY, PREF_BASE_URL_DEFAULT)!!
// ============================= Utilities =================================== // ============================= Utilities ===================================
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
companion object { companion object {
private const val PREF_QUALITY_KEY = "preferred_quality" private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Preferred quality" private const val PREF_QUALITY_TITLE = "Preferred quality"

View File

@ -8,7 +8,6 @@ ext {
pkgNameSuffix = 'ar.okanime' pkgNameSuffix = 'ar.okanime'
extClass = '.Okanime' extClass = '.Okanime'
extVersionCode = 5 extVersionCode = 5
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -17,16 +17,12 @@ import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -69,14 +65,14 @@ class Okanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun latestUpdatesNextPageSelector() = "ul.pagination > li:last-child:not(.disabled)" override fun latestUpdatesNextPageSelector() = "ul.pagination > li:last-child:not(.disabled)"
// =============================== Search =============================== // =============================== Search ===============================
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> { override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
val id = query.removePrefix(PREFIX_SEARCH) val id = query.removePrefix(PREFIX_SEARCH)
client.newCall(GET("$baseUrl/anime/$id")) client.newCall(GET("$baseUrl/anime/$id"))
.asObservableSuccess() .awaitSuccess()
.map(::searchAnimeByIdParse) .use(::searchAnimeByIdParse)
} else { } else {
super.fetchSearchAnime(page, query, filters) super.getSearchAnime(page, query, filters)
} }
} }
@ -141,7 +137,7 @@ class Okanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val hosterSelection = preferences.getStringSet(PREF_HOSTER_SELECTION_KEY, PREF_HOSTER_SELECTION_DEFAULT)!! val hosterSelection = preferences.getStringSet(PREF_HOSTER_SELECTION_KEY, PREF_HOSTER_SELECTION_DEFAULT)!!
return response.use { it.asJsoup() } return response.use { it.asJsoup() }
.select("a.ep-link") .select("a.ep-link")
.parallelMap { element -> .parallelCatchingFlatMapBlocking { element ->
val quality = element.selectFirst("span")?.text().orEmpty().let { val quality = element.selectFirst("span")?.text().orEmpty().let {
when (it) { when (it) {
"HD" -> "720p" "HD" -> "720p"
@ -152,7 +148,7 @@ class Okanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
val url = element.attr("data-src") val url = element.attr("data-src")
extractVideosFromUrl(url, quality, hosterSelection) extractVideosFromUrl(url, quality, hosterSelection)
}.flatten() }
} }
// Inspirated by JavGuru(all) // Inspirated by JavGuru(all)
@ -163,8 +159,7 @@ class Okanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private val vidBomExtractor by lazy { VidBomExtractor(client) } private val vidBomExtractor by lazy { VidBomExtractor(client) }
private fun extractVideosFromUrl(url: String, quality: String, selection: Set<String>): List<Video> { private fun extractVideosFromUrl(url: String, quality: String, selection: Set<String>): List<Video> {
return runCatching { return when {
when {
"https://doo" in url && "/e/" in url && selection.contains("Dood") -> { "https://doo" in url && "/e/" in url && selection.contains("Dood") -> {
doodExtractor.videoFromUrl(url, "DoodStream - $quality") doodExtractor.videoFromUrl(url, "DoodStream - $quality")
?.let(::listOf) ?.let(::listOf)
@ -183,8 +178,7 @@ class Okanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
vidBomExtractor.videosFromUrl(url) vidBomExtractor.videosFromUrl(url)
} }
else -> null else -> null
} }.orEmpty()
}.getOrNull() ?: emptyList()
} }
override fun videoListSelector(): String { override fun videoListSelector(): String {
@ -238,11 +232,6 @@ class Okanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
companion object { companion object {
const val PREFIX_SEARCH = "id:" const val PREFIX_SEARCH = "id:"

View File

@ -6,7 +6,6 @@ ext {
pkgNameSuffix = 'ar.tuktukcinema' pkgNameSuffix = 'ar.tuktukcinema'
extClass = '.Tuktukcinema' extClass = '.Tuktukcinema'
extVersionCode = 15 extVersionCode = 15
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -23,11 +23,7 @@ import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -48,8 +44,6 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -149,13 +143,8 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup() val document = response.asJsoup()
return document.select(videoListSelector()).parallelMap { return document.select(videoListSelector())
runCatching { extractVideos(it) }.getOrElse { emptyList() } .parallelCatchingFlatMapBlocking(::extractVideos)
}.flatten()
}
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
} }
private fun extractVideos(element: Element): List<Video> { private fun extractVideos(element: Element): List<Video> {

View File

@ -8,7 +8,6 @@ ext {
pkgNameSuffix = 'ar.witanime' pkgNameSuffix = 'ar.witanime'
extClass = '.WitAnime' extClass = '.WitAnime'
extVersionCode = 47 extVersionCode = 47
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -19,10 +19,7 @@ import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -40,8 +37,6 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client = network.cloudflareClient
override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl) override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl)
private val preferences by lazy { private val preferences by lazy {
@ -130,7 +125,7 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val document = response.asJsoup() val document = response.asJsoup()
return document.select("ul#episode-servers li a") return document.select("ul#episode-servers li a")
.distinctBy { it.text().substringBefore(" -") } // remove duplicates by server name .distinctBy { it.text().substringBefore(" -") } // remove duplicates by server name
.parallelCatchingFlatMap { .parallelCatchingFlatMapBlocking {
val url = it.attr("data-url") val url = it.attr("data-url")
.takeUnless(String::isBlank) .takeUnless(String::isBlank)
?.let { String(Base64.decode(it, Base64.DEFAULT)) } ?.let { String(Base64.decode(it, Base64.DEFAULT)) }
@ -237,15 +232,6 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
// ============================= Utilities ============================== // ============================= Utilities ==============================
private inline fun <A, B> Iterable<A>.parallelCatchingFlatMap(crossinline f: suspend (A) -> Iterable<B>): List<B> =
runBlocking {
map {
async(Dispatchers.Default) {
runCatching { f(it) }.getOrElse { emptyList() }
}
}.awaitAll().flatten()
}
private fun getRealDoc(document: Document): Document { private fun getRealDoc(document: Document): Document {
return document.selectFirst("div.anime-page-link a")?.let { return document.selectFirst("div.anime-page-link a")?.let {
client.newCall(GET(it.attr("href"), headers)).execute().asJsoup() client.newCall(GET(it.attr("href"), headers)).execute().asJsoup()

View File

@ -6,7 +6,6 @@ ext {
pkgNameSuffix = 'ar.xsanime' pkgNameSuffix = 'ar.xsanime'
extClass = '.XsAnime' extClass = '.XsAnime'
extVersionCode = 10 extVersionCode = 10
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -15,7 +15,6 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -34,8 +33,6 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = false override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }

View File

@ -6,7 +6,6 @@ ext {
pkgNameSuffix = 'ar.xsmovie' pkgNameSuffix = 'ar.xsmovie'
extClass = '.XsMovie' extClass = '.XsMovie'
extVersionCode = 5 extVersionCode = 5
libVersion = '13'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -13,7 +13,6 @@ import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -32,8 +31,6 @@ class XsMovie : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = false override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }

View File

@ -7,7 +7,6 @@ ext {
pkgNameSuffix = 'de.aniflix' pkgNameSuffix = 'de.aniflix'
extClass = '.Aniflix' extClass = '.Aniflix'
extVersionCode = 25 extVersionCode = 25
libVersion = '13'
} }
dependencies { dependencies {

View File

@ -23,16 +23,13 @@ import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Headers import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -46,8 +43,6 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -59,16 +54,8 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
private val refererHeader = Headers.headersOf("Referer", baseUrl) private val refererHeader = Headers.headersOf("Referer", baseUrl)
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> { override fun getAnimeUrl(anime: SAnime): String {
return client.newCall(GET(baseUrl + anime.url)) return baseUrl + anime.url.replace("api/", "")
.asObservableSuccess()
.map { response ->
animeDetailsParse(response).apply { initialized = true }
}
}
override fun animeDetailsRequest(anime: SAnime): Request {
return GET(baseUrl + anime.url.replace("api/", ""))
} }
override fun animeDetailsParse(response: Response): SAnime { override fun animeDetailsParse(response: Response): SAnime {

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