aniage: Anime
This commit is contained in:
parent
8c801c19c0
commit
c2d51968ea
8 changed files with 371 additions and 0 deletions
30
AniageProvider/build.gradle.kts
Normal file
30
AniageProvider/build.gradle.kts
Normal 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%"
|
||||||
|
}
|
2
AniageProvider/src/main/AndroidManifest.xml
Normal file
2
AniageProvider/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="com.lagradost"/>
|
204
AniageProvider/src/main/kotlin/com/lagradost/AniageProvider.kt
Normal file
204
AniageProvider/src/main/kotlin/com/lagradost/AniageProvider.kt
Normal 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()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -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
|
||||||
|
)
|
|
@ -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
|
||||||
|
)
|
|
@ -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
|
||||||
|
)
|
Loading…
Reference in a new issue