Initial commit
This commit is contained in:
commit
98ed7e8839
2263 changed files with 108711 additions and 0 deletions
17
src/id/animeindo/build.gradle
Normal file
17
src/id/animeindo/build.gradle
Normal file
|
@ -0,0 +1,17 @@
|
|||
ext {
|
||||
extName = 'AnimeIndo'
|
||||
extClass = '.AnimeIndo'
|
||||
themePkg = 'animestream'
|
||||
baseUrl = 'https://animeindo.skin'
|
||||
overrideVersionCode = 10
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":lib:mp4upload-extractor"))
|
||||
implementation(project(":lib:gdriveplayer-extractor"))
|
||||
implementation(project(":lib:streamtape-extractor"))
|
||||
implementation(project(":lib:yourupload-extractor"))
|
||||
implementation(project(":lib:okru-extractor"))
|
||||
}
|
BIN
src/id/animeindo/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/id/animeindo/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
BIN
src/id/animeindo/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/id/animeindo/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
src/id/animeindo/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/id/animeindo/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
BIN
src/id/animeindo/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/id/animeindo/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
BIN
src/id/animeindo/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/id/animeindo/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,131 @@
|
|||
package eu.kanade.tachiyomi.animeextension.id.animeindo
|
||||
|
||||
import android.util.Log
|
||||
import eu.kanade.tachiyomi.animeextension.id.animeindo.AnimeIndoFilters.GenresFilter
|
||||
import eu.kanade.tachiyomi.animeextension.id.animeindo.AnimeIndoFilters.OrderFilter
|
||||
import eu.kanade.tachiyomi.animeextension.id.animeindo.AnimeIndoFilters.SeasonFilter
|
||||
import eu.kanade.tachiyomi.animeextension.id.animeindo.AnimeIndoFilters.StatusFilter
|
||||
import eu.kanade.tachiyomi.animeextension.id.animeindo.AnimeIndoFilters.StudioFilter
|
||||
import eu.kanade.tachiyomi.animeextension.id.animeindo.AnimeIndoFilters.TypeFilter
|
||||
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.lib.gdriveplayerextractor.GdrivePlayerExtractor
|
||||
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
|
||||
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
||||
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
||||
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
|
||||
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
|
||||
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class AnimeIndo : AnimeStream(
|
||||
"id",
|
||||
"AnimeIndo",
|
||||
"https://animeindo.skin",
|
||||
) {
|
||||
override val animeListUrl = "$baseUrl/browse"
|
||||
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeRequest(page: Int) = GET("$animeListUrl/browse?sort=view&page=$page")
|
||||
|
||||
// =============================== Latest ===============================
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$animeListUrl/browse?sort=created_at&page=$page")
|
||||
|
||||
// =============================== Search ===============================
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val params = AnimeIndoFilters.getSearchParameters(filters)
|
||||
val multiString = buildString {
|
||||
if (params.genres.isNotEmpty()) append(params.genres + "&")
|
||||
if (params.seasons.isNotEmpty()) append(params.seasons + "&")
|
||||
if (params.studios.isNotEmpty()) append(params.studios + "&")
|
||||
}
|
||||
|
||||
return GET("$animeListUrl/browse?page=$page&title=$query&$multiString&status=${params.status}&type=${params.type}&order=${params.order}")
|
||||
}
|
||||
|
||||
override fun searchAnimeSelector() = "div.animepost > div > a"
|
||||
|
||||
override fun searchAnimeFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
title = element.selectFirst("div.title")!!.text()
|
||||
thumbnail_url = element.selectFirst("img")!!.getImageUrl()
|
||||
}
|
||||
|
||||
override fun searchAnimeNextPageSelector() = "div.pagination a:has(i#nextpagination)"
|
||||
|
||||
// ============================== Filters ===============================
|
||||
override val filtersSelector = "div.filtersearch tbody > tr:not(:has(td.filter_title:contains(Search))) > td.filter_act"
|
||||
|
||||
override fun getFilterList(): AnimeFilterList {
|
||||
return if (AnimeStreamFilters.filterInitialized()) {
|
||||
AnimeFilterList(
|
||||
OrderFilter(orderFilterText),
|
||||
StatusFilter(statusFilterText),
|
||||
TypeFilter(typeFilterText),
|
||||
AnimeFilter.Separator(),
|
||||
GenresFilter(genresFilterText),
|
||||
SeasonFilter(seasonsFilterText),
|
||||
StudioFilter(studioFilterText),
|
||||
)
|
||||
} else {
|
||||
AnimeFilterList(AnimeFilter.Header(filtersMissingWarning))
|
||||
}
|
||||
}
|
||||
|
||||
// =========================== Anime Details ============================
|
||||
override fun parseStatus(statusString: String?): Int {
|
||||
return when (statusString?.trim()?.lowercase()) {
|
||||
"finished airing" -> SAnime.COMPLETED
|
||||
"currently airing" -> SAnime.ONGOING
|
||||
else -> SAnime.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== Episodes ==============================
|
||||
override fun episodeListSelector() = "div.listeps li:has(.epsleft)"
|
||||
|
||||
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
|
||||
val ahref = element.selectFirst("a")!!
|
||||
setUrlWithoutDomain(ahref.attr("href"))
|
||||
val num = ahref.text()
|
||||
name = "Episode $num"
|
||||
episode_number = num.trim().toFloatOrNull() ?: 0F
|
||||
date_upload = element.selectFirst("span.date")?.text().toDate()
|
||||
}
|
||||
|
||||
// ============================ Video Links =============================
|
||||
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
|
||||
private val gdrivePlayerExtractor by lazy { GdrivePlayerExtractor(client) }
|
||||
private val streamTapeExtractor by lazy { StreamTapeExtractor(client) }
|
||||
private val yourUploadExtractor by lazy { YourUploadExtractor(client) }
|
||||
private val okruExtractor by lazy { OkruExtractor(client) }
|
||||
|
||||
override fun getVideoList(url: String, name: String): List<Video> {
|
||||
return with(name) {
|
||||
when {
|
||||
contains("streamtape") -> streamTapeExtractor.videoFromUrl(url)?.let(::listOf).orEmpty()
|
||||
contains("mp4") -> mp4uploadExtractor.videosFromUrl(url, headers)
|
||||
contains("yourupload") -> yourUploadExtractor.videoFromUrl(url, headers)
|
||||
url.contains("ok.ru") -> okruExtractor.videosFromUrl(url)
|
||||
contains("gdrive") -> {
|
||||
val gdriveUrl = when {
|
||||
baseUrl in url -> "https:" + url.toHttpUrl().queryParameter("data")!!
|
||||
else -> url
|
||||
}
|
||||
gdrivePlayerExtractor.videosFromUrl(gdriveUrl, "Gdrive", headers)
|
||||
}
|
||||
else -> {
|
||||
// just to detect video hosts easily
|
||||
Log.i("AnimeIndo", "Unrecognized at getVideoList => Name -> $name || URL => $url")
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package eu.kanade.tachiyomi.animeextension.id.animeindo
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.CheckBoxFilterList
|
||||
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.QueryPartFilter
|
||||
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.asQueryPart
|
||||
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.filterElements
|
||||
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.filterInitialized
|
||||
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamFilters.parseCheckbox
|
||||
|
||||
object AnimeIndoFilters {
|
||||
internal class GenresFilter(name: String) : CheckBoxFilterList(name, GENRES_LIST)
|
||||
internal class SeasonFilter(name: String) : CheckBoxFilterList(name, SEASON_LIST)
|
||||
internal class StudioFilter(name: String) : CheckBoxFilterList(name, STUDIO_LIST)
|
||||
|
||||
internal class StatusFilter(name: String) : QueryPartFilter(name, STATUS_LIST)
|
||||
internal class TypeFilter(name: String) : QueryPartFilter(name, TYPE_LIST)
|
||||
internal class OrderFilter(name: String) : QueryPartFilter(name, ORDER_LIST)
|
||||
|
||||
internal data class FilterSearchParams(
|
||||
val genres: String = "",
|
||||
val seasons: String = "",
|
||||
val studios: String = "",
|
||||
val status: String = "",
|
||||
val type: String = "",
|
||||
val order: String = "",
|
||||
)
|
||||
|
||||
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
|
||||
if (filters.isEmpty()) return FilterSearchParams()
|
||||
if (!filterInitialized()) return FilterSearchParams()
|
||||
|
||||
return FilterSearchParams(
|
||||
filters.parseCheckbox<GenresFilter>(GENRES_LIST, "genre"),
|
||||
filters.parseCheckbox<SeasonFilter>(SEASON_LIST, "season"),
|
||||
filters.parseCheckbox<StudioFilter>(STUDIO_LIST, "studio"),
|
||||
filters.asQueryPart<StatusFilter>(),
|
||||
filters.asQueryPart<TypeFilter>(),
|
||||
filters.asQueryPart<OrderFilter>(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getPairListByIndex(index: Int) = filterElements.get(index)
|
||||
.select("ul > li, td > label")
|
||||
.map { element ->
|
||||
val key = element.text()
|
||||
val value = element.selectFirst("input")!!.attr("value")
|
||||
Pair(key, value)
|
||||
}.toTypedArray()
|
||||
|
||||
private val ORDER_LIST by lazy {
|
||||
getPairListByIndex(0)
|
||||
.filterNot { it.first.contains("Most favorite", true) }
|
||||
.toTypedArray()
|
||||
}
|
||||
|
||||
private val STATUS_LIST by lazy { getPairListByIndex(1) }
|
||||
private val TYPE_LIST by lazy { getPairListByIndex(2) }
|
||||
private val GENRES_LIST by lazy { getPairListByIndex(3) }
|
||||
private val SEASON_LIST by lazy { getPairListByIndex(4) }
|
||||
private val STUDIO_LIST by lazy { getPairListByIndex(5) }
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue