Turkanime: Add some sources (#1554)

This commit is contained in:
Secozzi
2023-04-29 20:27:30 +02:00
committed by GitHub
parent 1e04850bd4
commit ffee01f3ec
18 changed files with 712 additions and 8 deletions

View File

@ -9,13 +9,16 @@ dependencies {
implementation(project(":lib-synchrony"))
implementation(project(":lib-voe-extractor"))
implementation(project(":lib-streamsb-extractor"))
implementation(project(":lib-dood-extractor"))
implementation(project(":lib-okru-extractor"))
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
}
ext {
extName = 'Türk Anime TV'
pkgNameSuffix = 'tr.turkanime'
extClass = '.TurkAnime'
extVersionCode = 2
extVersionCode = 3
libVersion = '13'
}

View File

@ -4,7 +4,24 @@ import android.app.Application
import android.content.SharedPreferences
import android.util.Base64
import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.AlucardExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.EmbedgramExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.FilemoonExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.GoogleDriveExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.MVidooExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.MailRuExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.Mp4uploadExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.MytvExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.SendvidExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.SibnetExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.StreamVidExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.UqloadExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.VTubeExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.VkExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.VudeoExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.WolfstreamExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.SAnime
@ -12,6 +29,8 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamsbextractor.StreamSBExtractor
import eu.kanade.tachiyomi.lib.synchrony.Deobfuscator
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
@ -153,18 +172,24 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val selectedHoster = document.select("div#videodetay div.btn-group:not(.pull-right) > button.btn-danger")
val hosters = document.select("div#videodetay div.btn-group:not(.pull-right) > button.btn-default[onclick*=videosec]")
val hosterSelection = preferences.getStringSet(
"hoster_selection",
setOf("GDRIVE", "STREAMSB", "VOE"),
)!!
val videoList = mutableListOf<Video>()
val selectedHosterName = selectedHoster.text().trim()
if (selectedHosterName in SUPPORTED_HOSTERS) {
if (selectedHosterName in SUPPORTED_HOSTERS && selectedHosterName in hosterSelection) {
val src = document.select("iframe").attr("src")
videoList.addAll(getVideosFromSource(src, selectedHosterName, subber))
}
hosters.parallelMap {
val hosterName = it.text().trim()
if (hosterName !in SUPPORTED_HOSTERS) return@parallelMap
if (hosterName !in hosterSelection) return@parallelMap
val url = it.attr("onclick").trimOnClick()
val videoDoc = client.newCall(GET(url, xmlHeader)).execute().asJsoup()
val src = videoDoc.select("iframe").attr("src")
val src = videoDoc.select("iframe").attr("src").replace("^//".toRegex(), "https://")
videoList.addAll(getVideosFromSource(src, hosterName, subber))
}
return videoList
@ -191,14 +216,68 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
).content
when (hosterName) {
"ALUCARD(BETA)" -> {
videoList.addAll(AlucardExtractor(client, json, baseUrl).extractVideos(hosterLink, subber))
}
"DOODSTREAM" -> {
videoList.addAll(DoodExtractor(client).videosFromUrl(hosterLink, "$subber: DOODSTREAM", redirect = false))
}
"EMBEDGRAM" -> {
videoList.addAll(EmbedgramExtractor(client, headers).videosFromUrl(hosterLink, prefix = "$subber: "))
}
"FILEMOON" -> {
videoList.addAll(FilemoonExtractor(client, headers).videosFromUrl(hosterLink, prefix = "$subber: "))
}
"GDRIVE" -> {
Regex("""[\w-]{28,}""").find(hosterLink)?.groupValues?.get(0)?.let {
videoList.addAll(GoogleDriveExtractor(client, headers).videosFromUrl("https://drive.google.com/uc?id=$it", "$subber: Gdrive"))
}
}
"MAIL" -> {
videoList.addAll(MailRuExtractor(client, headers).videosFromUrl(hosterLink, prefix = "$subber: "))
}
"MP4UPLOAD" -> {
videoList.addAll(Mp4uploadExtractor(client).getVideoFromUrl(hosterLink, headers, prefix = "$subber: "))
}
"MYVI" -> {
videoList.addAll(MytvExtractor(client).videosFromUrl(hosterLink, prefix = "$subber: "))
}
"MVIDOO" -> {
videoList.addAll(MVidooExtractor(client).videosFromUrl(hosterLink, prefix = "$subber: "))
}
"ODNOKLASSNIKI" -> {
videoList.addAll(OkruExtractor(client).videosFromUrl(hosterLink, prefix = "$subber: "))
}
"SENDVID" -> {
videoList.addAll(SendvidExtractor(client, headers).videosFromUrl(hosterLink, prefix = "$subber: "))
}
"SIBNET" -> {
videoList.addAll(SibnetExtractor(client).getVideosFromUrl(hosterLink, prefix = "$subber: "))
}
"STREAMSB" -> {
videoList.addAll(StreamSBExtractor(client).videosFromUrl(hosterLink, refererHeader, "$subber:"))
videoList.addAll(StreamSBExtractor(client).videosFromUrl(hosterLink, refererHeader, prefix = "$subber: "))
}
"STREAMVID" -> {
videoList.addAll(StreamVidExtractor(client).videosFromUrl(hosterLink, headers, prefix = "$subber: "))
}
"UQLOAD" -> {
videoList.addAll(UqloadExtractor(client).videosFromUrl(hosterLink, headers, "$subber: Uqload"))
}
"VK" -> {
val vkUrl = "https://vk.com" + hosterLink.substringAfter("vk.com")
videoList.addAll(VkExtractor(client).getVideosFromUrl(vkUrl, prefix = "$subber: "))
}
"VOE" -> {
VoeExtractor(client).videoFromUrl(hosterLink, "$subber: VOE")?.let { video -> videoList.add(video) }
}
"ALUCARD(BETA)" -> {
videoList.addAll(AlucardExtractor(client, json, baseUrl).extractVideos(hosterLink, subber))
"VTUBE" -> {
videoList.addAll(VTubeExtractor(client, headers).videosFromUrl(hosterLink, baseUrl, prefix = "$subber: "))
}
"VUDEA" -> {
videoList.addAll(VudeoExtractor(client).videosFromUrl(hosterLink, prefix = "$subber: "))
}
"WOLFSTREAM" -> {
videoList.addAll(WolfstreamExtractor(client).videosFromUrl(hosterLink, prefix = "$subber: "))
}
}
return videoList
@ -259,7 +338,19 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
preferences.edit().putString(key, entry).commit()
}
}
val hostSelection = MultiSelectListPreference(screen.context).apply {
key = "hoster_selection"
title = "Enable/Disable Hosts"
entries = SUPPORTED_HOSTERS.toTypedArray()
entryValues = SUPPORTED_HOSTERS.toTypedArray()
setDefaultValue(setOf("GDRIVE", "STREAMSB", "VOE"))
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}
screen.addPreference(videoQualityPref)
screen.addPreference(hostSelection)
}
override fun List<Video>.sort(): List<Video> {
@ -314,8 +405,26 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private val SUPPORTED_HOSTERS = listOf(
// TODO: Fix Alucard
// "ALUCARD(BETA)",
"DOODSTREAM",
"EMBEDGRAM",
"FILEMOON",
"GDRIVE",
"MAIL",
"MP4UPLOAD",
"MYVI",
"MVIDOO",
"ODNOKLASSNIKI",
"SENDVID",
"SIBNET",
"STREAMSB",
"VOE")
"STREAMVID",
"UQLOAD",
"VK",
"VOE",
"VTUBE",
"VUDEA",
"WOLFSTREAM",
)
private const val PREF_KEY_KEY = "key"
private const val DEFAULT_KEY = "710^8A@3@>T2}#zN5xK?kR7KNKb@-A!LzYL5~M1qU0UfdWsZoBm4UUat%}ueUv6E--*hDPPbH7K2bp9^3o41hw,khL:}Kx8080@M"

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET

