chore: Remove (known) dead sources (#2593)

This commit is contained in:
Claudemirovsky 2023-12-02 07:29:02 -03:00 committed by GitHub
parent 1fd38ddddb
commit 03bba081b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 0 additions and 1833 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

View File

@ -1,136 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.animeonline360
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.multisrc.dooplay.DooPlay
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Element
class AnimeOnline360 : DooPlay(
"en",
"AnimeOnline360",
"https://animeonline360.me",
) {
// ============================== Popular ===============================
override fun popularAnimeSelector(): String = "div.content article > div.poster"
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/trending/page/$page/")
override fun popularAnimeNextPageSelector() = latestUpdatesNextPageSelector()
override fun popularAnimeFromElement(element: Element): SAnime {
return super.popularAnimeFromElement(element).apply { title = title.addSubPrefix() }
}
// =============================== Latest ===============================
override fun latestUpdatesSelector(): String = "div#archive-content > article"
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/anime-a/page/$page/")
override fun latestUpdatesNextPageSelector(): String = "#nextpagination"
override fun latestUpdatesFromElement(element: Element): SAnime {
return super.latestUpdatesFromElement(element).apply { title = title.addSubPrefix() }
}
// =============================== Search ===============================
override fun searchAnimeNextPageSelector(): String = "div.pagination > span.current + a"
// ============================== Episodes ==============================
override fun episodeListSelector() = "ul.episodios > li"
override fun episodeListParse(response: Response): List<SEpisode> {
return if (response.request.url.pathSegments.first().contains("movie", true)) {
val document = response.use { getRealAnimeDoc(it.asJsoup()) }
listOf(
SEpisode.create().apply {
episode_number = 0F
date_upload = document.selectFirst("div.extra > span.date")
?.text()
?.toDate() ?: 0L
name = "Movie"
setUrlWithoutDomain(response.request.url.toString())
},
)
} else {
response.use {
getRealAnimeDoc(it.asJsoup())
}.select(episodeListSelector()).map(::episodeFromElement)
}
}
override fun episodeFromElement(element: Element): SEpisode {
return SEpisode.create().apply {
val epNum = element.selectFirst("div.numerando")!!.text()
.trim()
.let(episodeNumberRegex::find)
?.groupValues
?.last() ?: "0"
val href = element.selectFirst("a[href]")!!
val episodeName = href.ownText()
episode_number = epNum.toFloatOrNull() ?: 0F
date_upload = element.selectFirst(episodeDateSelector)
?.text()
?.toDate() ?: 0L
name = "Ep. $epNum - $episodeName"
setUrlWithoutDomain(href.attr("href"))
}
}
// ============================== Filters ===============================
override val fetchGenres = false
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
AnimeFilter.Header("Text search ignores filters"),
SubPageFilter(),
)
private class SubPageFilter : UriPartFilter(
"Sub-Page",
arrayOf(
Pair("<select>", ""),
Pair("Dubbed", "genre-a/dubbed"),
Pair("Movies", "movies-a"),
),
)
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val document = response.use { it.asJsoup() }
return document.select("iframe[src~=player]").mapNotNull {
if (it.attr("src").toHttpUrl().queryParameter("source") != null) {
runCatching {
val link = it.attr("src").toHttpUrl().queryParameter("source")!!
Video(link, "Video", link)
}.getOrNull()
} else {
null
}
}
}
// ============================= Utilities ==============================
private fun String.addSubPrefix(): String {
return if (this.contains(" dubbed", true)) {
"[DUB] ${this.substringBefore(" Dubbed")}"
} else if (this.contains(" subbed", true)) {
"[SUB] ${this.substringBefore(" Subbed")}"
} else {
this
}
}
}

View File

