fix: Update dead URLs and purge dead sources (#1672)

This commit is contained in:
Claudemirovsky 2023-06-02 19:36:09 +00:00 committed by GitHub
parent b0d170ebce
commit e55e75b833
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 4 additions and 2011 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = 'موفيزلاند'
pkgNameSuffix = 'ar.movizland'
extClass = '.Movizland'
extVersionCode = 10
extVersionCode = 11
libVersion = '13'
}

View File

@ -35,7 +35,7 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "موفيزلاند"
override val baseUrl = "https://movizland.icu"
override val baseUrl = "https://eg.movizlaand.top"
override val lang = "ar"

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,19 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Anime24'
pkgNameSuffix = 'de.anime24'
extClass = '.Anime24'
extVersionCode = 9
libVersion = '13'
}
dependencies {
implementation(project(':lib-voe-extractor'))
implementation(project(':lib-streamtape-extractor'))
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,216 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.anime24
import android.app.Application
import android.content.SharedPreferences
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.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.Exception
class Anime24 : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Anime24"
override val baseUrl = "https://anime24.to"
override val lang = "de"
override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun popularAnimeSelector(): String = "div#blog-entries article"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/page/$page")
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("div.post-thumbnail a").attr("href"))
anime.thumbnail_url = element.select("div.post-thumbnail a img ").attr("data-lazy-src")
anime.title = element.select("div.blog-entry-content h2.entry-title a").text()
return anime
}
override fun popularAnimeNextPageSelector(): String = "div.nav-links a.next"
// episodes
override fun episodeListSelector() = throw Exception("not used")
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val episodeList = mutableListOf<SEpisode>()
val episodeElement = document.select("div.entry-content div.su-spoiler")
episodeElement.forEach {
val episode = episodeFromElement(it)
episodeList.add(episode)
}
return episodeList
}
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
if (element.select("div.su-spoiler-title").text().contains("Ger Sub")) {
episode.episode_number = element.select("div.su-spoiler-title").text()
.substringAfter("Episode ").substringBefore(" ").toFloat()
} else {
episode.episode_number = element.select("div.su-spoiler-title").text()
.substringAfter("Folge ").substringBefore(" ").toFloat()
}
episode.name = element.select("div.su-spoiler-title").text()
episode.url = (element.select("div.su-spoiler-content center iframe").attr("data-lazy-src"))
return episode
}
// Video Extractor
override fun videoListRequest(episode: SEpisode): Request {
val header = episode.url.replace(baseUrl, "")
return GET(baseUrl, headers = Headers.headersOf("url", header))
}
override fun videoListParse(response: Response): List<Video> {
val url = response.request.header("url").toString()
return videosFromElement(url)
}
private fun videosFromElement(url: String): List<Video> {
val videoList = mutableListOf<Video>()
val hosterSelection = preferences.getStringSet("hoster_selection", setOf("stape", "voe"))
when {
url.contains("https://streamtape") && hosterSelection?.contains("stape") == true -> {
val quality = "Streamtape"
val video = StreamTapeExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
}
url.contains("https://voe.sx") && hosterSelection?.contains("voe") == true -> {
val quality = "Voe"
val video = VoeExtractor(client).videoFromUrl(url, quality)
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 Exception("not used")
override fun videoFromElement(element: Element) = throw Exception("not used")
override fun videoUrlParse(document: Document) = throw Exception("not used")
// Search
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("div.post-thumbnail a").attr("href"))
anime.thumbnail_url = element.select("div.post-thumbnail a img ").attr("src")
anime.title = element.select("div.blog-entry-content h2.entry-title a").text()
return anime
}
override fun searchAnimeNextPageSelector(): String = "div.nav-links a.next"
override fun searchAnimeSelector(): String = "div#blog-entries article"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = GET("$baseUrl/page/$page/?s=$query")
// Details
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = document.select("div.su-column-inner img").attr("data-lazy-src")
anime.title = document.select("header.entry-header h1.entry-title").text()
anime.description = document.select("div.su-column-inner p").toString()
.substringAfter("<br>").substringBefore("</p>")
anime.status = SAnime.COMPLETED
return anime
}
// Latest
override fun latestUpdatesNextPageSelector(): String = throw Exception("Not used")
override fun latestUpdatesFromElement(element: Element): SAnime = throw Exception("Not used")
override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used")
override fun latestUpdatesSelector(): String = throw Exception("Not used")
// Preferences
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val hosterPref = ListPreference(screen.context).apply {
key = "preferred_hoster"
title = "Standard-Hoster"
entries = arrayOf("Streamtape", "Voe")
entryValues = arrayOf("https://streamtape", "https://voe.sx")
setDefaultValue("https://streamtape")
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 subSelection = MultiSelectListPreference(screen.context).apply {
key = "hoster_selection"
title = "Hoster auswählen"
entries = arrayOf("Streamtape", "Voe")
entryValues = arrayOf("stape", "voe")
setDefaultValue(setOf("stape", "voe"))
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}
screen.addPreference(hosterPref)
screen.addPreference(subSelection)
}
}

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,18 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'Moviedream'
pkgNameSuffix = 'de.moviedream'
extClass = '.Moviedream'
extVersionCode = 4
libVersion = '13'
}
dependencies {
implementation(project(':lib-voe-extractor'))
implementation(project(':lib-streamtape-extractor'))
implementation(project(':lib-dood-extractor'))
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,756 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.moviedream
import app.cash.quickjs.QuickJs
fun getLink(parameters: String): String {
val quickJs = QuickJs.create()
val link = quickJs.evaluate(decodeScript(parameters)).toString()
quickJs.close()
return link
}
private fun decodeScript(parameters: String) = """
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS = CryptoJS || function(u, p) {
var d = {},
l = d.lib = {},
s = function() {},
t = l.Base = {
extend: function(a) {
s.prototype = this;
var c = new s;
a && c.mixIn(a);
c.hasOwnProperty("init") || (c.init = function() {
c.${'$'}super.init.apply(this, arguments)
});
c.init.prototype = c;
c.${'$'}super = this;
return c
},
create: function() {
var a = this.extend();
a.init.apply(a, arguments);
return a
},
init: function() {},
mixIn: function(a) {
for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]);
a.hasOwnProperty("toString") && (this.toString = a.toString)
},
clone: function() {
return this.init.prototype.extend(this)
}
},
r = l.WordArray = t.extend({
init: function(a, c) {
a = this.words = a || [];
this.sigBytes = c != p ? c : 4 * a.length
},
toString: function(a) {
return (a || v).stringify(this)
},
concat: function(a) {
var c = this.words,
e = a.words,
j = this.sigBytes;
a = a.sigBytes;
this.clamp();
if (j % 4)
for (var k = 0; k < a; k++) c[j + k >>> 2] |= (e[k >>> 2] >>> 24 - 8 * (k % 4) & 255) << 24 - 8 * ((j + k) % 4);
else if (65535 < e.length)
for (k = 0; k < a; k += 4) c[j + k >>> 2] = e[k >>> 2];
else c.push.apply(c, e);
this.sigBytes += a;
return this
},
clamp: function() {
var a = this.words,
c = this.sigBytes;
a[c >>> 2] &= 4294967295 <<
32 - 8 * (c % 4);
a.length = u.ceil(c / 4)
},
clone: function() {
var a = t.clone.call(this);
a.words = this.words.slice(0);
return a
},
random: function(a) {
for (var c = [], e = 0; e < a; e += 4) c.push(4294967296 * u.random() | 0);
return new r.init(c, a)
}
}),
w = d.enc = {},
v = w.Hex = {
stringify: function(a) {
var c = a.words;
a = a.sigBytes;
for (var e = [], j = 0; j < a; j++) {
var k = c[j >>> 2] >>> 24 - 8 * (j % 4) & 255;
e.push((k >>> 4).toString(16));
e.push((k & 15).toString(16))
}
return e.join("")
},
parse: function(a) {
for (var c = a.length, e = [], j = 0; j < c; j += 2) e[j >>> 3] |= parseInt(a.substr(j,
2), 16) << 24 - 4 * (j % 8);
return new r.init(e, c / 2)
}
},
b = w.Latin1 = {
stringify: function(a) {
var c = a.words;
a = a.sigBytes;
for (var e = [], j = 0; j < a; j++) e.push(String.fromCharCode(c[j >>> 2] >>> 24 - 8 * (j % 4) & 255));
return e.join("")
},
parse: function(a) {
for (var c = a.length, e = [], j = 0; j < c; j++) e[j >>> 2] |= (a.charCodeAt(j) & 255) << 24 - 8 * (j % 4);
return new r.init(e, c)
}
},
x = w.Utf8 = {
stringify: function(a) {
try {
return decodeURIComponent(escape(b.stringify(a)))
} catch (c) {
throw Error("Malformed UTF-8 data");
}
},
parse: function(a) {
return b.parse(unescape(encodeURIComponent(a)))
}
},
q = l.BufferedBlockAlgorithm = t.extend({
reset: function() {
this._data = new r.init;
this._nDataBytes = 0
},
_append: function(a) {
"string" == typeof a && (a = x.parse(a));
this._data.concat(a);
this._nDataBytes += a.sigBytes
},
_process: function(a) {
var c = this._data,
e = c.words,
j = c.sigBytes,
k = this.blockSize,
b = j / (4 * k),
b = a ? u.ceil(b) : u.max((b | 0) - this._minBufferSize, 0);
a = b * k;
j = u.min(4 * a, j);
if (a) {
for (var q = 0; q < a; q += k) this._doProcessBlock(e, q);
q = e.splice(0, a);
c.sigBytes -= j
}
return new r.init(q, j)
},
clone: function() {
var a = t.clone.call(this);
a._data = this._data.clone();
return a
},
_minBufferSize: 0
});
l.Hasher = q.extend({
cfg: t.extend(),
init: function(a) {
this.cfg = this.cfg.extend(a);
this.reset()
},
reset: function() {
q.reset.call(this);
this._doReset()
},
update: function(a) {
this._append(a);
this._process();
return this
},
finalize: function(a) {
a && this._append(a);
return this._doFinalize()
},
blockSize: 16,
_createHelper: function(a) {
return function(b, e) {
return (new a.init(e)).finalize(b)
}
},
_createHmacHelper: function(a) {
return function(b, e) {
return (new n.HMAC.init(a,
e)).finalize(b)
}
}
});
var n = d.algo = {};
return d
}(Math);
(function() {
var u = CryptoJS,
p = u.lib.WordArray;
u.enc.Base64 = {
stringify: function(d) {
var l = d.words,
p = d.sigBytes,
t = this._map;
d.clamp();
d = [];
for (var r = 0; r < p; r += 3)
for (var w = (l[r >>> 2] >>> 24 - 8 * (r % 4) & 255) << 16 | (l[r + 1 >>> 2] >>> 24 - 8 * ((r + 1) % 4) & 255) << 8 | l[r + 2 >>> 2] >>> 24 - 8 * ((r + 2) % 4) & 255, v = 0; 4 > v && r + 0.75 * v < p; v++) d.push(t.charAt(w >>> 6 * (3 - v) & 63));
if (l = t.charAt(64))
for (; d.length % 4;) d.push(l);
return d.join("")
},
parse: function(d) {
var l = d.length,
s = this._map,
t = s.charAt(64);
t && (t = d.indexOf(t), -1 != t && (l = t));
for (var t = [], r = 0, w = 0; w <
l; w++)
if (w % 4) {
var v = s.indexOf(d.charAt(w - 1)) << 2 * (w % 4),
b = s.indexOf(d.charAt(w)) >>> 6 - 2 * (w % 4);
t[r >>> 2] |= (v | b) << 24 - 8 * (r % 4);
r++
} return p.create(t, r)
},
_map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
}
})();
(function(u) {
function p(b, n, a, c, e, j, k) {
b = b + (n & a | ~n & c) + e + k;
return (b << j | b >>> 32 - j) + n
}
function d(b, n, a, c, e, j, k) {
b = b + (n & c | a & ~c) + e + k;
return (b << j | b >>> 32 - j) + n
}
function l(b, n, a, c, e, j, k) {
b = b + (n ^ a ^ c) + e + k;
return (b << j | b >>> 32 - j) + n
}
function s(b, n, a, c, e, j, k) {
b = b + (a ^ (n | ~c)) + e + k;
return (b << j | b >>> 32 - j) + n
}
for (var t = CryptoJS, r = t.lib, w = r.WordArray, v = r.Hasher, r = t.algo, b = [], x = 0; 64 > x; x++) b[x] = 4294967296 * u.abs(u.sin(x + 1)) | 0;
r = r.MD5 = v.extend({
_doReset: function() {
this._hash = new w.init([1732584193, 4023233417, 2562383102, 271733878])
},
_doProcessBlock: function(q, n) {
for (var a = 0; 16 > a; a++) {
var c = n + a,
e = q[c];
q[c] = (e << 8 | e >>> 24) & 16711935 | (e << 24 | e >>> 8) & 4278255360
}
var a = this._hash.words,
c = q[n + 0],
e = q[n + 1],
j = q[n + 2],
k = q[n + 3],
z = q[n + 4],
r = q[n + 5],
t = q[n + 6],
w = q[n + 7],
v = q[n + 8],
A = q[n + 9],
B = q[n + 10],
C = q[n + 11],
u = q[n + 12],
D = q[n + 13],
E = q[n + 14],
x = q[n + 15],
f = a[0],
m = a[1],
g = a[2],
h = a[3],
f = p(f, m, g, h, c, 7, b[0]),
h = p(h, f, m, g, e, 12, b[1]),
g = p(g, h, f, m, j, 17, b[2]),
m = p(m, g, h, f, k, 22, b[3]),
f = p(f, m, g, h, z, 7, b[4]),
h = p(h, f, m, g, r, 12, b[5]),
g = p(g, h, f, m, t, 17, b[6]),
m = p(m, g, h, f, w, 22, b[7]),
f = p(f, m, g, h, v, 7, b[8]),
h = p(h, f, m, g, A, 12, b[9]),
g = p(g, h, f, m, B, 17, b[10]),
m = p(m, g, h, f, C, 22, b[11]),
f = p(f, m, g, h, u, 7, b[12]),
h = p(h, f, m, g, D, 12, b[13]),
g = p(g, h, f, m, E, 17, b[14]),
m = p(m, g, h, f, x, 22, b[15]),
f = d(f, m, g, h, e, 5, b[16]),
h = d(h, f, m, g, t, 9, b[17]),
g = d(g, h, f, m, C, 14, b[18]),
m = d(m, g, h, f, c, 20, b[19]),
f = d(f, m, g, h, r, 5, b[20]),
h = d(h, f, m, g, B, 9, b[21]),
g = d(g, h, f, m, x, 14, b[22]),
m = d(m, g, h, f, z, 20, b[23]),
f = d(f, m, g, h, A, 5, b[24]),
h = d(h, f, m, g, E, 9, b[25]),
g = d(g, h, f, m, k, 14, b[26]),
m = d(m, g, h, f, v, 20, b[27]),
f = d(f, m, g, h, D, 5, b[28]),
h = d(h, f,
m, g, j, 9, b[29]),
g = d(g, h, f, m, w, 14, b[30]),
m = d(m, g, h, f, u, 20, b[31]),
f = l(f, m, g, h, r, 4, b[32]),
h = l(h, f, m, g, v, 11, b[33]),
g = l(g, h, f, m, C, 16, b[34]),
m = l(m, g, h, f, E, 23, b[35]),
f = l(f, m, g, h, e, 4, b[36]),
h = l(h, f, m, g, z, 11, b[37]),
g = l(g, h, f, m, w, 16, b[38]),
m = l(m, g, h, f, B, 23, b[39]),
f = l(f, m, g, h, D, 4, b[40]),
h = l(h, f, m, g, c, 11, b[41]),
g = l(g, h, f, m, k, 16, b[42]),
m = l(m, g, h, f, t, 23, b[43]),
f = l(f, m, g, h, A, 4, b[44]),
h = l(h, f, m, g, u, 11, b[45]),
g = l(g, h, f, m, x, 16, b[46]),
m = l(m, g, h, f, j, 23, b[47]),
f = s(f, m, g, h, c, 6, b[48]),
h = s(h, f, m, g, w, 10, b[49]),
g = s(g, h, f, m,
E, 15, b[50]),
m = s(m, g, h, f, r, 21, b[51]),
f = s(f, m, g, h, u, 6, b[52]),
h = s(h, f, m, g, k, 10, b[53]),
g = s(g, h, f, m, B, 15, b[54]),
m = s(m, g, h, f, e, 21, b[55]),
f = s(f, m, g, h, v, 6, b[56]),
h = s(h, f, m, g, x, 10, b[57]),
g = s(g, h, f, m, t, 15, b[58]),
m = s(m, g, h, f, D, 21, b[59]),
f = s(f, m, g, h, z, 6, b[60]),
h = s(h, f, m, g, C, 10, b[61]),
g = s(g, h, f, m, j, 15, b[62]),
m = s(m, g, h, f, A, 21, b[63]);
a[0] = a[0] + f | 0;
a[1] = a[1] + m | 0;
a[2] = a[2] + g | 0;
a[3] = a[3] + h | 0
},
_doFinalize: function() {
var b = this._data,
n = b.words,
a = 8 * this._nDataBytes,
c = 8 * b.sigBytes;
n[c >>> 5] |= 128 << 24 - c % 32;
var e = u.floor(a /
4294967296);
n[(c + 64 >>> 9 << 4) + 15] = (e << 8 | e >>> 24) & 16711935 | (e << 24 | e >>> 8) & 4278255360;
n[(c + 64 >>> 9 << 4) + 14] = (a << 8 | a >>> 24) & 16711935 | (a << 24 | a >>> 8) & 4278255360;
b.sigBytes = 4 * (n.length + 1);
this._process();
b = this._hash;
n = b.words;
for (a = 0; 4 > a; a++) c = n[a], n[a] = (c << 8 | c >>> 24) & 16711935 | (c << 24 | c >>> 8) & 4278255360;
return b
},
clone: function() {
var b = v.clone.call(this);
b._hash = this._hash.clone();
return b
}
});
t.MD5 = v._createHelper(r);
t.HmacMD5 = v._createHmacHelper(r)
})(Math);
(function() {
var u = CryptoJS,
p = u.lib,
d = p.Base,
l = p.WordArray,
p = u.algo,
s = p.EvpKDF = d.extend({
cfg: d.extend({
keySize: 4,
hasher: p.MD5,
iterations: 1
}),
init: function(d) {
this.cfg = this.cfg.extend(d)
},
compute: function(d, r) {
for (var p = this.cfg, s = p.hasher.create(), b = l.create(), u = b.words, q = p.keySize, p = p.iterations; u.length < q;) {
n && s.update(n);
var n = s.update(d).finalize(r);
s.reset();
for (var a = 1; a < p; a++) n = s.finalize(n), s.reset();
b.concat(n)
}
b.sigBytes = 4 * q;
return b
}
});
u.EvpKDF = function(d, l, p) {
return s.create(p).compute(d,
l)
}
})();
CryptoJS.lib.Cipher || function(u) {
var p = CryptoJS,
d = p.lib,
l = d.Base,
s = d.WordArray,
t = d.BufferedBlockAlgorithm,
r = p.enc.Base64,
w = p.algo.EvpKDF,
v = d.Cipher = t.extend({
cfg: l.extend(),
createEncryptor: function(e, a) {
return this.create(this._ENC_XFORM_MODE, e, a)
},
createDecryptor: function(e, a) {
return this.create(this._DEC_XFORM_MODE, e, a)
},
init: function(e, a, b) {
this.cfg = this.cfg.extend(b);
this._xformMode = e;
this._key = a;
this.reset()
},
reset: function() {
t.reset.call(this);
this._doReset()
},
process: function(e) {
this._append(e);
return this._process()
},
finalize: function(e) {
e && this._append(e);
return this._doFinalize()
},
keySize: 4,
ivSize: 4,
_ENC_XFORM_MODE: 1,
_DEC_XFORM_MODE: 2,
_createHelper: function(e) {
return {
encrypt: function(b, k, d) {
return ("string" == typeof k ? c : a).encrypt(e, b, k, d)
},
decrypt: function(b, k, d) {
return ("string" == typeof k ? c : a).decrypt(e, b, k, d)
}
}
}
});
d.StreamCipher = v.extend({
_doFinalize: function() {
return this._process(!0)
},
blockSize: 1
});
var b = p.mode = {},
x = function(e, a, b) {
var c = this._iv;
c ? this._iv = u : c = this._prevBlock;
for (var d = 0; d < b; d++) e[a + d] ^=
c[d]
},
q = (d.BlockCipherMode = l.extend({
createEncryptor: function(e, a) {
return this.Encryptor.create(e, a)
},
createDecryptor: function(e, a) {
return this.Decryptor.create(e, a)
},
init: function(e, a) {
this._cipher = e;
this._iv = a
}
})).extend();
q.Encryptor = q.extend({
processBlock: function(e, a) {
var b = this._cipher,
c = b.blockSize;
x.call(this, e, a, c);
b.encryptBlock(e, a);
this._prevBlock = e.slice(a, a + c)
}
});
q.Decryptor = q.extend({
processBlock: function(e, a) {
var b = this._cipher,
c = b.blockSize,
d = e.slice(a, a + c);
b.decryptBlock(e, a);
x.call(this,
e, a, c);
this._prevBlock = d
}
});
b = b.CBC = q;
q = (p.pad = {}).Pkcs7 = {
pad: function(a, b) {
for (var c = 4 * b, c = c - a.sigBytes % c, d = c << 24 | c << 16 | c << 8 | c, l = [], n = 0; n < c; n += 4) l.push(d);
c = s.create(l, c);
a.concat(c)
},
unpad: function(a) {
a.sigBytes -= a.words[a.sigBytes - 1 >>> 2] & 255
}
};
d.BlockCipher = v.extend({
cfg: v.cfg.extend({
mode: b,
padding: q
}),
reset: function() {
v.reset.call(this);
var a = this.cfg,
b = a.iv,
a = a.mode;
if (this._xformMode == this._ENC_XFORM_MODE) var c = a.createEncryptor;
else c = a.createDecryptor, this._minBufferSize = 1;
this._mode = c.call(a,
this, b && b.words)
},
_doProcessBlock: function(a, b) {
this._mode.processBlock(a, b)
},
_doFinalize: function() {
var a = this.cfg.padding;
if (this._xformMode == this._ENC_XFORM_MODE) {
a.pad(this._data, this.blockSize);
var b = this._process(!0)
} else b = this._process(!0), a.unpad(b);
return b
},
blockSize: 4
});
var n = d.CipherParams = l.extend({
init: function(a) {
this.mixIn(a)
},
toString: function(a) {
return (a || this.formatter).stringify(this)
}
}),
b = (p.format = {}).OpenSSL = {
stringify: function(a) {
var b = a.ciphertext;
a = a.salt;
return (a ? s.create([1398893684,
1701076831
]).concat(a).concat(b) : b).toString(r)
},
parse: function(a) {
a = r.parse(a);
var b = a.words;
if (1398893684 == b[0] && 1701076831 == b[1]) {
var c = s.create(b.slice(2, 4));
b.splice(0, 4);
a.sigBytes -= 16
}
return n.create({
ciphertext: a,
salt: c
})
}
},
a = d.SerializableCipher = l.extend({
cfg: l.extend({
format: b
}),
encrypt: function(a, b, c, d) {
d = this.cfg.extend(d);
var l = a.createEncryptor(c, d);
b = l.finalize(b);
l = l.cfg;
return n.create({
ciphertext: b,
key: c,
iv: l.iv,
algorithm: a,
mode: l.mode,
padding: l.padding,
blockSize: a.blockSize,
formatter: d.format
})
},
decrypt: function(a, b, c, d) {
d = this.cfg.extend(d);
b = this._parse(b, d.format);
return a.createDecryptor(c, d).finalize(b.ciphertext)
},
_parse: function(a, b) {
return "string" == typeof a ? b.parse(a, this) : a
}
}),
p = (p.kdf = {}).OpenSSL = {
execute: function(a, b, c, d) {
d || (d = s.random(8));
a = w.create({
keySize: b + c
}).compute(a, d);
c = s.create(a.words.slice(b), 4 * c);
a.sigBytes = 4 * b;
return n.create({
key: a,
iv: c,
salt: d
})
}
},
c = d.PasswordBasedCipher = a.extend({
cfg: a.cfg.extend({
kdf: p
}),
encrypt: function(b, c, d, l) {
l = this.cfg.extend(l);
d = l.kdf.execute(d,
b.keySize, b.ivSize);
l.iv = d.iv;
b = a.encrypt.call(this, b, c, d.key, l);
b.mixIn(d);
return b
},
decrypt: function(b, c, d, l) {
l = this.cfg.extend(l);
c = this._parse(c, l.format);
d = l.kdf.execute(d, b.keySize, b.ivSize, c.salt);
l.iv = d.iv;
return a.decrypt.call(this, b, c, d.key, l)
}
})
}();
(function() {
for (var u = CryptoJS, p = u.lib.BlockCipher, d = u.algo, l = [], s = [], t = [], r = [], w = [], v = [], b = [], x = [], q = [], n = [], a = [], c = 0; 256 > c; c++) a[c] = 128 > c ? c << 1 : c << 1 ^ 283;
for (var e = 0, j = 0, c = 0; 256 > c; c++) {
var k = j ^ j << 1 ^ j << 2 ^ j << 3 ^ j << 4,
k = k >>> 8 ^ k & 255 ^ 99;
l[e] = k;
s[k] = e;
var z = a[e],
F = a[z],
G = a[F],
y = 257 * a[k] ^ 16843008 * k;
t[e] = y << 24 | y >>> 8;
r[e] = y << 16 | y >>> 16;
w[e] = y << 8 | y >>> 24;
v[e] = y;
y = 16843009 * G ^ 65537 * F ^ 257 * z ^ 16843008 * e;
b[k] = y << 24 | y >>> 8;
x[k] = y << 16 | y >>> 16;
q[k] = y << 8 | y >>> 24;
n[k] = y;
e ? (e = z ^ a[a[a[G ^ z]]], j ^= a[a[j]]) : e = j = 1
}
var H = [0, 1, 2, 4, 8,
16, 32, 64, 128, 27, 54
],
d = d.AES = p.extend({
_doReset: function() {
for (var a = this._key, c = a.words, d = a.sigBytes / 4, a = 4 * ((this._nRounds = d + 6) + 1), e = this._keySchedule = [], j = 0; j < a; j++)
if (j < d) e[j] = c[j];
else {
var k = e[j - 1];
j % d ? 6 < d && 4 == j % d && (k = l[k >>> 24] << 24 | l[k >>> 16 & 255] << 16 | l[k >>> 8 & 255] << 8 | l[k & 255]) : (k = k << 8 | k >>> 24, k = l[k >>> 24] << 24 | l[k >>> 16 & 255] << 16 | l[k >>> 8 & 255] << 8 | l[k & 255], k ^= H[j / d | 0] << 24);
e[j] = e[j - d] ^ k
} c = this._invKeySchedule = [];
for (d = 0; d < a; d++) j = a - d, k = d % 4 ? e[j] : e[j - 4], c[d] = 4 > d || 4 >= j ? k : b[l[k >>> 24]] ^ x[l[k >>> 16 & 255]] ^ q[l[k >>>
8 & 255]] ^ n[l[k & 255]]
},
encryptBlock: function(a, b) {
this._doCryptBlock(a, b, this._keySchedule, t, r, w, v, l)
},
decryptBlock: function(a, c) {
var d = a[c + 1];
a[c + 1] = a[c + 3];
a[c + 3] = d;
this._doCryptBlock(a, c, this._invKeySchedule, b, x, q, n, s);
d = a[c + 1];
a[c + 1] = a[c + 3];
a[c + 3] = d
},
_doCryptBlock: function(a, b, c, d, e, j, l, f) {
for (var m = this._nRounds, g = a[b] ^ c[0], h = a[b + 1] ^ c[1], k = a[b + 2] ^ c[2], n = a[b + 3] ^ c[3], p = 4, r = 1; r < m; r++) var q = d[g >>> 24] ^ e[h >>> 16 & 255] ^ j[k >>> 8 & 255] ^ l[n & 255] ^ c[p++],
s = d[h >>> 24] ^ e[k >>> 16 & 255] ^ j[n >>> 8 & 255] ^ l[g & 255] ^ c[p++],
t =
d[k >>> 24] ^ e[n >>> 16 & 255] ^ j[g >>> 8 & 255] ^ l[h & 255] ^ c[p++],
n = d[n >>> 24] ^ e[g >>> 16 & 255] ^ j[h >>> 8 & 255] ^ l[k & 255] ^ c[p++],
g = q,
h = s,
k = t;
q = (f[g >>> 24] << 24 | f[h >>> 16 & 255] << 16 | f[k >>> 8 & 255] << 8 | f[n & 255]) ^ c[p++];
s = (f[h >>> 24] << 24 | f[k >>> 16 & 255] << 16 | f[n >>> 8 & 255] << 8 | f[g & 255]) ^ c[p++];
t = (f[k >>> 24] << 24 | f[n >>> 16 & 255] << 16 | f[g >>> 8 & 255] << 8 | f[h & 255]) ^ c[p++];
n = (f[n >>> 24] << 24 | f[g >>> 16 & 255] << 16 | f[h >>> 8 & 255] << 8 | f[k & 255]) ^ c[p++];
a[b] = q;
a[b + 1] = s;
a[b + 2] = t;
a[b + 3] = n
},
keySize: 8
});
u.AES = p._createHelper(d)
})();
var CryptoJSAesJson = {
/**
* Encrypt any value
* @param {*} value
* @param {string} password
* @return {string}
*/
'encrypt': function (value, password) {
return CryptoJS.AES.encrypt(JSON.stringify(value), password, { format: CryptoJSAesJson }).toString()
},
/**
* Decrypt a previously encrypted value
* @param {string} jsonStr
* @param {string} password
* @return {*}
*/
'decrypt': function (jsonStr, password) {
return JSON.parse(CryptoJS.AES.decrypt(jsonStr, password, { format: CryptoJSAesJson }).toString(CryptoJS.enc.Utf8))
},
/**
* Stringify cryptojs data
* @param {Object} cipherParams
* @return {string}
*/
'stringify': function (cipherParams) {
var j = { ct: cipherParams.ciphertext.toString(CryptoJS.enc.Base64) }
if (cipherParams.iv) j.iv = cipherParams.iv.toString()
if (cipherParams.salt) j.s = cipherParams.salt.toString()
return JSON.stringify(j).replace(/\s/g, '')
},
/**
* Parse cryptojs data
* @param {string} jsonStr
* @return {*}
*/
'parse': function (jsonStr) {
var j = JSON.parse(jsonStr)
var cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Base64.parse(j.ct) })
if (j.iv) cipherParams.iv = CryptoJS.enc.Hex.parse(j.iv)
if (j.s) cipherParams.salt = CryptoJS.enc.Hex.parse(j.s)
return cipherParams
}
};
CryptoJSAesJson.decrypt($parameters);
"""

View File

@ -1,212 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.moviedream
import android.app.Application
import android.content.SharedPreferences
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.util.asJsoup
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.Exception
class Moviedream : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Moviedream"
override val baseUrl = "https://moviedream.co"
override val lang = "de"
override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// Popular Anime
override fun popularAnimeSelector(): String = "div.boxshow a.linkto"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/beliebtefilme?p=$page")
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain("/" + element.attr("href"))
anime.title = element.select("div.imgboxwiths").text()
anime.thumbnail_url = "$baseUrl/" + element.select("div.imgboxwiths img").attr("src")
return anime
}
override fun popularAnimeNextPageSelector(): String = "a.righter"
// Episodes
override fun episodeListSelector() = throw Exception("not used")
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val episodeList = mutableListOf<SEpisode>()
val episode = SEpisode.create()
episode.name = document.select("div.filmboxsmall p").toString()
.substringAfter("Originaltitel: </b>").substringBefore("<br>")
episode.episode_number = 1F
episode.setUrlWithoutDomain(response.request.url.toString())
episodeList.add(episode)
return episodeList
}
override fun episodeFromElement(element: Element): SEpisode = throw Exception("not Used")
// Video urls
override fun videoListSelector(): String = throw Exception("not Used")
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
return videosFromElement(document)
}
private fun videosFromElement(document: Document): List<Video> {
val videoList = mutableListOf<Video>()
val links = document.select("div#streamlinks script")
val hosterSelection = preferences.getStringSet("hoster_selection", setOf("stape", "dood", "voe"))
links.forEach {
val paremeter = it.toString()
.substringAfter("decrypt(").substringBefore(")+")
val link = getLink(paremeter)
when {
link.contains("https://streamtape.com") && hosterSelection?.contains("stape") == true -> {
val quality = "Streamtape"
val video = StreamTapeExtractor(client).videoFromUrl(link, quality)
if (video != null) {
videoList.add(video)
}
}
link.contains("https://dood") && hosterSelection?.contains("dood") == true -> {
val quality = "Doodstream"
val video = try { DoodExtractor(client).videoFromUrl(link, quality, redirect = false) } catch (e: Exception) { null }
if (video != null) {
videoList.add(video)
}
}
link.contains("https://voe.sx") && hosterSelection?.contains("voe") == true -> {
val quality = "Voe"
val video = VoeExtractor(client).videoFromUrl(link, quality)
if (video != null) {
videoList.add(video)
}
}
}
}
return videoList
}
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 videoFromElement(element: Element) = throw Exception("not used")
override fun videoUrlParse(document: Document) = throw Exception("not used")
// search
override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector(): String = "a.righter"
override fun searchAnimeSelector(): String = popularAnimeSelector()
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = GET("$baseUrl/suchergebnisse.php?p=$page&text=$query&sprache=Deutsch")
// Details
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = baseUrl + document.select("div.filmboxsmall img").attr("src").replace("../..", "")
anime.title = document.select("div.filmboxsmall p").toString()
.substringAfter("Originaltitel: </b>").substringBefore("<br>")
anime.genre = document.select("div.filmboxsmall p").toString()
.substringAfter("Genre: </b>").substringBefore("<br>")
anime.status = SAnime.COMPLETED
anime.author = document.select("div.filmboxsmall p").toString()
.substringAfter("Regisseur: </b>").substringBefore("<br>")
anime.description = document.select("div.filmboxsmall p[style]").text()
return anime
}
// Latest
override fun latestUpdatesNextPageSelector(): String = throw Exception("Not used")
override fun latestUpdatesFromElement(element: Element): SAnime = throw Exception("Not used")
override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used")
override fun latestUpdatesSelector(): String = throw Exception("Not used")
// settings
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val hosterPref = ListPreference(screen.context).apply {
key = "preferred_hoster"
title = "Standard-Hoster"
entries = arrayOf("Streamtape", "Doodstream", "Voe")
entryValues = arrayOf("https://streamtape.com", "https://dood", "https://voe.sx")
setDefaultValue("https://streamtape.com")
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 subSelection = MultiSelectListPreference(screen.context).apply {
key = "hoster_selection"
title = "Hoster auswählen"
entries = arrayOf("Streamtape", "Doodstream", "Voe")
entryValues = arrayOf("stape", "dood", "voe")
setDefaultValue(setOf("stape", "dood", "voe"))
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}
screen.addPreference(hosterPref)
screen.addPreference(subSelection)
}
}

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,12 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'Animerush'
pkgNameSuffix = 'en.animerush'
extClass = '.Animerush'
extVersionCode = 1
libVersion = '13'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 842 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View File

