From 6115485bd41accdf581553926f7416869ec16b8f Mon Sep 17 00:00:00 2001 From: Claudemirovsky <63046606+Claudemirovsky@users.noreply.github.com> Date: Wed, 27 Sep 2023 03:21:26 -0300 Subject: [PATCH] fix(en/rule34video): Fix http 403 error (#2268) --- src/en/rule34video/build.gradle | 8 +- .../en/rule34video/DdosGuardInterceptor.kt | 7 +- .../en/rule34video/Rule34Video.kt | 240 +++++++++--------- 3 files changed, 134 insertions(+), 121 deletions(-) diff --git a/src/en/rule34video/build.gradle b/src/en/rule34video/build.gradle index 5c7271260..4758016ce 100644 --- a/src/en/rule34video/build.gradle +++ b/src/en/rule34video/build.gradle @@ -1,11 +1,13 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +} ext { extName = 'Rule34Video' pkgNameSuffix = 'en.rule34video' extClass = '.Rule34Video' - extVersionCode = 6 + extVersionCode = 7 libVersion = '13' containsNsfw = true } diff --git a/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/DdosGuardInterceptor.kt b/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/DdosGuardInterceptor.kt index 9d2a5d87f..7913a854d 100644 --- a/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/DdosGuardInterceptor.kt +++ b/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/DdosGuardInterceptor.kt @@ -34,11 +34,8 @@ class DdosGuardInterceptor(private val client: OkHttpClient) : Interceptor { } val newCookie = getNewCookie(originalRequest.url) ?: return chain.proceed(originalRequest) - val newCookieHeader = buildString { - (oldCookie + newCookie).forEachIndexed { index, cookie -> - if (index > 0) append("; ") - append(cookie.name).append('=').append(cookie.value) - } + val newCookieHeader = (oldCookie + newCookie).joinToString("; ") { cookie -> + "${cookie.name}=${cookie.value}" } return chain.proceed(originalRequest.newBuilder().addHeader("cookie", newCookieHeader).build()) diff --git a/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/Rule34Video.kt b/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/Rule34Video.kt index b200afa9f..51898f1db 100644 --- a/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/Rule34Video.kt +++ b/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/Rule34Video.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.animeextension.en.rule34video import android.app.Application -import android.content.SharedPreferences import androidx.preference.ListPreference import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource @@ -13,11 +12,11 @@ import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.lang.Exception @@ -34,53 +33,120 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { private val ddgInterceptor = DdosGuardInterceptor(network.client) - override val client: OkHttpClient = network.client + override val client = network.client .newBuilder() .addInterceptor(ddgInterceptor) .build() - private val preferences: SharedPreferences by lazy { + private val preferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } - // Popular Videos + // ============================== Popular =============================== + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/latest-updates/$page/") - override fun popularAnimeSelector(): String = "div.item.thumb" + override fun popularAnimeSelector() = "div.item.thumb" - override fun popularAnimeRequest(page: Int): Request = - - GET("$baseUrl/latest-updates/$page/") - - override fun popularAnimeFromElement(element: Element): SAnime { - val anime = SAnime.create() - anime.setUrlWithoutDomain(element.select("a.th").attr("href")) - anime.title = element.select("a.th div.thumb_title").text() - anime.thumbnail_url = element.select("a.th div.img img").attr("data-original") - - return anime + override fun popularAnimeFromElement(element: Element) = SAnime.create().apply { + setUrlWithoutDomain(element.selectFirst("a.th")!!.attr("href")) + title = element.selectFirst("a.th div.thumb_title")!!.text() + thumbnail_url = element.selectFirst("a.th div.img img")?.attr("abs:data-original") } - override fun popularAnimeNextPageSelector(): String = "div.item.pager.next a" + override fun popularAnimeNextPageSelector() = "div.item.pager.next a" - override fun episodeListParse(response: Response): List { - val episodes = mutableListOf() + // =============================== Latest =============================== + override fun latestUpdatesRequest(page: Int) = throw Exception("not used") - val episode = SEpisode.create().apply { - name = "Video" - date_upload = System.currentTimeMillis() + override fun latestUpdatesSelector() = throw Exception("not used") + + override fun latestUpdatesFromElement(element: Element) = throw Exception("not used") + + override fun latestUpdatesNextPageSelector() = throw Exception("not used") + + // =============================== Search =============================== + private inline fun AnimeFilterList.getUriPart() = + (find { it is R } as? UriPartFilter)?.toUriPart() ?: "" + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val orderFilter = filters.getUriPart() + val categoryFilter = filters.getUriPart() + val sortType = when (orderFilter) { + "latest-updates" -> "post_date" + "most-popular" -> "video_viewed" + "top-rated" -> "rating" + else -> "" } - episode.setUrlWithoutDomain(response.request.url.toString()) - episodes.add(episode) - return episodes + val tagFilter = (filters.find { it is TagFilter } as? TagFilter)?.state ?: "" + + val url = "$baseUrl/search_ajax.php?tag=${tagFilter.ifBlank { "." }}" + val response = client.newCall(GET(url, headers)).execute() + tagDocument = response.use { it.asJsoup() } + + val tagSearch = filters.getUriPart() + + return if (query.isNotEmpty()) { + if (query.startsWith(PREFIX_SEARCH)) { + val newQuery = query.removePrefix(PREFIX_SEARCH).dropLastWhile { it.isDigit() } + GET("$baseUrl/search/$newQuery") + } else { + GET("$baseUrl/search/${query.replace(Regex("\\s"), "-")}/?flag1=$categoryFilter&sort_by=$sortType&from_videos=$page&tag_ids=all%2C$tagSearch") + } + } else { + GET("$baseUrl/search/?flag1=$categoryFilter&sort_by=$sortType&from_videos=$page&tag_ids=all%2C$tagSearch") + } } + override fun searchAnimeSelector() = popularAnimeSelector() + override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element) + override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector() + + // =========================== Anime Details ============================ + override fun animeDetailsParse(document: Document) = SAnime.create().apply { + title = document.selectFirst("h1.title_video")!!.text() + val info = document.selectFirst("#tab_video_info")!! + author = info.select("div.label:contains(Artist:) + a").eachText().joinToString() + description = buildString { + info.selectFirst("div.label:contains(Description:) > em")?.text()?.also { append("$it\n") } + info.selectFirst("i.icon-eye + span")?.text()?.also { append("\nViews : ${it.replace(" ", ",")}") } + info.selectFirst("i.icon-clock + span")?.text()?.also { append("\nDuration : $it") } + document.select("div.label:contains(Download) ~ a.tag_item") + .eachText() + .joinToString { it.substringAfter(" ") } + .also { append("\nQuality : $it") } + } + genre = document.select("div.label:contains(Tags) ~ a.tag_item:not(:contains(Suggest))") + .eachText() + .joinToString() + status = SAnime.COMPLETED + } + + // ============================== Episodes ============================== + override fun fetchEpisodeList(anime: SAnime): Observable> { + return Observable.just( + listOf( + SEpisode.create().apply { + url = anime.url + name = "Video" + }, + ), + ) + } + + override fun episodeListParse(response: Response) = throw Exception("not used") + override fun episodeListSelector() = throw Exception("not used") override fun episodeFromElement(element: Element) = throw Exception("not used") + private val noRedirectClient by lazy { + client.newBuilder().followRedirects(false).build() + } + + // ============================ Video Links ============================= override fun videoListParse(response: Response): List