Initial commit

This commit is contained in:
almightyhak 2024-06-20 11:54:12 +07:00
commit 98ed7e8839
2263 changed files with 108711 additions and 0 deletions

View file

@ -0,0 +1,7 @@
ext {
extName = 'Akwam'
extClass = '.Akwam'
extVersionCode = 9
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -0,0 +1,355 @@
package eu.kanade.tachiyomi.animeextension.ar.akwam
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.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
class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "أكوام"
override val baseUrl = "https://akw-cdn1.link"
override val lang = "ar"
override val supportsLatest = false
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// Popular
override fun popularAnimeSelector(): String = "div.entry-box-1 div.entry-image a.box"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/movies?page=$page")
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = element.select("picture img").attr("data-src")
anime.setUrlWithoutDomain(element.attr("href"))
anime.title = element.select("picture img").attr("alt")
return anime
}
override fun popularAnimeNextPageSelector(): String = "ul.pagination li.page-item a[rel=next]"
// episodes
override fun episodeListSelector() = "div.bg-primary2 h2 a"
override fun episodeListParse(response: Response): List<SEpisode> {
val episodes = mutableListOf<SEpisode>()
fun addEpisodes(document: Document) {
if (document.select(episodeListSelector()).isNullOrEmpty()) {
// add movie
document.select("input#reportInputUrl").map { episodes.add(episodeFromElement(it)) }
} else {
document.select(episodeListSelector()).map { episodes.add(episodesFromElement(it)) }
}
}
addEpisodes(response.asJsoup())
return episodes
}
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.attr("value"))
episode.name = "مشاهدة"
return episode
}
private fun episodesFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
val epNum = getNumberFromEpsString(element.text())
episode.setUrlWithoutDomain(element.attr("href"))
episode.name = element.text()
episode.episode_number = when {
epNum.isNotEmpty() -> epNum.toFloatOrNull() ?: 1F
else -> 1F
}
return episode
}
private fun getNumberFromEpsString(epsStr: String): String {
return epsStr.filter { it.isDigit() }
}
// Video links
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val iframe = "https://akw-cdn1.link/watch" + document.select("a.link-show").attr("href").substringAfter("watch") + "/" + document.ownerDocument()!!.select("input#page_id").attr("value")
val referer = response.request.url.toString()
val refererHeaders = Headers.headersOf("referer", referer)
val iframeResponse = client.newCall(GET(iframe, refererHeaders))
.execute().asJsoup()
return iframeResponse.select(videoListSelector()).map { videoFromElement(it) }
}
override fun videoListSelector() = "source"
override fun videoFromElement(element: Element): Video {
return Video(element.attr("src").replace("https", "http"), element.attr("size") + "p", element.attr("src").replace("https", "http"))
}
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 videoUrlParse(document: Document) = throw UnsupportedOperationException()
// Search
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = element.select("picture img").attr("data-src")
anime.setUrlWithoutDomain(element.attr("href"))
anime.title = element.select("picture img").attr("alt")
return anime
}
override fun searchAnimeNextPageSelector(): String = "ul.pagination li.page-item a[rel=next]"
override fun searchAnimeSelector(): String = "div.widget div.widget-body div.col-lg-auto div.entry-box div.entry-image a.box"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val url = if (query.isNotBlank()) {
val url = "$baseUrl/search?q=$query&page=$page".toHttpUrlOrNull()!!.newBuilder()
filters.forEach { filter ->
when (filter) {
is SectionFilter -> url.addQueryParameter("section", filter.toUriPart())
is RatingFilter -> url.addQueryParameter("rating", filter.toUriPart())
is FormatFilter -> url.addQueryParameter("formats", filter.toUriPart())
is QualityFilter -> url.addQueryParameter("quality", filter.toUriPart())
else -> {}
}
}
url.toString()
} else {
val url = "$baseUrl/search?page=$page".toHttpUrlOrNull()!!.newBuilder()
var type = "movies"
filters.forEach { filter ->
when (filter) {
is TypeFilter -> type = filter.toUriPart().toString()
is SectionSFilter -> url.addQueryParameter("section", filter.toUriPart())
is CategorySFilter -> url.addQueryParameter("category", filter.toUriPart())
is RatingSFilter -> url.addQueryParameter("rating", filter.toUriPart())
// is LanguageSFilter -> url.addQueryParameter("quality", filter.toUriPart())
else -> {}
}
}
url.toString().replace("search", type)
}
return GET(url, headers)
}
// Anime Details
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
// anime.thumbnail_url = document.select("div.container div div a picture > img.img-fluid").attr("data-src")
anime.title = document.select("picture > img.img-fluid").attr("alt")
anime.genre = document.select("div.font-size-16.d-flex.align-items-center.mt-3 a.badge, span.badge-info, span:contains(جودة الفيلم), span:contains(انتاج)").joinToString(", ") { it.text().replace("جودة الفيلم : ", "") }
anime.author = document.select("span:contains(انتاج)").text().replace("انتاج : ", "")
anime.description = document.select("div.widget:contains(قصة )").text()
anime.status = SAnime.COMPLETED
return anime
}
// Latest
override fun latestUpdatesNextPageSelector(): String = throw UnsupportedOperationException()
override fun latestUpdatesFromElement(element: Element): SAnime = throw UnsupportedOperationException()
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
override fun latestUpdatesSelector(): String = throw UnsupportedOperationException()
// Filters
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("فلترات البحث"),
AnimeFilter.Separator(),
SectionFilter(getSectionFilter()),
RatingFilter(getRatingFilter()),
FormatFilter(getFormatFilter()),
QualityFilter(getQualityFilter()),
AnimeFilter.Header("تصفح الموقع (تعمل فقط لو كان البحث فارغ)"),
AnimeFilter.Separator(),
TypeFilter(getTypeFilter()),
SectionSFilter(getSectionSFilter()),
CategorySFilter(getCategorySFilter()),
RatingSFilter(getRatingSFilter()),
)
private class SectionFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("الأقسام", vals)
private class RatingFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("التقيم", vals)
private class FormatFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("الجودة", vals)
private class QualityFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("الدقة", vals)
private class TypeFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("النوع", vals)
private class SectionSFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("القسم", vals)
private class CategorySFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("التصنيف", vals)
private class RatingSFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("التقييم", vals)
private fun getTypeFilter(): Array<Pair<String?, String>> = arrayOf(
Pair("movies", "افلام"),
Pair("series", "مسلسلات"),
)
private fun getSectionFilter(): Array<Pair<String?, String>> = arrayOf(
Pair("0", "الكل"),
Pair("movie", "افلام"),
Pair("series", "مسلسلات"),
Pair("show", "تلفزيون"),
)
private fun getRatingFilter(): Array<Pair<String?, String>> = arrayOf(
Pair("0", "التقييم"),
Pair("1", "1+"),
Pair("2", "2+"),
Pair("3", "3+"),
Pair("4", "4+"),
Pair("5", "5+"),
Pair("6", "6+"),
Pair("7", "7+"),
Pair("8", "8+"),
Pair("9", "9+"),
)
private fun getFormatFilter(): Array<Pair<String?, String>> = arrayOf(
Pair("0", "الكل"),
Pair("BluRay", "BluRay"),
Pair("WebRip", "WebRip"),
Pair("BRRIP", "BRRIP"),
Pair("DVDrip", "DVDrip"),
Pair("DVDSCR", "DVDSCR"),
Pair("HD", "HD"),
Pair("HDTS", "HDTS"),
Pair("HDTV", "HDTV"),
Pair("CAM", "CAM"),
Pair("WEB-DL", "WEB-DL"),
Pair("HDTC", "HDTC"),
Pair("BDRIP", "BDRIP"),
Pair("HDRIP", "HDRIP"),
Pair("HC+HDRIP", "HC HDRIP"),
)
private fun getQualityFilter(): Array<Pair<String?, String>> = arrayOf(
Pair("0", "الدقة"),
Pair("240p", "240p"),
Pair("360p", "360p"),
Pair("480p", "480p"),
Pair("720p", "720p"),
Pair("1080p", "1080p"),
Pair("3D", "3D"),
Pair("4K", "4K"),
)
private fun getSectionSFilter(): Array<Pair<String?, String>> = arrayOf(
Pair("0", "القسم"),
Pair("29", "عربي"),
Pair("30", "اجنبي"),
Pair("31", "هندي"),
Pair("32", "تركي"),
Pair("33", "اسيوي"),
)
private fun getCategorySFilter(): Array<Pair<String?, String>> = arrayOf(
Pair("0", "التصنيف"),
Pair("87", "رمضان"),
Pair("30", "انمي"),
Pair("18", "اكشن"),
Pair("71", "مدبلج"),
Pair("72", "NETFLIX"),
Pair("20", "كوميدي"),
Pair("35", "اثارة"),
Pair("34", "غموض"),
Pair("33", "عائلي"),
Pair("88", "اطفال"),
Pair("25", "حربي"),
Pair("32", "رياضي"),
Pair("89", "قصير"),
Pair("43", "فانتازيا"),
Pair("24", "خيال علمي"),
Pair("31", "موسيقى"),
Pair("29", "سيرة ذاتية"),
Pair("28", "وثائقي"),
Pair("27", "رومانسي"),
Pair("26", "تاريخي"),
Pair("23", "دراما"),
Pair("22", "رعب"),
Pair("21", "جريمة"),
Pair("19", "مغامرة"),
Pair("91", "غربي"),
)
private fun getRatingSFilter(): Array<Pair<String?, String>> = arrayOf(
Pair("0", "التقييم"),
Pair("1", "1+"),
Pair("2", "2+"),
Pair("3", "3+"),
Pair("4", "4+"),
Pair("5", "5+"),
Pair("6", "6+"),
Pair("7", "7+"),
Pair("8", "8+"),
Pair("9", "9+"),
)
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String?, String>>) :
AnimeFilter.Select<String>(displayName, vals.map { it.second }.toTypedArray()) {
fun toUriPart() = vals[state].first
}
// 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

