diff --git a/multisrc/overrides/datalifeengine/frenchanime/additional.gradle b/multisrc/overrides/datalifeengine/frenchanime/additional.gradle new file mode 100644 index 0000000000..4a22e1a0b3 --- /dev/null +++ b/multisrc/overrides/datalifeengine/frenchanime/additional.gradle @@ -0,0 +1,12 @@ +dependencies { + implementation(project(':lib-dood-extractor')) + implementation(project(':lib-vido-extractor')) + implementation(project(':lib-uqload-extractor')) + implementation(project(':lib-vudeo-extractor')) + implementation(project(':lib-streamhidevid-extractor')) + implementation(project(':lib-upstream-extractor')) + implementation(project(':lib-streamvid-extractor')) + implementation(project(':lib-sibnet-extractor')) + implementation(project(':lib-okru-extractor')) + implementation(project(':lib-streamhub-extractor')) +} \ No newline at end of file diff --git a/src/fr/frenchanime/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/datalifeengine/frenchanime/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from src/fr/frenchanime/res/mipmap-hdpi/ic_launcher.png rename to multisrc/overrides/datalifeengine/frenchanime/res/mipmap-hdpi/ic_launcher.png diff --git a/src/fr/frenchanime/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/datalifeengine/frenchanime/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from src/fr/frenchanime/res/mipmap-mdpi/ic_launcher.png rename to multisrc/overrides/datalifeengine/frenchanime/res/mipmap-mdpi/ic_launcher.png diff --git a/src/fr/frenchanime/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/datalifeengine/frenchanime/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from src/fr/frenchanime/res/mipmap-xhdpi/ic_launcher.png rename to multisrc/overrides/datalifeengine/frenchanime/res/mipmap-xhdpi/ic_launcher.png diff --git a/src/fr/frenchanime/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/datalifeengine/frenchanime/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from src/fr/frenchanime/res/mipmap-xxhdpi/ic_launcher.png rename to multisrc/overrides/datalifeengine/frenchanime/res/mipmap-xxhdpi/ic_launcher.png diff --git a/src/fr/frenchanime/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/datalifeengine/frenchanime/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from src/fr/frenchanime/res/mipmap-xxxhdpi/ic_launcher.png rename to multisrc/overrides/datalifeengine/frenchanime/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/src/fr/frenchanime/res/web_hi_res_512.png b/multisrc/overrides/datalifeengine/frenchanime/res/web_hi_res_512.png similarity index 100% rename from src/fr/frenchanime/res/web_hi_res_512.png rename to multisrc/overrides/datalifeengine/frenchanime/res/web_hi_res_512.png diff --git a/multisrc/overrides/datalifeengine/frenchanime/src/FrenchAnime.kt b/multisrc/overrides/datalifeengine/frenchanime/src/FrenchAnime.kt new file mode 100644 index 0000000000..c10505f193 --- /dev/null +++ b/multisrc/overrides/datalifeengine/frenchanime/src/FrenchAnime.kt @@ -0,0 +1,119 @@ +package eu.kanade.tachiyomi.animeextension.fr.frenchanime + +import eu.kanade.tachiyomi.animesource.model.SEpisode +import eu.kanade.tachiyomi.animesource.model.Video +import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor +import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor +import eu.kanade.tachiyomi.lib.sibnetextractor.SibnetExtractor +import eu.kanade.tachiyomi.lib.streamhidevidextractor.StreamHideVidExtractor +import eu.kanade.tachiyomi.lib.streamhubextractor.StreamHubExtractor +import eu.kanade.tachiyomi.lib.streamvidextractor.StreamVidExtractor +import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor +import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor +import eu.kanade.tachiyomi.lib.vidoextractor.VidoExtractor +import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor +import eu.kanade.tachiyomi.multisrc.datalifeengine.DataLifeEngine +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import rx.Observable +import java.lang.Exception + +class FrenchAnime : DataLifeEngine( + "French Anime", + "https://french-anime.com", + "fr", +) { + + override val categories = arrayOf( + Pair("", ""), + Pair("Animes VF", "/animes-vf/"), + Pair("Animes VOSTFR", "/animes-vostfr/"), + Pair("Films VF et VOSTFR", "/films-vf-vostfr/"), + ) + + override val genres = arrayOf( + Pair("", ""), + Pair("Action", "/genre/action/"), + Pair("Aventure", "/genre/aventure/"), + Pair("Arts martiaux", "/genre/arts-martiaux/"), + Pair("Combat", "/genre/combat/"), + Pair("Comédie", "/genre/comedie/"), + Pair("Drame", "/genre/drame/"), + Pair("Epouvante", "/genre/epouvante/"), + Pair("Fantastique", "/genre/fantastique/"), + Pair("Fantasy", "/genre/fantasy/"), + Pair("Mystère", "/genre/mystere/"), + Pair("Romance", "/genre/romance/"), + Pair("Shonen", "/genre/shonen/"), + Pair("Surnaturel", "/genre/surnaturel/"), + Pair("Sci-Fi", "/genre/sci-fi/"), + Pair("School life", "/genre/school-life/"), + Pair("Ninja", "/genre/ninja/"), + Pair("Seinen", "/genre/seinen/"), + Pair("Horreur", "/genre/horreur/"), + Pair("Tranche de vie", "/genre/tranchedevie/"), + Pair("Psychologique", "/genre/psychologique/"), + ) + + // ============================== Popular =============================== + + override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/animes-vostfr/page/$page/") + + // ============================== Episodes ============================== + + override fun episodeListParse(response: Response): List { + val document = response.asJsoup() + val episodeList = mutableListOf() + + val epsData = document.selectFirst("div.eps")?.text() ?: return emptyList() + epsData.split(" ").filter { it.isNotBlank() }.forEach { + val data = it.split("!", limit = 2) + val episode = SEpisode.create() + episode.episode_number = data[0].toFloatOrNull() ?: 0F + episode.name = "Episode ${data[0]}" + episode.url = data[1] + episodeList.add(episode) + } + + return episodeList.reversed() + } + + override fun episodeListSelector(): String = throw Exception("not used") + + override fun episodeFromElement(element: Element): SEpisode = throw Exception("not used") + + // ============================ Video Links ============================= + + override fun fetchVideoList(episode: SEpisode): Observable> { + val list = episode.url.split(",").filter { it.isNotBlank() }.parallelCatchingFlatMap { + with(it) { + when { + contains("dood") -> DoodExtractor(client).videosFromUrl(this) + contains("upstream") -> UpstreamExtractor(client).videosFromUrl(this) + contains("vudeo") -> VudeoExtractor(client).videosFromUrl(this) + contains("uqload") -> UqloadExtractor(client).videosFromUrl(this) + contains("guccihide") || + contains("streamhide") -> StreamHideVidExtractor(client).videosFromUrl(this) + contains("streamvid") -> StreamVidExtractor(client).videosFromUrl(this) + contains("vido") -> VidoExtractor(client).videosFromUrl(this) + contains("sibnet") -> SibnetExtractor(client).videosFromUrl(this) + contains("ok.ru") -> OkruExtractor(client).videosFromUrl(this) + contains("streamhub.gg") -> StreamHubExtractor(client).videosFromUrl(this) + else -> emptyList() + } + } + }.sort() + if (list.isEmpty()) throw Exception("no player found") + return Observable.just(list) + } + + override fun videoFromElement(element: Element): Video = throw Exception("Not Used") + + override fun videoListSelector(): String = throw Exception("Not Used") + + override fun videoUrlParse(document: Document): String = throw Exception("Not Used") +} diff --git a/multisrc/overrides/datalifeengine/wiflix/additional.gradle b/multisrc/overrides/datalifeengine/wiflix/additional.gradle new file mode 100644 index 0000000000..bb870e92d5 --- /dev/null +++ b/multisrc/overrides/datalifeengine/wiflix/additional.gradle @@ -0,0 +1,11 @@ +dependencies { + implementation(project(':lib-dood-extractor')) + implementation(project(':lib-vido-extractor')) + implementation(project(':lib-uqload-extractor')) + implementation(project(':lib-streamdav-extractor')) + // ? implementation(project(':lib-waaw1-extractor')) + implementation(project(':lib-vudeo-extractor')) + implementation(project(':lib-streamhidevid-extractor')) + implementation(project(':lib-upstream-extractor')) + implementation(project(':lib-voe-extractor')) +} \ No newline at end of file diff --git a/multisrc/overrides/datalifeengine/wiflix/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/datalifeengine/wiflix/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..dfbf1d5d77 Binary files /dev/null and b/multisrc/overrides/datalifeengine/wiflix/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/datalifeengine/wiflix/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/datalifeengine/wiflix/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..747117f8dd Binary files /dev/null and b/multisrc/overrides/datalifeengine/wiflix/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/datalifeengine/wiflix/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/datalifeengine/wiflix/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..47bce6e03b Binary files /dev/null and b/multisrc/overrides/datalifeengine/wiflix/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/datalifeengine/wiflix/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/datalifeengine/wiflix/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..a92a1fa384 Binary files /dev/null and b/multisrc/overrides/datalifeengine/wiflix/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/datalifeengine/wiflix/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/datalifeengine/wiflix/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..d3d49b0e95 Binary files /dev/null and b/multisrc/overrides/datalifeengine/wiflix/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/datalifeengine/wiflix/res/web_hi_res_512.png b/multisrc/overrides/datalifeengine/wiflix/res/web_hi_res_512.png new file mode 100644 index 0000000000..1aacf919df Binary files /dev/null and b/multisrc/overrides/datalifeengine/wiflix/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/datalifeengine/wiflix/src/Wiflix.kt b/multisrc/overrides/datalifeengine/wiflix/src/Wiflix.kt new file mode 100644 index 0000000000..af2469d76b --- /dev/null +++ b/multisrc/overrides/datalifeengine/wiflix/src/Wiflix.kt @@ -0,0 +1,104 @@ +package eu.kanade.tachiyomi.animeextension.fr.wiflix + +import eu.kanade.tachiyomi.animesource.model.SEpisode +import eu.kanade.tachiyomi.animesource.model.Video +import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor +import eu.kanade.tachiyomi.lib.streamdavextractor.StreamDavExtractor +import eu.kanade.tachiyomi.lib.streamhidevidextractor.StreamHideVidExtractor +import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor +import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor +import eu.kanade.tachiyomi.lib.vidoextractor.VidoExtractor +import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor +import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor +import eu.kanade.tachiyomi.multisrc.datalifeengine.DataLifeEngine +import eu.kanade.tachiyomi.network.GET +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import rx.Observable + +class Wiflix : DataLifeEngine( + "Wiflix", + "https://wiflix.voto", + "fr", +) { + + override val categories = arrayOf( + Pair("", ""), + Pair("Séries", "/serie-en-streaming/"), + Pair("Films", "/film-en-streaming/"), + ) + + override val genres = arrayOf( + Pair("", ""), + Pair("Action", "/film-en-streaming/action/"), + Pair("Animation", "/film-en-streaming/animation/"), + Pair("Arts Martiaux", "/film-en-streaming/arts-martiaux/"), + Pair("Aventure", "/film-en-streaming/aventure/"), + Pair("Biopic", "/film-en-streaming/biopic/"), + Pair("Comédie", "/film-en-streaming/comedie/"), + Pair("Comédie Dramatique", "/film-en-streaming/comedie-dramatique/"), + Pair("Épouvante Horreur", "/film-en-streaming/horreur/"), + Pair("Drame", "/film-en-streaming/drame/"), + Pair("Documentaire", "/film-en-streaming/documentaire/"), + Pair("Espionnage", "/film-en-streaming/espionnage/"), + Pair("Famille", "/film-en-streaming/famille/"), + Pair("Fantastique", "/film-en-streaming/fantastique/"), + Pair("Guerre", "/film-en-streaming/guerre/"), + Pair("Historique", "/film-en-streaming/historique/"), + Pair("Musical", "/film-en-streaming/musical/"), + Pair("Policier", "/film-en-streaming/policier/"), + Pair("Romance", "/film-en-streaming/romance/"), + Pair("Science-Fiction", "/film-en-streaming/science-fiction/"), + Pair("Spectacles", "/film-en-streaming/spectacles/"), + Pair("Thriller", "/film-en-streaming/thriller/"), + Pair("Western", "/film-en-streaming/western/"), + ) + + // ============================== Popular =============================== + + override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/serie-en-streaming/page/$page/") + + // ============================== Episodes ============================== + + override fun episodeListSelector(): String = ".hostsblock div:has(a[href*=https])" + + override fun episodeListParse(response: Response): List = super.episodeListParse(response).sort() + + override fun episodeFromElement(element: Element): SEpisode = SEpisode.create().apply { + episode_number = element.className().filter { it.isDigit() }.toFloat() + name = "Episode ${episode_number.toInt()}" + scanlator = if (element.className().contains("vf")) "VF" else "VOSTFR" + url = element.select("a").joinToString(",") { it.attr("href").removePrefix("/vd.php?u=") } + } + + // ============================ Video Links ============================= + + override fun fetchVideoList(episode: SEpisode): Observable> { + val list = episode.url.split(",").filter { it.isNotBlank() }.parallelCatchingFlatMap { + with(it) { + when { + contains("doods.pro") -> DoodExtractor(client).videosFromUrl(this) + contains("vido.lol") -> VidoExtractor(client).videosFromUrl(this) + contains("uqload.co") -> UqloadExtractor(client).videosFromUrl(this) + contains("waaw1.tv") -> emptyList() + contains("vudeo.co") -> VudeoExtractor(client).videosFromUrl(this) + contains("streamvid.net") -> StreamHideVidExtractor(client).videosFromUrl(this) + contains("upstream.to") -> UpstreamExtractor(client).videosFromUrl(this) + contains("streamdav.com") -> StreamDavExtractor(client).videosFromUrl(this) + contains("voe.sx") -> listOfNotNull(VoeExtractor(client).videoFromUrl(this)) + else -> emptyList() + } + } + }.sort() + if (list.isEmpty()) throw Exception("no player found") + return Observable.just(list) + } + + override fun videoFromElement(element: Element): Video = throw Exception("Not Used") + + override fun videoListSelector(): String = throw Exception("Not Used") + + override fun videoUrlParse(document: Document): String = throw Exception("Not Used") +} diff --git a/src/fr/frenchanime/src/eu/kanade/tachiyomi/animeextension/fr/frenchanime/FrenchAnime.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/datalifeengine/DataLifeEngine.kt similarity index 52% rename from src/fr/frenchanime/src/eu/kanade/tachiyomi/animeextension/fr/frenchanime/FrenchAnime.kt rename to multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/datalifeengine/DataLifeEngine.kt index f46cd418db..412ffa1e56 100644 --- a/src/fr/frenchanime/src/eu/kanade/tachiyomi/animeextension/fr/frenchanime/FrenchAnime.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/datalifeengine/DataLifeEngine.kt @@ -1,13 +1,8 @@ -package eu.kanade.tachiyomi.animeextension.fr.frenchanime +package eu.kanade.tachiyomi.multisrc.datalifeengine import android.app.Application -import android.content.SharedPreferences import androidx.preference.ListPreference import androidx.preference.PreferenceScreen -import eu.kanade.tachiyomi.animeextension.fr.frenchanime.extractors.StreamHideExtractor -import eu.kanade.tachiyomi.animeextension.fr.frenchanime.extractors.StreamVidExtractor -import eu.kanade.tachiyomi.animeextension.fr.frenchanime.extractors.UpstreamExtractor -import eu.kanade.tachiyomi.animeextension.fr.frenchanime.extractors.VidoExtractor import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.AnimeFilterList @@ -15,11 +10,6 @@ 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.okruextractor.OkruExtractor -import eu.kanade.tachiyomi.lib.sibnetextractor.SibnetExtractor -import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor -import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.asObservableSuccess @@ -28,7 +18,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking -import kotlinx.serialization.json.Json import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient @@ -40,30 +29,21 @@ import org.jsoup.nodes.Element import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy -class FrenchAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() { - - override val name = "French Anime" - - override val baseUrl = "https://french-anime.com" - - override val lang = "fr" +abstract class DataLifeEngine( + override val name: String, + override val baseUrl: String, + override val lang: String, +) : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override val supportsLatest = false override val client: OkHttpClient = network.cloudflareClient - private val json: Json by injectLazy() - - private val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) - } + private val preferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } // ============================== Popular =============================== - override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/animes-vostfr/page/$page/") - override fun popularAnimeSelector(): String = "div#dle-content > div.mov" override fun popularAnimeNextPageSelector(): String = "span.navigation > span:not(.nav_ext) + a" @@ -88,6 +68,7 @@ class FrenchAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() { // =============================== Search =============================== + // TODO: Implement the *actual* search filters from : https://${baseUrl}/index.php?do=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 @@ -132,54 +113,30 @@ class FrenchAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() { // ============================== Filters =============================== + abstract val categories: Array> + + abstract val genres: Array> + override fun getFilterList(): AnimeFilterList = AnimeFilterList( AnimeFilter.Header("La recherche de texte ignore les filtres"), - SubPageFilter(), - GenreFilter(), + SubPageFilter(categories), + GenreFilter(genres), ) - private class SubPageFilter : UriPartFilter( + private class SubPageFilter(categories: Array>) : UriPartFilter( "Catégories", - arrayOf( - Pair("", ""), - Pair("Animes VF", "/animes-vf/"), - Pair("Animes VOSTFR", "/animes-vostfr/"), - Pair("Films VF et VOSTFR", "/films-vf-vostfr/"), - ), + categories, ) - private class GenreFilter : UriPartFilter( - "Animes par genre", - arrayOf( - Pair("", ""), - Pair("Action", "/genre/action/"), - Pair("Aventure", "/genre/aventure/"), - Pair("Arts martiaux", "/genre/arts-martiaux/"), - Pair("Combat", "/genre/combat/"), - Pair("Comédie", "/genre/comedie/"), - Pair("Drame", "/genre/drame/"), - Pair("Epouvante", "/genre/epouvante/"), - Pair("Fantastique", "/genre/fantastique/"), - Pair("Fantasy", "/genre/fantasy/"), - Pair("Mystère", "/genre/mystere/"), - Pair("Romance", "/genre/romance/"), - Pair("Shonen", "/genre/shonen/"), - Pair("Surnaturel", "/genre/surnaturel/"), - Pair("Sci-Fi", "/genre/sci-fi/"), - Pair("School life", "/genre/school-life/"), - Pair("Ninja", "/genre/ninja/"), - Pair("Seinen", "/genre/seinen/"), - Pair("Horreur", "/genre/horreur/"), - Pair("Tranche de vie", "/genre/tranchedevie/"), - Pair("Psychologique", "/genre/psychologique/"), - ), + private class GenreFilter(genres: Array>) : UriPartFilter( + "Genres", + genres, ) private open class UriPartFilter(displayName: String, val vals: Array>) : AnimeFilter.Select(displayName, vals.map { it.first }.toTypedArray()) { fun toUriPart() = vals[state].second } - // =========================== Anime Details ============================ override fun fetchAnimeDetails(anime: SAnime): Observable { @@ -194,108 +151,26 @@ class FrenchAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() { private fun animeDetailsParse(response: Response, baseAnime: SAnime): SAnime { val document = response.asJsoup() - val anime = SAnime.create() - - anime.title = baseAnime.title - anime.thumbnail_url = baseAnime.thumbnail_url - anime.description = document.selectFirst("div.mov-desc span[itemprop=description]")?.text() ?: "" - anime.genre = document.select("div.mov-desc span[itemprop=genre] a").joinToString(", ") { it.text() } - return anime - } - - // ============================== Episodes ============================== - - override fun episodeListParse(response: Response): List { - val document = response.asJsoup() - val episodeList = mutableListOf() - - val epsData = document.selectFirst("div.eps")?.text() ?: return emptyList() - epsData.split(" ").filter { it.isNotBlank() }.forEach { - val data = it.split("!", limit = 2) - val episode = SEpisode.create() - episode.episode_number = data[0].toFloatOrNull() ?: 0F - episode.name = "Episode ${data[0]}" - episode.url = data[1] - episodeList.add(episode) - } - - return episodeList.reversed() - } - - override fun episodeListSelector(): String = throw Exception("Not used") - - override fun episodeFromElement(element: Element): SEpisode = throw Exception("Not used") - - // ============================ Video Links ============================= - - override fun fetchVideoList(episode: SEpisode): Observable> { - val videoList = mutableListOf