@ -11,7 +11,6 @@ class DooPlayGenerator : ThemeSourceGenerator {
override val baseVersionCode = 1
override val sources = listOf(
SingleLang("AnimeOnline360", "https://animeonline360.me", "en", isNsfw = false),
SingleLang("AnimeOnline.Ninja", "https://ww3.animeonline.ninja", "es", className = "AnimeOnlineNinja", isNsfw = false, overrideVersionCode = 34),
SingleLang("AnimesOnline", "https://animesonline.nz", "pt-BR", isNsfw = false, overrideVersionCode = 6, pkgName = "animesgratis"),
SingleLang("AnimePlayer", "https://animeplayer.com.br", "pt-BR", isNsfw = true, overrideVersionCode = 2),

View File

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

View File

@ -1,20 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'HDFilme'
pkgNameSuffix = 'de.hdfilme'
extClass = '.HDFilme'
extVersionCode = 3
libVersion = '13'
}
dependencies {
implementation(project(':lib-vudeo-extractor'))
implementation(project(':lib-mixdrop-extractor'))
implementation(project(':lib-dood-extractor'))
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -1,233 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.hdfilme
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.mixdropextractor.MixDropExtractor
import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor
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 HDFilme : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "HDFilme"
override val baseUrl = "https://www.hdfilme.fun"
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 = "#dle-content div.short"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/filmestreamen/page-$page.html")
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("a.short-img").attr("href"))
anime.thumbnail_url = baseUrl + element.select("a.short-img img ").attr("src")
anime.title = element.select("a.short-img img").attr("alt")
return anime
}
override fun popularAnimeNextPageSelector(): String = "span.pnext a"
// episodes
override fun episodeListSelector() = throw Exception("not used")
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val url = document.select("link[rel=\"canonical\"]").attr("href")
val episodeList = mutableListOf<SEpisode>()
if (url.contains("serienstreamen")) {
val episodeElement = document.select("div.seasontab div[align=\"center\"] a")
episodeElement.forEach {
val episodes = parseEpisodesFromSeries(it)
episodeList.addAll(episodes)
}
} else {
val episode = SEpisode.create()
episode.episode_number = 1F
episode.name = "Film"
episode.setUrlWithoutDomain(document.select("link[rel=\"canonical\"]").attr("href"))
episodeList.add(episode)
}
return episodeList.reversed()
}
private fun parseEpisodesFromSeries(element: Element): List<SEpisode> {
val seasonurl = element.attr("href")
val episodesHtml = client.newCall(GET(seasonurl)).execute().asJsoup()
val episodeElements = episodesHtml.select("div.seasontab div[align=\"center\"] a")
return episodeElements.map { episodeFromElement(it) }
}
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.episode_number = element.select("span.number").text().toFloat()
episode.name = element.attr("title")
episode.setUrlWithoutDomain(element.attr("href"))
return episode
}
// Video Extractor
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 hosterSelection = preferences.getStringSet("hoster_selection", setOf("dood", "vud", "mix"))
document.select("div.undervideo li").forEach {
val url = it.select("div.lien").attr("data-url")
when {
url.contains("https://dood") && hosterSelection?.contains("dood") == true -> {
val quality = "Doodstream"
val video = DoodExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
}
url.contains("https://vudeo") && hosterSelection?.contains("vud") == true -> {
val video = VudeoExtractor(client).videosFromUrl(url)
videoList.addAll(video)
}
url.contains("https://mixdrop") && hosterSelection?.contains("mix") == true -> {
val video = MixDropExtractor(client).videoFromUrl(url)
videoList.addAll(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 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("a.short-img").attr("href"))
anime.thumbnail_url = baseUrl + element.select("a.short-img img ").attr("src")
anime.title = element.select("a.short-img img").attr("alt")
return anime
}
override fun searchAnimeNextPageSelector(): String = "span.pnext a"
override fun searchAnimeSelector(): String = "#dle-content div.short"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = GET("$baseUrl/recherche?q=$query&page=$page")
// Details
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = baseUrl + document.select("div.movie-page div.mimg img").attr("src")
anime.title = document.select("meta[property=\"og:title\"]").attr("content")
anime.description = document.select("meta[property=\"og:title\"]").attr("content")
document.select("div.short-info div.short-info").forEach { el ->
if (el.text().contains("Schauspieler:")) {
anime.author = el.text().replace("Schauspieler:", "").split(" , ").joinToString(", ") { it }
}
}
document.select("div.short-info div.short-info").forEach { el ->
if (el.text().contains("Genre:")) {
anime.genre = el.select("a").joinToString(", ") { it.text() }
}
}
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("Doodstream", "Vudeo", "Mixdrop")
entryValues = arrayOf("https://dood", "https://vudeo", "https://mixdrop")
setDefaultValue("https://dood")
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("Doodstream", "Vudeo", "MixDrop")
entryValues = arrayOf("dood", "vud", "mix")
setDefaultValue(setOf("dood", "vud", "mix"))
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}
screen.addPreference(hosterPref)
screen.addPreference(subSelection)
}
}

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".de.kiste.KisteUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="kiste.to"
android:pathPattern="/..*/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,20 +0,0 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
}
ext {
extName = 'Kiste'
pkgNameSuffix = 'de.kiste'
extClass = '.Kiste'
extVersionCode = 4
libVersion = '13'
containsNsfw = true
}
dependencies {
implementation(project(":lib-playlist-utils"))
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,204 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.kiste
import android.util.Base64
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.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.net.URLEncoder
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
class Kiste : ParsedAnimeHttpSource() {
override val name = "Kiste"
override val baseUrl = "https://kiste.to"
override val lang = "de"
override val supportsLatest = true
private val json: Json by injectLazy()
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/search?sort=imdb:desc&page=$page")
override fun popularAnimeSelector() = "div.filmlist > div.item > a"
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
setUrlWithoutDomain(element.attr("href"))
title = element.attr("title")
thumbnail_url = element.selectFirst("img")?.absUrl("src")
}
override fun popularAnimeNextPageSelector() = "li > a[rel=next]"
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int) = GET(baseUrl)
override fun latestUpdatesSelector() = "div[data-name=alles] > div.filmlist > div.item > a"
override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
override fun latestUpdatesNextPageSelector() = null
// =============================== Search ===============================
override fun getFilterList() = KisteFilters.FILTER_LIST
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {
return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
val path = query.removePrefix(PREFIX_SEARCH)
client.newCall(GET("$baseUrl/$path"))
.asObservableSuccess()
.map(::searchAnimeByIdParse)
} else {
super.fetchSearchAnime(page, query, filters)
}
}
private fun searchAnimeByIdParse(response: Response): AnimesPage {
val details = animeDetailsParse(response.use { it.asJsoup() })
return AnimesPage(listOf(details), false)
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val params = KisteFilters.getSearchParameters(filters)
val url = buildString {
append("$baseUrl/search?page=$page")
if (query.isNotBlank()) append("&keyword=$query")
with(params) {
listOf(genres, types, countries, years, qualities)
.filter(String::isNotBlank)
.forEach { append("&$it") }
}
}
return GET(url, headers)
}
override fun searchAnimeSelector() = popularAnimeSelector()
override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector()
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document) = SAnime.create().apply {
val section = document.selectFirst("section.info")!!
thumbnail_url = section.selectFirst("img")?.absUrl("src")
title = section.selectFirst("h1.title")!!.text()
genre = section.select("span:containsOwn(Genre:) + span > a")
.eachText()
.joinToString()
.takeIf(String::isNotBlank)
description = section.selectFirst("div.desc")?.text()
}
// ============================== Episodes ==============================
@Serializable data class HtmlData(val html: String)
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
val slug = anime.url.substringAfterLast("/")
val vrf = URLEncoder.encode(encryptRC4(slug).trimEnd(), "utf-8")
val newDoc = client.newCall(GET("$baseUrl/ajax/film/servers.php?id=$slug&vrf=$vrf&episode=1-1&token="))
.execute()
.use { json.decodeFromString<HtmlData>(it.body.string()).html }
.let(Jsoup::parse)
val episodes = newDoc.select(episodeListSelector())
.map(::episodeFromElement)
.sortedByDescending { it.episode_number }
return Observable.just(episodes)
}
override fun episodeListParse(response: Response) = throw Exception("not used")
override fun episodeListSelector() = "div.episode > a"
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
val id = element.attr("data-ep").substringAfter("\":\"").substringBefore('"')
setUrlWithoutDomain(element.attr("href") + "?id=$id")
val kname = element.attr("data-kname")
val (seasonNum, epNum) = kname.split("-", limit = 2)
name = "Staffel $seasonNum - Episode $epNum"
episode_number = "$seasonNum.${epNum.padStart(3, '0')}".toFloatOrNull() ?: 1F
}
// ============================ Video Links =============================
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
val id = episode.url.substringAfter("?id=")
val headers = headersBuilder()
.add("Referer", episode.url)
.add("X-Requested-With", "XMLHttpRequest")
.build()
val url = client.newCall(GET("$baseUrl/ajax/episode/info.php?id=$id", headers))
.execute()
.use { it.body.string().substringAfter(":\"").substringBefore('"') }
.let(::decryptRC4)
val playlistUrl = baseUrl + client.newCall(GET(url, headers)).execute()
.use { it.body.string() }
.substringAfter("file: \"", "")
.substringBefore('"')
return Observable.just(playlistUtils.extractFromHls(playlistUrl, baseUrl + episode.url))
}
override fun videoListParse(response: Response): List<Video> {
throw UnsupportedOperationException("Not used.")
}
override fun videoListSelector(): String {
throw UnsupportedOperationException("Not used.")
}
override fun videoFromElement(element: Element): Video {
throw UnsupportedOperationException("Not used.")
}
override fun videoUrlParse(document: Document): String {
throw UnsupportedOperationException("Not used.")
}
// ============================= Utilities ==============================
private fun decryptRC4(data: String): String {
val b64decoded = Base64.decode(data, Base64.DEFAULT)
val rc4Key = SecretKeySpec(KISTE_KEY, "RC4")
val cipher = Cipher.getInstance("RC4")
cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.getParameters())
return cipher.doFinal(b64decoded).toString(Charsets.UTF_8)
}
private fun encryptRC4(data: String): String {
val rc4Key = SecretKeySpec(KISTE_KEY, "RC4")
val cipher = Cipher.getInstance("RC4")
cipher.init(Cipher.ENCRYPT_MODE, rc4Key, cipher.getParameters())
return Base64.encodeToString(cipher.doFinal(data.toByteArray()), Base64.DEFAULT)
}
companion object {
const val PREFIX_SEARCH = "path:"
private val KISTE_KEY = "DZmuZuXqa9O0z3b7".toByteArray()
}
}