@ -0,0 +1,19 @@
ext {
extName = 'Anime4up'
extClass = '.Anime4Up'
extVersionCode = 55
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:dood-extractor'))
implementation(project(':lib:gdriveplayer-extractor'))
implementation(project(':lib:mp4upload-extractor'))
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:uqload-extractor'))
implementation(project(':lib:vidbom-extractor'))
implementation(project(':lib:voe-extractor'))
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View file

@ -0,0 +1,233 @@
package eu.kanade.tachiyomi.animeextension.ar.anime4up
import android.app.Application
import android.util.Base64
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.ar.anime4up.extractors.SharedExtractor
import eu.kanade.tachiyomi.animeextension.ar.anime4up.extractors.VidYardExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
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.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.gdriveplayerextractor.GdrivePlayerExtractor
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
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 uy.kohesive.injekt.injectLazy
import java.lang.Exception
class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Anime4Up"
override val baseUrl = "https://anime4up.cam"
override val lang = "ar"
override val supportsLatest = false
private val json: Json by injectLazy()
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun headersBuilder() = super.headersBuilder().add("Referer", "$baseUrl/")
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/anime-list-3/page/$page/")
override fun popularAnimeSelector() = "div.anime-list-content div.anime-card-poster > div.hover"
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
element.selectFirst("img")!!.run {
thumbnail_url = absUrl("src")
title = attr("alt")
}
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
}
override fun popularAnimeNextPageSelector() = "ul.pagination > li > a.next"
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
override fun latestUpdatesSelector() = throw UnsupportedOperationException()
override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException()
override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException()
// =============================== Search ===============================
override fun getFilterList() = Anime4UpFilters.FILTER_LIST
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
if (query.isNotBlank()) {
return GET("$baseUrl/?search_param=animes&s=$query", headers)
}
return with(Anime4UpFilters.getSearchParameters(filters)) {
val url = when {
genre.isNotBlank() -> "$baseUrl/anime-genre/$genre"
type.isNotBlank() -> "$baseUrl/anime-type/$type"
status.isNotBlank() -> "$baseUrl/anime-status/$status"
else -> throw Exception("اختر فلتر")
}
GET(url, headers)
}
}
override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector()
override fun searchAnimeSelector() = popularAnimeSelector()
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document) = SAnime.create().apply {
val doc = document // Shortcut
thumbnail_url = doc.selectFirst("img.thumbnail")!!.attr("src")
title = doc.selectFirst("h1.anime-details-title")!!.text()
// Genres + useful info
genre = doc.select("ul.anime-genres > li > a, div.anime-info > a").eachText().joinToString()
description = buildString {
// Additional info
doc.select("div.anime-info").eachText().forEach {
append("$it\n")
}
// Description
doc.selectFirst("p.anime-story")?.text()?.also {
append("\n$it")
}
}
doc.selectFirst("div.anime-info:contains(حالة الأنمي)")?.text()?.also {
status = when {
it.contains("يعرض الان", true) -> SAnime.ONGOING
it.contains("مكتمل", true) -> SAnime.COMPLETED
else -> SAnime.UNKNOWN
}
}
}
// ============================== Episodes ==============================
override fun episodeListParse(response: Response) = super.episodeListParse(response).reversed()
override fun episodeListSelector() = "div.ehover6 > div.episodes-card-title > h3 > a, ul.all-episodes-list li > a"
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
setUrlWithoutDomain(element.attr("href"))
name = element.text()
episode_number = name.substringAfterLast(" ").toFloatOrNull() ?: 0F
}
// ============================ Video Links =============================
@Serializable
data class Qualities(
val fhd: Map<String, String> = emptyMap(),
val hd: Map<String, String> = emptyMap(),
val sd: Map<String, String> = emptyMap(),
)
override fun videoListParse(response: Response): List<Video> {
val base64 = response.asJsoup().selectFirst("input[name=wl]")
?.attr("value")
?.let { String(Base64.decode(it, Base64.DEFAULT)) }
?: return emptyList()
val parsedData = json.decodeFromString<Qualities>(base64)
val streamLinks = with(parsedData) { fhd + hd + sd }
return streamLinks.values.distinct().parallelCatchingFlatMapBlocking(::extractVideos)
}
private val uqloadExtractor by lazy { UqloadExtractor(client) }
private val doodExtractor by lazy { DoodExtractor(client) }
private val gdriveplayerExtractor by lazy { GdrivePlayerExtractor(client) }
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val sharedExtractor by lazy { SharedExtractor(client) }
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
private val vidbomExtractor by lazy { VidBomExtractor(client) }
private val vidyardExtractor by lazy { VidYardExtractor(client, headers) }
private val voeExtractor by lazy { VoeExtractor(client) }
private fun extractVideos(url: String): List<Video> {
return when {
url.contains("drive.google") -> {
val embedUrlG = "https://gdriveplayer.to/embed2.php?link=$url"
gdriveplayerExtractor.videosFromUrl(embedUrlG, "GdrivePlayer", headers)
}
url.contains("vidyard") -> vidyardExtractor.videosFromUrl(url)
url.contains("ok.ru") -> okruExtractor.videosFromUrl(url)
url.contains("mp4upload") -> mp4uploadExtractor.videosFromUrl(url, headers)
url.contains("uqload") -> uqloadExtractor.videosFromUrl(url)
url.contains("voe") -> voeExtractor.videosFromUrl(url)
url.contains("shared") -> sharedExtractor.videosFromUrl(url)?.let(::listOf)
DOOD_REGEX.containsMatchIn(url) -> doodExtractor.videosFromUrl(url, "Dood mirror")
VIDBOM_REGEX.containsMatchIn(url) -> vidbomExtractor.videosFromUrl(url)
STREAMWISH_REGEX.containsMatchIn(url) -> streamwishExtractor.videosFromUrl(url) { "Mirror: $it" }
else -> null
} ?: emptyList()
}
override fun videoListSelector() = throw UnsupportedOperationException()
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
entryValues = PREF_QUALITY_VALUES
setDefaultValue(PREF_QUALITY_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)
}
// ============================= 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 val VIDBOM_REGEX = Regex("(?:v[aie]d[bp][aoe]?m|myvii?d|segavid|v[aei]{1,2}dshar[er]?)\\.(?:com|net|org|xyz)(?::\\d+)?/(?:embed[/-])?([A-Za-z0-9]+)")
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]+)")
private val STREAMWISH_REGEX = Regex("((?:streamwish|anime7u|animezd|ajmidyad|khadhnayad|yadmalik|hayaatieadhab)\\.(?:com|to|sbs))/(?:e/|v/|f/)?([0-9a-zA-Z]+)")
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Preferred quality"
private const val PREF_QUALITY_DEFAULT = "1080p"
private val PREF_QUALITY_ENTRIES = arrayOf("1080p", "720p", "480p", "360p", "dood")
private val PREF_QUALITY_VALUES = PREF_QUALITY_ENTRIES
}
}

View file

@ -0,0 +1,110 @@
package eu.kanade.tachiyomi.animeextension.ar.anime4up
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
object Anime4UpFilters {
open class QueryPartFilter(
displayName: String,
val vals: Array<Pair<String, String>>,
) : AnimeFilter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray(),
) {
fun toQueryPart() = vals[state].second
}
private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
return (first { it is R } as QueryPartFilter).toQueryPart()
}
internal class GenreFilter : QueryPartFilter("تصنيف الأنمي", Anime4UpFiltersData.GENRES)
internal class TypeFilter : QueryPartFilter("نوع الأنمي", Anime4UpFiltersData.TYPES)
internal class StatusFilter : QueryPartFilter("حالة الأنمي", Anime4UpFiltersData.STATUS)
val FILTER_LIST get() = AnimeFilterList(
AnimeFilter.Header("الفلترات مش هتشتغل لو بتبحث او وهي فاضيه"),
GenreFilter(),
TypeFilter(),
StatusFilter(),
)
data class FilterSearchParams(
val genre: String = "",
val type: String = "",
val status: String = "",
)
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
if (filters.isEmpty()) return FilterSearchParams()
return FilterSearchParams(
filters.asQueryPart<GenreFilter>(),
filters.asQueryPart<TypeFilter>(),
filters.asQueryPart<StatusFilter>(),
)
}
private object Anime4UpFiltersData {
private val ANY = Pair("أختر", "")
val GENRES = arrayOf(
ANY,
Pair("أطفال", "%d8%a3%d8%b7%d9%81%d8%a7%d9%84"),
Pair("أكشن", "%d8%a3%d9%83%d8%b4%d9%86/"),
Pair("إيتشي", "%d8%a5%d9%8a%d8%aa%d8%b4%d9%8a/"),
Pair("اثارة", "%d8%a7%d8%ab%d8%a7%d8%b1%d8%a9/"),
Pair("العاب", "%d8%a7%d9%84%d8%b9%d8%a7%d8%a8/"),
Pair("بوليسي", "%d8%a8%d9%88%d9%84%d9%8a%d8%b3%d9%8a/"),
Pair("تاريخي", "%d8%aa%d8%a7%d8%b1%d9%8a%d8%ae%d9%8a/"),
Pair("جنون", "%d8%ac%d9%86%d9%88%d9%86/"),
Pair("جوسي", "%d8%ac%d9%88%d8%b3%d9%8a/"),
Pair("حربي", "%d8%ad%d8%b1%d8%a8%d9%8a/"),
Pair("حريم", "%d8%ad%d8%b1%d9%8a%d9%85/"),
Pair("خارق للعادة", "%d8%ae%d8%a7%d8%b1%d9%82-%d9%84%d9%84%d8%b9%d8%a7%d8%af%d8%a9/"),
Pair("خيال علمي", "%d8%ae%d9%8a%d8%a7%d9%84-%d8%b9%d9%84%d9%85%d9%8a/"),
Pair("دراما", "%d8%af%d8%b1%d8%a7%d9%85%d8%a7/"),
Pair("رعب", "%d8%b1%d8%b9%d8%a8/"),
Pair("رومانسي", "%d8%b1%d9%88%d9%85%d8%a7%d9%86%d8%b3%d9%8a/"),
Pair("رياضي", "%d8%b1%d9%8a%d8%a7%d8%b6%d9%8a/"),
Pair("ساموراي", "%d8%b3%d8%a7%d9%85%d9%88%d8%b1%d8%a7%d9%8a/"),
Pair("سحر", "%d8%b3%d8%ad%d8%b1/"),
Pair("سينين", "%d8%b3%d9%8a%d9%86%d9%8a%d9%86/"),
Pair("شريحة من الحياة", "%d8%b4%d8%b1%d9%8a%d8%ad%d8%a9-%d9%85%d9%86-%d8%a7%d9%84%d8%ad%d9%8a%d8%a7%d8%a9/"),
Pair("شوجو", "%d8%b4%d9%88%d8%ac%d9%88/"),
Pair("شوجو اَي", "%d8%b4%d9%88%d8%ac%d9%88-%d8%a7%d9%8e%d9%8a/"),
Pair("شونين", "%d8%b4%d9%88%d9%86%d9%8a%d9%86/"),
Pair("شونين اي", "%d8%b4%d9%88%d9%86%d9%8a%d9%86-%d8%a7%d9%8a/"),
Pair("شياطين", "%d8%b4%d9%8a%d8%a7%d8%b7%d9%8a%d9%86/"),
Pair("غموض", "%d8%ba%d9%85%d9%88%d8%b6/"),
Pair("فضائي", "%d9%81%d8%b6%d8%a7%d8%a6%d9%8a/"),
Pair("فنتازيا", "%d9%81%d9%86%d8%aa%d8%a7%d8%b2%d9%8a%d8%a7/"),
Pair("فنون قتالية", "%d9%81%d9%86%d9%88%d9%86-%d9%82%d8%aa%d8%a7%d9%84%d9%8a%d8%a9/"),
Pair("قوى خارقة", "%d9%82%d9%88%d9%89-%d8%ae%d8%a7%d8%b1%d9%82%d8%a9/"),
Pair("كوميدي", "%d9%83%d9%88%d9%85%d9%8a%d8%af%d9%8a/"),
Pair("محاكاة ساخرة", "%d9%85%d8%ad%d8%a7%d9%83%d8%a7%d8%a9-%d8%b3%d8%a7%d8%ae%d8%b1%d8%a9/"),
Pair("مدرسي", "%d9%85%d8%af%d8%b1%d8%b3%d9%8a/"),
Pair("مصاصي دماء", "%d9%85%d8%b5%d8%a7%d8%b5%d9%8a-%d8%af%d9%85%d8%a7%d8%a1/"),
Pair("مغامرات", "%d9%85%d8%ba%d8%a7%d9%85%d8%b1%d8%a7%d8%aa/"),
Pair("موسيقي", "%d9%85%d9%88%d8%b3%d9%8a%d9%82%d9%8a/"),
Pair("ميكا", "%d9%85%d9%8a%d9%83%d8%a7/"),
Pair("نفسي", "%d9%86%d9%81%d8%b3%d9%8a/"),
)
val TYPES = arrayOf(
ANY,
Pair("Movie", "movie-3"),
Pair("ONA", "ona1"),
Pair("OVA", "ova1"),
Pair("Special", "special1"),
Pair("TV", "tv2"),
)
val STATUS = arrayOf(
ANY,
Pair("لم يعرض بعد", "%d9%84%d9%85-%d9%8a%d8%b9%d8%b1%d8%b6-%d8%a8%d8%b9%d8%af"),
Pair("مكتمل", "complete"),
Pair("يعرض الان", "%d9%8a%d8%b9%d8%b1%d8%b6-%d8%a7%d9%84%d8%a7%d9%86-1"),
)
}
}

