fix(ar/animelek): Fix thumbnails & prevent DDoS (#1572)

* fix: Fix popular animes page

* fix: Fix search page

* fix: Fix episode list order

* feat: Implement latest updates page

* refactor: Minor refactoration

* chore: Bump version
This commit is contained in:
Claudemirovsky 2023-05-02 09:20:43 -03:00 committed by GitHub
parent 48bb1811f4
commit 6e7dec14e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 192 deletions

View File

@ -1,11 +1,13 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
ext {
extName = 'AnimeLek'
pkgNameSuffix = 'ar.animelek'
extClass = '.AnimeLek'
extVersionCode = 22
extVersionCode = 23
libVersion = '13'
}
@ -14,6 +16,7 @@ dependencies {
implementation(project(':lib-okru-extractor'))
implementation(project(':lib-streamsb-extractor'))
implementation(project(':lib-dood-extractor'))
implementation(project(':lib-vidbom-extractor'))
}
apply from: "$rootDir/common.gradle"

View File

@ -5,7 +5,6 @@ import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.ar.animelek.extractors.SharedExtractor
import eu.kanade.tachiyomi.animeextension.ar.animelek.extractors.VidBomExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.SAnime
@ -16,9 +15,9 @@ import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamsbextractor.StreamSBExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -36,7 +35,7 @@ class AnimeLek : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val lang = "ar"
override val supportsLatest = false
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
@ -44,192 +43,150 @@ class AnimeLek : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// Popular Anime
// ============================== Popular ===============================
override fun popularAnimeSelector() = "div.slider-episode-container div.episodes-card-container"
override fun popularAnimeSelector(): String = "div.anime-card-details div.anime-card-title"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/قائمة-الأنمي/?page=$page") // page/$page
override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("h3 a").attr("href"))
// anime.thumbnail_url = element.select("div.img div.picture img").attr("src")
anime.title = element.attr("title")
return anime
return SAnime.create().apply {
val ahref = element.selectFirst("h3 a")!!
setUrlWithoutDomain(ahref.attr("href"))
title = ahref.text()
thumbnail_url = element.selectFirst("img")?.attr("src")
}
}
override fun popularAnimeNextPageSelector(): String = "li.page-item a[rel=next]"
// Episodes
override fun popularAnimeNextPageSelector() = null
// ============================== Episodes ==============================
override fun episodeListSelector() = "div.ep-card-anime-title-detail h3 a"
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.attr("href"))
episode.name = element.text()
val epNum = getNumberFromEpsString(element.text())
episode.episode_number = when {
(epNum.isNotEmpty()) -> epNum.toFloat()
return SEpisode.create().apply {
setUrlWithoutDomain(element.attr("href"))
val text = element.text()
name = text
val epNum = text.filter { it.isDigit() }
episode_number = when {
(epNum.isNotEmpty()) -> epNum.toFloatOrNull() ?: 1F
else -> 1F
}
return episode
}
}
private fun getNumberFromEpsString(epsStr: String): String {
return epsStr.filter { it.isDigit() }
}
// Video urls
override fun videoListRequest(episode: SEpisode): Request {
val iframe = baseUrl + episode.url
return GET(iframe)
}
override fun episodeListParse(response: Response) = super.episodeListParse(response).reversed()
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
return videosFromElement(document)
return response.use { videosFromElement(it.asJsoup()) }
}
override fun videoListSelector() = "ul#episode-servers li.watch a"
private fun videosFromElement(document: Document): List<Video> {
val videoList = mutableListOf<Video>()
val elements = document.select(videoListSelector())
for (element in elements) {
val streamSBServers = listOf(
"sbembed", "sbplay", "sbvideo.net",
"streamsb.net", "cloudemb.com", "playersb.com",
"tubesb.com", "embedsb.com", "watchsb.com",
"japopav.tv", "viewsb.com", "sbfast",
"sbfull.com", "javplaya.com", "ssbstream.net",
"p1ayerjavseen.com", "sbthe.com",
)
val vidbomServers = listOf("vidbam", "vadbam", "vidbom", "vidbm")
return document.select(videoListSelector()).mapNotNull { element ->
val url = element.attr("data-ep-url")
val qualityy = element.text()
val location = element.ownerDocument()!!.location()
val videoHeaders = Headers.headersOf("Referer", location)
when {
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)
videoList.addAll(videos)
streamSBServers.any(url::contains) -> {
StreamSBExtractor(client).videosFromUrl(url, headers)
}
vidbomServers.any(url::contains) -> {
VidBomExtractor(client).videosFromUrl(url)
}
url.contains("dood") -> {
val video = DoodExtractor(client).videoFromUrl(url, qualityy)
if (video != null) {
videoList.add(video)
}
DoodExtractor(client).videoFromUrl(url, qualityy)
?.let(::listOf)
}
url.contains("ok.ru") -> {
val videos = OkruExtractor(client).videosFromUrl(url)
videoList.addAll(videos)
OkruExtractor(client).videosFromUrl(url)
}
url.contains("streamtape") -> {
val video = StreamTapeExtractor(client).videoFromUrl(url)
if (video != null) {
videoList.add(video)
}
StreamTapeExtractor(client).videoFromUrl(url)
?.let(::listOf)
}
url.contains("4shared") -> {
val video = SharedExtractor(client).videoFromUrl(url, qualityy)
if (video != null) {
videoList.add(video)
SharedExtractor(client).videoFromUrl(url, qualityy)
?.let(::listOf)
}
else -> null
}
url.contains("vidbam") -> { // , vidbm, vidbom
val videos = VidBomExtractor(client).videosFromUrl(url)
videoList.addAll(videos)
}
url.contains("vidbm") -> { // , vidbm, vidbom
val videos = VidBomExtractor(client).videosFromUrl(url)
videoList.addAll(videos)
}
url.contains("vidbom") -> { // , vidbm, vidbom
val videos = VidBomExtractor(client).videosFromUrl(url)
videoList.addAll(videos)
}
}
}
return videoList
}.flatten()
}
override fun videoFromElement(element: Element) = throw Exception("not used")
override fun videoUrlParse(document: Document) = throw Exception("not used")
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "480p")
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
}
// search
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("h3 a").attr("href"))
// anime.thumbnail_url = element.select("div.img div.picture img").attr("src")
anime.title = element.attr("title")
return anime
}
// =============================== Search ===============================
// TODO: Add search filters
override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector(): String = "li.page-item a[rel=next]"
override fun searchAnimeSelector(): String = "div.anime-card-details div.anime-card-title"
override fun searchAnimeSelector() = "div.anime-list-content div.anime-card-container"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = GET("$baseUrl/search/?s=$query&page=$page")
// Anime Details
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = GET("$baseUrl/search/?s=$query&page=$page")
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = document.select("div.anime-thumbnail-pic img").attr("src")
anime.title = document.select("h1.anime-details-title").text()
anime.genre = document.select("ul.anime-genres li a, div.full-list-info:contains(النوع) small a, div.full-list-info:contains(الموسم) small a").joinToString(", ") { it.text() }
anime.description = document.select("p.anime-story").text()
// anime.author = document.select("div:contains(الاستديو) span > a").text()
document.select("div.full-list-info:contains(حالة) small a")?.text()?.also { statusText ->
return SAnime.create().apply {
val infos = document.selectFirst("div.anime-container-infos")!!
val datas = document.selectFirst("div.anime-container-data")!!
thumbnail_url = infos.selectFirst("img")!!.attr("src")
title = datas.selectFirst("h1")!!.text()
genre = datas.select("ul li > a").joinToString { it.text() }
status = infos.selectFirst("div.full-list-info:contains(حالة الأنمي) a")?.text()?.let {
when {
statusText.contains("يعرض", true) -> anime.status = SAnime.ONGOING
statusText.contains("مكتمل", true) -> anime.status = SAnime.COMPLETED
else -> anime.status = SAnime.UNKNOWN
it.contains("يعرض الان") -> SAnime.ONGOING
it.contains("مكتمل", true) -> SAnime.COMPLETED
else -> null
}
} ?: SAnime.UNKNOWN
artist = document.selectFirst("div:contains(المخرج) > span.info")?.text()
description = buildString {
append(datas.selectFirst("p.anime-story")!!.text() + "\n\n")
infos.select("div.full-list-info").forEach {
append(it.text() + "\n")
}
}
}
anime.artist = document.select("div:contains(المخرج) > span.info").text()
return anime
}
// Latest
// =============================== Latest ===============================
override fun latestUpdatesNextPageSelector() = searchAnimeNextPageSelector()
override fun latestUpdatesNextPageSelector(): String = throw Exception("Not used")
override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
override fun latestUpdatesFromElement(element: Element): SAnime = throw Exception("Not used")
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/episode/?page=$page")
override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used")
override fun latestUpdatesSelector(): String = throw Exception("Not used")
// Preferences
override fun latestUpdatesSelector() = "div.episodes-list-content div.episodes-card-container"
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "Doodstream", "StreamTape")
entryValues = arrayOf("1080", "720", "480", "360", "Doodstream", "StreamTape")
setDefaultValue("1080")
key = PREF_QUALITY_KEY
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
entryValues = PREF_QUALITY_ENTRIES
setDefaultValue(PREF_QUALITY_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
@ -241,4 +198,19 @@ class AnimeLek : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
screen.addPreference(videoQualityPref)
}
// ============================= Utilities ==============================
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
return sortedWith(
compareBy { it.quality.contains(quality) },
).reversed()
}
companion object {
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Preferred quality"
private const val PREF_QUALITY_DEFAULT = "720p"
private val PREF_QUALITY_ENTRIES = arrayOf("1080p", "720p", "480p", "360p", "Doodstream", "StreamTape")
}
}