@ -1,235 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.animerush
import android.app.Application
import android.content.SharedPreferences
import android.os.Build
import android.text.Html
import androidx.annotation.RequiresApi
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.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.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.Exception
class Animerush : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Animerush"
override val baseUrl = "https://www.animerush.tv"
override val lang = "en"
override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// Popular Anime
override fun popularAnimeSelector(): String = "div#popular ul li a"
override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
anime.title = element.select("img").attr("title")
anime.thumbnail_url = baseUrl + element.select("img").attr("src")
return anime
}
override fun popularAnimeNextPageSelector(): String? = null
// Episodes
override fun episodeListSelector() = throw Exception("not used")
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val episodeList = mutableListOf<SEpisode>()
val epElements = document.select("div.desc_box_mid div.episode_list")
epElements.forEach {
val episode = episodeFromElement(it)
episodeList.add(episode)
}
return episodeList
}
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.episode_number = element.select("a.fixedLinkColor").attr("title").substringAfter("Episode ").toFloat()
episode.name = element.select("a.fixedLinkColor").attr("title")
episode.setUrlWithoutDomain(element.select("a.fixedLinkColor").attr("href"))
return episode
}
// Video urls
@RequiresApi(Build.VERSION_CODES.N)
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val videoList = mutableListOf<Video>()
val episodes = document.select("div#episodes div.episode1")
episodes.forEach {
val quality = it.select("h3 a").text()
val epurl = "https:" + it.select("h3 a").attr("href")
val epdoc = client.newCall(GET(epurl)).execute().asJsoup()
val urlenc = epdoc.select("div#embed_holder iframe").attr("src")
val urldec = Html.fromHtml(urlenc, 1).toString()
val emdoc = client.newCall(GET(urldec)).execute().asJsoup().toString()
val script = emdoc.substringAfter("eval(function").substringBefore("</script>")
val videoUrlstring = JsUnpacker("eval(function$script").unpack().toString()
when {
videoUrlstring.contains("player.src") -> {
val videoUrl = videoUrlstring.substringAfter("player.src(\"").substringBefore("\")")
val videoHeaders = Headers.headersOf("Referer", "https://www.mp4upload.com/", "Accept", "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5")
val video = Video(videoUrl, quality, videoUrl, headers = videoHeaders)
videoList.add(video)
}
videoUrlstring.contains("MDCore.wurl") -> {
val videoUrl = "https:" + videoUrlstring.substringAfter("MDCore.wurl=\"").substringBefore("\";")
val videoHeaders = Headers.headersOf("Referer", "https://mixdrop.co/", "Accept", "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5", "origin", "https://mixdrop.co")
val video = Video(videoUrl, quality, videoUrl, headers = videoHeaders)
videoList.add(video)
}
}
}
return videoList
}
override fun videoListSelector() = throw Exception("not Used")
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", null)
if (quality != null) {
val newList = mutableListOf<Video>()
var preferred = 0
for (video in this) {
if (video.quality.contains(quality)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
return this
}
override fun videoFromElement(element: Element) = throw Exception("not used")
override fun videoUrlParse(document: Document) = throw Exception("not used")
// search
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("a.highlightit, div.genre_title a").attr("href"))
anime.title = element.select("a.highlightit h3, div.genre_title a").text()
anime.thumbnail_url = "https:" + element.select("a.highlightit object, object").attr("data")
return anime
}
override fun searchAnimeNextPageSelector(): String? = null
override fun searchAnimeSelector(): String = "div#left-column div.search-page_in_box_mid_link"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
return if (query.isNotBlank()) {
GET("$baseUrl/search.php?searchquery=$query")
} else {
val url = "$baseUrl/genres/".toHttpUrlOrNull()!!.newBuilder()
filters.forEach { filter ->
when (filter) {
is GenreFilter -> url.addPathSegment(filter.toUriPart())
else -> {}
}
}
GET(url.toString())
}
}
// Details
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.title = document.select("div.amin_week_box_up1 h1").text()
anime.genre = document.select("div.cat_box_desc a").joinToString(", ") { it.text() }
anime.status = parseStatus(document.select("div.cat_box_desc ").toString().substringAfter("Status:</h3>").substringBefore("<br>")) // span.Qlty
anime.description = document.select("div.cat_box_desc div[align]").text()
return anime
}
private fun parseStatus(status: String?) = when {
status == null -> SAnime.UNKNOWN
status.contains("On-Going", ignoreCase = true) -> SAnime.ONGOING
else -> SAnime.COMPLETED
}
// Latest
override fun latestUpdatesNextPageSelector(): String = throw Exception("Not used")
override fun latestUpdatesFromElement(element: Element): SAnime = throw Exception("Not used")
override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used")
override fun latestUpdatesSelector(): String = throw Exception("Not used")
// Filters
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("NOTE: Ignored if using text search!"),
AnimeFilter.Separator(),
GenreFilter(getGenreList()),
)
private class GenreFilter(vals: Array<Pair<String, String>>) : UriPartFilter("Genres", vals)
private fun getGenreList() = arrayOf(
Pair("Action", "Action"),
Pair("Adventure", "Adventure"),
Pair("Comedy", "Comedy"),
Pair("Crime", "Crime"),
Pair("Drama", "Drama"),
Pair("Family", "Family"),
Pair("Fantasy", "Fantasy"),
Pair("History", "History"),
Pair("Horror", "Horror"),
Pair("Kids", "Kids"),
Pair("Music", "Music"),
Pair("Mystery", "Mystery"),
Pair("Romance", "Romance"),
Pair("Sci-Fi", "Sci-Fi"),
Pair("Science Fiction", "Science Fiction"),
Pair("Thriller", "Thriller"),
)
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
// settings
override fun setupPreferenceScreen(screen: PreferenceScreen) {}
}

