From 7fcde32166edcbbba7d2fa910b1ce99aaa22009a Mon Sep 17 00:00:00 2001 From: Nathan Date: Fri, 3 Jan 2025 02:12:54 -0500 Subject: [PATCH] Fix fetching video data and add new setting to filter by specific uploaders by ID (#462) --- src/en/rule34video/build.gradle | 2 +- .../en/rule34video/Rule34Video.kt | 120 +++++++++++++++--- 2 files changed, 102 insertions(+), 20 deletions(-) diff --git a/src/en/rule34video/build.gradle b/src/en/rule34video/build.gradle index 1ab31bdb..fa562806 100644 --- a/src/en/rule34video/build.gradle +++ b/src/en/rule34video/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Rule34Video' extClass = '.Rule34Video' - extVersionCode = 8 + extVersionCode = 9 isNsfw = true } 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 52a84082..cbf1632b 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,8 +1,10 @@ package eu.kanade.tachiyomi.animeextension.en.rule34video - import android.app.Application +import android.util.Log +import androidx.preference.EditTextPreference import androidx.preference.ListPreference import androidx.preference.PreferenceScreen +import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.AnimeFilterList @@ -41,7 +43,21 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { } // ============================== Popular =============================== - override fun popularAnimeRequest(page: Int) = GET("$baseUrl/latest-updates/$page/") + override fun popularAnimeRequest(page: Int): Request { + return if (preferences.getBoolean(PREF_UPLOADER_FILTER_ENABLED_KEY, false)) { + val uploaderId = preferences.getString(PREF_UPLOADER_ID_KEY, "") ?: "" + if (uploaderId.isNotBlank()) { + val url = "$baseUrl/members/$uploaderId/videos/?mode=async&function=get_block&block_id=list_videos_uploaded_videos&sort_by=&from_videos=$page" + Log.e("Rule34Video", "Loading popular videos from uploader ID: $uploaderId, page: $page, URL: $url") + GET(url) + } else { + Log.e("Rule34Video", "Uploader filter enabled but ID is blank, loading latest updates.") + GET("$baseUrl/latest-updates/$page/") + } + } else { + GET("$baseUrl/latest-updates/$page/") + } + } override fun popularAnimeSelector() = "div.item.thumb" @@ -102,21 +118,60 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { // =========================== 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() + title = document.selectFirst("h1.title_video")?.text().toString() + + val infoRow = document.selectFirst("div.info.row") + val detailRows = document.select("div.row") + + val artistElement = detailRows.select("div.col:has(div.label:contains(Artist)) a.item span.name").firstOrNull() + author = artistElement?.text().orEmpty() + 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") + detailRows.select("div.row:has(div.label > em) > div.label > em").html() + .replace("
", "\n") // Ensure single
tags are followed by a newline + .let { text -> + append(text) + } + append("\n\n") // Add extra spacing + + infoRow?.selectFirst("div.item_info:nth-child(1) > span")?.text()?.let { + append("Uploaded: $it\n") + } + + val artist = detailRows.select("div.col:has(div.label:contains(Artist)) a.item span.name") + .eachText() + .joinToString() + if (artist.isNotEmpty()) { + append("Artists: $artist\n") + } + + val categories = detailRows.select("div.col:has(div.label:contains(Categories)) a.item span") + .eachText() + .joinToString() + if (categories.isNotEmpty()) { + append("Categories: $categories\n") + } + + val uploader = detailRows.select("div.col:has(div.label:contains(Uploaded by)) a.item").text() + if (uploader.isNotEmpty()) { + append("Uploader: $uploader\n") + } + + infoRow?.select("div.item_info:nth-child(2) > span")?.text()?.let { + val views = it.substringBefore(" ").replace(",", "") + append("Views: $views\n") + } + infoRow?.select("div.item_info:nth-child(3) > span")?.text()?.let { append("Duration: $it\n") } + document.select("div.row:has(div.label:contains(Download)) a.tag_item") .eachText() .joinToString { it.substringAfter(" ") } - .also { append("\nQuality : $it") } + .also { append("Quality: $it") } } - genre = document.select("div.label:contains(Tags) ~ a.tag_item:not(:contains(Suggest))") + + genre = document.select("div.row_spacer:has(div.label:contains(Tags)) a.tag_item:not(:contains(Suggest))") .eachText() .joinToString() + status = SAnime.COMPLETED } @@ -186,6 +241,24 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { // ============================== Settings ============================== override fun setupPreferenceScreen(screen: PreferenceScreen) { + SwitchPreferenceCompat(screen.context).apply { + key = PREF_UPLOADER_FILTER_ENABLED_KEY + title = "Filter by Uploader" + summary = "Load videos only from the specified uploader ID." + setDefaultValue(false) + }.also(screen::addPreference) + + EditTextPreference(screen.context).apply { + key = PREF_UPLOADER_ID_KEY + title = "Uploader ID" + summary = "Enter the ID of the uploader (e.g., 98965). Requires \"Filter by Uploader\" to be enabled." + dialogTitle = "Enter Uploader ID" + var dependency = PREF_UPLOADER_FILTER_ENABLED_KEY + setOnPreferenceChangeListener { _, newValue -> + newValue?.toString().isNullOrBlank().not() + } + }.also(screen::addPreference) + ListPreference(screen.context).apply { key = PREF_QUALITY_KEY title = PREF_QUALITY_TITLE @@ -218,14 +291,20 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { return tagList.toTypedArray() } - override fun getFilterList(): AnimeFilterList = AnimeFilterList( - OrderFilter(), - CategoryBy(), - AnimeFilter.Separator(), - AnimeFilter.Header("Entered a \"tag\", click on \"filter\" then Click \"reset\" to load tags."), - TagFilter(), - TagSearch(tagsResults(tagDocument)), - ) + override fun getFilterList(): AnimeFilterList = if (preferences.getBoolean(PREF_UPLOADER_FILTER_ENABLED_KEY, false) && + preferences.getString(PREF_UPLOADER_ID_KEY, "")?.isNotBlank() == true + ) { + AnimeFilterList() // If uploader filter is enabled and ID is set, show no other filters + } else { + AnimeFilterList( + OrderFilter(), + CategoryBy(), + AnimeFilter.Separator(), + AnimeFilter.Header("Entered a \"tag\", click on \"filter\" then Click \"reset\" to load tags."), + TagFilter(), + TagSearch(tagsResults(tagDocument)), + ) + } private class TagFilter : AnimeFilter.Text("Click \"reset\" without any text to load all A-Z tags.", "") @@ -267,5 +346,8 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { private const val PREF_QUALITY_TITLE = "Preferred quality" private const val PREF_QUALITY_DEFAULT = "1080p" private val PREF_QUALITY_ENTRIES = arrayOf("2160p", "1080p", "720p", "480p", "360p") + + private const val PREF_UPLOADER_FILTER_ENABLED_KEY = "uploader_filter_enabled" + private const val PREF_UPLOADER_ID_KEY = "uploader_id" } }