Fix(hi/AnimeWorld India): Complete revamp (#1796)

This commit is contained in:
Quickdev
2023-06-29 23:42:58 +05:30
committed by GitHub
parent 73ed49b829
commit 8647eefe09
18 changed files with 551 additions and 252 deletions

View File

@ -2,10 +2,10 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'AnimeWorld (experimental)'
pkgNameSuffix = 'hi.animeWorld'
extClass = '.AnimeWorld'
extVersionCode = 20
extName = 'AnimeWorld India'
pkgNameSuffix = 'all.animeworldindia'
extClass = '.AnimeWorldIndiaFactory'
extVersionCode = 1
libVersion = '13'
}
@ -14,4 +14,4 @@ dependencies {
}
apply from: "$rootDir/common.gradle"
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,295 @@
package eu.kanade.tachiyomi.animeextension.all.animeworldindia
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.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.streamsbextractor.StreamSBExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
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 uy.kohesive.injekt.injectLazy
import java.lang.Exception
open class AnimeWorldIndia(
final override val lang: String,
private val language: String,
) : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "AnimeWorld India"
override val baseUrl = "https://anime-world.in"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val json: Json by injectLazy()
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// =============================== Search ===============================
override fun searchAnimeNextPageSelector(): String = "ul.page-numbers li:has(span[aria-current=\"page\"]) + li"
override fun searchAnimeSelector(): String = "div.col-span-1"
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
var thumbnail = element.selectFirst("img")!!.attr("src")
if (!thumbnail.contains("https")) {
thumbnail = "$baseUrl/$thumbnail"
}
anime.thumbnail_url = thumbnail
anime.title = element.select("div.font-medium.line-clamp-2.mb-3").text()
return anime
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val searchParams = AnimeWorldIndiaFilters().getSearchParams(filters)
return GET("$baseUrl/advanced-search/page/$page/?s_keyword=$query&s_lang=$lang$searchParams")
}
override fun getFilterList() = AnimeWorldIndiaFilters().filters
// ============================== Popular ===============================
override fun popularAnimeNextPageSelector(): String = searchAnimeNextPageSelector()
override fun popularAnimeSelector(): String = searchAnimeSelector()
override fun popularAnimeFromElement(element: Element): SAnime = searchAnimeFromElement(element)
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/advanced-search/page/$page/?s_keyword=&s_type=all&s_status=all&s_lang=$lang&s_sub_type=all&s_year=all&s_orderby=viewed&s_genre=")
// =============================== Latest ===============================
override fun latestUpdatesNextPageSelector(): String = searchAnimeNextPageSelector()
override fun latestUpdatesSelector(): String = searchAnimeSelector()
override fun latestUpdatesFromElement(element: Element): SAnime = searchAnimeFromElement(element)
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/advanced-search/page/$page/?s_keyword=&s_type=all&s_status=all&s_lang=$lang&s_sub_type=all&s_year=all&s_orderby=update&s_genre=")
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create().apply {
genre = document.select("span.leading-6 a[class~=border-opacity-30]").joinToString(", ") { it.text() }
description = document.select("span.block.w-full.max-h-24.overflow-scroll.my-3.overflow-x-hidden.text-xs.text-gray-200").text()
author = document.select("span.leading-6 a[href*=\"producer\"]:first-child").text()
artist = document.select("span.leading-6 a[href*=\"studio\"]:first-child").text()
status = parseStatus(document)
}
return anime
}
private val selector = "ul li:has(div.w-1.h-1.bg-gray-500.rounded-full) + li"
private fun parseStatus(document: Document): Int {
return when (document.select("$selector a:not(:contains(Ep))").text()) {
"Movie" -> SAnime.COMPLETED
else -> {
val episodeString = document.select("$selector a:not(:contains(TV))").text().drop(3).split("/")
if (episodeString[0].trim().compareTo(episodeString[1].trim()) == 0) {
SAnime.COMPLETED
} else SAnime.ONGOING
}
}
}
// ============================== Episodes ==============================
override fun episodeListSelector() = throw Exception("not used")
override fun episodeFromElement(element: Element): SEpisode = throw Exception("not used")
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val episodeList = mutableListOf<SEpisode>()
val seasonsJson = json.decodeFromString<JsonArray>(
document.html()
.substringAfter("var season_list = ")
.substringBefore("var season_label =")
.trim().dropLast(1),
)
var seasonNumber = 1
var episodeNumber = 1f
val isAnimeCompleted = parseStatus(document) == SAnime.COMPLETED
seasonsJson.forEach { season ->
val seasonName = if (seasonsJson.size == 1) "" else "Season $seasonNumber"
val episodesJson = season.jsonObject["episodes"]!!.jsonObject[language]!!.jsonArray.reversed()
episodesJson.forEach {
val episodeTitle = it.jsonObject["metadata"]!!
.jsonObject["title"]!!
.toString()
.drop(1).dropLast(1)
val epNum = it.jsonObject["metadata"]!!
.jsonObject["number"]!!
.toString().drop(1)
.dropLast(1).toInt()
val episodeName = if (isAnimeCompleted && seasonsJson.size == 1 && episodesJson.size == 1) {
"Movie"
} else if (episodeTitle.isNotEmpty()) {
"$seasonName Ep $epNum - $episodeTitle"
} else {
"$seasonName - Episode $epNum"
}
val episode = SEpisode.create().apply {
name = episodeName
episode_number = episodeNumber
setUrlWithoutDomain(url = "$baseUrl/wp-json/kiranime/v1/episode?id=${it.jsonObject["id"]}")
date_upload = it.jsonObject["metadata"]
?.jsonObject?.get("released")?.toString()
?.drop(1)?.dropLast(1)
?.toLong()?.times(1000) ?: 0L
}
episodeNumber += 1
episodeList.add(episode)
}
seasonNumber += 1
}
return episodeList.reversed()
}
// ============================ Video Links =============================
override fun videoFromElement(element: Element): Video = throw Exception("not used")
override fun videoUrlParse(document: Document) = throw Exception("not used")
override fun videoListSelector() = throw Exception("not used")
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val videoList = mutableListOf<Video>()
val playersIndex = document.html().lastIndexOf("players")
val documentTrimmed = document.html().substring(playersIndex)
.substringAfter("players\":")
.substringBefore(",\"noplayer\":")
.trim()
val playerJson = json.decodeFromString<JsonArray>(documentTrimmed)
val filterStreams = playerJson.filter {
it.jsonObject["type"].toString()
.drop(1).dropLast(1)
.compareTo("stream") == 0
}
val filterLanguages = filterStreams.filter {
it.jsonObject["language"].toString()
.drop(1).dropLast(1)
.compareTo(language) == 0
}
// Abyss - Server does not work
val abyssStreams = filterLanguages.filter {
it.jsonObject["server"].toString()
.drop(1).dropLast(1)
.compareTo("Abyss") == 0
}
// MyStream
filterLanguages.filter {
it.jsonObject["server"].toString()
.drop(1).dropLast(1)
.compareTo("Mystream") == 0
}.forEach {
val url = it.jsonObject["url"].toString().drop(1).dropLast(1)
val videos = MyStreamExtractor().videosFromUrl(url, headers)
videoList.addAll(videos)
}
// StreamSB
filterLanguages.filter {
it.jsonObject["server"].toString()
.drop(1).dropLast(1)
.compareTo("Streamsb") == 0
}.forEach {
val url = "https://cloudemb.com/e/${it.jsonObject["url"].toString()
.drop(1).dropLast(1)
.substringAfter("id=")}.html"
val videos = StreamSBExtractor(client).videosFromUrl(url, headers)
videoList.addAll(videos)
}
return videoList
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "1080")!!
val server = preferences.getString("preferred_server", "MyStream")!!
return sortedWith(
compareBy(
{ it.quality.lowercase().contains(quality.lowercase()) },
{ it.quality.lowercase().contains(server.lowercase()) },
),
).reversed()
}
// ============================ Preferences =============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "240p")
entryValues = arrayOf("1080", "720", "480", "360", "240")
setDefaultValue("1080")
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 = "preferred_server"
title = "Preferred server"
entries = arrayOf("MyStream", "StreamSB")
entryValues = arrayOf("MyStream", "StreamSB")
setDefaultValue("MyStream")
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)
}
}

