fix(Yomiroll): Fix coroutine bug in episodes list (#1568)
* fix: Fix parallel episode list * refactor: Remove (now) useless preference * refactor: Minor refactoration * chore: Bump version
This commit is contained in:
@ -1,12 +1,14 @@
|
|||||||
apply plugin: 'com.android.application'
|
plugins {
|
||||||
apply plugin: 'kotlin-android'
|
alias(libs.plugins.android.application)
|
||||||
apply plugin: 'kotlinx-serialization'
|
alias(libs.plugins.kotlin.android)
|
||||||
|
alias(libs.plugins.kotlin.serialization)
|
||||||
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
extName = 'Yomiroll'
|
extName = 'Yomiroll'
|
||||||
pkgNameSuffix = 'all.kamyroll'
|
pkgNameSuffix = 'all.kamyroll'
|
||||||
extClass = '.Yomiroll'
|
extClass = '.Yomiroll'
|
||||||
extVersionCode = 22
|
extVersionCode = 23
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ import kotlinx.serialization.decodeFromString
|
|||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.jsonObject
|
import kotlinx.serialization.json.jsonObject
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -45,6 +44,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override val baseUrl = "https://crunchyroll.com"
|
override val baseUrl = "https://crunchyroll.com"
|
||||||
|
|
||||||
private val crUrl = "https://beta-api.crunchyroll.com"
|
private val crUrl = "https://beta-api.crunchyroll.com"
|
||||||
|
private val crApiUrl = "$crUrl/content/v2"
|
||||||
|
|
||||||
override val lang = "all"
|
override val lang = "all"
|
||||||
|
|
||||||
@ -58,31 +58,25 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val tokenInterceptor = AccessTokenInterceptor(crUrl, json, preferences, PREF_USE_LOCAL_Token)
|
private val tokenInterceptor by lazy {
|
||||||
|
AccessTokenInterceptor(crUrl, json, preferences, PREF_USE_LOCAL_TOKEN_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
override val client: OkHttpClient = OkHttpClient().newBuilder()
|
override val client by lazy {
|
||||||
.addInterceptor(tokenInterceptor).build()
|
super.client.newBuilder().addInterceptor(tokenInterceptor).build()
|
||||||
|
}
|
||||||
|
|
||||||
// ============================== Popular ===============================
|
// ============================== Popular ===============================
|
||||||
|
|
||||||
override fun popularAnimeRequest(page: Int): Request {
|
override fun popularAnimeRequest(page: Int): Request {
|
||||||
val start = if (page != 1) "start=${(page - 1) * 36}&" else ""
|
val start = if (page != 1) "start=${(page - 1) * 36}&" else ""
|
||||||
return GET("$crUrl/content/v2/discover/browse?${start}n=36&sort_by=popularity&locale=en-US")
|
return GET("$crApiUrl/discover/browse?${start}n=36&sort_by=popularity&locale=en-US")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||||
val parsed = json.decodeFromString<AnimeResult>(response.body.string())
|
val parsed = json.decodeFromString<AnimeResult>(response.body.string())
|
||||||
val animeList = parsed.data.parallelMap { ani ->
|
val animeList = parsed.data.mapNotNull { it.toSAnimeOrNull() }
|
||||||
runCatching {
|
val position = response.request.url.queryParameter("start")?.toIntOrNull() ?: 0
|
||||||
ani.toSAnime()
|
|
||||||
}.getOrNull()
|
|
||||||
}.filterNotNull()
|
|
||||||
val queries = response.request.url.encodedQuery ?: "0"
|
|
||||||
val position = if (queries.contains("start=")) {
|
|
||||||
queries.substringAfter("start=").substringBefore("&").toInt()
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
return AnimesPage(animeList, position + 36 < parsed.total)
|
return AnimesPage(animeList, position + 36 < parsed.total)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +84,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request {
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
val start = if (page != 1) "start=${(page - 1) * 36}&" else ""
|
val start = if (page != 1) "start=${(page - 1) * 36}&" else ""
|
||||||
return GET("$crUrl/content/v2/discover/browse?${start}n=36&sort_by=newly_added&locale=en-US")
|
return GET("$crApiUrl/discover/browse?${start}n=36&sort_by=newly_added&locale=en-US")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response): AnimesPage = popularAnimeParse(response)
|
override fun latestUpdatesParse(response: Response): AnimesPage = popularAnimeParse(response)
|
||||||
@ -102,9 +96,9 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val start = if (page != 1) "start=${(page - 1) * 36}&" else ""
|
val start = if (page != 1) "start=${(page - 1) * 36}&" else ""
|
||||||
val url = if (query.isNotBlank()) {
|
val url = if (query.isNotBlank()) {
|
||||||
val cleanQuery = query.replace(" ", "+").lowercase()
|
val cleanQuery = query.replace(" ", "+").lowercase()
|
||||||
"$crUrl/content/v2/discover/search?${start}n=36&q=$cleanQuery&type=${params.type}"
|
"$crApiUrl/discover/search?${start}n=36&q=$cleanQuery&type=${params.type}"
|
||||||
} else {
|
} else {
|
||||||
"$crUrl/content/v2/discover/browse?${start}n=36${params.media}${params.language}&sort_by=${params.sort}${params.category}"
|
"$crApiUrl/discover/browse?${start}n=36${params.media}${params.language}&sort_by=${params.sort}${params.category}"
|
||||||
}
|
}
|
||||||
return GET(url)
|
return GET(url)
|
||||||
}
|
}
|
||||||
@ -112,7 +106,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||||
val bod = response.body.string()
|
val bod = response.body.string()
|
||||||
val total: Int
|
val total: Int
|
||||||
val animeList = (
|
val items =
|
||||||
if (response.request.url.encodedPath.contains("search")) {
|
if (response.request.url.encodedPath.contains("search")) {
|
||||||
val parsed = json.decodeFromString<SearchAnimeResult>(bod).data.first()
|
val parsed = json.decodeFromString<SearchAnimeResult>(bod).data.first()
|
||||||
total = parsed.count
|
total = parsed.count
|
||||||
@ -122,17 +116,9 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
total = parsed.total
|
total = parsed.total
|
||||||
parsed.data
|
parsed.data
|
||||||
}
|
}
|
||||||
).parallelMap { ani ->
|
|
||||||
runCatching {
|
val animeList = items.mapNotNull { it.toSAnimeOrNull() }
|
||||||
ani.toSAnime()
|
val position = response.request.url.queryParameter("start")?.toIntOrNull() ?: 0
|
||||||
}.getOrNull()
|
|
||||||
}.filterNotNull()
|
|
||||||
val queries = response.request.url.encodedQuery ?: "0"
|
|
||||||
val position = if (queries.contains("start=")) {
|
|
||||||
queries.substringAfter("start=").substringBefore("&").toInt()
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
return AnimesPage(animeList, position + 36 < total)
|
return AnimesPage(animeList, position + 36 < total)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,9 +130,9 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val mediaId = json.decodeFromString<LinkData>(anime.url)
|
val mediaId = json.decodeFromString<LinkData>(anime.url)
|
||||||
val resp = client.newCall(
|
val resp = client.newCall(
|
||||||
if (mediaId.media_type == "series") {
|
if (mediaId.media_type == "series") {
|
||||||
GET("$crUrl/content/v2/cms/series/${mediaId.id}?locale=en-US")
|
GET("$crApiUrl/cms/series/${mediaId.id}?locale=en-US")
|
||||||
} else {
|
} else {
|
||||||
GET("$crUrl/content/v2/cms/movie_listings/${mediaId.id}?locale=en-US")
|
GET("$crApiUrl/cms/movie_listings/${mediaId.id}?locale=en-US")
|
||||||
},
|
},
|
||||||
).execute()
|
).execute()
|
||||||
val info = json.decodeFromString<AnimeResult>(resp.body.string())
|
val info = json.decodeFromString<AnimeResult>(resp.body.string())
|
||||||
@ -169,75 +155,78 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun episodeListRequest(anime: SAnime): Request {
|
override fun episodeListRequest(anime: SAnime): Request {
|
||||||
val mediaId = json.decodeFromString<LinkData>(anime.url)
|
val mediaId = json.decodeFromString<LinkData>(anime.url)
|
||||||
return if (mediaId.media_type == "series") {
|
return if (mediaId.media_type == "series") {
|
||||||
GET("$crUrl/content/v2/cms/series/${mediaId.id}/seasons")
|
GET("$crApiUrl/cms/series/${mediaId.id}/seasons")
|
||||||
} else {
|
} else {
|
||||||
GET("$crUrl/content/v2/cms/movie_listings/${mediaId.id}/movies")
|
GET("$crApiUrl/cms/movie_listings/${mediaId.id}/movies")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||||
val seasons = json.decodeFromString<SeasonResult>(response.body.string())
|
val seasons = json.decodeFromString<SeasonResult>(response.body.string())
|
||||||
val series = response.request.url.encodedPath.contains("series/")
|
val series = response.request.url.encodedPath.contains("series/")
|
||||||
val chunkSize = if (preferences.getBoolean(PREF_DISABLE_SEASON_PARALLEL_MAP, false)) 1 else 6
|
val chunkSize = Runtime.getRuntime().availableProcessors()
|
||||||
|
|
||||||
return if (series) {
|
return if (series) {
|
||||||
seasons.data.sortedBy { it.season_number }.chunked(chunkSize).map { chunk ->
|
seasons.data.sortedBy { it.season_number }.chunked(chunkSize).flatMap { chunk ->
|
||||||
chunk.parallelMap { seasonData ->
|
chunk.parallelMap { seasonData ->
|
||||||
runCatching {
|
runCatching {
|
||||||
val episodeResp =
|
getEpisodes(seasonData)
|
||||||
client.newCall(GET("$crUrl/content/v2/cms/seasons/${seasonData.id}/episodes"))
|
|
||||||
.execute()
|
|
||||||
val body = episodeResp.body.string()
|
|
||||||
val episodes =
|
|
||||||
json.decodeFromString<EpisodeResult>(body)
|
|
||||||
episodes.data.sortedBy { it.episode_number }.parallelMap EpisodeMap@{ ep ->
|
|
||||||
SEpisode.create().apply {
|
|
||||||
url = EpisodeData(
|
|
||||||
ep.versions?.map { Pair(it.mediaId, it.audio_locale) }
|
|
||||||
?: listOf(
|
|
||||||
Pair(
|
|
||||||
ep.streams_link?.substringAfter("videos/")
|
|
||||||
?.substringBefore("/streams")
|
|
||||||
?: return@EpisodeMap null,
|
|
||||||
ep.audio_locale,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).toJsonString()
|
|
||||||
name = if (ep.episode_number > 0 && ep.episode.isNumeric()) {
|
|
||||||
"Season ${seasonData.season_number} Ep ${df.format(ep.episode_number)}: " + ep.title
|
|
||||||
} else {
|
|
||||||
ep.title
|
|
||||||
}
|
|
||||||
episode_number = ep.episode_number
|
|
||||||
date_upload = ep.airDate?.let { parseDate(it) } ?: 0L
|
|
||||||
scanlator = ep.versions?.sortedBy { it.audio_locale }
|
|
||||||
?.joinToString { it.audio_locale.substringBefore("-") }
|
|
||||||
?: ep.audio_locale.substringBefore("-")
|
|
||||||
}
|
|
||||||
}.filterNotNull()
|
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
}.filterNotNull().flatten()
|
}.filterNotNull().flatten()
|
||||||
}.flatten().reversed()
|
}.reversed()
|
||||||
} else {
|
} else {
|
||||||
seasons.data.mapIndexed { index, movie ->
|
seasons.data.mapIndexed { index, movie ->
|
||||||
SEpisode.create().apply {
|
SEpisode.create().apply {
|
||||||
this.url = EpisodeData(listOf(Pair(movie.id, ""))).toJsonString()
|
url = EpisodeData(listOf(Pair(movie.id, ""))).toJsonString()
|
||||||
this.name = "Movie"
|
name = "Movie"
|
||||||
this.episode_number = (index + 1).toFloat()
|
episode_number = (index + 1).toFloat()
|
||||||
this.date_upload = movie.date?.let { parseDate(it) } ?: 0L
|
date_upload = movie.date?.let(::parseDate) ?: 0L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getEpisodes(seasonData: SeasonResult.Season): List<SEpisode> {
|
||||||
|
val episodeResp =
|
||||||
|
client.newCall(GET("$crApiUrl/cms/seasons/${seasonData.id}/episodes"))
|
||||||
|
.execute()
|
||||||
|
val body = episodeResp.body.string()
|
||||||
|
val episodes = json.decodeFromString<EpisodeResult>(body)
|
||||||
|
|
||||||
|
return episodes.data.sortedBy { it.episode_number }.mapNotNull EpisodeMap@{ ep ->
|
||||||
|
SEpisode.create().apply {
|
||||||
|
url = EpisodeData(
|
||||||
|
ep.versions?.map { Pair(it.mediaId, it.audio_locale) }
|
||||||
|
?: listOf(
|
||||||
|
Pair(
|
||||||
|
ep.streams_link?.substringAfter("videos/")
|
||||||
|
?.substringBefore("/streams")
|
||||||
|
?: return@EpisodeMap null,
|
||||||
|
ep.audio_locale,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).toJsonString()
|
||||||
|
name = if (ep.episode_number > 0 && ep.episode.isNumeric()) {
|
||||||
|
"Season ${seasonData.season_number} Ep ${df.format(ep.episode_number)}: " + ep.title
|
||||||
|
} else {
|
||||||
|
ep.title
|
||||||
|
}
|
||||||
|
episode_number = ep.episode_number
|
||||||
|
date_upload = ep.airDate?.let(::parseDate) ?: 0L
|
||||||
|
scanlator = ep.versions?.sortedBy { it.audio_locale }
|
||||||
|
?.joinToString { it.audio_locale.substringBefore("-") }
|
||||||
|
?: ep.audio_locale.substringBefore("-")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ============================ Video Links =============================
|
// ============================ Video Links =============================
|
||||||
|
|
||||||
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
|
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
|
||||||
val urlJson = json.decodeFromString<EpisodeData>(episode.url)
|
val urlJson = json.decodeFromString<EpisodeData>(episode.url)
|
||||||
val dubLocale = preferences.getString("preferred_audio", "en-US")!!
|
val dubLocale = preferences.getString(PREF_AUD_KEY, PREF_AUD_DEFAULT)!!
|
||||||
|
|
||||||
if (urlJson.ids.isEmpty()) throw Exception("No IDs found for episode")
|
if (urlJson.ids.isEmpty()) throw Exception("No IDs found for episode")
|
||||||
val isUsingLocalToken = preferences.getBoolean(PREF_USE_LOCAL_Token, false)
|
val isUsingLocalToken = preferences.getBoolean(PREF_USE_LOCAL_TOKEN_KEY, false)
|
||||||
|
|
||||||
val videoList = urlJson.ids.filter {
|
val videoList = urlJson.ids.filter {
|
||||||
it.second == dubLocale ||
|
it.second == dubLocale ||
|
||||||
@ -261,22 +250,18 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val response = client.newCall(getVideoRequest(mediaId)).execute()
|
val response = client.newCall(getVideoRequest(mediaId)).execute()
|
||||||
val streams = json.decodeFromString<VideoStreams>(response.body.string())
|
val streams = json.decodeFromString<VideoStreams>(response.body.string())
|
||||||
|
|
||||||
var subsList = emptyList<Track>()
|
val subLocale = preferences.getString(PREF_SUB_KEY, PREF_SUB_DEFAULT)!!.getLocale()
|
||||||
val subLocale = preferences.getString("preferred_sub", "en-US")!!.getLocale()
|
val subsList = runCatching {
|
||||||
try {
|
|
||||||
val tempSubs = mutableListOf<Track>()
|
|
||||||
streams.subtitles?.entries?.map { (_, value) ->
|
streams.subtitles?.entries?.map { (_, value) ->
|
||||||
val sub = json.decodeFromString<Subtitle>(value.jsonObject.toString())
|
val sub = json.decodeFromString<Subtitle>(value.jsonObject.toString())
|
||||||
tempSubs.add(Track(sub.url, sub.locale.getLocale()))
|
Track(sub.url, sub.locale.getLocale())
|
||||||
}
|
}?.sortedWith(
|
||||||
|
|
||||||
subsList = tempSubs.sortedWith(
|
|
||||||
compareBy(
|
compareBy(
|
||||||
{ it.lang },
|
{ it.lang },
|
||||||
{ it.lang.contains(subLocale) },
|
{ it.lang.contains(subLocale) },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} catch (_: Error) {}
|
}.getOrNull() ?: emptyList()
|
||||||
|
|
||||||
val audLang = aud.ifBlank { streams.audio_locale } ?: "ja-JP"
|
val audLang = aud.ifBlank { streams.audio_locale } ?: "ja-JP"
|
||||||
return getStreams(streams, audLang, subsList)
|
return getStreams(streams, audLang, subsList)
|
||||||
@ -322,15 +307,13 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
return GET("$crUrl/cms/v2{0}/videos/$mediaId/streams?Policy={1}&Signature={2}&Key-Pair-Id={3}")
|
return GET("$crUrl/cms/v2{0}/videos/$mediaId/streams?Policy={1}&Signature={2}&Key-Pair-Id={3}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val df = DecimalFormat("0.#")
|
private val df by lazy { DecimalFormat("0.#") }
|
||||||
|
|
||||||
private fun String.getLocale(): String {
|
private fun String.getLocale(): String {
|
||||||
return locale.firstOrNull { it.first == this }?.second ?: ""
|
return locale.firstOrNull { it.first == this }?.second ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String?.isNumeric(): Boolean {
|
private fun String?.isNumeric() = this?.toDoubleOrNull() != null
|
||||||
return this@isNumeric?.toDoubleOrNull() != null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new locales to the bottom so it doesn't mess with pref indexes
|
// Add new locales to the bottom so it doesn't mess with pref indexes
|
||||||
private val locale = arrayOf(
|
private val locale = arrayOf(
|
||||||
@ -373,21 +356,23 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
.getOrNull() ?: 0L
|
.getOrNull() ?: 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Anime.toSAnimeOrNull() = runCatching { toSAnime() }.getOrNull()
|
||||||
|
|
||||||
private fun Anime.toSAnime(): SAnime =
|
private fun Anime.toSAnime(): SAnime =
|
||||||
SAnime.create().apply {
|
SAnime.create().apply {
|
||||||
title = this@toSAnime.title
|
title = this@toSAnime.title
|
||||||
thumbnail_url = this@toSAnime.images.poster_tall?.getOrNull(0)?.thirdLast()?.source
|
thumbnail_url = images.poster_tall?.getOrNull(0)?.thirdLast()?.source
|
||||||
?: this@toSAnime.images.poster_tall?.getOrNull(0)?.last()?.source
|
?: images.poster_tall?.getOrNull(0)?.last()?.source
|
||||||
url = LinkData(this@toSAnime.id, this@toSAnime.type!!).toJsonString()
|
url = LinkData(id, type!!).toJsonString()
|
||||||
genre = this@toSAnime.series_metadata?.genres?.joinToString()
|
genre = series_metadata?.genres?.joinToString()
|
||||||
?: this@toSAnime.movie_metadata?.genres?.joinToString() ?: ""
|
?: movie_metadata?.genres?.joinToString() ?: ""
|
||||||
status = SAnime.COMPLETED
|
status = SAnime.COMPLETED
|
||||||
var desc = this@toSAnime.description + "\n"
|
var desc = this@toSAnime.description + "\n"
|
||||||
desc += "\nLanguage:" +
|
desc += "\nLanguage:" +
|
||||||
(
|
(
|
||||||
if (this@toSAnime.series_metadata?.subtitle_locales?.any() == true ||
|
if (series_metadata?.subtitle_locales?.any() == true ||
|
||||||
this@toSAnime.movie_metadata?.subtitle_locales?.any() == true ||
|
movie_metadata?.subtitle_locales?.any() == true ||
|
||||||
this@toSAnime.series_metadata?.is_subbed == true
|
series_metadata?.is_subbed == true
|
||||||
) {
|
) {
|
||||||
" Sub"
|
" Sub"
|
||||||
} else {
|
} else {
|
||||||
@ -395,8 +380,8 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
) +
|
) +
|
||||||
(
|
(
|
||||||
if ((this@toSAnime.series_metadata?.audio_locales?.size ?: 0) > 1 ||
|
if ((series_metadata?.audio_locales?.size ?: 0) > 1 ||
|
||||||
this@toSAnime.movie_metadata?.is_dubbed == true
|
movie_metadata?.is_dubbed == true
|
||||||
) {
|
) {
|
||||||
" Dub"
|
" Dub"
|
||||||
} else {
|
} else {
|
||||||
@ -405,48 +390,47 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
)
|
)
|
||||||
desc += "\nMaturity Ratings: " +
|
desc += "\nMaturity Ratings: " +
|
||||||
(
|
(
|
||||||
this@toSAnime.series_metadata?.maturity_ratings?.joinToString()
|
series_metadata?.maturity_ratings?.joinToString()
|
||||||
?: this@toSAnime.movie_metadata?.maturity_ratings?.joinToString() ?: ""
|
?: movie_metadata?.maturity_ratings?.joinToString() ?: ""
|
||||||
)
|
)
|
||||||
desc += if (this@toSAnime.series_metadata?.is_simulcast == true) "\nSimulcast" else ""
|
desc += if (series_metadata?.is_simulcast == true) "\nSimulcast" else ""
|
||||||
desc += "\n\nAudio: " + (
|
desc += "\n\nAudio: " + (
|
||||||
this@toSAnime.series_metadata?.audio_locales?.sortedBy { it.getLocale() }
|
series_metadata?.audio_locales?.sortedBy { it.getLocale() }
|
||||||
?.joinToString { it.getLocale() } ?: ""
|
?.joinToString { it.getLocale() } ?: ""
|
||||||
)
|
)
|
||||||
desc += "\n\nSubs: " + (
|
desc += "\n\nSubs: " + (
|
||||||
this@toSAnime.series_metadata?.subtitle_locales?.sortedBy { it.getLocale() }
|
series_metadata?.subtitle_locales?.sortedBy { it.getLocale() }
|
||||||
?.joinToString { it.getLocale() }
|
?.joinToString { it.getLocale() }
|
||||||
?: this@toSAnime.movie_metadata?.subtitle_locales?.sortedBy { it.getLocale() }
|
?: movie_metadata?.subtitle_locales?.sortedBy { it.getLocale() }
|
||||||
?.joinToString { it.getLocale() } ?: ""
|
?.joinToString { it.getLocale() } ?: ""
|
||||||
)
|
)
|
||||||
description = desc
|
description = desc
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun List<Video>.sort(): List<Video> {
|
override fun List<Video>.sort(): List<Video> {
|
||||||
val quality = preferences.getString("preferred_quality", "1080")!!
|
val quality = preferences.getString(PREF_QLT_KEY, PREF_QLT_DEFAULT)!!
|
||||||
val dubLocale = preferences.getString("preferred_audio", "en-US")!!
|
val dubLocale = preferences.getString(PREF_AUD_KEY, PREF_AUD_DEFAULT)!!
|
||||||
val subLocale = preferences.getString("preferred_sub", "en-US")!!
|
val subLocale = preferences.getString(PREF_SUB_KEY, PREF_SUB_DEFAULT)!!
|
||||||
val subType = preferences.getString("preferred_sub_type", "soft")!!
|
val subType = preferences.getString(PREF_SUB_TYPE_KEY, PREF_SUB_TYPE_DEFAULT)!!
|
||||||
val shouldContainHard = subType == "hard"
|
val shouldContainHard = subType == "hard"
|
||||||
|
|
||||||
return this.sortedWith(
|
return sortedWith(
|
||||||
compareBy(
|
compareBy(
|
||||||
{ it.quality.contains(quality) },
|
{ it.quality.contains(quality) },
|
||||||
{ it.quality.contains("Aud: ${dubLocale.getLocale()}") },
|
{ it.quality.contains("Aud: ${dubLocale.getLocale()}") },
|
||||||
{ it.quality.contains("HardSub") == shouldContainHard },
|
{ it.quality.contains("HardSub") == shouldContainHard },
|
||||||
{ it.quality.contains(subLocale) },
|
{ it.quality.contains(subLocale) },
|
||||||
{ it.quality.contains("en-US") },
|
|
||||||
),
|
),
|
||||||
).reversed()
|
).reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
val videoQualityPref = ListPreference(screen.context).apply {
|
val videoQualityPref = ListPreference(screen.context).apply {
|
||||||
key = PREF_QLT
|
key = PREF_QLT_KEY
|
||||||
title = "Preferred quality"
|
title = PREF_QLT_TITLE
|
||||||
entries = arrayOf("1080p", "720p", "480p", "360p", "240p", "80p")
|
entries = PREF_QLT_ENTRIES
|
||||||
entryValues = arrayOf("1080", "720", "480", "360", "240", "80")
|
entryValues = PREF_QLT_VALUES
|
||||||
setDefaultValue("1080")
|
setDefaultValue(PREF_QLT_DEFAULT)
|
||||||
summary = "%s"
|
summary = "%s"
|
||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
@ -458,11 +442,11 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val audLocalePref = ListPreference(screen.context).apply {
|
val audLocalePref = ListPreference(screen.context).apply {
|
||||||
key = PREF_AUD
|
key = PREF_AUD_KEY
|
||||||
title = "Preferred Audio Language"
|
title = PREF_AUD_TITLE
|
||||||
entries = locale.map { it.second }.toTypedArray()
|
entries = locale.map { it.second }.toTypedArray()
|
||||||
entryValues = locale.map { it.first }.toTypedArray()
|
entryValues = locale.map { it.first }.toTypedArray()
|
||||||
setDefaultValue("en-US")
|
setDefaultValue(PREF_AUD_DEFAULT)
|
||||||
summary = "%s"
|
summary = "%s"
|
||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
@ -474,11 +458,11 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val subLocalePref = ListPreference(screen.context).apply {
|
val subLocalePref = ListPreference(screen.context).apply {
|
||||||
key = PREF_SUB
|
key = PREF_SUB_KEY
|
||||||
title = "Preferred Sub Language"
|
title = PREF_SUB_TITLE
|
||||||
entries = locale.map { it.second }.toTypedArray()
|
entries = locale.map { it.second }.toTypedArray()
|
||||||
entryValues = locale.map { it.first }.toTypedArray()
|
entryValues = locale.map { it.first }.toTypedArray()
|
||||||
setDefaultValue("en-US")
|
setDefaultValue(PREF_SUB_DEFAULT)
|
||||||
summary = "%s"
|
summary = "%s"
|
||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
@ -490,11 +474,11 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val subTypePref = ListPreference(screen.context).apply {
|
val subTypePref = ListPreference(screen.context).apply {
|
||||||
key = PREF_SUB_TYPE
|
key = PREF_SUB_TYPE_KEY
|
||||||
title = "Preferred Sub Type"
|
title = PREF_SUB_TYPE_TITLE
|
||||||
entries = arrayOf("Softsub", "Hardsub")
|
entries = PREF_SUB_TYPE_ENTRIES
|
||||||
entryValues = arrayOf("soft", "hard")
|
entryValues = PREF_SUB_TYPE_VALUES
|
||||||
setDefaultValue("soft")
|
setDefaultValue(PREF_SUB_TYPE_DEFAULT)
|
||||||
summary = "%s"
|
summary = "%s"
|
||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
@ -505,20 +489,10 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val disableParallelEpMap = SwitchPreferenceCompat(screen.context).apply {
|
|
||||||
key = PREF_DISABLE_SEASON_PARALLEL_MAP
|
|
||||||
title = "Disable Parallel Requests for Seasons"
|
|
||||||
setDefaultValue(false)
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
preferences.edit().putBoolean(key, newValue as Boolean).commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
screen.addPreference(videoQualityPref)
|
screen.addPreference(videoQualityPref)
|
||||||
screen.addPreference(audLocalePref)
|
screen.addPreference(audLocalePref)
|
||||||
screen.addPreference(subLocalePref)
|
screen.addPreference(subLocalePref)
|
||||||
screen.addPreference(subTypePref)
|
screen.addPreference(subTypePref)
|
||||||
screen.addPreference(disableParallelEpMap)
|
|
||||||
screen.addPreference(localSubsPreference(screen))
|
screen.addPreference(localSubsPreference(screen))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,8 +506,8 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
object : LocalSubsPreference(screen.context) {
|
object : LocalSubsPreference(screen.context) {
|
||||||
override fun reload() {
|
override fun reload() {
|
||||||
this.apply {
|
this.apply {
|
||||||
key = PREF_USE_LOCAL_Token
|
key = PREF_USE_LOCAL_TOKEN_KEY
|
||||||
title = "Use Local Token (Don't Spam this please!)"
|
title = PREF_USE_LOCAL_TOKEN_TITLE
|
||||||
summary = runBlocking {
|
summary = runBlocking {
|
||||||
withContext(Dispatchers.IO) { getTokenDetail() }
|
withContext(Dispatchers.IO) { getTokenDetail() }
|
||||||
}
|
}
|
||||||
@ -577,11 +551,27 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH)
|
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH)
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val PREF_QLT = "preferred_quality"
|
private const val PREF_QLT_KEY = "preferred_quality"
|
||||||
private const val PREF_AUD = "preferred_audio"
|
private const val PREF_QLT_TITLE = "Preferred quality"
|
||||||
private const val PREF_SUB = "preferred_sub"
|
private const val PREF_QLT_DEFAULT = "1080p"
|
||||||
private const val PREF_SUB_TYPE = "preferred_sub_type"
|
private val PREF_QLT_ENTRIES = arrayOf("1080p", "720p", "480p", "360p", "240p", "80p")
|
||||||
private const val PREF_DISABLE_SEASON_PARALLEL_MAP = "preferred_disable_parallelMap"
|
private val PREF_QLT_VALUES = PREF_QLT_ENTRIES
|
||||||
private const val PREF_USE_LOCAL_Token = "preferred_local_Token"
|
|
||||||
|
private const val PREF_AUD_KEY = "preferred_audio"
|
||||||
|
private const val PREF_AUD_TITLE = "Preferred Audio Language"
|
||||||
|
private const val PREF_AUD_DEFAULT = "en-US"
|
||||||
|
|
||||||
|
private const val PREF_SUB_KEY = "preferred_sub"
|
||||||
|
private const val PREF_SUB_TITLE = "Preferred Sub Language"
|
||||||
|
private const val PREF_SUB_DEFAULT = "en-US"
|
||||||
|
|
||||||
|
private const val PREF_SUB_TYPE_KEY = "preferred_sub_type"
|
||||||
|
private const val PREF_SUB_TYPE_TITLE = "Preferred Sub Type"
|
||||||
|
private const val PREF_SUB_TYPE_DEFAULT = "soft"
|
||||||
|
private val PREF_SUB_TYPE_ENTRIES = arrayOf("Softsub", "Hardsub")
|
||||||
|
private val PREF_SUB_TYPE_VALUES = arrayOf("soft", "hard")
|
||||||
|
|
||||||
|
private const val PREF_USE_LOCAL_TOKEN_KEY = "preferred_local_Token"
|
||||||
|
private const val PREF_USE_LOCAL_TOKEN_TITLE = "Use Local Token (Don't Spam this please!)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user