aniage: Anime

This commit is contained in:
CakesTwix 2023-08-03 19:59:25 +03:00
parent 8c801c19c0
commit c2d51968ea
Signed by: CakesTwix
GPG key ID: 7B11051D5CE19825
8 changed files with 371 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 = "Перший нормальний сайт, який зроблено з нуля без використання руснявих технологій. Поки що мало контенту."
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=aniage.net&sz=%size%"
}

View file

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

View file

@ -0,0 +1,204 @@
package com.lagradost
import android.util.Log
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.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.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.models.AnimeDetail
import com.lagradost.models.EpisodesModel
import com.lagradost.models.FindModel
import com.lagradost.models.TeamsModel
import org.json.JSONArray
import org.json.JSONObject
class AniageProvider : MainAPI() {
// Basic Info
override var mainUrl = "https://aniage.net"
override var name = "Aniage"
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 = "https://master.api.aniage.net"
private val cdnUrl = "https://aniage.fra1.cdn.digitaloceanspaces.com/main/"
private val pageSize = 30
private val listEpisodeModel = object : TypeToken<List<EpisodesModel>>() { }.type
private val listTeamsModel = object : TypeToken<List<TeamsModel>>() { }.type
// Sections
override val mainPage = mainPageOf(
mainUrl to "Нове",
)
// Done
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
// Log.d("CakesTwix-Debug", page.toString())
val body = JSONObject()
body.put("cleanup", JSONArray())
val orderBody = JSONObject()
orderBody.put("by", "lastUpdated")
orderBody.put("direction", "DESC")
body.put("order", orderBody)
body.put("page", page)
body.put("pageSize", pageSize)
val document = app.post("$apiUrl/v2/anime/find",
json = body
).text
val parsedJSON = Gson().fromJson(document, FindModel::class.java)
// Log.d("CakesTwix-Debug", parsedJSON.data[0].title)
val homeList = parsedJSON.data.map {
newAnimeSearchResponse(it.title, it.id, TvType.Anime) {
this.posterUrl = "$cdnUrl${it.posterId}"
addDubStatus(isDub = true, it.episodes)
this.otherName = it.alternativeTitle
}
}
// Log.d("CakesTwix-Debug", "$cdnUrl${parsedJSON.data[1].posterId}")
return newHomePageResponse(request.name, homeList)
}
// TODO
override suspend fun search(query: String): List<SearchResponse> {
return emptyList()
}
// Detailed information
override suspend fun load(url: String): LoadResponse {
val animeID = url.replace("$mainUrl/", "")
val document = app.get("$mainUrl/watch/$animeID").document
val jsonObject = JSONObject(document.selectFirst("script[type*=application/json]")!!.html())
val buildId = jsonObject.getString("buildId")
// https://www.aniage.net/_next/data/F64n_RAvOkYPvB3Z9Bmw2/watch/fea3c510-f42d-4a18-b438-bfab102f4424.json
// Log.d("CakesTwix-Debug", app.get("$mainUrl/_next/data/$buildId/watch/$animeID.json").text)
val animeJSON = Gson().fromJson(app.get("$mainUrl/_next/data/$buildId/watch/$animeID.json").text, AnimeDetail::class.java)
// Log.d("CakesTwix-Debug", animeJSON.pageProps.title)
val showStatus = with(animeJSON.pageProps.titleStatus){
when{
contains("Онгоїнг") -> ShowStatus.Ongoing
contains("Вийшло") -> ShowStatus.Completed
else -> null
}
}
// Episodes
// https://master.api.aniage.net/anime/episodes
// ?animeId=2c60c269-049e-428b-96ba-fae23ac718ec
// &page=1
// &pageSize=30
// &sortOrder=ASC
// &teamId=99012182-f177-45df-a21f-6823bb9955c3
// &volume=1
val episodes = mutableListOf<Episode>()
// Log.d("CakesTwix-Debug", app.get("https://master.api.aniage.net/anime/episodes?animeId=$animeID&page=1&pageSize=30&sortOrder=ASC&teamId=${teams.teamId}&volume=1").url)
Gson().fromJson<List<EpisodesModel>>(app.get("https://master.api.aniage.net/anime/episodes?animeId=$animeID&page=1&pageSize=30&sortOrder=ASC&teamId=${animeJSON.pageProps.teams[0].teamId}&volume=1").text, listEpisodeModel).map {
episodes.add(Episode
(
"${it.animeId}, ${it.episodeNum}",
"Серія ${it.title}",
it.volume,
it.episodeNum,
"$cdnUrl${it.previewPath}",
)
)
}
return newAnimeLoadResponse(
animeJSON.pageProps.title,
"$mainUrl/watch/$animeID",
TvType.Anime,
) {
this.posterUrl = "$cdnUrl${animeJSON.pageProps.posterId}"
this.engName = animeJSON.pageProps.alternativeTitle
this.tags = animeJSON.pageProps.genres.map { it }
this.plot = animeJSON.pageProps.description
// addTrailer(animeJSON.pageProps.trailerUrl)
this.showStatus = showStatus
this.duration = animeJSON.pageProps.averageDuration
addEpisodes(DubStatus.Dubbed, episodes)
this.year = extractIntFromString(animeJSON.pageProps.season)
}
}
// It works when I click to view the series
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
// animeID, Num Episode
val dataList = data.split(", ")
// Get Episodes List
val document = app.get("$mainUrl/watch/${dataList[0]}").document
val jsonObject = JSONObject(document.selectFirst("script[type*=application/json]")!!.html())
val buildId = jsonObject.getString("buildId")
val animeJSON = Gson().fromJson(app.get("$mainUrl/_next/data/$buildId/watch/${dataList[0]}.json").text, AnimeDetail::class.java)
// Parse list, by episode
animeJSON.pageProps.teams.map { teams ->
val TeamsList = Gson().fromJson<List<TeamsModel>>(app.get("$apiUrl/anime/teams/by-ids?ids=${teams.teamId}").text, listTeamsModel)[0]
// Log.d("CakesTwix-Debug", app.get("https://master.api.aniage.net/anime/episodes?animeId=$animeID&page=1&pageSize=30&sortOrder=ASC&teamId=${teams.teamId}&volume=1").url)
Gson().fromJson<List<EpisodesModel>>(app.get("https://master.api.aniage.net/anime/episodes?animeId=${dataList[0]}&page=1&pageSize=30&sortOrder=ASC&teamId=${teams.teamId}&volume=1").text, listEpisodeModel).map {
if(it.episodeNum == dataList[1].toInt()){
// Log.d("CakesTwix-Debug", app.get(it.playPath).document.select("source[type*=application/x-mpegURL]").attr("src"))
M3u8Helper.generateM3u8(
source = TeamsList.name,
streamUrl = app.get(it.playPath).document.select("source[type*=application/x-mpegURL]").attr("src"),
referer = mainUrl
).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 AniageProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AniageProvider())
}
}