View File

@ -0,0 +1,17 @@
package eu.kanade.tachiyomi.animeextension.all.animeworldindia
import eu.kanade.tachiyomi.animesource.AnimeSourceFactory
class AnimeWorldIndiaFactory : AnimeSourceFactory {
override fun createSources() = listOf(
AnimeWorldIndia("bn", "bengali"),
AnimeWorldIndia("en", "english"),
AnimeWorldIndia("hi", "hindi"),
AnimeWorldIndia("ja", "japanese"),
AnimeWorldIndia("ml", "malayalam"),
AnimeWorldIndia("mr", "marathi"),
AnimeWorldIndia("ta", "tamil"),
AnimeWorldIndia("te", "telugu"),
)
}

View File

@ -0,0 +1,181 @@
package eu.kanade.tachiyomi.animeextension.all.animeworldindia
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
class AnimeWorldIndiaFilters {
private data class StringQuery(val name: String, val query: String)
private class TypeList(types: Array<String>) : AnimeFilter.Select<String>("Type", types)
private val typesName = getTypeList().map {
it.name
}.toTypedArray()
private fun getTypeList() = listOf(
StringQuery("Any", "all"),
StringQuery("TV", "tv"),
StringQuery("Movie", "movie"),
)
private class StatusList(statuses: Array<String>) : AnimeFilter.Select<String>("Status", statuses)
private val statusesName = getStatusesList().map {
it.name
}.toTypedArray()
private fun getStatusesList() = listOf(
StringQuery("Any", "all"),
StringQuery("Currently Airing", "airing"),
StringQuery("Finished Airing", "completed"),
)
private class StyleList(styles: Array<String>) : AnimeFilter.Select<String>("Style", styles)
private val stylesName = getStyleList().map {
it.name
}.toTypedArray()
private fun getStyleList() = listOf(
StringQuery("Any", "all"),
StringQuery("Anime", "anime"),
StringQuery("Cartoon", "cartoon"),
)
private class YearList(years: Array<String>) : AnimeFilter.Select<String>("Year", years)
private val yearsName = getYearList().map {
it.name
}.toTypedArray()
private fun getYearList() = listOf(
StringQuery("Any", "all"),
StringQuery("2023", "2023"),
StringQuery("2022", "2022"),
StringQuery("2021", "2021"),
StringQuery("2020", "2020"),
StringQuery("2019", "2019"),
StringQuery("2018", "2018"),
StringQuery("2017", "2017"),
StringQuery("2016", "2016"),
StringQuery("2015", "2015"),
StringQuery("2014", "2014"),
StringQuery("2013", "2013"),
StringQuery("2012", "2012"),
StringQuery("2011", "2011"),
StringQuery("2010", "2010"),
StringQuery("2009", "2009"),
StringQuery("2008", "2008"),
StringQuery("2007", "2007"),
StringQuery("2006", "2006"),
StringQuery("2005", "2005"),
StringQuery("2004", "2004"),
StringQuery("2003", "2003"),
StringQuery("2002", "2002"),
StringQuery("2001", "2001"),
StringQuery("2000", "2000"),
StringQuery("1999", "1999"),
StringQuery("1998", "1998"),
StringQuery("1997", "1997"),
StringQuery("1996", "1996"),
StringQuery("1995", "1995"),
StringQuery("1994", "1994"),
StringQuery("1993", "1993"),
StringQuery("1992", "1992"),
StringQuery("1991", "1991"),
StringQuery("1990", "1990"),
)
private class SortList(sorts: Array<String>) : AnimeFilter.Select<String>("Sort", sorts)
private val sortsName = getSortList().map {
it.name
}.toTypedArray()
private fun getSortList() = listOf(
StringQuery("Default", "default"),
StringQuery("Ascending", "title_a_z"),
StringQuery("Descending", "title_z_a"),
StringQuery("Updated", "update"),
StringQuery("Published", "date"),
StringQuery("Most Viewed", "viewed"),
StringQuery("Favourite", "favorite"),
)
internal class Genre(val id: String) : AnimeFilter.CheckBox(id)
private class GenreList(genres: List<Genre>) : AnimeFilter.Group<Genre>("Genres", genres)
private fun genresName() = listOf(
Genre("Action"),
Genre("Adult Cast"),
Genre("Adventure"),
Genre("Animation"),
Genre("Comedy"),
Genre("Detective"),
Genre("Drama"),
Genre("Ecchi"),
Genre("Family"),
Genre("Fantasy"),
Genre("Isekai"),
Genre("Kids"),
Genre("Martial Arts"),
Genre("Mecha"),
Genre("Military"),
Genre("Mystery"),
Genre("Otaku Culture"),
Genre("Reality"),
Genre("Romance"),
Genre("School"),
Genre("Sci-Fi"),
Genre("Seinen"),
Genre("Shounen"),
Genre("Slice of Life"),
Genre("Sports"),
Genre("Super Power"),
Genre("SuperHero"),
Genre("Supernatural"),
Genre("TV Movie"),
)
val filters = AnimeFilterList(
TypeList(typesName),
StatusList(statusesName),
StyleList(stylesName),
YearList(yearsName),
SortList(sortsName),
GenreList(genresName()),
)
fun getSearchParams(filters: AnimeFilterList) {
var params = ""
filters.forEach { filter ->
when (filter) {
is TypeList -> {
val type = getTypeList()[filter.state].query
params = "$params&s_type=$type"
}
is StatusList -> {
val status = getStatusesList()[filter.state].query
params = "$params&s_status=$status"
}
is StyleList -> {
val style = getStyleList()[filter.state].query
params = "$params&s_sub_type=$style"
}
is YearList -> {
val year = getYearList()[filter.state].query
params = "$params&s_year=$year"
}
is SortList -> {
val sort = getSortList()[filter.state].query
params = "$params&s_orderby=$sort"
}
is GenreList -> {
params = "$params&s_genre="
filter.state.forEach {
if (it.state) {
val genre = it.id.replace(" ", "-")
params = "$params$genre%2C"
}
}
}
else -> {}
}
}
}
}

