fix(ar/tuktukcinema): Fix empty video list (#2698)
Co-authored-by: jmir1 <jhmiramon@gmail.com>
This commit is contained in:
@ -5,7 +5,7 @@ ext {
|
|||||||
extName = 'توك توك سينما'
|
extName = 'توك توك سينما'
|
||||||
pkgNameSuffix = 'ar.tuktukcinema'
|
pkgNameSuffix = 'ar.tuktukcinema'
|
||||||
extClass = '.Tuktukcinema'
|
extClass = '.Tuktukcinema'
|
||||||
extVersionCode = 14
|
extVersionCode = 15
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,7 +15,6 @@ dependencies {
|
|||||||
implementation(project(':lib-dood-extractor'))
|
implementation(project(':lib-dood-extractor'))
|
||||||
implementation(project(':lib-streamtape-extractor'))
|
implementation(project(':lib-streamtape-extractor'))
|
||||||
implementation(project(':lib-vidbom-extractor'))
|
implementation(project(':lib-vidbom-extractor'))
|
||||||
implementation(project(':lib-streamwish-extractor'))
|
|
||||||
implementation(project(':lib-playlist-utils'))
|
implementation(project(':lib-playlist-utils'))
|
||||||
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
|
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import androidx.preference.EditTextPreference
|
|||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.animeextension.BuildConfig
|
import eu.kanade.tachiyomi.animeextension.BuildConfig
|
||||||
import eu.kanade.tachiyomi.animeextension.ar.tuktukcinema.extractors.UpStreamExtractor
|
|
||||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||||
@ -17,10 +16,11 @@ import eu.kanade.tachiyomi.animesource.model.Video
|
|||||||
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
||||||
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
|
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
|
||||||
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
||||||
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
|
||||||
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
||||||
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
|
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
|
||||||
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
|
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
|
||||||
|
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
|
||||||
|
import dev.datlag.jsunpacker.JsUnpacker
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -108,7 +108,7 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val url = response.request.url.toString()
|
val url = response.request.url.toString()
|
||||||
if (document.select(seasonsNextPageSelector()).isNullOrEmpty()) {
|
if (document.select(seasonsNextPageSelector()).isNullOrEmpty()) {
|
||||||
addEpisodeNew(url, "مشاهدة")
|
addEpisodeNew("$url/watch/", "مشاهدة")
|
||||||
} else {
|
} else {
|
||||||
document.select(seasonsNextPageSelector()).reversed().forEach { season ->
|
document.select(seasonsNextPageSelector()).reversed().forEach { season ->
|
||||||
val seasonNum = season.select("h3").text()
|
val seasonNum = season.select("h3").text()
|
||||||
@ -137,8 +137,6 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun episodeFromElement(element: Element): SEpisode = throw Exception("not used")
|
override fun episodeFromElement(element: Element): SEpisode = throw Exception("not used")
|
||||||
|
|
||||||
// ============================ video links ============================
|
// ============================ video links ============================
|
||||||
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
|
|
||||||
|
|
||||||
override fun videoListRequest(episode: SEpisode): Request {
|
override fun videoListRequest(episode: SEpisode): Request {
|
||||||
val refererHeaders = headers.newBuilder().apply {
|
val refererHeaders = headers.newBuilder().apply {
|
||||||
add("Referer", "$baseUrl/")
|
add("Referer", "$baseUrl/")
|
||||||
@ -152,7 +150,7 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
return document.select(videoListSelector()).parallelMap {
|
return document.select(videoListSelector()).parallelMap {
|
||||||
runCatching { extractVideos(it.attr("data-link")) }.getOrElse { emptyList() }
|
runCatching { extractVideos(it) }.getOrElse { emptyList() }
|
||||||
}.flatten()
|
}.flatten()
|
||||||
}
|
}
|
||||||
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
|
private inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> =
|
||||||
@ -160,20 +158,20 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
|
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractVideos(url: String): List<Video> {
|
private fun extractVideos(element: Element): List<Video> {
|
||||||
|
val url = element.attr("data-link")
|
||||||
|
val txt = element.text()
|
||||||
return when {
|
return when {
|
||||||
STREAMWISH_REGEX.containsMatchIn(url) -> {
|
"Main" in txt -> {
|
||||||
streamWishExtractor.videosFromUrl(url)
|
videosFromMain(url)
|
||||||
}
|
}
|
||||||
url.contains("ok") -> {
|
url.contains("ok") -> {
|
||||||
OkruExtractor(client).videosFromUrl(url)
|
OkruExtractor(client).videosFromUrl(url)
|
||||||
}
|
}
|
||||||
VIDBOM_REGEX.containsMatchIn(url) -> {
|
"Vidbom" in txt || "Vidshare" in txt || "Govid" in txt -> {
|
||||||
val finalUrl = VIDBOM_REGEX.find(url)!!.groupValues[0]
|
VidBomExtractor(client).videosFromUrl(url)
|
||||||
VidBomExtractor(client).videosFromUrl("https://www.$finalUrl")
|
|
||||||
}
|
}
|
||||||
DOOD_REGEX.containsMatchIn(url) -> {
|
"Doodstream" in txt -> {
|
||||||
val finalUrl = DOOD_REGEX.find(url)!!.groupValues[0]
|
|
||||||
DoodExtractor(client).videoFromUrl(url, "Dood mirror")?.let(::listOf)
|
DoodExtractor(client).videoFromUrl(url, "Dood mirror")?.let(::listOf)
|
||||||
}
|
}
|
||||||
url.contains("uqload") -> {
|
url.contains("uqload") -> {
|
||||||
@ -182,35 +180,36 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
url.contains("tape") -> {
|
url.contains("tape") -> {
|
||||||
StreamTapeExtractor(client).videoFromUrl(url)?.let(::listOf)
|
StreamTapeExtractor(client).videoFromUrl(url)?.let(::listOf)
|
||||||
}
|
}
|
||||||
url.contains("upstream", ignoreCase = true) -> {
|
"Upstream" in txt || "Streamruby" in txt || "Streamwish" in txt -> {
|
||||||
UpStreamExtractor(client).videoFromUrl(url.replace("//", "//www."))
|
videosFromOthers(url, txt)
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun List<Video>.sort(): List<Video> {
|
override fun List<Video>.sort(): List<Video> {
|
||||||
val quality = preferences.getString("preferred_quality", null)
|
val quality = preferences.getString("preferred_quality", null)!!
|
||||||
if (quality != null) {
|
return sortedWith(
|
||||||
val newList = mutableListOf<Video>()
|
compareBy { it.quality.contains(quality) },
|
||||||
var preferred = 0
|
).reversed()
|
||||||
for (video in this) {
|
|
||||||
if (video.quality.contains(quality)) {
|
|
||||||
newList.add(preferred, video)
|
|
||||||
preferred++
|
|
||||||
} else {
|
|
||||||
newList.add(video)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newList
|
|
||||||
}
|
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun videoUrlParse(document: Document) = throw Exception("not used")
|
override fun videoUrlParse(document: Document) = throw Exception("not used")
|
||||||
|
|
||||||
override fun videoFromElement(element: Element) = throw Exception("not used")
|
override fun videoFromElement(element: Element) = throw Exception("not used")
|
||||||
|
|
||||||
|
private fun videosFromMain(url: String): List<Video> {
|
||||||
|
val jsE = client.newCall(GET(url)).execute().asJsoup().selectFirst("script:containsData(player)")!!.data()
|
||||||
|
val fileLinks = JsUnpacker.unpackAndCombine(jsE)!!.substringAfter("file").substringBefore("\",")
|
||||||
|
return Regex("\\[(.*?)\\](.*?mp4)").findAll(fileLinks).map {
|
||||||
|
Video(it.groupValues[2], "Main: " + it.groupValues[1], it.groupValues[2])
|
||||||
|
}.toList()
|
||||||
|
}
|
||||||
|
private fun videosFromOthers(url: String, prefix: String): List<Video> {
|
||||||
|
val jsE = client.newCall(GET(url)).execute().asJsoup().selectFirst("script:containsData(source)")!!.data()
|
||||||
|
val masterUrl = JsUnpacker.unpackAndCombine(jsE)!!.substringAfter("file").substringAfter("\"").substringBefore("\"")
|
||||||
|
return PlaylistUtils(client).extractFromHls(masterUrl, url, videoNameGen = { "$prefix - $it" } )
|
||||||
|
}
|
||||||
// ============================ search ============================
|
// ============================ search ============================
|
||||||
|
|
||||||
override fun searchAnimeSelector(): String = "div.Block--Item"
|
override fun searchAnimeSelector(): String = "div.Block--Item"
|
||||||
@ -334,8 +333,8 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val videoQualityPref = ListPreference(screen.context).apply {
|
val videoQualityPref = ListPreference(screen.context).apply {
|
||||||
key = "preferred_quality"
|
key = "preferred_quality"
|
||||||
title = "Preferred quality"
|
title = "Preferred quality"
|
||||||
entries = arrayOf("1080p", "720p", "480p", "360p", "240p", "DoodStream", "Uqload")
|
entries = arrayOf("720p", "480p", "Low", "Normal", "HD", "UHD", "DoodStream", "Uqload")
|
||||||
entryValues = arrayOf("1080", "720", "480", "360", "240", "Dood", "Uqload")
|
entryValues = arrayOf("720", "480", "Low", "Normal", "HD", "UHD", "Dood", "Uqload")
|
||||||
setDefaultValue("1080")
|
setDefaultValue("1080")
|
||||||
summary = "%s"
|
summary = "%s"
|
||||||
|
|
||||||
@ -349,9 +348,4 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
screen.addPreference(defaultDomain)
|
screen.addPreference(defaultDomain)
|
||||||
screen.addPreference(videoQualityPref)
|
screen.addPreference(videoQualityPref)
|
||||||
}
|
}
|
||||||
companion object {
|
|
||||||
private val VIDBOM_REGEX = Regex("(?:v[aie]d[bp][aoe]?m|myvii?d|govad|segavid|v[aei]{1,2}dshar[er]?)\\.(?:com|net|org|xyz)(?::\\d+)?/(?:embed[/-])?([A-Za-z0-9]+).html")
|
|
||||||
private val DOOD_REGEX = Regex("(do*d(?:stream)?\\.(?:com?|watch|to|s[ho]|cx|la|w[sf]|pm|re|yt|stream))/[de]/([0-9a-zA-Z]+)|ds2play")
|
|
||||||
private val STREAMWISH_REGEX = Regex("ajmidyad|alhayabambi|atabknh[ks]|https://.*\\.sbs/e/")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.animeextension.ar.tuktukcinema.extractors
|
|
||||||
|
|
||||||
import dev.datlag.jsunpacker.JsUnpacker
|
|
||||||
import eu.kanade.tachiyomi.animesource.model.Video
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
|
|
||||||
class UpStreamExtractor(private val client: OkHttpClient) {
|
|
||||||
fun videoFromUrl(url: String): List<Video> {
|
|
||||||
val doc = client.newCall(GET(url)).execute().asJsoup()
|
|
||||||
val script = doc.selectFirst("script:containsData(sources)")?.data() ?: return emptyList()
|
|
||||||
val scriptData = if ("eval" in script) JsUnpacker.unpackAndCombine(script)!! else script
|
|
||||||
val m3u8 = Regex("sources:\\s*\\[\\{\\s*\\t*file:\\s*[\"']([^\"']+)").find(scriptData)!!.groupValues[1]
|
|
||||||
return Video(m3u8, "Upstream", m3u8).let(::listOf)
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user