View file

@ -0,0 +1,15 @@
package eu.kanade.tachiyomi.animeextension.ar.anime4up.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
class SharedExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String, quality: String = "mirror"): Video? {
val document = client.newCall(GET(url)).execute().asJsoup()
return document.selectFirst("source")?.let {
Video(it.attr("src"), "4Shared: $quality", it.attr("src"))
}
}
}

View file

@ -0,0 +1,30 @@
package eu.kanade.tachiyomi.animeextension.ar.anime4up.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers
import okhttp3.OkHttpClient
class VidYardExtractor(private val client: OkHttpClient, private val headers: Headers) {
private val newHeaders by lazy {
headers.newBuilder().set("Referer", VIDYARD_URL).build()
}
fun videosFromUrl(url: String): List<Video> {
val id = url.substringAfter("com/").substringBefore("?")
val playerUrl = "$VIDYARD_URL/player/$id.json"
val callPlayer = client.newCall(GET(playerUrl, newHeaders)).execute()
.body.string()
val data = callPlayer.substringAfter("hls\":[").substringBefore("]")
val sources = data.split("profile\":\"").drop(1)
return sources.map { source ->
val src = source.substringAfter("url\":\"").substringBefore("\"")
val quality = source.substringBefore("\"")
Video(src, quality, src, headers = newHeaders)
}
}
}
private const val VIDYARD_URL = "https://play.vidyard.com"

View file

@ -0,0 +1,12 @@
ext {
extName = 'Anime Blkom'
extClass = '.AnimeBlkom'
extVersionCode = 17
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:mp4upload-extractor'))
implementation(project(":lib:okru-extractor"))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View file

@ -0,0 +1,252 @@
package eu.kanade.tachiyomi.animeextension.ar.animeblkom
import android.app.Application
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.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
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 AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "أنمي بالكوم"
override val baseUrl by lazy { preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)!! }
override val lang = "ar"
override val supportsLatest = false
override fun headersBuilder() = super.headersBuilder()
.add("referer", baseUrl)
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/animes-list/?sort_by=rate&page=$page", headers)
override fun popularAnimeSelector() = "div.contents div.poster > a"
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
val img = element.selectFirst("img")!!
thumbnail_url = img.attr("abs:data-original")
title = img.attr("alt").removeSuffix(" poster")
setUrlWithoutDomain(element.attr("href"))
}
override fun popularAnimeNextPageSelector() = "ul.pagination li.page-item a[rel=next]"
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
override fun latestUpdatesSelector(): String = throw UnsupportedOperationException()
override fun latestUpdatesFromElement(element: Element): SAnime = throw UnsupportedOperationException()
override fun latestUpdatesNextPageSelector(): String = throw UnsupportedOperationException()
// =============================== Search ===============================
override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector()
override fun searchAnimeSelector() = popularAnimeSelector()
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val url = if (query.isNotBlank()) {
"$baseUrl/search?query=$query&page=$page"
} else {
filters
.filterIsInstance<TypeList>()
.firstOrNull()
?.takeIf { it.state > 0 }
?.let { filter ->
val genreN = getTypeList()[filter.state].query
"$baseUrl/$genreN?page=$page"
}
?: throw Exception("اختر فلتر")
}
return GET(url, headers)
}
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document) = SAnime.create().apply {
thumbnail_url = document.selectFirst("div.poster img")!!.attr("abs:data-original")
title = document.selectFirst("div.name span h1")!!.text()
genre = document.select("p.genres a").joinToString { it.text() }
description = document.selectFirst("div.story p, div.story")?.text()
author = document.selectFirst("div:contains(الاستديو) span > a")?.text()
status = document.selectFirst("div.info-table div:contains(حالة الأنمي) span.info")?.text()?.let {
when {
it.contains("مستمر") -> SAnime.ONGOING
it.contains("مكتمل") -> SAnime.COMPLETED
else -> null
}
} ?: SAnime.UNKNOWN
artist = document.selectFirst("div:contains(المخرج) > span.info")?.text()
}
// ============================== Episodes ==============================
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
if (document.selectFirst(episodeListSelector()) == null) {
return oneEpisodeParse(document)
}
return document.select(episodeListSelector()).map(::episodeFromElement).reversed()
}
private fun oneEpisodeParse(document: Document): List<SEpisode> {
return SEpisode.create().apply {
setUrlWithoutDomain(document.location())
episode_number = 1F
name = document.selectFirst("div.name.col-xs-12 span h1")!!.text()
}.let(::listOf)
}
override fun episodeListSelector() = "ul.episodes-links li a"
override fun episodeFromElement(element: Element): SEpisode {
return SEpisode.create().apply {
setUrlWithoutDomain(element.attr("href"))
val eptitle = element.selectFirst("span:nth-child(3)")!!.text()
val epNum = eptitle.filter { it.isDigit() }
episode_number = when {
epNum.isNotEmpty() -> epNum.toFloatOrNull() ?: 1F
else -> 1F
}
name = eptitle + " :" + element.selectFirst("span:nth-child(1)")!!.text()
}
}
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
return document.select("span.server a").flatMap {
runCatching { extractVideos(it) }.getOrElse { emptyList() }
}
}
private val okruExtractor by lazy { OkruExtractor(client) }
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
private fun extractVideos(element: Element): List<Video> {
val url = element.attr("data-src").replace("http://", "https://")
return when {
".vid4up" in url || "Blkom" in element.text() -> {
val videoDoc = client.newCall(GET(url, headers)).execute()
.asJsoup()
videoDoc.select(videoListSelector()).map(::videoFromElement)
}
"ok.ru" in url -> okruExtractor.videosFromUrl(url)
"mp4upload" in url -> mp4uploadExtractor.videosFromUrl(url, headers)
else -> emptyList()
}
}
override fun videoListSelector() = "source"
override fun videoFromElement(element: Element): Video {
val videoUrl = element.attr("src")
return Video(videoUrl, "Blkom - " + element.attr("label"), videoUrl, headers)
}
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
// ============================== Filters ===============================
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("الفلترات مش هتشتغل لو بتبحث او وهي فاضيه"),
TypeList(typesName),
)
private class TypeList(types: Array<String>) : AnimeFilter.Select<String>("نوع الأنمي", types)
private data class Type(val name: String, val query: String)
private val typesName = getTypeList().map {
it.name
}.toTypedArray()
private fun getTypeList() = listOf(
Type("اختر", ""),
Type("قائمة الأنمي", "anime-list"),
Type(" قائمة المسلسلات ", "series-list"),
Type(" قائمة الأفلام ", "movie-list"),
Type(" قائمة الأوفا ", "ova-list"),
Type(" قائمة الأونا ", "ona-list"),
Type(" قائمة الحلقات خاصة ", "special-list"),
)
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
entryValues = PREF_QUALITY_ENTRIES
setDefaultValue(PREF_QUALITY_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)
ListPreference(screen.context).apply {
key = PREF_DOMAIN_KEY
title = PREF_DOMAIN_TITLE
entries = PREF_DOMAIN_ENTRIES
entryValues = PREF_DOMAIN_VALUES
setDefaultValue(PREF_DOMAIN_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)
}
// ============================= 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", "240p")
private const val PREF_DOMAIN_KEY = "pref_domain_key"
private const val PREF_DOMAIN_TITLE = "Preferred domain"
private const val PREF_DOMAIN_DEFAULT = "https://animeblkom.net"
private val PREF_DOMAIN_ENTRIES = arrayOf("animeblkom.net", "animeblkom.tv", "blkom.com")
private val PREF_DOMAIN_VALUES by lazy {
PREF_DOMAIN_ENTRIES.map { "https://$it" }.toTypedArray()
}
}
}

View file