View File

@ -0,0 +1,28 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
class EmbedgramExtractor(private val client: OkHttpClient, private val headers: Headers) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val response = client.newCall(GET(url)).execute()
val xsrfToken = response.headers.firstOrNull {
it.first == "set-cookie" && it.second.startsWith("XSRF-TOKEN", true)
}?.second?.substringBefore(";") ?: ""
val sourceElement = response.asJsoup().selectFirst("video#my-video > source[src~=.]") ?: return emptyList()
val videoUrl = sourceElement.attr("src").replace("^//".toRegex(), "https://")
val videoHeaders = headers.newBuilder()
.add("Cookie", xsrfToken)
.add("Host", videoUrl.toHttpUrl().host)
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
return listOf(
Video(videoUrl, "${prefix}Embedgram", videoUrl, headers = videoHeaders),
)
}
}

View File

@ -0,0 +1,34 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
class FilemoonExtractor(private val client: OkHttpClient, private val headers: Headers) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val jsE = client.newCall(GET(url)).execute().asJsoup().selectFirst("script:containsData(eval)")!!.data()
val masterUrl = JsUnpacker.unpackAndCombine(jsE)?.substringAfter("{file:\"")
?.substringBefore("\"}") ?: return emptyList()
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body.string()
val videoList = mutableListOf<Video>()
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:")
.forEach {
val quality = "Filemoon:" + it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p "
val videoUrl = it.substringAfter("\n").substringBefore("\n")
val videoHeaders = headers.newBuilder()
.add("Accept", "*/*")
.add("Host", videoUrl.toHttpUrl().host)
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
videoList.add(Video(videoUrl, prefix + quality, videoUrl, headers = videoHeaders))
}
return videoList
}
}

