[skip ci] AllAnime Refactor (#1663)

This commit is contained in:
Secozzi
2023-05-31 19:20:22 +02:00
committed by GitHub
parent 843e8ad04a
commit 93519cd8a5
4 changed files with 381 additions and 366 deletions

View File

@ -25,7 +25,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -40,8 +39,7 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "AllAnime" override val name = "AllAnime"
// allanime.to override val baseUrl by lazy { preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)!! }
override val baseUrl by lazy { preferences.getString("preferred_domain", "https://api.allanime.to")!! }
override val lang = "en" override val lang = "en"
@ -51,108 +49,6 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
private val json: Json by injectLazy() private val json: Json by injectLazy()
private val popularQuery = """
query(
${'$'}type: VaildPopularTypeEnumType!
${'$'}size: Int!
${'$'}page: Int
${'$'}dateRange: Int
) {
queryPopular(
type: ${'$'}type
size: ${'$'}size
dateRange: ${'$'}dateRange
page: ${'$'}page
) {
total
recommendations {
anyCard {
_id
name
thumbnail
englishName
nativeName
slugTime
}
}
}
}
""".trimIndent().trim()
private val searchQuery = """
query(
${'$'}search: SearchInput
${'$'}limit: Int
${'$'}page: Int
${'$'}translationType: VaildTranslationTypeEnumType
${'$'}countryOrigin: VaildCountryOriginEnumType
) {
shows(
search: ${'$'}search
limit: ${'$'}limit
page: ${'$'}page
translationType: ${'$'}translationType
countryOrigin: ${'$'}countryOrigin
) {
pageInfo {
total
}
edges {
_id
name
thumbnail
englishName
nativeName
slugTime
}
}
}
""".trimIndent().trim()
private val detailsQuery = """
query (${'$'}_id: String!) {
show(
_id: ${'$'}_id
) {
thumbnail
description
type
season
score
genres
status
studios
}
}
""".trimIndent().trim()
private val episodesQuery = """
query (${'$'}_id: String!) {
show(
_id: ${'$'}_id
) {
_id
availableEpisodesDetail
}
}
""".trimIndent().trim()
private val streamQuery = """
query(
${'$'}showId: String!,
${'$'}translationType: VaildTranslationTypeEnumType!,
${'$'}episodeString: String!
) {
episode(
showId: ${'$'}showId
translationType: ${'$'}translationType
episodeString: ${'$'}episodeString
) {
sourceUrls
}
}
""".trimIndent().trim()
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -160,18 +56,15 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================== Popular =============================== // ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request { override fun popularAnimeRequest(page: Int): Request {
val variables = """{"type":"anime","size":26,"dateRange":7,"page":$page}""" val variables = """{"type":"anime","size":$PAGE_SIZE,"dateRange":7,"page":$page}"""
val headers = headers.newBuilder() return GET("$baseUrl/allanimeapi?variables=$variables&query=$POPULAR_QUERY", headers = headers)
.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0")
.build()
return GET("$baseUrl/allanimeapi?variables=$variables&query=$popularQuery", headers = headers)
} }
override fun popularAnimeParse(response: Response): AnimesPage { override fun popularAnimeParse(response: Response): AnimesPage {
val parsed = json.decodeFromString<PopularResult>(response.body.string()) val parsed = json.decodeFromString<PopularResult>(response.body.string())
val animeList = mutableListOf<SAnime>() val animeList = mutableListOf<SAnime>()
val titleStyle = preferences.getString("preferred_title_style", "romaji")!! val titleStyle = preferences.getString(PREF_TITLE_STYLE_KEY, PREF_TITLE_STYLE_DEFAULT)!!
parsed.data.queryPopular.recommendations.forEach { parsed.data.queryPopular.recommendations.forEach {
if (it.anyCard != null) { if (it.anyCard != null) {
@ -189,17 +82,16 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} }
} }
return AnimesPage(animeList, animeList.size == 26) return AnimesPage(animeList, animeList.size == PAGE_SIZE)
} }
// =============================== Latest =============================== // =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request { override fun latestUpdatesRequest(page: Int): Request {
val variables = """{"search":{"allowAdult":false,"allowUnknown":false},"limit":26,"page":$page,"translationType":"${preferences.getString("preferred_sub", "sub")!!}","countryOrigin":"ALL"}""" // Could be lazily loaded along with url, but would require user to restart
val headers = headers.newBuilder() val subPref = preferences.getString(PREF_SUB_KEY, PREF_SUB_DEFAULT)!!
.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0") val variables = """{"search":{"allowAdult":false,"allowUnknown":false},"limit":$PAGE_SIZE,"page":$page,"translationType":"$subPref","countryOrigin":"ALL"}"""
.build() return GET("$baseUrl/allanimeapi?variables=$variables&query=$SEARCH_QUERY", headers = headers)
return GET("$baseUrl/allanimeapi?variables=$variables&query=$searchQuery", headers = headers)
} }
override fun latestUpdatesParse(response: Response): AnimesPage { override fun latestUpdatesParse(response: Response): AnimesPage {
@ -220,12 +112,10 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = throw Exception("not used") override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = throw Exception("not used")
private fun searchAnimeRequest(page: Int, query: String, filters: AllAnimeFilters.FilterSearchParams): Request { private fun searchAnimeRequest(page: Int, query: String, filters: AllAnimeFilters.FilterSearchParams): Request {
val subPref = preferences.getString(PREF_SUB_KEY, PREF_SUB_DEFAULT)!!
return if (query.isNotEmpty()) { return if (query.isNotEmpty()) {
val variables = """{"search":{"query":"$query","allowAdult":false,"allowUnknown":false},"limit":26,"page":$page,"translationType":"${preferences.getString("preferred_sub", "sub")!!}","countryOrigin":"ALL"}""" val variables = """{"search":{"query":"$query","allowAdult":false,"allowUnknown":false},"limit":$PAGE_SIZE,"page":$page,"translationType":"$subPref","countryOrigin":"ALL"}"""
val headers = headers.newBuilder() GET("$baseUrl/allanimeapi?variables=$variables&query=$SEARCH_QUERY", headers = headers)
.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0")
.build()
GET("$baseUrl/allanimeapi?variables=$variables&query=$searchQuery", headers = headers)
} else { } else {
val seasonString = if (filters.season == "all") "" else ""","season":"${filters.season}"""" val seasonString = if (filters.season == "all") "" else ""","season":"${filters.season}""""
val yearString = if (filters.releaseYear == "all") "" else ""","year":${filters.releaseYear}""" val yearString = if (filters.releaseYear == "all") "" else ""","year":${filters.releaseYear}"""
@ -234,12 +124,9 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
val sortByString = if (filters.sortBy == "update") "" else ""","sortBy":"${filters.sortBy}"""" val sortByString = if (filters.sortBy == "update") "" else ""","sortBy":"${filters.sortBy}""""
var variables = """{"search":{"allowAdult":false,"allowUnknown":false$seasonString$yearString$genresString$typesString$sortByString""" var variables = """{"search":{"allowAdult":false,"allowUnknown":false$seasonString$yearString$genresString$typesString$sortByString"""
variables += """},"limit":26,"page":$page,"translationType":"${preferences.getString("preferred_sub", "sub")!!}","countryOrigin":"${filters.origin}"}""" variables += """},"limit":$PAGE_SIZE,"page":$page,"translationType":"$subPref","countryOrigin":"${filters.origin}"}"""
val headers = headers.newBuilder() GET("$baseUrl/allanimeapi?variables=$variables&query=$SEARCH_QUERY", headers = headers)
.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0")
.build()
GET("$baseUrl/allanimeapi?variables=$variables&query=$searchQuery", headers = headers)
} }
} }
@ -263,10 +150,7 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
private fun animeDetailsRequestInternal(anime: SAnime): Request { private fun animeDetailsRequestInternal(anime: SAnime): Request {
val variables = """{"_id":"${anime.url.split("<&sep>").first()}"}""" val variables = """{"_id":"${anime.url.split("<&sep>").first()}"}"""
val headers = headers.newBuilder() return GET("$baseUrl/allanimeapi?variables=$variables&query=$DETAILS_QUERY", headers = headers)
.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0")
.build()
return GET("$baseUrl/allanimeapi?variables=$variables&query=$detailsQuery", headers = headers)
} }
override fun animeDetailsRequest(anime: SAnime): Request { override fun animeDetailsRequest(anime: SAnime): Request {
@ -276,73 +160,53 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} else { } else {
time time
} }
val siteUrl = preferences.getString("preferred_site_domain", "https://allanime.to")!! val siteUrl = preferences.getString(PREF_SITE_DOMAIN_KEY, PREF_SITE_DOMAIN_DEFAULT)!!
return GET("$siteUrl/anime/$id/$slug$slugTime") return GET("$siteUrl/anime/$id/$slug$slugTime")
} }
private fun animeDetailsParse(response: Response, animeOld: SAnime): SAnime { private fun animeDetailsParse(response: Response, animeOld: SAnime): SAnime {
val show = json.decodeFromString<DetailsResult>(response.body.string()).data.show val show = json.decodeFromString<DetailsResult>(response.body.string()).data.show
val anime = SAnime.create() return SAnime.create().apply {
title = animeOld.title
anime.title = animeOld.title genre = show.genres?.joinToString(separator = ", ") ?: ""
status = parseStatus(show.status)
anime.description = Jsoup.parse( author = show.studios?.firstOrNull()
show.description?.replace("<br>", "br2n") ?: "", description = Jsoup.parse(
).text().replace("br2n", "\n") + "\n\n" show.description?.replace("<br>", "br2n") ?: "",
anime.description += "Type: ${show.type ?: "Unknown"}" ).text().replace("br2n", "\n") +
anime.description += "\nAired: ${show.season?.quarter ?: "-"} ${show.season?.year ?: "-"}" "\n\n" +
anime.description += "\nScore: ${show.score ?: "-"}" "Type: ${show.type ?: "Unknown"}" +
"\nAired: ${show.season?.quarter ?: "-"} ${show.season?.year ?: "-"}" +
anime.genre = show.genres?.joinToString(separator = ", ") ?: "" "\nScore: ${show.score ?: "-"}"
anime.status = parseStatus(show.status)
if (show.studios?.isNotEmpty() == true) {
anime.author = show.studios.first()
} }
return anime
} }
// ============================== Episodes ============================== // ============================== Episodes ==============================
override fun episodeListRequest(anime: SAnime): Request { override fun episodeListRequest(anime: SAnime): Request {
val variables = """{"_id":"${anime.url.split("<&sep>").first()}"}""" val variables = """{"_id":"${anime.url.split("<&sep>").first()}"}"""
val headers = headers.newBuilder() return GET("$baseUrl/allanimeapi?variables=$variables&query=$EPISODES_QUERY", headers = headers)
.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0")
.build()
return GET("$baseUrl/allanimeapi?variables=$variables&query=$episodesQuery", headers = headers)
} }
override fun episodeListParse(response: Response): List<SEpisode> { override fun episodeListParse(response: Response): List<SEpisode> {
val subPref = preferences.getString(PREF_SUB_KEY, PREF_SUB_DEFAULT)!!
val medias = json.decodeFromString<SeriesResult>(response.body.string()) val medias = json.decodeFromString<SeriesResult>(response.body.string())
val episodeList = mutableListOf<SEpisode>()
val subOrDub = preferences.getString("preferred_sub", "sub")!! val episodesDetail = if (subPref == "sub") {
medias.data.show.availableEpisodesDetail.sub!!
if (subOrDub == "sub") {
for (ep in medias.data.show.availableEpisodesDetail.sub!!) {
val episode = SEpisode.create()
episode.episode_number = ep.toFloatOrNull() ?: 0F
val numName = ep.toIntOrNull() ?: (ep.toFloatOrNull() ?: "1")
episode.name = "Episode $numName (sub)"
val variables = """{"showId":"${medias.data.show._id}","translationType":"sub","episodeString":"$ep"}"""
episode.setUrlWithoutDomain("/allanimeapi?variables=$variables&query=$streamQuery")
episodeList.add(episode)
}
} else { } else {
for (ep in medias.data.show.availableEpisodesDetail.dub!!) { medias.data.show.availableEpisodesDetail.dub!!
val episode = SEpisode.create()
episode.episode_number = ep.toFloatOrNull() ?: 0F
val numName = ep.toIntOrNull() ?: (ep.toFloatOrNull() ?: "1")
episode.name = "Episode $numName (dub)"
val variables = """{"showId":"${medias.data.show._id}","translationType":"dub","episodeString":"$ep"}"""
episode.setUrlWithoutDomain("/allanimeapi?variables=$variables&query=$streamQuery")
episodeList.add(episode)
}
} }
return episodeList return episodesDetail.map { ep ->
val numName = ep.toIntOrNull() ?: (ep.toFloatOrNull() ?: "1")
val variables = """{"showId":"${medias.data.show._id}","translationType":"$subPref","episodeString":"$ep"}"""
SEpisode.create().apply {
episode_number = ep.toFloatOrNull() ?: 0F
name = "Episode $numName ($subPref)"
setUrlWithoutDomain("/allanimeapi?variables=$variables&query=$STREAMS_QUERY")
}
}
} }
// ============================ Video Links ============================= // ============================ Video Links =============================
@ -355,61 +219,47 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} }
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val body = response.body.string() val videoJson = json.decodeFromString<EpisodeResult>(response.body.string())
val videoJson = json.decodeFromString<EpisodeResult>(body)
val videoList = mutableListOf<Pair<Video, Float>>() val videoList = mutableListOf<Pair<Video, Float>>()
val serverList = mutableListOf<Server>() val serverList = mutableListOf<Server>()
val altHosterSelection = preferences.getStringSet( val altHosterSelection = preferences.getStringSet(
"alt_hoster_selection", PREF_ALT_HOSTER_KEY,
setOf("player", "vidstreaming", "okru", "mp4upload", "streamlare", "doodstream"), ALT_HOSTER_NAMES.toSet(),
)!! )!!
val hosterSelection = preferences.getStringSet( val hosterSelection = preferences.getStringSet(
"hoster_selection", PREF_HOSTER_KEY,
setOf("default", "ac", "luf-mp4", "si-hls", "s-mp4", "ac-hls"), PREF_HOSTER_DEFAULT,
)!! )!!
for (video in videoJson.data.episode.sourceUrls) { // list of alternative hosters
val mappings = listOf(
"streamsb" to listOf("streamsb"),
"vidstreaming" to listOf("vidstreaming", "https://gogo", "playgo1.cc"),
"doodstream" to listOf("dood"),
"okru" to listOf("ok.ru"),
"mp4upload" to listOf("mp4upload.com"),
"streamlare" to listOf("streamlare.com"),
)
videoJson.data.episode.sourceUrls.forEach { video ->
val matchingMapping = mappings.firstOrNull { (altHoster, urlMatches) ->
altHosterSelection.contains(altHoster) && video.sourceUrl.containsAny(urlMatches)
}
when { when {
video.sourceUrl.startsWith("/apivtwo/") && ( video.sourceUrl.startsWith("/apivtwo/") && INTERAL_HOSTER_NAMES.any {
(hosterSelection.contains("default") && video.sourceName.lowercase().contains("default")) || Regex("""\b${it.lowercase()}\b""").find(video.sourceName.lowercase()) != null &&
(hosterSelection.contains("ac") && video.sourceName.lowercase().contains("ac")) || hosterSelection.contains(it.lowercase())
(hosterSelection.contains("ak") && video.sourceName.lowercase().contains("ak")) || } -> {
(hosterSelection.contains("kir") && video.sourceName.lowercase().contains("kir")) ||
(hosterSelection.contains("luf-mp4") && video.sourceName.lowercase().contains("luf-mp4")) ||
(hosterSelection.contains("si-hls") && video.sourceName.lowercase().contains("si-hls")) ||
(hosterSelection.contains("s-mp4") && video.sourceName.lowercase().contains("s-mp4")) ||
(hosterSelection.contains("ac-hls") && video.sourceName.lowercase().contains("ac-hls")) ||
(hosterSelection.contains("uv-mp4") && video.sourceName.lowercase().contains("uv-mp4")) ||
(hosterSelection.contains("pn-hls") && video.sourceName.lowercase().contains("pn-hls"))
) -> {
serverList.add(Server(video.sourceUrl, "internal ${video.sourceName}", video.priority)) serverList.add(Server(video.sourceUrl, "internal ${video.sourceName}", video.priority))
} }
altHosterSelection.contains("player") && video.type == "player" -> { altHosterSelection.contains("player") && video.type == "player" -> {
serverList.add(Server(video.sourceUrl, "player", video.priority)) serverList.add(Server(video.sourceUrl, "player", video.priority))
} }
altHosterSelection.contains("streamsb") && video.sourceUrl.contains("streamsb") -> { matchingMapping != null -> {
serverList.add(Server(video.sourceUrl, "streamsb", video.priority)) serverList.add(Server(video.sourceUrl, matchingMapping.first, video.priority))
}
altHosterSelection.contains("vidstreaming") && (
video.sourceUrl.contains("vidstreaming") || video.sourceUrl.contains("https://gogo") ||
video.sourceUrl.contains("playgo1.cc")
) -> {
serverList.add(Server(video.sourceUrl, "gogo", video.priority))
}
altHosterSelection.contains("doodstream") && video.sourceUrl.contains("dood") -> {
serverList.add(Server(video.sourceUrl, "dood", video.priority))
}
altHosterSelection.contains("okru") && video.sourceUrl.contains("ok.ru") -> {
serverList.add(Server(video.sourceUrl, "okru", video.priority))
}
altHosterSelection.contains("mp4upload") && video.sourceUrl.contains("mp4upload.com") -> {
serverList.add(Server(video.sourceUrl, "mp4upload", video.priority))
}
altHosterSelection.contains("streamlare") && video.sourceUrl.contains("streamlare.com") -> {
serverList.add(Server(video.sourceUrl, "streamlare", video.priority))
} }
} }
} }
@ -421,78 +271,68 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
when { when {
sName.startsWith("internal ") -> { sName.startsWith("internal ") -> {
val extractor = AllAnimeExtractor(client) val extractor = AllAnimeExtractor(client)
val videos = runCatching { runCatching {
extractor.videoFromUrl(server.sourceUrl, server.sourceName) extractor.videoFromUrl(server.sourceUrl, server.sourceName)
}.getOrNull() ?: emptyList() }.getOrNull()?.map {
videos.map {
Pair(it, server.priority) Pair(it, server.priority)
} } ?: emptyList()
} }
sName == "player" -> { sName == "player" -> {
listOf( listOf(
Pair( Video(
Video( server.sourceUrl,
server.sourceUrl, "Original (player ${server.sourceName})",
"Original (player ${server.sourceName})", server.sourceUrl,
server.sourceUrl, ) to server.priority,
),
server.priority,
),
) )
} }
sName == "streamsb" -> { sName == "streamsb" -> {
val extractor = StreamSBExtractor(client) val extractor = StreamSBExtractor(client)
val videos = runCatching { runCatching {
extractor.videosFromUrl(server.sourceUrl, headers) extractor.videosFromUrl(server.sourceUrl, headers)
}.getOrNull() ?: emptyList() }.getOrNull()?.map {
videos.map {
Pair(it, server.priority) Pair(it, server.priority)
} } ?: emptyList()
} }
sName == "gogo" -> { sName == "gogo" -> {
val extractor = VidstreamingExtractor(client, json) val extractor = VidstreamingExtractor(client, json)
val videos = runCatching { runCatching {
extractor.videosFromUrl(server.sourceUrl.replace(Regex("^//"), "https://")) extractor.videosFromUrl(server.sourceUrl.replace(Regex("^//"), "https://"))
}.getOrNull() ?: emptyList() }.getOrNull()?.map {
videos.map {
Pair(it, server.priority) Pair(it, server.priority)
} } ?: emptyList()
} }
sName == "dood" -> { sName == "dood" -> {
val extractor = DoodExtractor(client) val extractor = DoodExtractor(client)
val videos = runCatching { runCatching {
extractor.videosFromUrl(server.sourceUrl) extractor.videosFromUrl(server.sourceUrl)
}.getOrNull() ?: emptyList() }.getOrNull()?.map {
videos.map {
Pair(it, server.priority) Pair(it, server.priority)
} } ?: emptyList()
} }
sName == "okru" -> { sName == "okru" -> {
val extractor = OkruExtractor(client) val extractor = OkruExtractor(client)
val videos = runCatching { runCatching {
extractor.videosFromUrl(server.sourceUrl) extractor.videosFromUrl(server.sourceUrl)
}.getOrNull() ?: emptyList() }.getOrNull()?.map {
videos.map {
Pair(it, server.priority) Pair(it, server.priority)
} } ?: emptyList()
} }
sName == "mp4upload" -> { sName == "mp4upload" -> {
val headers = headers.newBuilder().set("referer", "https://mp4upload.com/").build() val headers = headers.newBuilder().set("referer", "https://mp4upload.com/").build()
val videos = runCatching { runCatching {
Mp4uploadExtractor(client).getVideoFromUrl(server.sourceUrl, headers) Mp4uploadExtractor(client).getVideoFromUrl(server.sourceUrl, headers)
}.getOrNull() ?: emptyList() }.getOrNull()?.map {
videos.map {
Pair(it, server.priority) Pair(it, server.priority)
} } ?: emptyList()
} }
sName == "streamlare" -> { sName == "streamlare" -> {
val extractor = StreamlareExtractor(client) val extractor = StreamlareExtractor(client)
val videos = runCatching { runCatching {
extractor.videosFromUrl(server.sourceUrl) extractor.videosFromUrl(server.sourceUrl)
}.getOrNull() ?: emptyList() }.getOrNull()?.map {
videos.map {
Pair(it, server.priority) Pair(it, server.priority)
} } ?: emptyList()
} }
else -> null else -> null
} }
@ -506,15 +346,15 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================= Utilities ============================== // ============================= Utilities ==============================
private fun prioritySort(pList: List<Pair<Video, Float>>): List<Video> { private fun prioritySort(pList: List<Pair<Video, Float>>): List<Video> {
val prefServer = preferences.getString("preferred_server", "site_default")!! val prefServer = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
val quality = preferences.getString("preferred_quality", "1080")!! val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
val subOrDub = preferences.getString("preferred_sub", "sub")!! val subPref = preferences.getString(PREF_SUB_KEY, PREF_SUB_DEFAULT)!!
return pList.sortedWith( return pList.sortedWith(
compareBy( compareBy(
{ if (prefServer == "site_default") it.second else it.first.quality.contains(prefServer, true) }, { if (prefServer == "site_default") it.second else it.first.quality.contains(prefServer, true) },
{ it.first.quality.contains(quality, true) }, { it.first.quality.contains(quality, true) },
{ it.first.quality.contains(subOrDub, true) }, { it.first.quality.contains(subPref, true) },
), ),
).reversed().map { t -> t.first } ).reversed().map { t -> t.first }
} }
@ -542,7 +382,7 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
private fun parseAnime(response: Response): AnimesPage { private fun parseAnime(response: Response): AnimesPage {
val parsed = json.decodeFromString<SearchResult>(response.body.string()) val parsed = json.decodeFromString<SearchResult>(response.body.string())
val titleStyle = preferences.getString("preferred_title_style", "romaji")!! val titleStyle = preferences.getString(PREF_TITLE_STYLE_KEY, PREF_TITLE_STYLE_DEFAULT)!!
val animeList = parsed.data.shows.edges.map { ani -> val animeList = parsed.data.shows.edges.map { ani ->
SAnime.create().apply { SAnime.create().apply {
@ -556,16 +396,28 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} }
} }
return AnimesPage(animeList, animeList.size == 26) return AnimesPage(animeList, animeList.size == PAGE_SIZE)
} }
private fun String.containsAny(keywords: List<String>): Boolean {
return keywords.any { this.contains(it) }
}
// From Dopebox
private fun <A, B> Iterable<A>.parallelMap(f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val domainSitePref = ListPreference(screen.context).apply { val domainSitePref = ListPreference(screen.context).apply {
key = "preferred_site_domain" key = PREF_SITE_DOMAIN_KEY
title = "Preferred domain for site (requires app restart)" title = PREF_SITE_DOMAIN_TITLE
entries = arrayOf("allanime.to", "allanime.co") entries = PREF_SITE_DOMAIN_ENTRIES
entryValues = arrayOf("https://allanime.to", "https://allanime.co") entryValues = PREF_SITE_DOMAIN_ENTRY_VALUES
setDefaultValue("https://allanime.to") setDefaultValue(PREF_SITE_DOMAIN_DEFAULT)
summary = "%s" summary = "%s"
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
@ -576,11 +428,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} }
} }
val domainPref = ListPreference(screen.context).apply { val domainPref = ListPreference(screen.context).apply {
key = "preferred_domain" key = PREF_DOMAIN_KEY
title = "Preferred domain (requires app restart)" title = PREF_DOMAIN_TITLE
entries = arrayOf("api.allanime.to", "api.allanime.co") entries = PREF_DOMAIN_ENTRIES
entryValues = arrayOf("https://api.allanime.to", "https://api.allanime.co") entryValues = PREF_DOMAIN_ENTRY_VALUES
setDefaultValue("https://api.allanime.to") setDefaultValue(PREF_DOMAIN_DEFAULT)
summary = "%s" summary = "%s"
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
@ -592,11 +444,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} }
val serverPref = ListPreference(screen.context).apply { val serverPref = ListPreference(screen.context).apply {
key = "preferred_server" key = PREF_SERVER_KEY
title = "Preferred Video Server" title = PREF_SERVER_TITLE
entries = arrayOf("Site Default", "Ac", "Luf-mp4", "Vid-mp4", "Yt-mp4", "Ok.ru", "Mp4upload", "Sl-mp4", "Uv-mp4", "S-mp4", "Ac-Hls", "Default") entries = PREF_SERVER_ENTRIES
entryValues = arrayOf("site_default", "ac", "luf-mp4", "vid-mp4", "yt-mp4", "okru", "mp4upload", "sl-mp4", "uv-mp4", "s-mp4", "ac-hls", "default") entryValues = PREF_SERVER_ENTRY_VALUES
setDefaultValue("site_default") setDefaultValue(PREF_SERVER_DEFAULT)
summary = "%s" summary = "%s"
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
@ -608,11 +460,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} }
val hostSelection = MultiSelectListPreference(screen.context).apply { val hostSelection = MultiSelectListPreference(screen.context).apply {
key = "hoster_selection" key = PREF_HOSTER_KEY
title = "Enable/Disable Hosts" title = PREF_HOSTER_TITLE
entries = arrayOf("Default", "Ac", "Ak", "Kir", "Luf-mp4", "Si-Hls", "S-mp4", "Ac-Hls", "Uv-mp4", "Pn-Hls") entries = PREF_HOSTER_ENTRIES
entryValues = arrayOf("default", "ac", "ak", "kir", "luf-mp4", "si-hls", "s-mp4", "ac-hls", "uv-mp4", "pn-hls") entryValues = PREF_HOSTER_ENTRY_VALUES
setDefaultValue(setOf("default", "ac", "ak", "kir", "luf-mp4", "si-hls", "s-mp4", "ac-hls")) setDefaultValue(PREF_HOSTER_DEFAULT)
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit() preferences.edit().putStringSet(key, newValue as Set<String>).commit()
@ -620,11 +472,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} }
val altHostSelection = MultiSelectListPreference(screen.context).apply { val altHostSelection = MultiSelectListPreference(screen.context).apply {
key = "alt_hoster_selection" key = PREF_ALT_HOSTER_KEY
title = "Enable/Disable Alternative Hosts" title = PREF_ALT_HOSTER_TITLE
entries = arrayOf("Direct Player", "Vidstreaming/Gogo", "Ok.ru", "Mp4upload.com", "Streamlare.com", "StreamSB", "Doodstream") entries = ALT_HOSTER_NAMES
entryValues = arrayOf("player", "vidstreaming", "okru", "mp4upload", "streamlare", "streamsb", "doodstream") entryValues = ALT_HOSTER_NAMES
setDefaultValue(setOf("player", "vidstreaming", "okru", "mp4upload", "streamlare", "doodstream")) setDefaultValue(ALT_HOSTER_NAMES.toSet())
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit() preferences.edit().putStringSet(key, newValue as Set<String>).commit()
@ -632,11 +484,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} }
val videoQualityPref = ListPreference(screen.context).apply { val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality" key = PREF_QUALITY_KEY
title = "Preferred quality" title = PREF_QUALITY_TITLE
entries = arrayOf("1080p", "720p", "480p", "360p", "240p", "80p", "1440p (okru only)", "2160p (okru only)") entries = PREF_QUALITY_ENTRIES
entryValues = arrayOf("1080", "720", "480", "360", "240", "80", "1440", "2160") entryValues = PREF_QUALITY_ENTRY_VALUES
setDefaultValue("1080") setDefaultValue(PREF_QUALITY_DEFAULT)
summary = "%s" summary = "%s"
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
@ -648,11 +500,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} }
val titleStylePref = ListPreference(screen.context).apply { val titleStylePref = ListPreference(screen.context).apply {
key = "preferred_title_style" key = PREF_TITLE_STYLE_KEY
title = "Preferred Title Style" title = PREF_TITLE_STYLE_TITLE
entries = arrayOf("Romaji", "English", "Native") entries = PREF_TITLE_STYLE_ENTRIES
entryValues = arrayOf("romaji", "eng", "native") entryValues = PREF_TITLE_STYLE_ENTRY_VALUES
setDefaultValue("romaji") setDefaultValue(PREF_TITLE_STYLE_DEFAULT)
summary = "%s" summary = "%s"
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
@ -664,11 +516,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} }
val subPref = ListPreference(screen.context).apply { val subPref = ListPreference(screen.context).apply {
key = "preferred_sub" key = PREF_SUB_KEY
title = "Prefer subs or dubs?" title = PREF_SUB_TITLE
entries = arrayOf("Subs", "Dubs") entries = PREF_SUB_ENTRIES
entryValues = arrayOf("sub", "dub") entryValues = PREF_SUB_ENTRY_VALUES
setDefaultValue("sub") setDefaultValue(PREF_SUB_DEFAULT)
summary = "%s" summary = "%s"
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
@ -689,9 +541,85 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
screen.addPreference(subPref) screen.addPreference(subPref)
} }
// From Dopebox companion object {
private fun <A, B> Iterable<A>.parallelMap(f: suspend (A) -> B): List<B> = private const val PAGE_SIZE = 26 // number of items to retrieve when calling API
runBlocking { private val INTERAL_HOSTER_NAMES = arrayOf(
map { async(Dispatchers.Default) { f(it) } }.awaitAll() "Default", "Ac", "Ak", "Kir", "Luf-mp4",
} "Si-Hls", "S-mp4", "Ac-Hls", "Uv-mp4", "Pn-Hls",
)
private val ALT_HOSTER_NAMES = arrayOf(
"player",
"vidstreaming",
"okru",
"mp4upload",
"streamlare",
"streamsb",
"doodstream",
)
private const val PREF_SITE_DOMAIN_KEY = "preferred_site_domain"
private const val PREF_SITE_DOMAIN_TITLE = "Preferred domain for site (requires app restart)"
private val PREF_SITE_DOMAIN_ENTRIES = arrayOf("allanime.to", "allanime.co")
private val PREF_SITE_DOMAIN_ENTRY_VALUES = PREF_SITE_DOMAIN_ENTRIES.map { "https://$it" }.toTypedArray()
private const val PREF_SITE_DOMAIN_DEFAULT = "https://allanime.to"
private const val PREF_DOMAIN_KEY = "preferred_domain"
private const val PREF_DOMAIN_TITLE = "Preferred domain (requires app restart)"
private val PREF_DOMAIN_ENTRIES = arrayOf("api.allanime.to", "api.allanime.co")
private val PREF_DOMAIN_ENTRY_VALUES = PREF_DOMAIN_ENTRIES.map { "https://$it" }.toTypedArray()
private const val PREF_DOMAIN_DEFAULT = "https://api.allanime.to"
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_TITLE = "Preferred Video Server"
private val PREF_SERVER_ENTRIES = arrayOf("Site Default") +
INTERAL_HOSTER_NAMES.sliceArray(1 until INTERAL_HOSTER_NAMES.size) +
ALT_HOSTER_NAMES
private val PREF_SERVER_ENTRY_VALUES = arrayOf("site_default") +
INTERAL_HOSTER_NAMES.sliceArray(1 until INTERAL_HOSTER_NAMES.size).map {
it.lowercase()
}.toTypedArray() +
ALT_HOSTER_NAMES
private const val PREF_SERVER_DEFAULT = "site_default"
private const val PREF_HOSTER_KEY = "hoster_selection"
private const val PREF_HOSTER_TITLE = "Enable/Disable Hosts"
private val PREF_HOSTER_ENTRIES = INTERAL_HOSTER_NAMES
private val PREF_HOSTER_ENTRY_VALUES = INTERAL_HOSTER_NAMES.map {
it.lowercase()
}.toTypedArray()
private val PREF_HOSTER_DEFAULT = setOf("default", "ac", "ak", "kir", "luf-mp4", "si-hls", "s-mp4", "ac-hls")
private const val PREF_ALT_HOSTER_KEY = "alt_hoster_selection"
private const val PREF_ALT_HOSTER_TITLE = "Enable/Disable Alternative Hosts"
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Preferred quality"
private val PREF_QUALITY_ENTRIES = arrayOf(
"1080p",
"720p",
"480p",
"360p",
"240p",
"80p",
"1440p (okru only)",
"2160p (okru only)",
)
private val PREF_QUALITY_ENTRY_VALUES = PREF_QUALITY_ENTRIES.map {
it.substringBefore("p")
}.toTypedArray()
private const val PREF_QUALITY_DEFAULT = "1080"
private const val PREF_TITLE_STYLE_KEY = "preferred_title_style"
private const val PREF_TITLE_STYLE_TITLE = "Preferred Title Style"
private val PREF_TITLE_STYLE_ENTRIES = arrayOf("Romaji", "English", "Native")
private val PREF_TITLE_STYLE_ENTRY_VALUES = arrayOf("romaji", "eng", "native")
private const val PREF_TITLE_STYLE_DEFAULT = "romaji"
private const val PREF_SUB_KEY = "preferred_sub"
private const val PREF_SUB_TITLE = "Prefer subs or dubs?"
private val PREF_SUB_ENTRIES = arrayOf("Subs", "Dubs")
private val PREF_SUB_ENTRY_VALUES = arrayOf("sub", "dub")
private const val PREF_SUB_DEFAULT = "sub"
}
} }

View File

@ -0,0 +1,119 @@
package eu.kanade.tachiyomi.animeextension.en.allanime
fun buildQuery(queryAction: () -> String): String {
return queryAction()
.trimIndent()
.replace("%", "$")
}
val POPULAR_QUERY: String = buildQuery {
"""
query(
%type: VaildPopularTypeEnumType!
%size: Int!
%page: Int
%dateRange: Int
) {
queryPopular(
type: %type
size: %size
dateRange: %dateRange
page: %page
) {
total
recommendations {
anyCard {
_id
name
thumbnail
englishName
nativeName
slugTime
}
}
}
}
"""
}
val SEARCH_QUERY: String = buildQuery {
"""
query(
%search: SearchInput
%limit: Int
%page: Int
%translationType: VaildTranslationTypeEnumType
%countryOrigin: VaildCountryOriginEnumType
) {
shows(
search: %search
limit: %limit
page: %page
translationType: %translationType
countryOrigin: %countryOrigin
) {
pageInfo {
total
}
edges {
_id
name
thumbnail
englishName
nativeName
slugTime
}
}
}
"""
}
val DETAILS_QUERY = buildQuery {
"""
query (%_id: String!) {
show(
_id: %_id
) {
thumbnail
description
type
season
score
genres
status
studios
}
}
"""
}
val EPISODES_QUERY = buildQuery {
"""
query (%_id: String!) {
show(
_id: %_id
) {
_id
availableEpisodesDetail
}
}
"""
}
val STREAMS_QUERY = buildQuery {
"""
query(
%showId: String!,
%translationType: VaildTranslationTypeEnumType!,
%episodeString: String!
) {
episode(
showId: %showId
translationType: %translationType
episodeString: %episodeString
) {
sourceUrls
}
}
"""
}

View File

@ -104,37 +104,27 @@ class AllAnimeExtractor(private val client: OkHttpClient) {
for (link in linkJson.links) { for (link in linkJson.links) {
val subtitles = mutableListOf<Track>() val subtitles = mutableListOf<Track>()
if (!link.subtitles.isNullOrEmpty()) { if (!link.subtitles.isNullOrEmpty()) {
try { subtitles.addAll(
for (sub in link.subtitles) { link.subtitles.map { sub ->
val label = if (sub.label != null) { val label = if (sub.label != null) {
" - ${sub.label}" " - ${sub.label}"
} else { } else {
"" ""
} }
subtitles.add(Track(sub.src, Locale(sub.lang).displayLanguage + label)) Track(sub.src, Locale(sub.lang).displayLanguage + label)
} }
} catch (_: Error) {} )
} }
if (link.mp4 == true) { if (link.mp4 == true) {
try { videoList.add(
videoList.add( Video(
Video( link.link,
link.link, "Original ($name - ${link.resolutionStr})",
"Original ($name - ${link.resolutionStr})", link.link,
link.link, subtitleTracks = subtitles,
subtitleTracks = subtitles, ),
), )
)
} catch (_: Error) {
videoList.add(
Video(
link.link,
"Original ($name - ${link.resolutionStr})",
link.link,
),
)
}
} else if (link.hls == true) { } else if (link.hls == true) {
val newClient = OkHttpClient() val newClient = OkHttpClient()
val resp = runCatching { val resp = runCatching {
@ -168,14 +158,10 @@ class AllAnimeExtractor(private val client: OkHttpClient) {
"User-Agent", "User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0",
) )
return try { return if (audioList.isEmpty()) {
if (audioList.isEmpty()) { listOf(Video(link.link, "$name - ${link.resolutionStr}", link.link, subtitleTracks = subtitles, headers = headers))
listOf(Video(link.link, "$name - ${link.resolutionStr}", link.link, subtitleTracks = subtitles, headers = headers)) } else {
} else { listOf(Video(link.link, "$name - ${link.resolutionStr}", link.link, subtitleTracks = subtitles, audioTracks = audioList, headers = headers))
listOf(Video(link.link, "$name - ${link.resolutionStr}", link.link, subtitleTracks = subtitles, audioTracks = audioList, headers = headers))
}
} catch (_: Error) {
listOf(Video(link.link, "$name - ${link.resolutionStr}", link.link, headers = headers))
} }
} }
@ -194,38 +180,24 @@ class AllAnimeExtractor(private val client: OkHttpClient) {
videoUrl = resp.request.url.toString().substringBeforeLast("/") + "/$videoUrl" videoUrl = resp.request.url.toString().substringBeforeLast("/") + "/$videoUrl"
} }
try { if (audioList.isEmpty()) {
if (audioList.isEmpty()) { videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles))
videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles)) } else {
} else { videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles, audioTracks = audioList))
videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles, audioTracks = audioList))
}
} catch (_: Error) {
videoList.add(Video(videoUrl, quality, videoUrl))
} }
} }
} }
} else if (link.crIframe == true) { } else if (link.crIframe == true) {
link.portData!!.streams.forEach { link.portData!!.streams.forEach {
if (it.format == "adaptive_dash") { if (it.format == "adaptive_dash") {
try { videoList.add(
videoList.add( Video(
Video( it.url,
it.url, "Original (AC - Dash${if (it.hardsub_lang.isEmpty()) "" else " - Hardsub: ${it.hardsub_lang}"})",
"Original (AC - Dash${if (it.hardsub_lang.isEmpty()) "" else " - Hardsub: ${it.hardsub_lang}"})", it.url,
it.url, subtitleTracks = subtitles,
subtitleTracks = subtitles, ),
), )
)
} catch (a: Error) {
videoList.add(
Video(
it.url,
"Original (AC - Dash${if (it.hardsub_lang.isEmpty()) "" else " - Hardsub: ${it.hardsub_lang}"})",
it.url,
),
)
}
} else if (it.format == "adaptive_hls") { } else if (it.format == "adaptive_hls") {
val resp = runCatching { val resp = runCatching {
client.newCall( client.newCall(
@ -240,11 +212,7 @@ class AllAnimeExtractor(private val client: OkHttpClient) {
val quality = t.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p (AC - HLS${if (it.hardsub_lang.isEmpty()) "" else " - Hardsub: ${it.hardsub_lang}"})" val quality = t.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p (AC - HLS${if (it.hardsub_lang.isEmpty()) "" else " - Hardsub: ${it.hardsub_lang}"})"
var videoUrl = t.substringAfter("\n").substringBefore("\n") var videoUrl = t.substringAfter("\n").substringBefore("\n")
try { videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles))
videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles))
} catch (_: Error) {
videoList.add(Video(videoUrl, quality, videoUrl))
}
} }
} }
} }