View File

@ -1,202 +0,0 @@
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
object KisteFilters {
open class CheckBoxFilterList(name: String, val pairs: Array<Pair<String, String>>) :
AnimeFilter.Group<AnimeFilter.CheckBox>(name, pairs.map { CheckBoxVal(it.first, false) })
private class CheckBoxVal(name: String, state: Boolean = false) : AnimeFilter.CheckBox(name, state)
private inline fun <reified R> AnimeFilterList.parseCheckbox(
options: Array<Pair<String, String>>,
name: String,
): String {
return (first { it is R } as CheckBoxFilterList).state
.filter { it.state }
.map { checkbox -> options.find { it.first == checkbox.name }!!.second }
.filter(String::isNotBlank)
.joinToString("&") { "$name[]=$it" }
}
class GenresFilter : CheckBoxFilterList("Genres", KisteFiltersData.GENRES)
class TypesFilter : CheckBoxFilterList("Types", KisteFiltersData.TYPES)
class CountriesFilter : CheckBoxFilterList("Countries", KisteFiltersData.COUNTRIES)
class YearsFilter : CheckBoxFilterList("Jaht", KisteFiltersData.YEARS)
class QualitiesFilter : CheckBoxFilterList("Qualitat", KisteFiltersData.QUALITIES)
val FILTER_LIST get() = AnimeFilterList(
GenresFilter(),
TypesFilter(),
CountriesFilter(),
YearsFilter(),
QualitiesFilter(),
)
data class FilterSearchParams(
val genres: String = "",
val types: String = "",
val countries: String = "",
val years: String = "",
val qualities: String = "",
)
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
if (filters.isEmpty()) return FilterSearchParams()
return FilterSearchParams(
filters.parseCheckbox<GenresFilter>(KisteFiltersData.GENRES, "genre"),
filters.parseCheckbox<TypesFilter>(KisteFiltersData.TYPES, "type"),
filters.parseCheckbox<CountriesFilter>(KisteFiltersData.COUNTRIES, "country"),
filters.parseCheckbox<YearsFilter>(KisteFiltersData.YEARS, "release"),
filters.parseCheckbox<QualitiesFilter>(KisteFiltersData.QUALITIES, "quality"),
)
}
private object KisteFiltersData {
val GENRES = arrayOf(
Pair("Abenteuer", "43"),
Pair("Action", "1"),
Pair("Adventure", "2"),
Pair("Amerikanisch", "53"),
Pair("Animation", "14"),
Pair("Anime", "19"),
Pair("Biography", "36"),
Pair("Britisch", "54"),
Pair("Cartoon", "55"),
Pair("Children", "22"),
Pair("Chinesisch", "52"),
Pair("Comedy", "5"),
Pair("Crime", "10"),
Pair("Deutsch", "49"),
Pair("Documentary", "18"),
Pair("Dokumentarfilm", "40"),
Pair("Drama", "9"),
Pair("Familie", "39"),
Pair("Family", "23"),
Pair("Fantasy", "3"),
Pair("Film", "4"),
Pair("Food", "16"),
Pair("Game Show", "17"),
Pair("Historie", "46"),
Pair("History", "20"),
Pair("Horror", "15"),
Pair("Japanisch", "50"),
Pair("Kids", "29"),
Pair("Kinofilme", "57"),
Pair("Komödie", "37"),
Pair("Koreanisch", "51"),
Pair("Kriegsfilm", "47"),
Pair("Krimi", "38"),
Pair("Liebesfilm", "44"),
Pair("Martial Arts", "24"),
Pair("Mini-Series", "31"),
Pair("Musical", "28"),
Pair("Musik", "48"),
Pair("Mystery", "11"),
Pair("News", "45"),
Pair("Politics", "41"),
Pair("Reality", "26"),
Pair("Romance", "7"),
Pair("Sci-Fi", "34"),
Pair("Science Fiction", "6"),
Pair("Serie", "12"),
Pair("Short", "25"),
Pair("Soap", "30"),
Pair("Sport", "33"),
Pair("Supernatural", "27"),
Pair("Suspense", "13"),
Pair("Talk", "42"),
Pair("Thriller", "8"),
Pair("Travel", "32"),
Pair("War", "21"),
Pair("Western", "35"),
)
val TYPES = arrayOf(
Pair("Filme", "movie"),
Pair("TV-Serien", "series"),
)
val COUNTRIES = arrayOf(
Pair("Argentinien", "AR"),
Pair("Austrailen", "AU"),
Pair("Belgien", "BE"),
Pair("Brasilien", "BR"),
Pair("China", "CN"),
Pair("Deutschland", "DE"),
Pair("Dänemark", "DK"),
Pair("Finnland", "FI"),
Pair("Frankreich", "FR"),
Pair("Großbritannien", "GB"),
Pair("Hong-Kong", "HK"),
Pair("Indien", "IN"),
Pair("Irland", "IE"),
Pair("Israel", "IL"),
Pair("Italien", "IT"),
Pair("Japan", "JP"),
Pair("Kanada", "CA"),
Pair("Mexiko", "MX"),
Pair("Neuseeland", "NZ"),
Pair("Niederlanden", "NL"),
Pair("Norwergen", "NO"),
Pair("Philippinen", "PH"),
Pair("Poland", "PL"),
Pair("Rumänien", "RO"),
Pair("Russland", "RU"),
Pair("Schweden", "SE"),
Pair("Schweiz", "CH"),
Pair("Spanien", "ES"),
Pair("Südafrika", "ZA"),
Pair("Südkorea", "KR"),
Pair("Thailand", "TH"),
Pair("Tschechien", "CZ"),
Pair("Türkei", "TR"),
Pair("USA", "US"),
Pair("Ungarn", "HU"),
Pair("Österreich", "AT"),
)
val YEARS = arrayOf(
Pair("2023", "2023"),
Pair("2022", "2022"),
Pair("2021", "2021"),
Pair("2020", "2020"),
Pair("2019", "2019"),
Pair("2018", "2018"),
Pair("2017", "2017"),
Pair("2016", "2016"),
Pair("2015", "2015"),
Pair("2014", "2014"),
Pair("2013", "2013"),
Pair("2012", "2012"),
Pair("2011", "2011"),
Pair("2010", "2010"),
Pair("2009", "2009"),
Pair("2008", "2008"),
Pair("2007", "2007"),
Pair("2006", "2006"),
Pair("2005", "2005"),
Pair("2004", "2004"),
Pair("2003", "2003"),
Pair("2000s", "2000s"),
Pair("1990s", "1990s"),
Pair("1980s", "1980s"),
Pair("1970s", "1970s"),
Pair("1960s", "1960s"),
Pair("1950s", "1950s"),
Pair("1940s", "1940s"),
Pair("1930s", "1930s"),
Pair("1920s", "1920s"),
Pair("1910s", "1910s"),
Pair("1900s", "1900s"),
)
val QUALITIES = arrayOf(
Pair("CAM", "CAM"),
Pair("HD", "HD"),
Pair("HDRip", "HDRip"),
Pair("SD", "SD"),
Pair("TS", "TS"),
)
}
}