@ -0,0 +1,7 @@
ext {
extName = 'Animeiat'
extClass = '.Animeiat'
extVersionCode = 1
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View file

@ -0,0 +1,232 @@
package eu.kanade.tachiyomi.animeextension.ar.animeiat
import android.app.Application
import android.content.SharedPreferences
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.ar.animeiat.dto.AnimeEpisodesList
import eu.kanade.tachiyomi.animeextension.ar.animeiat.dto.AnimePageResponse
import eu.kanade.tachiyomi.animeextension.ar.animeiat.dto.LatestAnimeResponse
import eu.kanade.tachiyomi.animeextension.ar.animeiat.dto.PopularAnimeResponse
import eu.kanade.tachiyomi.animeextension.ar.animeiat.dto.StreamLinks
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.AnimesPage
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.AnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import kotlinx.serialization.json.Json
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Base64
class Animeiat : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "Animeiat"
override val baseUrl = "https://api.animeiat.co/v1"
override val lang = "ar"
override val supportsLatest = true
private val json = Json {
ignoreUnknownKeys = true
}
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeParse(response: Response): AnimesPage {
val responseJson = json.decodeFromString<PopularAnimeResponse>(response.body.string())
val animeList = responseJson.data.map {
SAnime.create().apply {
url = "/anime/${it.slug}"
title = it.anime_name
thumbnail_url = "https://api.animeiat.co/storage/${it.poster_path}"
}
}
val hasNextPage = responseJson.meta.current_page < responseJson.meta.last_page
return AnimesPage(animeList, hasNextPage)
}
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/anime?page=$page")
// ============================== Episodes ==============================
override fun episodeListParse(response: Response): List<SEpisode> {
val episodeList = mutableListOf<SEpisode>()
val pagesUrl = response.request.url.toString() + "/episodes"
val firstPage = client.newCall(GET(pagesUrl)).execute()
fun addEpisodes(res: Response) {
val jr = json.decodeFromString<AnimeEpisodesList>(res.body.string())
episodeList.addAll(
jr.data.map {
SEpisode.create().apply {
name = it.title
episode_number = it.number
url = "episode/${it.slug}"
}
},
)
jr.links.next?.let {
addEpisodes(client.newCall(GET(it)).execute())
}
}
addEpisodes(firstPage)
return episodeList.reversed()
}
// ============================ Video Links =============================
override fun videoListRequest(episode: SEpisode): Request = GET("$baseUrl/${episode.url}")
@RequiresApi(Build.VERSION_CODES.O)
override fun videoListParse(response: Response): List<Video> {
val playerHash = response.body.string().substringAfter("\"hash\":\"").substringBefore("\"")
val playerID = String(Base64.getDecoder().decode(playerHash)).split("\"").reversed()[1]
val playerUrls = client.newCall(GET("$baseUrl/video/$playerID")).execute()
val jr = json.decodeFromString<StreamLinks>(playerUrls.body.string()).data
return jr.sources.map {
Video(
it.file,
"${it.label} ${it.quality}",
it.file,
)
}
}
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()
}
// =========================== Anime Details ============================
override fun animeDetailsParse(response: Response): SAnime {
val details = json.decodeFromString<AnimePageResponse>(response.body.string()).data
val anime = SAnime.create().apply {
url = "/anime/${details.slug}"
title = details.anime_name
status = when (details.status) {
"ongoing" -> SAnime.ONGOING
"completed" -> SAnime.COMPLETED
else -> SAnime.UNKNOWN
}
author = details.studios.joinToString { it.name }
genre = details.genres.joinToString { it.name }
description = details.story
thumbnail_url = "https://api.animeiat.co/storage/${details.poster_path}"
}
return anime
}
// =============================== Search ===============================
override fun searchAnimeParse(response: Response): AnimesPage = popularAnimeParse(response)
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
if (query.isNotEmpty()) {
return GET("$baseUrl/anime?q=$query&page=$page")
} else {
var type = ""
var status = ""
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
is TypeCategoryList -> {
type = getTypeFilterList()[filter.state].query
}
is StatCategoryList -> {
status = getStatFilterList()[filter.state].query
}
else -> {}
}
}
type = if (type.isEmpty()) "" else "&type=$type"
status = if (status.isEmpty()) "" else "&status=$status"
return GET("$baseUrl/anime?page=$page$type$status")
}
}
// =============================== Latest ===============================
override fun latestUpdatesParse(response: Response): AnimesPage {
val responseJson = json.decodeFromString<LatestAnimeResponse>(response.body.string())
val animeList = responseJson.data.map {
SAnime.create().apply {
url = "/anime/${it.slug.substringBefore("-episode-")}"
title = it.title
thumbnail_url = "https://api.animeiat.co/storage/${it.poster_path}"
}
}
val hasNextPage = responseJson.meta.current_page < responseJson.meta.last_page
return AnimesPage(animeList, hasNextPage)
}
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/home/sticky-episodes?page=$page")
// ============================== filters ==============================
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("فلترة الموقع"),
TypeCategoryList(typeFilterList),
StatCategoryList(statFilterList),
)
private class TypeCategoryList(categories: Array<String>) : AnimeFilter.Select<String>("النوع", categories)
private class StatCategoryList(categories: Array<String>) : AnimeFilter.Select<String>("الحالة", categories)
private data class CatUnit(val name: String, val query: String)
private val typeFilterList = getTypeFilterList().map { it.name }.toTypedArray()
private val statFilterList = getStatFilterList().map { it.name }.toTypedArray()
private fun getTypeFilterList() = listOf(
CatUnit("اختر", ""),
CatUnit("فيلم", "movie"),
CatUnit("اوفا", "ova"),
CatUnit("اونا", "ona"),
CatUnit("حلقة خاصة", "special"),
)
private fun getStatFilterList() = listOf(
CatUnit("اختر", ""),
CatUnit("جارى رفعة", "uploading"),
CatUnit("مكتمل", "completed"),
CatUnit("يعرض حاليا", "ongoing"),
CatUnit("قريبا", "upcoming"),
)
// =============================== Preferences ===============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
entryValues = PREF_QUALITY_VALUES
setDefaultValue(PREF_QUALITY_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(videoQualityPref)
}
companion object {
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Preferred quality"
private const val PREF_QUALITY_DEFAULT = "1080"
private val PREF_QUALITY_ENTRIES = arrayOf("1080p", "720p", "480p", "360p", "240p")
private val PREF_QUALITY_VALUES by lazy {
PREF_QUALITY_ENTRIES.map { it.substringBefore("p") }.toTypedArray()
}
}
}

View file

@ -0,0 +1,97 @@
package eu.kanade.tachiyomi.animeextension.ar.animeiat.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class PopularAnimeResponse(
@SerialName("data")
val `data`: List<PopularAnimeList>,
val links: Links,
val meta: Meta,
)
@Serializable
data class LatestAnimeResponse(
@SerialName("data")
val `data`: List<EpisodesList>,
val links: Links,
val meta: Meta,
)
@Serializable
data class PopularAnimeList(
val anime_name: String,
val poster_path: String,
val slug: String,
)
@Serializable
data class Links(
val first: String?,
val last: String?,
val next: String?,
val prev: String?,
)
@Serializable
data class Meta(
val current_page: Int,
val last_page: Int,
)
@Serializable
data class AnimePageResponse(
@SerialName("data")
val `data`: AnimeDetails,
)
@Serializable
data class AnimeDetails(
val anime_name: String,
val genres: List<Genre>,
val poster_path: String,
val slug: String,
val status: String,
val story: String,
val studios: List<Studio>,
)
@Serializable
data class Genre(val name: String)
@Serializable
data class Studio(val name: String)
@Serializable
data class AnimeEpisodesList(
val `data`: List<EpisodesList>,
val links: Links,
)
@Serializable
data class EpisodesList(
val number: Float,
val slug: String,
val title: String,
val poster_path: String,
)
@Serializable
data class StreamLinks(
val `data`: VideoInformation,
)
@Serializable
data class VideoInformation(
val sources: List<Sources>,
)
@Serializable
data class Sources(
val `file`: String,
val label: String,
val name: String,
val newfile: String,
val quality: String,
)

View file

@ -0,0 +1,14 @@
ext {
extName = 'AnimeLek'
extClass = '.AnimeLek'
extVersionCode = 29
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:dood-extractor'))
implementation(project(':lib:vidbom-extractor'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View file

@ -0,0 +1,197 @@
package eu.kanade.tachiyomi.animeextension.ar.animelek
import android.app.Application
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.animesource.ConfigurableAnimeSource
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.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
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.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
class AnimeLek : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "AnimeLek"
override val baseUrl = "https://animelek.me"
override val lang = "ar"
override val supportsLatest = true
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeSelector() = "div.slider-episode-container div.episodes-card-container"
override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
override fun popularAnimeFromElement(element: Element): SAnime {
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() = null
// ============================== Episodes ==============================
override fun episodeListSelector() = "div.ep-card-anime-title-detail h3 a"
override fun episodeFromElement(element: Element): SEpisode {
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
}
}
}
override fun episodeListParse(response: Response) = super.episodeListParse(response).reversed()
// ============================ Video Links =============================
override fun videoListParse(response: Response) = videosFromElement(response.asJsoup())
override fun videoListSelector() = "ul#episode-servers li.watch a"
private fun videosFromElement(document: Document): List<Video> {
val vidbomServers = listOf("vidbam", "vadbam", "vidbom", "vidbm")
return document.select(videoListSelector()).mapNotNull { element ->
val url = element.attr("data-ep-url")
val qualityy = element.text()
when {
vidbomServers.any(url::contains) -> {
VidBomExtractor(client).videosFromUrl(url)
}
url.contains("dood") -> {
DoodExtractor(client).videoFromUrl(url, qualityy)
?.let(::listOf)
}
url.contains("ok.ru") -> {
OkruExtractor(client).videosFromUrl(url)
}
url.contains("streamtape") -> {
StreamTapeExtractor(client).videoFromUrl(url)
?.let(::listOf)
}
url.contains("4shared") -> {
SharedExtractor(client).videoFromUrl(url, qualityy)
?.let(::listOf)
}
else -> null
}
}.flatten()
}
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
// =============================== 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() = "div.anime-list-content div.anime-card-container"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = GET("$baseUrl/search/?s=$query&page=$page")
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime {
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 {
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")
}
}
}
}
// =============================== Latest ===============================
override fun latestUpdatesNextPageSelector() = searchAnimeNextPageSelector()
override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/episode/?page=$page")
override fun latestUpdatesSelector() = "div.episodes-list-content div.episodes-card-container"
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
entryValues = PREF_QUALITY_ENTRIES
setDefaultValue(PREF_QUALITY_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(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

@ -0,0 +1,19 @@
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 SharedExtractor(private val client: OkHttpClient) {
fun videoFromUrl(url: String, quality: String): Video? {
val document = client.newCall(GET(url)).execute().asJsoup()
val check = document.select("div.error4shared").text()
val videoUrl = document.select("source").attr("src")
return if (check.contains("This file is not available any more")) {
Video(url, "no 1video", "https")
} else {
Video(url, quality, videoUrl)
}
}
}

View file

@ -0,0 +1,19 @@
ext {
extName = 'Animerco'
extClass = '.Animerco'
extVersionCode = 36
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:uqload-extractor'))
implementation(project(':lib:gdriveplayer-extractor'))
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:dood-extractor'))
implementation(project(':lib:vidbom-extractor'))
implementation(project(':lib:mp4upload-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:yourupload-extractor'))
implementation(project(':lib:okru-extractor'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -0,0 +1,259 @@
package eu.kanade.tachiyomi.animeextension.ar.animerco
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.ar.animerco.extractors.SharedExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
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.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.gdriveplayerextractor.GdrivePlayerExtractor
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import okhttp3.FormBody
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
class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Animerco"
override val baseUrl = "https://animerco.org"
override val lang = "ar"
override val supportsLatest = false
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/animes/page/$page/")
override fun popularAnimeSelector() = "div.media-block > div > a.image"
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
setUrlWithoutDomain(element.attr("href"))
thumbnail_url = element.attr("data-src")
title = element.attr("title")
}
override fun popularAnimeNextPageSelector() = "ul.pagination li a:has(i.fa-left-long)"
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
override fun latestUpdatesSelector(): String = throw UnsupportedOperationException()
override fun latestUpdatesFromElement(element: Element): SAnime = throw UnsupportedOperationException()
override fun latestUpdatesNextPageSelector(): String = throw UnsupportedOperationException()
// =============================== Search ===============================
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = GET("$baseUrl/page/$page/?s=$query")
override fun searchAnimeSelector() = popularAnimeSelector()
override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector()
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document) = SAnime.create().apply {
document.selectFirst("a.poster")?.run {
thumbnail_url = attr("data-src")
title = attr("title").ifEmpty {
document.selectFirst("div.media-title h1")!!.text()
}
}
val infosDiv = document.selectFirst("ul.media-info")!!
author = infosDiv.select("li:contains(الشبكات) a").eachText()
.joinToString()
.takeIf(String::isNotBlank)
artist = infosDiv.select("li:contains(الأستوديو) a").eachText()
.joinToString()
.takeIf(String::isNotBlank)
genre = document.select("nav.Nvgnrs a, ul.media-info li:contains(النوع) a")
.eachText()
.joinToString()
description = buildString {
document.selectFirst("div.media-story p")?.also {
append(it.text())
}
document.selectFirst("div.media-title > h3.alt-title")?.also {
append("\n\nAlternative title: " + it.text())
}
}
status = document.select("ul.chapters-list a.se-title > span.badge")
.eachText()
.let { items ->
when {
items.all { it.contains("مكتمل") } -> SAnime.COMPLETED
items.any { it.contains("يعرض الأن") } -> SAnime.ONGOING
else -> SAnime.UNKNOWN
}
}
}
// ============================== Episodes ==============================
override fun episodeListSelector() = "ul.chapters-list li a:has(h3)"
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
if (document.location().contains("/movies/")) {
return listOf(
SEpisode.create().apply {
setUrlWithoutDomain(document.location())
episode_number = 1F
name = "Movie"
},
)
}
return document.select(episodeListSelector()).flatMap { el ->
val doc = client.newCall(GET(el.attr("abs:href"), headers)).execute()
.asJsoup()
val seasonName = doc.selectFirst("div.media-title h1")!!.text()
val seasonNum = seasonName.substringAfterLast(" ").toIntOrNull() ?: 1
doc.select(episodeListSelector()).map {
episodeFromElement(it, seasonName, seasonNum)
}.reversed()
}.reversed()
}
private fun episodeFromElement(element: Element, seasonName: String, seasonNum: Int) = SEpisode.create().apply {
setUrlWithoutDomain(element.attr("href"))
val epText = element.selectFirst("h3")!!.ownText()
name = "$seasonName: " + epText
val epNum = epText.filter(Char::isDigit)
// good luck trying to track this xD
episode_number = "$seasonNum.${epNum.padStart(3, '0')}".toFloatOrNull() ?: 1F
}
override fun episodeFromElement(element: Element) = throw UnsupportedOperationException()
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val players = document.select(videoListSelector())
return players.parallelCatchingFlatMapBlocking(::getPlayerVideos)
}
override fun videoListSelector() = "li.dooplay_player_option" // ul#playeroptionsul
private val doodExtractor by lazy { DoodExtractor(client) }
private val gdrivePlayerExtractor by lazy { GdrivePlayerExtractor(client) }
private val streamTapeExtractor by lazy { StreamTapeExtractor(client) }
private val sharedExtractor by lazy { SharedExtractor(client) }
private val uqloadExtractor by lazy { UqloadExtractor(client) }
private val vidBomExtractor by lazy { VidBomExtractor(client) }
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
private val yourUploadExtractor by lazy { YourUploadExtractor(client) }
private fun getPlayerVideos(player: Element): List<Video> {
val url = getPlayerUrl(player) ?: return emptyList()
val name = player.selectFirst("span.title")!!.text().lowercase()
return when {
"ok.ru" in url -> okruExtractor.videosFromUrl(url)
"mp4upload" in url -> mp4uploadExtractor.videosFromUrl(url, headers)
"wish" in name -> streamWishExtractor.videosFromUrl(url)
"yourupload" in url -> yourUploadExtractor.videoFromUrl(url, headers)
"dood" in url -> doodExtractor.videoFromUrl(url)?.let(::listOf)
"drive.google" in url -> {
val newUrl = "https://gdriveplayer.to/embed2.php?link=$url"
gdrivePlayerExtractor.videosFromUrl(newUrl, "GdrivePlayer", headers)
}
"streamtape" in url -> streamTapeExtractor.videoFromUrl(url)?.let(::listOf)
"4shared" in url -> sharedExtractor.videoFromUrl(url)?.let(::listOf)
"uqload" in url -> uqloadExtractor.videosFromUrl(url)
VIDBOM_DOMAINS.any(url::contains) -> vidBomExtractor.videosFromUrl(url)
else -> null
} ?: emptyList()
}
private fun getPlayerUrl(player: Element): String? {
val body = FormBody.Builder()
.add("action", "doo_player_ajax")
.add("post", player.attr("data-post"))
.add("nume", player.attr("data-nume"))
.add("type", player.attr("data-type"))
.build()
return client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", headers, body))
.execute()
.let { response ->
response.body.string()
.substringAfter("\"embed_url\":\"")
.substringBefore("\",")
.replace("\\", "")
.takeIf(String::isNotBlank)
}
}
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
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()
}
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
entryValues = PREF_QUALITY_VALUES
setDefaultValue(PREF_QUALITY_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)
}
// ============================= Utilities ==============================
companion object {
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Preferred quality"
private const val PREF_QUALITY_DEFAULT = "1080"
private val PREF_QUALITY_ENTRIES = arrayOf("1080p", "720p", "480p", "360p", "Doodstream", "StreamTape")
private val PREF_QUALITY_VALUES = arrayOf("1080", "720", "480", "360", "Doodstream", "StreamTape")
private val VIDBOM_DOMAINS = listOf(
"vidbom.com", "vidbem.com", "vidbm.com", "vedpom.com",
"vedbom.com", "vedbom.org", "vadbom.com",
"vidbam.org", "myviid.com", "myviid.net",
"myvid.com", "vidshare.com", "vedsharr.com",
"vedshar.com", "vedshare.com", "vadshar.com", "vidshar.org",
)
}
}

