merge faselHD extensions (#960)

* merge faselHD extensions

* merge faselHD extensions
This commit is contained in:
adly98
2022-10-21 13:31:55 +02:00
committed by GitHub
parent d587144598
commit cad0e59451
12 changed files with 35 additions and 355 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = 'فاصل اعلاني'
pkgNameSuffix = 'ar.faselhd'
extClass = '.FASELHD'
extVersionCode = 10
extVersionCode = 11
libVersion = '13'
}

View File

@ -42,7 +42,7 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun headersBuilder(): Headers.Builder {
return super.headersBuilder()
.add("Referer", "https://www.faselhd.club/")
.add("Referer", baseUrl)
}
// Popular Anime
@ -63,18 +63,36 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun popularAnimeNextPageSelector(): String = "ul.pagination li a.page-link:contains()"
// 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)"
override fun episodeListParse(response: Response): List<SEpisode> {
val episodes = mutableListOf<SEpisode>()
var seasonNumber = 1
fun episodeExtract(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.select("span#liskSh").text())
episode.name = "مشاهدة"
return episode
}
fun addEpisodes(document: Document) {
if (document.select(episodeListSelector()).isNullOrEmpty())
document.select("div.shortLink").map { episodes.add(episodeExtract(it)) }
else {
document.select(episodeListSelector()).map { episodes.add(episodeFromElement(it)) }
document.select(seasonsNextPageSelector(seasonNumber)).firstOrNull()?.let {
seasonNumber++
addEpisodes(client.newCall(GET("https://www.faselhd.club/?p=" + it.select("div.seasonDiv").attr("data-href"), headers)).execute().asJsoup())
addEpisodes(
client.newCall(
GET(
"$baseUrl/?p=" + it.select("div.seasonDiv")
.attr("data-href"),
headers
)
).execute().asJsoup()
)
}
}
}
@ -82,8 +100,6 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return episodes.reversed()
}
override fun episodeListSelector() = "div.epAll a"
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.attr("abs:href"))
@ -155,8 +171,8 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val url = "$baseUrl/".toHttpUrlOrNull()!!.newBuilder()
filters.forEach { filter ->
when (filter) {
is GenreFilter -> url.addPathSegment(filter.toUriPart())
else -> {}
}
}
url.addPathSegment("page")
@ -219,6 +235,15 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
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"),

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.animeextension" />

View File

@ -1,12 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'فاصل اعلاني افلام بس'
pkgNameSuffix = 'ar.faselhdmovies'
extClass = '.FASELHDMOVIES'
extVersionCode = 7
libVersion = '13'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,232 +0,0 @@
package eu.kanade.tachiyomi.animeextension.ar.faselhdmovies
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.lang.Exception
class FASELHDMOVIES : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "فاصل اعلاني افلام بس"
override val baseUrl = "https://www.faselhd.club"
override val lang = "ar"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun headersBuilder(): Headers.Builder {
return super.headersBuilder()
.add("Referer", "https://www.faselhd.club/")
}
// Popular Anime
override fun popularAnimeSelector(): String = "div#postList div.col-xl-2 a"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/anime-movies/page/$page")
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")
return anime
}
override fun popularAnimeNextPageSelector(): String = "ul.pagination li a.page-link:contains()"
// Episodes
override fun episodeListSelector() = "div.shortLink"
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.select("span#liskSh").text())
episode.name = "movie"
return episode
}
// Video urls
override fun videoListSelector() = throw UnsupportedOperationException("Not used.")
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
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", null)
if (quality != null) {
val newList = mutableListOf<Video>()
var preferred = 0
for (video in this) {
if (video.quality.contains(quality)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
return this
}
override fun videoFromElement(element: Element) = throw Exception("not used")
override fun videoUrlParse(document: Document) = throw Exception("not used")
// search
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
anime.title = element.select("div.imgdiv-class img, img").attr("alt")
anime.thumbnail_url = element.select("div.imgdiv-class img, img").attr("data-src")
return anime
}
override fun searchAnimeNextPageSelector(): String = "ul.pagination li a.page-link:contains()"
override fun searchAnimeSelector(): String = "div#postList div.col-xl-2 a"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
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())
}
}
url.addPathSegment("page")
url.addPathSegment("$page")
GET(url.toString(), headers)
}
}
// Details
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.title = document.select("div.title.h1").text()
anime.genre = document.select("span:contains(تصنيف) > a, span:contains(مستوى) > a").joinToString(", ") { it.text() }
anime.thumbnail_url = document.select("div.posterImg img.poster").attr("src")
anime.description = document.select("div.singleDesc p").text()
anime.status = parseStatus(document.select("span:contains(حالة)").text().replace("حالة ", "").replace("المسلسل : ", ""))
return anime
}
private fun parseStatus(statusString: String): Int {
return when (statusString) {
"مستمر" -> SAnime.ONGOING
else -> SAnime.COMPLETED
}
}
// Latest
override fun latestUpdatesNextPageSelector(): String = "ul.pagination li a.page-link:contains()"
override fun latestUpdatesFromElement(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")
return anime
}
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/most_recent/page/$page")
override fun latestUpdatesSelector(): String = "div#postList div.col-xl-2 a"
// Filters
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("NOTE: Ignored if using text search!"),
AnimeFilter.Separator(),
GenreFilter(getGenreList())
)
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"),
)
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
// preferred quality settings
override fun setupPreferenceScreen(screen: PreferenceScreen) {
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")
setDefaultValue("1080")
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
screen.addPreference(videoQualityPref)
}
}

View File

@ -1,99 +0,0 @@
package eu.kanade.tachiyomi.animeextension.ar.faselhdmovies
import android.annotation.SuppressLint
import android.app.Application
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 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>()
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? {
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
handler.post {
val webview = WebView(context)
webView = webview
with(webview.settings) {
javaScriptEnabled = true
domStorageEnabled = true
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0"
}
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)
latch.countDown()
}
return super.shouldInterceptRequest(view, request)
}
}
webView?.loadUrl(origRequestUrl, headers)
}
latch.await(TIMEOUT_SEC, TimeUnit.SECONDS)
handler.post {
webView?.stopLoading()
webView?.destroy()
webView = null
}
return newRequest
}
companion object {
const val TIMEOUT_SEC: Long = 20
}
}