View File

@ -0,0 +1,98 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody
class GoogleDriveExtractor(private val client: OkHttpClient, private val headers: Headers) {
// Needs to be the form of `https://drive.google.com/uc?id=GOOGLEDRIVEITEMID`
fun videosFromUrl(itemUrl: String, videoName: String = "Video"): List<Video> {
val itemHeaders = headers.newBuilder()
.add("Accept", "*/*")
.add("Accept-Language", "en-US,en;q=0.5")
.add("Connection", "keep-alive")
.add("Cookie", getCookie(itemUrl))
.add("Host", "drive.google.com")
.build()
val itemResponse = client.newCall(
GET(itemUrl, headers = itemHeaders),
).execute()
val noRedirectClient = OkHttpClient().newBuilder().followRedirects(false).build()
val document = itemResponse.asJsoup()
val itemSize = document.selectFirst("span.uc-name-size")?.let {
" ${it.ownText().trim()} "
} ?: ""
val url = document.selectFirst("form#download-form")?.attr("action") ?: return emptyList()
val redirectHeaders = headers.newBuilder()
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
.add("Connection", "keep-alive")
.add("Content-Length", "0")
.add("Content-Type", "application/x-www-form-urlencoded")
.add("Cookie", getCookie(url))
.add("Host", "drive.google.com")
.add("Origin", "https://drive.google.com")
.add("Referer", url.substringBeforeLast("&at="))
.build()
val response = noRedirectClient.newCall(
POST(url, headers = redirectHeaders, body = "".toRequestBody("application/x-www-form-urlencoded".toMediaType())),
).execute()
val redirected = response.headers["location"] ?: return listOf(Video(url, videoName + itemSize, url))
val redirectedHeaders = headers.newBuilder()
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
.add("Connection", "keep-alive")
.add("Host", redirected.toHttpUrl().host)
.add("Referer", "https://drive.google.com/")
.build()
val redirectedResponseHeaders = noRedirectClient.newCall(
GET(redirected, headers = redirectedHeaders),
).execute().headers
val authCookie = redirectedResponseHeaders.firstOrNull {
it.first == "set-cookie" && it.second.startsWith("AUTH_")
}?.second?.substringBefore(";") ?: return listOf(Video(url, videoName + itemSize, url))
val newRedirected = redirectedResponseHeaders["location"] ?: return listOf(Video(redirected, videoName + itemSize, redirected))
val googleDriveRedirectHeaders = headers.newBuilder()
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
.add("Connection", "keep-alive")
.add("Cookie", getCookie(newRedirected))
.add("Host", "drive.google.com")
.add("Referer", "https://drive.google.com/")
.build()
val googleDriveRedirectUrl = noRedirectClient.newCall(
GET(newRedirected, headers = googleDriveRedirectHeaders),
).execute().headers["location"]!!
val videoHeaders = headers.newBuilder()
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
.add("Connection", "keep-alive")
.add("Cookie", authCookie)
.add("Host", googleDriveRedirectUrl.toHttpUrl().host)
.add("Referer", "https://drive.google.com/")
.build()
return listOf(
Video(googleDriveRedirectUrl, videoName + itemSize, googleDriveRedirectUrl, headers = videoHeaders),
)
}
private fun getCookie(url: String): String {
val cookieList = client.cookieJar.loadForRequest(url.toHttpUrl())
return if (cookieList.isNotEmpty()) {
cookieList.joinToString("; ") { "${it.name}=${it.value}" }
} else {
""
}
}
}