View file

@ -0,0 +1,15 @@
package eu.kanade.tachiyomi.animeextension.ar.animerco.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
class SharedExtractor(private val client: OkHttpClient) {
fun videoFromUrl(url: String, quality: String = "mirror"): Video? {
val document = client.newCall(GET(url)).execute().asJsoup()
return document.selectFirst("source")?.let {
Video(it.attr("src"), "4Shared: $quality", it.attr("src"))
}
}
}

View file

@ -0,0 +1,7 @@
ext {
extName = 'ArabAnime'
extClass = '.ArabAnime'
extVersionCode = 2
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View file

@ -0,0 +1,268 @@
package eu.kanade.tachiyomi.animeextension.ar.arabanime
import android.app.Application
import android.util.Base64
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.ar.arabanime.dto.AnimeItem
import eu.kanade.tachiyomi.animeextension.ar.arabanime.dto.Episode
import eu.kanade.tachiyomi.animeextension.ar.arabanime.dto.PopularAnimeResponse
import eu.kanade.tachiyomi.animeextension.ar.arabanime.dto.ShowItem
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.AnimesPage
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.AnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import okhttp3.FormBody
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
class ArabAnime : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "ArabAnime"
override val baseUrl = "https://www.arabanime.net"
override val lang = "ar"
override val supportsLatest = true
private val json: Json by injectLazy()
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/api?page=$page")
override fun popularAnimeParse(response: Response): AnimesPage {
val responseJson = json.decodeFromString<PopularAnimeResponse>(response.body.string())
val animeList = responseJson.Shows.mapNotNull {
runCatching {
val animeJson = json.decodeFromString<AnimeItem>(it.decodeBase64())
SAnime.create().apply {
setUrlWithoutDomain(animeJson.info_src)
title = animeJson.anime_name
thumbnail_url = animeJson.anime_cover_image_url
}
}.getOrNull()
}
val hasNextPage = responseJson.current_page < responseJson.last_page
return AnimesPage(animeList, hasNextPage)
}
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int) = GET(baseUrl)
override fun latestUpdatesParse(response: Response): AnimesPage {
val latestEpisodes = response.asJsoup().select("div.as-episode")
val animeList = latestEpisodes.map {
SAnime.create().apply {
val ahref = it.selectFirst("a.as-info")!!
title = ahref.text()
val url = ahref.attr("href").replace("watch", "show").substringBeforeLast("/")
setUrlWithoutDomain(url)
thumbnail_url = it.selectFirst("img")?.absUrl("src")
}
}
return AnimesPage(animeList, false)
}
// =============================== Search ===============================
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
return if (query.isNotEmpty()) {
val body = FormBody.Builder().add("searchq", query).build()
POST("$baseUrl/searchq", body = body)
} else {
val type = filters.asQueryPart<TypeFilter>()
val status = filters.asQueryPart<StatusFilter>()
val order = filters.asQueryPart<OrderFilter>()
GET("$baseUrl/api?order=$order&type=$type&stat=$status&tags=&page=$page")
}
}
override fun searchAnimeParse(response: Response): AnimesPage {
return if (response.body.contentType() == "application/json".toMediaType()) {
popularAnimeParse(response)
} else {
val searchResult = response.asJsoup().select("div.show")
val animeList = searchResult.map {
SAnime.create().apply {
setUrlWithoutDomain(it.selectFirst("a")!!.attr("href"))
title = it.selectFirst("h3")!!.text()
thumbnail_url = it.selectFirst("img")?.absUrl("src")
}
}
return AnimesPage(animeList, false)
}
}
// ============================== filters ==============================
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("فلترة الموقع"),
OrderFilter(),
TypeFilter(),
StatusFilter(),
)
open class QueryPartFilter(
displayName: String,
val vals: Array<Pair<String, String>>,
) : AnimeFilter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray(),
) {
fun toQueryPart() = vals[state].second
}
private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
return (firstOrNull { it is R } as? QueryPartFilter)?.toQueryPart() ?: ""
}
private class OrderFilter : QueryPartFilter("ترتيب", ORDER_LIST)
private class TypeFilter : QueryPartFilter("النوع", TYPE_LIST)
private class StatusFilter : QueryPartFilter("الحالة", STATUS_LIST)
// =========================== Anime Details ============================
override fun animeDetailsParse(response: Response): SAnime {
val showData = response.asJsoup().selectFirst("div#data")!!
.text()
.decodeBase64()
val details = json.decodeFromString<ShowItem>(showData).show[0]
return SAnime.create().apply {
url = "/show-${details.anime_id}/${details.anime_slug}"
title = details.anime_name
status = when (details.anime_status) {
"Ongoing" -> SAnime.ONGOING
"Completed" -> SAnime.COMPLETED
else -> SAnime.UNKNOWN
}
genre = details.anime_genres
description = details.anime_description
thumbnail_url = details.anime_cover_image_url
}
}
// ============================== Episodes ==============================
override fun episodeListParse(response: Response): List<SEpisode> {
val showData = response.asJsoup().selectFirst("div#data")
?.text()
?.decodeBase64()
?: return emptyList()
val episodesJson = json.decodeFromString<ShowItem>(showData)
return episodesJson.EPS.map {
SEpisode.create().apply {
name = it.episode_name
episode_number = it.episode_number.toFloat()
setUrlWithoutDomain(it.`info-src`)
}
}.reversed()
}
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val watchData = response.asJsoup().selectFirst("div#datawatch")
?.text()
?.decodeBase64()
?: return emptyList()
val serversJson = json.decodeFromString<Episode>(watchData)
val selectServer = serversJson.ep_info[0].stream_servers[0].decodeBase64()
val watchPage = client.newCall(GET(selectServer)).execute().asJsoup()
return watchPage.select("option")
.map { it.text() to it.attr("data-src").decodeBase64() } // server : url
.filter { it.second.contains("$baseUrl/embed") } // filter urls
.flatMap { (name, url) ->
client.newCall(GET(url)).execute()
.asJsoup()
.select("source")
.mapNotNull { source ->
val videoUrl = source.attr("src")
if (!videoUrl.contains("static")) {
val quality = source.attr("label").let { q ->
if (q.contains("p")) q else q + "p"
}
Video(videoUrl, "$name: $quality", videoUrl)
} else {
null
}
}
}
}
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()
}
// =============================== Preferences ===============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
entryValues = PREF_QUALITY_VALUES
setDefaultValue(PREF_QUALITY_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(videoQualityPref)
}
private fun String.decodeBase64() = String(Base64.decode(this, Base64.DEFAULT))
companion object {
private val ORDER_LIST = arrayOf(
Pair("اختر", ""),
Pair("التقييم", "2"),
Pair("اخر الانميات المضافة", "1"),
Pair("الابجدية", "0"),
)
private val TYPE_LIST = arrayOf(
Pair("اختر", ""),
Pair("الكل", ""),
Pair("فيلم", "0"),
Pair("انمى", "1"),
)
private val STATUS_LIST = arrayOf(
Pair("اختر", ""),
Pair("الكل", ""),
Pair("مستمر", "1"),
Pair("مكتمل", "0"),
)
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Preferred quality"
private const val PREF_QUALITY_DEFAULT = "1080"
private val PREF_QUALITY_ENTRIES = arrayOf("1080p", "720p", "480p", "360p", "240p")
private val PREF_QUALITY_VALUES by lazy {
PREF_QUALITY_ENTRIES.map { it.substringBefore("p") }.toTypedArray()
}
}
}

