Merge pull request #7 from CakesTwix/4-add-anitubeinua

4 add anitubeinua
This commit is contained in:
CakesTwix 2023-02-20 14:02:02 +02:00 committed by GitHub
commit 985ce4a1bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 314 additions and 0 deletions

View file

@ -0,0 +1,25 @@
// use an integer for version numbers
version = 1
cloudstream {
language = "uk"
// All of these properties are optional, you can safely remove them
description = "На нашому сайті ви можете подивитися аніме онлайн українською безкоштовно. Великий список аніме онлайн тільки у нас."
authors = listOf("CakesTwix")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 3 // will be 3 if unspecified
tvTypes = listOf(
"Anime",
)
iconUrl = "https://www.google.com/s2/favicons?domain=anitube.in.ua&sz=%size%"
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View file

@ -0,0 +1,253 @@
package com.lagradost
import com.lagradost.models.PlayerJson
import com.lagradost.extractors.AshdiExtractor
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.util.*
class AnitubeinuaProvider : MainAPI() {
// Basic Info
override var mainUrl = "https://anitube.in.ua"
override var name = "Anitubeinua Beta"
override val hasMainPage = true
override var lang = "uk"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.Anime,
)
// Sections
override val mainPage = mainPageOf(
"$mainUrl/anime/page/" to "Нові",
"$mainUrl/f/sort=rating/order=desc/page/" to "Популярне",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val document = app.get(request.data + page).document
val home = document.select(".story").map {
it.toSearchResponse()
}
return newHomePageResponse(request.name, home)
}
private fun Element.toSearchResponse(): SearchResponse {
val title = this.selectFirst(".story_c h2 a")?.text()?.trim().toString()
val href = this.selectFirst(".story_c h2 a")?.attr("href").toString()
val posterUrl = mainUrl + this.selectFirst(".story_c_l span.story_post img")?.attr("src")
return newMovieSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
}
}
override suspend fun search(query: String): List<SearchResponse> {
val document = app.post(
url = mainUrl,
data = mapOf(
"do" to "search",
"subaction" to "search",
"story" to query.replace(" ", "+")
)
).document
return document.select("article.story").map {
it.toSearchResponse()
}
}
// Detailed information
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val someInfo = document.select("div.story_c_r")[1]
// Parse info
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 tags = someInfo.select("noindex a").html().split("\n").map { it }
val year = someInfo.select("strong:contains(Рік випуску аніме:)").next().html().toIntOrNull()
val tvType = TvType.Anime
val description = document.selectFirst("div.my-text")?.text()?.trim()
// val author = someInfo.select("strong:contains(Студія:)").next().html()
val rating = document.selectFirst(".lexington-box > div:last-child span")?.text().toRatingInt()
val recommendations = document.select(".horizontal ul").map {
it.toSearchResponse()
}
// Return to app
// Players, Episodes, Number of episodes
var episodes: List<Episode> = emptyList()
val id = url.split("/").last().split("-").first()
val responseGet = app.get("$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}").parsedSafe<Responses>()!!
if (responseGet.success == true) { // First type players
episodes =
app.get("$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}")
.parsedSafe<Responses>()?.response.let {
Jsoup.parse(it.toString()).select("div.playlists-videos li")
.mapIndexedNotNull() { index, eps ->
val href =
"$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}"
val name = eps.text().trim() // Серія 1
if (href.isNotEmpty()) {
Episode(
"$href, $index", // link, Серія 1
name,
)
} else {
null
}
}
}
} else {
document.select("script").map{ script ->
if (script.data().contains("RalodePlayer.init(")) {
val playerScriptRawJson = script.data().substringAfterLast(".init(").substringBefore(");")
val playerEpisodesRawJson = playerScriptRawJson.substringAfter("],").substringBeforeLast(",")
// val playerNamesArray = (playerScriptRawJson.substringBefore("],") + "]").dropLast(1).drop(1).replace("\",\"", ",,,").split(",,,")
// val numberOfEpisodesInt = playerScriptRawJson.substringAfterLast(",").toIntOrNull()
val playerJson = tryParseJson<List<List<PlayerJson>>>(playerEpisodesRawJson)!!
for(item in playerJson) {
item.forEachIndexed { index, item2 ->
if(!item2.name.contains("ПЛЕЙЛИСТ")) // UFDub player
{
episodes = episodes.plus(
Episode(
"$index, $url",
item2.name,
episode = item2.name.replace("Серія ","").toIntOrNull(),
)
)
}
}
}
}
}
}
return newTvSeriesLoadResponse(title, url, tvType, episodes.distinctBy{ it.name }) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
this.recommendations = recommendations
}
}
// It works when I click to view the series
override suspend fun loadLinks(
data: String, // (First) link, index | (Two) index, url title
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val dataList = data.split(", ")
if(dataList[0].contains("https://")){ // Its First type player
// Get episodes list (as json)
val responseGet = app.get(dataList[0]).parsedSafe<Responses>() // ajax link
responseGet?.response?.let { it ->
// List Players
val playersTab = Jsoup.parse(it).select("div.playlists-items")
// Parse all episodes by name
var index = 0
var playerTabId = ""
Jsoup.parse(it).select("div.playlists-videos li")
.mapNotNull { eps ->
// 0 - idk, 1 - dub, 2 - player
// if with sub
// 0 - idk 1 - dub 2 - sub or dub 3 - player
// dataList[1] - index
// 0_1_2
if(playerTabId != eps.attr("data-id")){
index = -1
playerTabId = eps.attr("data-id")
}
if(dataList[1].toInt() == index){
var href = eps.attr("data-file") // m3u url
// Can be without https:
if (!href.contains("https://")) {
href = "https:$href"
}
val dubName = playersTab[0].select(" li[data-id=${ playerTabId.take(3) }]").text() // G&M
var playerName = playersTab[1].select(" li[data-id=$playerTabId]").text() // ПЛЕЄР ASHDI
if(playerTabId.count { it == '_' } == 3) {
playerName =
playersTab[2].selectFirst(" li[data-id=$playerTabId]")!!
.text() // ПЛЕЄР ASHDI
}
if (href.contains("https://ashdi.vip/vod")) {
// Add as source
M3u8Helper.generateM3u8(
source = "$playerName ($dubName)",
streamUrl = AshdiExtractor().ParseM3U8(href),
referer = "https://qeruya.cyou"
).forEach(callback)
}
}
index += 1
}
}
} else {
val document = app.get(dataList[1]).document
document.select("script").map { script ->
if (script.data().contains("RalodePlayer.init(")) {
val playerScriptRawJson = script.data().substringAfterLast(".init(").substringBefore(");")
val playerEpisodesRawJson = playerScriptRawJson.substringAfter("],").substringBeforeLast(",")
val playerNamesArray = (playerScriptRawJson.substringBefore("],") + "]").dropLast(1).drop(1).replace("\",\"", ",,,").split(",,,")
val playerJson = tryParseJson<List<List<PlayerJson>>>(playerEpisodesRawJson)!!
playerJson.forEachIndexed { index, dub ->
if(dub[dataList[0].toInt()].code.contains("https://ashdi.vip")){
M3u8Helper.generateM3u8(
source = decode(playerNamesArray[index]),
streamUrl = AshdiExtractor().ParseM3U8(Jsoup.parse(dub[dataList[0].toInt()].code).select("iframe").attr("src")),
referer = "https://qeruya.cyou"
).forEach(callback)
}
}
}
}
}
return true
}
private fun decode(input: String): String{
// Decoded string, thanks to Secozzi
val hexRegex = Regex("\\\\u([0-9a-fA-F]{4})")
return hexRegex.replace(input) { matchResult ->
Integer.parseInt(matchResult.groupValues[1], 16).toChar().toString()
}
}
data class Responses(
val success: Boolean?,
val response: String?,
val message: String?
)
}

View file

@ -0,0 +1,13 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnitubeinuaProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnitubeinuaProvider())
}
}

View file

@ -0,0 +1,12 @@
package com.lagradost.extractors;
import com.lagradost.cloudstream3.app
class AshdiExtractor() {
suspend fun ParseM3U8(url: String): String{
return app.get(url).document.select("script").html()
.substringAfterLast("file:\"")
.substringBefore("\",")
}
}

View file

@ -0,0 +1,9 @@
package com.lagradost.models
data class PlayerJson (
val name : String, // Серія 0
val code : String, // iframe block
val zid : Int,
val sid : Int
)