View File

@ -1,54 +0,0 @@
package eu.kanade.tachiyomi.animeextension.ar.animelek.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
class VidBomExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val doc = client.newCall(GET(url)).execute().asJsoup()
val script = doc.selectFirst("script:containsData(sources)")!!
val data = script.data().substringAfter("sources: [").substringBefore("],")
val sources = data.split("file:\"").drop(1)
val videoList = mutableListOf<Video>()
for (source in sources) {
val src = source.substringBefore("\"")
val quality = "Vidbom:" + source.substringAfter("label:\"").substringBefore("\"") // .substringAfter("format: '")
val video = Video(src, quality, src)
videoList.add(video)
}
return videoList
/*Log.i("looool", "$js")
val json = JSONObject(js)
Log.i("looool", "$json")
val videoList = mutableListOf<Video>()
val jsonArray = json.getJSONArray("sources")
for (i in 0 until jsonArray.length()) {
val `object` = jsonArray.getJSONObject(i)
val videoUrl = `object`.getString("file")
Log.i("looool", videoUrl)
val quality = "Vidbom:" + `object`.getString("label")
videoList.add(Video(videoUrl, quality, videoUrl))
}
return videoList*/
/*if (jas.contains("sources")) {
val js = script.data()
val json = JSONObject(js)
val videoList = mutableListOf<Video>()
val jsonArray = json.getJSONArray("sources")
for (i in 0 until jsonArray.length()) {
val `object` = jsonArray.getJSONObject(i)
val videoUrl = `object`.getString("file")
Log.i("lol", videoUrl)
val quality = "Vidbom:" + `object`.getString("label")
videoList.add(Video(videoUrl, quality, videoUrl))
}
return videoList
} else {
val videoList = mutableListOf<Video>()
videoList.add(Video(url, "no 2video", null))
return videoList
}*/
}
}