View file

@ -0,0 +1,62 @@
package eu.kanade.tachiyomi.animeextension.ar.arabanime.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class PopularAnimeResponse(
val Shows: List<String>,
val current_page: Int,
val last_page: Int,
)
@Serializable
data class AnimeItem(
val anime_cover_image_url: String,
val anime_id: String,
val anime_name: String,
val anime_score: String,
val anime_slug: String,
val anime_type: String,
val info_src: String,
)
@Serializable
data class ShowItem(
val EPS: List<EPS>,
val show: List<Show>,
)
@Serializable
data class EPS(
val episode_name: String,
val episode_number: Int,
@SerialName("info-src")
val `info-src`: String,
)
@Serializable
data class Show(
val anime_cover_image_url: String,
val anime_description: String,
val anime_genres: String,
val anime_id: Int,
val anime_name: String,
val anime_release_date: String,
val anime_score: String,
val anime_slug: String,
val anime_status: String,
val anime_type: String,
val show_episode_count: Int,
val wallpapaer: String,
)
@Serializable
data class Episode(
val ep_info: List<EpInfo>,
)
@Serializable
data class EpInfo(
val stream_servers: List<String>,
)

View file

@ -0,0 +1,13 @@
ext {
extName = 'Arab Seed'
extClass = '.ArabSeed'
extVersionCode = 13
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:dood-extractor"))
implementation(project(":lib:voe-extractor"))
implementation(project(":lib:streamwish-extractor"))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View file

@ -0,0 +1,237 @@
package eu.kanade.tachiyomi.animeextension.ar.arabseed
import android.app.Application
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.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
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 ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "عرب سيد"
// TODO: Check frequency of url changes to potentially
// add back overridable baseurl preference
override val baseUrl = "https://m.asd.homes"
override val lang = "ar"
override val supportsLatest = false
override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl)
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeSelector() = "ul.Blocks-UL div.MovieBlock a"
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/movies/?offset=$page")
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
setUrlWithoutDomain(element.attr("href"))
title = element.selectFirst("div.BlockName > h4")!!.text()
thumbnail_url = element.selectFirst("div.Poster img")!!.attr("data-src")
}
override fun popularAnimeNextPageSelector() = "ul.page-numbers li a.next"
// ============================== Episode ===============================
override fun episodeListSelector() = "div.ContainerEpisodesList a"
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val episodes = document.select(episodeListSelector())
return when {
episodes.isEmpty() -> {
SEpisode.create().apply {
setUrlWithoutDomain(document.location())
name = "مشاهدة"
}.let(::listOf)
}
else -> episodes.map(::episodeFromElement)
}
}
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
setUrlWithoutDomain(element.attr("href"))
name = element.text()
episode_number = element.selectFirst("em")?.text()?.toFloatOrNull() ?: 0F
}
// ============================ Video Links =============================
override fun videoListParse(response: Response): List<Video> {
val doc = response.asJsoup()
val watchUrl = doc.selectFirst("a.watchBTn")!!.attr("href")
val element = client.newCall(GET(watchUrl, headers)).execute().asJsoup()
return videosFromElement(element)
}
override fun videoListSelector() = "div.containerServers ul li"
private fun videosFromElement(document: Document): List<Video> {
return document.select(videoListSelector()).parallelCatchingFlatMapBlocking { element ->
val quality = element.text()
val embedUrl = element.attr("data-link")
getVideosFromUrl(embedUrl, quality)
}
}
private val doodExtractor by lazy { DoodExtractor(client) }
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
private val voeExtractor by lazy { VoeExtractor(client) }
private fun getVideosFromUrl(url: String, quality: String): List<Video> {
return when {
"reviewtech" in url || "reviewrate" in url -> {
val iframeResponse = client.newCall(GET(url)).execute()
.asJsoup()
val videoUrl = iframeResponse.selectFirst("source")!!.attr("abs:src")
listOf(Video(videoUrl, quality + "p", videoUrl))
}
"dood" in url -> doodExtractor.videosFromUrl(url)
"fviplions" in url || "wish" in url -> streamwishExtractor.videosFromUrl(url)
"voe.sx" in url -> voeExtractor.videosFromUrl(url)
else -> null
} ?: emptyList()
}
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
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()
}
// =============================== Search ===============================
override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector()
override fun searchAnimeSelector() = popularAnimeSelector()
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val url = if (query.isNotBlank()) {
"$baseUrl/find/?find=$query&offset=$page"
} else {
val filterList = if (filters.isEmpty()) getFilterList() else filters
val typeFilter = filterList.find { it is TypeFilter } as TypeFilter
val category = typeFilter.toUriPart()
if (category.isEmpty()) throw Exception("اختر فلتر")
"$baseUrl/category/$category"
}
return GET(url, headers)
}
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document) = SAnime.create().apply {
thumbnail_url = document.selectFirst("div.Poster img")!!.let { img ->
img.attr("abs:data-src")
.ifEmpty { img.attr("abs:data-lazy-src") }
.ifEmpty { img.attr("abs:src") }
}
title = document.selectFirst("div.BreadCrumbs ol li:last-child a span")!!
.text()
.replace(" مترجم", "").replace("فيلم ", "")
genre = document.select("div.MetaTermsInfo > li:contains(النوع) > a").eachText().joinToString()
description = document.selectFirst("div.StoryLine p")!!.text()
status = when {
document.location().contains("/selary/") -> SAnime.UNKNOWN
else -> SAnime.COMPLETED
}
}
// ============================== Filters ===============================
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("الفلترات مش هتشتغل لو بتبحث او وهي فاضيه"),
TypeFilter(),
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
private class TypeFilter : UriPartFilter(
"نوع الفلم",
arrayOf(
Pair("أختر", ""),
Pair("افلام عربي", "arabic-movies-5/"),
Pair("افلام اجنبى", "foreign-movies3/"),
Pair("افلام اسيوية", "%d8%a7%d9%81%d9%84%d8%a7%d9%85-%d8%a7%d8%b3%d9%8a%d9%88%d9%8a%d8%a9/"),
Pair("افلام هندى", "indian-movies/"),
Pair("افلام تركية", "%d8%a7%d9%81%d9%84%d8%a7%d9%85-%d8%aa%d8%b1%d9%83%d9%8a%d8%a9/"),
Pair("افلام انيميشن", "%d8%a7%d9%81%d9%84%d8%a7%d9%85-%d8%a7%d9%86%d9%8a%d9%85%d9%8a%d8%b4%d9%86/"),
Pair("افلام كلاسيكيه", "%d8%a7%d9%81%d9%84%d8%a7%d9%85-%d9%83%d9%84%d8%a7%d8%b3%d9%8a%d9%83%d9%8a%d9%87/"),
Pair("افلام مدبلجة", "%d8%a7%d9%81%d9%84%d8%a7%d9%85-%d9%85%d8%af%d8%a8%d9%84%d8%ac%d8%a9/"),
Pair("افلام Netfilx", "netfilx/افلام-netfilx/"),
Pair("مسلسلات عربي", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b9%d8%b1%d8%a8%d9%8a/"),
Pair("مسلسلات اجنبي", "foreign-series/"),
Pair("مسلسلات تركيه", "turkish-series-1/"),
Pair("برامج تلفزيونية", "%d8%a8%d8%b1%d8%a7%d9%85%d8%ac-%d8%aa%d9%84%d9%81%d8%b2%d9%8a%d9%88%d9%86%d9%8a%d8%a9/"),
Pair("مسلسلات كرتون", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d9%83%d8%b1%d8%aa%d9%88%d9%86/"),
Pair("مسلسلات رمضان 2019", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2019/"),
Pair("مسلسلات رمضان 2020", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2020-hd/"),
Pair("مسلسلات رمضان 2021", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2021/"),
Pair("مسلسلات Netfilx", "netfilx/%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-netfilz/"),
),
)
// =============================== Latest ===============================
override fun latestUpdatesNextPageSelector(): String? = throw UnsupportedOperationException()
override fun latestUpdatesFromElement(element: Element): SAnime = throw UnsupportedOperationException()
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
override fun latestUpdatesSelector(): String = throw UnsupportedOperationException()
// =============================== Preferences ===============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
entryValues = PREF_QUALITY_VALUES
setDefaultValue(PREF_QUALITY_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(videoQualityPref)
}
// ============================= Utilities ==============================
companion object {
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Preferred quality"
private const val PREF_QUALITY_DEFAULT = "1080"
private val PREF_QUALITY_ENTRIES = arrayOf("1080p", "720p", "480p", "360p")
private val PREF_QUALITY_VALUES by lazy {
PREF_QUALITY_ENTRIES.map { it.substringBefore("p") }.toTypedArray()
}
}
}

View file

@ -0,0 +1,16 @@
ext {
extName = 'asia2tv'
extClass = '.Asia2TV'
extVersionCode = 17
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:dood-extractor'))
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:uqload-extractor'))
implementation(project(':lib:vidbom-extractor'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View file

@ -0,0 +1,266 @@
package eu.kanade.tachiyomi.animeextension.ar.asia2tv
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.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
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 Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Asia2TV"
override val baseUrl = "https://ww1.asia2tv.pw"
override val lang = "ar"
override val supportsLatest = false
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeSelector(): String = "div.postmovie-photo a[title]"
override fun popularAnimeNextPageSelector(): String = "div.nav-links a.next"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/category/asian-drama/page/$page/") // page/$page
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
// anime.thumbnail_url = element.selectFirst("div.image img")!!.attr("data-src")
anime.title = element.attr("title")
return anime
}
// ============================== Episodes ==============================
override fun episodeListSelector() = "div.loop-episode a"
override fun episodeListParse(response: Response): List<SEpisode> {
return super.episodeListParse(response).reversed()
}
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.attr("href"))
episode.name = element.attr("href").substringAfterLast("-").substringBeforeLast("/") + " : الحلقة"
return episode
}
// ============================ Video Links =============================
override fun videoListSelector() = "ul.server-list-menu li"
override fun videoListRequest(episode: SEpisode): Request {
val document = client.newCall(GET(baseUrl + episode.url)).execute()
.asJsoup()
val link = document.selectFirst("div.loop-episode a.current")!!.attr("href")
return GET(link)
}
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
return document.select(videoListSelector()).parallelCatchingFlatMapBlocking {
val url = it.attr("data-server")
getVideosFromUrl(url)
}
}
private val doodExtractor by lazy { DoodExtractor(client) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
private val uqloadExtractor by lazy { UqloadExtractor(client) }
private val vidbomExtractor by lazy { VidBomExtractor(client) }
private fun getVideosFromUrl(url: String): List<Video> {
return when {
"dood" in url || "ds2play" in url -> doodExtractor.videosFromUrl(url)
"ok.ru" in url || "odnoklassniki.ru" in url -> okruExtractor.videosFromUrl(url)
"streamtape" in url -> streamtapeExtractor.videoFromUrl(url)?.let(::listOf)
STREAM_WISH_DOMAINS.any(url::contains) -> streamwishExtractor.videosFromUrl(url)
"uqload" in url -> uqloadExtractor.videosFromUrl(url)
VID_BOM_DOMAINS.any(url::contains) -> vidbomExtractor.videosFromUrl(url)
"youdbox" in url || "yodbox" in url -> {
client.newCall(GET(url)).execute().let {
val doc = it.asJsoup()
val videoUrl = doc.selectFirst("source")?.attr("abs:src")
when (videoUrl) {
null -> emptyList()
else -> listOf(Video(videoUrl, "Yodbox: mirror", videoUrl))
}
}
}
else -> null
} ?: emptyList()
}
override fun videoFromElement(element: Element): Video = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document): String = throw UnsupportedOperationException()
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
}
// =============================== Search ===============================
override fun searchAnimeSelector(): String = "div.postmovie-photo a[title]"
override fun searchAnimeNextPageSelector(): String = "div.nav-links a.next"
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
// anime.thumbnail_url = element.selectFirst("div.image img")!!.attr("data-src")
anime.title = element.attr("title")
return anime
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val url = if (query.isNotBlank()) {
"$baseUrl/page/$page/?s=$query"
} else {
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
is TypeList -> {
if (filter.state > 0) {
val genreN = getTypeList()[filter.state].query
val genreUrl = "$baseUrl/category/asian-drama/$genreN/page/$page/".toHttpUrlOrNull()!!.newBuilder()
return GET(genreUrl.toString(), headers)
}
}
is StatusList -> {
if (filter.state > 0) {
val statusN = getStatusList()[filter.state].query
val statusUrl = "$baseUrl/$statusN/page/$page/".toHttpUrlOrNull()!!.newBuilder()
return GET(statusUrl.toString(), headers)
}
}
else -> {}
}
}
throw Exception("اختر فلتر")
}
return GET(url, headers)
}
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.title = document.select("h1 span.title").text()
anime.thumbnail_url = document.select("div.single-thumb-bg > img").attr("src")
anime.description = document.select("div.getcontent p").text()
anime.genre = document.select("div.box-tags a, li:contains(البلد) a").joinToString(", ") { it.text() }
return anime
}
// =============================== Latest ===============================
override fun latestUpdatesNextPageSelector(): String = throw UnsupportedOperationException()
override fun latestUpdatesFromElement(element: Element): SAnime = throw UnsupportedOperationException()
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
override fun latestUpdatesSelector(): String = throw UnsupportedOperationException()
// ============================== Filters ===============================
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("الفلترات مش هتشتغل لو بتبحث او وهي فاضيه"),
TypeList(typesName),
StatusList(statusesName),
)
private class TypeList(types: Array<String>) : AnimeFilter.Select<String>("نوع الدراما", types)
private data class Type(val name: String, val query: String)
private val typesName = getTypeList().map {
it.name
}.toTypedArray()
private class StatusList(statuse: Array<String>) : AnimeFilter.Select<String>("حالة الدراما", statuse)
private data class Status(val name: String, val query: String)
private val statusesName = getStatusList().map {
it.name
}.toTypedArray()
private fun getTypeList() = listOf(
Type("اختر", ""),
Type("الدراما الكورية", "korean"),
Type("الدراما اليابانية", "japanese"),
Type("الدراما الصينية والتايوانية", "chinese-taiwanese"),
Type("الدراما التايلاندية", "thai"),
Type("برامج الترفيه", "kshow"),
)
private fun getStatusList() = listOf(
Status("أختر", ""),
Status("يبث حاليا", "status/ongoing-drama"),
Status("الدراما المكتملة", "completed-dramas"),
Status("الدراما القادمة", "status/upcoming-drama"),
)
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality & Server"
entries = arrayOf("StreamTape", "DooDStream", "1080p", "720p", "480p", "360p")
entryValues = arrayOf("StreamTape", "Dood", "1080", "720", "480", "360")
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)
}
// ============================= Utilities ==============================
companion object {
private val STREAM_WISH_DOMAINS by lazy { listOf("wishfast", "fviplions", "filelions", "streamwish", "dwish") }
private val VID_BOM_DOMAINS by lazy { listOf("vidbam", "vadbam", "vidbom", "vidbm") }
}
}

