Shahed4u: fix and added more video sources (#879)
* Shahid4U extension * optimize search and fix arabic movies name not showing * optimized searching and some series not showing episode * fixed and added more video source * fixed and added more video source
This commit is contained in:
@ -5,7 +5,7 @@ ext {
|
||||
extName = 'شاهد فور يو'
|
||||
pkgNameSuffix = 'ar.shahid4u'
|
||||
extClass = '.Shahid4U'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
libVersion = '13'
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,10 @@ import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors.DoodExtractor
|
||||
import eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors.OkruExtractor
|
||||
import eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors.UQLoadExtractor
|
||||
import eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors.VidBomExtractor
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
@ -29,7 +33,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override val name = "شاهد فور يو"
|
||||
|
||||
override val baseUrl = "https://shahed4u.mx/"
|
||||
override val baseUrl = "https://shahed4u.team/"
|
||||
|
||||
override val lang = "ar"
|
||||
|
||||
@ -45,7 +49,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override fun popularAnimeSelector(): String = "div.glide-slides div.media-block"
|
||||
|
||||
override fun popularAnimeRequest(page: Int): Request = GET(if (page == 1)" $baseUrl/home2/" else "$baseUrl")
|
||||
override fun popularAnimeRequest(page: Int): Request = GET(if (page == 1)" $baseUrl/home2/" else baseUrl)
|
||||
|
||||
override fun popularAnimeFromElement(element: Element): SAnime {
|
||||
val anime = SAnime.create()
|
||||
@ -55,15 +59,25 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
return anime
|
||||
}
|
||||
|
||||
private fun titleEdit(title: String): String {
|
||||
return if (title.contains("فيلم"))
|
||||
Regex("فيلم(.*?)مترجم").find(title)!!.groupValues[1] + "(فيلم)"
|
||||
else if (title.contains("مسلسل"))
|
||||
Regex(if (title.contains("الموسم"))"مسلسل(.*?)الموسم" else "مسلسل(.*?)الحلقة").find(title)!!.groupValues[1] + "(مسلسل)"
|
||||
else if (title.contains("انمي"))
|
||||
Regex(if (title.contains("الموسم"))"انمي(.*?)الموسم" else "انمي(.*?)الحلقة").find(title)!!.groupValues[1] + "(انمى)"
|
||||
private fun titleEdit(title: String, details: Boolean = false): String {
|
||||
return if (Regex("فيلم (.*?) مترجم").containsMatchIn(title))
|
||||
Regex("فيلم (.*?) مترجم").find(title)!!.groupValues[1] + " (فيلم)" // افلام اجنبيه مترجمه
|
||||
else if (Regex("فيلم (.*?) مدبلج").containsMatchIn(title))
|
||||
Regex("فيلم (.*?) مدبلج").find(title)!!.groupValues[1] + " (مدبلج)(فيلم)" // افلام اجنبيه مدبلجه
|
||||
else if (Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").containsMatchIn(title)) // افلام عربى
|
||||
Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").find(title)!!.groupValues[1] + " (فيلم)"
|
||||
else if (title.contains("مسلسل")) {
|
||||
if (title.contains("الموسم") and details) {
|
||||
val newTitle = Regex("مسلسل (.*?) الموسم (.*?) الحلقة ([0-9]+)").find(title)
|
||||
return "${newTitle!!.groupValues[1]} (م.${newTitle.groupValues[2]})(${newTitle.groupValues[3]}ح)"
|
||||
} else if (title.contains("الحلقة") and details) {
|
||||
val newTitle = Regex("مسلسل (.*?) الحلقة ([0-9]+)").find(title)
|
||||
return "${newTitle!!.groupValues[1]} (${newTitle.groupValues[2]}ح)"
|
||||
} else Regex(if (title.contains("الموسم")) "مسلسل (.*?) الموسم" else "مسلسل (.*?) الحلقة").find(title)!!.groupValues[1] + " (مسلسل)"
|
||||
} else if (title.contains("انمي"))
|
||||
return Regex(if (title.contains("الموسم"))"انمي (.*?) الموسم" else "انمي (.*?) الحلقة").find(title)!!.groupValues[1] + " (انمى)"
|
||||
else if (title.contains("برنامج"))
|
||||
Regex(if (title.contains("الموسم"))"برنامج(.*?)الموسم" else "برنامج(.*?)الحلقة").find(title)!!.groupValues[1] + "(برنامج)"
|
||||
Regex(if (title.contains("الموسم"))"برنامج (.*?) الموسم" else "برنامج (.*?) الحلقة").find(title)!!.groupValues[1] + " (برنامج)"
|
||||
else
|
||||
title
|
||||
}
|
||||
@ -76,28 +90,47 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val episodes = mutableListOf<SEpisode>()
|
||||
fun addEpisode(document: Document, season: String) {
|
||||
document.select(episodeListSelector()).map { episodes.add(episodeFromElement(it, season)) }
|
||||
fun addEpisodeNew(url: String, type: String, title: String = "") {
|
||||
val episode = SEpisode.create()
|
||||
episode.setUrlWithoutDomain(url)
|
||||
if (type == "assembly")
|
||||
episode.name = title.replace("فيلم", "").trim()
|
||||
else if (type == "movie")
|
||||
episode.name = "شاهد"
|
||||
else
|
||||
episode.name = title
|
||||
|
||||
episodes.add(episode)
|
||||
}
|
||||
fun addEpisodes(document: Document, url: String) {
|
||||
fun addEpisodes(response: Response) {
|
||||
val document = response.asJsoup()
|
||||
val url = response.request.url.toString()
|
||||
if (url.contains("assemblies")) {
|
||||
for (movie in document.select(popularAnimeSelector())) {
|
||||
addEpisode(client.newCall(GET(movie.select("a.fullClick").attr("href") + "watch/", headers)).execute().asJsoup(), "assembly")
|
||||
addEpisodeNew(
|
||||
movie.select("a.fullClick").attr("href") + "watch/",
|
||||
"assembly",
|
||||
movie.select("h3").text()
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (document.select("div.seasons--episodes").isNullOrEmpty()) {
|
||||
// Movies
|
||||
addEpisode(document, "0")
|
||||
addEpisodeNew(url, "movie")
|
||||
} else {
|
||||
// Series
|
||||
// look for what is wrong
|
||||
for (season in document.select(seasonsNextPageSelector())) {
|
||||
val seasonNum = season.text().replace("الموسم ", "")
|
||||
if (season.attr("class").contains("active")) {
|
||||
val seasonNum = season.text()
|
||||
if (season.hasClass("active")) {
|
||||
// get episodes from page
|
||||
for (episode in document.select("ul.episodes-list li a")) {
|
||||
addEpisode(client.newCall(GET(episode.attr("href"), headers)).execute().asJsoup(), seasonNum)
|
||||
addEpisodeNew(
|
||||
episode.attr("href"),
|
||||
"series",
|
||||
seasonNum + " " + episode.text()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// send request to get episodes
|
||||
@ -106,13 +139,17 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
val requestBody = FormBody.Builder().add("season", seasonData).build()
|
||||
val getEpisodes = client.newCall(POST("$baseUrl/wp-content/themes/Shahid4u-WP_HOME/Ajaxat/Single/Episodes.php", refererHeaders, requestBody)).execute().asJsoup()
|
||||
for (episode in getEpisodes.select("li a")) {
|
||||
addEpisode(client.newCall(GET(episode.attr("href"), headers)).execute().asJsoup(), seasonNum)
|
||||
addEpisodeNew(
|
||||
episode.attr("href"),
|
||||
"series",
|
||||
seasonNum + " " + episode.text()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addEpisodes(response.asJsoup(), response.request.url.toString())
|
||||
addEpisodes(response)
|
||||
return episodes
|
||||
}
|
||||
|
||||
@ -120,41 +157,47 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override fun episodeFromElement(element: Element): SEpisode = throw Exception("not used")
|
||||
|
||||
private fun episodeFromElement(element: Element, season: String): SEpisode {
|
||||
val episode = SEpisode.create()
|
||||
episode.setUrlWithoutDomain(element.attr("href"))
|
||||
episode.name = element.ownerDocument().select("meta[property=og:title]").attr("content")
|
||||
if (season != "assembly")
|
||||
if (episode.name.contains("فيلم"))
|
||||
episode.name = "watch"
|
||||
else
|
||||
episode.name = "S" + season + ".E" + episode.name.replace("[^0-9]".toRegex(), "").trim()
|
||||
return episode
|
||||
}
|
||||
// Video links
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
val videos = mutableListOf<Video>()
|
||||
val servers = document.select(videoListSelector())
|
||||
fun getUrl(v_id: String, i: String): String {
|
||||
val refererHeaders = Headers.headersOf(
|
||||
"referer", response.request.url.toString(),
|
||||
"x-requested-with", "XMLHttpRequest",
|
||||
"Content-Type", "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
"user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.42"
|
||||
)
|
||||
val requestBody = FormBody.Builder().add("id", v_id).add("i", i).build()
|
||||
val iframe = client.newCall(
|
||||
POST(
|
||||
"https://shahed4u.team/wp-content/themes/Shahid4u-WP_HOME/Ajaxat/Single/Server.php",
|
||||
refererHeaders,
|
||||
requestBody
|
||||
)
|
||||
).execute().asJsoup()
|
||||
return iframe.select("iframe").attr("src")
|
||||
}
|
||||
for (server in servers) {
|
||||
if (server.hasClass("active")) {
|
||||
// special server
|
||||
val videosFromURL = videosFromElement(client.newCall(GET(document.select("input[name=fserver]").`val`(), headers)).execute().asJsoup())
|
||||
videos.addAll(videosFromURL)
|
||||
} /* else if (server.text().contains("ok")) {
|
||||
val refererHeaders = Headers.headersOf(
|
||||
"referer", response.request.url.toString(),
|
||||
"x-requested-with", "XMLHttpRequest",
|
||||
"Content-Length", "13",
|
||||
"Content-Type", "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
"Connection", "keep-alive"
|
||||
)
|
||||
val requestBody = FormBody.Builder().add("i", server.attr("data-i")).add("id", server.attr("data-id")).build()
|
||||
val iframe = client.newCall(POST("$baseUrl/wp-content/themes/Shahid4u-WP_HOME/Ajaxat/Single/Server.php", refererHeaders, requestBody)).execute().asJsoup()
|
||||
val videosFromURL = OkruExtractor(client).videosFromUrl(iframe.select("iframe").attr("href"))
|
||||
} else if (server.text().contains("ok")) {
|
||||
val videosFromURL = OkruExtractor(client).videosFromUrl(getUrl(server.attr("data-id"), server.attr("data-i")))
|
||||
videos.addAll(videosFromURL)
|
||||
}*/
|
||||
} else if (server.text().contains("vidbom", ignoreCase = true) or server.text().contains("Vidshare", ignoreCase = true)) {
|
||||
val videosFromURL = VidBomExtractor(client).videosFromUrl(getUrl(server.attr("data-id"), server.attr("data-i")))
|
||||
videos.addAll(videosFromURL)
|
||||
} else if (server.text().contains("dood", ignoreCase = true)) {
|
||||
val videosFromURL = DoodExtractor(client).videoFromUrl(getUrl(server.attr("data-id"), server.attr("data-i")))
|
||||
if (videosFromURL != null) videos.add(videosFromURL)
|
||||
} else if (server.text().contains("uqload", ignoreCase = true)) {
|
||||
val videosFromURL = UQLoadExtractor(client).videoFromUrl(getUrl(server.attr("data-id"), server.attr("data-i")), "Uqload: 720p")
|
||||
if (videosFromURL != null) videos.add(videosFromURL)
|
||||
}
|
||||
}
|
||||
return videos
|
||||
}
|
||||
@ -205,7 +248,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
override fun searchAnimeFromElement(element: Element): SAnime {
|
||||
val anime = SAnime.create()
|
||||
var link = element.select("a.fullClick").attr("href")
|
||||
anime.title = titleEdit(element.select("h3").text()).trim()
|
||||
anime.title = titleEdit(element.select("h3").text(), details = true).trim()
|
||||
if (link.contains("assemblies"))
|
||||
anime.thumbnail_url = element.select("a.image img").attr("data-src")
|
||||
else
|
||||
|
@ -0,0 +1,43 @@
|
||||
package eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class DoodExtractor(private val client: OkHttpClient) {
|
||||
fun videoFromUrl(url: String): Video? {
|
||||
val response = client.newCall(GET(url)).execute()
|
||||
// Overwrite url to the redirected one
|
||||
val url = response.request.url.toString()
|
||||
val doodTld = url.substringAfter("https://dood.").substringBefore("/")
|
||||
val content = response.body!!.string()
|
||||
if (!content.contains("'/pass_md5/")) return null
|
||||
val md5 = content.substringAfter("'/pass_md5/").substringBefore("',")
|
||||
val token = md5.substringAfterLast("/")
|
||||
val randomString = getRandomString()
|
||||
val expiry = System.currentTimeMillis()
|
||||
val videoUrlStart = client.newCall(
|
||||
GET(
|
||||
"https://dood.$doodTld/pass_md5/$md5",
|
||||
Headers.headersOf("referer", url)
|
||||
)
|
||||
).execute().body!!.string()
|
||||
val videoUrl = "$videoUrlStart$randomString?token=$token&expiry=$expiry"
|
||||
val newQuality = "Doodstream: 1080p"
|
||||
|
||||
return Video(url, newQuality, videoUrl, headers = doodHeaders(doodTld))
|
||||
}
|
||||
|
||||
private fun getRandomString(length: Int = 10): String {
|
||||
val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
|
||||
return (1..length)
|
||||
.map { allowedChars.random() }
|
||||
.joinToString("")
|
||||
}
|
||||
|
||||
private fun doodHeaders(tld: String) = Headers.Builder().apply {
|
||||
add("User-Agent", "Aniyomi")
|
||||
add("Referer", "https://dood.$tld/")
|
||||
}.build()
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.OkHttpClient
|
||||
import java.lang.Exception
|
||||
|
||||
class OkruExtractor(private val client: OkHttpClient) {
|
||||
fun videosFromUrl(url: String): List<Video> {
|
||||
val document = client.newCall(GET(url)).execute().asJsoup()
|
||||
if (document == null) {
|
||||
throw Exception("Not used")
|
||||
} else {
|
||||
val videoList = mutableListOf<Video>()
|
||||
val qualityMap = mapOf(
|
||||
"Okru: mobile" to "Okru: 140p",
|
||||
"Okru: lowest" to "Okru: 240p",
|
||||
"Okru: low" to "Okru: 360p",
|
||||
"Okru: sd" to "Okru: 480p",
|
||||
"Okru: hd" to "Okru: 720p",
|
||||
"Okru: fhd" to "Okru: 1080p"
|
||||
)
|
||||
val videosString = document.select("div[data-options]").attr("data-options")
|
||||
.substringAfter("\\\"videos\\\":[{\\\"name\\\":\\\"")
|
||||
.substringBefore("]")
|
||||
videosString.split("{\\\"name\\\":\\\"").reversed().forEach { it1 ->
|
||||
val videoUrl = it1.substringAfter("url\\\":\\\"")
|
||||
.substringBefore("\\\"")
|
||||
.replace("\\\\u0026", "&")
|
||||
val videoQuality = "Okru: " + it1.substringBefore("\\\"")
|
||||
if (videoUrl.startsWith("https://")) {
|
||||
val video = qualityMap[videoQuality]?.let { Video(videoUrl, it, videoUrl) }
|
||||
if (video != null)
|
||||
videoList.add(video)
|
||||
else
|
||||
videoList.add(Video(videoUrl, videoQuality, videoUrl))
|
||||
}
|
||||
}
|
||||
return videoList
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class UQLoadExtractor(private val client: OkHttpClient) {
|
||||
fun videoFromUrl(url: String, quality: String): Video? {
|
||||
val document = client.newCall(GET(url)).execute().asJsoup()
|
||||
val check = document.selectFirst("script:containsData(sources)").data()
|
||||
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
|
||||
return if (check.contains("sources")) {
|
||||
Video(url, quality, videoUrl)
|
||||
} else {
|
||||
Video(url, "no 1video", "https")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class VidBomExtractor(private val client: OkHttpClient) {
|
||||
fun videosFromUrl(url: String): List<Video> {
|
||||
val doc = client.newCall(GET(url)).execute().asJsoup()
|
||||
val script = doc.selectFirst("script:containsData(sources)")
|
||||
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
||||
val sources = data.split("file:\"").drop(1)
|
||||
val videoList = mutableListOf<Video>()
|
||||
for (source in sources) {
|
||||
val src = source.substringBefore("\"")
|
||||
var quality = "Vidbom: " + source.substringAfter("label:\"").substringBefore("\"") // .substringAfter("format: '")
|
||||
if (quality.length > 15)
|
||||
quality = "Vidshare: 480p"
|
||||
val video = Video(src, quality, src)
|
||||
videoList.add(video)
|
||||
}
|
||||
return videoList
|
||||
/*Log.i("looool", "$js")
|
||||
val json = JSONObject(js)
|
||||
Log.i("looool", "$json")
|
||||
val videoList = mutableListOf<Video>()
|
||||
val jsonArray = json.getJSONArray("sources")
|
||||
for (i in 0 until jsonArray.length()) {
|
||||
val `object` = jsonArray.getJSONObject(i)
|
||||
val videoUrl = `object`.getString("file")
|
||||
Log.i("looool", videoUrl)
|
||||
val quality = "Vidbom:" + `object`.getString("label")
|
||||
videoList.add(Video(videoUrl, quality, videoUrl))
|
||||
}
|
||||
return videoList*/
|
||||
/*if (jas.contains("sources")) {
|
||||
val js = script.data()
|
||||
val json = JSONObject(js)
|
||||
val videoList = mutableListOf<Video>()
|
||||
val jsonArray = json.getJSONArray("sources")
|
||||
for (i in 0 until jsonArray.length()) {
|
||||
val `object` = jsonArray.getJSONObject(i)
|
||||
val videoUrl = `object`.getString("file")
|
||||
Log.i("lol", videoUrl)
|
||||
val quality = "Vidbom:" + `object`.getString("label")
|
||||
videoList.add(Video(videoUrl, quality, videoUrl))
|
||||
}
|
||||
return videoList
|
||||
} else {
|
||||
val videoList = mutableListOf<Video>()
|
||||
videoList.add(Video(url, "no 2video", null))
|
||||
return videoList
|
||||
}*/
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user