Hide non-manga entries in search on Madara sources (#11304)

* Hide non-manga entries in search on Madara sources.

* Remove deleted method in overrides.

* Remove unused methods and fix final modifier warning.

* Remove commented lines in NS.

* Remove hardcoded selector in old sources.
This commit is contained in:
Alessandro Jean 2022-04-06 08:33:17 -03:00 committed by GitHub
parent 6b3a52ddd7
commit aa9e858dc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 360 additions and 168 deletions

View File

@ -17,41 +17,4 @@ class CafeComYaoi : Madara(
override val client: OkHttpClient = super.client.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.addInterceptor(RateLimitInterceptor(1, 2, TimeUnit.SECONDS)) .addInterceptor(RateLimitInterceptor(1, 2, TimeUnit.SECONDS))
.build() .build()
// [...document.querySelectorAll('input[name="genre[]"]')]
// .map(x => `Genre("${document.querySelector('label[for=' + x.id + ']').innerHTML.trim()}", "${x.value}")`)
// .join(',\n')
override fun getGenreList(): List<Genre> = listOf(
Genre("Ação", "acao"),
Genre("Aventura", "aventura"),
Genre("BDSM", "bdsm"),
Genre("BL", "bl"),
Genre("Comédia", "comedia"),
Genre("Doujinshi", "doujinshi"),
Genre("Drama", "drama"),
Genre("Fantasia", "fantasia"),
Genre("Gender Bender", "gender-bender"),
Genre("Harem", "harem"),
Genre("Histórico", "historico"),
Genre("Horror", "horror"),
Genre("Máfia", "mafia"),
Genre("Mangá", "manga"),
Genre("Manhua", "manhua"),
Genre("Manhwa", "manhwa"),
Genre("Mature", "mature"),
Genre("Mistério", "misterio"),
Genre("Omegaverse", "omegaverse"),
Genre("One shot", "one-shot"),
Genre("Psicológico", "psicologico"),
Genre("Romance", "romance"),
Genre("School Life", "school-life"),
Genre("Sci-fi", "sci-fi"),
Genre("Shoujo", "shoujo"),
Genre("Slice of Life", "slice-of-life"),
Genre("Smut", "smut"),
Genre("Sobrenatural", "sobrenatural"),
Genre("Tragédia", "tragedia"),
Genre("Triângulo Amoroso", "triangulo-amoroso"),
Genre("Webcomic", "webcomic")
)
} }

View File

@ -11,7 +11,16 @@ import org.jsoup.nodes.Element
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class DoujinHentai : Madara("DoujinHentai", "https://doujinhentai.net", "es", SimpleDateFormat("d MMM. yyyy", Locale.ENGLISH)) { class DoujinHentai : Madara(
"DoujinHentai",
"https://doujinhentai.net",
"es",
SimpleDateFormat("d MMM. yyyy", Locale.ENGLISH),
fetchGenresOnInit = false
) {
override val useLoadMoreSearch = false
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/lista-manga-hentai?orderby=views&page=$page", headers) override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/lista-manga-hentai?orderby=views&page=$page", headers)
override fun popularMangaSelector() = "div.col-md-3 a" override fun popularMangaSelector() = "div.col-md-3 a"
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {

View File

@ -13,11 +13,11 @@ class EGYManga : Madara(
SimpleDateFormat("MMMM dd, yyyy", Locale("ar")) SimpleDateFormat("MMMM dd, yyyy", Locale("ar"))
) { ) {
override val pageListParseSelector = "div.separator" // The website does not flag the content.
override val useLoadMoreSearch = false
override val filterNonMangaItems = false
// The website does not flag the content, so we just use the old selector. override val pageListParseSelector = "div.separator"
override fun popularMangaSelector() =
"div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))"
override fun chapterListParse(response: Response): List<SChapter> = override fun chapterListParse(response: Response): List<SChapter> =
super.chapterListParse(response).reversed() super.chapterListParse(response).reversed()

View File

@ -6,6 +6,16 @@ import eu.kanade.tachiyomi.source.model.FilterList
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class GeceninLordu : Madara("Gecenin Lordu", "https://geceninlordu.com/", "tr", SimpleDateFormat("dd MMM yyyy", Locale("tr"))) { class GeceninLordu : Madara(
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = GET("$baseUrl/?s=$query&post_type=wp-manga") "Gecenin Lordu",
"https://geceninlordu.com/",
"tr",
SimpleDateFormat("dd MMM yyyy", Locale("tr")),
fetchGenresOnInit = false
) {
override val useLoadMoreSearch = false
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
GET("$baseUrl/?s=$query&post_type=wp-manga")
} }

View File

@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara
class Gemanga : Madara("Gemanga", "https://gemanga.com", "ar") { class Gemanga : Madara("Gemanga", "https://gemanga.com", "ar") {
// The website does not flag the content, so we just use the old selector. // The website does not flag the content.
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))" override val useLoadMoreSearch = false
override val filterNonMangaItems = false
} }

View File

@ -11,6 +11,7 @@ class HentaiManga : Madara(
dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US) dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US)
) { ) {
// The website does not flag the content, so we just use the old selector. // The website does not flag the content.
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))" override val useLoadMoreSearch = false
override val filterNonMangaItems = false
} }

