Skip to content

Commit

Permalink
chore(ar/faselhd): Refactor + add more filters (#3267)
Browse files Browse the repository at this point in the history
  • Loading branch information
adly98 authored May 26, 2024
1 parent f35c36d commit a56d5f4
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 118 deletions.
6 changes: 5 additions & 1 deletion src/ar/faselhd/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
ext {
extName = 'FASELHD'
extClass = '.FASELHD'
extVersionCode = 14
extVersionCode = 15
}

apply from: "$rootDir/common.gradle"

dependencies {
implementation(project(":lib:playlist-utils"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ 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.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
Expand All @@ -36,29 +37,31 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}

private val webViewResolver by lazy { WebViewResolver() }

private val playlistUtils by lazy { PlaylistUtils(client, headers) }

override fun headersBuilder(): Headers.Builder {
return super.headersBuilder()
.add("Referer", baseUrl)
}

// Popular Anime

// ============================== Popular ===============================
override fun popularAnimeSelector(): String = "div#postList div.col-xl-2 a"

override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/anime/page/$page")
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/anime/page/$page", headers)

override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
anime.title = element.select("div.imgdiv-class img").attr("alt")
// anime.thumbnail_url = element.select("div.imgdiv-class img").attr("data-src")

anime.thumbnail_url = element.select("div.imgdiv-class img").attr("data-src")
return anime
}

override fun popularAnimeNextPageSelector(): String = "ul.pagination li a.page-link:contains(›)"

// Episodes
// ============================== Episodes ==============================
override fun episodeListSelector() = "div.epAll a"

private fun seasonsNextPageSelector(seasonNumber: Int) = "div#seasonList div.col-xl-2:nth-child($seasonNumber)" // "div.List--Seasons--Episodes > a:nth-child($seasonNumber)"
Expand Down Expand Up @@ -104,50 +107,29 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return episode
}

// Video urls
// ============================ Video Links =============================

override fun videoListSelector() = throw UnsupportedOperationException()

override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val getSources = "master.m3u8"
val referer = Headers.headersOf("Referer", "$baseUrl/")
val iframe = document.selectFirst("iframe")!!.attr("src").substringBefore("&img")
val webViewIncpec = client.newBuilder().addInterceptor(GetSourcesInterceptor(getSources, client)).build()
val lol = webViewIncpec.newCall(GET(iframe, referer)).execute().body.string()
val videoList = mutableListOf<Video>()
lol.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:").forEach {
val quality = it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p"
val videoUrl = it.substringAfter("\n").substringBefore("\n").replace("https", "http")
videoList.add(Video(videoUrl, quality, videoUrl, headers = referer))
}
return videoList
val webViewResult = webViewResolver.getUrl(iframe, headers)
return if (webViewResult.isNotBlank()) playlistUtils.extractFromHls(webViewResult) else emptyList()
}

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
val quality = preferences.getString("preferred_quality", "1080")!!
return sortedWith(
compareBy { it.quality.contains(quality) },
).reversed()
}

override fun videoFromElement(element: Element) = throw UnsupportedOperationException()

override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()

// search

// =============================== Search ===============================
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
Expand All @@ -161,27 +143,32 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun searchAnimeSelector(): String = "div#postList div.col-xl-2 a"

override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val filterList = if (filters.isEmpty()) getFilterList() else filters
val sectionFilter = filterList.find { it is SectionFilter } as SectionFilter
val categoryFilter = filterList.find { it is CategoryFilter } as CategoryFilter
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
return if (query.isNotBlank()) {
GET("$baseUrl/page/$page?s=$query", headers)
} else {
val url = "$baseUrl/".toHttpUrlOrNull()!!.newBuilder()
filters.forEach { filter ->
when (filter) {
is GenreFilter -> url.addPathSegment(filter.toUriPart())
else -> {}
}
if (sectionFilter.state != 0) {
url.addPathSegment(sectionFilter.toUriPart())
} else if (categoryFilter.state != 0) {
url.addPathSegment(categoryFilter.toUriPart())
url.addPathSegment(genreFilter.toUriPart().lowercase())
} else {
throw Exception("من فضلك اختر قسم او نوع")
}
url.addPathSegment("page")
url.addPathSegment("$page")
GET(url.toString(), headers)
}
}

