GalleryAdults & Fox additions (#6686)

* Expand tag search to 5 pages as 3 are not enough for HentaiFox

* Make launchIO protected instead of private

* Add Top Rated, Most Faved, Most Fapped, Most Downloaded search

* Update versioning

* Address comments

* Move sidebar categories to a map

* Fetch csrf token from tags page

* Move launchIO back to private as it is no longer needed

* Clean position of csrf functions

* Use map with key lookup for popular categories
This commit is contained in:
SilverBeamx 2024-12-22 06:02:45 +01:00 committed by Draff
parent 5588f4d762
commit 003545ac44
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
3 changed files with 114 additions and 3 deletions

View File

@ -2,4 +2,4 @@ plugins {
id("lib-multisrc")
}
baseVersionCode = 3
baseVersionCode = 4

View File

@ -814,8 +814,8 @@ abstract class GalleryAdults(
val tags = mutableListOf<Genre>()
runBlocking {
val jobsPool = mutableListOf<Job>()
// Get first 3 pages
(1..3).forEach { page ->
// Get first 5 pages
(1..5).forEach { page ->
jobsPool.add(
launchIO {
runCatching {

View File

@ -1,10 +1,22 @@
package eu.kanade.tachiyomi.extension.all.hentaifox
import eu.kanade.tachiyomi.multisrc.galleryadults.GalleryAdults
import eu.kanade.tachiyomi.multisrc.galleryadults.Genre
import eu.kanade.tachiyomi.multisrc.galleryadults.SortOrderFilter
import eu.kanade.tachiyomi.multisrc.galleryadults.imgAttr
import eu.kanade.tachiyomi.multisrc.galleryadults.toDate
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
class HentaiFox(
@ -100,4 +112,103 @@ class HentaiFox(
Filter.Header("HINT: Use double quote (\") for exact match"),
) + super.getFilterList().list,
)
private val sidebarPath = "includes/sidebar.php"
private fun sidebarMangaSelector() = "div.item"
private fun Element.sidebarMangaTitle() =
selectFirst("img")?.attr("alt")
private fun Element.sidebarMangaUrl() =
selectFirst("a")?.attr("abs:href")
private fun Element.sidebarMangaThumbnail() =
selectFirst("img")?.imgAttr()
private var csrfToken: String? = null
override fun tagsParser(document: Document): List<Genre> {
csrfToken = csrfParser(document)
return super.tagsParser(document)
}
private fun csrfParser(document: Document): String {
return document.select("[name=csrf-token]").attr("content")
}
private fun setSidebarHeaders(csrfToken: String?): Headers {
if (csrfToken == null) {
return xhrHeaders
}
return xhrHeaders.newBuilder()
.add("X-Csrf-Token", csrfToken)
.build()
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
// Sidebar mangas should always override any other search, so they should appear first
// and only propagate to super when a "normal" search is issued
val sortOrderFilter = filters.filterIsInstance<SortOrderFilter>().firstOrNull()
sortOrderFilter?.let {
val selectedCategory = sortOrderFilter.values.get(sortOrderFilter.state)
if (sidebarCategoriesFilterStateMap.containsKey(selectedCategory)) {
return sidebarRequest(
sidebarCategoriesFilterStateMap.getValue(selectedCategory),
)
}
}
return super.searchMangaRequest(page, query, filters)
}
private fun sidebarRequest(category: String): Request {
val url = "$baseUrl/$sidebarPath"
return POST(
url,
setSidebarHeaders(csrfToken),
FormBody.Builder()
.add("type", category)
.build(),
)
}
override fun searchMangaParse(response: Response): MangasPage {
if (response.request.url.encodedPath.endsWith(sidebarPath)) {
val document = response.asJsoup()
val mangas = document.select(sidebarMangaSelector())
.map {
SMangaDto(
title = it.sidebarMangaTitle()!!,
url = it.sidebarMangaUrl()!!,
thumbnail = it.sidebarMangaThumbnail(),
lang = LANGUAGE_MULTI,
)
}
.map {
SManga.create().apply {
title = it.title
setUrlWithoutDomain(it.url)
thumbnail_url = it.thumbnail
}
}
return MangasPage(mangas, false)
} else {
return super.searchMangaParse(response)
}
}
override fun getSortOrderURIs(): List<Pair<String, String>> {
return super.getSortOrderURIs() + sidebarCategoriesFilterStateMap.toList()
}
companion object {
private val sidebarCategoriesFilterStateMap = mapOf(
"Top Rated" to "top_rated",
"Most Faved" to "top_faved",
"Most Fapped" to "top_fapped",
"Most Downloaded" to "top_downloaded",
).withDefault { "top_rated" }
}
}