diff --git a/src/all/kamyroll/build.gradle b/src/all/kamyroll/build.gradle index 9be55e458..940ae70ac 100644 --- a/src/all/kamyroll/build.gradle +++ b/src/all/kamyroll/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Consumyroll' pkgNameSuffix = 'all.kamyroll' extClass = '.Consumyroll' - extVersionCode = 9 + extVersionCode = 10 libVersion = '13' } diff --git a/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/AccessTokenInterceptor.kt b/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/AccessTokenInterceptor.kt index f4c090e01..970b7a794 100644 --- a/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/AccessTokenInterceptor.kt +++ b/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/AccessTokenInterceptor.kt @@ -18,12 +18,14 @@ class AccessTokenInterceptor( private val preferences: SharedPreferences ) : Interceptor { private var accessToken = preferences.getString("access_token", null) ?: "" + private val userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:106.0) Gecko/20100101 Firefox/106.0" override fun intercept(chain: Interceptor.Chain): Response { if (accessToken.isBlank()) accessToken = refreshAccessToken() val request = chain.request().newBuilder() .header("authorization", accessToken) + .header("User-Agent", userAgent) .build() val response = chain.proceed(request) @@ -57,6 +59,7 @@ class AccessTokenInterceptor( private fun newRequestWithAccessToken(request: Request, accessToken: String): Request { return request.newBuilder() .header("authorization", accessToken) + .header("User-Agent", userAgent) .build() } @@ -64,7 +67,7 @@ class AccessTokenInterceptor( val client = OkHttpClient().newBuilder().build() val headers = Headers.headersOf( "Content-Type", "application/x-www-form-urlencoded", - "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:106.0) Gecko/20100101 Firefox/106.0", + "User-Agent", userAgent, "Authorization", "Basic a3ZvcGlzdXZ6Yy0teG96Y21kMXk6R21JSTExenVPVnRnTjdlSWZrSlpibzVuLTRHTlZ0cU8=" ) val postBody = "grant_type=client_id".toRequestBody("application/x-www-form-urlencoded".toMediaType()) diff --git a/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/Consumyroll.kt b/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/Consumyroll.kt index 3b9f7d88c..3cd11d45f 100644 --- a/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/Consumyroll.kt +++ b/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/Consumyroll.kt @@ -21,6 +21,8 @@ import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response @@ -90,16 +92,12 @@ class Consumyroll : ConfigurableAnimeSource, AnimeHttpSource() { override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { val cleanQuery = query.replace(" ", "+").lowercase() - return GET("$crUrl/content/v2/discover/search?q=$cleanQuery&n=6&type=&locale=en-US") + return GET("$baseUrl/search/$cleanQuery") } override fun searchAnimeParse(response: Response): AnimesPage { val parsed = json.decodeFromString(response.body!!.string()) - val animeList = parsed.data.filter { it.type == "top_results" }.map { result -> - result.items.filter { it.type == "series" }.map { ani -> - ani.toSAnime() - } - }.flatten() + val animeList = parsed.results.map { it.toSAnime() } return AnimesPage(animeList, false) } @@ -108,8 +106,23 @@ class Consumyroll : ConfigurableAnimeSource, AnimeHttpSource() { override fun fetchAnimeDetails(anime: SAnime): Observable { val mediaId = json.decodeFromString(anime.url) val resp = client.newCall(GET("$crUrl/content/v2/cms/series/${mediaId.id}?locale=en-US")).execute() - val info = json.decodeFromString(resp.body!!.string()) - return Observable.just(anime.apply { author = info.data.first().content_provider }) + val info = json.decodeFromString(resp.body!!.string()) + val ani = info.data.first() + val aniDetail = SAnime.create() + aniDetail.apply { + author = ani.content_provider + status = SAnime.COMPLETED + if (anime.description.isNullOrBlank()) { + var desc = ani.description + "\n" + desc += "\nLanguage: Sub" + (if (ani.audio_locales.size > 1) " Dub" else "") + desc += "\nMaturity Ratings: ${ani.maturity_ratings.joinToString()}" + desc += if (ani.is_simulcast) "\nSimulcast" else "" + desc += "\n\nAudio: " + ani.audio_locales.joinToString { it.getLocale() } + desc += "\n\nSubs: " + ani.subtitle_locales.joinToString { it.getLocale() } + description = desc + } + } + return Observable.just(aniDetail) } override fun animeDetailsParse(response: Response): SAnime = throw Exception("not used") @@ -123,31 +136,75 @@ class Consumyroll : ConfigurableAnimeSource, AnimeHttpSource() { override fun episodeListParse(response: Response): List { val seasons = json.decodeFromString(response.body!!.string()) - return seasons.data.parallelMap { seasonData -> - runCatching { - val episodeResp = client.newCall(GET("$crUrl/content/v2/cms/seasons/${seasonData.id}/episodes")).execute() - val episodes = json.decodeFromString(episodeResp.body!!.string()) - episodes.data.sortedBy { it.episode_number }.map { ep -> + return if (seasons.data.isNotEmpty()) { + seasons.data.parallelMap { seasonData -> + runCatching { + val episodeResp = client.newCall(GET("$crUrl/content/v2/cms/seasons/${seasonData.id}/episodes")).execute() + val episodes = json.decodeFromString(episodeResp.body!!.string()) + episodes.data.sortedBy { it.episode_number }.map { ep -> + SEpisode.create().apply { + url = EpisodeData( + ep.versions.map { Pair(it.id, it.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 = parseDate(ep.airDate) + scanlator = ep.versions.sortedBy { it.audio_locale } + .joinToString { it.audio_locale.substringBefore("-") } + } + } + }.getOrNull() + }.filterNotNull().flatten().reversed() + } else { + val aniId = response.request.url.toString().substringAfter("series/").substringBefore("/seasons") + val resp = client.newCall(GET("$baseUrl/info/$aniId?type=series&fetchAllSeasons=true")).execute() + val medias = json.decodeFromString(resp.body!!.string()) + + medias.episodes.entries.map { (key, value) -> + val audLang = key.replace("[^A-Za-z ]".toRegex(), "") + .replace("Dub", "", true) + .replace("subbed", "Japanese", true) + val episodes = value.jsonArray.map { + json.decodeFromString(it.jsonObject.toString()) + } + episodes.map { ep -> + RawEpisode( + ep.id, + ep.title, + ep.season_number, + ep.episode_number, + ep.releaseDate, + audLang + ) + } + }.flatten().groupBy { "${it.season}_${it.episode}" } + .mapNotNull { group -> + val (season, episode) = group.key.split("_") + val ep = episode.toFloatOrNull() ?: 0F SEpisode.create().apply { url = EpisodeData( - ep.versions.map { Pair(it.id, it.audio_locale) } + group.value.map { Pair(it.id, it.audLang) } ).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 = parseDate(ep.airDate) + name = if (ep > 0) "Season $season Ep ${df.format(ep)}: " + + group.value.first().title else group.value.first().title + episode_number = ep + date_upload = parseDate(group.value.first().releaseDate) } - } - }.getOrNull() - }.filterNotNull().flatten().reversed() + }.reversed() + } } // ============================ Video Links ============================= override fun fetchVideoList(episode: SEpisode): Observable> { val urlJson = json.decodeFromString(episode.url) - val videoList = urlJson.ids.parallelMap { media -> + val dubLang = preferences.getString("preferred_audio", "en-US")!!.getLocale() + val videoList = urlJson.ids.filter { + val secLang = if (it.second.contains("-")) it.second.getLocale() else it.second + secLang == "Japanese" || dubLang.contains(secLang) + }.parallelMap { media -> runCatching { extractVideo(media) }.getOrNull() @@ -162,7 +219,8 @@ class Consumyroll : ConfigurableAnimeSource, AnimeHttpSource() { private fun extractVideo(media: Pair): List