Turkanime: Add some sources (#1554)
This commit is contained in:
@ -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'
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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 {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
@ -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/")),
|
||||
)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user