From e8dde7e4fa96c74fc78c8d39f873961b60be805c Mon Sep 17 00:00:00 2001 From: giorgio Date: Tue, 2 Jan 2024 18:59:52 +0100 Subject: [PATCH] Updated playlist name, removed no longer existent url argument, fix filename without proper extension --- .../it/animeunity/AnimeUnity.kt | 473 +++++++++++------- 1 file changed, 281 insertions(+), 192 deletions(-) diff --git a/src/it/animeunity/src/eu/kanade/tachiyomi/animeextension/it/animeunity/AnimeUnity.kt b/src/it/animeunity/src/eu/kanade/tachiyomi/animeextension/it/animeunity/AnimeUnity.kt index 70692bd953..dca56e322c 100644 --- a/src/it/animeunity/src/eu/kanade/tachiyomi/animeextension/it/animeunity/AnimeUnity.kt +++ b/src/it/animeunity/src/eu/kanade/tachiyomi/animeextension/it/animeunity/AnimeUnity.kt @@ -36,8 +36,9 @@ import uy.kohesive.injekt.injectLazy import java.lang.Exception import java.text.SimpleDateFormat -class AnimeUnity : ConfigurableAnimeSource, AnimeHttpSource() { - +class AnimeUnity : + AnimeHttpSource(), + ConfigurableAnimeSource { override val name = "AnimeUnity" override val baseUrl by lazy { preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)!! } @@ -56,23 +57,25 @@ class AnimeUnity : ConfigurableAnimeSource, AnimeHttpSource() { // ============================== Popular =============================== - override fun popularAnimeRequest(page: Int): Request = - GET("$baseUrl/top-anime?popular=true&page=$page", headers = headers) + override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/top-anime?popular=true&page=$page", headers = headers) override fun popularAnimeParse(response: Response): AnimesPage { - val parsed = response.parseAs { - it.substringAfter("top-anime animes=\"") - .substringBefore("\">") - .replace(""", "\"") - } + val parsed = + response.parseAs { + it + .substringAfter("top-anime animes=\"") + .substringBefore("\">") + .replace(""", "\"") + } - val animeList = parsed.data.map { ani -> - SAnime.create().apply { - title = ani.title_eng - url = "${ani.id}-${ani.slug}" - thumbnail_url = ani.imageurl ?: "" + val animeList = + parsed.data.map { ani -> + SAnime.create().apply { + title = ani.title_eng + url = "${ani.id}-${ani.slug}" + thumbnail_url = ani.imageurl ?: "" + } } - } return AnimesPage(animeList, parsed.current_page < parsed.last_page) } @@ -84,13 +87,14 @@ class AnimeUnity : ConfigurableAnimeSource, AnimeHttpSource() { override fun latestUpdatesParse(response: Response): AnimesPage { val document = response.asJsoup() - val animeList = document.select("div.home-wrapper-body > div.row > div.latest-anime-container").map { - SAnime.create().apply { - title = it.select("a > strong").text() - url = it.selectFirst("a")!!.attr("href").substringAfter("/anime/") - thumbnail_url = it.select("img").attr("src") + val animeList = + document.select("div.home-wrapper-body > div.row > div.latest-anime-container").map { + SAnime.create().apply { + title = it.select("a > strong").text() + url = it.selectFirst("a")!!.attr("href").substringAfter("/anime/") + thumbnail_url = it.select("img").attr("src") + } } - } val hasNextPage = document.select("ul.pagination > li.active ~ li").first() != null @@ -99,21 +103,36 @@ class AnimeUnity : ConfigurableAnimeSource, AnimeHttpSource() { // =============================== Search =============================== - override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = throw Exception("Not used") - - override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable { + override fun searchAnimeRequest( + page: Int, + query: String, + filters: AnimeFilterList, + ): Request = throw Exception("Not used") + + override fun fetchSearchAnime( + page: Int, + query: String, + filters: AnimeFilterList, + ): Observable { val params = AnimeUnityFilters.getSearchParameters(filters) - return client.newCall(searchAnimeRequest(page, query, params)) + return client + .newCall(searchAnimeRequest(page, query, params)) .asObservableSuccess() .map { response -> searchAnimeParse(response, page) } } - private fun searchAnimeRequest(page: Int, query: String, filters: AnimeUnityFilters.FilterSearchParams): Request { - val archivioResponse = client.newCall( - GET("$baseUrl/archivio", headers = headers), - ).execute() + private fun searchAnimeRequest( + page: Int, + query: String, + filters: AnimeUnityFilters.FilterSearchParams, + ): Request { + val archivioResponse = + client + .newCall( + GET("$baseUrl/archivio", headers = headers), + ).execute() val document = archivioResponse.asJsoup() @@ -121,68 +140,83 @@ class AnimeUnity : ConfigurableAnimeSource, AnimeHttpSource() { var newHeadersBuilder = headers.newBuilder() for (cookie in archivioResponse.headers) { if (cookie.first == "set-cookie" && cookie.second.startsWith("XSRF-TOKEN")) { - newHeadersBuilder.add("X-XSRF-TOKEN", cookie.second.substringAfter("=").substringBefore(";").replace("%3D", "=")) + newHeadersBuilder.add( + "X-XSRF-TOKEN", + cookie + .second + .substringAfter("=") + .substringBefore(";") + .replace("%3D", "="), + ) } if (cookie.first == "set-cookie" && cookie.second.startsWith("animeunity_session")) { newHeadersBuilder.add("Cookie", cookie.second.substringBefore(";").replace("%3D", "=")) } } - newHeadersBuilder.add("X-CSRF-TOKEN", crsfToken) + newHeadersBuilder + .add("X-CSRF-TOKEN", crsfToken) .add("Accept-Language", "en-US,en;q=0.5") .add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0") if (filters.top.isNotEmpty()) { - val topHeaders = newHeadersBuilder.add("X-CSRF-TOKEN", crsfToken) - .add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") - .add("Referer", "$baseUrl/${filters.top}") + val topHeaders = + newHeadersBuilder + .add("X-CSRF-TOKEN", crsfToken) + .add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") + .add("Referer", "$baseUrl/${filters.top}") return GET("$baseUrl/${filters.top}", headers = topHeaders.build()) } - val searchHeaders = newHeadersBuilder - .add("Accept", "application/json, text/plain, */*") - .add("Content-Type", "application/json;charset=utf-8") - .add("Origin", baseUrl) - .add("Referer", archivioResponse.request.url.toString()) - .add("X-Requested-With", "XMLHttpRequest") - .build() - - val body = """ - { - "title": ${query.falseIfEmpty()}, - "type": ${filters.type.falseIfEmpty()}, - "year": ${filters.year.falseIfEmpty()}, - "order": ${filters.order.falseIfEmpty()}, - "status": ${filters.state.falseIfEmpty()}, - "genres": ${filters.genre.ifEmpty { "false" }}, - "offset": ${(page - 1) * 30}, - "dubbed": ${if (filters.dub.isEmpty()) "false" else "true"}, - "season": ${filters.season.falseIfEmpty()} - } - """.trimIndent().toRequestBody("application/json".toMediaType()) + val searchHeaders = + newHeadersBuilder + .add("Accept", "application/json, text/plain, */*") + .add("Content-Type", "application/json;charset=utf-8") + .add("Origin", baseUrl) + .add("Referer", archivioResponse.request.url.toString()) + .add("X-Requested-With", "XMLHttpRequest") + .build() + + val body = + """ + { + "title": ${query.falseIfEmpty()}, + "type": ${filters.type.falseIfEmpty()}, + "year": ${filters.year.falseIfEmpty()}, + "order": ${filters.order.falseIfEmpty()}, + "status": ${filters.state.falseIfEmpty()}, + "genres": ${filters.genre.ifEmpty { "false" }}, + "offset": ${(page - 1) * 30}, + "dubbed": ${if (filters.dub.isEmpty()) "false" else "true"}, + "season": ${filters.season.falseIfEmpty()} + } + """.trimIndent().toRequestBody("application/json".toMediaType()) return POST("$baseUrl/archivio/get-animes", body = body, headers = searchHeaders) } override fun searchAnimeParse(response: Response): AnimesPage = throw Exception("Not used") - private fun searchAnimeParse(response: Response, page: Int): AnimesPage { - return if (response.request.method == "POST") { + private fun searchAnimeParse( + response: Response, + page: Int, + ): AnimesPage = + if (response.request.method == "POST") { val data = response.parseAs() - val animeList = data.records.map { - SAnime.create().apply { - title = it.title_eng - thumbnail_url = it.imageurl - url = "${it.id}-${it.slug}" + val animeList = + data.records.map { + SAnime.create().apply { + title = it.title_eng + thumbnail_url = it.imageurl + url = "${it.id}-${it.slug}" + } } - } AnimesPage(animeList, data.tot - page * 30 >= 30 && data.tot > 30) } else { popularAnimeParse(response) } - } override fun getFilterList(): AnimeFilterList = AnimeUnityFilters.FILTER_LIST @@ -195,21 +229,23 @@ class AnimeUnity : ConfigurableAnimeSource, AnimeHttpSource() { val videoPlayer = document.selectFirst("video-player[episodes_count]")!! - val animeDetails = json.decodeFromString( - videoPlayer.attr("anime").replace(""", "\""), - ) + val animeDetails = + json.decodeFromString( + videoPlayer.attr("anime").replace(""", "\""), + ) return SAnime.create().apply { title = animeDetails.title_eng status = parseStatus(animeDetails.status) artist = animeDetails.studio ?: "" genre = animeDetails.genres.joinToString(", ") { it.name } - description = buildString { - append(animeDetails.plot) - append("\n\nTipo: ${animeDetails.type}") - append("\nStagione: ${animeDetails.season} ${animeDetails.date}") - append("\nValutazione: ★${animeDetails.score ?: "-"}") - } + description = + buildString { + append(animeDetails.plot) + append("\n\nTipo: ${animeDetails.type}") + append("\nStagione: ${animeDetails.season} ${animeDetails.date}") + append("\nValutazione: ★${animeDetails.score ?: "-"}") + } } } @@ -225,14 +261,22 @@ class AnimeUnity : ConfigurableAnimeSource, AnimeHttpSource() { var newHeadersBuilder = headers.newBuilder() for (cookie in response.headers) { if (cookie.first == "set-cookie" && cookie.second.startsWith("XSRF-TOKEN")) { - newHeadersBuilder.add("X-XSRF-TOKEN", cookie.second.substringAfter("=").substringBefore(";").replace("%3D", "=")) + newHeadersBuilder.add( + "X-XSRF-TOKEN", + cookie + .second + .substringAfter("=") + .substringBefore(";") + .replace("%3D", "="), + ) } if (cookie.first == "set-cookie" && cookie.second.startsWith("animeunity_session")) { newHeadersBuilder.add("Cookie", cookie.second.substringBefore(";").replace("%3D", "=")) } } - newHeadersBuilder.add("X-CSRF-TOKEN", crsfToken) + newHeadersBuilder + .add("X-CSRF-TOKEN", crsfToken) .add("Content-Type", "application/json") .add("Referer", response.request.url.toString()) .add("Accept", "application/json, text/plain, */*") @@ -242,27 +286,38 @@ class AnimeUnity : ConfigurableAnimeSource, AnimeHttpSource() { val videoPlayer = document.selectFirst("video-player[episodes_count]")!! val episodeCount = videoPlayer.attr("episodes_count").toInt() - val animeId = response.request.url.toString().substringAfter("/anime/").substringBefore("-") - - val episodes = json.decodeFromString>( - videoPlayer.attr("episodes").replace(""", "\""), - ) + val animeId = + response + .request + .url + .toString() + .substringAfter("/anime/") + .substringBefore("-") + + val episodes = + json.decodeFromString>( + videoPlayer.attr("episodes").replace(""", "\""), + ) episodeList.addAll( - episodes.filter { - it.id != null - }.map { - SEpisode.create().apply { - name = "Episode ${it.number}" - date_upload = parseDate(it.created_at) - episode_number = it.number.split("-")[0].toFloatOrNull() ?: 0F - setUrlWithoutDomain( - response.request.url.newBuilder() - .addPathSegment(it.id.toString()) - .toString(), - ) - } - }, + episodes + .filter { + it.id != null + }.map { + SEpisode.create().apply { + name = "Episode ${it.number}" + date_upload = parseDate(it.created_at) + episode_number = it.number.split("-")[0].toFloatOrNull() ?: 0F + setUrlWithoutDomain( + response + .request + .url + .newBuilder() + .addPathSegment(it.id.toString()) + .toString(), + ) + } + }, ) if (episodeCount > 120) { @@ -291,48 +346,66 @@ class AnimeUnity : ConfigurableAnimeSource, AnimeHttpSource() { override fun fetchVideoList(episode: SEpisode): Observable> { val videoList = mutableListOf