View File

@ -0,0 +1,32 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
class MVidooExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val body = client.newCall(GET(url)).execute().body.string()
val url = Regex("""\{var\s?.*?\s?=\s?(\[.*?\])""").find(body)?.groupValues?.get(1)?.let {
Json.decodeFromString<List<String>>(it.replace("\\x", ""))
.joinToString("") { t -> t.decodeHex() }.reversed()
.substringAfter("src=\"").substringBefore("\"")
} ?: return emptyList()
return listOf(
Video(url, "${prefix}MVidoo", url),
)
}
// Stolen from BestDubbedAnime
private fun String.decodeHex(): String {
require(length % 2 == 0) { "Must have an even length" }
return chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
.toString(Charsets.UTF_8)
}
}

View File

@ -0,0 +1,67 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
class MailRuExtractor(private val client: OkHttpClient, private val headers: Headers) {
private val json = Json {
ignoreUnknownKeys = true
}
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val document = client.newCall(GET(url)).execute().asJsoup()
val metaUrl = document.selectFirst("script:containsData(metadataUrl)")?.let {
it.data().substringAfter("metadataUrl\":\"").substringBefore("\"").replace("^//".toRegex(), "https://")
} ?: return emptyList()
val metaHeaders = headers.newBuilder()
.add("Accept", "application/json, text/javascript, */*; q=0.01")
.add("Host", url.toHttpUrl().host)
.add("Referer", url)
.build()
val metaResponse = client.newCall(GET(metaUrl, headers = metaHeaders)).execute()
val metaJson = json.decodeFromString<MetaResponse>(
metaResponse.body.string(),
)
val videoKey = metaResponse.headers.firstOrNull {
it.first.equals("set-cookie", true) && it.second.startsWith("video_key", true)
}?.second?.substringBefore(";") ?: ""
return metaJson.videos.map {
val videoUrl = it.url
.replace("^//".toRegex(), "https://")
.replace(".mp4", ".mp4/stream.mpd")
val videoHeaders = headers.newBuilder()
.add("Accept", "*/*")
.add("Cookie", videoKey)
.add("Host", videoUrl.toHttpUrl().host)
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
Video(videoUrl, "${prefix}Mail.ru ${it.key}", videoUrl, headers = videoHeaders)
}
}
@Serializable
data class MetaResponse(
val videos: List<VideoObject>,
) {
@Serializable
data class VideoObject(
val url: String,
val key: String,
)
}
}

View File

@ -0,0 +1,21 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers
import okhttp3.OkHttpClient
class Mp4uploadExtractor(private val client: OkHttpClient) {
fun getVideoFromUrl(url: String, headers: Headers, prefix: String = ""): List<Video> {
val body = client.newCall(GET(url, headers = headers)).execute().body.string()
val packed = body.substringAfter("<script type='text/javascript'>eval(function(p,a,c,k,e,d)")
.substringBefore("</script>")
val unpacked = JsUnpacker.unpackAndCombine("eval(function(p,a,c,k,e,d)" + packed) ?: return emptyList()
val videoUrl = unpacked.substringAfter("player.src(\"").substringBefore("\");")
return listOf(
Video(videoUrl, "${prefix}Mp4upload", videoUrl, headers = Headers.headersOf("Referer", "https://www.mp4upload.com/")),
)
}
}