View File

@ -1,43 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.kiste
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.util.Log
import kotlin.system.exitProcess
/**
* Springboard that accepts https://kiste.to/<type>/<item> intents
* and redirects them to the main Aniyomi process.
*/
class KisteUrlActivity : Activity() {
private val tag = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
if (pathSegments != null && pathSegments.size > 1) {
val type = pathSegments[0]
val item = pathSegments[1]
val path = "$type/$item"
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.ANIMESEARCH"
putExtra("query", "${Kiste.PREFIX_SEARCH}$path")
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e(tag, e.toString())
}
} else {
Log.e(tag, "could not parse uri from intent $intent")
}
finish()
exitProcess(0)
}
}

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 = 'NollyVerse'
pkgNameSuffix = 'en.nollyverse'
extClass = '.NollyVerse'
extVersionCode = 2
libVersion = '13'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 KiB

View File

@ -1,555 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.nollyverse
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
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.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.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
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 java.lang.Exception
class NollyVerse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "NollyVerse"
override val baseUrl = "https://www.nollyverse.com"
override val lang = "en"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/category/trending-movies/page/$page/")
override fun popularAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup()
val animes = document.select(popularAnimeSelector()).map { element ->
popularAnimeFromElement(element)
}
val hasNextPage = popularAnimeNextPageSelector()?.let { selector ->
if (document.select(selector).text() != ">") {
return AnimesPage(animes, false)
}
document.select(selector).first()
} != null
return AnimesPage(animes, hasNextPage)
}
override fun popularAnimeSelector(): String = "div.col-md-8 div.row div.col-md-6"
override fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
title = element.select("div.post-body h3 a").text()
thumbnail_url = element.select("a.post-img img").attr("data-src")
setUrlWithoutDomain(element.select("a.post-img").attr("href"))
}
override fun popularAnimeNextPageSelector(): String = "div.loadmore ul.pagination.pagination-md li:nth-last-child(2)"
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/category/new-series/page/$page/")
override fun latestUpdatesParse(response: Response): AnimesPage {
val document = response.asJsoup()
val animes = document.select(latestUpdatesSelector()).map { element ->
latestUpdatesFromElement(element)
}
val hasNextPage = latestUpdatesNextPageSelector()?.let { selector ->
if (document.select(selector).text() != ">") {
return AnimesPage(animes, false)
}
document.select(selector).first()
} != null
return AnimesPage(animes, hasNextPage)
}
override fun latestUpdatesSelector(): String = "div.section div.container div.row div.post.post-row"
override fun latestUpdatesFromElement(element: Element): SAnime = SAnime.create().apply {
title = element.select("div.post-body h3 a").text()
thumbnail_url = element.select("a.post-img img").attr("data-src").ifEmpty {
element.select("a.post-img img").attr("src")
}
setUrlWithoutDomain(element.select("a.post-img").attr("href"))
}
override fun latestUpdatesNextPageSelector(): String = "div.loadmore ul.pagination.pagination-md li:nth-last-child(2)"
// =============================== Search ===============================
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
return if (query.isNotBlank()) {
val body = FormBody.Builder()
.add("name", "$query")
.build()
POST("$baseUrl/livesearch.php", body = body)
} else {
var searchPath = ""
filters.filter { it.state != 0 }.forEach { filter ->
when (filter) {
is CategoryFilter -> searchPath = if (filter.toUriPart() == "/series/") filter.toUriPart() else "${filter.toUriPart()}page/$page"
is MovieGenreFilter -> searchPath = "/movies/genre/${filter.toUriPart()}/page/$page"
is SeriesGenreFilter -> searchPath = "/series/genre/${filter.toUriPart()}/page/$page"
else -> ""
}
}
require(searchPath.isNotEmpty()) { "Search must not be empty" }
GET(baseUrl + searchPath)
}
}
override fun searchAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup()
val path = response.request.url.encodedPath
var hasNextPage: Boolean
var animes: List<SAnime>
when {
path.startsWith("/livesearch") -> {
hasNextPage = false
animes = document.select(searchAnimeSelector()).map { element ->
searchAnimeFromElement(element)
}
}
path.startsWith("/movies/genre/") -> {
animes = document.select(movieGenreSelector()).map { element ->
movieGenreFromElement(element)
}
hasNextPage = nextPageSelector()?.let { selector ->
if (document.select(selector).text() != ">") {
return AnimesPage(animes, false)
}
document.select(selector).first()
} != null
}
path.startsWith("/series/genre/") ||
path.startsWith("/category/popular-movies/") ||
path.startsWith("/category/trending-movies/") -> {
animes = document.select(seriesGenreSelector()).map { element ->
seriesGenreFromElement(element)
}
hasNextPage = nextPageSelector()?.let { selector ->
if (document.select(selector).text() != ">") {
return AnimesPage(animes, false)
}
document.select(selector).first()
} != null
}
path.startsWith("/category/korean-movies/") || path.startsWith("/category/korean-series/") -> {
animes = document.select(koreanSelector()).map { element ->
koreanFromElement(element)
}
hasNextPage = nextPageSelector()?.let { selector ->
if (document.select(selector).text() != ">") {
return AnimesPage(animes, false)
}
document.select(selector).first()
} != null
}
path.startsWith("/category/latest-movies/") ||
path.startsWith("/category/new-series/") ||
path.startsWith("/category/latest-uploads/") -> {
animes = document.select(latestSelector()).map { element ->
latestFromElement(element)
}
hasNextPage = nextPageSelector()?.let { selector ->
if (document.select(selector).text() != ">") {
return AnimesPage(animes, false)
}
document.select(selector).first()
} != null
}
path.startsWith("/series/") -> {
animes = document.select(seriesSelector()).map { element ->
seriesFromElement(element)
}
hasNextPage = false
}
else -> {
animes = document.select(movieSelector()).map { element ->
movieFromElement(element)
}
hasNextPage = nextPageSelector()?.let { selector ->
if (document.select(selector).text() != ">") {
return AnimesPage(animes, false)
}
document.select(selector).first()
} != null
}
}
return AnimesPage(animes, hasNextPage)
}
private fun movieGenreSelector(): String = "div.container > div.row > div.col-md-4"
private fun movieGenreFromElement(element: Element): SAnime = latestUpdatesFromElement(element)
private fun seriesGenreSelector(): String = "div.row div.col-md-8 div.col-md-6"
private fun seriesGenreFromElement(element: Element): SAnime = latestUpdatesFromElement(element)
private fun koreanSelector(): String = "div.col-md-8 div.row div.col-md-6"
private fun koreanFromElement(element: Element): SAnime = latestUpdatesFromElement(element)
private fun latestSelector(): String = latestUpdatesSelector()
private fun latestFromElement(element: Element): SAnime = latestUpdatesFromElement(element)
private fun seriesSelector(): String = "div.section-row ul.list-style li"
private fun seriesFromElement(element: Element): SAnime = SAnime.create().apply {
title = element.select("a").text()
thumbnail_url = toImgUrl(element.select("a").attr("href"))
setUrlWithoutDomain(element.select("a").attr("href"))
}
private fun movieSelector(): String = "div.container div.row div.col-md-12 div.col-md-4"
private fun movieFromElement(element: Element): SAnime = SAnime.create().apply {
title = element.select("h3 a").text()
thumbnail_url = element.select("a img").attr("src")
setUrlWithoutDomain(element.select("a.post-img").attr("href"))
}
override fun searchAnimeSelector(): String = "a"
override fun searchAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
title = element.text()
thumbnail_url = toImgUrl(element.attr("href"))
setUrlWithoutDomain(element.attr("href"))
}
private fun nextPageSelector(): String = "ul.pagination.pagination-md li:nth-last-child(2)"
override fun searchAnimeNextPageSelector(): String = throw Exception("Not used")
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime = SAnime.create().apply {
title = document.select("div.page-header div.container div.row div.text-center h1").text()
description = document.select("blockquote.blockquote small").text()
genre = document.select("div.col-md-8 ul.list-style li").firstOrNull {
it.text().startsWith("Genre: ")
}?.text()?.substringAfter("Genre: ")?.replace(",", ", ")
}
// ============================== Episodes ==============================
override fun episodeListRequest(anime: SAnime): Request {
return if (anime.url.startsWith("/movie/")) {
GET(baseUrl + anime.url + "/download/", headers)
} else {
GET(baseUrl + anime.url + "/seasons/", headers)
}
}
override fun episodeListParse(response: Response): List<SEpisode> {
val path = response.request.url.encodedPath
val document = response.asJsoup()
val episodeList = mutableListOf<SEpisode>()
if (path.startsWith("/movie/")) {
episodeList.add(
SEpisode.create().apply {
name = "Movie"
episode_number = 1F
setUrlWithoutDomain(path)
},
)
} else {
var counter = 1
for (season in document.select("table.table.table-striped tbody tr").reversed()) {
val seasonUrl = season.select("td a[href]").attr("href")
val seasonSoup = client.newCall(
GET(seasonUrl, headers),
).execute().asJsoup()
val episodeTable = seasonSoup.select("table.table.table-striped")
val seasonNumber = episodeTable.select("thead th").eachText().find {
t ->
"""Season (\d+)""".toRegex().matches(t)
}?.split(" ")!![1]
for (ep in episodeTable.select("tbody tr")) {
episodeList.add(
SEpisode.create().apply {
name = "Episode S${seasonNumber}E${ep.selectFirst("td")!!.text().split(" ")!![1]}"
episode_number = counter.toFloat()
setUrlWithoutDomain(seasonUrl + "#$counter")
},
)
counter++
}
// Stop abuse
Thread.sleep(500)
}
}
return episodeList.reversed()
}
override fun episodeFromElement(element: Element): SEpisode {
val seasonNum = element.ownerDocument()!!.select("div.Title span").text()
return SEpisode.create().apply {
name = "Season $seasonNum" + "x" + element.select("td span.Num").text() + " : " + element.select("td.MvTbTtl > a").text()
episode_number = element.select("td > span.Num").text().toFloat()
setUrlWithoutDomain(element.select("td.MvTbPly > a.ClA").attr("abs:href"))
}
}
override fun episodeListSelector() = throw Exception("not used")
// ============================ Video Links =============================
override fun videoListRequest(episode: SEpisode): Request {
return if (episode.name == "Movie") {
GET(baseUrl + episode.url + "#movie", headers)
} else {
val episodeIndex = """Episode S(\d+)E(?<num>\d+)""".toRegex().matchEntire(
episode.name,
)!!.groups["num"]!!.value
GET(baseUrl + episode.url.replaceAfterLast("#", "") + episodeIndex, headers)
}
}
override fun videoListParse(response: Response): List<Video> {
val videoList = mutableListOf<Video>()
val document = response.asJsoup()
val fragment = response.request.url.fragment!!
if (fragment == "movie") {
for (res in document.select("table.table.table-striped tbody tr")) {
val url = res.select("td a").attr("href")
val name = res.select("td:not(:has(a))").text().trim()
videoList.add(Video(url, name, url))
}
} else {
val episodeIndex = fragment.toInt() - 1
val episodeList = document.select("table.table.table-striped tbody tr").toList()
for (res in episodeList[episodeIndex].select("td").reversed()) {
val url = res.select("a").attr("href")
if (url.isNotEmpty()) {
videoList.add(
Video(url, res.text().trim(), url),
)
}
}
}
require(videoList.isNotEmpty()) { "Failed to fetch videos" }
return videoList.sort()
}
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")
// ============================= Utilities ==============================
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
val codec = preferences.getString(PREF_CODEC_KEY, PREF_CODEC_KEY)!!
return this.sortedWith(
compareBy(
{ it.quality.contains(quality) },
{ it.quality.contains(codec) },
),
).reversed()
}
private fun toImgUrl(inputUrl: String): String {
val url = inputUrl.removeSuffix("/").toHttpUrl()
val pathSeg = url.encodedPathSegments.toMutableList()
pathSeg.add(1, "img")
return url.scheme +
"://" +
url.host +
"/" +
pathSeg.joinToString(separator = "/") +
".jpg"
}
companion object {
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_DEFAULT = "1080"
private const val PREF_CODEC_KEY = "preferred_codec"
private const val PREF_CODEC_DEFAULT = "265"
}
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "240p")
entryValues = arrayOf("1080", "720", "480", "360", "240")
setDefaultValue(PREF_QUALITY_DEFAULT)
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()
}
}.also(screen::addPreference)
ListPreference(screen.context).apply {
key = PREF_CODEC_KEY
title = "Preferred Video Codec"
entries = arrayOf("h265", "h264")
entryValues = arrayOf("265", "264")
setDefaultValue(PREF_CODEC_DEFAULT)
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()
}
}.also(screen::addPreference)
}
// ============================== Filters ===============================
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("NOTE: Only one works at a time"),
AnimeFilter.Separator(),
CategoryFilter(getCategoryList()),
MovieGenreFilter(getMovieGenreList()),
SeriesGenreFilter(getSeriesGenreList()),
)
private class CategoryFilter(vals: Array<Pair<String, String>>) : UriPartFilter("Category", vals)
private class MovieGenreFilter(vals: Array<Pair<String, String>>) : UriPartFilter("Movies Genre", vals)
private class SeriesGenreFilter(vals: Array<Pair<String, String>>) : UriPartFilter("Series Genres", vals)
private fun getCategoryList() = arrayOf(
Pair("None", "none"),
Pair("All series", "/series/"),
Pair("All movies", "/movies/"),
Pair("Koren movies", "/category/korean-movies/"),
Pair("Koren series", "/category/korean-series/"),
Pair("Latest movies", "/category/latest-movies/"),
Pair("Latest series", "/category/new-series/"),
Pair("Latest uploads", "/category/latest-uploads/"),
Pair("Popular movies", "/category/popular-movies/"),
Pair("Trending movies", "/category/trending-movies/"),
)
private fun getMovieGenreList() = arrayOf(
Pair("All", "all"),
Pair("Drama", "drama"),
Pair("Comedy", "comedy"),
Pair("Action", "action"),
Pair("Thriller", "thriller"),
Pair("Crime", "crime"),
Pair("Adventure", "adventure"),
Pair("Mystery", "mystery"),
Pair("Sci-Fi", "sci-Fi"),
Pair("Fantasy", "fantasy"),
Pair("Horror", "horror"),
Pair("Animation", "animation"),
Pair("Romance", "romance"),
Pair("Documentary", "documentary"),
Pair("Family", "family"),
Pair("Biography", "biography"),
Pair("History", "history"),
Pair("Music", "music"),
Pair("Sport", "sport"),
Pair("Musical", "musical"),
Pair("War", "war"),
Pair("Western", "western"),
Pair("News", "news"),
)
private fun getSeriesGenreList() = arrayOf(
Pair("All", "all"),
Pair("Drama", "drama"),
Pair("Comedy", "comedy"),
Pair("Action", "action"),
Pair("Thriller", "thriller"),
Pair("Crime", "crime"),
Pair("Adventure", "adventure"),
Pair("Mystery", "mystery"),
Pair("Sci-Fi", "sci-Fi"),
Pair("Fantasy", "fantasy"),
Pair("Horror", "horror"),
Pair("Animation", "animation"),
Pair("Romance", "romance"),
Pair("Documentary", "documentary"),
Pair("Family", "family"),
Pair("Biography", "biography"),
Pair("History", "history"),
Pair("Music", "music"),
Pair("Sport", "sport"),
Pair("Musical", "musical"),
Pair("War", "war"),
Pair("Western", "western"),
Pair("Reality-TV", "reality-TV"),
Pair("Game-Show", "game-show"),
Pair("News", "news"),
Pair("Sitcom", "sitcom"),
Pair("Talk-Show", "talk-show"),
Pair("Costume", "Costume"),
)
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
}
}

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 = 'AnimeLove'
pkgNameSuffix = 'it.animelove'
extClass = '.AnimeLove'
extVersionCode = 2
libVersion = '13'
containsNsfw = true
}
dependencies {
implementation(project(':lib-streamtape-extractor'))
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View File

@ -1,361 +0,0 @@
package eu.kanade.tachiyomi.animeextension.it.animelove
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
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.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.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
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
class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "AnimeLove"
override val baseUrl = "https://www.animelove.tv"
override val lang = "it"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/anime-in-corso/page/$page/")
override fun popularAnimeSelector(): String = "div.containerlista > div.row > div.col-6"
override fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
thumbnail_url = element.selectFirst("img")?.attr("src")
title = element.selectFirst("div.default-text")!!.text()
}
override fun popularAnimeNextPageSelector(): String = "div > ul.page-nav > li:last-child:not(:has(a.disabled))"
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/nuovi-anime/page/$page/")
override fun latestUpdatesSelector(): String = popularAnimeSelector()
override fun latestUpdatesFromElement(element: Element): SAnime = popularAnimeFromElement(element)
override fun latestUpdatesNextPageSelector(): String = popularAnimeNextPageSelector()
// =============================== Search ===============================
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val filterList = if (filters.isEmpty()) getFilterList() else filters
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
val letterFilter = filterList.find { it is LetterFilter } as LetterFilter
return when {
query.isNotBlank() -> GET("$baseUrl/cerca?q=$query")
genreFilter.state != 0 -> GET("$baseUrl/genere/${genreFilter.toUriPart()}/page/$page/")
letterFilter.state != 0 -> {
val slug = if (page == 1) "/lista-anime?alphabet=${letterFilter.toUriPart()}" else "/lista-anime/page/$page/${letterFilter.toUriPart()}/"
GET(baseUrl + slug)
}
else -> popularAnimeRequest(page)
}
}
override fun searchAnimeParse(response: Response): AnimesPage {
if (response.request.url.encodedPath != "/cerca") {
return super.searchAnimeParse(response)
}
val animeList = response.asJsoup()
.select(searchAnimeSelectorSearch())
.map(::searchAnimeFromElementSearch)
return AnimesPage(animeList, false)
}
override fun searchAnimeSelector(): String = popularAnimeSelector()
private fun searchAnimeSelectorSearch(): String = "div.col-md-8 > div.card > div.card-body > div.row > div.col-6"
override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
private fun searchAnimeFromElementSearch(element: Element): SAnime = SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
thumbnail_url = element.selectFirst("img")?.attr("src")
title = element.selectFirst("p.card-text")!!.text()
}
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
// ============================== Filters ===============================
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
AnimeFilter.Header("La ricerca testuale ignora i filtri"),
GenreFilter(),
LetterFilter(),
)
private class GenreFilter : UriPartFilter(
"Generi",
arrayOf(
Pair("<Selezionare>", ""),
Pair("Action", "Action"),
Pair("Adventure", "Adventure"),
Pair("Avant GardeAvant Garde", "Avant-GardeAvant-Garde"),
Pair("Award WiningAward Wining", "Award-WiningAward-Wining"),
Pair("Bender", "Bender"),
Pair("Boys LoveBoys Love", "Boys-LoveBoys-Love"),
Pair("Cars", "Cars"),
Pair("Comedy", "Comedy"),
Pair("Dantasy", "Dantasy"),
Pair("Dementia", "Dementia"),
Pair("Demons", "Demons"),
Pair("Detective", "Detective"),
Pair("Drama", "Drama"),
Pair("Drammatico", "Drammatico"),
Pair("Ecchi", "Ecchi"),
Pair("Erotic", "Erotic"),
Pair("Erotica", "Erotica"),
Pair("Fantasy", "Fantasy"),
Pair("Fighting", "Fighting"),
Pair("Game", "Game"),
Pair("Gender", "Gender"),
Pair("Girls LoveGirls Love", "Girls-LoveGirls-Love"),
Pair("Goofy", "Goofy"),
Pair("Gourmet", "Gourmet"),
Pair("Harem", "Harem"),
Pair("Hentai", "Hentai"),
Pair("Historical", "Historical"),
Pair("Horror", "Horror"),
Pair("Josei", "Josei"),
Pair("Kids", "Kids"),
Pair("Lifestyle", "Lifestyle"),
Pair("Lolicon", "Lolicon"),
Pair("Magic", "Magic"),
Pair("Martial Arts", "Martial-Arts"),
Pair("Mecha", "Mecha"),
Pair("Military", "Military"),
Pair("Music", "Music"),
Pair("Mystery", "Mystery"),
Pair("N/A", "A"),
Pair("null", "null"),
Pair("Parody", "Parody"),
Pair("Police", "Police"),
Pair("Psychological", "Psychological"),
Pair("Romance", "Romance"),
Pair("SAction", "SAction"),
Pair("Samurai", "Samurai"),
Pair("School", "School"),
Pair("Sci-fi", "Sci-fi"),
Pair("Sci-FiSci-Fi", "Sci-FiSci-Fi"),
Pair("Seinen", "Seinen"),
Pair("Sentimental", "Sentimental"),
Pair("Shoujo", "Shoujo"),
Pair("Shoujo Ai", "Shoujo-Ai"),
Pair("Shounen", "Shounen"),
Pair("Shounen Ai", "Shounen-Ai"),
Pair("Slice of Life", "Slice-of-Life"),
Pair("Slice of LifeSlice of Life", "Slice-of-LifeSlice-of-Life"),
Pair("Smut", "Smut"),
Pair("Space", "Space"),
Pair("Splatter", "Splatter"),
Pair("Sport", "Sport"),
Pair("Super Power", "Super-Power"),
Pair("Supernatural", "Supernatural"),
Pair("Suspense", "Suspense"),
Pair("Tamarro", "Tamarro"),
Pair("Thriller", "Thriller"),
Pair("Vampire", "Vampire"),
Pair("Yaoi", "Yaoi"),
Pair("Yuri", "Yuri"),
),
)
private class LetterFilter : UriPartFilter(
"Lettera",
arrayOf(
Pair("<Selezionare>", ""),
Pair("A", "A"),
Pair("B", "B"),
Pair("C", "C"),
Pair("D", "D"),
Pair("E", "E"),
Pair("F", "F"),
Pair("G", "G"),
Pair("H", "H"),
Pair("I", "I"),
Pair("J", "J"),
Pair("K", "K"),
Pair("L", "L"),
Pair("M", "M"),
Pair("N", "N"),
Pair("O", "O"),
Pair("P", "P"),
Pair("Q", "Q"),
Pair("R", "R"),
Pair("S", "S"),
Pair("T", "T"),
Pair("U", "U"),
Pair("V", "V"),
Pair("W", "W"),
Pair("X", "X"),
Pair("Y", "Y"),
Pair("Z", "Z"),
),
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime = SAnime.create().apply {
title = document.selectFirst("div.card-body > p:contains(TITOLO:)")?.ownText() ?: ""
thumbnail_url = document.selectFirst("div.card-body > div > img")?.attr("src") ?: ""
author = document.selectFirst("div.card-body > p:contains(STUDIO:)")?.ownText() ?: ""
status = document.selectFirst("div.card-body > p:contains(STATO:)")?.let {
parseStatus(it.ownText())
} ?: SAnime.UNKNOWN
genre = document.selectFirst("div.card-body > p:contains(GENERI:)")?.ownText() ?: ""
description = buildString {
append(document.selectFirst("div.card-body > p:contains(TRAMA:) ~ p")?.text() ?: "")
append("\n\n")
append(document.selectFirst("div.card-body > p:contains(TIPO:)")?.text() ?: "")
append("\n")
append(document.selectFirst("div.card-body > p:contains(ANNO)")?.text() ?: "")
}
}
// ============================== Episodes ==============================
override fun episodeListParse(response: Response): List<SEpisode> = super.episodeListParse(response).reversed()
override fun episodeListSelector(): String = "div.card ul.page-nav-list-episodi > li"
override fun episodeFromElement(element: Element): SEpisode = SEpisode.create().apply {
name = "Episodi ${element.text()}"
episode_number = element.selectFirst("span")?.text()?.toFloatOrNull() ?: 0F
setUrlWithoutDomain(element.selectFirst("a[href]")!!.attr("href"))
}
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val videoList = mutableListOf<Video>()
val videosREGEX = Regex("""\('\.video-container'\)\.append\((.*?)\);""")
val script = document.selectFirst("script:containsData(.video-container)")
if (script == null) {
val video = document.selectFirst("div.video-container source")?.attr("src")
if (video != null) {
videoList.add(extractAnimeLove(video))
}
} else {
videosREGEX.findAll(script.data()).forEach { videoSource ->
val url = videoSource.groupValues[1].substringAfter("src=\"").substringBefore("\"")
when {
url.contains("animelove.tv") -> {
videoList.add(extractAnimeLove(url))
}
url.contains("streamtape") -> {
StreamTapeExtractor(client).videoFromUrl(url)?.let {
videoList.add(it)
}
}
}
}
}
require(videoList.isNotEmpty()) { "Failed to fetch videos" }
return videoList
}
override fun videoListSelector(): String = throw Exception("Not Used")
override fun videoFromElement(element: Element): Video = throw Exception("Not Used")
override fun videoUrlParse(document: Document): String = throw Exception("Not Used")
// ============================= Utilities ==============================
private fun extractAnimeLove(videoUrl: String): Video {
val redirected = client.newCall(GET(videoUrl)).execute().request
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", redirected.url.host)
.build()
return Video(
redirected.url.toString(),
"AnimeLove",
redirected.url.toString(),
headers = videoHeaders,
)
}
override fun List<Video>.sort(): List<Video> {
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
return this.sortedWith(
compareBy { it.quality.contains(server, true) },
).reversed()
}
private fun parseStatus(statusString: String): Int {
return when (statusString) {
"In Corso" -> SAnime.ONGOING
"Terminato" -> SAnime.COMPLETED
else -> SAnime.UNKNOWN
}
}
companion object {
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_DEFAULT = "AnimeLove"
}
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = PREF_SERVER_KEY
title = "Preferred server"
entries = arrayOf("AnimeLove", "StreamTape")
entryValues = arrayOf("AnimeLove", "StreamTape")
setDefaultValue(PREF_SERVER_DEFAULT)
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()
}
}.also(screen::addPreference)
}
}