Fixed AnimePaheProvider

This commit is contained in:
no-commit 2023-03-03 20:07:14 +01:00
parent 17ac5f1736
commit 2b174a3f57
4 changed files with 130 additions and 132 deletions

View File

@ -16,7 +16,8 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 1 // will be 3 if unspecified // Status 0 because it it just a gogo scraper
status = 0 // will be 3 if unspecified
tvTypes = listOf( tvTypes = listOf(
"AnimeMovie", "AnimeMovie",
"Anime", "Anime",

View File

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 1 version = 2
cloudstream { cloudstream {
@ -16,11 +16,10 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 0 // will be 3 if unspecified status = 1 // will be 3 if unspecified
tvTypes = listOf( tvTypes = listOf(
"AnimeMovie", "AnimeMovie",
"Anime", "Anime",
"OVA", "OVA",
) )
} }

View File

@ -1,16 +1,15 @@
package com.lagradost package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.JsUnpacker
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.nicehttp.NiceResponse import com.lagradost.nicehttp.NiceResponse
import org.jsoup.Jsoup import org.jsoup.Jsoup
import kotlin.math.pow import kotlin.math.pow
@ -18,7 +17,7 @@ import kotlin.math.pow
class AnimePaheProvider : MainAPI() { class AnimePaheProvider : MainAPI() {
// credit to https://github.com/justfoolingaround/animdl/tree/master/animdl/core/codebase/providers/animepahe // credit to https://github.com/justfoolingaround/animdl/tree/master/animdl/core/codebase/providers/animepahe
companion object { companion object {
const val MAIN_URL = "https://animepahe.com" const val MAIN_URL = "https://animepahe.ru"
var cookies: Map<String, String> = mapOf() var cookies: Map<String, String> = mapOf()
private fun getType(t: String): TvType { private fun getType(t: String): TvType {
@ -58,15 +57,18 @@ class AnimePaheProvider : MainAPI() {
TvType.OVA TvType.OVA
) )
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { override val mainPage =
listOf(MainPageData("Latest Releases", "$mainUrl/api?m=airing&page=", true))
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
data class Data( data class Data(
@JsonProperty("id") val id: Int, // @JsonProperty("id") val id: Int,
@JsonProperty("anime_id") val animeId: Int, // @JsonProperty("anime_id") val animeId: Int,
@JsonProperty("anime_title") val animeTitle: String, @JsonProperty("anime_title") val animeTitle: String,
@JsonProperty("anime_slug") val animeSlug: String, // @JsonProperty("anime_slug") val animeSlug: String,
@JsonProperty("episode") val episode: Int, @JsonProperty("episode") val episode: Int?,
@JsonProperty("snapshot") val snapshot: String, @JsonProperty("snapshot") val snapshot: String?,
@JsonProperty("created_at") val createdAt: String, @JsonProperty("created_at") val createdAt: String?,
@JsonProperty("anime_session") val animeSession: String, @JsonProperty("anime_session") val animeSession: String,
) )
@ -75,47 +77,34 @@ class AnimePaheProvider : MainAPI() {
@JsonProperty("data") val data: List<Data> @JsonProperty("data") val data: List<Data>
) )
val urls = listOf( val response = app.get(request.data + page).text
Pair("$mainUrl/api?m=airing&page=1", "Latest Releases"), val episodes = parseJson<AnimePaheLatestReleases>(response).data.map {
) newAnimeSearchResponse(
it.animeTitle,
val items = ArrayList<HomePageList>() LoadData(it.animeSession, unixTime, it.animeTitle).toJson(),
for (i in urls) { fix = false
try { ) {
val response = app.get(i.first).text this.posterUrl = it.snapshot
val episodes = parseJson<AnimePaheLatestReleases>(response).data.map { addDubStatus(DubStatus.Subbed, it.episode)
newAnimeSearchResponse(
it.animeTitle,
"https://pahe.win/a/${it.animeId}?slug=${it.animeTitle}",
fix = false
) {
this.posterUrl = it.snapshot
addDubStatus(DubStatus.Subbed, it.episode)
}
}
items.add(HomePageList(i.second, episodes))
} catch (e: Exception) {
e.printStackTrace()
} }
} }
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items) return HomePageResponse(listOf(HomePageList(request.name, episodes, request.horizontalImages)), episodes.isNotEmpty())
} }
data class AnimePaheSearchData( data class AnimePaheSearchData(
@JsonProperty("id") val id: Int, @JsonProperty("id") val id: Int?,
@JsonProperty("slug") val slug: String, @JsonProperty("slug") val slug: String?,
@JsonProperty("title") val title: String, @JsonProperty("title") val title: String,
@JsonProperty("type") val type: String, @JsonProperty("type") val type: String?,
@JsonProperty("episodes") val episodes: Int, @JsonProperty("episodes") val episodes: Int?,
@JsonProperty("status") val status: String, @JsonProperty("status") val status: String?,
@JsonProperty("season") val season: String, @JsonProperty("season") val season: String?,
@JsonProperty("year") val year: Int, @JsonProperty("year") val year: Int?,
@JsonProperty("score") val score: Double, @JsonProperty("score") val score: Double?,
@JsonProperty("poster") val poster: String, @JsonProperty("poster") val poster: String?,
@JsonProperty("session") val session: String, @JsonProperty("session") val session: String,
@JsonProperty("relevance") val relevance: String @JsonProperty("relevance") val relevance: String?
) )
data class AnimePaheSearch( data class AnimePaheSearch(
@ -123,20 +112,6 @@ class AnimePaheProvider : MainAPI() {
@JsonProperty("data") val data: List<AnimePaheSearchData> @JsonProperty("data") val data: List<AnimePaheSearchData>
) )
private suspend fun getAnimeByIdAndTitle(title: String, animeId: Int): String? {
val url = "$mainUrl/api?m=search&l=8&q=$title"
val headers = mapOf("referer" to "$mainUrl/")
val req = app.get(url, headers = headers).text
val data = parseJson<AnimePaheSearch>(req)
for (anime in data.data) {
if (anime.id == animeId) {
return "https://animepahe.com/anime/${anime.session}"
}
}
return null
}
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/api?m=search&l=8&q=$query" val url = "$mainUrl/api?m=search&l=8&q=$query"
@ -148,7 +123,7 @@ class AnimePaheProvider : MainAPI() {
return data.data.map { return data.data.map {
newAnimeSearchResponse( newAnimeSearchResponse(
it.title, it.title,
"https://pahe.win/a/${it.id}?slug=${it.title}", LoadData(it.session, unixTime, it.title).toJson(),
fix = false fix = false
) { ) {
this.posterUrl = it.poster this.posterUrl = it.poster
@ -180,12 +155,30 @@ class AnimePaheProvider : MainAPI() {
@JsonProperty("data") val data: List<AnimeData> @JsonProperty("data") val data: List<AnimeData>
) )
private suspend fun generateListOfEpisodes(link: String): ArrayList<Episode> { data class LinkLoadData(
try { @JsonProperty("mainUrl") val mainUrl: String,
val attrs = link.split('/') @JsonProperty("is_play_page") val is_play_page: Boolean,
val id = attrs[attrs.size - 1].split("?")[0] @JsonProperty("episode_num") val episode_num: Int,
@JsonProperty("page") val page: Int,
@JsonProperty("session") val session: String,
@JsonProperty("episode_session") val episode_session: String,
) {
suspend fun getUrl(): String? {
return if (is_play_page) {
"$mainUrl/play/${session}/${episode_session}"
} else {
val url = "$mainUrl/api?m=release&id=${session}&sort=episode_asc&page=${page + 1}"
val jsonResponse = app.get(url).parsedSafe<AnimePaheAnimeData>() ?: return null
val episode = jsonResponse.data.firstOrNull { it.episode == episode_num }?.session
?: return null
"$mainUrl/play/${session}/${episode}"
}
}
}
val uri = "$mainUrl/api?m=release&id=$id&sort=episode_asc&page=1" private suspend fun generateListOfEpisodes(session: String): ArrayList<Episode> {
try {
val uri = "$mainUrl/api?m=release&id=$session&sort=episode_asc&page=1"
val headers = mapOf("referer" to "$mainUrl/") val headers = mapOf("referer" to "$mainUrl/")
val req = app.get(uri, headers = headers).text val req = app.get(uri, headers = headers).text
@ -206,7 +199,16 @@ class AnimePaheProvider : MainAPI() {
if (lastPage == 1 && perPage > total) { if (lastPage == 1 && perPage > total) {
data.data.forEach { data.data.forEach {
episodes.add( episodes.add(
newEpisode("$mainUrl/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!") { newEpisode(
LinkLoadData(
mainUrl,
true,
0,
0,
session,
it.session
).toJson()
) {
addDate(it.createdAt) addDate(it.createdAt)
this.name = getEpisodeTitle(it) this.name = getEpisodeTitle(it)
this.posterUrl = it.snapshot this.posterUrl = it.snapshot
@ -219,7 +221,14 @@ class AnimePaheProvider : MainAPI() {
if (ep <= total) { if (ep <= total) {
episodes.add( episodes.add(
Episode( Episode(
"$mainUrl/api?m=release&id=${id}&sort=episode_asc&page=${page + 1}&ep=${ep}!!FALSE!!" LinkLoadData(
mainUrl,
false,
ep,
page + 1,
session,
""
).toJson()
) )
) )
++ep ++ep
@ -233,16 +242,29 @@ class AnimePaheProvider : MainAPI() {
} }
} }
/**
* Required to make bookmarks work with a session system
**/
data class LoadData(val session: String, val sessionDate: Long, val name: String)
override suspend fun load(url: String): LoadResponse? { override suspend fun load(url: String): LoadResponse? {
return suspendSafeApiCall { return suspendSafeApiCall {
val regex = Regex("""a/(\d+)\?slug=(.+)""") val session = parseJson<LoadData>(url).let { data ->
val (animeId, animeTitle) = regex.find(url)!!.destructured // Outdated
val link = getAnimeByIdAndTitle(animeTitle, animeId.toInt())!! if (data.sessionDate + 60 * 10 < unixTime) {
parseJson<LoadData>(
search(data.name).firstOrNull()?.url ?: return@let null
).session
} else {
data.session
}
} ?: return@suspendSafeApiCall null
val html = app.get(link).text val html = app.get("https://animepahe.ru/anime/$session").text
val doc = Jsoup.parse(html) val doc = Jsoup.parse(html)
val japTitle = doc.selectFirst("h2.japanese")?.text() val japTitle = doc.selectFirst("h2.japanese")?.text()
val animeTitle = doc.selectFirst("span.sr-only.unselectable")?.text()
val poster = doc.selectFirst(".anime-poster a")?.attr("href") val poster = doc.selectFirst(".anime-poster a")?.attr("href")
val tvType = doc.selectFirst("""a[href*="/anime/type/"]""")?.text() val tvType = doc.selectFirst("""a[href*="/anime/type/"]""")?.text()
@ -253,17 +275,18 @@ class AnimePaheProvider : MainAPI() {
null null
} }
val episodes = generateListOfEpisodes(url) val episodes = generateListOfEpisodes(session)
val year = Regex("""<strong>Aired:</strong>[^,]*, (\d+)""") val year = Regex("""<strong>Aired:</strong>[^,]*, (\d+)""")
.find(html)!!.destructured.component1() .find(html)?.destructured?.component1()
.toIntOrNull() ?.toIntOrNull()
val status = val status =
when (Regex("""<strong>Status:</strong>[^a]*a href=["']/anime/(.*?)["']""") if (doc.selectFirst("a[href='/anime/airing']") != null)
.find(html)!!.destructured.component1()) { ShowStatus.Ongoing
"airing" -> ShowStatus.Ongoing else if (doc.selectFirst("a[href='/anime/completed']") != null)
"completed" -> ShowStatus.Completed ShowStatus.Completed
else -> null else null
}
val synopsis = doc.selectFirst(".anime-synopsis")?.text() val synopsis = doc.selectFirst(".anime-synopsis")?.text()
var anilistId: Int? = null var anilistId: Int? = null
@ -279,7 +302,7 @@ class AnimePaheProvider : MainAPI() {
} }
} }
newAnimeLoadResponse(animeTitle, url, getType(tvType.toString())) { newAnimeLoadResponse(animeTitle ?: japTitle ?: "", url, getType(tvType.toString())) {
engName = animeTitle engName = animeTitle
japName = japTitle japName = japTitle
@ -459,14 +482,8 @@ class AnimePaheProvider : MainAPI() {
headers = mapOf("referer" to mainUrl), headers = mapOf("referer" to mainUrl),
cookies = cookies cookies = cookies
).text ).text
Regex("eval((.|\\n)*?)</script>").find(response)?.groupValues?.get(1)?.let { jsEval -> val unpacked = getAndUnpack(response)
JsUnpacker("eval$jsEval").unpack()?.let { unPacked -> return Regex("source=\'(.*?)\'").find(unpacked)?.groupValues?.get(1)
Regex("source=\'(.*?)\'").find(unPacked)?.groupValues?.get(1)?.let { link ->
return link
}
}
}
return null
} }
private suspend fun getStreamUrlFromKwikAdfly(adflyUri: String): String { private suspend fun getStreamUrlFromKwikAdfly(adflyUri: String): String {
@ -505,48 +522,28 @@ class AnimePaheProvider : MainAPI() {
} }
private suspend fun extractVideoLinks( private suspend fun extractVideoLinks(
episodeLink: String, data: String,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
var link = episodeLink val parsed = parseJson<LinkLoadData>(data)
val headers = mapOf("referer" to "$mainUrl/") val headers = mapOf("referer" to "$mainUrl/")
val episodeUrl = parsed.getUrl() ?: return
if (link.contains("!!TRUE!!")) { val text = app.get(episodeUrl, headers = headers).text
link = link.replace("!!TRUE!!", "") val urlRegex = Regex("""let url = "(.*?)";""")
} else { val embed = urlRegex.find(text)?.groupValues?.getOrNull(1) ?: return
val regex = """&ep=(\d+)!!FALSE!!""".toRegex()
val episodeNum = regex.find(link)?.destructured?.component1()?.toIntOrNull()
link = link.replace(regex, "")
val req = app.get(link, headers = headers).text getStreamUrlFromKwik(embed)?.let { link ->
val jsonResponse = parseJson<AnimePaheAnimeData>(req) callback(
val ep = ((jsonResponse.data.map { ExtractorLink(
if (it.episode == episodeNum) { this.name,
it "Kwik",
} else { link,
null "https://kwik.cx/",
} Qualities.Unknown.value,
}).filterNotNull())[0] link.contains(".m3u8")
link = "$mainUrl/api?m=links&id=${ep.animeId}&session=${ep.session}&p=kwik" )
} )
val req = app.get(link, headers = headers).text
val data = mapper.readValue<AnimePaheEpisodeLoadLinks>(req)
data.data.forEach {
it.entries.toList().apmap { quality ->
getStreamUrlFromKwik(quality.value.kwik)?.let { link ->
callback(
ExtractorLink(
"KWIK",
"KWIK - ${quality.key} [${quality.value.audio ?: "jpn"}]",
link,
"https://kwik.cx/",
getQualityFromName(quality.key),
link.contains(".m3u8")
)
)
}
}
} }
} }

View File

@ -16,7 +16,8 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 1 // will be 3 if unspecified // Removed as it is an AsianLoad scraper
status = 0 // will be 3 if unspecified
tvTypes = listOf( tvTypes = listOf(
"AsianDrama", "AsianDrama",
"OVA", "OVA",