View File

@ -0,0 +1,26 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
class MytvExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val document = client.newCall(GET(url)).execute().asJsoup()
val videoList = mutableListOf<Video>()
document.select("script").forEach { script ->
if (script.data().contains("CreatePlayer(\"v")) {
val videosString = script.data().toString()
val videoUrl = videosString.substringAfter("\"v=").substringBefore("\\u0026tp=video").replace("%26", "&").replace("%3a", ":").replace("%2f", "/").replace("%3f", "?").replace("%3d", "=")
if (!videoUrl.contains("https:")) {
val videoUrl = "https:$videoUrl"
videoList.add(Video(videoUrl, "${prefix}Mytv Stream", videoUrl))
} else {
videoList.add(Video(videoUrl, "${prefix}Mytv", videoUrl))
}
}
}
return videoList
}
}

View File

@ -0,0 +1,45 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
class SendvidExtractor(private val client: OkHttpClient, private val headers: Headers) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val videoList = mutableListOf<Video>()
val document = client.newCall(GET(url)).execute().asJsoup()
val masterUrl = document.selectFirst("source#video_source")?.attr("src") ?: return emptyList()
val masterHeaders = headers.newBuilder()
.add("Accept", "*/*")
.add("Host", masterUrl.toHttpUrl().host)
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
val masterPlaylist = client.newCall(
GET(masterUrl, headers = masterHeaders),
).execute().body.string()
val masterBase = "https://${masterUrl.toHttpUrl().host}${masterUrl.toHttpUrl().encodedPath}"
.substringBeforeLast("/") + "/"
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:")
.forEach {
val quality = "Sendvid:" + it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p "
val videoUrl = masterBase + it.substringAfter("\n").substringBefore("\n")
val videoHeaders = headers.newBuilder()
.add("Accept", "*/*")
.add("Host", videoUrl.toHttpUrl().host)
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
videoList.add(Video(videoUrl, prefix + quality, videoUrl, headers = videoHeaders))
}
return videoList
}
}

View File

@ -0,0 +1,38 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
class SibnetExtractor(private val client: OkHttpClient) {
fun getVideosFromUrl(url: String, prefix: String = ""): List<Video> {
val videoList = mutableListOf<Video>()
val document = client.newCall(
GET(url),
).execute().asJsoup()
val script = document.selectFirst("script:containsData(player.src)")?.data() ?: return emptyList()
val slug = script.substringAfter("player.src").substringAfter("src:")
.substringAfter("\"").substringBefore("\"")
val videoUrl = if (slug.contains("http")) {
slug
} else {
"https://${url.toHttpUrl().host}$slug"
}
val videoHeaders = Headers.headersOf(
"Referer",
url,
)
videoList.add(
Video(videoUrl, "${prefix}Sibnet", videoUrl, headers = videoHeaders),
)
return videoList
}
}

View File

@ -0,0 +1,46 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
class StreamVidExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, headers: Headers, prefix: String = ""): List<Video> {
val videoList = mutableListOf<Video>()
val packed = client.newCall(GET(url)).execute()
.asJsoup().selectFirst("script:containsData(m3u8)")?.data() ?: return emptyList()
val unpacked = JsUnpacker.unpackAndCombine(packed) ?: return emptyList()
val masterUrl = Regex("""src: ?"(.*?)"""").find(unpacked)?.groupValues?.get(1) ?: return emptyList()
val masterHeaders = headers.newBuilder()
.add("Accept", "*/*")
.add("Host", masterUrl.toHttpUrl().host)
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
val masterPlaylist = client.newCall(
GET(masterUrl, headers = masterHeaders),
).execute().body.string()
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:")
.forEach {
val quality = "StreamVid:" + it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p "
val videoUrl = it.substringAfter("\n").substringBefore("\n")
val videoHeaders = headers.newBuilder()
.add("Accept", "*/*")
.add("Host", videoUrl.toHttpUrl().host)
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
videoList.add(Video(videoUrl, prefix + quality, videoUrl, headers = videoHeaders))
}
return videoList
}
}

