animeon: Done, but need rework parsing players

This commit is contained in:
CakesTwix 2024-04-08 20:57:39 +03:00
parent 1cd64e0ab7
commit eca14921b8
Signed by: CakesTwix
GPG key ID: 7B11051D5CE19825
9 changed files with 499 additions and 0 deletions

View file

@ -0,0 +1,30 @@
// use an integer for version numbers
version = 1
dependencies{
implementation("com.google.code.gson:gson:2.9.0")
}
cloudstream {
language = "uk"
// All of these properties are optional, you can safely remove them
description = "AnimeON - Дивитися аніме україньскою"
authors = listOf("CakesTwix")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
tvTypes = listOf(
"Anime",
"AnimeMovie",
"OVA",
)
iconUrl = "https://www.google.com/s2/favicons?domain=animeon.club&sz=%size%"
}

View file

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

View file

@ -0,0 +1,248 @@
package com.lagradost
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.Episode
import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.MainPageRequest
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.ShowStatus
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.addDubStatus
import com.lagradost.cloudstream3.addEpisodes
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mainPageOf
import com.lagradost.cloudstream3.newAnimeLoadResponse
import com.lagradost.cloudstream3.newAnimeSearchResponse
import com.lagradost.cloudstream3.newHomePageResponse
import com.lagradost.cloudstream3.newMovieLoadResponse
import com.lagradost.cloudstream3.toRatingInt
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.models.AnimeInfoModel
import com.lagradost.models.AnimeModel
import com.lagradost.models.NewAnimeModel
import com.lagradost.models.SearchModel
import com.lagradost.models.PlayerJson
class AnimeONProvider : MainAPI() {
// Basic Info
override var mainUrl = "https://animeon.club"
override var name = "AnimeON"
override val hasMainPage = true
override var lang = "uk"
override val hasDownloadSupport = true
override val supportedTypes =
setOf(
TvType.Anime,
TvType.AnimeMovie,
TvType.OVA,
)
private val apiUrl = "$mainUrl/api/anime/"
private val posterApi = "$mainUrl/api/uploads/images/%s"
private val searchApi = "$apiUrl/search/%s?full=true"
// Sections
override val mainPage =
mainPageOf(
"$apiUrl/popular" to "Популярне",
"$apiUrl/seasons" to "Аніме поточного сезону ",
"$apiUrl?pageSize=24&pageIndex=%s&sortType=DESC&sort=created" to "Нове",
)
private val listAnimeModel = object : TypeToken<List<AnimeModel>>() {}.type
// Done
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
if(!request.data.contains("pageIndex") && page !=1) return HomePageResponse(emptyList())
val document = app.get(request.data.format(page)).text
// Нове
if(request.data.contains("pageIndex")) {
val parsedJSON = Gson().fromJson(document, NewAnimeModel::class.java)
val homeList =
parsedJSON.results.map {
newAnimeSearchResponse(it.titleUa, "anime/${it.id}", TvType.Anime) {
this.posterUrl = posterApi.format(it.poster)
}
}
// Log.d("CakesTwix-Debug", "$cdnUrl${parsedJSON.data[1].posterId}")
return newHomePageResponse(request.name, homeList)
} else {
val parsedJSON = Gson().fromJson<List<AnimeModel>>(document, listAnimeModel)
val homeList =
parsedJSON.map {
newAnimeSearchResponse(it.titleUa, "anime/${it.id}", TvType.Anime) {
this.posterUrl = posterApi.format(it.poster)
}
}
// Log.d("CakesTwix-Debug", "$cdnUrl${parsedJSON.data[1].posterId}")
return newHomePageResponse(request.name, homeList)
}
}
override suspend fun search(query: String): List<SearchResponse> {
val animeJSON =
Gson().fromJson(app.get(searchApi.format(query)).text, SearchModel::class.java)
val findList =
animeJSON.result.map {
newAnimeSearchResponse(it.titleUa, "anime/${it.id}", TvType.Anime) {
this.posterUrl = posterApi.format(it.poster)
addDubStatus(isDub = true, it.episodes)
}
}
return findList
}
// Detailed information
override suspend fun load(url: String): LoadResponse {
val animeJSON =
Gson()
.fromJson(
app.get(url.replace("/anime/", "/api/anime/")).text,
AnimeInfoModel::class.java
)
val showStatus =
with(animeJSON.status.name) {
when {
contains("Онґоїнґ") -> ShowStatus.Ongoing
contains("Завершений") -> ShowStatus.Completed
else -> null
}
}
val tvType =
with(animeJSON.type.name) {
when {
contains("ТБ-Серіал") -> TvType.Anime
contains("OVA") -> TvType.OVA
contains("Спеціальний випуск") -> TvType.OVA
contains("ONA") -> TvType.OVA
contains("Фільм") -> TvType.AnimeMovie
else -> TvType.Anime
}
}
val episodes = mutableListOf<Episode>()
val playerRawJson = app.get(animeJSON.player.url).document.select("script").html()
.substringAfterLast("file:\'")
.substringBefore("\',")
tryParseJson<List<PlayerJson>>(playerRawJson)?.map { dubs -> // Dubs
for (season in dubs.folder) { // Seasons
for (episode in season.folder) { // Episodes
episodes.add(
Episode(
"${season.title}, ${episode.title}, ${animeJSON.player.url}",
episode.title,
season.title.replace(" Сезон ", "").toIntOrNull(),
episode.title.replace("Серія ", "").toIntOrNull(),
episode.poster
)
)
}
}
}
return if (tvType == TvType.Anime || tvType == TvType.OVA) {
newAnimeLoadResponse(
animeJSON.titleUa,
"$mainUrl/anime/${animeJSON.id}",
tvType,
) {
this.posterUrl = posterApi.format(animeJSON.poster)
this.engName = animeJSON.titleEn
this.tags = animeJSON.genres.map { it.name }
this.plot = animeJSON.description
addTrailer(animeJSON.trailer)
this.showStatus = showStatus
this.duration = extractIntFromString(animeJSON.episodeTime)
this.year = animeJSON.releaseDate
this.backgroundPosterUrl = posterApi.format(animeJSON.backgroundImage)
this.rating = animeJSON.rating.toString().toRatingInt()
addEpisodes(DubStatus.Dubbed, episodes)
addMalId(animeJSON.malId)
}
} else {
newMovieLoadResponse(animeJSON.titleUa, "$mainUrl/anime/${animeJSON.id}", tvType, "${animeJSON.titleUa}, ${animeJSON.player.url}") {
this.posterUrl = posterApi.format(animeJSON.poster)
this.tags = animeJSON.genres.map { it.name }
this.plot = animeJSON.description
addTrailer(animeJSON.trailer)
this.duration = extractIntFromString(animeJSON.episodeTime)
this.year = animeJSON.releaseDate
this.backgroundPosterUrl = posterApi.format(animeJSON.backgroundImage)
this.rating = animeJSON.rating.toString().toRatingInt()
addMalId(animeJSON.malId)
}
}
}
// It works when I click to view the series
override suspend fun loadLinks(
data: String, // (Serisl) [Season, Episode, Player Url] | (Film) [Title, Player Url]
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val dataList = data.split(", ")
// Its film, parse one m3u8
if(dataList.size == 2){
val m3u8Url = app.get(dataList[1]).document.select("script").html()
.substringAfterLast("file:\"")
.substringBefore("\",")
M3u8Helper.generateM3u8(
source = dataList[0],
streamUrl = m3u8Url,
referer = "https://tortuga.wtf/"
).forEach(callback)
return true
}
val playerRawJson = app.get(dataList[2]).document.select("script").html()
.substringAfterLast("file:\'")
.substringBefore("\',")
tryParseJson<List<PlayerJson>>(playerRawJson)?.map { dubs -> // Dubs
for(season in dubs.folder){ // Seasons
if(season.title == dataList[0]){
for(episode in season.folder){ // Episodes
if(episode.title == dataList[1]){
// Add as source
M3u8Helper.generateM3u8(
source = dubs.title,
streamUrl = episode.file,
referer = "https://tortuga.wtf/"
).forEach(callback)
}
}
}
}
}
return true
}
private fun extractIntFromString(string: String): Int? {
val value = Regex("(\\d+)").findAll(string).lastOrNull() ?: return null
if (value.value[0].toString() == "0") {
return value.value.drop(1).toIntOrNull()
}
return value.value.toIntOrNull()
}
}

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 AnimeONProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimeONProvider())
}
}

