diff --git a/src/ar/shahid4u/AndroidManifest.xml b/src/ar/shahid4u/AndroidManifest.xml new file mode 100644 index 000000000..acb4de356 --- /dev/null +++ b/src/ar/shahid4u/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/ar/shahid4u/build.gradle b/src/ar/shahid4u/build.gradle new file mode 100644 index 000000000..7abbe5c17 --- /dev/null +++ b/src/ar/shahid4u/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + extName = 'شاهد فور يو' + pkgNameSuffix = 'ar.shahid4u' + extClass = '.Shahid4U' + extVersionCode = 1 + libVersion = '13' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/ar/shahid4u/res/mipmap-hdpi/ic_launcher.png b/src/ar/shahid4u/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..12cfa74d5 Binary files /dev/null and b/src/ar/shahid4u/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/ar/shahid4u/res/mipmap-mdpi/ic_launcher.png b/src/ar/shahid4u/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..cac0af0ca Binary files /dev/null and b/src/ar/shahid4u/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/ar/shahid4u/res/mipmap-xhdpi/ic_launcher.png b/src/ar/shahid4u/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..e6472e8d4 Binary files /dev/null and b/src/ar/shahid4u/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/ar/shahid4u/res/mipmap-xxhdpi/ic_launcher.png b/src/ar/shahid4u/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..b8e3e4c5b Binary files /dev/null and b/src/ar/shahid4u/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/ar/shahid4u/res/mipmap-xxxhdpi/ic_launcher.png b/src/ar/shahid4u/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..2a26fb744 Binary files /dev/null and b/src/ar/shahid4u/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/ar/shahid4u/res/web_hi_res_512.png b/src/ar/shahid4u/res/web_hi_res_512.png new file mode 100644 index 000000000..5cec2a3e2 Binary files /dev/null and b/src/ar/shahid4u/res/web_hi_res_512.png differ diff --git a/src/ar/shahid4u/src/eu/kanade/tachiyomi/animeextension/ar/shahid4u/Shahid4U.kt b/src/ar/shahid4u/src/eu/kanade/tachiyomi/animeextension/ar/shahid4u/Shahid4U.kt new file mode 100644 index 000000000..01d67dde3 --- /dev/null +++ b/src/ar/shahid4u/src/eu/kanade/tachiyomi/animeextension/ar/shahid4u/Shahid4U.kt @@ -0,0 +1,320 @@ +package eu.kanade.tachiyomi.animeextension.ar.shahid4u + +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.network.POST +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.FormBody +import okhttp3.Headers +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.lang.Exception + +class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() { + + override val name = "شاهد فور يو" + + override val baseUrl = "https://shahed4u.mx/" + + override val lang = "ar" + + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient + + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + + // Popular + + 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 popularAnimeFromElement(element: Element): SAnime { + val anime = SAnime.create() + anime.title = titleEdit(element.select("h3").text()).trim() + anime.thumbnail_url = element.select("a.image img").attr("data-src") + anime.setUrlWithoutDomain(element.select("a.fullClick").attr("href") + "watch/") + 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] + "(انمى)" + else if (title.contains("برنامج")) + Regex(if (title.contains("الموسم"))"برنامج(.*?)الموسم" else "برنامج(.*?)الحلقة").find(title)!!.groupValues[1] + "(برنامج)" + else + title + } + + override fun popularAnimeNextPageSelector(): String = "div.paginate ul.page-numbers li.active + li a" + + // episodes + + private fun seasonsNextPageSelector() = "div.allseasonstab ul li" + + override fun episodeListParse(response: Response): List { + val episodes = mutableListOf() + fun addEpisode(document: Document, season: String) { + document.select(episodeListSelector()).map { episodes.add(episodeFromElement(it, season)) } + } + fun addEpisodes(document: Document, url: String) { + 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") + } + return + } + if (document.select("div.seasons--episodes").isNullOrEmpty()) { + // Movies + addEpisode(document, "0") + } else { + // Series + // look for what is wrong + for (season in document.select(seasonsNextPageSelector())) { + val seasonNum = season.text().replace("الموسم ", "") + if (season.attr("class").contains("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) + } + } else { + // send request to get episodes + val seasonData = season.attr("data-id") + val refererHeaders = Headers.headersOf("referer", response.request.url.toString(), "x-requested-with", "XMLHttpRequest") + 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) + } + } + } + } + } + addEpisodes(response.asJsoup(), response.request.url.toString()) + return episodes + } + + override fun episodeListSelector() = "link[rel=canonical]" + + 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