View File

@ -0,0 +1,26 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
class UqloadExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, headers: Headers, quality: String = "Uqload"): List<Video> {
val document = client.newCall(GET(url)).execute().asJsoup()
val check = document.selectFirst("script:containsData(sources)")!!.data()
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
val videoHeaders = headers.newBuilder()
.add("Accept", "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5")
.add("Host", videoUrl.toHttpUrl().host)
.add("Referer", "https://uqload.co/")
.build()
return if (check.contains("sources")) {
listOf(Video(url, quality, videoUrl, headers = videoHeaders))
} else {
emptyList()
}
}
}

View File

@ -0,0 +1,50 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
class VTubeExtractor(private val client: OkHttpClient, private val headers: Headers) {
fun videosFromUrl(url: String, baseUrl: String, prefix: String = ""): List<Video> {
val documentHeaders = headers.newBuilder()
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
.add("Host", url.toHttpUrl().host)
.add("Referer", "$baseUrl/")
.build()
val document = client.newCall(
GET(url, headers = documentHeaders),
).execute().asJsoup()
val masterUrl = document.selectFirst("script:containsData(sources)")?.let {
it.data().substringAfter("{file:\"").substringBefore("\"")
} ?: return emptyList()
val masterHeaders = headers.newBuilder()
.add("Accept", "*/*")
.add("Host", masterUrl.toHttpUrl().host)
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
val masterPlaylist = client.newCall(
GET(masterUrl, headers = masterHeaders),
).execute().body.string()
val videoList = mutableListOf<Video>()
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:")
.forEach {
val quality = "VTube:" + it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p "
val videoUrl = it.substringAfter("\n").substringBefore("\n")
val videoHeaders = headers.newBuilder()
.add("Accept", "*/*")
.add("Host", videoUrl.toHttpUrl().host)
.add("Origin", "https://${url.toHttpUrl().host}")
.add("Referer", "https://${url.toHttpUrl().host}/")
.build()
videoList.add(Video(videoUrl, prefix + quality, videoUrl, headers = videoHeaders))
}
return videoList
}
}

View File

@ -0,0 +1,41 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
class VkExtractor(private val client: OkHttpClient) {
fun getVideosFromUrl(url: String, prefix: String = ""): List<Video> {
val videoList = mutableListOf<Video>()
val documentHeaders = Headers.Builder()
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
.add("Host", "vk.com")
.add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36")
.build()
val data = client.newCall(
GET(url, headers = documentHeaders),
).execute().body.string()
val videoRegex = """\"url(\d+)\":\"(.*?)\"""".toRegex()
videoRegex.findAll(data).forEach {
val quality = it.groupValues[1]
val videoUrl = it.groupValues[2].replace("\\/", "/")
val videoHeaders = Headers.Builder()
.add("Accept", "*/*")
.add("Host", videoUrl.toHttpUrl().host)
.add("Origin", "https://vk.com")
.add("Referer", "https://vk.com/")
.add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36")
.build()
videoList.add(
Video(videoUrl, "${prefix}vk.com - ${quality}p", videoUrl, headers = videoHeaders),
)
}
return videoList
}
}

View File

@ -0,0 +1,21 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.OkHttpClient
class VudeoExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val document = client.newCall(GET(url)).execute().asJsoup()
val videoList = mutableListOf<Video>()
document.select("script:containsData(sources: [)").forEach { script ->
val videoUrl = script.data().substringAfter("sources: [").substringBefore("]").replace("\"", "").split(",")
videoUrl.forEach {
videoList.add(Video(it, "${prefix}Vudeo", it, headers = Headers.headersOf("referer", "https://vudeo.net/")))
}
}
return videoList
}
}

View File

@ -0,0 +1,19 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
class WolfstreamExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val url = client.newCall(
GET(url),
).execute().asJsoup().selectFirst("script:containsData(sources)")?.let {
it.data().substringAfter("{file:\"").substringBefore("\"")
} ?: return emptyList()
return listOf(
Video(url, "${prefix}WolfStream", url),
)
}
}