// Details

// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.title = document.select("div.title.h1").text()
anime.title = document.select("meta[itemprop=name]").attr("content")
anime.genre = document.select("span:contains(تصنيف) > a, span:contains(مستوى) > a").joinToString(", ") { it.text() }
// anime.thumbnail_url = document.select("div.posterImg img.poster").attr("src")

Expand All @@ -191,21 +178,19 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} else {
cover
}
anime.description = document.select("div.singleDesc p").text()
anime.description = document.select("div.singleDesc").text()
anime.status = parseStatus(document.select("span:contains(حالة)").text().replace("حالة ", "").replace("المسلسل : ", ""))
return anime
}

private fun parseStatus(statusString: String): Int {
return when (statusString) {
"مستمر" -> SAnime.ONGOING
// "Completed" -> SAnime.COMPLETED
else -> SAnime.COMPLETED
}
}

// Latest

// =============================== Latest ===============================
override fun latestUpdatesNextPageSelector(): String = "ul.pagination li a.page-link:contains(›)"

override fun latestUpdatesFromElement(element: Element): SAnime {
Expand All @@ -216,41 +201,66 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return anime
}

override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/most_recent/page/$page")
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/most_recent/page/$page", headers)

override fun latestUpdatesSelector(): String = "div#postList div.col-xl-2 a"

// Filters
// ============================ Filters =============================

override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("NOTE: Ignored if using text search!"),
AnimeFilter.Header("هذا القسم يعمل لو كان البحث فارع"),
SectionFilter(),
AnimeFilter.Separator(),
GenreFilter(getGenreList()),
AnimeFilter.Header("الفلتره تعمل فقط لو كان اقسام الموقع على 'اختر'"),
CategoryFilter(),
GenreFilter(),
)

private class GenreFilter(vals: Array<Pair<String, String>>) : UriPartFilter("تصنيف المسلسلات", vals)