View File

@ -0,0 +1,53 @@
package eu.kanade.tachiyomi.animeextension.all.animeworldindia
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.OkHttpClient
class MyStreamExtractor {
fun videosFromUrl(
url: String,
headers: Headers,
): List<Video> {
val host = url.substringBefore("/watch")
val client = OkHttpClient()
return runCatching {
val response = client.newCall(GET(url)).execute()
val document = response.asJsoup().html()
val streamCode = document
.substringAfter("${url.substringAfter("?v=")}\", \"")
.substringBefore("\",null,null")
val streamUrl = "$host/m3u8/$streamCode/master.txt?s=1&cache=1"
val cookie = response.headers.firstOrNull {
it.first.startsWith("set-cookie", true) && it.second.startsWith("PHPSESSID", true)
}?.second?.substringBefore(";") ?: ""
val newHeaders = headers.newBuilder()
.set("cookie", cookie)
.set("accept", "*/*")
.build()
val masterPlaylist = client.newCall(GET(streamUrl, newHeaders))
.execute()
.use { it.body.string() }
val separator = "#EXT-X-STREAM-INF"
masterPlaylist.substringAfter(separator).split(separator).map {
val resolution = it.substringAfter("RESOLUTION=")
.substringBefore("\n")
.substringAfter("x")
.substringBefore(",") + "p"
val quality = ("MyStream: $resolution")
val videoUrl = it.substringAfter("\n").substringBefore("\n")
Video(videoUrl, quality, videoUrl, headers = newHeaders)
}
}.getOrNull() ?: emptyList<Video>()
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,247 +0,0 @@
package eu.kanade.tachiyomi.animeextension.hi.animeWorld
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.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.streamsbextractor.StreamSBExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.FormBody
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 AnimeWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "AnimeWorld (experimental)"
override val baseUrl = "https://anime-world.in/"
override val lang = "hi"
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 = "li.series, li.movies"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/")
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
var url = element.selectFirst("img")!!.attr("src")
if (!url.contains("https")) {
url = "https:$url"
}
anime.setUrlWithoutDomain(element.select("a").attr("href"))
anime.thumbnail_url = url
anime.title = element.select("h2.entry-title").text()
return anime
}
override fun popularAnimeNextPageSelector(): String = "ul.pagination li:last-child:not(.selected)"
override fun episodeListSelector() = throw Exception("not used")
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
return if (response.request.url.toString().contains("movies/")) {
val episodeList = mutableListOf<SEpisode>()
val episode = SEpisode.create()
episode.name = document.select("h1.entry-title").text()
episode.episode_number = 1F
episode.setUrlWithoutDomain(response.request.url.toString())
episodeList.add(episode)
episodeList
} else {
val seasonDataElements = document.select("li.sel-temp")
val episodesList = mutableListOf<SEpisode>()
seasonDataElements.map {
val epList = episodeFromSeason(it)
episodesList.addAll(epList)
}
episodesList
}
}
private fun episodeFromSeason(element: Element): List<SEpisode> {
val seasonNo = element.select("a").attr("data-season")
val postNo = element.select("a").attr("data-post")
val episodeList = mutableListOf<SEpisode>()
val postUrl = "https://anime-world.in/wp-admin/admin-ajax.php"
val body = FormBody.Builder()
.add("action", "action_select_season")
.add("season", seasonNo)
.add("post", postNo)
.build()
val epListResponse = client.newCall(
POST(
postUrl,
headers,
body,
),
).execute().asJsoup()
val episodesElements = epListResponse.select("li")
episodesElements.map {
val episode = SEpisode.create()
val url = it.select("a").attr("href")
episode.setUrlWithoutDomain(url)
val epNo = it.select("span.num-epi").text()
episode.name = "$epNo : ${it.select("h2.entry-title").text()}"
episode.episode_number = epNo.substringAfter("x").toFloat()
episodeList.add(episode)
}
return episodeList
}
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(baseUrl + element.attr("href"))
val epName = element.selectFirst("div.name")!!.ownText()
val ep = epName.substringAfter("Episode ")
val epNo = try {
ep.substringBefore(" ").toFloat()
} catch (e: NumberFormatException) {
0.toFloat()
}
episode.episode_number = epNo
episode.name = if (ep == epName) epName else "Episode $ep"
return episode
}
override fun videoListSelector() = throw Exception("not used")
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val videoList = mutableListOf<Video>()
val playerElement = document.select("section.player")
val languagesElement = playerElement.select("span.rtg")
for (element in languagesElement) {
val tabId = element.attr("tab")
val language = element.text()
val options = playerElement.select("div#$tabId li a")
options.map {
val optionId = it.attr("href")
val videos = videosFromElement(playerElement.select("div$optionId").first()!!, language)
videoList.addAll(videos)
}
}
return videoList
}
override fun videoFromElement(element: Element): Video = throw Exception("not used")
override fun videoUrlParse(document: Document) = throw Exception("not used")
private fun videosFromElement(element: Element, language: String): List<Video> {
val iframeElm = element.select("iframe")
val videoList = mutableListOf<Video>()
val url = iframeElm.attr("data-src")
when {
url.contains("embedsb") || url.contains("cloudemb") || url.contains("sbembed.com") ||
url.contains("sbembed1.com") || url.contains("sbplay.org") ||
url.contains("sbvideo.net") || url.contains("streamsb.net") || url.contains("sbplay.one") ||
url.contains("cloudemb.com") || url.contains("playersb.com") || url.contains("tubesb.com") ||
url.contains("sbplay1.com") || url.contains("embedsb.com") || url.contains("watchsb.com") ||
url.contains("sbplay2.com") || url.contains("japopav.tv") || url.contains("viewsb.com") ||
url.contains("sbfast") || url.contains("sbfull.com") || url.contains("javplaya.com") ||
url.contains("ssbstream.net") || url.contains("p1ayerjavseen.com") || url.contains("sbthe.com")
-> {
val videos = StreamSBExtractor(client).videosFromUrl(url, headers, "$language:")
videoList.addAll(videos)
}
}
return videoList
}
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 searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
var url = element.selectFirst("img")!!.attr("src")
if (!url.contains("https")) {
url = "https:$url"
}
anime.setUrlWithoutDomain(element.select("a").attr("href"))
anime.thumbnail_url = url
anime.title = element.select("h2.entry-title").text()
return anime
}
override fun searchAnimeNextPageSelector(): String = "nav.navigation.pagination div.nav-links a:last-child:not(.current)"
override fun searchAnimeSelector(): String = "div#movies-a li"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
return when {
query.isNotBlank() -> GET("$baseUrl/page/$page/?s=$query", headers)
else -> GET("$baseUrl/")
}
}
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.title = document.select("h1.entry-title").text()
anime.description = document.select("div.description").text()
return anime
}
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")
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p")
entryValues = arrayOf("1080", "720", "480", "360")
setDefaultValue("1080")
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()
}
}
screen.addPreference(videoQualityPref)
}
}