View File

@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara
class HentaiWebtoon : Madara("HentaiWebtoon", "https://hentaiwebtoon.com", "en") { class HentaiWebtoon : Madara("HentaiWebtoon", "https://hentaiwebtoon.com", "en") {
// The website does not flag the content, so we just use the old selector. // The website does not flag the content.
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))" override val useLoadMoreSearch = false
override val filterNonMangaItems = false
} }

View File

@ -18,9 +18,12 @@ class InstaManhwa : Madara(
"InstaManhwa", "InstaManhwa",
"https://www.instamanhwa.com", "https://www.instamanhwa.com",
"en", "en",
SimpleDateFormat("dd MMMM, yyyy", Locale.US) SimpleDateFormat("dd MMMM, yyyy", Locale.US),
fetchGenresOnInit = false
) { ) {
override val supportsLatest: Boolean = false override val supportsLatest: Boolean = false
override val useLoadMoreSearch = false
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/latest?page=$page", headers) override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/latest?page=$page", headers)
@ -57,9 +60,6 @@ class InstaManhwa : Madara(
return client.newCall(POST("$baseUrl/ajax", headers, body)).execute().asJsoup() return client.newCall(POST("$baseUrl/ajax", headers, body)).execute().asJsoup()
} }
// Not used
override fun getGenreList(): List<Genre> = emptyList()
// Not used // Not used
override fun getFilterList(): FilterList = FilterList() override fun getFilterList(): FilterList = FilterList()
} }

View File

@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara
class MangaGreat : Madara("MangaGreat", "https://mangagreat.com", "en") { class MangaGreat : Madara("MangaGreat", "https://mangagreat.com", "en") {
// The website does not flag the content, so we just use the old selector. // The website does not flag the content.
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))" override val useLoadMoreSearch = false
override val filterNonMangaItems = false
} }

View File

@ -8,10 +8,9 @@ import okhttp3.Response
class ManhuaES : Madara("Manhua ES", "https://manhuaes.com", "en") { class ManhuaES : Madara("Manhua ES", "https://manhuaes.com", "en") {
// The website is incorrectly flagging a lot of their // The website does not flag the content.
// manga content as video and text instead. To bypass this, we override val useLoadMoreSearch = false
// use the old selector that includes all. override val filterNonMangaItems = false
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))"
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
var chapterList = super.chapterListParse(response) var chapterList = super.chapterListParse(response)

View File

@ -4,10 +4,9 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara
class ManhuaPlus : Madara("Manhua Plus", "https://manhuaplus.com", "en") { class ManhuaPlus : Madara("Manhua Plus", "https://manhuaplus.com", "en") {
// The website is incorrectly flagging a lot of their // The website does not flag the content.
// manga content as video instead. To bypass this, we override val useLoadMoreSearch = false
// use the old selector that includes all. override val filterNonMangaItems = false
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))"
override val pageListParseSelector = ".read-container img" override val pageListParseSelector = ".read-container img"
} }

View File