View file

@ -0,0 +1,104 @@
package com.lagradost.models
import com.google.gson.annotations.SerializedName
class AnimeInfoModel (
@SerializedName("id") val id : Int,
@SerializedName("titleUa") val titleUa : String,
@SerializedName("titleEn") val titleEn : String,
@SerializedName("description") val description : String,
@SerializedName("releaseDate") val releaseDate : Int,
@SerializedName("producer") val producer : String,
@SerializedName("views") val views : Int,
@SerializedName("episodes") val episodes : Int,
@SerializedName("episodeTime") val episodeTime : String,
@SerializedName("poster") val poster : String,
@SerializedName("backgroundImage") val backgroundImage : String,
@SerializedName("episodesAired") val episodesAired : Int,
@SerializedName("createdAt") val createdAt : String,
@SerializedName("updatedAt") val updatedAt : String,
@SerializedName("trailer") val trailer : String,
@SerializedName("ashdiId") val ashdiId : String,
@SerializedName("malId") val malId : Int,
@SerializedName("pl") val pl : String,
@SerializedName("season") val season : Int,
@SerializedName("rating") val rating : Double,
@SerializedName("genres") val genres : List<Genres>,
@SerializedName("tags") val tags : List<Tags>,
@SerializedName("studio") val studio : Studio,
@SerializedName("status") val status : Status,
@SerializedName("age") val age : Age,
@SerializedName("fundups") val fundups : List<Fundups>,
@SerializedName("type") val type : Type,
@SerializedName("schedule") val schedule : Schedule,
@SerializedName("franchise") val franchise : Franchise,
@SerializedName("player") val player : Player,
@SerializedName("screenshots") val screenshots : List<Screenshots>,
@SerializedName("rank") val rank : Int,
@SerializedName("votes") val votes : Int
)
data class Schedule (
@SerializedName("id") val id : Int,
@SerializedName("date") val name : String,
@SerializedName("episode") val episode : String,
)
data class Age (
@SerializedName("id") val id : Int,
@SerializedName("name") val name : String
)
data class Franchise (
@SerializedName("id") val id : Int,
@SerializedName("weight") val weight : Int
)
data class Fundups (
@SerializedName("id") val id : Int,
@SerializedName("name") val name : String
)
data class Player (
@SerializedName("id") val id : Int,
@SerializedName("url") val url : String,
@SerializedName("numEpisodeFrom") val numEpisodeFrom : String,
@SerializedName("numEpisodeTo") val numEpisodeTo : String
)
data class Screenshots (
@SerializedName("id") val id : Int,
@SerializedName("original") val original : String,
@SerializedName("preview") val preview : String
)
data class Status (
@SerializedName("id") val id : Int,
@SerializedName("name") val name : String
)
data class Studio (
@SerializedName("id") val id : Int,
@SerializedName("name") val name : String
)
data class Tags (
@SerializedName("id") val id : Int,
@SerializedName("name") val name : String
)
data class Type (
@SerializedName("id") val id : Int,
@SerializedName("name") val name : String
)