View File

@ -1,214 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.animerush
import java.util.regex.Pattern
import kotlin.math.pow
// https://github.com/cylonu87/JsUnpacker
class JsUnpacker(packedJS: String?) {
private var packedJS: String? = null
/**
* Detects whether the javascript is P.A.C.K.E.R. coded.
*
* @return true if it's P.A.C.K.E.R. coded.
*/
fun detect(): Boolean {
val js = packedJS!!.replace(" ", "")
val p = Pattern.compile("eval\\(function\\(p,a,c,k,e,[rd]")
val m = p.matcher(js)
return m.find()
}
/**
* Unpack the javascript
*
* @return the javascript unpacked or null.
*/
fun unpack(): String? {
val js = packedJS
try {
var p =
Pattern.compile("""\}\s*\('(.*)',\s*(.*?),\s*(\d+),\s*'(.*?)'\.split\('\|'\)""", Pattern.DOTALL)
var m = p.matcher(js)
if (m.find() && m.groupCount() == 4) {
val payload = m.group(1).replace("\\'", "'")
val radixStr = m.group(2)
val countStr = m.group(3)
val symtab = m.group(4).split("\\|".toRegex()).toTypedArray()
var radix = 36
var count = 0
try {
radix = radixStr.toInt()
} catch (e: Exception) {
}
try {
count = countStr.toInt()
} catch (e: Exception) {
}
if (symtab.size != count) {
throw Exception("Unknown p.a.c.k.e.r. encoding")
}
val unbase = Unbase(radix)
p = Pattern.compile("\\b\\w+\\b")
m = p.matcher(payload)
val decoded = StringBuilder(payload)
var replaceOffset = 0
while (m.find()) {
val word = m.group(0)
val x = unbase.unbase(word)
var value: String? = null
if (x < symtab.size && x >= 0) {
value = symtab[x]
}
if (value != null && value.isNotEmpty()) {
decoded.replace(m.start() + replaceOffset, m.end() + replaceOffset, value)
replaceOffset += value.length - word.length
}
}
return decoded.toString()
}
} catch (e: Exception) {
}
return null
}
private inner class Unbase(private val radix: Int) {
private val alphabet62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
private val alphabet95 =
" !\"#$%&\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
private var alphabet: String? = null
private var dictionary: HashMap<String, Int>? = null
fun unbase(str: String): Int {
var ret = 0
if (alphabet == null) {
ret = str.toInt(radix)
} else {
val tmp = StringBuilder(str).reverse().toString()
for (i in tmp.indices) {
ret += (radix.toDouble().pow(i.toDouble()) * dictionary!![tmp.substring(i, i + 1)]!!).toInt()
}
}
return ret
}
init {
if (radix > 36) {
when {
radix < 62 -> {
alphabet = alphabet62.substring(0, radix)
}
radix in 63..94 -> {
alphabet = alphabet95.substring(0, radix)
}
radix == 62 -> {
alphabet = alphabet62
}
radix == 95 -> {
alphabet = alphabet95
}
}
dictionary = HashMap(95)
for (i in 0 until alphabet!!.length) {
dictionary!![alphabet!!.substring(i, i + 1)] = i
}
}
}
}
/**
* @param packedJS javascript P.A.C.K.E.R. coded.
*/
init {
this.packedJS = packedJS
}
companion object {
private val C =
listOf(
0x63,
0x6f,
0x6d,
0x2e,
0x67,
0x6f,
0x6f,
0x67,
0x6c,
0x65,
0x2e,
0x61,
0x6e,
0x64,
0x72,
0x6f,
0x69,
0x64,
0x2e,
0x67,
0x6d,
0x73,
0x2e,
0x61,
0x64,
0x73,
0x2e,
0x4d,
0x6f,
0x62,
0x69,
0x6c,
0x65,
0x41,
0x64,
0x73,
)
private val Z =
listOf(
0x63,
0x6f,
0x6d,
0x2e,
0x66,
0x61,
0x63,
0x65,
0x62,
0x6f,
0x6f,
0x6b,
0x2e,
0x61,
0x64,
0x73,
0x2e,
0x41,
0x64,
)
fun String.load(): String? {
return try {
var load = this
for (q in C.indices) {
if (C[q % 4] > 270) {
load += C[q % 3]
} else {
load += C[q].toChar()
}
}
Class.forName(load.substring(load.length - C.size, load.length)).name
} catch (_: Exception) {
try {
var f = C[2].toChar().toString()
for (w in Z.indices) {
f += Z[w].toChar()
}
return Class.forName(f.substring(0b001, f.length)).name
} catch (_: Exception) {
null
}
}
}
}
}

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,12 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'twist.moe'
pkgNameSuffix = 'en.twistmoe'
extClass = '.TwistMoe'
extVersionCode = 9
libVersion = '13'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,63 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.twistmoe
import android.util.Base64
import java.security.MessageDigest
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
class AESDecrypt {
fun aesEncrypt(v: String, secretKey: ByteArray, initializationVector: ByteArray) = encrypt(v, secretKey, initializationVector)
fun aesDecrypt(v: ByteArray, secretKey: ByteArray, initializationVector: ByteArray) = decrypt(v, secretKey, initializationVector)
fun getIvAndKey(v: String): ByteArray {
// credits: https://github.com/anime-dl/anime-downloader/blob/c030fded0b7f79d5bb8a07f5cf6b2ae8fa3954a1/anime_downloader/sites/twistmoe.py
val byteStr = Base64.decode(v.toByteArray(Charsets.UTF_8), Base64.DEFAULT)
val md5 = MessageDigest.getInstance("MD5")
assert(byteStr.decodeToString(0, 8) == "Salted__")
val salt = byteStr.sliceArray(8..15)
assert(salt.lastIndex == 7)
val secretStr = "267041df55ca2b36f2e322d05ee2c9cf"
val secret = secretStr
.map { it.toByte() }
.toByteArray()
val data = secret + salt
var key = md5.digest(data)
var finalKey = key
while (finalKey.lastIndex < 47) {
key = md5.digest(key + data)
finalKey += key
}
return finalKey.sliceArray(0..47)
}
fun unpad(v: String): String {
return v.substring(0..v.lastIndex - v.last().toInt())
}
fun getToDecode(v: String): ByteArray {
val byteStr = Base64.decode(v.toByteArray(Charsets.UTF_8), Base64.DEFAULT)
assert(byteStr.decodeToString(0, 8) == "Salted__")
return byteStr.sliceArray(16..byteStr.lastIndex)
}
private fun cipher(opmode: Int, secretKey: ByteArray, initializationVector: ByteArray): Cipher {
if (secretKey.lastIndex != 31) throw RuntimeException("SecretKey length is not 32 chars")
if (initializationVector.lastIndex != 15) throw RuntimeException("IV length is not 16 chars")
val c = Cipher.getInstance("AES/CBC/NoPadding")
val sk = SecretKeySpec(secretKey, "AES")
val iv = IvParameterSpec(initializationVector)
c.init(opmode, sk, iv)
return c
}
private fun encrypt(str: String, secretKey: ByteArray, iv: ByteArray): String {
val encrypted = cipher(Cipher.ENCRYPT_MODE, secretKey, iv).doFinal(str.toByteArray(Charsets.UTF_8))
return String(Base64.encode(encrypted, Base64.DEFAULT))
}
private fun decrypt(str: ByteArray, secretKey: ByteArray, iv: ByteArray): String {
return String(cipher(Cipher.DECRYPT_MODE, secretKey, iv).doFinal(str))
}
}