View file

@ -0,0 +1,38 @@
package com.lagradost.models
import com.google.gson.annotations.SerializedName
data class AnimeDetail (
@SerializedName("pageProps") val pageProps : PageProps,
@SerializedName("__N_SSP") val __N_SSP : Boolean
)
data class PageProps (
@SerializedName("id") val id : String,
@SerializedName("posterId") val posterId : String,
@SerializedName("title") val title : String,
@SerializedName("alternativeTitle") val alternativeTitle : String,
@SerializedName("type") val type : String,
@SerializedName("titleStatus") val titleStatus : String,
@SerializedName("publishedAt") val publishedAt : String,
@SerializedName("genres") val genres : List<String>,
@SerializedName("description") val description : String,
@SerializedName("season") val season : String,
@SerializedName("studios") val studios : List<String>,
@SerializedName("adult") val adult : Int,
@SerializedName("episodes") val episodes : Int,
@SerializedName("trailerUrl") val trailerUrl : String,
@SerializedName("maxEpisodes") val maxEpisodes : Int,
@SerializedName("averageDuration") val averageDuration : Int,
@SerializedName("teams") val teams : List<Teams>
)
data class Teams (
@SerializedName("animeId") val animeId : String,
@SerializedName("teamId") val teamId : String,
@SerializedName("episodes") val episodes : Int,
@SerializedName("views") val views : Int,
@SerializedName("lastUpdated") val lastUpdated : String
)

View file

@ -0,0 +1,19 @@
package com.lagradost.models
import com.google.gson.annotations.SerializedName
data class EpisodesModel(
@SerializedName("views") val views : Int,
@SerializedName("commented") val commented : Int,
@SerializedName("id") val id : String,
@SerializedName("animeId") val animeId : String,
@SerializedName("teamId") val teamId : String,
@SerializedName("volume") val volume : Int,
@SerializedName("episodeNum") val episodeNum : Int,
@SerializedName("subEpisodeNum") val subEpisodeNum : Int,
@SerializedName("lastUpdated") val lastUpdated : String,
@SerializedName("resourcePath") val resourcePath : String,
@SerializedName("playPath") val playPath : String,
@SerializedName("title") val title : String,
@SerializedName("previewPath") val previewPath : String
)

View file

@ -0,0 +1,46 @@
package com.lagradost.models
import com.google.gson.annotations.SerializedName
data class FindModel (
// @SerializedName("cursorNext") val cursorNext : CursorNext?,
// @SerializedName("cursorPrev") val cursorPrev : String?,
@SerializedName("counter") val counter : Int,
@SerializedName("data") val data : List<Data>
)
data class CursorNext (
@SerializedName("page") val page : Int,
@SerializedName("pageSize") val pageSize : Int,
@SerializedName("order") val order : Order,
@SerializedName("cleanup") val cleanup : List<String>
)
data class Data (
@SerializedName("id") val id : String,
@SerializedName("posterId") val posterId : String,
@SerializedName("title") val title : String,
@SerializedName("alternativeTitle") val alternativeTitle : String,
@SerializedName("type") val type : String,
@SerializedName("titleStatus") val titleStatus : String,
@SerializedName("publishedAt") val publishedAt : String,
@SerializedName("genres") val genres : List<String>,
@SerializedName("description") val description : String,
@SerializedName("season") val season : String,
@SerializedName("studios") val studios : List<String>,
@SerializedName("adult") val adult : Int,
@SerializedName("episodes") val episodes : Int,
@SerializedName("trailerUrl") val trailerUrl : String,
@SerializedName("maxEpisodes") val maxEpisodes : Int,
@SerializedName("averageDuration") val averageDuration : Int
)
data class Order (
@SerializedName("by") val by : String,
@SerializedName("direction") val direction : String
)

View file

@ -0,0 +1,19 @@
package com.lagradost.models
import com.google.gson.annotations.SerializedName
data class TeamsModel (
@SerializedName("socials") val socials : List<Socials>,
@SerializedName("id") val id : String,
@SerializedName("ownerId") val ownerId : String,
@SerializedName("description") val description : String,
@SerializedName("name") val name : String,
@SerializedName("logo") val logo : String,
@SerializedName("type") val type : String
)
data class Socials (
@SerializedName("url") val url : String,
@SerializedName("type") val type : String
)