View file

@ -0,0 +1,20 @@
package com.lagradost.models
import com.google.gson.annotations.SerializedName
class AnimeModel (
@SerializedName("id") val id : Int,
@SerializedName("titleUa") val titleUa : String,
@SerializedName("description") val description : String,
@SerializedName("poster") val poster : String,
@SerializedName("backgroundImage") val backgroundImage : String,
@SerializedName("rating") val rating : Double,
@SerializedName("genres") val genres : List<Genres>
)
data class Genres (
@SerializedName("id") val id : Int,
@SerializedName("name") val name : String,
@SerializedName("malId") val malId : Int
)

View file

@ -0,0 +1,27 @@
package com.lagradost.models
import com.google.gson.annotations.SerializedName
class NewAnimeModel (
@SerializedName("results") val results : List<Results>,
@SerializedName("totalCount") val totalCount : Int
)
data class Results (
@SerializedName("id") val id : Int,
@SerializedName("titleUa") val titleUa : String,
@SerializedName("description") val description : String,
@SerializedName("releaseDate") val releaseDate : Int,
@SerializedName("views") val views : Int,
@SerializedName("episodes") val episodes : Int,
@SerializedName("poster") val poster : String,
@SerializedName("episodesAired") val episodesAired : Int,
@SerializedName("createdAt") val createdAt : String,
@SerializedName("malId") val malId : Int,
@SerializedName("rating") val rating : Double,
@SerializedName("status") val status : Status,
@SerializedName("type") val type : Type,
@SerializedName("genres") val genres : List<Genres>
)

View file

@ -0,0 +1,22 @@
package com.lagradost.models
data class PlayerJson (
val title : String,
val folder : List<Season>
)
data class Season (
val title : String,
val folder : List<Episode>
)
data class Episode (
val title : String,
val file : String,
val id : String,
val poster : String,
val subtitle : String,
)

View file

@ -0,0 +1,33 @@
package com.lagradost.models
import com.google.gson.annotations.SerializedName
class SearchModel (
@SerializedName("result") val result : List<Result>,
@SerializedName("count") val count : Int
)
data class Result (
@SerializedName("id") val id : Int,
@SerializedName("titleUa") val titleUa : String,
@SerializedName("titleEn") val titleEn : String,
@SerializedName("description") val description : String,
@SerializedName("releaseDate") val releaseDate : Int,
@SerializedName("producer") val producer : String,
@SerializedName("views") val views : Int,
@SerializedName("episodes") val episodes : Int,
@SerializedName("episodeTime") val episodeTime : String,
@SerializedName("poster") val poster : String,
@SerializedName("backgroundImage") val backgroundImage : String,
@SerializedName("episodesAired") val episodesAired : Int,
@SerializedName("createdAt") val createdAt : String,
@SerializedName("updatedAt") val updatedAt : String,
@SerializedName("ashdiId") val ashdiId : String,
@SerializedName("malId") val malId : Int,
@SerializedName("season") val season : Int,
@SerializedName("rating") val rating : Double,
@SerializedName("type") val type : Type,
@SerializedName("status") val status : Status
)