View File

@ -1,242 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.twistmoe
import android.annotation.SuppressLint
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.float
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
import java.lang.Exception
import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.collections.ArrayList
class TwistMoe : AnimeHttpSource() {
override val name = "twist.moe"
override val baseUrl = "https://twist.moe"
override val lang = "en"
override val supportsLatest = false
private val json: Json by injectLazy()
override fun headersBuilder() = Headers.Builder().apply {
add("User-Agent", "Aniyomi")
add("Referer", "https://twist.moe/")
}
private val requestHeaders =
Headers.headersOf("x-access-token", "0df14814b9e590a1f26d3071a4ed7974", "referer", baseUrl)
override fun popularAnimeRequest(page: Int): Request =
GET("https://api.twist.moe/api/anime#$page", requestHeaders)
override fun popularAnimeParse(response: Response): AnimesPage {
val responseString = response.body.string()
val array = json.decodeFromString<JsonArray>(responseString)
val list = mutableListOf<JsonElement>()
array.toCollection(list)
val page = response.request.url.fragment!!.toInt() - 1
val start = page * 10
val end = if (list.lastIndex > start + 9) start + 9 else list.lastIndex
val range = start..end
return AnimesPage(parseSearchJson(list.slice(range)), end != list.lastIndex)
}
private fun parseSearchJson(array: List<JsonElement>): List<SAnime> {
val animeList = mutableListOf<SAnime>()
for (item in array) {
val anime = SAnime.create()
anime.title = item.jsonObject["title"]!!.jsonPrimitive.content
anime.setUrlWithoutDomain("$baseUrl/a/" + item.jsonObject["slug"]!!.jsonObject["slug"]!!.jsonPrimitive.content)
anime.status = when (item.jsonObject["ongoing"]!!.jsonPrimitive.int) {
0 -> SAnime.COMPLETED
1 -> SAnime.ONGOING
else -> SAnime.UNKNOWN
}
animeList.add(anime)
}
return animeList
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request =
GET("https://api.twist.moe/api/anime#query=$query;page=$page", requestHeaders)
override fun searchAnimeParse(response: Response): AnimesPage {
val responseString = response.body.string()
val array = json.decodeFromString<JsonArray>(responseString)
val list = mutableListOf<JsonElement>()
array.toCollection(list)
val query = response.request.url.fragment!!
.substringAfter("query=")
.substringBeforeLast(";page=")
.toLowerCase(Locale.ROOT)
val toRemove = mutableListOf<JsonElement>()
for (entry in list) {
val title = entry.jsonObject["title"]!!.jsonPrimitive.content.toLowerCase(Locale.ROOT)
val altTitle = try {
entry.jsonObject["alt_title"]!!.jsonPrimitive.content.toLowerCase(Locale.ROOT)
} catch (e: Exception) { "" }
if (!(title.contains(query) || altTitle.contains(query))) toRemove.add(entry)
}
list.removeAll(toRemove)
val page = response.request.url.fragment!!.substringAfterLast(";page=").toInt() - 1
val start = page * 10
val end = if (list.lastIndex > start + 9) start + 9 else list.lastIndex
val range = start..end
return AnimesPage(parseSearchJson(list.slice(range)), end != list.lastIndex)
}
override fun animeDetailsRequest(anime: SAnime): Request {
val slug = anime.url.substringAfter("/a/")
return GET("https://api.twist.moe/api/anime/$slug", requestHeaders)
}
override fun animeDetailsParse(response: Response): SAnime {
val responseString = response.body.string()
val jObject = json.decodeFromString<JsonObject>(responseString)
val anime = SAnime.create()
anime.title = jObject["title"]!!.jsonPrimitive.content
anime.setUrlWithoutDomain("$baseUrl/a/" + jObject["slug"]!!.jsonObject["slug"]!!.jsonPrimitive.content)
anime.description = jObject["description"]!!.jsonPrimitive.content
anime.status = when (jObject["ongoing"]!!.jsonPrimitive.int) {
0 -> SAnime.COMPLETED
1 -> SAnime.ONGOING
else -> SAnime.UNKNOWN
}
getCover(jObject, anime)
return anime
}
private fun getCover(jObject: JsonObject, anime: SAnime) {
try {
val malID = try {
jObject["mal_id"]!!.jsonPrimitive.contentOrNull
} catch (e: Exception) {
""
}
if (malID != null) {
val headers = Headers.Builder().apply {
add("Content-Type", "application/json")
add("Accept", "application/json")
}.build()
val bodyString = "{\"query\":\"query(\$id: Int){Media(type:ANIME,idMal:\$id){coverImage{large}}}\",\"variables\":{\"id\":$malID}}"
val body = bodyString.toRequestBody("application/json".toMediaType())
val coverResponse = client.newCall(POST("https://graphql.anilist.co", headers, body)).execute()
val imageUrl = json.decodeFromString<JsonObject>(coverResponse.body.string())["data"]!!
.jsonObject["Media"]!!
.jsonObject["coverImage"]!!
.jsonObject["large"]!!.jsonPrimitive.content
if (imageUrl.isNotEmpty()) anime.thumbnail_url = imageUrl
} else {
val query = anime.title
val headers = Headers.Builder().apply {
add("Content-Type", "application/json")
add("Accept", "application/json")
}.build()
val bodyString = "{\"query\":\"query(\$query: String){Media(type:ANIME,search:\$query){coverImage{large}}}\",\"variables\":{\"query\":\"$query\"}}"
val body = bodyString.toRequestBody("application/json".toMediaType())
val coverResponse = client.newCall(POST("https://graphql.anilist.co", headers, body)).execute()
val imageUrl = json.decodeFromString<JsonObject>(coverResponse.body.string())["data"]!!
.jsonObject["Media"]!!
.jsonObject["coverImage"]!!
.jsonObject["large"]!!.jsonPrimitive.content
if (imageUrl.isNotEmpty()) anime.thumbnail_url = imageUrl
}
} catch (e: Exception) {
}
}
override fun videoListRequest(episode: SEpisode): Request {
return GET(episode.url, requestHeaders)
}
override fun videoListParse(response: Response): List<Video> {
val responseString = response.body.string()
val array = json.decodeFromString<JsonArray>(responseString)
val list = mutableListOf<JsonElement>()
array.toCollection(list)
val episodeNumber = response.request.url.fragment!!.toFloat()
val videoList = mutableListOf<Video>()
val aes = AESDecrypt()
for (entry in list) {
if (entry.jsonObject["number"]!!.jsonPrimitive.float == episodeNumber) {
val source = entry.jsonObject["source"]!!.jsonPrimitive.content
val ivAndKey = aes.getIvAndKey(source)
val toDecode = aes.getToDecode(source)
val url = "https://air-cdn.twist.moe" +
aes.unpad(aes.aesDecrypt(toDecode, ivAndKey.sliceArray(0..31), ivAndKey.sliceArray(32..47)))
videoList.add(Video(url, "1080p", url))
}
}
return videoList
}
override fun episodeListRequest(anime: SAnime): Request {
// aes.unpad(aes.aesDecrypt(toDecode, ivAndKey.sliceArray(0..31), ivAndKey.sliceArray(32..47)))
val slug = anime.url.substringAfter("/a/")
return GET("https://api.twist.moe/api/anime/$slug/sources", requestHeaders)
}
override fun episodeListParse(response: Response): List<SEpisode> {
val responseString = response.body.string()
val array = json.decodeFromString<JsonArray>(responseString)
val list = mutableListOf<JsonElement>()
array.toCollection(list)
val episodeList = mutableListOf<SEpisode>()
for (entry in list) {
try {
val episode = SEpisode.create()
episode.date_upload = parseDate(entry.jsonObject["updated_at"]!!.jsonPrimitive.content)
episode.name = "Episode " + entry.jsonObject["number"]!!.jsonPrimitive.content
episode.episode_number = entry.jsonObject["number"]!!.jsonPrimitive.float
episode.url = response.request.url.toString() + "#${episode.episode_number}"
episodeList.add(episode)
} catch (e: Exception) {
}
}
return episodeList.reversed()
}
@SuppressLint("SimpleDateFormat")
private fun parseDate(date: String): Long {
val knownPatterns: MutableList<SimpleDateFormat> = ArrayList()
knownPatterns.add(SimpleDateFormat("yyyy-MM-dd hh:mm:ss"))
for (pattern in knownPatterns) {
try {
// Take a try
return pattern.parse(date)!!.time
} catch (e: Throwable) {
// Loop on
}
}
return System.currentTimeMillis()
}
override fun latestUpdatesRequest(page: Int): Request = throw Exception("not used")
override fun latestUpdatesParse(response: Response): AnimesPage = throw Exception("not used")
}

View File

@ -5,7 +5,7 @@ ext {
extName = 'Vostfree'
pkgNameSuffix = 'fr.vostfree'
extClass = '.Vostfree'
extVersionCode = 12
extVersionCode = 13
libVersion = '13'
}

View File

@ -33,7 +33,7 @@ class Vostfree : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Vostfree"
override val baseUrl = "https://vostfree.cx"
override val baseUrl = "https://vostfree.ws"
override val lang = "fr"