@ -3,10 +3,10 @@ package eu.kanade.tachiyomi.extension.en.manhuaus
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
class ManhuaUS : Madara("ManhuaUS", "https://manhuaus.com", "en") { class ManhuaUS : Madara("ManhuaUS", "https://manhuaus.com", "en") {
override val useNewChapterEndpoint: Boolean = true override val useNewChapterEndpoint: Boolean = true
// The website is incorrectly flagging a lot of their // The website does not flag the content.
// manga content as text instead. To bypass this, we override val useLoadMoreSearch = false
// use the old selector that includes all. override val filterNonMangaItems = false
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))"
} }

View File

@ -30,7 +30,9 @@ abstract class Manhwa18Cc(
override val name: String, override val name: String,
override val baseUrl: String, override val baseUrl: String,
lang: String lang: String
) : Madara(name, baseUrl, lang) { ) : Madara(name, baseUrl, lang, fetchGenresOnInit = false) {
override val useLoadMoreSearch = false
override fun popularMangaSelector() = "div.manga-item" override fun popularMangaSelector() = "div.manga-item"
override val popularMangaUrlSelector = "div.data > h3 > a" override val popularMangaUrlSelector = "div.data > h3 > a"

View File

@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara
class Manhwa18Org : Madara("Manhwa18.org", "https://manhwa18.org", "en") { class Manhwa18Org : Madara("Manhwa18.org", "https://manhwa18.org", "en") {
// The website does not flag the content, so we just use the old selector. // The website does not flag the content.
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))" override val useLoadMoreSearch = false
override val filterNonMangaItems = false
} }

View File

@ -11,6 +11,7 @@ class Manhwa68 : Madara(
dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US) dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US)
) { ) {
// The website does not flag the content, so we just use the old selector. // The website does not flag the content.
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))" override val useLoadMoreSearch = false
override val filterNonMangaItems = false
} }

View File

@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara
class Manhwatop : Madara("Manhwatop", "https://manhwatop.com", "en") { class Manhwatop : Madara("Manhwatop", "https://manhwatop.com", "en") {
// The website does not flag the content, so we just use the old selector. // The website does not flag the content.
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))" override val useLoadMoreSearch = false
override val filterNonMangaItems = false
} }

View File

@ -6,6 +6,7 @@ class ManyToonMe : Madara("ManyToon.me", "https://manytoon.me", "en") {
override val useNewChapterEndpoint: Boolean = true override val useNewChapterEndpoint: Boolean = true
// The website does not flag the content, so we just use the old selector. // The website does not flag the content.
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))" override val useLoadMoreSearch = false
override val filterNonMangaItems = false
} }

View File

@ -11,14 +11,11 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -32,7 +29,7 @@ class NeoxScanlator :
"Neox Scanlator", "Neox Scanlator",
DEFAULT_BASE_URL, DEFAULT_BASE_URL,
"pt-BR", "pt-BR",
SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")) SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR"))
), ),
ConfigurableSource { ConfigurableSource {
@ -57,13 +54,6 @@ class NeoxScanlator :
.add("Accept-Language", ACCEPT_LANGUAGE) .add("Accept-Language", ACCEPT_LANGUAGE)
.add("Referer", REFERER) .add("Referer", REFERER)
override fun searchMangaParse(response: Response): MangasPage {
val mangaPage = super.searchMangaParse(response)
val filteredResult = mangaPage.mangas.filter { it.title.contains(NOVEL_REGEX).not() }
return MangasPage(filteredResult, mangaPage.hasNextPage)
}
// Sometimes the site changes the manga URL. This override will // Sometimes the site changes the manga URL. This override will
// add an error instead of the HTTP 404 to inform the user to // add an error instead of the HTTP 404 to inform the user to
// migrate from Neox to Neox to update the URL. // migrate from Neox to Neox to update the URL.
@ -90,9 +80,6 @@ class NeoxScanlator :
return GET(page.imageUrl!!, newHeaders) return GET(page.imageUrl!!, newHeaders)
} }
// Only status and order by filter work.
override fun getFilterList(): FilterList = FilterList(super.getFilterList().slice(3..4))
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val baseUrlPref = EditTextPreference(screen.context).apply { val baseUrlPref = EditTextPreference(screen.context).apply {
key = BASE_URL_PREF_KEY key = BASE_URL_PREF_KEY
@ -136,7 +123,5 @@ class NeoxScanlator :
"extensão, esta configuração será apagada." "extensão, esta configuração será apagada."
private const val RESTART_TACHIYOMI = "Reinicie o Tachiyomi para aplicar as configurações." private const val RESTART_TACHIYOMI = "Reinicie o Tachiyomi para aplicar as configurações."
private val NOVEL_REGEX = "novel|livro".toRegex(RegexOption.IGNORE_CASE)
} }
} }

View File

@ -9,7 +9,17 @@ import okhttp3.Request
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class PojokManga : Madara("Pojok Manga", "https://pojokmanga.com", "id", SimpleDateFormat("MMM dd, yyyy", Locale.US)) { class PojokManga : Madara(
"Pojok Manga",
"https://pojokmanga.com",
"id",
SimpleDateFormat("MMM dd, yyyy", Locale.US)
) {
override val useLoadMoreSearch = false
override val useNewChapterEndpoint = true
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$baseUrl/${searchPage(page)}".toHttpUrlOrNull()!!.newBuilder() var url = "$baseUrl/${searchPage(page)}".toHttpUrlOrNull()!!.newBuilder()
url.addQueryParameter("s", query) url.addQueryParameter("s", query)
@ -76,22 +86,16 @@ class PojokManga : Madara("Pojok Manga", "https://pojokmanga.com", "id", SimpleD
) )
) )
override fun getFilterList() = FilterList( override fun getFilterList(): FilterList {
AuthorFilter(authorFilterTitle), val filters = super.getFilterList().toMutableList()
ArtistFilter(artistFilterTitle),
YearFilter(yearFilterTitle), filters += listOf(
StatusFilter(statusFilterTitle, getStatusList()),
OrderByFilter(orderByFilterTitle, orderByFilterOptions.zip(orderByFilterOptionsValues)),
AdultContentFilter(adultContentFilterTitle, adultContentFilterOptions),
Filter.Separator(),
Filter.Header(genreFilterHeader),
GenreConditionFilter(genreConditionFilterTitle, genreConditionFilterOptions),
GenreList(genreFilterTitle, getGenreList()),
Filter.Separator(), Filter.Separator(),
Filter.Header("NOTE: cant be used with other filter!"), Filter.Header("NOTE: cant be used with other filter!"),
Filter.Header("$name Project List page"), Filter.Header("$name Project List page"),
ProjectFilter(), ProjectFilter()
) )
override val useNewChapterEndpoint = true return FilterList(filters)
}
} }

View File

@ -14,9 +14,9 @@ class ShieldManga : Madara("Shield Manga", "https://shieldmanga.io", "en") {
.addNetworkInterceptor(rateLimitInterceptor) .addNetworkInterceptor(rateLimitInterceptor)
.build() .build()
// The website does not flag the content, so we just use the old selector. // The website does not flag the content.
override fun popularMangaSelector() = override val useLoadMoreSearch = false
"div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))" override val filterNonMangaItems = false
override fun chapterListSelector() = "li.wp-manga-hapter, .version-chap li" override fun chapterListSelector() = "li.wp-manga-hapter, .version-chap li"
} }

View File

@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara
class Zinmanga : Madara("Zinmanga", "https://zinmanga.com", "en") { class Zinmanga : Madara("Zinmanga", "https://zinmanga.com", "en") {
// The website does not flag the content, so we just use the old selector. // The website does not flag the content.
override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))" override val useLoadMoreSearch = false
override val filterNonMangaItems = false
} }

View File

@ -24,6 +24,8 @@ import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import rx.Single
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -37,7 +39,8 @@ abstract class Madara(
override val name: String, override val name: String,
override val baseUrl: String, override val baseUrl: String,
final override val lang: String, final override val lang: String,
private val dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US) private val dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US),
protected val fetchGenresOnInit: Boolean = true
) : ParsedHttpSource() { ) : ParsedHttpSource() {
override val supportsLatest = true override val supportsLatest = true
@ -52,6 +55,12 @@ abstract class Madara(
protected open val json: Json by injectLazy() protected open val json: Json by injectLazy()
/**
* If enabled, will remove non-manga items in search.
* Can be disabled if the source incorrectly sets the entry types.
*/
protected open val filterNonMangaItems = true
override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/78.0$userAgentRandomizer") .add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/78.0$userAgentRandomizer")
.add("Referer", baseUrl) .add("Referer", baseUrl)
@ -59,7 +68,7 @@ abstract class Madara(
// Popular Manga // Popular Manga
// exclude/filter bilibili manga from list // exclude/filter bilibili manga from list
override fun popularMangaSelector() = "div.page-item-detail.manga:not(:has(a[href*='bilibilicomics.com']))" override fun popularMangaSelector() = "div.page-item-detail:not(:has(a[href*='bilibilicomics.com']))"
open val popularMangaUrlSelector = "div.post-title a" open val popularMangaUrlSelector = "div.post-title a"
@ -93,12 +102,22 @@ abstract class Madara(
add("vars[order]", "desc") add("vars[order]", "desc")
add("vars[sidebar]", if (popular) "full" else "right") add("vars[sidebar]", if (popular) "full" else "right")
add("vars[manga_archives_item_layout]", "big_thumbnail") add("vars[manga_archives_item_layout]", "big_thumbnail")
if (filterNonMangaItems) {
add("vars[meta_query][0][key]", "_wp_manga_chapter_type")
add("vars[meta_query][0][value]", "manga")
}
} }
open val formHeaders: Headers by lazy { headersBuilder().build() } open val formHeaders: Headers by lazy { headersBuilder().build() }
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
return POST("$baseUrl/wp-admin/admin-ajax.php", formHeaders, formBuilder(page, true).build(), CacheControl.FORCE_NETWORK) return POST(
"$baseUrl/wp-admin/admin-ajax.php",
formHeaders,
formBuilder(page, true).build(),
CacheControl.FORCE_NETWORK
)
} }
override fun popularMangaNextPageSelector(): String? = "body:not(:has(.no-posts))" override fun popularMangaNextPageSelector(): String? = "body:not(:has(.no-posts))"
@ -124,18 +143,37 @@ abstract class Madara(
return MangasPage(mangas, mp.hasNextPage) return MangasPage(mangas, mp.hasNextPage)
} }
override fun popularMangaParse(response: Response): MangasPage {
if (genresList == null)
genresList = parseGenres(client.newCall(searchMangaRequest(1, "genre", getFilterList())).execute().asJsoup())
return super.popularMangaParse(response)
}
// Search Manga // Search Manga
open val mangaSubString = "manga" open val mangaSubString = "manga"
/**
* If enabled, the search will use the madara_load_more action instead of
* the normal page. This allows more control over the query and will permit
* the filtering of non-manga items such as novels or videos.
*/
open val useLoadMoreSearch = true
open fun searchFormBuilder(showOnlyManga: Boolean): FormBody.Builder = FormBody.Builder().apply {
add("action", "madara_load_more")
add("page", "0")
add("template", "madara-core/content/content-search")
add("vars[paged]", "1")
add("vars[template]", "archive")
add("vars[sidebar]", "right")
add("vars[post_type]", "wp-manga")
add("vars[post_status]", "publish")
add("vars[manga_archives_item_layout]", "big_thumbnail")
add("vars[post_per_page]", "20")
if (filterNonMangaItems && showOnlyManga) {
add("vars[meta_query][0][key]", "_wp_manga_chapter_type")
add("vars[meta_query][0][value]", "manga")
}
}
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
if (query.startsWith(URL_SEARCH_PREFIX)) { if (query.startsWith(URL_SEARCH_PREFIX) && !useLoadMoreSearch) {
val mangaUrl = "$baseUrl/$mangaSubString/${query.substringAfter(URL_SEARCH_PREFIX)}" val mangaUrl = "$baseUrl/$mangaSubString/${query.substringAfter(URL_SEARCH_PREFIX)}"
return client.newCall(GET(mangaUrl, headers)) return client.newCall(GET(mangaUrl, headers))
.asObservable().map { response -> .asObservable().map { response ->
@ -157,21 +195,25 @@ abstract class Madara(
} }
} }
override fun searchMangaParse(response: Response): MangasPage { protected open fun parseGenres(document: Document): List<Genre> {
if (genresList == null) return document.selectFirst("div.checkbox-group")
genresList = parseGenres(response.asJsoup(response.peekBody(Long.MAX_VALUE).string())) ?.select("div.checkbox")
return super.searchMangaParse(response) .orEmpty()
} .map { li ->
Genre(
private fun parseGenres(document: Document): List<Genre>? { li.selectFirst("label").text(),
return document.selectFirst("div.checkbox-group")?.select("div.checkbox")?.map { li -> li.selectFirst("input[type=checkbox]").`val`()
Genre(li.selectFirst("label").text(), li.selectFirst("input[type=checkbox]").`val`()) )
} }
} }
protected open fun searchPage(page: Int): String = "page/$page/" protected open fun searchPage(page: Int): String = "page/$page/"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (useLoadMoreSearch) {
return searchLoadMoreRequest(page, query, filters)
}
val url = "$baseUrl/${searchPage(page)}".toHttpUrlOrNull()!!.newBuilder() val url = "$baseUrl/${searchPage(page)}".toHttpUrlOrNull()!!.newBuilder()
url.addQueryParameter("s", query) url.addQueryParameter("s", query)
url.addQueryParameter("post_type", "wp-manga") url.addQueryParameter("post_type", "wp-manga")
@ -222,6 +264,147 @@ abstract class Madara(
return GET(url.toString(), headers) return GET(url.toString(), headers)
} }
protected open fun searchLoadMoreRequest(page: Int, query: String, filters: FilterList): Request {
val showOnlyManga = filters.filterIsInstance<ShowOnlyMangaFilter>()
.firstOrNull()?.state ?: true
val formBodyBuilder = searchFormBuilder(showOnlyManga).apply {
if (query.startsWith(URL_SEARCH_PREFIX)) {
add("vars[name]", query.removePrefix(URL_SEARCH_PREFIX))
return@apply
}
add("vars[s]", query)
var metaQueryIdx = if (filterNonMangaItems && showOnlyManga) 1 else 0
var taxQueryIdx = 0
val genres = filters.filterIsInstance<GenreList>().firstOrNull()?.state
?.filter { it.state }
?.map { it.id }
.orEmpty()
filters.forEach { filter ->
when (filter) {
is AuthorFilter -> {
if (filter.state.isNotBlank()) {
add("vars[tax_query][$taxQueryIdx][taxonomy]", "wp-manga-author")
add("vars[tax_query][$taxQueryIdx][field]", "name")
add("vars[tax_query][$taxQueryIdx][terms]", filter.state)
taxQueryIdx++
}
}
is ArtistFilter -> {
if (filter.state.isNotBlank()) {
add("vars[tax_query][$taxQueryIdx][taxonomy]", "wp-manga-artist")
add("vars[tax_query][$taxQueryIdx][field]", "name")
add("vars[tax_query][$taxQueryIdx][terms]", filter.state)
taxQueryIdx++
}
}
is YearFilter -> {
if (filter.state.isNotBlank()) {
add("vars[tax_query][$taxQueryIdx][taxonomy]", "wp-manga-release")
add("vars[tax_query][$taxQueryIdx][field]", "name")
add("vars[tax_query][$taxQueryIdx][terms]", filter.state)
taxQueryIdx++
}
}
is StatusFilter -> {
val statuses = filter.state
.filter { it.state }
.map { it.id }
if (statuses.isNotEmpty()) {
add("vars[meta_query][$metaQueryIdx][key]", "_wp_manga_status")
statuses.forEachIndexed { i, slug ->
add("vars[meta_query][$metaQueryIdx][value][$i]", slug)
}
metaQueryIdx++
}
}
is OrderByFilter -> {
if (filter.state != 0) {
when (filter.toUriPart()) {
"latest" -> {
add("vars[orderby]", "meta_value_num")
add("vars[order]", "DESC")
add("vars[meta_key]", "_latest_update")
}
"alphabet" -> {
add("vars[orderby]", "post_title")
add("vars[order]", "ASC")
}
"rating" -> {
add("vars[orderby][query_average_reviews]", "DESC")
add("vars[orderby][query_total_reviews]", "DESC")
}
"trending" -> {
add("vars[orderby]", "meta_value_num")
add("vars[meta_key]", "_wp_manga_week_views_value")
add("vars[order]", "DESC")
}
"views" -> {
add("vars[orderby]", "meta_value_num")
add("vars[meta_key]", "_wp_manga_views")
add("vars[order]", "DESC")
}
else -> {
add("vars[orderby]", "date")
add("vars[order]", "DESC")
}
}
}
}
is AdultContentFilter -> {
if (filter.state != 0) {
add("vars[meta_query][$metaQueryIdx][key]", "manga_adult_content")
add(
"vars[meta_query][$metaQueryIdx][compare]",
if (filter.state == 1) "not exists" else "exists"
)
metaQueryIdx++
}
}
is GenreConditionFilter -> {
if (filter.state == 1 && genres.isNotEmpty()) {
add("vars[tax_query][$taxQueryIdx][operation]", "AND")
}
}
is GenreList -> {
if (genres.isNotEmpty()) {
add("vars[tax_query][$taxQueryIdx][taxonomy]", "wp-manga-genre")
add("vars[tax_query][$taxQueryIdx][field]", "slug")
genres.forEachIndexed { i, slug ->
add("vars[tax_query][$taxQueryIdx][terms][$i]", slug)
}
taxQueryIdx++
}
}
}
}
}
val searchHeaders = headersBuilder()
.add("X-Requested-With", "XMLHttpRequest")
.build()
return POST(
"$baseUrl/wp-admin/admin-ajax.php",
searchHeaders,
formBodyBuilder.build(),
CacheControl.FORCE_NETWORK
)
}
protected open val authorFilterTitle: String = when (lang) { protected open val authorFilterTitle: String = when (lang) {
"pt-BR" -> "Autor" "pt-BR" -> "Autor"
else -> "Author" else -> "Author"
@ -258,11 +441,11 @@ abstract class Madara(
protected open val orderByFilterOptions: Array<String> = when (lang) { protected open val orderByFilterOptions: Array<String> = when (lang) {
"pt-BR" -> arrayOf( "pt-BR" -> arrayOf(
"<selecione>", "Recentes", "A-Z", "Avaliação", "Relevância", "Recentes", "A-Z", "Avaliação",
"Tendência", "Visualizações", "Novos" "Tendência", "Visualizações", "Novos"
) )
else -> arrayOf( else -> arrayOf(
"<select>", "Latest", "A-Z", "Rating", "Relevance", "Latest", "A-Z", "Rating",
"Trending", "Most Views", "New" "Trending", "Most Views", "New"
) )
} }
@ -292,7 +475,7 @@ abstract class Madara(
} }
protected open val genreFilterHeader: String = when (lang) { protected open val genreFilterHeader: String = when (lang) {
"pt-BR" -> "O filtro de gêneros pode não funcionar em algumas fontes" "pt-BR" -> "O filtro de gêneros pode não funcionar"
else -> "Genres filter may not work for all sources" else -> "Genres filter may not work for all sources"
} }
@ -301,9 +484,9 @@ abstract class Madara(
else -> "Genres" else -> "Genres"
} }
protected open val genreFilterResetWarning: String = when (lang) { protected open val showOnlyMangaEntriesLabel: String = when (lang) {
"pt-BR" -> "Aperte redefinir para tentar carregar os gêneros" "pt-BR" -> "Mostrar somente mangás"
else -> "Press reset to attempt to fetch genres" else -> "Show only manga entries"
} }
protected class AuthorFilter(title: String) : Filter.Text(title) protected class AuthorFilter(title: String) : Filter.Text(title)
@ -312,8 +495,8 @@ abstract class Madara(
protected class StatusFilter(title: String, status: List<Tag>) : protected class StatusFilter(title: String, status: List<Tag>) :
Filter.Group<Tag>(title, status) Filter.Group<Tag>(title, status)
protected class OrderByFilter(title: String, options: List<Pair<String, String>>) : protected class OrderByFilter(title: String, options: List<Pair<String, String>>, state: Int = 0) :
UriPartFilter(title, options.toTypedArray()) UriPartFilter(title, options.toTypedArray(), state)
protected class GenreConditionFilter(title: String, options: Array<String>) : UriPartFilter( protected class GenreConditionFilter(title: String, options: Array<String>) : UriPartFilter(
title, title,
@ -328,34 +511,46 @@ abstract class Madara(
protected class GenreList(title: String, genres: List<Genre>) : Filter.Group<Genre>(title, genres) protected class GenreList(title: String, genres: List<Genre>) : Filter.Group<Genre>(title, genres)
class Genre(name: String, val id: String = name) : Filter.CheckBox(name) class Genre(name: String, val id: String = name) : Filter.CheckBox(name)
private var genresList: List<Genre>? = null protected class ShowOnlyMangaFilter(label: String) : Filter.CheckBox(label, true)
protected open fun getGenreList(): List<Genre> { private var genresList: List<Genre> = emptyList()
// Filters are fetched immediately once an extension loads
// We're only able to get filters after a loading the manga directory, and resetting
// the filters is the only thing that seems to reinflate the view
return genresList ?: listOf(Genre(genreFilterResetWarning, ""))
}
override fun getFilterList() = FilterList( override fun getFilterList(): FilterList {
val filters = mutableListOf(
AuthorFilter(authorFilterTitle), AuthorFilter(authorFilterTitle),
ArtistFilter(artistFilterTitle), ArtistFilter(artistFilterTitle),
YearFilter(yearFilterTitle), YearFilter(yearFilterTitle),
StatusFilter(statusFilterTitle, getStatusList()), StatusFilter(statusFilterTitle, getStatusList()),
OrderByFilter(orderByFilterTitle, orderByFilterOptions.zip(orderByFilterOptionsValues)), OrderByFilter(
AdultContentFilter(adultContentFilterTitle, adultContentFilterOptions), orderByFilterTitle,
orderByFilterOptions.zip(orderByFilterOptionsValues),
if (useLoadMoreSearch) 5 else 0
),
AdultContentFilter(adultContentFilterTitle, adultContentFilterOptions)
)
if (useLoadMoreSearch) {
filters.add(ShowOnlyMangaFilter(showOnlyMangaEntriesLabel))
}
if (genresList.isNotEmpty()) {
filters += listOf(
Filter.Separator(), Filter.Separator(),
Filter.Header(genreFilterHeader), Filter.Header(genreFilterHeader),
GenreConditionFilter(genreConditionFilterTitle, genreConditionFilterOptions), GenreConditionFilter(genreConditionFilterTitle, genreConditionFilterOptions),
GenreList(genreFilterTitle, getGenreList()) GenreList(genreFilterTitle, genresList)
) )
}
return FilterList(filters)
}
protected fun getStatusList() = statusFilterOptionsValues protected fun getStatusList() = statusFilterOptionsValues
.zip(statusFilterOptions) .zip(statusFilterOptions)
.map { Tag(it.first, it.second) } .map { Tag(it.first, it.second) }
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) : open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>, state: Int = 0) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) { Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray(), state) {
fun toUriPart() = vals[state].second fun toUriPart() = vals[state].second
} }
@ -755,8 +950,25 @@ abstract class Madara(
runCatching { client.newCall(request).execute().close() } runCatching { client.newCall(request).execute().close() }
} }
init {
if (fetchGenresOnInit && genresList.isEmpty()) {
Single
.fromCallable {
val genres = runCatching {
client.newCall(GET("$baseUrl/?s=&post_type=wp-manga")).execute()
.use { parseGenres(it.asJsoup()) }
}
genresList = genres.getOrNull().orEmpty()
}
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe()
}
}
companion object { companion object {
const val URL_SEARCH_PREFIX = "SLUG:" const val URL_SEARCH_PREFIX = "slug:"
} }
} }

View File

@ -10,7 +10,7 @@ class MadaraGenerator : ThemeSourceGenerator {
override val themeClass = "Madara" override val themeClass = "Madara"
override val baseVersionCode: Int = 17 override val baseVersionCode: Int = 18
override val sources = listOf( override val sources = listOf(
MultiLang("Leviatan Scans", "https://leviatanscans.com", listOf("en", "es"), className = "LeviatanScansFactory", overrideVersionCode = 9), MultiLang("Leviatan Scans", "https://leviatanscans.com", listOf("en", "es"), className = "LeviatanScansFactory", overrideVersionCode = 9),