View file

@ -0,0 +1,11 @@
ext {
extName = 'Cimaleek'
extClass = '.Cimaleek'
extVersionCode = 1
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:playlist-utils"))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,281 @@
package eu.kanade.tachiyomi.animeextension.ar.cimaleek
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.ar.cimaleek.interceptor.WebViewResolver
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.Track
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 eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import okhttp3.HttpUrl.Companion.toHttpUrl
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
class Cimaleek : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "سيما ليك"
override val baseUrl = "https://m.cimaleek.to"
override val lang = "ar"
override val supportsLatest = true
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
private val webViewResolver by lazy { WebViewResolver(headers) }
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
// ============================== Popular ===============================
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.title = element.select("div.data .title").text()
anime.thumbnail_url = element.select("img").attr("data-src")
anime.setUrlWithoutDomain(element.select("a").attr("href"))
return anime
}
override fun popularAnimeNextPageSelector(): String = "div.pagination div.pagination-num i#nextpagination"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/trending/page/$page/", headers)
override fun popularAnimeSelector(): String = "div.film_list-wrap div.item"
// ============================== Episodes ==============================
override fun episodeFromElement(element: Element): SEpisode = throw UnsupportedOperationException()
override fun episodeListParse(response: Response): List<SEpisode> {
val episodes = mutableListOf<SEpisode>()
val document = response.asJsoup()
val url = response.request.url.toString()
if (url.contains("movies")) {
val episode = SEpisode.create().apply {
name = "مشاهدة"
setUrlWithoutDomain("$url/watch/")
}
episodes.add(episode)
} else {
document.select(seasonListSelector()).parallelCatchingFlatMapBlocking { sElement ->
val seasonNum = sElement.select("span.se-a").text()
val seasonUrl = sElement.attr("href")
val seasonPage = client.newCall(GET(seasonUrl, headers)).execute().asJsoup()
seasonPage.select(episodeListSelector()).map { eElement ->
val episodeNum = eElement.select("span.serie").text().substringAfter("(").substringBefore(")")
val episodeUrl = eElement.attr("href")
val finalNum = ("$seasonNum.$episodeNum").toFloat()
val episodeTitle = "الموسم ${seasonNum.toInt()} الحلقة ${episodeNum.toInt()}"
val episode = SEpisode.create().apply {
name = episodeTitle
episode_number = finalNum
setUrlWithoutDomain("$episodeUrl/watch/")
}
episodes.add(episode)
}
}
}
return episodes.sortedBy { it.episode_number }.reversed()
}
override fun episodeListSelector(): String = "div.season-a ul.episodios li.episodesList a"
private fun seasonListSelector(): String = "div.season-a ul.seas-list li.sealist a"
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = document.select("div.ani_detail-stage div.film-poster img").attr("src")
anime.title = document.select("div.anisc-more-info div.item:contains(الاسم) span:nth-child(3)").text()
anime.author = document.select("div.anisc-more-info div.item:contains(البلد) span:nth-child(3)").text()
anime.genre = document.select("div.anisc-detail div.item-list a").joinToString(", ") { it.text() }
anime.description = document.select("div.anisc-detail div.film-description div.text").text()
anime.status = if (document.select("div.anisc-detail div.item-list").text().contains("افلام")) SAnime.COMPLETED else SAnime.UNKNOWN
return anime
}
// ============================ Video Links =============================
override fun videoFromElement(element: Element): Video = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document): String = throw UnsupportedOperationException()
override fun videoListSelector(): String = "div#servers-content div.server-item div"
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val script = document.selectFirst("script:containsData(dtAjax)")!!.data()
val version = script.substringAfter("ver\":\"").substringBefore("\"")
return document.select(videoListSelector()).parallelCatchingFlatMapBlocking {
extractVideos(it, version)
}
}
private fun generateRandomString(): String {
val characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
val result = StringBuilder(16)
for (i in 0 until 16) {
val randomIndex = (Math.random() * characters.length).toInt()
result.append(characters[randomIndex])
}
return result.toString()
}
private fun extractVideos(element: Element, version: String): List<Video> {
val videoUrl = "$baseUrl/wp-json/lalaplayer/v2/".toHttpUrl().newBuilder()
videoUrl.addQueryParameter("p", element.attr("data-post"))
videoUrl.addQueryParameter("t", element.attr("data-type"))
videoUrl.addQueryParameter("n", element.attr("data-nume"))
videoUrl.addQueryParameter("ver", version)
videoUrl.addQueryParameter("rand", generateRandomString())
val videoFrame = client.newCall(GET(videoUrl.toString(), headers)).execute().body.string()
val embedUrl = videoFrame.substringAfter("embed_url\":\"").substringBefore("\"")
val referer = headers.newBuilder().add("Referer", "$baseUrl/").build()
val webViewResult = webViewResolver.getUrl(embedUrl, referer)
return when {
".mp4" in webViewResult.url -> {
Video(webViewResult.url, element.text(), webViewResult.url, headers = referer).let(::listOf)
}
".m3u8" in webViewResult.url -> {
val subtitleList = if (webViewResult.subtitle.isNotBlank()) Track(webViewResult.subtitle, "Arabic").let(::listOf) else emptyList()
playlistUtils.extractFromHls(webViewResult.url, videoNameGen = { "${element.text()}: $it" }, subtitleList = subtitleList)
}
else -> emptyList()
}
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "1080")!!
return sortedWith(
compareBy { it.quality.contains(quality) },
).reversed()
}
// =============================== Search ===============================
override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
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.toHttpUrl().newBuilder()
if (sectionFilter.state != 0) {
url.addPathSegment("category")
url.addPathSegment(sectionFilter.toUriPart())
} else if (categoryFilter.state != 0) {
url.addPathSegment("genre")
url.addPathSegment(genreFilter.toUriPart().lowercase())
} else {
throw Exception("من فضلك اختر قسم او نوع")
}
url.addPathSegment("page")
url.addPathSegment("$page")
if (categoryFilter.state != 0) {
url.addQueryParameter("type", categoryFilter.toUriPart())
}
GET(url.toString(), headers)
}
}
override fun searchAnimeSelector(): String = popularAnimeSelector()
// ============================ Filters =============================
override fun getFilterList() = AnimeFilterList(
AnimeFilter.Header("هذا القسم يعمل لو كان البحث فارع"),
SectionFilter(),
AnimeFilter.Separator(),
AnimeFilter.Header("الفلتره تعمل فقط لو كان اقسام الموقع على 'اختر'"),
CategoryFilter(),
GenreFilter(),
)
private class SectionFilter : PairFilter(
"اقسام الموقع",
arrayOf(
Pair("اختر", "none"),
Pair("افلام اجنبي", "aflam-online"),
Pair("افلام نتفليكس", "netflix-movies"),
Pair("افلام هندي", "indian-movies"),
Pair("افلام اسيوي", "asian-aflam"),
Pair("افلام كرتون", "cartoon-movies"),
Pair("افلام انمي", "anime-movies"),
Pair("مسلسلات اجنبي", "english-series"),
Pair("مسلسلات نتفليكس", "netflix-series"),
Pair("مسلسلات اسيوي", "asian-series"),
Pair("مسلسلات كرتون", "anime-series"),
Pair("مسلسلات انمي", "netflix-anime"),
),
)
private class CategoryFilter : PairFilter(
"النوع",
arrayOf(
Pair("اختر", "none"),
Pair("افلام", "movies"),
Pair("مسلسلات", "series"),
),
)
private class GenreFilter : SingleFilter(
"التصنيف",
arrayOf(
"Action", "Adventure", "Animation", "Western", "Documentary", "Fantasy", "Science-fiction", "Romance", "Comedy", "Family", "Drama", "Thriller", "Crime", "Horror",
).sortedArray(),
)
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
}
// =============================== Latest ===============================
override fun latestUpdatesFromElement(element: Element): SAnime = popularAnimeFromElement(element)
override fun latestUpdatesNextPageSelector(): String = popularAnimeNextPageSelector()
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/recent/page/$page/", headers)
override fun latestUpdatesSelector(): String = popularAnimeSelector()
// =============================== 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

