AnimeStream (multi-src): Refactor & add extractors (#397)

* Use suspend methods
* Use helpers
* AnimeIndo: Avoid crash when initializing filter list
This commit is contained in:
Cuong-Tran
2026-05-30 00:31:05 +07:00
committed by GitHub
parent f302e0109d
commit 7295fe5ee7
24 changed files with 470 additions and 354 deletions

View File

@@ -1,8 +1,8 @@
package eu.kanade.tachiyomi.multisrc.animestream
import android.content.SharedPreferences
import android.util.Base64
import android.util.Log
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
@@ -21,11 +21,12 @@ import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.SubFilter
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.TypeFilter
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup
import keiyoushi.utils.addListPreference
import keiyoushi.utils.getPreferencesLazy
import keiyoushi.utils.parallelCatchingFlatMapBlocking
import keiyoushi.utils.tryParse
import keiyoushi.utils.useAsJsoup
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
@@ -58,11 +59,19 @@ abstract class AnimeStream(
"pt-BR" -> "Qualidade preferida"
else -> "Preferred quality"
}
protected open val prefQualityValues = arrayOf("1080p", "720p", "480p", "360p")
protected open val prefQualityEntries = prefQualityValues
protected open val prefQualityValues = listOf("1080p", "720p", "480p", "360p")
protected open val prefQualityEntries: List<String>
get() = prefQualityValues
protected open val videoSortPrefKey = prefQualityKey
protected open val videoSortPrefDefault = prefQualityDefault
protected open val videoSortPrefKey: String
get() = prefQualityKey
protected open val videoSortPrefDefault: String
get() = prefQualityDefault
protected open val SharedPreferences.qualityPref: String
get() = getString(prefQualityKey, prefQualityDefault) ?: prefQualityDefault
protected open val SharedPreferences.videoSortPref: String
get() = getString(videoSortPrefKey, videoSortPrefDefault) ?: videoSortPrefDefault
protected open val dateFormatter by lazy {
val locale = when (lang) {
@@ -125,7 +134,7 @@ abstract class AnimeStream(
}
protected open fun searchAnimeByPathParse(response: Response): AnimesPage {
val details = animeDetailsParse(response.asJsoup()).apply {
val details = animeDetailsParse(response.useAsJsoup()).apply {
setUrlWithoutDomain(response.request.url.toString())
initialized = true
}
@@ -167,13 +176,14 @@ abstract class AnimeStream(
protected open val filtersSelector = "span.sec1 > div.filter > ul"
private fun fetchFilterList() {
protected open suspend fun fetchFilterList() {
if (fetchFilters && !AnimeStreamFilters.filterInitialized()) {
AnimeStreamFilters.filterElements = runBlocking {
withContext(Dispatchers.IO) {
client.newCall(GET(animeListUrl)).execute()
.asJsoup()
.select(filtersSelector)
withContext(Dispatchers.IO) {
runCatching {
AnimeStreamFilters.filterElements =
client.newCall(GET(animeListUrl)).awaitSuccess()
.useAsJsoup()
.select(filtersSelector)
}
}
}
@@ -221,7 +231,7 @@ abstract class AnimeStream(
else -> "Order"
}
override fun getFilterList(): AnimeFilterList = if (fetchFilters && AnimeStreamFilters.filterInitialized()) {
override fun getFilterList(): AnimeFilterList = if (fetchFilters && AnimeStreamFilters.filterInitialized() && AnimeStreamFilters.filterElements.isNotEmpty()) {
AnimeFilterList(
GenresFilter(genresFilterText),
SeasonFilter(seasonsFilterText),
@@ -290,7 +300,7 @@ abstract class AnimeStream(
// ============================== Episodes ==============================
override fun episodeListParse(response: Response): List<SEpisode> {
val doc = response.asJsoup()
val doc = response.useAsJsoup()
return doc.select(episodeListSelector()).map(::episodeFromElement)
}
@@ -311,14 +321,14 @@ abstract class AnimeStream(
episode_number = it.substringBefore(" ").toFloatOrNull() ?: 0F
}
element.selectFirst(".epl-sub")?.text()?.let { scanlator = it }
date_upload = element.selectFirst(".epl-date")?.text().toDate()
date_upload = element.selectFirst(".epl-date")?.text().let { dateFormatter.tryParse(it) }
}
// ============================ Video Links =============================
override fun videoListSelector() = "select.mirror > option[data-index], ul.mirror a[data-em]"
override fun videoListParse(response: Response): List<Video> {
val items = response.asJsoup().select(videoListSelector())
val items = response.useAsJsoup().select(videoListSelector())
return items.parallelCatchingFlatMapBlocking { element ->
val name = element.text()
val url = getHosterUrl(element)
@@ -326,7 +336,7 @@ abstract class AnimeStream(
}
}
protected open fun getHosterUrl(element: Element): String {
protected open suspend fun getHosterUrl(element: Element): String {
val encodedData = when (element.tagName()) {
"option" -> element.attr("value")
"a" -> element.attr("data-em")
@@ -337,13 +347,13 @@ abstract class AnimeStream(
}
// Taken from LuciferDonghua
protected open fun getHosterUrl(encodedData: String): String {
protected open suspend fun getHosterUrl(encodedData: String): String {
val doc = if (encodedData.toHttpUrlOrNull() == null) {
Base64.decode(encodedData, Base64.DEFAULT)
.let(::String) // bytearray -> string
.let(Jsoup::parse) // string -> document
} else {
client.newCall(GET(encodedData, headers)).execute().asJsoup()
client.newCall(GET(encodedData, headers)).awaitSuccess().useAsJsoup()
}
return doc.selectFirst("iframe[src~=.]")?.safeUrl()
@@ -359,7 +369,7 @@ abstract class AnimeStream(
}
}
protected open fun getVideoList(url: String, name: String): List<Video> {
protected open suspend fun getVideoList(url: String, name: String): List<Video> {
Log.i(name, "getVideoList -> URL => $url || Name => $name")
return emptyList()
}
@@ -370,27 +380,19 @@ abstract class AnimeStream(
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = prefQualityKey
title = prefQualityTitle
entries = prefQualityEntries
entryValues = prefQualityValues
setDefaultValue(prefQualityDefault)
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)
screen.addListPreference(
key = prefQualityKey,
title = prefQualityTitle,
entries = prefQualityEntries,
entryValues = prefQualityValues,
default = prefQualityDefault,
summary = "%s",
)
}
// ============================= Utilities ==============================
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(videoSortPrefKey, videoSortPrefDefault)!!
val quality = preferences.videoSortPref
return sortedWith(
compareBy { it.quality.contains(quality, true) },
).reversed()
@@ -407,12 +409,6 @@ abstract class AnimeStream(
selectFirst("a")?.text() ?: ownText()
}
protected open fun String?.toDate(): Long = this?.let {
runCatching {
dateFormatter.parse(trim())?.time
}.getOrNull()
} ?: 0L
/**
* Tries to get the image url via various possible attributes.
* Taken from Tachiyomi's Madara multisrc.

View File

@@ -15,7 +15,7 @@ object AnimeStreamFilters {
fun toQueryPart() = vals[state].second
}
open class CheckBoxFilterList(name: String, val pairs: Array<Pair<String, String>>) : AnimeFilter.Group<AnimeFilter.CheckBox>(name, pairs.map { CheckBoxVal(it.first, false) })
open class CheckBoxFilterList(name: String, 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)
@@ -69,13 +69,14 @@ object AnimeStreamFilters {
fun filterInitialized() = ::filterElements.isInitialized
fun getPairListByIndex(index: Int) = filterElements.get(index)
.select("li")
.map { element ->
val key = element.selectFirst("label")!!.text()
val value = element.selectFirst("input")!!.attr("value")
fun getPairListByIndex(index: Int) = filterElements.getOrNull(index)
?.select("li")
?.mapNotNull { element ->
val key = element.selectFirst("label")?.text() ?: return@mapNotNull null
val value = element.selectFirst("input")?.attr("value") ?: return@mapNotNull null
Pair(key, value)
}.toTypedArray()
}?.toTypedArray()
?: arrayOf("" to "")
private val GENRES_LIST by lazy { getPairListByIndex(0) }
private val SEASON_LIST by lazy { getPairListByIndex(1) }

View File

@@ -0,0 +1,7 @@
plugins {
alias(kei.plugins.library)
}
dependencies {
implementation(project(":lib:playlistutils"))
}

View File

@@ -0,0 +1,21 @@
package aniyomi.lib.rumbleextractor
import aniyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.animesource.model.Video
import okhttp3.Headers
import okhttp3.OkHttpClient
class RumbleExtractor(private val client: OkHttpClient, private val headers: Headers) {
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val id = extractRumbleId(url) ?: return emptyList()
val sourceUrl = "https://rumble.com/hls-vod/$id/playlist.m3u8"
return playlistUtils.extractFromHls(sourceUrl, referer = url, subtitleList = emptyList(), videoNameGen = { q -> "$prefix $q" })
}
private val regex by lazy { Regex("""rumble\.com/embed/v([a-zA-Z0-9]+)""") }
private fun extractRumbleId(url: String): String? = regex.find(url)?.groupValues?.get(1)
}

View File

@@ -0,0 +1,8 @@
plugins {
alias(kei.plugins.library)
}
dependencies {
implementation(project(":lib:playlistutils"))
implementation(project(":lib:unpacker"))
}

View File

@@ -0,0 +1,130 @@
package aniyomi.lib.streamplayextractor
import aniyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.awaitSuccess
import keiyoushi.lib.jsunpacker.JsUnpacker
import keiyoushi.utils.UrlUtils
import keiyoushi.utils.parallelCatchingFlatMap
import keiyoushi.utils.parseAs
import keiyoushi.utils.useAsJsoup
import kotlinx.serialization.Serializable
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody
class StreamPlayExtractor(private val client: OkHttpClient, private val headers: Headers) {
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
suspend fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val document = client.newCall(
GET(url, headers),
).awaitSuccess().useAsJsoup()
return document.select("#servers a").parallelCatchingFlatMap { element ->
extractAndDecodeFromDocument(element.attr("abs:href"), "$prefix ${element.text()}")
}
}
private val kakenRegex by lazy { Regex("window\\.kaken ?= ?\"([^\"]+)\";") }
/**
* Server 3 has issue with playlist compatibility, it only plays the first segment
*/
private suspend fun extractAndDecodeFromDocument(url: String, prefix: String): List<Video> {
val document = client.newCall(
GET(url, headers),
).awaitSuccess().useAsJsoup()
// Find script containing the packed code
val packedScript = document.selectFirst("script:containsData(function(p,a,c,k,e,d))")
val kaken = if (packedScript != null) {
val scriptContent = packedScript.data()
JsUnpacker.unpackAndCombine(scriptContent)?.let {
kakenRegex.find(it)?.groupValues?.get(1)
}
} else {
// For mobile UA, it's non-packed
document.selectFirst("script:containsData(window.kaken)")
?.data()?.let {
// Extract kaken
kakenRegex.find(it)?.groupValues?.get(1)
}
} ?: return emptyList()
val httpUrl = url.toHttpUrlOrNull() ?: return emptyList()
val apiHeaders = headers.newBuilder().apply {
add("Accept", "application/json, text/javascript, */*; q=0.01")
add("Origin", "${httpUrl.scheme}://${httpUrl.host}")
add("Referer", url)
add("X-Requested-With", "XMLHttpRequest")
}.build()
val apiResponse = client.newCall(
POST(
"https://play.streamplay.co.in/api/",
headers = apiHeaders,
body = kaken.toRequestBody("text/plain".toMediaType()),
),
).awaitSuccess().parseAs<APIResponse>()
val subtitleList = apiResponse.tracks?.let { t ->
t.map { Track(it.file, it.label) }
} ?: emptyList()
val videos = apiResponse.sources.parallelCatchingFlatMap { source ->
val sourceUrl = UrlUtils.fixUrl(source.videoUrl) ?: return@parallelCatchingFlatMap emptyList()
if (source.type == "hls" && sourceUrl.endsWith("master.m3u8")) {
playlistUtils.extractFromHls(
playlistUrl = sourceUrl,
referer = url,
subtitleList = subtitleList,
videoNameGen = { q -> "$prefix $q (StreamPlay)" },
)
} else {
listOf(
Video(
sourceUrl,
"$prefix (StreamPlay) Original",
sourceUrl,
headers = headers.newBuilder()
.set("Referer", url)
.build(),
subtitleTracks = subtitleList,
),
)
}
}
return videos
}
@Serializable
data class APIResponse(
val sources: List<SourceObject>,
val tracks: List<TrackObject>? = null,
) {
@Serializable
data class SourceObject(
val file: String,
val label: String,
val type: String,
) {
val videoUrl: String
get() = file.replace("master.txt", "master.m3u8")
}
@Serializable
data class TrackObject(
val file: String,
val label: String,
)
}
}

View File

@@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.animeextension.all.animexin
import androidx.preference.ListPreference
import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import aniyomi.lib.dailymotionextractor.DailymotionExtractor
import aniyomi.lib.doodextractor.DoodExtractor
@@ -10,7 +10,8 @@ import eu.kanade.tachiyomi.animeextension.all.animexin.extractors.VidstreamingEx
import eu.kanade.tachiyomi.animeextension.all.animexin.extractors.YouTubeExtractor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import kotlinx.coroutines.runBlocking
import keiyoushi.utils.addListPreference
import keiyoushi.utils.delegate
class AnimeXin :
AnimeStream(
@@ -28,9 +29,9 @@ class AnimeXin :
private val vidstreamingExtractor by lazy { VidstreamingExtractor(client) }
private val youTubeExtractor by lazy { YouTubeExtractor(client) }
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
override suspend fun getVideoList(url: String, name: String): List<Video> {
val prefix = "$name - "
when {
return when {
url.contains("ok.ru") -> okruExtractor.videosFromUrl(url, prefix)
url.contains("dailymotion") -> dailymotionExtractor.videosFromUrl(url, prefix)
@@ -57,27 +58,22 @@ class AnimeXin :
override fun setupPreferenceScreen(screen: PreferenceScreen) {
super.setupPreferenceScreen(screen) // Quality preferences
ListPreference(screen.context).apply {
key = PREF_LANG_KEY
title = PREF_LANG_TITLE
entries = PREF_LANG_VALUES
entryValues = PREF_LANG_VALUES
setDefaultValue(PREF_LANG_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)
screen.addListPreference(
key = PREF_LANG_KEY,
title = PREF_LANG_TITLE,
entries = PREF_LANG_VALUES,
entryValues = PREF_LANG_VALUES,
default = PREF_LANG_DEFAULT,
summary = "%s",
)
}
private val SharedPreferences.langPref by preferences.delegate(PREF_LANG_KEY, PREF_LANG_DEFAULT)
// ============================= Utilities ==============================
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(prefQualityKey, prefQualityDefault)!!
val language = preferences.getString(PREF_LANG_KEY, PREF_LANG_DEFAULT)!!
val quality = preferences.videoSortPref
val language = preferences.langPref
return sortedWith(
compareBy(
@@ -91,7 +87,7 @@ class AnimeXin :
private const val PREF_LANG_KEY = "preferred_language"
private const val PREF_LANG_TITLE = "Preferred Video Language"
private const val PREF_LANG_DEFAULT = "All Sub"
private val PREF_LANG_VALUES = arrayOf(
private val PREF_LANG_VALUES = listOf(
"All Sub", "Arabic", "English", "German", "Indonesia", "Italian",
"Polish", "Portuguese", "Spanish", "Thai", "Turkish",
)

View File

@@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.animeextension.all.chineseanime
import androidx.preference.ListPreference
import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import aniyomi.lib.dailymotionextractor.DailymotionExtractor
import aniyomi.lib.streamwishextractor.StreamWishExtractor
@@ -8,7 +8,8 @@ import aniyomi.lib.vidhideextractor.VidHideExtractor
import eu.kanade.tachiyomi.animeextension.all.chineseanime.extractors.VatchusExtractor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import kotlinx.coroutines.runBlocking
import keiyoushi.utils.addListPreference
import keiyoushi.utils.delegate
class ChineseAnime :
AnimeStream(
@@ -32,13 +33,13 @@ class ChineseAnime :
private val vidHideExtractor by lazy { VidHideExtractor(client, headers) }
private val vatchusExtractor by lazy { VatchusExtractor(client, headers) }
override fun getVideoList(url: String, name: String): List<Video> {
override suspend fun getVideoList(url: String, name: String): List<Video> {
val prefix = "$name - "
return when {
url.contains("dailymotion") -> dailymotionExtractor.videosFromUrl(url, prefix)
url.contains("embedwish") -> streamwishExtractor.videosFromUrl(url, prefix)
url.contains("vatchus") -> vatchusExtractor.videosFromUrl(url, prefix)
url.contains("donghua.xyz/v/") -> runBlocking { vidHideExtractor.videosFromUrl(url) { "$prefix $it" } }
url.contains("donghua.xyz/v/") -> vidHideExtractor.videosFromUrl(url) { "$prefix $it" }
else -> emptyList()
}
}
@@ -46,29 +47,23 @@ class ChineseAnime :
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
super.setupPreferenceScreen(screen) // Quality preferences
val videoLangPref = ListPreference(screen.context).apply {
key = PREF_LANG_KEY
title = PREF_LANG_TITLE
entries = PREF_LANG_VALUES
entryValues = PREF_LANG_VALUES
setDefaultValue(PREF_LANG_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()
}
}
screen.addPreference(videoLangPref)
screen.addListPreference(
key = PREF_LANG_KEY,
title = PREF_LANG_TITLE,
entries = PREF_LANG_VALUES,
entryValues = PREF_LANG_VALUES,
default = PREF_LANG_DEFAULT,
summary = "%s",
)
}
private val SharedPreferences.langPref by preferences.delegate(PREF_LANG_KEY, PREF_LANG_DEFAULT)
// ============================= Utilities ==============================
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(prefQualityKey, prefQualityDefault)!!
val language = preferences.getString(PREF_LANG_KEY, PREF_LANG_DEFAULT)!!
val quality = preferences.videoSortPref
val language = preferences.langPref
return sortedWith(
compareBy(
@@ -82,7 +77,7 @@ class ChineseAnime :
private const val PREF_LANG_KEY = "preferred_language"
private const val PREF_LANG_TITLE = "Preferred Video Language"
private const val PREF_LANG_DEFAULT = "All Sub"
private val PREF_LANG_VALUES = arrayOf(
private val PREF_LANG_VALUES = listOf(
"All Sub", "Arabic", "English", "Indonesia", "Persian", "Malay",
"Polish", "Portuguese", "Spanish", "Thai", "Vietnamese",
)

View File

@@ -1,15 +1,17 @@
package eu.kanade.tachiyomi.animeextension.all.lmanime
import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference
import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import aniyomi.lib.dailymotionextractor.DailymotionExtractor
import aniyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import aniyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import eu.kanade.tachiyomi.util.asJsoup
import keiyoushi.utils.addListPreference
import keiyoushi.utils.addSetPreference
import keiyoushi.utils.delegate
import keiyoushi.utils.parallelCatchingFlatMapBlocking
import keiyoushi.utils.useAsJsoup
import okhttp3.Response
class LMAnime :
@@ -19,12 +21,11 @@ class LMAnime :
"https://lmanime.com",
) {
// ============================ Video Links =============================
override val prefQualityValues = arrayOf("144p", "288p", "480p", "720p", "1080p")
override val prefQualityEntries = prefQualityValues
override val prefQualityValues = listOf("144p", "288p", "480p", "720p", "1080p")
override fun videoListParse(response: Response): List<Video> {
val items = response.asJsoup().select(videoListSelector())
val allowed = preferences.getStringSet(PREF_ALLOWED_LANGS_KEY, PREF_ALLOWED_LANGS_DEFAULT)!!
val items = response.useAsJsoup().select(videoListSelector())
val allowed = preferences.allowedLangsPref
return items
.filter { element ->
val text = element.text()
@@ -40,11 +41,11 @@ class LMAnime :
private val dailyExtractor by lazy { DailymotionExtractor(client, headers) }
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
override fun getVideoList(url: String, name: String): List<Video> {
override suspend fun getVideoList(url: String, name: String): List<Video> {
val prefix = "($name) - "
return when {
"dailymotion" in url -> dailyExtractor.videosFromUrl(url, "Dailymotion ($name)")
"mp4upload" in url -> mp4uploadExtractor.videosFromUrl(url, headers, "$prefix")
"mp4upload" in url -> mp4uploadExtractor.videosFromUrl(url, headers, prefix)
"filelions" in url -> streamwishExtractor.videosFromUrl(url, "StreamWish ($name)")
else -> emptyList()
}
@@ -55,38 +56,32 @@ class LMAnime :
override fun setupPreferenceScreen(screen: PreferenceScreen) {
super.setupPreferenceScreen(screen) // Quality preferences
ListPreference(screen.context).apply {
key = PREF_LANG_KEY
title = PREF_LANG_TITLE
entries = PREF_LANG_ENTRIES
entryValues = PREF_LANG_ENTRIES
setDefaultValue(PREF_LANG_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)
screen.addListPreference(
key = PREF_LANG_KEY,
title = PREF_LANG_TITLE,
entries = PREF_LANG_ENTRIES,
entryValues = PREF_LANG_ENTRIES,
default = PREF_LANG_DEFAULT,
summary = "%s",
)
MultiSelectListPreference(screen.context).apply {
key = PREF_ALLOWED_LANGS_KEY
title = PREF_ALLOWED_LANGS_TITLE
entries = PREF_ALLOWED_LANGS_ENTRIES
entryValues = PREF_ALLOWED_LANGS_ENTRIES
setDefaultValue(PREF_ALLOWED_LANGS_DEFAULT)
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}.also(screen::addPreference)
screen.addSetPreference(
key = PREF_ALLOWED_LANGS_KEY,
title = PREF_ALLOWED_LANGS_TITLE,
entries = PREF_ALLOWED_LANGS_ENTRIES,
entryValues = PREF_ALLOWED_LANGS_ENTRIES,
default = PREF_ALLOWED_LANGS_DEFAULT,
summary = "",
)
}
private val SharedPreferences.langPref by preferences.delegate(PREF_LANG_KEY, PREF_LANG_DEFAULT)
private val SharedPreferences.allowedLangsPref by preferences.delegate(PREF_ALLOWED_LANGS_KEY, PREF_ALLOWED_LANGS_DEFAULT)
// ============================= Utilities ==============================
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(prefQualityKey, prefQualityDefault)!!
val lang = preferences.getString(PREF_LANG_KEY, PREF_LANG_DEFAULT)!!
val quality = preferences.videoSortPref
val lang = preferences.langPref
return sortedWith(
compareBy(
{ it.quality.contains(quality) },
@@ -99,7 +94,7 @@ class LMAnime :
private const val PREF_LANG_KEY = "pref_language"
private const val PREF_LANG_TITLE = "Preferred language"
private const val PREF_LANG_DEFAULT = "English"
private val PREF_LANG_ENTRIES = arrayOf(
private val PREF_LANG_ENTRIES = listOf(
"English",
"Español",
"Indonesian",

View File

@@ -5,7 +5,6 @@ import aniyomi.lib.streamwishextractor.StreamWishExtractor
import aniyomi.lib.vidhideextractor.VidHideExtractor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import kotlinx.coroutines.runBlocking
class AnimeKhor :
AnimeStream(
@@ -15,9 +14,9 @@ class AnimeKhor :
) {
// ============================ Video Links =============================
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
override suspend fun getVideoList(url: String, name: String): List<Video> {
val prefix = "$name - "
when {
return when {
url.contains("ahvsh.com") || name.equals("streamhide", true) -> {
VidHideExtractor(client, headers).videosFromUrl(url) { "$prefix$it" }
}

View File

@@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.animeextension.en.animenosub
import androidx.preference.ListPreference
import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import aniyomi.lib.streamwishextractor.StreamWishExtractor
import aniyomi.lib.vidmolyextractor.VidMolyExtractor
@@ -9,7 +9,8 @@ import eu.kanade.tachiyomi.animeextension.en.animenosub.extractors.VtubeExtracto
import eu.kanade.tachiyomi.animeextension.en.animenosub.extractors.WolfstreamExtractor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import kotlinx.coroutines.runBlocking
import keiyoushi.utils.addListPreference
import keiyoushi.utils.delegate
import org.jsoup.nodes.Element
class Animenosub :
@@ -30,9 +31,9 @@ class Animenosub :
// ============================ Video Links =============================
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
override suspend fun getVideoList(url: String, name: String): List<Video> {
val prefix = "$name - "
when {
return when {
listOf(
"bysesayeveum",
"filemoon",
@@ -71,32 +72,32 @@ class Animenosub :
override fun setupPreferenceScreen(screen: PreferenceScreen) {
super.setupPreferenceScreen(screen) // Quality preferences
val videoTypePref = ListPreference(screen.context).apply {
key = PREF_TYPE_KEY
title = PREF_TYPE_TITLE
entries = PREF_TYPE_VALUES
entryValues = PREF_TYPE_VALUES
setDefaultValue(PREF_TYPE_DEFAULT)
summary = "%s"
}
val videoServerPref = ListPreference(screen.context).apply {
key = PREF_SERVER_KEY
title = PREF_SERVER_TITLE
entries = PREF_SERVER_VALUES
entryValues = PREF_SERVER_VALUES
setDefaultValue(PREF_SERVER_DEFAULT)
summary = "%s"
}
screen.addPreference(videoTypePref)
screen.addPreference(videoServerPref)
screen.addListPreference(
key = PREF_TYPE_KEY,
title = PREF_TYPE_TITLE,
entries = PREF_TYPE_VALUES,
entryValues = PREF_TYPE_VALUES,
default = PREF_TYPE_DEFAULT,
summary = "%s",
)
screen.addListPreference(
key = PREF_SERVER_KEY,
title = PREF_SERVER_TITLE,
entries = PREF_SERVER_VALUES,
entryValues = PREF_SERVER_VALUES,
default = PREF_SERVER_DEFAULT,
summary = "%s",
)
}
// ============================= Utilities ==============================
private val SharedPreferences.typePref by preferences.delegate(PREF_TYPE_KEY, PREF_TYPE_DEFAULT)
private val SharedPreferences.serverPref by preferences.delegate(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(prefQualityKey, prefQualityDefault)!!
val type = preferences.getString(PREF_TYPE_KEY, PREF_TYPE_DEFAULT)!!
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
val quality = preferences.videoSortPref
val type = preferences.typePref
val server = preferences.serverPref
return sortedWith(
compareBy(
{ it.quality.contains(type, ignoreCase = true) },
@@ -110,12 +111,12 @@ class Animenosub :
private const val PREF_TYPE_KEY = "preferred_type"
private const val PREF_TYPE_TITLE = "Preferred Video Type"
private const val PREF_TYPE_DEFAULT = "SUB"
private val PREF_TYPE_VALUES = arrayOf("SUB", "RAW")
private val PREF_TYPE_VALUES = listOf("SUB", "RAW")
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_TITLE = "Preferred Video Server"
private const val PREF_SERVER_DEFAULT = "Moon"
private val PREF_SERVER_VALUES = arrayOf(
private val PREF_SERVER_VALUES = listOf(
"Moon",
"StreamWish",
"VidMoly",

View File

@@ -11,15 +11,13 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import eu.kanade.tachiyomi.network.GET
import keiyoushi.utils.LazyMutable
import keiyoushi.utils.UrlUtils
import keiyoushi.utils.addSetPreference
import keiyoushi.utils.addSwitchPreference
import kotlinx.coroutines.runBlocking
import keiyoushi.utils.delegate
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import kotlin.getValue
class DonghuaStream :
AnimeStream(
@@ -27,8 +25,8 @@ class DonghuaStream :
"DonghuaStream",
"https://donghuastream.org",
) {
override val fetchFilters: Boolean
get() = false
override val fetchFilters = false
// ============================ Manual Changes ==========================
@@ -46,11 +44,11 @@ class DonghuaStream :
return GET(url)
}
private var SharedPreferences.ignorePreview
by LazyMutable { preferences.getBoolean(IGNORE_PREVIEW_KEY, IGNORE_PREVIEW_DEFAULT) }
private val SharedPreferences.ignorePreview
by preferences.delegate(IGNORE_PREVIEW_KEY, IGNORE_PREVIEW_DEFAULT)
private var SharedPreferences.enabledHosters
by LazyMutable { preferences.getStringSet(PREF_HOSTER_KEY, PREF_HOSTER_DEFAULT)!! }
by preferences.delegate(PREF_HOSTER_KEY, PREF_HOSTER_DEFAULT)
private companion object {
private const val PREF_HOSTER_KEY = "dm_hoster_selection"
@@ -72,22 +70,17 @@ class DonghuaStream :
entries = INTERNAL_HOSTER_NAMES,
entryValues = PREF_HOSTER_ENTRY_VALUES,
default = PREF_HOSTER_DEFAULT,
) {
preferences.enabledHosters = it
}
)
screen.addSwitchPreference(
key = IGNORE_PREVIEW_KEY,
title = "Skip Preview episodes",
summary = "",
default = IGNORE_PREVIEW_DEFAULT,
) {
preferences.ignorePreview = it
}
)
}
override val prefQualityValues = arrayOf("2160p", "1440p", "1080p", "720p", "480p", "360p")
override val prefQualityEntries = prefQualityValues
override val prefQualityValues = listOf("2160p", "1440p", "1080p", "720p", "480p", "360p")
override fun episodeListParse(response: Response): List<SEpisode> = super.episodeListParse(response)
.filter { !it.name.contains("Preview", ignoreCase = true) || !preferences.ignorePreview }
@@ -100,21 +93,19 @@ class DonghuaStream :
private val rumbleExtractor by lazy { RumbleExtractor(client, headers) }
override fun getVideoList(url: String, name: String): List<Video> {
override suspend fun getVideoList(url: String, name: String): List<Video> {
val prefix = "$name - "
return runBlocking {
when {
preferences.enabledHosters.contains("dailymotion") && url.contains("dailymotion") ->
dailymotionExtractor.videosFromUrl(url, prefix = prefix)
preferences.enabledHosters.contains("streamplay") && url.contains("streamplay") ->
streamPlayExtractor.videosFromUrl(url, prefix = prefix)
preferences.enabledHosters.contains("ok.ru") && url.contains("ok.ru") ->
UrlUtils.fixUrl(url)?.let { okruExtractor.videosFromUrl(url = it, prefix = prefix) }
?: emptyList()
preferences.enabledHosters.contains("rumble") && url.contains("rumble") ->
rumbleExtractor.videosFromUrl(url, prefix = prefix)
else -> emptyList()
}
return when {
preferences.enabledHosters.contains("dailymotion") && url.contains("dailymotion") ->
dailymotionExtractor.videosFromUrl(url, prefix = prefix)
preferences.enabledHosters.contains("streamplay") && url.contains("streamplay") ->
streamPlayExtractor.videosFromUrl(url, prefix = prefix)
preferences.enabledHosters.contains("ok.ru") && url.contains("ok.ru") ->
UrlUtils.fixUrl(url)?.let { okruExtractor.videosFromUrl(url = it, prefix = prefix) }
?: emptyList()
preferences.enabledHosters.contains("rumble") && url.contains("rumble") ->
rumbleExtractor.videosFromUrl(url, prefix = prefix)
else -> emptyList()
}
}
@@ -123,7 +114,7 @@ class DonghuaStream :
private val qualityRegex by lazy { Regex("""(\d+)p""") }
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(videoSortPrefKey, videoSortPrefDefault)!!
val quality = preferences.videoSortPref
return sortedWith(
compareBy<Video>(
{ it.quality.contains(quality, true) },

View File

@@ -5,7 +5,6 @@ import aniyomi.lib.okruextractor.OkruExtractor
import aniyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import kotlinx.coroutines.runBlocking
class LuciferDonghua :
AnimeStream(
@@ -21,9 +20,9 @@ class LuciferDonghua :
private val dailymotionExtractor by lazy { DailymotionExtractor(client, headers) }
private val filelionsExtractor by lazy { StreamWishExtractor(client, headers) }
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
override suspend fun getVideoList(url: String, name: String): List<Video> {
val prefix = "$name - "
when {
return when {
url.contains("ok.ru") -> okruExtractor.videosFromUrl(url, prefix = prefix)
url.contains("dailymotion") -> dailymotionExtractor.videosFromUrl(url, prefix)
url.contains("filelions") -> filelionsExtractor.videosFromUrl(url, videoNameGen = { quality -> "FileLions - $quality" })
@@ -33,7 +32,7 @@ class LuciferDonghua :
// ============================= Utilities ==============================
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(videoSortPrefKey, videoSortPrefDefault)!!
val quality = preferences.videoSortPref
return sortedWith(
compareBy(
{ it.quality.contains(quality) },

View File

@@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.animeextension.es.animeytes
import androidx.preference.ListPreference
import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import aniyomi.lib.burstcloudextractor.BurstCloudExtractor
import aniyomi.lib.filemoonextractor.FilemoonExtractor
@@ -11,8 +11,9 @@ import aniyomi.lib.universalextractor.UniversalExtractor
import aniyomi.lib.youruploadextractor.YourUploadExtractor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import keiyoushi.utils.addListPreference
import keiyoushi.utils.delegate
import keiyoushi.utils.getPreferencesLazy
import kotlinx.coroutines.runBlocking
class AnimeYTES :
AnimeStream(
@@ -22,14 +23,13 @@ class AnimeYTES :
) {
override val preferences by getPreferencesLazy()
companion object {
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_DEFAULT = "1080"
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
override val prefQualityDefault = "1080"
override val prefQualityValues = listOf("1080", "720", "480", "360")
companion object {
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_DEFAULT = "Amazon"
private val SERVER_LIST = arrayOf(
private val SERVER_LIST = listOf(
"YourUpload",
"SendVid",
"BurstCloud",
@@ -50,21 +50,21 @@ class AnimeYTES :
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
private val universalExtractor by lazy { UniversalExtractor(client) }
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
when (name) {
"OK" -> okruExtractor.videosFromUrl(url)
"Stream" -> streamtapeExtractor.videosFromUrl(url)
"Send" -> sendvidExtractor.videosFromUrl(url)
"Your" -> youruploadExtractor.videoFromUrl(url, headers)
"Alpha" -> burstcloudExtractor.videoFromUrl(url, headers)
"Moon" -> filemoonExtractor.videosFromUrl(url)
else -> universalExtractor.videosFromUrl(url, headers)
}
override suspend fun getVideoList(url: String, name: String): List<Video> = when (name) {
"OK" -> okruExtractor.videosFromUrl(url)
"Stream" -> streamtapeExtractor.videosFromUrl(url)
"Send" -> sendvidExtractor.videosFromUrl(url)
"Your" -> youruploadExtractor.videoFromUrl(url, headers)
"Alpha" -> burstcloudExtractor.videoFromUrl(url, headers)
"Moon" -> filemoonExtractor.videosFromUrl(url)
else -> universalExtractor.videosFromUrl(url, headers)
}
private val SharedPreferences.serverPref by preferences.delegate(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
val quality = preferences.videoSortPref
val server = preferences.serverPref
return this.sortedWith(
compareBy(
{ it.quality.contains(server, true) },
@@ -75,36 +75,15 @@ class AnimeYTES :
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = "Preferred quality"
entries = QUALITY_LIST
entryValues = QUALITY_LIST
setDefaultValue(PREF_QUALITY_DEFAULT)
summary = "%s"
super.setupPreferenceScreen(screen) // Quality preferences
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_SERVER_KEY
title = "Preferred server"
entries = SERVER_LIST
entryValues = SERVER_LIST
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)
screen.addListPreference(
key = PREF_SERVER_KEY,
title = "Preferred server",
entries = SERVER_LIST,
entryValues = SERVER_LIST,
default = PREF_SERVER_DEFAULT,
summary = "%s",
)
}
}

View File

@@ -6,7 +6,6 @@ import aniyomi.lib.voeextractor.VoeExtractor
import aniyomi.lib.youruploadextractor.YourUploadExtractor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import kotlinx.coroutines.runBlocking
class Tiodonghua :
AnimeStream(
@@ -21,16 +20,13 @@ class Tiodonghua :
private val youruploadExtractor by lazy { YourUploadExtractor(client) }
private val mixdropExtractor by lazy { MixDropExtractor(client) }
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
when (name) {
"Okru" -> okruExtractor.videosFromUrl(url)
"Voe" -> voeExtractor.videosFromUrl(url)
"YourUpload" -> youruploadExtractor.videoFromUrl(url, headers)
"MixDrop" -> mixdropExtractor.videosFromUrl(url)
else -> emptyList()
}
override suspend fun getVideoList(url: String, name: String): List<Video> = when (name) {
"Okru" -> okruExtractor.videosFromUrl(url)
"Voe" -> voeExtractor.videosFromUrl(url)
"YourUpload" -> youruploadExtractor.videoFromUrl(url, headers)
"MixDrop" -> mixdropExtractor.videosFromUrl(url)
else -> emptyList()
}
override val fetchFilters: Boolean
get() = false
override val fetchFilters = false
}

View File

@@ -17,10 +17,8 @@ import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup
import keiyoushi.utils.parallelCatchingFlatMapBlocking
import keiyoushi.utils.useAsJsoup
import kotlinx.coroutines.runBlocking
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
@@ -76,11 +74,10 @@ class MyKdrama :
}
// ============================ Video Links =============================
override val prefQualityValues = arrayOf("1080p", "720p", "480p", "360p", "240p", "144p")
override val prefQualityEntries = prefQualityValues
override val prefQualityValues = listOf("1080p", "720p", "480p", "360p", "240p", "144p")
override fun videoListParse(response: Response): List<Video> {
val doc = response.use { it.asJsoup() }
val doc = response.useAsJsoup()
return doc.select(videoListSelector()).parallelCatchingFlatMapBlocking { element ->
val name = element.text()
val url = getHosterUrl(element)
@@ -101,14 +98,12 @@ class MyKdrama :
private val doodExtractor by lazy { DoodExtractor(client) }
private val vudeoExtractor by lazy { VudeoExtractor(client) }
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
when {
"ok.ru" in url -> okruExtractor.videosFromUrl(url)
"uqload" in url -> uqloadExtractor.videosFromUrl(url)
"dood" in url || "doodstream" in url -> doodExtractor.videosFromUrl(url)
"vudeo" in url -> vudeoExtractor.videosFromUrl(url)
else -> emptyList()
}
override suspend fun getVideoList(url: String, name: String): List<Video> = when {
"ok.ru" in url -> okruExtractor.videosFromUrl(url)
"uqload" in url -> uqloadExtractor.videosFromUrl(url)
"dood" in url || "doodstream" in url -> doodExtractor.videosFromUrl(url)
"vudeo" in url -> vudeoExtractor.videosFromUrl(url)
else -> emptyList()
}
// ============================= Utilities ==============================

View File

@@ -20,7 +20,7 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters
import eu.kanade.tachiyomi.network.GET
import kotlinx.coroutines.runBlocking
import keiyoushi.utils.tryParse
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import org.jsoup.nodes.Element
@@ -32,6 +32,9 @@ class AnimeIndo :
"https://animeindo.skin",
) {
// ============================== Filters ============================== (fetch URL)
override val animeListUrl = "$baseUrl/browse"
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/browse?sort=view&page=$page")
@@ -47,7 +50,22 @@ class AnimeIndo :
if (params.studios.isNotEmpty()) append(params.studios + "&")
}
return GET("$baseUrl/browse?page=$page&title=$query&$multiString&status=${params.status}&type=${params.type}&order=${params.order}")
val urlBuilder = baseUrl.toHttpUrl().newBuilder().apply {
addPathSegment("browse")
addQueryParameter("page", page.toString())
if (query.isNotBlank()) addQueryParameter("title", query)
if (params.status.isNotBlank()) addQueryParameter("status", params.status)
if (params.type.isNotBlank()) addQueryParameter("type", params.type)
if (params.order.isNotBlank()) addQueryParameter("order", params.order)
}
val url = urlBuilder.build().toString().let {
if (multiString.isNotBlank()) {
"$it&$multiString".removeSuffix("&")
} else {
it
}
}
return GET(url)
}
override fun searchAnimeSelector() = "div.animepost > div > a"
@@ -63,7 +81,7 @@ class AnimeIndo :
// ============================== Filters ===============================
override val filtersSelector = "div.filtersearch tbody > tr:not(:has(td.filter_title:contains(Search))) > td.filter_act"
override fun getFilterList(): AnimeFilterList = if (AnimeStreamFilters.filterInitialized()) {
override fun getFilterList(): AnimeFilterList = if (AnimeStreamFilters.filterInitialized() && AnimeStreamFilters.filterElements.isNotEmpty()) {
AnimeFilterList(
OrderFilter(orderFilterText),
StatusFilter(statusFilterText),
@@ -93,7 +111,7 @@ class AnimeIndo :
val num = ahref.text()
name = "Episode $num"
episode_number = num.trim().toFloatOrNull() ?: 0F
date_upload = element.selectFirst("span.date")?.text().toDate()
date_upload = element.selectFirst("span.date")?.text().let { dateFormatter.tryParse(it) }
}
// ============================ Video Links =============================
@@ -103,30 +121,28 @@ class AnimeIndo :
private val yourUploadExtractor by lazy { YourUploadExtractor(client) }
private val okruExtractor by lazy { OkruExtractor(client) }
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
with(name) {
when {
contains("streamtape") -> streamTapeExtractor.videoFromUrl(url)?.let(::listOf).orEmpty()
override suspend fun getVideoList(url: String, name: String): List<Video> = with(name) {
when {
contains("streamtape") -> streamTapeExtractor.videoFromUrl(url)?.let(::listOf).orEmpty()
contains("mp4") -> mp4uploadExtractor.videosFromUrl(url, headers)
contains("mp4") -> mp4uploadExtractor.videosFromUrl(url, headers)
contains("yourupload") -> yourUploadExtractor.videoFromUrl(url, headers)
contains("yourupload") -> yourUploadExtractor.videoFromUrl(url, headers)
url.contains("ok.ru") -> okruExtractor.videosFromUrl(url)
url.contains("ok.ru") -> okruExtractor.videosFromUrl(url)
contains("gdrive") -> {
val gdriveUrl = when {
baseUrl in url -> "https:" + url.toHttpUrl().queryParameter("data")!!
else -> url
}
gdrivePlayerExtractor.videosFromUrl(gdriveUrl, "Gdrive", headers)
contains("gdrive") -> {
val gdriveUrl = when {
baseUrl in url -> "https:" + url.toHttpUrl().queryParameter("data")!!
else -> url
}
gdrivePlayerExtractor.videosFromUrl(gdriveUrl, "Gdrive", headers)
}
else -> {
// just to detect video hosts easily
Log.i("AnimeIndo", "Unrecognized at getVideoList => Name -> $name || URL => $url")
emptyList()
}
else -> {
// just to detect video hosts easily
Log.i("AnimeIndo", "Unrecognized at getVideoList => Name -> $name || URL => $url")
emptyList()
}
}
}

View File

@@ -40,13 +40,14 @@ object AnimeIndoFilters {
)
}
private fun getPairListByIndex(index: Int) = filterElements.get(index)
.select("ul > li, td > label")
.map { element ->
private fun getPairListByIndex(index: Int) = filterElements.getOrNull(index)
?.select("ul > li, td > label")
?.mapNotNull { element ->
val key = element.text()
val value = element.selectFirst("input")!!.attr("value")
val value = element.selectFirst("input")?.attr("value") ?: return@mapNotNull null
Pair(key, value)
}.toTypedArray()
}?.toTypedArray()
?: arrayOf("" to "")
private val ORDER_LIST by lazy {
getPairListByIndex(0)

View File

@@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import eu.kanade.tachiyomi.network.GET
import keiyoushi.utils.tryParse
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import org.jsoup.nodes.Document
@@ -49,11 +50,11 @@ class MiniOppai :
}
}
element.selectFirst(".epl-sub")?.text()?.let { scanlator = it }
date_upload = element.selectFirst(".epl-date")?.text().toDate()
date_upload = element.selectFirst(".epl-date")?.text().let { dateFormatter.tryParse(it) }
}
// ============================ Video Links =============================
override fun getVideoList(url: String, name: String): List<Video> {
override suspend fun getVideoList(url: String, name: String): List<Video> {
return when {
"gdriveplayer" in url -> {
val playerUrl = buildString {

View File

@@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.animeextension.pl.desuonline
import androidx.preference.ListPreference
import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import aniyomi.lib.googledriveextractor.GoogleDriveExtractor
import aniyomi.lib.okruextractor.OkruExtractor
@@ -8,7 +8,8 @@ import aniyomi.lib.sibnetextractor.SibnetExtractor
import eu.kanade.tachiyomi.animeextension.pl.desuonline.extractors.CDAExtractor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import kotlinx.coroutines.runBlocking
import keiyoushi.utils.addListPreference
import keiyoushi.utils.delegate
import okhttp3.Response
import java.text.SimpleDateFormat
import java.util.Locale
@@ -35,8 +36,8 @@ class DesuOnline :
private val sibnetExtractor by lazy { SibnetExtractor(client) }
private val gdriveExtractor by lazy { GoogleDriveExtractor(client, headers) }
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
when {
override suspend fun getVideoList(url: String, name: String): List<Video> {
return when {
url.contains("ok.ru") -> okruExtractor.videosFromUrl(url, name)
url.contains("cda.pl") -> cdaExtractor.videosFromUrl(url, name)
@@ -44,7 +45,7 @@ class DesuOnline :
url.contains("sibnet") -> sibnetExtractor.videosFromUrl(url, prefix = "$name - ")
url.contains("drive.google.com") -> {
val id = Regex("[\\w-]{28,}").find(url)?.groupValues?.get(0) ?: return@runBlocking emptyList()
val id = Regex("[\\w-]{28,}").find(url)?.groupValues?.get(0) ?: return emptyList()
gdriveExtractor.videosFromUrl("https://drive.google.com/uc?id=$id", videoName = name)
}
@@ -54,9 +55,11 @@ class DesuOnline :
// ============================= Utilities ==============================
private val SharedPreferences.serverPref by preferences.delegate(prefServerKey, prefServerDefault)
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(videoSortPrefKey, videoSortPrefDefault)!!
val server = preferences.getString(prefServerKey, prefServerDefault)!!
val quality = preferences.videoSortPref
val server = preferences.serverPref
return sortedWith(
compareBy(
@@ -72,20 +75,13 @@ class DesuOnline :
override fun setupPreferenceScreen(screen: PreferenceScreen) {
super.setupPreferenceScreen(screen) // Quality preferences
ListPreference(screen.context).apply {
key = prefServerKey
title = "Preferred server"
entries = arrayOf("CDA", "Sibnet", "Google Drive", "ok.ru")
entryValues = arrayOf("CDA", "sibnet", "gd", "okru")
setDefaultValue(prefServerDefault)
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)
screen.addListPreference(
key = prefServerKey,
title = "Preferred server",
entries = listOf("CDA", "Sibnet", "Google Drive", "ok.ru"),
entryValues = listOf("CDA", "sibnet", "gd", "okru"),
default = prefServerDefault,
summary = "%s",
)
}
}

View File

@@ -52,8 +52,7 @@ class DarkMahou :
}
// ============================ Video Links =============================
override val prefQualityValues = arrayOf("1080p", "720p", "480p", "360p", "240p")
override val prefQualityEntries = prefQualityValues
override val prefQualityValues = listOf("1080p", "720p", "480p", "360p", "240p")
private val darkmahouExtractor by lazy { DarkMahouExtractor(client, headers) }

View File

@@ -6,8 +6,8 @@ import eu.kanade.tachiyomi.animeextension.sr.animebalkan.extractors.MailRuExtrac
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.runBlocking
import eu.kanade.tachiyomi.network.awaitSuccess
import keiyoushi.utils.useAsJsoup
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
@@ -25,7 +25,7 @@ class AnimeBalkan :
}
// ============================ Video Links =============================
override fun getHosterUrl(element: Element): String {
override suspend fun getHosterUrl(element: Element): String {
if (element.text().contains("Server AB")) {
return element.attr("value")
}
@@ -37,8 +37,8 @@ class AnimeBalkan :
private val mailruExtractor by lazy { MailRuExtractor(client, headers) }
private val okruExtractor by lazy { OkruExtractor(client) }
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
when {
override suspend fun getVideoList(url: String, name: String): List<Video> {
return when {
"Server OK" in name || "ok.ru" in url -> okruExtractor.videosFromUrl(url)
"Server Ru" in name || "mail.ru" in url -> mailruExtractor.videosFromUrl(url)
@@ -57,9 +57,9 @@ class AnimeBalkan :
}
"Server AB" in name && baseUrl in url -> {
val doc = client.newCall(GET(url)).execute().asJsoup()
val doc = client.newCall(GET(url)).awaitSuccess().useAsJsoup()
val videoUrl = doc.selectFirst("source")?.attr("src")
?: return@runBlocking emptyList()
?: return emptyList()
listOf(Video(videoUrl, "Server AB - Default", videoUrl))
}
@@ -67,6 +67,5 @@ class AnimeBalkan :
}
}
override val prefQualityValues = arrayOf("1080p", "720p", "480p", "360p", "240p")
override val prefQualityEntries = prefQualityValues
override val prefQualityValues = listOf("1080p", "720p", "480p", "360p", "240p")
}

View File

@@ -19,7 +19,6 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters
import eu.kanade.tachiyomi.network.GET
import kotlinx.coroutines.runBlocking
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
@@ -97,34 +96,29 @@ class AsyaAnimeleri :
override val episodePrefix = "Bölüm"
// ============================ Video Links =============================
override val prefQualityValues = arrayOf("1080p", "720p", "480p", "360p", "240p", "144p")
override val prefQualityEntries = prefQualityValues
override val prefQualityValues = listOf("1080p", "720p", "480p", "360p", "240p", "144p")
private val vkExtractor by lazy { VkExtractor(client, headers) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val sibnetExtractor by lazy { SibnetExtractor(client) }
private val gdrivePlayerExtractor by lazy { GdrivePlayerExtractor(client) }
private val doodExtractor by lazy { DoodExtractor(client) }
// private val dailyExtractor by lazy { DailymotionExtractor(client, headers) }
override fun getVideoList(url: String, name: String): List<Video> = runBlocking {
when (name.lowercase().trim()) {
"vk" -> vkExtractor.videosFromUrl(url)
override suspend fun getVideoList(url: String, name: String): List<Video> = when (name.lowercase().trim()) {
"vk" -> vkExtractor.videosFromUrl(url)
"ok.ru" -> okruExtractor.videosFromUrl(url)
"ok.ru" -> okruExtractor.videosFromUrl(url)
"sibnet" -> sibnetExtractor.videosFromUrl(url)
"sibnet" -> sibnetExtractor.videosFromUrl(url)
// "daily" -> dailyExtractor.videosFromUrl(url)
"dood", "doodstream" -> doodExtractor.videoFromUrl(url)?.let(::listOf) ?: emptyList()
"dood", "doodstream" -> doodExtractor.videoFromUrl(url)?.let(::listOf) ?: emptyList()
"gdrive" -> {
val newUrl = "https://gdriveplayer.to/embed2.php?link=$url"
gdrivePlayerExtractor.videosFromUrl(newUrl, "Gdrive", headers)
}
else -> emptyList()
"gdrive" -> {
val newUrl = "https://gdriveplayer.to/embed2.php?link=$url"
gdrivePlayerExtractor.videosFromUrl(newUrl, "Gdrive", headers)
}
else -> emptyList()
}
// ============================= Utilities ==============================

View File

@@ -13,7 +13,8 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import keiyoushi.utils.tryParse
import keiyoushi.utils.useAsJsoup
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
@@ -119,12 +120,12 @@ class TRAnimeCI :
name = "Bölüm $epNum"
episode_number = epNum.toFloat()
date_upload = element.selectFirst(".epl-date")?.text().toDate()
date_upload = element.selectFirst(".epl-date")?.text().let { dateFormatter.tryParse(it) }
}
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val doc = response.asJsoup()
val doc = response.useAsJsoup()
val script = doc.selectFirst("script:containsData(let video_source)")!!.data()
return script.substringAfter("[").substringBefore("]")
.split("{")