private fun getGenreList() = arrayOf(
Pair("افلام انمي", "anime-movies"),
Pair("جميع الافلام", "all-movies"),
Pair("جوائز الأوسكار لهذا العام⭐", "oscars-winners"),
Pair("افلام اجنبي", "movies"),
Pair("افلام مدبلجة", "dubbed-movies"),
Pair("افلام هندي", "hindi"),
Pair("افلام اسيوي", "asian-movies"),
Pair("الاعلي مشاهدة", "movies_top_views"),
Pair("الافلام الاعلي تقييما IMDB", "movies_top_imdb"),
Pair("مسلسلات الأنمي", "anime"),
Pair("جميع المسلسلات", "series"),
Pair("الاعلي مشاهدة", "series_top_views"),
Pair("الاعلي تقييما IMDB", "series_top_imdb"),
Pair("المسلسلات القصيرة", "short_series"),
Pair("المسلسلات الاسيوية", "asian-series"),
Pair("المسلسلات الاسيوية الاعلي مشاهدة", "asian_top_views"),
Pair("الانمي الاعلي مشاهدة", "anime_top_views"),
private class SectionFilter : PairFilter(
"اقسام الموقع",
arrayOf(
Pair("اختر", "none"),
Pair("جميع الافلام", "all-movies"),
Pair("افلام اجنبي", "movies"),
Pair("افلام مدبلجة", "dubbed-movies"),
Pair("افلام هندي", "hindi"),
Pair("افلام اسيوي", "asian-movies"),
Pair("افلام انمي", "anime-movies"),
Pair("الافلام الاعلي تصويتا", "movies_top_votes"),
Pair("الافلام الاعلي مشاهدة", "movies_top_views"),
Pair("الافلام الاعلي تقييما IMDB", "movies_top_imdb"),
Pair("جميع المسلسلات", "series"),
Pair("مسلسلات الأنمي", "anime"),
Pair("المسلسلات الاعلي تقييما IMDB", "series_top_imdb"),
Pair("المسلسلات القصيرة", "short_series"),
Pair("المسلسلات الاسيوية", "asian-series"),
Pair("المسلسلات الاعلي مشاهدة", "series_top_views"),
Pair("المسلسلات الاسيوية الاعلي مشاهدة", "asian_top_views"),
Pair("الانمي الاعلي مشاهدة", "anime_top_views"),
Pair("البرامج التليفزيونية", "tvshows"),
Pair("البرامج التليفزيونية الاعلي مشاهدة", "tvshows_top_views"),
),
)
private class CategoryFilter : PairFilter(
"النوع",
arrayOf(
Pair("اختر", "none"),
Pair("افلام", "movies-cats"),
Pair("مسلسلات", "series_genres"),
Pair("انمى", "anime-cats"),
),
)
private class GenreFilter : SingleFilter(
"التصنيف",
arrayOf(
"Action", "Adventure", "Animation", "Western", "Sport", "Short", "Documentary", "Fantasy", "Sci-fi", "Romance", "Comedy", "Family", "Drama", "Thriller", "Crime", "Horror", "Biography",
).sortedArray(),
)

open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
open class SingleFilter(displayName: String, private val vals: Array<String>) :
AnimeFilter.Select<String>(displayName, vals) {
fun toUriPart() = vals[state]
}
open class PairFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
Expand All @@ -261,8 +271,8 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "240p")
entryValues = arrayOf("1080", "720", "480", "360", "240")
entries = arrayOf("1080p", "720p", "480p", "360p")
entryValues = arrayOf("1080", "720", "480", "360")
setDefaultValue("1080")
summary = "%s"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,53 +6,23 @@ import android.os.Handler
import android.os.Looper
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers.Companion.toHeaders
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.IOException
import okhttp3.Headers
import uy.kohesive.injekt.injectLazy
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

class GetSourcesInterceptor(private val getSources: String, private val client: OkHttpClient) : Interceptor {
private val context = Injekt.get<Application>()
class WebViewResolver() {
private val context: Application by injectLazy()
private val handler by lazy { Handler(Looper.getMainLooper()) }

private val initWebView by lazy {
WebSettings.getDefaultUserAgent(context)
}

@Synchronized
override fun intercept(chain: Interceptor.Chain): Response {
initWebView

val request = chain.request()

try {
val newRequest = resolveWithWebView(request)

return chain.proceed(newRequest ?: request)
} catch (e: Exception) {
throw IOException(e)
}
}

@SuppressLint("SetJavaScriptEnabled")
private fun resolveWithWebView(request: Request): Request? {
fun getUrl(origRequestUrl: String, origRequestheader: Headers): String {
val latch = CountDownLatch(1)

var webView: WebView? = null

val origRequestUrl = request.url.toString()
val headers = request.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap()
var newRequest: Request? = null
var resultUrl = ""
val headers = origRequestheader.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap()

handler.post {
val webview = WebView(context)
Expand All @@ -63,17 +33,16 @@ class GetSourcesInterceptor(private val getSources: String, private val client:
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0"
userAgentString = origRequestheader["User-Agent"]
}
webview.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest,
): WebResourceResponse? {
val url = request.url.toString()
if (url.contains(getSources)) {
val newHeaders = request.requestHeaders.toHeaders()
newRequest = GET(url, newHeaders)
if (VIDEO_REGEX.containsMatchIn(url)) {
resultUrl = url
latch.countDown()
}
return super.shouldInterceptRequest(view, request)
Expand All @@ -90,10 +59,11 @@ class GetSourcesInterceptor(private val getSources: String, private val client:
webView?.destroy()
webView = null
}
return newRequest
return resultUrl
}

companion object {
const val TIMEOUT_SEC: Long = 20
private val VIDEO_REGEX by lazy { Regex("\\.(mp4|m3u8)") }
}
}

0 comments on commit a56d5f4

Please sign in to comment.