Initial commit

This commit is contained in:
almightyhak 2024-06-20 11:54:12 +07:00
commit 98ed7e8839
2263 changed files with 108711 additions and 0 deletions

View file

@ -0,0 +1,14 @@
ext {
extName = 'Anime-Loads'
extClass = '.AnimeLoads'
extVersionCode = 15
isNsfw = true
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:voe-extractor'))
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:dood-extractor'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,813 @@
package eu.kanade.tachiyomi.animeextension.de.animeloads
import android.app.Application
import android.content.SharedPreferences
import android.util.Log
import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.select.Elements
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.net.URLDecoder
import kotlin.Exception
class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Anime-Loads"
override val baseUrl = "https://www.anime-loads.org"
override val lang = "de"
override val supportsLatest = false
override val id: Long = 655155856096L
override val client = network.client.newBuilder()
.addInterceptor(DdosGuardInterceptor(network.client))
.build()
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun popularAnimeSelector(): String = "div.row div.col-sm-6 div.panel-body"
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/anime-series/page/$page")
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("div.row a.cover-img").attr("href"))
anime.thumbnail_url = element.select("div.row a.cover-img img").attr("src")
anime.title = element.select("div.row h4.title-list a").text()
return anime
}
override fun popularAnimeNextPageSelector(): String = "i.glyphicon-forward"
// episodes
override fun episodeListSelector() = throw UnsupportedOperationException()
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val episodeList = mutableListOf<SEpisode>()
val episode = SEpisode.create()
val series = document.select("a[title=\"Anime Serien\"]")
if (series.attr("title").contains("Anime Serien")) {
val eplist = document.select("#streams_episodes_1 div.list-group")
val url = document.select("meta[property=\"og:url\"]").attr("content")
val ep = parseEpisodesFromSeries(eplist, url)
episodeList.addAll(ep)
} else {
episode.name = document.select("div.page-header > h1").attr("title")
episode.episode_number = 1F
episode.setUrlWithoutDomain(document.select("meta[property=\"og:url\"]").attr("content"))
episodeList.add(episode)
}
return episodeList.reversed()
}
private fun parseEpisodesFromSeries(element: Elements, url: String): List<SEpisode> {
val episodeElement = element.select("a.list-group-item")
return episodeElement.map { episodeFromElement(it, url) }
}
override fun episodeFromElement(element: Element): SEpisode = throw UnsupportedOperationException()
private fun episodeFromElement(element: Element, url: String): SEpisode {
val episode = SEpisode.create()
val id = element.attr("aria-controls")
episode.setUrlWithoutDomain("$url#$id")
episode.name = "Ep." + element.select("span:nth-child(1)").text()
episode.episode_number = element.select("span strong").text().toFloat()
return episode
}
// Video Extractor
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val url = response.request.url.toString()
val idep = url
.substringAfter("#")
return videosFromElement(document, idep, url)
}
private fun videosFromElement(document: Document, idep: String, url: String): List<Video> {
val videoList = mutableListOf<Video>()
val hosterSelection = preferences.getStringSet("hoster_selection", setOf("dood", "voe", "stape"))
val subSelection = preferences.getStringSet("sub_selection", setOf("sub", "dub"))
val lang = document.select("div#streams ul.nav li[role=\"presentation\"]")
lang.forEach { langit ->
Log.i("videosFromElement", "Langit: $langit")
when {
langit.select("a i.flag-de").attr("title").contains("Subtitles: German") || langit.select("a i.flag-de").attr("title").contains("Untertitel: Deutsch") && subSelection?.contains("sub") == true -> {
val aria = langit.select("a").attr("aria-controls")
val id = document.select("#$aria div.episodes").attr("id")
val epnum = idep.substringAfter("streams_episodes_1")
val element = document.select("div#$id$epnum")
val enc = element.attr("data-enc")
val capfiles = client.newCall(
POST(
"$baseUrl/files/captcha",
body = "cID=0&rT=1".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"application/json, text/javascript, */*; q=0.01",
"cache-control",
"max-age=15",
),
),
).execute().asJsoup()
val hashes = capfiles.toString().substringAfter("[").substringBefore("]").split(",")
val hashlist = mutableListOf<String>()
val pnglist = mutableListOf<String>()
var max = "1"
var min = "99999"
hashes.forEach {
val hash = it.replace("<body>", "")
.replace("[", "")
.replace("\"", "").replace("]", "")
.replace("</body>", "").replace("%20", "")
val png = client.newCall(
GET(
"$baseUrl/files/captcha?cid=0&hash=$hash",
headers = Headers.headersOf(
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"image/avif,image/webp,*/*",
"cache-control",
"max-age=15",
),
),
).execute().body.byteString()
val size = png.toString()
.substringAfter("[size=").substringBefore(" hex")
pnglist.add("$size | $hash")
hashlist.add(size)
for (num in hashlist) {
if (max < num) {
max = num
}
}
for (num in hashlist) {
if (min > num) {
min = num
}
}
}
var int = 0
pnglist.forEach { diffit ->
if (int == 0) {
if (diffit.substringBefore(" |").toInt() != max.toInt() && diffit.substringBefore(" |").toInt() != min.toInt()) {
int = 1
val hash = diffit.substringBefore(" |").toInt()
val diffmax = max.toInt() - hash
val diffmin = hash - min.toInt()
if (diffmax > diffmin) {
pnglist.forEach { itmax ->
if (max.toInt() == itmax.substringBefore(" |").toInt()) {
val maxhash = itmax.substringAfter("| ")
network.client.newCall(
POST(
"$baseUrl/files/captcha",
body = "cID=0&pC=$maxhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*", "cache-control", "max-age=15",
),
),
).execute()
val maxdoc = client.newCall(
POST(
"$baseUrl/ajax/captcha",
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$maxhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
"Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15",
),
),
).execute().asJsoup().toString()
if (maxdoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
} else {
val links = maxdoc.substringAfter("\"content\":").substringBefore("</body>").split("{\"links\":")
links.forEach {
if (it.contains("link")) {
val hoster = it.substringAfter("\"hoster\":\"").substringBefore("\",\"")
val linkpart = it.substringAfter("\"link\":\"").substringBefore("\"}]")
val leaveurl = client.newCall(GET("$baseUrl/leave/$linkpart")).execute().request.url.toString()
val decode = "https://www." + URLDecoder.decode(leaveurl.substringAfter("www."), "utf-8")
if (decode.contains(baseUrl)) {
val link = client.newCall(GET(decode)).execute().request.url.toString()
when {
hoster.contains("voesx") && hosterSelection?.contains("voe") == true -> {
videoList.addAll(VoeExtractor(client).videosFromUrl(link, "(Deutsch Sub) "))
}
hoster.contains("streamtapecom") && hosterSelection?.contains("stape") == true -> {
val quality = "Streamtape Deutsch Sub"
val video = try {
StreamTapeExtractor(client).videoFromUrl(link, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
hoster.contains("doodstream") && hosterSelection?.contains("dood") == true -> {
val quality = "Doodstreams Deutsch Sub"
val video = try {
DoodExtractor(client).videoFromUrl(link, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
}
} else {
when {
hoster.contains("voesx") && hosterSelection?.contains("voe") == true -> {
videoList.addAll(VoeExtractor(client).videosFromUrl(leaveurl, "(Deutsch Sub) "))
}
hoster.contains("streamtapecom") && hosterSelection?.contains("stape") == true -> {
val quality = "Streamtape Deutsch Sub"
val video = try {
StreamTapeExtractor(client).videoFromUrl(leaveurl, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
hoster.contains("doodstream") && hosterSelection?.contains("dood") == true -> {
val quality = "Doodstreams Deutsch Sub"
val video = try {
DoodExtractor(client).videoFromUrl(leaveurl, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
}
}
}
}
}
}
}
} else {
pnglist.forEach { itmin ->
if (min.toInt() == itmin.substringBefore(" |").toInt()) {
val minhash = itmin.substringAfter("| ")
network.client.newCall(
POST(
"$baseUrl/files/captcha",
body = "cID=0&pC=$minhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin",
baseUrl,
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"*/*",
),
),
).execute()
val mindoc = client.newCall(
POST(
"$baseUrl/ajax/captcha",
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$minhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin",
baseUrl,
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"application/json, text/javascript, */*; q=0.01",
),
),
).execute().asJsoup().toString()
if (mindoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
} else {
val links = mindoc.substringAfter("\"content\":[").substringBefore("</body>").split("{\"links\":")
links.forEach {
if (it.contains("link")) {
val hoster = it.substringAfter("\"hoster\":\"").substringBefore("\",\"")
val linkpart = it.substringAfter("\"link\":\"").substringBefore("\"}]")
val leaveurl = client.newCall(GET("$baseUrl/leave/$linkpart")).execute().request.url.toString()
val decode = "https://www." + URLDecoder.decode(leaveurl.substringAfter("www."), "utf-8")
if (decode.contains(baseUrl)) {
val link = client.newCall(GET(decode)).execute().request.url.toString()
when {
hoster.contains("voesx") && hosterSelection?.contains("voe") == true -> {
videoList.addAll(VoeExtractor(client).videosFromUrl(link, "(Deutsch Sub) "))
}
hoster.contains("streamtapecom") && hosterSelection?.contains("stape") == true -> {
val quality = "Streamtape Deutsch Sub"
val video = try {
StreamTapeExtractor(client).videoFromUrl(link, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
hoster.contains("doodstream") && hosterSelection?.contains("dood") == true -> {
val quality = "Doodstreams Deutsch Sub"
val video = try {
DoodExtractor(client).videoFromUrl(link, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
}
} else {
when {
hoster.contains("voesx") && hosterSelection?.contains("voe") == true -> {
videoList.addAll(VoeExtractor(client).videosFromUrl(leaveurl, "(Deutsch Sub) "))
}
hoster.contains("streamtapecom") && hosterSelection?.contains("stape") == true -> {
val quality = "Streamtape Deutsch Sub"
val video = try {
StreamTapeExtractor(client).videoFromUrl(leaveurl, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
hoster.contains("doodstream") && hosterSelection?.contains("dood") == true -> {
val quality = "Doodstreams Deutsch Sub"
val video = try {
DoodExtractor(client).videoFromUrl(leaveurl, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
}
}
}
}
}
}
}
}
}
}
}
}
langit.select("a i.flag-de").attr("title").contains("Language: German") || langit.select("a i.flag-de").attr("title").contains("Sprache: Deutsch") && subSelection?.contains("dub") == true -> {
val aria = langit.select("a").attr("aria-controls")
val id = document.select("#$aria div.episodes").attr("id")
val epnum = idep.substringAfter("streams_episodes_1")
val element = document.select("div#$id$epnum")
val enc = element.attr("data-enc")
val capfiles = client.newCall(
POST(
"$baseUrl/files/captcha",
body = "cID=0&rT=1".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"application/json, text/javascript, */*; q=0.01",
"cache-control",
"max-age=15",
),
),
).execute().asJsoup()
val hashes = capfiles.toString().substringAfter("[").substringBefore("]").split(",")
val hashlist = mutableListOf<String>()
val pnglist = mutableListOf<String>()
var max = "1"
var min = "99999"
hashes.forEach {
val hash = it.replace("<body>", "")
.replace("[", "")
.replace("\"", "").replace("]", "")
.replace("</body>", "").replace("%20", "")
val png = client.newCall(
GET(
"$baseUrl/files/captcha?cid=0&hash=$hash",
headers = Headers.headersOf(
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"image/avif,image/webp,*/*",
"cache-control",
"max-age=15",
),
),
).execute().body.byteString()
val size = png.toString()
.substringAfter("[size=").substringBefore(" hex")
pnglist.add("$size | $hash")
hashlist.add(size)
for (num in hashlist) {
if (max < num) {
max = num
}
}
for (num in hashlist) {
if (min > num) {
min = num
}
}
}
var int = 0
pnglist.forEach { diffit ->
if (int == 0) {
if (diffit.substringBefore(" |").toInt() != max.toInt() && diffit.substringBefore(" |").toInt() != min.toInt()) {
int = 1
val hash = diffit.substringBefore(" |").toInt()
val diffmax = max.toInt() - hash
val diffmin = hash - min.toInt()
if (diffmax > diffmin) {
pnglist.forEach { itmax ->
if (max.toInt() == itmax.substringBefore(" |").toInt()) {
val maxhash = itmax.substringAfter("| ")
network.client.newCall(
POST(
"$baseUrl/files/captcha",
body = "cID=0&pC=$maxhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*", "cache-control", "max-age=15",
),
),
).execute()
val maxdoc = client.newCall(
POST(
"$baseUrl/ajax/captcha",
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$maxhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
"Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15",
),
),
).execute().asJsoup().toString()
if (maxdoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
} else {
val links = maxdoc.substringAfter("\"content\":").substringBefore("</body>").split("{\"links\":")
links.forEach {
if (it.contains("link")) {
val hoster = it.substringAfter("\"hoster\":\"").substringBefore("\",\"")
val linkpart = it.substringAfter("\"link\":\"").substringBefore("\"}]")
val leaveurl = client.newCall(GET("$baseUrl/leave/$linkpart")).execute().request.url.toString()
val decode = "https://www." + URLDecoder.decode(leaveurl.substringAfter("www."), "utf-8")
if (decode.contains(baseUrl)) {
val link = client.newCall(GET(decode)).execute().request.url.toString()
when {
hoster.contains("voesx") && hosterSelection?.contains("voe") == true -> {
videoList.addAll(VoeExtractor(client).videosFromUrl(link, "(Deutsch Dub) "))
}
hoster.contains("streamtapecom") && hosterSelection?.contains("stape") == true -> {
val quality = "Streamtape Deutsch Dub"
val video = try {
StreamTapeExtractor(client).videoFromUrl(link, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
hoster.contains("doodstream") && hosterSelection?.contains("dood") == true -> {
val quality = "Doodstream Deutsch Dub"
val video = try {
DoodExtractor(client).videoFromUrl(link, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
}
} else {
when {
hoster.contains("voesx") && hosterSelection?.contains("voe") == true -> {
videoList.addAll(VoeExtractor(client).videosFromUrl(leaveurl, "(Deutsch Dub) "))
}
hoster.contains("streamtapecom") && hosterSelection?.contains("stape") == true -> {
val quality = "Streamtape Deutsch Dub"
val video = try {
StreamTapeExtractor(client).videoFromUrl(leaveurl, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
hoster.contains("doodstream") && hosterSelection?.contains("dood") == true -> {
val quality = "Doodstream Deutsch Dub"
val video = try {
DoodExtractor(client).videoFromUrl(leaveurl, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
}
}
}
}
}
}
}
} else {
pnglist.forEach { itmin ->
if (min.toInt() == itmin.substringBefore(" |").toInt()) {
val minhash = itmin.substringAfter("| ")
network.client.newCall(
POST(
"$baseUrl/files/captcha",
body = "cID=0&pC=$minhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin",
baseUrl,
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"*/*",
),
),
).execute()
val mindoc = client.newCall(
POST(
"$baseUrl/ajax/captcha",
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$minhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = Headers.headersOf(
"Origin",
baseUrl,
"X-Requested-With",
"XMLHttpRequest",
"Referer",
url.replace("#$id$epnum", ""),
"Accept",
"application/json, text/javascript, */*; q=0.01",
),
),
).execute().asJsoup().toString()
if (mindoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
} else {
val links = mindoc.substringAfter("\"content\":[").substringBefore("</body>").split("{\"links\":")
links.forEach {
if (it.contains("link")) {
val hoster = it.substringAfter("\"hoster\":\"").substringBefore("\",\"")
val linkpart = it.substringAfter("\"link\":\"").substringBefore("\"}]")
val leaveurl = client.newCall(GET("$baseUrl/leave/$linkpart")).execute().request.url.toString()
val decode = "https://www." + URLDecoder.decode(leaveurl.substringAfter("www."), "utf-8")
if (decode.contains(baseUrl)) {
val link = client.newCall(GET(decode)).execute().request.url.toString()
when {
hoster.contains("voesx") && hosterSelection?.contains("voe") == true -> {
videoList.addAll(VoeExtractor(client).videosFromUrl(link, "(Deutsch Dub) "))
}
hoster.contains("streamtapecom") && hosterSelection?.contains("stape") == true -> {
val quality = "Streamtape Deutsch Dub"
val video = try {
StreamTapeExtractor(client).videoFromUrl(link, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
hoster.contains("doodstream") && hosterSelection?.contains("dood") == true -> {
val quality = "Doodstream Deutsch Dub"
val video = try {
DoodExtractor(client).videoFromUrl(link, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
}
} else {
when {
hoster.contains("voesx") && hosterSelection?.contains("voe") == true -> {
videoList.addAll(VoeExtractor(client).videosFromUrl(leaveurl, "(Deutsch Dub) "))
}
hoster.contains("streamtapecom") && hosterSelection?.contains("stape") == true -> {
val quality = "Streamtape Deutsch Dub"
val video = try {
StreamTapeExtractor(client).videoFromUrl(leaveurl, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
hoster.contains("doodstream") && hosterSelection?.contains("dood") == true -> {
val quality = "Doodstream Deutsch Dub"
val video = try {
DoodExtractor(client).videoFromUrl(leaveurl, quality)
} catch (e: Exception) {
null
}
if (video != null) {
videoList.add(video)
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
return videoList.reversed()
}
override fun List<Video>.sort(): List<Video> {
val hoster = preferences.getString("preferred_hoster", null)
if (hoster != null) {
val newList = mutableListOf<Video>()
var preferred = 0
for (video in this) {
if (video.quality.contains(hoster)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
return this
}
override fun videoListSelector() = throw UnsupportedOperationException()
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
// Search
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("div.row a.cover-img").attr("href"))
anime.thumbnail_url = element.select("div.row a.cover-img img").attr("src")
anime.title = element.select("div.row h4.title-list a").text()
return anime
}
override fun searchAnimeNextPageSelector(): String = "i.glyphicon-forward"
override fun searchAnimeSelector(): String = "div.row div.col-sm-6 div.panel-body"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = GET("$baseUrl/search/page/$page?q=$query")
// Details
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = document.select("#description img.img-responsive").attr("src")
anime.title = document.select("div.page-header > h1").attr("title")
anime.genre = document.select("#description div.label-group a.label.label-info").joinToString(", ") { it.text() }
anime.description = document.select("div.pt20").not("strong").text()
anime.author = document.select("div.col-md-6.text-left p:nth-child(3) a").joinToString(", ") { it.text() }
anime.status = SAnime.COMPLETED
return anime
}
// Latest
override fun latestUpdatesNextPageSelector(): String = throw UnsupportedOperationException()
override fun latestUpdatesFromElement(element: Element): SAnime = throw UnsupportedOperationException()
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
override fun latestUpdatesSelector(): String = throw UnsupportedOperationException()
// Preferences
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val hosterPref = ListPreference(screen.context).apply {
key = "preferred_hoster"
title = "Standard-Hoster"
entries = arrayOf("Doodstream", "Voe", "MIXdrop")
entryValues = arrayOf("https://dood", "https://voe.sx", "https://streamtape.com")
setDefaultValue("https://voe.sx")
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
val hostSelection = MultiSelectListPreference(screen.context).apply {
key = "hoster_selection"
title = "Hoster auswählen"
entries = arrayOf("Doodstream", "Voe", "Streamtape")
entryValues = arrayOf("dood", "voe", "stape")
setDefaultValue(setOf("dood", "voe", "stape"))
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}
val subSelection = MultiSelectListPreference(screen.context).apply {
key = "sub_selection"
title = "Sprache auswählen"
entries = arrayOf("SUB", "DUB")
entryValues = arrayOf("sub", "dub")
setDefaultValue(setOf("sub"))
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}
screen.addPreference(hosterPref)
screen.addPreference(hostSelection)
screen.addPreference(subSelection)
}
}

View file

@ -0,0 +1,74 @@
package eu.kanade.tachiyomi.animeextension.de.animeloads
import android.util.Log
import android.webkit.CookieManager
import eu.kanade.tachiyomi.network.GET
import okhttp3.Cookie
import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
class DdosGuardInterceptor(private val client: OkHttpClient) : Interceptor {
private val cookieManager by lazy { CookieManager.getInstance() }
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val response = chain.proceed(originalRequest)
// Check if DDos-GUARD is on
if (response.code !in ERROR_CODES || response.header("Server") !in SERVER_CHECK) {
return response
}
response.close()
val cookies = cookieManager.getCookie(originalRequest.url.toString())
val oldCookie = if (cookies != null && cookies.isNotEmpty()) {
cookies.split(";").mapNotNull { Cookie.parse(originalRequest.url, it) }
} else {
emptyList()
}
Log.i("newCookie", "OldCookies: $oldCookie")
val ddg2Cookie = oldCookie.firstOrNull { it.name == "__ddg2_" }
if (!ddg2Cookie?.value.isNullOrEmpty()) {
return chain.proceed(originalRequest)
}
val newCookie = getNewCookie(originalRequest.url) ?: return chain.proceed(originalRequest)
val newCookieHeader = buildString {
(oldCookie + newCookie).forEachIndexed { index, cookie ->
if (index > 0) append("; ")
append(cookie.name).append('=').append(cookie.value)
}
}
return chain.proceed(originalRequest.newBuilder().addHeader("cookie", newCookieHeader).build())
}
fun getNewCookie(url: HttpUrl): Cookie? {
val cookies = cookieManager.getCookie(url.toString())
val oldCookie = if (cookies != null && cookies.isNotEmpty()) {
cookies.split(";").mapNotNull { Cookie.parse(url, it) }
} else {
emptyList()
}
val ddg2Cookie = oldCookie.firstOrNull { it.name == "__ddg2_" }
if (!ddg2Cookie?.value.isNullOrEmpty()) {
return ddg2Cookie
}
val wellKnown = client.newCall(GET("https://check.ddos-guard.net/check.js"))
.execute().body.string()
.substringAfter("'", "")
.substringBefore("'", "")
val checkUrl = "${url.scheme}://${url.host + wellKnown}"
return client.newCall(GET(checkUrl)).execute().header("set-cookie")?.let {
Cookie.parse(url, it)
}
}
companion object {
private val ERROR_CODES = listOf(403)
private val SERVER_CHECK = listOf("ddos-guard")
}
}