Initial commit
This commit is contained in:
commit
98ed7e8839
2263 changed files with 108711 additions and 0 deletions
11
src/es/animeid/build.gradle
Normal file
11
src/es/animeid/build.gradle
Normal file
|
@ -0,0 +1,11 @@
|
|||
ext {
|
||||
extName = 'AnimeID'
|
||||
extClass = '.AnimeID'
|
||||
extVersionCode = 8
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:streamtape-extractor'))
|
||||
}
|
BIN
src/es/animeid/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/es/animeid/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
src/es/animeid/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/es/animeid/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
BIN
src/es/animeid/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/es/animeid/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
BIN
src/es/animeid/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/es/animeid/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
BIN
src/es/animeid/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/es/animeid/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
|
@ -0,0 +1,365 @@
|
|||
package eu.kanade.tachiyomi.animeextension.es.animeid
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
||||
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
class AnimeID : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override val name = "AnimeID"
|
||||
|
||||
override val baseUrl = "https://www.animeid.tv/"
|
||||
|
||||
override val lang = "es"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override fun popularAnimeSelector(): String = "#result article.item"
|
||||
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/series?sort=views&pag=$page")
|
||||
|
||||
override fun popularAnimeFromElement(element: Element): SAnime {
|
||||
val anime = SAnime.create()
|
||||
anime.setUrlWithoutDomain(baseUrl + element.select("a").attr("href"))
|
||||
anime.title = element.select("a header").text()
|
||||
anime.thumbnail_url = element.select("a figure img").attr("src")
|
||||
anime.description = element.select("p div").text().removeSurrounding("\"")
|
||||
return anime
|
||||
}
|
||||
|
||||
override fun popularAnimeNextPageSelector(): String = "#paginas ul li:nth-last-child(2) a"
|
||||
|
||||
override fun episodeListSelector() = throw UnsupportedOperationException()
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val document = response.asJsoup()
|
||||
val animeId = document.select("#ord").attr("data-id")
|
||||
return episodeJsonParse(response.request.url.toString(), animeId)
|
||||
}
|
||||
|
||||
private fun episodeJsonParse(url: String, animeId: String): MutableList<SEpisode> {
|
||||
val capList = mutableListOf<SEpisode>()
|
||||
var nextPage = 1
|
||||
do {
|
||||
val headers = headers.newBuilder()
|
||||
.set("Referer", url)
|
||||
.set("sec-fetch-site", "same-origin")
|
||||
.set("x-requested-with", "XMLHttpRequest")
|
||||
.set("User-Agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0")
|
||||
.set("Accept-Language", "es-MX,es-419;q=0.9,es;q=0.8,en;q=0.7")
|
||||
.build()
|
||||
|
||||
val responseString = client.newCall(GET("https://www.animeid.tv/ajax/caps?id=$animeId&ord=DESC&pag=$nextPage", headers))
|
||||
.execute().asJsoup().body()!!.toString().substringAfter("<body>").substringBefore("</body>")
|
||||
val jObject = json.decodeFromString<JsonObject>(responseString)
|
||||
val listCaps = jObject["list"]!!.jsonArray
|
||||
listCaps!!.forEach { cap ->
|
||||
val capParsed = cap.jsonObject
|
||||
val epNum = capParsed["numero"]!!.jsonPrimitive.content!!.toFloat()
|
||||
val episode = SEpisode.create()
|
||||
val dateUpload = manualDateParse(capParsed["date"]!!.jsonPrimitive.content!!.toString())
|
||||
episode.episode_number = epNum
|
||||
episode.name = "Episodio $epNum"
|
||||
dateUpload!!.also { episode.date_upload = it }
|
||||
episode.setUrlWithoutDomain(baseUrl + capParsed["href"]!!.jsonPrimitive.content!!.toString())
|
||||
capList.add(episode)
|
||||
}
|
||||
if (listCaps!!.any()) nextPage += 1 else nextPage = -1
|
||||
} while (nextPage != -1)
|
||||
return capList
|
||||
}
|
||||
|
||||
private fun manualDateParse(stringDate: String): Long? {
|
||||
return try {
|
||||
val format = SimpleDateFormat("dd MMM yyyy")
|
||||
format.parse(stringDate!!.toString()).time
|
||||
} catch (e: Exception) {
|
||||
var dateParsed = stringDate.split(" ")
|
||||
val arrMonths = arrayOf("Jun", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
|
||||
val day = dateParsed[0]!!.trim().toInt()
|
||||
val month = arrMonths.indexOf(dateParsed[1].trim()) + 1
|
||||
val year = dateParsed[2]!!.trim().toInt()
|
||||
Date(year, month, day).time
|
||||
}
|
||||
}
|
||||
|
||||
override fun episodeFromElement(element: Element) = throw UnsupportedOperationException()
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
val videoList = mutableListOf<Video>()
|
||||
document.select("#partes div.container li.subtab div.parte").forEach { script ->
|
||||
val jsonString = script.attr("data")
|
||||
val jsonUnescape = unescapeJava(jsonString)!!.replace("\\", "")
|
||||
val url = jsonUnescape.substringAfter("src=\"").substringBefore("\"").replace("\\\\", "\\")
|
||||
if (url.contains("streamtape")) {
|
||||
StreamTapeExtractor(client).videoFromUrl(url)?.let { videoList.add(it) }
|
||||
}
|
||||
}
|
||||
return videoList
|
||||
}
|
||||
|
||||
private fun unescapeJava(escaped: String): String {
|
||||
var escaped = escaped
|
||||
if (escaped.indexOf("\\u") == -1) return escaped
|
||||
var processed = ""
|
||||
var position = escaped.indexOf("\\u")
|
||||
while (position != -1) {
|
||||
if (position != 0) processed += escaped.substring(0, position)
|
||||
val token = escaped.substring(position + 2, position + 6)
|
||||
escaped = escaped.substring(position + 6)
|
||||
processed += token.toInt(16).toChar()
|
||||
position = escaped.indexOf("\\u")
|
||||
}
|
||||
processed += escaped
|
||||
return processed
|
||||
}
|
||||
|
||||
override fun animeDetailsParse(document: Document): SAnime {
|
||||
val anime = SAnime.create()
|
||||
anime.thumbnail_url = document.selectFirst("#anime figure img.cover")!!.attr("abs:src")
|
||||
anime.title = document.selectFirst("#anime section hgroup h1")!!.text()
|
||||
anime.description = document.selectFirst("#anime section p.sinopsis")!!.text().removeSurrounding("\"")
|
||||
anime.genre = document.select("#anime section ul.tags li a").joinToString { it.text() }
|
||||
anime.status = parseStatus(document.select("div.main div section div.status-left div.cuerpo div:nth-child(2) span").text().trim())
|
||||
return anime
|
||||
}
|
||||
|
||||
override fun videoListSelector() = throw UnsupportedOperationException()
|
||||
|
||||
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
|
||||
|
||||
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
|
||||
val orderByFilter = filters.find { it is OrderByFilter } as OrderByFilter
|
||||
|
||||
return when {
|
||||
query.isNotBlank() -> GET("$baseUrl/buscar?q=$query&pag=$page&sort=${orderByFilter.toUriPart()}")
|
||||
genreFilter.state != 0 -> GET("$baseUrl/genero/${genreFilter.toUriPart()}?pag=$page&sort=${orderByFilter.toUriPart()}")
|
||||
orderByFilter.state != 0 -> GET("$baseUrl/series?sort=${orderByFilter.toUriPart()}&pag=$page")
|
||||
else -> popularAnimeRequest(page)
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
|
||||
|
||||
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
|
||||
|
||||
override fun searchAnimeSelector(): String = popularAnimeSelector()
|
||||
|
||||
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
|
||||
AnimeFilter.Header("La busqueda por texto ignora el filtro"),
|
||||
GenreFilter(),
|
||||
OrderByFilter(),
|
||||
)
|
||||
|
||||
private class GenreFilter : UriPartFilter(
|
||||
"Géneros",
|
||||
arrayOf(
|
||||
Pair("<Selecionar>", ""),
|
||||
Pair("+18", "18"),
|
||||
Pair("Acción", "accion"),
|
||||
Pair("Animación", "animacion"),
|
||||
Pair("Arte", "arte"),
|
||||
Pair("Artes Marciales", "artes-marciales"),
|
||||
Pair("Aventura", "aventura"),
|
||||
Pair("Bizarro", "bizarro"),
|
||||
Pair("Carreras", "carreras"),
|
||||
Pair("Ciencia Ficción", "ciencia-ficcion"),
|
||||
Pair("Colegialas", "colegialas"),
|
||||
Pair("Comedia", "comedia"),
|
||||
Pair("Concert", "concert"),
|
||||
Pair("Cyberpunk", "cyberpunk"),
|
||||
Pair("Demonios", "demonios"),
|
||||
Pair("Deportes", "deportes"),
|
||||
Pair("Drama", "drama"),
|
||||
Pair("Ecchi", "ecchi"),
|
||||
Pair("Escolares", "escolares"),
|
||||
Pair("Fantasía", "fantasia"),
|
||||
Pair("Fútbol", "futbol"),
|
||||
Pair("Game", "game"),
|
||||
Pair("Gore", "gore"),
|
||||
Pair("Guerra", "guerra"),
|
||||
Pair("Harem", "harem"),
|
||||
Pair("Histórico", "historico"),
|
||||
Pair("Horror", "horror"),
|
||||
Pair("Idol", "idol"),
|
||||
Pair("Infantil", "infantil"),
|
||||
Pair("invier", "invier"),
|
||||
Pair("Invierno 2013", "invierno-2013"),
|
||||
Pair("Invierno 2014", "invierno-2014"),
|
||||
Pair("Invierno 2015", "invierno-2015"),
|
||||
Pair("Invierno 2016", "invierno-2016"),
|
||||
Pair("Invierno 2017", "invierno-2017"),
|
||||
Pair("Invierno 2019", "invierno-2019"),
|
||||
Pair("Invierno 2020", "invierno-2020"),
|
||||
Pair("Invierno 2021", "invierno-2021"),
|
||||
Pair("Invierno 2022", "invierno-2022"),
|
||||
Pair("Invierno-2018", "invierno-2018"),
|
||||
Pair("Josei", "josei"),
|
||||
Pair("Juegos", "juegos"),
|
||||
Pair("Juegos De Mesa", "juegos-de-mesa"),
|
||||
Pair("Kids", "kids"),
|
||||
Pair("Loli", "loli"),
|
||||
Pair("Lucha", "lucha"),
|
||||
Pair("Mafia", "mafia"),
|
||||
Pair("Magia", "magia"),
|
||||
Pair("Mahou Shōjo", "mahou-shojo"),
|
||||
Pair("Mecha", "mecha"),
|
||||
Pair("Militar", "militar"),
|
||||
Pair("Misterio", "misterio"),
|
||||
Pair("Música", "musica"),
|
||||
Pair("Otoño 2012", "otono-2012"),
|
||||
Pair("Otoño 2013", "otono-2013"),
|
||||
Pair("Otoño 2014", "otono-2014"),
|
||||
Pair("Otoño 2015", "otono-2015"),
|
||||
Pair("Otoño 2016", "otono-2016"),
|
||||
Pair("Otoño 2018", "otono-2018"),
|
||||
Pair("Otoño 2019", "otono-2019"),
|
||||
Pair("Otoño 2020", "otono-2020"),
|
||||
Pair("Otoño 2021", "otono-2021"),
|
||||
Pair("otono-2017", "otono-2017"),
|
||||
Pair("Pantsu", "pantsu"),
|
||||
Pair("Parodia", "parodia"),
|
||||
Pair("Policía", "policia"),
|
||||
Pair("Post Apocalitico", "post-apocalitico"),
|
||||
Pair("prima", "prima"),
|
||||
Pair("Primavera 2013", "primavera-2013"),
|
||||
Pair("Primavera 2014", "primavera-2014"),
|
||||
Pair("Primavera 2015", "primavera-2015"),
|
||||
Pair("Primavera 2016", "primavera-2016"),
|
||||
Pair("Primavera 2017", "primavera-2017"),
|
||||
Pair("primavera 2018", "primavera-2018"),
|
||||
Pair("Primavera 2019", "primavera-2019"),
|
||||
Pair("Primavera 2020", "primavera-2020"),
|
||||
Pair("Primavera 2021", "primavera-2021"),
|
||||
Pair("Primavera 2022", "primavera-2022"),
|
||||
Pair("Primvera 2018", "primvera-2018"),
|
||||
Pair("Psicológico", "psicologico"),
|
||||
Pair("Recuentos De La Vida", "recuentos-de-la-vida"),
|
||||
Pair("Romance", "romance"),
|
||||
Pair("Samurai", "samurai"),
|
||||
Pair("Sci-Fi", "sci-fi"),
|
||||
Pair("Seinen", "seinen"),
|
||||
Pair("Shōjo", "shojo"),
|
||||
Pair("Shōjo-ai", "shojo-ai"),
|
||||
Pair("Shōnen", "shonen"),
|
||||
Pair("Shōnen-ai", "shonen-ai"),
|
||||
Pair("Shooter", "shooter"),
|
||||
Pair("Shoujo", "shoujo"),
|
||||
Pair("Shoujo Ai", "shoujo-ai"),
|
||||
Pair("Shounen", "shounen"),
|
||||
Pair("shounen ai", "shounen-ai"),
|
||||
Pair("Slice Of Life", "slice-of-life"),
|
||||
Pair("Sobrenatural", "sobrenatural"),
|
||||
Pair("Super poder", "super-poder"),
|
||||
Pair("Supernatural", "supernatural"),
|
||||
Pair("Suspenso", "suspenso"),
|
||||
Pair("Terror", "terror"),
|
||||
Pair("Thriller", "thriller"),
|
||||
Pair("Torneo", "torneo"),
|
||||
Pair("Tragedia", "tragedia"),
|
||||
Pair("Vampiros", "vampiros"),
|
||||
Pair("ver", "ver"),
|
||||
Pair("vera", "vera"),
|
||||
Pair("Verano 2013", "verano-2013"),
|
||||
Pair("Verano 2014", "verano-2014"),
|
||||
Pair("Verano 2015", "verano-2015"),
|
||||
Pair("Verano 2016", "verano-2016"),
|
||||
Pair("Verano 2017", "verano-2017"),
|
||||
Pair("Verano 2018", "verano-2018"),
|
||||
Pair("Verano 2019", "verano-2019"),
|
||||
Pair("Verano 2020", "verano-2020"),
|
||||
Pair("Verano 2021", "verano-2021"),
|
||||
Pair("Verano 2022", "verano-2022"),
|
||||
Pair("Violencia", "violencia"),
|
||||
Pair("Vocaloid", "vocaloid"),
|
||||
Pair("Yaoi", "yaoi"),
|
||||
Pair("Yuri", "yuri"),
|
||||
),
|
||||
)
|
||||
|
||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
private class OrderByFilter : UriPartFilter(
|
||||
"Ordenar Por",
|
||||
arrayOf(
|
||||
Pair("<Seleccionar>", ""),
|
||||
Pair("Recientes", "newest"),
|
||||
Pair("A-Z", "asc"),
|
||||
Pair("Más vistos", "views"),
|
||||
),
|
||||
)
|
||||
|
||||
private fun parseStatus(statusString: String): Int {
|
||||
return when {
|
||||
statusString.contains("En emisión") -> SAnime.ONGOING
|
||||
statusString.contains("Finalizada") -> SAnime.COMPLETED
|
||||
else -> SAnime.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector()
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/series?sort=newest&pag=$page")
|
||||
|
||||
override fun latestUpdatesSelector() = popularAnimeSelector()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = "preferred_quality"
|
||||
title = "Preferred server"
|
||||
entries = arrayOf("StreamTape")
|
||||
entryValues = arrayOf("StreamTape")
|
||||
setDefaultValue("StreamTape")
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue