anitubeinua: Fix fucking csst -> monstro

Check btw https://monstro.site/ and https://csst.online)
@mail.ru moment on ukrainian site, lol
This commit is contained in:
CakesTwix 2024-02-05 20:44:12 +02:00
parent eeab703558
commit 596ab11cd0
Signed by: CakesTwix
GPG key ID: 7B11051D5CE19825
2 changed files with 119 additions and 107 deletions

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 5 version = 6
cloudstream { cloudstream {

View file

@ -1,20 +1,20 @@
package com.lagradost package com.lagradost
import com.lagradost.models.PlayerJson
import com.lagradost.extractors.AshdiExtractor
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.extractors.Mp4Upload
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.extractors.Mp4Upload
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.extractors.AshdiExtractor
import com.lagradost.extractors.csstExtractor import com.lagradost.extractors.csstExtractor
import com.lagradost.models.Ajax import com.lagradost.models.Ajax
import com.lagradost.models.Link import com.lagradost.models.Link
import com.lagradost.models.PlayerJson
import com.lagradost.models.videoConstructor import com.lagradost.models.videoConstructor
import java.util.*
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.util.*
class AnitubeinuaProvider : MainAPI() { class AnitubeinuaProvider : MainAPI() {
@ -24,37 +24,35 @@ class AnitubeinuaProvider : MainAPI() {
override val hasMainPage = true override val hasMainPage = true
override var lang = "uk" override var lang = "uk"
override val hasDownloadSupport = true override val hasDownloadSupport = true
override val supportedTypes = setOf( override val supportedTypes =
setOf(
TvType.AnimeMovie, TvType.AnimeMovie,
TvType.Anime, TvType.Anime,
) )
// Sections // Sections
override val mainPage = mainPageOf( override val mainPage =
mainPageOf(
"$mainUrl/anime/page/" to "Нові", "$mainUrl/anime/page/" to "Нові",
"$mainUrl/f/sort=rating/order=desc/page/" to "Популярні", "$mainUrl/f/sort=rating/order=desc/page/" to "Популярні",
) )
override suspend fun getMainPage( override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
page: Int,
request: MainPageRequest
): HomePageResponse {
val document = app.get(request.data + page).document val document = app.get(request.data + page).document
val home = document.select(".story").map { val home = document.select(".story").map { it.toSearchResponse() }
it.toSearchResponse()
}
return newHomePageResponse(request.name, home) return newHomePageResponse(request.name, home)
} }
private fun Element.toSearchResponse(): AnimeSearchResponse { private fun Element.toSearchResponse(): AnimeSearchResponse {
val title = this.selectFirst(".story_c h2 a, div.text_content a")?.text()?.trim().toString() val title = this.selectFirst(".story_c h2 a, div.text_content a")?.text()?.trim().toString()
val href = this.selectFirst(".story_c h2 a, div.text_content a")?.attr("href").toString() val href = this.selectFirst(".story_c h2 a, div.text_content a")?.attr("href").toString()
val posterUrl = mainUrl + this.selectFirst(".story_c_l span.story_post img, a img")?.attr("src") val posterUrl =
mainUrl + this.selectFirst(".story_c_l span.story_post img, a img")?.attr("src")
var isSub = this.select(".box .sub").isNotEmpty() var isSub = this.select(".box .sub").isNotEmpty()
var isDub = this.select(".box .ukr").isNotEmpty() var isDub = this.select(".box .ukr").isNotEmpty()
if (!isSub && !isDub){ if (!isSub && !isDub) {
isSub = true isSub = true
isDub = true isDub = true
} }
@ -62,22 +60,22 @@ class AnitubeinuaProvider : MainAPI() {
this.posterUrl = posterUrl this.posterUrl = posterUrl
addDubStatus(isDub, isSub) addDubStatus(isDub, isSub)
} }
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val document = app.post( val document =
app.post(
url = mainUrl, url = mainUrl,
data = mapOf( data =
mapOf(
"do" to "search", "do" to "search",
"subaction" to "search", "subaction" to "search",
"story" to query.replace(" ", "+") "story" to query.replace(" ", "+")
) )
).document )
.document
return document.select("article.story").map { return document.select("article.story").map { it.toSearchResponse() }
it.toSearchResponse()
}
} }
// Detailed information // Detailed information
@ -88,7 +86,8 @@ class AnitubeinuaProvider : MainAPI() {
// Parse info // Parse info
val title = document.selectFirst(".story_c h2")?.text()?.trim().toString() val title = document.selectFirst(".story_c h2")?.text()?.trim().toString()
val poster = mainUrl + document.selectFirst(".story_c_left span.story_post img")?.attr("src") val poster =
mainUrl + document.selectFirst(".story_c_left span.story_post img")?.attr("src")
val tags = someInfo.select("a[href*=/anime/]").map { it.text() } val tags = someInfo.select("a[href*=/anime/]").map { it.text() }
val year = someInfo.select("a[href*=/xfsearch/year/]").text().toIntOrNull() val year = someInfo.select("a[href*=/xfsearch/year/]").text().toIntOrNull()
@ -96,11 +95,10 @@ class AnitubeinuaProvider : MainAPI() {
val description = document.selectFirst("div.my-text")?.text()?.trim() val description = document.selectFirst("div.my-text")?.text()?.trim()
// val author = someInfo.select("strong:contains(Студія:)").next().html() // val author = someInfo.select("strong:contains(Студія:)").next().html()
val trailer = document.selectFirst(".rcol a.rollover")?.attr("href").toString() val trailer = document.selectFirst(".rcol a.rollover")?.attr("href").toString()
val rating = document.selectFirst(".lexington-box > div:last-child span")?.text().toRatingInt() val rating =
document.selectFirst(".lexington-box > div:last-child span")?.text().toRatingInt()
val recommendations = document.select(".horizontal ul li").map { val recommendations = document.select(".horizontal ul li").map { it.toSearchResponse() }
it.toSearchResponse()
}
// Return to app // Return to app
// Players, Episodes, Number of episodes // Players, Episodes, Number of episodes
@ -108,23 +106,30 @@ class AnitubeinuaProvider : MainAPI() {
val dubEpisodes = mutableListOf<Episode>() val dubEpisodes = mutableListOf<Episode>()
val id = url.split("/").last().split("-").first() val id = url.split("/").last().split("-").first()
val ajax = fromPlaylistAjax("$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}") val ajax =
fromPlaylistAjax(
"$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}"
)
if (!ajax.isNullOrEmpty()) { // Ajax list if (!ajax.isNullOrEmpty()) { // Ajax list
ajax.groupBy{ it.name }.forEach { episodes -> // Group by name ajax
.groupBy { it.name }
.forEach { episodes -> // Group by name
episodes.value.forEach lit@{ episodes.value.forEach lit@{
// UFDub player, drop // UFDub player, drop
if(it.name == "ПЛЕЙЛИСТ") return@lit if (it.name == "ПЛЕЙЛИСТ") return@lit
if(it.urls.isDub){ if (it.urls.isDub) {
dubEpisodes.add(Episode( dubEpisodes.add(
Episode(
"${it.name}, $id, ${it.urls.isDub}", "${it.name}, $id, ${it.urls.isDub}",
it.name, it.name,
episode = it.numberEpisode episode = it.numberEpisode
) )
) )
} else { } else {
subEpisodes.add(Episode( subEpisodes.add(
Episode(
"${it.name}, $id, ${it.urls.isDub}", "${it.name}, $id, ${it.urls.isDub}",
it.name, it.name,
episode = it.numberEpisode episode = it.numberEpisode
@ -134,16 +139,16 @@ class AnitubeinuaProvider : MainAPI() {
} }
} }
} else { } else {
document.select("script").map{ script -> document.select("script").map { script ->
if (script.data().contains("RalodePlayer.init(")) { if (script.data().contains("RalodePlayer.init(")) {
val episodesList = fromVideoContructor(script) val episodesList = fromVideoContructor(script)
episodesList.forEach { episode -> episodesList.forEach { episode ->
// UFDub player, drop // UFDub player, drop
var varEpisodeNumber = episode.episodeNumber var varEpisodeNumber = episode.episodeNumber
if(episode.episodeName == "ПЛЕЙЛИСТ") return@forEach if (episode.episodeName == "ПЛЕЙЛИСТ") return@forEach
if(varEpisodeNumber == null){ if (varEpisodeNumber == null) {
varEpisodeNumber = episodesList.last().episodeNumber?.plus(1); varEpisodeNumber = episodesList.last().episodeNumber?.plus(1)
} }
dubEpisodes.add( dubEpisodes.add(
Episode( Episode(
@ -152,10 +157,8 @@ class AnitubeinuaProvider : MainAPI() {
episode = varEpisodeNumber, episode = varEpisodeNumber,
) )
) )
} }
} }
} }
} }
@ -172,7 +175,6 @@ class AnitubeinuaProvider : MainAPI() {
} }
} }
// It works when I click to view the series // It works when I click to view the series
override suspend fun loadLinks( override suspend fun loadLinks(
data: String, // (Ajax) Name, id title, isDub | (Two) Episode name, url title data: String, // (Ajax) Name, id title, isDub | (Two) Episode name, url title
@ -181,30 +183,37 @@ class AnitubeinuaProvider : MainAPI() {
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val dataList = data.split(", ") val dataList = data.split(", ")
if(dataList[1].toIntOrNull() != null){ // Its ajax list // Log.d("CakesTwix-Debug", data)
if (dataList[1].toIntOrNull() != null) { // Its ajax list
val ajax = fromPlaylistAjax("$mainUrl/engine/ajax/playlists.php?news_id=${dataList[1]}&xfield=playlist&time=${Date().time}") val ajax =
fromPlaylistAjax(
"$mainUrl/engine/ajax/playlists.php?news_id=${dataList[1]}&xfield=playlist&time=${Date().time}"
)
// Filter by name and isDub // Filter by name and isDub
ajax?.filter { it.name == dataList[0] } ajax
?.filter { it.name == dataList[0] }
?.filter { it.urls.isDub == dataList[2].toBoolean() } ?.filter { it.urls.isDub == dataList[2].toBoolean() }
?.forEach { ?.forEach {
// Get m3u8 url // Get m3u8 url
with(it){ with(it) {
when{ when {
it.urls.url.contains("https://tortuga.wtf/vod/") -> { it.urls.url.contains("https://tortuga.wtf/vod/") -> {
M3u8Helper.generateM3u8( M3u8Helper.generateM3u8(
source = "${it.urls.playerName} (${it.urls.name})", source = "${it.urls.playerName} (${it.urls.name})",
streamUrl = AshdiExtractor().ParseM3U8(this.urls.url), streamUrl = AshdiExtractor().ParseM3U8(this.urls.url),
referer = "https://tortuga.wtf/" referer = "https://tortuga.wtf/"
).forEach(callback) )
.forEach(callback)
} }
it.urls.url.contains("https://ashdi.vip/vod") -> { it.urls.url.contains("https://ashdi.vip/vod") -> {
M3u8Helper.generateM3u8( M3u8Helper.generateM3u8(
source = "${it.urls.playerName} (${it.urls.name})", source = "${it.urls.playerName} (${it.urls.name})",
streamUrl = AshdiExtractor().ParseM3U8(this.urls.url), streamUrl = AshdiExtractor().ParseM3U8(this.urls.url),
referer = "https://qeruya.cyou" referer = "https://qeruya.cyou"
).forEach(callback) )
.forEach(callback)
} }
it.urls.url.contains("https://www.udrop.com") -> { it.urls.url.contains("https://www.udrop.com") -> {
callback.invoke( callback.invoke(
@ -218,8 +227,9 @@ class AnitubeinuaProvider : MainAPI() {
) )
) )
} }
it.urls.url.contains("https://csst.online/embed/") -> { it.urls.url.contains("https://csst.online/embed/") || it.urls.url.contains("https://monstro.site/embed/") -> {
csstExtractor().ParseUrl(it.urls.url).split(",").forEach{ csstUrl -> csstExtractor().ParseUrl(it.urls.url).split(",").forEach { csstUrl
->
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
this.urls.url, this.urls.url,
@ -233,7 +243,7 @@ class AnitubeinuaProvider : MainAPI() {
} }
} }
it.urls.url.contains("https://www.mp4upload.com/") -> { it.urls.url.contains("https://www.mp4upload.com/") -> {
Mp4Upload().getUrl(it.urls.url)?.forEach{ extlink -> Mp4Upload().getUrl(it.urls.url)?.forEach { extlink ->
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
extlink.source, extlink.source,
@ -243,7 +253,6 @@ class AnitubeinuaProvider : MainAPI() {
extlink.quality, extlink.quality,
extlink.type, extlink.type,
extlink.headers, extlink.headers,
) )
) )
} }
@ -258,16 +267,16 @@ class AnitubeinuaProvider : MainAPI() {
if (script.data().contains("RalodePlayer.init(")) { if (script.data().contains("RalodePlayer.init(")) {
var latestNumber: Int? = 0 var latestNumber: Int? = 0
fromVideoContructor(script).forEach { dub -> fromVideoContructor(script).forEach { dub ->
if(dub.episodeName == "ПЛЕЙЛИСТ") return@forEach if (dub.episodeName == "ПЛЕЙЛИСТ") return@forEach
// Parse by number episode // Parse by number episode
// If null, just add +1 // If null, just add +1
if(dub.episodeNumber == null){ if (dub.episodeNumber == null) {
dub.episodeNumber = latestNumber?.plus(1); dub.episodeNumber = latestNumber?.plus(1)
} }
latestNumber = dub.episodeNumber latestNumber = dub.episodeNumber
if(latestNumber != dataList[0].toIntOrNull()) return@forEach if (latestNumber != dataList[0].toIntOrNull()) return@forEach
with(dub.episodeUrl) { with(dub.episodeUrl) {
when { when {
@ -276,14 +285,16 @@ class AnitubeinuaProvider : MainAPI() {
source = dub.playerName, source = dub.playerName,
streamUrl = AshdiExtractor().ParseM3U8(this), streamUrl = AshdiExtractor().ParseM3U8(this),
referer = "https://tortuga.wtf/" referer = "https://tortuga.wtf/"
).forEach(callback) )
.forEach(callback)
} }
contains("https://ashdi.vip/vod") -> { contains("https://ashdi.vip/vod") -> {
M3u8Helper.generateM3u8( M3u8Helper.generateM3u8(
source = dub.playerName, source = dub.playerName,
streamUrl = AshdiExtractor().ParseM3U8(this), streamUrl = AshdiExtractor().ParseM3U8(this),
referer = "https://qeruya.cyou" referer = "https://qeruya.cyou"
).forEach(callback) )
.forEach(callback)
} }
contains("https://www.udrop.com") -> { contains("https://www.udrop.com") -> {
callback.invoke( callback.invoke(
@ -297,12 +308,12 @@ class AnitubeinuaProvider : MainAPI() {
) )
) )
} }
contains("https://csst.online/embed/") -> { contains("https://monstro.site/embed/") || contains("https://csst.online/embed/") -> {
csstExtractor().ParseUrl(this).split(",").forEach { csstExtractor().ParseUrl(this).split(",").forEach {
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
dub.playerName, dub.playerName,
name = "${dub.playerName} ${it.substringBefore("]").drop(1)}", name = "${dub.playerName.replace("\"", "")} ${it.substringBefore("]").drop(1)}",
it.substringAfter("]"), it.substringAfter("]"),
"", "",
0, 0,
@ -312,7 +323,7 @@ class AnitubeinuaProvider : MainAPI() {
} }
} }
contains("https://www.mp4upload.com/") -> { contains("https://www.mp4upload.com/") -> {
Mp4Upload().getUrl(this)?.forEach{ extlink -> Mp4Upload().getUrl(this)?.forEach { extlink ->
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
extlink.source, extlink.source,
@ -322,7 +333,6 @@ class AnitubeinuaProvider : MainAPI() {
extlink.quality, extlink.quality,
extlink.type, extlink.type,
extlink.headers, extlink.headers,
) )
) )
} }
@ -337,7 +347,7 @@ class AnitubeinuaProvider : MainAPI() {
return true return true
} }
private fun decode(input: String): String{ private fun decode(input: String): String {
// Decoded string, thanks to Secozzi // Decoded string, thanks to Secozzi
val hexRegex = Regex("\\\\u([0-9a-fA-F]{4})") val hexRegex = Regex("\\\\u([0-9a-fA-F]{4})")
return hexRegex.replace(input) { matchResult -> return hexRegex.replace(input) { matchResult ->
@ -345,11 +355,7 @@ class AnitubeinuaProvider : MainAPI() {
} }
} }
data class Responses( data class Responses(val success: Boolean?, val response: String?, val message: String?)
val success: Boolean?,
val response: String?,
val message: String?
)
// Thanks to Andro999b // Thanks to Andro999b
// https://github.com/Andro999b/movies-telegram-bot/blob/a296c7d4122a25fa70b612e75d741dd55c154640/functions/src/providers/AnitubeUAProvider.ts#L86-L137 // https://github.com/Andro999b/movies-telegram-bot/blob/a296c7d4122a25fa70b612e75d741dd55c154640/functions/src/providers/AnitubeUAProvider.ts#L86-L137
@ -357,7 +363,7 @@ class AnitubeinuaProvider : MainAPI() {
val responseGet = app.get(url).parsedSafe<Responses>() val responseGet = app.get(url).parsedSafe<Responses>()
// Not Ajax, return null // Not Ajax, return null
if(responseGet?.success == false){ if (responseGet?.success == false) {
// Log.d("load-debug", "Not Ajax") // Log.d("load-debug", "Not Ajax")
return null return null
} }
@ -365,15 +371,15 @@ class AnitubeinuaProvider : MainAPI() {
val returnEpisodes = mutableListOf<Ajax>() val returnEpisodes = mutableListOf<Ajax>()
val playlist = Jsoup.parse(responseGet?.response!!) val playlist = Jsoup.parse(responseGet?.response!!)
val audios = mutableListOf<Pair<String,String>>() // (INARI, 0_0, ...) val audios = mutableListOf<Pair<String, String>>() // (INARI, 0_0, ...)
val listDubStatus = mutableListOf<Pair<String,String>>() // (СУБТИТРИ, 0_0_0, ...) val listDubStatus = mutableListOf<Pair<String, String>>() // (СУБТИТРИ, 0_0_0, ...)
val listPlayers = mutableListOf<Pair<String,String>>() // (ПЛЕЄР МОНСТР, 0_0_0_0, ...) val listPlayers = mutableListOf<Pair<String, String>>() // (ПЛЕЄР МОНСТР, 0_0_0_0, ...)
playlist.select(".playlists-lists .playlists-items:first-child li").forEach { playlist.select(".playlists-lists .playlists-items:first-child li").forEach {
audios.add(Pair(it.text(), it.attr("data-id"))) audios.add(Pair(it.text(), it.attr("data-id")))
} }
// Set listPlayers and listDubStatus // Set listPlayers and listDubStatus
// If has subs - So players in 3 index // If has subs - So players in 3 index
if(playlist.select(".playlists-lists .playlists-items").count() == 3){ if (playlist.select(".playlists-lists .playlists-items").count() == 3) {
// Players // Players
playlist.select(".playlists-lists .playlists-items:nth-child(3) li").forEach { playlist.select(".playlists-lists .playlists-items:nth-child(3) li").forEach {
listPlayers.add(Pair(it.text(), it.attr("data-id"))) listPlayers.add(Pair(it.text(), it.attr("data-id")))
@ -401,15 +407,15 @@ class AnitubeinuaProvider : MainAPI() {
// Set this element Dub name // Set this element Dub name
audios.forEach { audios.forEach {
if(audioId.startsWith(it.second)){ if (audioId.startsWith(it.second)) {
audio = it.first audio = it.first
} }
} }
if(audioId.count { it == '_' } == 3){ if (audioId.count { it == '_' } == 3) {
listDubStatus.forEach { listDubStatus.forEach {
if(audioId.startsWith(it.second)){ if (audioId.startsWith(it.second)) {
if(it.first == "СУБТИТРИ"){ if (it.first == "СУБТИТРИ") {
isDub = false isDub = false
} }
} }
@ -417,7 +423,7 @@ class AnitubeinuaProvider : MainAPI() {
} }
listPlayers.forEach { listPlayers.forEach {
if(audioId.startsWith(it.second)){ if (audioId.startsWith(it.second)) {
playerName = it.first playerName = it.first
} }
} }
@ -441,8 +447,14 @@ class AnitubeinuaProvider : MainAPI() {
private fun fromVideoContructor(script: Element): List<videoConstructor> { private fun fromVideoContructor(script: Element): List<videoConstructor> {
val playerScriptRawJson = script.data().substringAfterLast(".init(").substringBefore(");") val playerScriptRawJson = script.data().substringAfterLast(".init(").substringBefore(");")
val playerEpisodesRawJson = playerScriptRawJson.substringAfter("],").substringBeforeLast(",") val playerEpisodesRawJson =
val playerNamesArray = (playerScriptRawJson.substringBefore("],") + "]").dropLast(1).drop(1).replace("\",\"", ",,,").split(",,,") playerScriptRawJson.substringAfter("],").substringBeforeLast(",")
val playerNamesArray =
(playerScriptRawJson.substringBefore("],") + "]")
.dropLast(1)
.drop(1)
.replace("\",\"", ",,,")
.split(",,,")
// val numberOfEpisodesInt = playerScriptRawJson.substringAfterLast(",").toIntOrNull() // val numberOfEpisodesInt = playerScriptRawJson.substringAfterLast(",").toIntOrNull()
val jsonEpisodes = tryParseJson<List<List<PlayerJson>>>(playerEpisodesRawJson)!! val jsonEpisodes = tryParseJson<List<List<PlayerJson>>>(playerEpisodesRawJson)!!
@ -450,7 +462,7 @@ class AnitubeinuaProvider : MainAPI() {
jsonEpisodes.forEachIndexed { index, episode -> jsonEpisodes.forEachIndexed { index, episode ->
val playerName = decode(playerNamesArray[index]) val playerName = decode(playerNamesArray[index])
episode.forEach{ episode.forEach {
episodes.add( episodes.add(
videoConstructor( videoConstructor(
playerName, playerName,
@ -464,13 +476,13 @@ class AnitubeinuaProvider : MainAPI() {
} }
return episodes.toList() return episodes.toList()
} }
private fun extractIntFromString(string: String): Int? { private fun extractIntFromString(string: String): Int? {
val value = Regex("(\\d+)").findAll(string).lastOrNull() ?: return null val value = Regex("(\\d+)").findAll(string).lastOrNull() ?: return null
if(value.value[0].toString() == "0"){ if (value.value[0].toString() == "0") {
return value.value.drop(1).toIntOrNull() return value.value.drop(1).toIntOrNull()
} }
return value.value.toIntOrNull() return value.value.toIntOrNull()
} }
} }