@ -0,0 +1,75 @@
package eu.kanade.tachiyomi.animeextension.ar.cimaleek.interceptor
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.WebView
import android.webkit.WebViewClient
import okhttp3.Headers
import uy.kohesive.injekt.injectLazy
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class WebViewResolver(private val globalHeaders: Headers) {
private val context: Application by injectLazy()
private val handler by lazy { Handler(Looper.getMainLooper()) }
@SuppressLint("SetJavaScriptEnabled")
fun getUrl(origRequestUrl: String, origRequestheader: Headers): Result {
val latch = CountDownLatch(2)
var webView: WebView? = null
val result = Result("", "")
val headers = origRequestheader.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap()
handler.post {
val webview = WebView(context)
webView = webview
with(webview.settings) {
javaScriptEnabled = true
domStorageEnabled = true
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = globalHeaders["User-Agent"]
}
webview.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest,
): WebResourceResponse? {
val url = request.url.toString()
if ("vtt" in url) {
result.subtitle = url
latch.countDown()
}
if (VIDEO_REGEX.containsMatchIn(url)) {
result.url = url
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 result
}
companion object {
const val TIMEOUT_SEC: Long = 25
private val VIDEO_REGEX by lazy { Regex("\\.(mp4|m3u8)") }
}
data class Result(var url: String, var subtitle: String)
}

View file

@ -0,0 +1,13 @@
ext {
extName = 'Egy Dead'
extClass = '.EgyDead'
extVersionCode = 12
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:dood-extractor'))
implementation(project(':lib:mixdrop-extractor'))
implementation(project(':lib:streamwish-extractor'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View file

@ -0,0 +1,320 @@
package eu.kanade.tachiyomi.animeextension.ar.egydead
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.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMap
import okhttp3.FormBody
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
class EgyDead : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Egy Dead"
// TODO: Check frequency of url changes to potentially
// add back overridable baseurl preference
override val baseUrl = "https://egydead.space"
override val lang = "ar"
override val supportsLatest = true
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ================================== popular ==================================
override fun popularAnimeSelector(): String = "div.pin-posts-list li.movieItem"
override fun popularAnimeNextPageSelector(): String = "div.whatever"
override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("a").attr("href"))
anime.title = element.select("h1.BottomTitle").text()
anime.thumbnail_url = element.select("a img").attr("src")
return anime
}
// ================================== episodes ==================================
override fun episodeListParse(response: Response): List<SEpisode> {
val episodes = mutableListOf<SEpisode>()
fun episodeExtract(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.attr("href"))
episode.name = element.attr("title")
return episode
}
fun addEpisodes(res: Response, final: Boolean = false) {
val document = res.asJsoup()
val url = res.request.url.toString()
if (final) {
document.select(episodeListSelector()).map {
val episode = episodeFromElement(it)
val season = document.select("div.infoBox div.singleTitle").text()
val seasonTxt = season.substringAfter("الموسم ").substringBefore(" ")
episode.name = if (season.contains("موسم"))"الموسم $seasonTxt ${episode.name}" else episode.name
episodes.add(episode)
}
} else if (url.contains("assembly")) {
document.select("div.salery-list li.movieItem a").map {
episodes.add(episodeExtract(it))
}
} else if (url.contains("serie") || url.contains("season")) {
if (document.select("div.seasons-list li.movieItem a").isNullOrEmpty()) {
document.select(episodeListSelector()).map {
episodes.add(episodeFromElement(it))
}
} else {
document.select("div.seasons-list li.movieItem a").map {
addEpisodes(client.newCall(GET(it.attr("href"))).execute(), true)
}
}
} else if (url.contains("episode")) {
document.selectFirst("#breadcrumbs li a[itemprop=url]")?.let {
addEpisodes(client.newCall(GET(it.attr("href"))).execute())
}
} else {
val episode = SEpisode.create()
episode.name = "مشاهدة"
episode.setUrlWithoutDomain(url)
episodes.add(episode)
}
// document.select(episodeListSelector()).map { episodes.add(episodeFromElement(it)) }
}
addEpisodes(response)
return episodes
}
override fun episodeListSelector() = "div.EpsList li a"
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.attr("href"))
episode.name = element.select("a").text()
episode.episode_number = element.select("a").text().filter { it.isDigit() }.toFloat()
return episode
}
// ================================== video urls ==================================
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
override suspend fun getVideoList(episode: SEpisode): List<Video> {
val requestBody = FormBody.Builder().add("View", "1").build()
val document = client.newCall(POST(baseUrl + episode.url, body = requestBody))
.await()
.asJsoup()
return document.select(videoListSelector()).parallelCatchingFlatMap {
val url = it.attr("data-link")
extractVideos(url)
}
}
private fun extractVideos(url: String): List<Video> {
return when {
DOOD_REGEX.containsMatchIn(url) -> {
DoodExtractor(client).videoFromUrl(url, "Dood mirror")?.let(::listOf)
}
url.contains("mdbekjwqa") -> {
MixDropExtractor(client).videoFromUrl(url)
}
url.contains("ahvsh") -> {
val request = client.newCall(GET(url, headers)).execute().asJsoup()
val script = request.selectFirst("script:containsData(sources)")!!.data()
val streamLink = Regex("sources:\\s*\\[\\{\\s*\\t*file:\\s*[\"']([^\"']+)").find(script)!!.groupValues[1]
val quality = Regex("'qualityLabels'\\s*:\\s*\\{\\s*\".*?\"\\s*:\\s*\"(.*?)\"").find(script)!!.groupValues[1]
Video(streamLink, "StreamHide: $quality", streamLink).let(::listOf)
}
STREAMWISH_REGEX.containsMatchIn(url) -> {
streamWishExtractor.videosFromUrl(url)
}
url.contains("fanakishtuna") -> {
val request = client.newCall(GET(url, headers)).execute().asJsoup()
val data = request.selectFirst("script:containsData(sources)")!!.data()
val streamLink = Regex("sources:\\s*\\[\\{\\s*\\t*file:\\s*[\"']([^\"']+)").find(data)!!.groupValues[1]
listOf(Video(streamLink, "Mirror: High Quality", streamLink))
}
url.contains("uqload") -> {
val newURL = url.replace("https://uqload.co/", "https://www.uqload.co/")
val request = client.newCall(GET(newURL, headers)).execute().asJsoup()
val data = request.selectFirst("script:containsData(sources)")!!.data()
val streamLink = data.substringAfter("sources: [\"").substringBefore("\"]")
listOf(Video(streamLink, "Uqload: Mirror", streamLink))
}
else -> null
} ?: emptyList()
}
override fun videoListSelector() = "ul.serversList li"
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "1080p")
if (quality != null) {
val newList = mutableListOf<Video>()
var preferred = 0
for (video in this) {
if (video.quality == quality) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
return this
}
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
// ================================== search ==================================
override fun searchAnimeNextPageSelector(): String = "div.pagination-two a:contains()"
override fun searchAnimeSelector(): String = "div.catHolder li.movieItem"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val url = if (query.isNotBlank()) {
"$baseUrl/page/$page/?s=$query"
} else {
val url = baseUrl
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
is CategoryList -> {
if (filter.state > 0) {
val catQ = getCategoryList()[filter.state].query
val catUrl = "$baseUrl/$catQ/?page=$page/"
return GET(catUrl, headers)
}
}
else -> {}
}
}
return GET(url, headers)
}
return GET(url, headers)
}
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("a").attr("href"))
anime.title = element.select("h1.BottomTitle").text()
anime.thumbnail_url = element.select("a img").attr("src")
return anime
}
override fun getFilterList() = AnimeFilterList(
CategoryList(categoriesName),
)
private class CategoryList(categories: Array<String>) : AnimeFilter.Select<String>("الأقسام", categories)
private data class CatUnit(val name: String, val query: String)
private val categoriesName = getCategoryList().map {
it.name
}.toTypedArray()
private fun getCategoryList() = listOf(
CatUnit("اختر القسم", ""),
CatUnit("افلام اجنبى", "category/افلام-اجنبي"),
CatUnit("افلام اسلام الجيزاوى", "category/ترجمات-اسلام-الجيزاوي"),
CatUnit("افلام انمى", "category/افلام-كرتون"),
CatUnit("افلام تركيه", "category/افلام-تركية"),
CatUnit("افلام اسيويه", "category/افلام-اسيوية"),
CatUnit("افلام مدبلجة", "category/افلام-اجنبية-مدبلجة"),
CatUnit("سلاسل افلام", "assembly"),
CatUnit("مسلسلات اجنبية", "series-category/مسلسلات-اجنبي"),
CatUnit("مسلسلات انمى", "series-category/مسلسلات-انمي"),
CatUnit("مسلسلات تركية", "series-category/مسلسلات-تركية"),
CatUnit("مسلسلات اسيوىة", "series-category/مسلسلات-اسيوية"),
CatUnit("مسلسلات لاتينية", "series-category/مسلسلات-لاتينية"),
CatUnit("المسلسلات الكاملة", "serie"),
CatUnit("المواسم الكاملة", "season"),
)
// ================================== details ==================================
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = document.select("div.single-thumbnail img").attr("src")
anime.title = document.select("div.infoBox div.singleTitle").text()
anime.author = document.select("div.LeftBox li:contains(البلد) a").text()
anime.artist = document.select("div.LeftBox li:contains(القسم) a").text()
anime.genre = document.select("div.LeftBox li:contains(النوع) a, div.LeftBox li:contains(اللغه) a, div.LeftBox li:contains(السنه) a").joinToString(", ") { it.text() }
anime.description = document.select("div.infoBox div.extra-content p").text()
anime.status = if (anime.title.contains("كامل") || anime.title.contains("فيلم")) SAnime.COMPLETED else SAnime.ONGOING
return anime
}
// ================================== latest ==================================
override fun latestUpdatesSelector(): String = "section.main-section li.movieItem"
override fun latestUpdatesNextPageSelector(): String = "div.pagination ul.page-numbers li a.next"
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/?page=$page/")
override fun latestUpdatesFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.select("a").attr("href"))
anime.title = element.select("h1.BottomTitle").text()
anime.thumbnail_url = element.select("a img").attr("src")
return anime
}
// ================================== preferences ==================================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "240p", "DoodStream", "Uqload")
entryValues = arrayOf("1080", "720", "480", "360", "240", "Dood", "Uqload")
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)
}
// like|kharabnahk
companion object {
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/")
}
}

View file

@ -0,0 +1,11 @@
ext {
extName = 'FASELHD'
extClass = '.FASELHD'
extVersionCode = 15
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:playlist-utils"))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Some files were not shown because too many files have changed in this diff Show more