[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.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import okhttp3.Request
@ -40,8 +39,7 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "AllAnime"
// allanime.to
override val baseUrl by lazy { preferences.getString("preferred_domain", "https://api.allanime.to")!! }
override val baseUrl by lazy { preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)!! }
override val lang = "en"
@ -51,108 +49,6 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
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 {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
@ -160,18 +56,15 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request {
val variables = """{"type":"anime","size":26,"dateRange":7,"page":$page}"""
val headers = headers.newBuilder()
.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)
val variables = """{"type":"anime","size":$PAGE_SIZE,"dateRange":7,"page":$page}"""
return GET("$baseUrl/allanimeapi?variables=$variables&query=$POPULAR_QUERY", headers = headers)
}
override fun popularAnimeParse(response: Response): AnimesPage {
val parsed = json.decodeFromString<PopularResult>(response.body.string())
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 {
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 ===============================
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"}"""
val headers = headers.newBuilder()
.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=$searchQuery", headers = headers)
// Could be lazily loaded along with url, but would require user to restart
val subPref = preferences.getString(PREF_SUB_KEY, PREF_SUB_DEFAULT)!!
val variables = """{"search":{"allowAdult":false,"allowUnknown":false},"limit":$PAGE_SIZE,"page":$page,"translationType":"$subPref","countryOrigin":"ALL"}"""
return GET("$baseUrl/allanimeapi?variables=$variables&query=$SEARCH_QUERY", headers = headers)
}
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")
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()) {
val variables = """{"search":{"query":"$query","allowAdult":false,"allowUnknown":false},"limit":26,"page":$page,"translationType":"${preferences.getString("preferred_sub", "sub")!!}","countryOrigin":"ALL"}"""
val headers = headers.newBuilder()
.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)
val variables = """{"search":{"query":"$query","allowAdult":false,"allowUnknown":false},"limit":$PAGE_SIZE,"page":$page,"translationType":"$subPref","countryOrigin":"ALL"}"""
GET("$baseUrl/allanimeapi?variables=$variables&query=$SEARCH_QUERY", headers = headers)
} else {
val seasonString = if (filters.season == "all") "" else ""","season":"${filters.season}""""
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}""""
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()
.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)
GET("$baseUrl/allanimeapi?variables=$variables&query=$SEARCH_QUERY", headers = headers)
}
}
@ -263,10 +150,7 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
private fun animeDetailsRequestInternal(anime: SAnime): Request {
val variables = """{"_id":"${anime.url.split("<&sep>").first()}"}"""
val headers = headers.newBuilder()
.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)
return GET("$baseUrl/allanimeapi?variables=$variables&query=$DETAILS_QUERY", headers = headers)
}
override fun animeDetailsRequest(anime: SAnime): Request {
@ -276,73 +160,53 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
} else {
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")
}
private fun animeDetailsParse(response: Response, animeOld: SAnime): SAnime {
val show = json.decodeFromString<DetailsResult>(response.body.string()).data.show
val anime = SAnime.create()
anime.title = animeOld.title
anime.description = Jsoup.parse(
show.description?.replace("<br>", "br2n") ?: "",
).text().replace("br2n", "\n") + "\n\n"
anime.description += "Type: ${show.type ?: "Unknown"}"
anime.description += "\nAired: ${show.season?.quarter ?: "-"} ${show.season?.year ?: "-"}"
anime.description += "\nScore: ${show.score ?: "-"}"
anime.genre = show.genres?.joinToString(separator = ", ") ?: ""
anime.status = parseStatus(show.status)
if (show.studios?.isNotEmpty() == true) {
anime.author = show.studios.first()
return SAnime.create().apply {
title = animeOld.title
genre = show.genres?.joinToString(separator = ", ") ?: ""
status = parseStatus(show.status)
author = show.studios?.firstOrNull()
description = Jsoup.parse(
show.description?.replace("<br>", "br2n") ?: "",
).text().replace("br2n", "\n") +
"\n\n" +
"Type: ${show.type ?: "Unknown"}" +
"\nAired: ${show.season?.quarter ?: "-"} ${show.season?.year ?: "-"}" +
"\nScore: ${show.score ?: "-"}"
}
return anime
}
// ============================== Episodes ==============================
override fun episodeListRequest(anime: SAnime): Request {
val variables = """{"_id":"${anime.url.split("<&sep>").first()}"}"""
val headers = headers.newBuilder()
.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)
return GET("$baseUrl/allanimeapi?variables=$variables&query=$EPISODES_QUERY", headers = headers)
}
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 episodeList = mutableListOf<SEpisode>()
val subOrDub = preferences.getString("preferred_sub", "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)
}
val episodesDetail = if (subPref == "sub") {
medias.data.show.availableEpisodesDetail.sub!!
} else {
for (ep in 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)
}
medias.data.show.availableEpisodesDetail.dub!!
}
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 =============================
@ -355,61 +219,47 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun videoListParse(response: Response): List<Video> {
val body = response.body.string()
val videoJson = json.decodeFromString<EpisodeResult>(body)
val videoJson = json.decodeFromString<EpisodeResult>(response.body.string())
val videoList = mutableListOf<Pair<Video, Float>>()
val serverList = mutableListOf<Server>()
val altHosterSelection = preferences.getStringSet(
"alt_hoster_selection",
setOf("player", "vidstreaming", "okru", "mp4upload", "streamlare", "doodstream"),
PREF_ALT_HOSTER_KEY,
ALT_HOSTER_NAMES.toSet(),
)!!
val hosterSelection = preferences.getStringSet(
"hoster_selection",
setOf("default", "ac", "luf-mp4", "si-hls", "s-mp4", "ac-hls"),
PREF_HOSTER_KEY,
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 {
video.sourceUrl.startsWith("/apivtwo/") && (
(hosterSelection.contains("default") && video.sourceName.lowercase().contains("default")) ||
(hosterSelection.contains("ac") && video.sourceName.lowercase().contains("ac")) ||
(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"))
) -> {
video.sourceUrl.startsWith("/apivtwo/") && INTERAL_HOSTER_NAMES.any {
Regex("""\b${it.lowercase()}\b""").find(video.sourceName.lowercase()) != null &&
hosterSelection.contains(it.lowercase())
} -> {
serverList.add(Server(video.sourceUrl, "internal ${video.sourceName}", video.priority))
}
altHosterSelection.contains("player") && video.type == "player" -> {
serverList.add(Server(video.sourceUrl, "player", video.priority))
}
altHosterSelection.contains("streamsb") && video.sourceUrl.contains("streamsb") -> {
serverList.add(Server(video.sourceUrl, "streamsb", 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))
matchingMapping != null -> {
serverList.add(Server(video.sourceUrl, matchingMapping.first, video.priority))
}
}
}
@ -421,78 +271,68 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
when {
sName.startsWith("internal ") -> {
val extractor = AllAnimeExtractor(client)
val videos = runCatching {
runCatching {
extractor.videoFromUrl(server.sourceUrl, server.sourceName)
}.getOrNull() ?: emptyList()
videos.map {
}.getOrNull()?.map {
Pair(it, server.priority)
}
} ?: emptyList()
}
sName == "player" -> {
listOf(
Pair(
Video(
server.sourceUrl,
"Original (player ${server.sourceName})",
server.sourceUrl,
),
server.priority,
),
Video(
server.sourceUrl,
"Original (player ${server.sourceName})",
server.sourceUrl,
) to server.priority,
)
}
sName == "streamsb" -> {
val extractor = StreamSBExtractor(client)
val videos = runCatching {
runCatching {
extractor.videosFromUrl(server.sourceUrl, headers)
}.getOrNull() ?: emptyList()
videos.map {
}.getOrNull()?.map {
Pair(it, server.priority)
}
} ?: emptyList()
}
sName == "gogo" -> {
val extractor = VidstreamingExtractor(client, json)
val videos = runCatching {
runCatching {
extractor.videosFromUrl(server.sourceUrl.replace(Regex("^//"), "https://"))
}.getOrNull() ?: emptyList()
videos.map {
}.getOrNull()?.map {
Pair(it, server.priority)
}
} ?: emptyList()
}
sName == "dood" -> {
val extractor = DoodExtractor(client)
val videos = runCatching {
runCatching {
extractor.videosFromUrl(server.sourceUrl)
}.getOrNull() ?: emptyList()
videos.map {
}.getOrNull()?.map {
Pair(it, server.priority)
}
} ?: emptyList()
}
sName == "okru" -> {
val extractor = OkruExtractor(client)
val videos = runCatching {
runCatching {
extractor.videosFromUrl(server.sourceUrl)
}.getOrNull() ?: emptyList()
videos.map {
}.getOrNull()?.map {
Pair(it, server.priority)
}
} ?: emptyList()
}
sName == "mp4upload" -> {
val headers = headers.newBuilder().set("referer", "https://mp4upload.com/").build()
val videos = runCatching {
runCatching {
Mp4uploadExtractor(client).getVideoFromUrl(server.sourceUrl, headers)
}.getOrNull() ?: emptyList()
videos.map {
}.getOrNull()?.map {
Pair(it, server.priority)
}
} ?: emptyList()
}
sName == "streamlare" -> {
val extractor = StreamlareExtractor(client)
val videos = runCatching {
runCatching {
extractor.videosFromUrl(server.sourceUrl)
}.getOrNull() ?: emptyList()
videos.map {
}.getOrNull()?.map {
Pair(it, server.priority)
}
} ?: emptyList()
}
else -> null
}
@ -506,15 +346,15 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================= Utilities ==============================
private fun prioritySort(pList: List<Pair<Video, Float>>): List<Video> {
val prefServer = preferences.getString("preferred_server", "site_default")!!
val quality = preferences.getString("preferred_quality", "1080")!!
val subOrDub = preferences.getString("preferred_sub", "sub")!!
val prefServer = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
val subPref = preferences.getString(PREF_SUB_KEY, PREF_SUB_DEFAULT)!!
return pList.sortedWith(
compareBy(
{ if (prefServer == "site_default") it.second else it.first.quality.contains(prefServer, true) },
{ it.first.quality.contains(quality, true) },
{ it.first.quality.contains(subOrDub, true) },
{ it.first.quality.contains(subPref, true) },
),
).reversed().map { t -> t.first }
}
@ -542,7 +382,7 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
private fun parseAnime(response: Response): AnimesPage {
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 ->
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) {
val domainSitePref = ListPreference(screen.context).apply {
key = "preferred_site_domain"
title = "Preferred domain for site (requires app restart)"
entries = arrayOf("allanime.to", "allanime.co")
entryValues = arrayOf("https://allanime.to", "https://allanime.co")
setDefaultValue("https://allanime.to")
key = PREF_SITE_DOMAIN_KEY
title = PREF_SITE_DOMAIN_TITLE
entries = PREF_SITE_DOMAIN_ENTRIES
entryValues = PREF_SITE_DOMAIN_ENTRY_VALUES
setDefaultValue(PREF_SITE_DOMAIN_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
@ -576,11 +428,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
}
}
val domainPref = ListPreference(screen.context).apply {
key = "preferred_domain"
title = "Preferred domain (requires app restart)"
entries = arrayOf("api.allanime.to", "api.allanime.co")
entryValues = arrayOf("https://api.allanime.to", "https://api.allanime.co")
setDefaultValue("https://api.allanime.to")
key = PREF_DOMAIN_KEY
title = PREF_DOMAIN_TITLE
entries = PREF_DOMAIN_ENTRIES
entryValues = PREF_DOMAIN_ENTRY_VALUES
setDefaultValue(PREF_DOMAIN_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
@ -592,11 +444,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
}
val serverPref = ListPreference(screen.context).apply {
key = "preferred_server"
title = "Preferred Video Server"
entries = arrayOf("Site Default", "Ac", "Luf-mp4", "Vid-mp4", "Yt-mp4", "Ok.ru", "Mp4upload", "Sl-mp4", "Uv-mp4", "S-mp4", "Ac-Hls", "Default")
entryValues = arrayOf("site_default", "ac", "luf-mp4", "vid-mp4", "yt-mp4", "okru", "mp4upload", "sl-mp4", "uv-mp4", "s-mp4", "ac-hls", "default")
setDefaultValue("site_default")
key = PREF_SERVER_KEY
title = PREF_SERVER_TITLE
entries = PREF_SERVER_ENTRIES
entryValues = PREF_SERVER_ENTRY_VALUES
setDefaultValue(PREF_SERVER_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
@ -608,11 +460,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
}
val hostSelection = MultiSelectListPreference(screen.context).apply {
key = "hoster_selection"
title = "Enable/Disable Hosts"
entries = arrayOf("Default", "Ac", "Ak", "Kir", "Luf-mp4", "Si-Hls", "S-mp4", "Ac-Hls", "Uv-mp4", "Pn-Hls")
entryValues = arrayOf("default", "ac", "ak", "kir", "luf-mp4", "si-hls", "s-mp4", "ac-hls", "uv-mp4", "pn-hls")
setDefaultValue(setOf("default", "ac", "ak", "kir", "luf-mp4", "si-hls", "s-mp4", "ac-hls"))
key = PREF_HOSTER_KEY
title = PREF_HOSTER_TITLE
entries = PREF_HOSTER_ENTRIES
entryValues = PREF_HOSTER_ENTRY_VALUES
setDefaultValue(PREF_HOSTER_DEFAULT)
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
@ -620,11 +472,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
}
val altHostSelection = MultiSelectListPreference(screen.context).apply {
key = "alt_hoster_selection"
title = "Enable/Disable Alternative Hosts"
entries = arrayOf("Direct Player", "Vidstreaming/Gogo", "Ok.ru", "Mp4upload.com", "Streamlare.com", "StreamSB", "Doodstream")
entryValues = arrayOf("player", "vidstreaming", "okru", "mp4upload", "streamlare", "streamsb", "doodstream")
setDefaultValue(setOf("player", "vidstreaming", "okru", "mp4upload", "streamlare", "doodstream"))
key = PREF_ALT_HOSTER_KEY
title = PREF_ALT_HOSTER_TITLE
entries = ALT_HOSTER_NAMES
entryValues = ALT_HOSTER_NAMES
setDefaultValue(ALT_HOSTER_NAMES.toSet())
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
@ -632,11 +484,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
}
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "240p", "80p", "1440p (okru only)", "2160p (okru only)")
entryValues = arrayOf("1080", "720", "480", "360", "240", "80", "1440", "2160")
setDefaultValue("1080")
key = PREF_QUALITY_KEY
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
entryValues = PREF_QUALITY_ENTRY_VALUES
setDefaultValue(PREF_QUALITY_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
@ -648,11 +500,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
}
val titleStylePref = ListPreference(screen.context).apply {
key = "preferred_title_style"
title = "Preferred Title Style"
entries = arrayOf("Romaji", "English", "Native")
entryValues = arrayOf("romaji", "eng", "native")
setDefaultValue("romaji")
key = PREF_TITLE_STYLE_KEY
title = PREF_TITLE_STYLE_TITLE
entries = PREF_TITLE_STYLE_ENTRIES
entryValues = PREF_TITLE_STYLE_ENTRY_VALUES
setDefaultValue(PREF_TITLE_STYLE_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
@ -664,11 +516,11 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
}
val subPref = ListPreference(screen.context).apply {
key = "preferred_sub"
title = "Prefer subs or dubs?"
entries = arrayOf("Subs", "Dubs")
entryValues = arrayOf("sub", "dub")
setDefaultValue("sub")
key = PREF_SUB_KEY
title = PREF_SUB_TITLE
entries = PREF_SUB_ENTRIES
entryValues = PREF_SUB_ENTRY_VALUES
setDefaultValue(PREF_SUB_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
@ -689,9 +541,85 @@ class AllAnime : ConfigurableAnimeSource, AnimeHttpSource() {
screen.addPreference(subPref)
}
// From Dopebox
private fun <A, B> Iterable<A>.parallelMap(f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
companion object {
private const val PAGE_SIZE = 26 // number of items to retrieve when calling API
private val INTERAL_HOSTER_NAMES = arrayOf(
"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) {
val subtitles = mutableListOf<Track>()
if (!link.subtitles.isNullOrEmpty()) {
try {
for (sub in link.subtitles) {
subtitles.addAll(
link.subtitles.map { sub ->
val label = if (sub.label != null) {
" - ${sub.label}"
} 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) {
try {
videoList.add(
Video(
link.link,
"Original ($name - ${link.resolutionStr})",
link.link,
subtitleTracks = subtitles,
),
)
} catch (_: Error) {
videoList.add(
Video(
link.link,
"Original ($name - ${link.resolutionStr})",
link.link,
),
)
}
videoList.add(
Video(
link.link,
"Original ($name - ${link.resolutionStr})",
link.link,
subtitleTracks = subtitles,
),
)
} else if (link.hls == true) {
val newClient = OkHttpClient()
val resp = runCatching {
@ -168,14 +158,10 @@ class AllAnimeExtractor(private val client: OkHttpClient) {
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0",
)
return try {
if (audioList.isEmpty()) {
listOf(Video(link.link, "$name - ${link.resolutionStr}", link.link, subtitleTracks = subtitles, headers = headers))
} else {
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))
return if (audioList.isEmpty()) {
listOf(Video(link.link, "$name - ${link.resolutionStr}", link.link, subtitleTracks = subtitles, headers = headers))
} else {
listOf(Video(link.link, "$name - ${link.resolutionStr}", link.link, subtitleTracks = subtitles, audioTracks = audioList, headers = headers))
}
}
@ -194,38 +180,24 @@ class AllAnimeExtractor(private val client: OkHttpClient) {
videoUrl = resp.request.url.toString().substringBeforeLast("/") + "/$videoUrl"
}
try {
if (audioList.isEmpty()) {
videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles))
} else {
videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles, audioTracks = audioList))
}
} catch (_: Error) {
videoList.add(Video(videoUrl, quality, videoUrl))
if (audioList.isEmpty()) {
videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles))
} else {
videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles, audioTracks = audioList))
}
}
}
} else if (link.crIframe == true) {
link.portData!!.streams.forEach {
if (it.format == "adaptive_dash") {
try {
videoList.add(
Video(
it.url,
"Original (AC - Dash${if (it.hardsub_lang.isEmpty()) "" else " - Hardsub: ${it.hardsub_lang}"})",
it.url,
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,
),
)
}
videoList.add(
Video(
it.url,
"Original (AC - Dash${if (it.hardsub_lang.isEmpty()) "" else " - Hardsub: ${it.hardsub_lang}"})",
it.url,
subtitleTracks = subtitles,
),
)
} else if (it.format == "adaptive_hls") {
val resp = runCatching {
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}"})"
var videoUrl = t.substringAfter("\n").substringBefore("\n")
try {
videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles))
} catch (_: Error) {
videoList.add(Video(videoUrl, quality, videoUrl))
}
videoList.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subtitles))
}
}
}