YushukeMangas: Fix content loading and bump versionID (#6986)
* Fix content loading and bump versionID * Remove mutableList instance Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> * Remove use function Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> * Remove unused code --------- Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
This commit is contained in:
parent
73013e9d46
commit
d7f724243c
|
@ -13,8 +13,8 @@
|
|||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="yushukemangas.com"
|
||||
android:pathPattern="/manga"
|
||||
android:host="new.yushukemangas.com"
|
||||
android:pathPattern="/manga/..*"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
ext {
|
||||
extName = 'Yushuke Mangas'
|
||||
extClass = '.YushukeMangas'
|
||||
extVersionCode = 1
|
||||
isNsfw = true
|
||||
extVersionCode = 2
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -11,81 +11,93 @@ import eu.kanade.tachiyomi.source.model.SChapter
|
|||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class YushukeMangas : ParsedHttpSource() {
|
||||
|
||||
override val name = "Yushuke Mangas"
|
||||
|
||||
override val baseUrl = "https://yushukemangas.com"
|
||||
override val baseUrl = "https://new.yushukemangas.com"
|
||||
|
||||
override val lang = "pt-BR"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val versionId = 2
|
||||
|
||||
override val client = network.cloudflareClient.newBuilder()
|
||||
.rateLimit(3)
|
||||
.rateLimit(1, 2)
|
||||
.build()
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
// ============================== Popular ===============================
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET(baseUrl, headers)
|
||||
|
||||
override fun popularMangaSelector() = ".popular-manga-widget .popular-manga-item"
|
||||
override fun popularMangaSelector() = "#semanal a.top-item"
|
||||
|
||||
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
|
||||
title = element.selectFirst("h3")!!.text()
|
||||
thumbnail_url = element.selectFirst("img")?.absUrl("src")
|
||||
setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
|
||||
setUrlWithoutDomain(element.absUrl("href"))
|
||||
}
|
||||
|
||||
override fun popularMangaNextPageSelector() = null
|
||||
|
||||
// ============================== Latest ===============================
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/?page=$page", headers)
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/obras", headers)
|
||||
|
||||
override fun latestUpdatesSelector() = ".manga-list .manga-item"
|
||||
override fun latestUpdatesSelector() = ".obras-grid .manga-card a"
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
|
||||
title = element.selectFirst("h2")!!.text()
|
||||
thumbnail_url = element.selectFirst("img")?.absUrl("src")
|
||||
setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
|
||||
}
|
||||
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = ".pagination-next"
|
||||
override fun latestUpdatesNextPageSelector() = null
|
||||
|
||||
// ============================== Search ===============================
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
var url = "$baseUrl/search".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("q", query)
|
||||
.addQueryParameter("page", "1")
|
||||
.build()
|
||||
filters.forEach { filter ->
|
||||
val urlFilterBuilder = filters.fold("$baseUrl/obras".toHttpUrl().newBuilder()) { urlBuilder, filter ->
|
||||
when (filter) {
|
||||
is GenreFilter -> {
|
||||
is RadioFilter -> {
|
||||
val selected = filter.selected()
|
||||
if (selected == all) return@forEach
|
||||
url = "$baseUrl/generos.php".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("genre", selected)
|
||||
.addQueryParameter("search", query)
|
||||
.build()
|
||||
if (selected == all) return@fold urlBuilder
|
||||
urlBuilder.addQueryParameter(filter.query, selected)
|
||||
}
|
||||
else -> {}
|
||||
is GenreFilter -> {
|
||||
filter.state
|
||||
.filter(GenreCheckBox::state)
|
||||
.fold(urlBuilder) { builder, genre ->
|
||||
builder.addQueryParameter(filter.query, genre.id)
|
||||
}
|
||||
}
|
||||
else -> urlBuilder
|
||||
}
|
||||
}
|
||||
return GET(url, headers)
|
||||
|
||||
val url = when {
|
||||
query.isBlank() -> urlFilterBuilder
|
||||
else -> baseUrl.toHttpUrl().newBuilder().addQueryParameter("search", query)
|
||||
}
|
||||
|
||||
return GET(url.build(), headers)
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
if (query.startsWith(PREFIX_SEARCH)) {
|
||||
val id = query.substringAfter(PREFIX_SEARCH)
|
||||
return client.newCall(GET("$baseUrl/manga?id=$id", headers))
|
||||
val slug = query.substringAfter(PREFIX_SEARCH)
|
||||
return client.newCall(GET("$baseUrl/manga/$slug", headers))
|
||||
.asObservableSuccess()
|
||||
.map {
|
||||
val manga = mangaDetailsParse(it.asJsoup())
|
||||
|
@ -95,12 +107,24 @@ class YushukeMangas : ParsedHttpSource() {
|
|||
return super.fetchSearchManga(page, query, filters)
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = "${latestUpdatesSelector()}, a.search-item"
|
||||
override fun searchMangaSelector() = ".search-result-item"
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
return if (response.request.url.queryParameter("search").isNullOrBlank()) {
|
||||
latestUpdatesParse(response)
|
||||
} else {
|
||||
super.searchMangaParse(response)
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchMangaFromElement(element: Element) = SManga.create().apply {
|
||||
title = element.selectFirst("h3, .search-title")!!.text()
|
||||
title = element.selectFirst(".search-result-title")!!.text()
|
||||
thumbnail_url = element.selectFirst("img")?.absUrl("src")
|
||||
setUrlWithoutDomain((element.selectFirst("a") ?: element).absUrl("href"))
|
||||
setUrlWithoutDomain(
|
||||
element.attr("onclick").let {
|
||||
SEARCH_URL_REGEX.find(it)?.groups?.get(1)?.value!!
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun searchMangaNextPageSelector() = null
|
||||
|
@ -108,71 +132,150 @@ class YushukeMangas : ParsedHttpSource() {
|
|||
// ============================== Manga Details =========================
|
||||
|
||||
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
|
||||
val details = document.selectFirst(".manga-header")!!
|
||||
val details = document.selectFirst(".manga-banner .container")!!
|
||||
title = details.selectFirst("h1")!!.text()
|
||||
thumbnail_url = details.selectFirst(".manga-image img")?.absUrl("src")
|
||||
genre = details.select(".manga-generos .genre-button").joinToString { it.text() }
|
||||
description = details.selectFirst("p.manga-sinopse")?.text()
|
||||
thumbnail_url = details.selectFirst("img")?.absUrl("src")
|
||||
genre = details.select(".genre-tag").joinToString { it.text() }
|
||||
description = details.selectFirst(".sinopse p")?.text()
|
||||
details.selectFirst(".manga-meta > div")?.ownText()?.let {
|
||||
status = when (it.lowercase()) {
|
||||
"em andamento" -> SManga.ONGOING
|
||||
"completo" -> SManga.COMPLETED
|
||||
"cancelado" -> SManga.CANCELLED
|
||||
"hiato" -> SManga.ON_HIATUS
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
||||
setUrlWithoutDomain(document.location())
|
||||
}
|
||||
|
||||
private fun SManga.fetchMangaId(): String {
|
||||
val document = client.newCall(mangaDetailsRequest(this)).execute().asJsoup()
|
||||
return document.select("script")
|
||||
.map(Element::data)
|
||||
.firstOrNull(MANGA_ID_REGEX::containsMatchIn)
|
||||
?.let { MANGA_ID_REGEX.find(it)?.groups?.get(1)?.value }
|
||||
?: throw Exception("Manga ID não encontrado")
|
||||
}
|
||||
|
||||
// ============================== Chapters ===============================
|
||||
|
||||
override fun chapterListSelector() = ".chapter-list .chapter-item a"
|
||||
override fun chapterListSelector() = "a.chapter-item"
|
||||
|
||||
override fun chapterFromElement(element: Element) = SChapter.create().apply {
|
||||
name = element.selectFirst(".chapter-number")!!.text()
|
||||
setUrlWithoutDomain(element.absUrl("href"))
|
||||
}
|
||||
|
||||
private fun chapterListNextPageSelector() = latestUpdatesNextPageSelector()
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
val mangaId = manga.fetchMangaId()
|
||||
val chapters = mutableListOf<SChapter>()
|
||||
var page = 1
|
||||
do {
|
||||
val document = fetchChapterListPage(manga, page++)
|
||||
val dto = fetchChapterListPage(mangaId, page++).parseAs<ChaptersDto>()
|
||||
val document = Jsoup.parseBodyFragment(dto.chapters, baseUrl)
|
||||
chapters += document.select(chapterListSelector()).map(::chapterFromElement)
|
||||
} while (document.selectFirst(chapterListNextPageSelector()) != null)
|
||||
} while (dto.hasNext())
|
||||
return Observable.just(chapters)
|
||||
}
|
||||
|
||||
private fun fetchChapterListPage(manga: SManga, page: Int): Document {
|
||||
private fun fetchChapterListPage(mangaId: String, page: Int): Response {
|
||||
val url = "$baseUrl/ajax/load_more_chapters.php?order=DESC".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("manga_id", mangaId)
|
||||
.addQueryParameter("page", page.toString())
|
||||
.build()
|
||||
|
||||
return client
|
||||
.newCall(GET("$baseUrl${manga.url}&page=$page", headers))
|
||||
.execute().asJsoup()
|
||||
.newCall(GET(url, headers))
|
||||
.execute()
|
||||
}
|
||||
|
||||
// ============================== Pages ===============================
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
return document.select(".chapter-images img").mapIndexed { index, imageUrl ->
|
||||
return document.select(".manga-container .manga-image").mapIndexed { index, imageUrl ->
|
||||
Page(index, imageUrl = imageUrl.absUrl("src"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document) = ""
|
||||
|
||||
// ============================== Filters ===============================
|
||||
// ============================== Filters =============================
|
||||
|
||||
open class GenreFilter(displayName: String, private val vals: Array<String>, state: Int = 0) :
|
||||
Filter.Select<String>(displayName, vals, state) {
|
||||
override fun getFilterList(): FilterList {
|
||||
return FilterList(
|
||||
RadioFilter("Status", "status", statusList),
|
||||
RadioFilter("Tipo", "tipo", typeList),
|
||||
GenreFilter("Gêneros", "tags[]", genresList),
|
||||
)
|
||||
}
|
||||
|
||||
class RadioFilter(
|
||||
displayName: String,
|
||||
val query: String,
|
||||
private val vals: Array<String>,
|
||||
state: Int = 0,
|
||||
) : Filter.Select<String>(displayName, vals, state) {
|
||||
fun selected() = vals[state]
|
||||
}
|
||||
|
||||
override fun getFilterList() = FilterList(GenreFilter("Gêneros", genresList))
|
||||
protected class GenreFilter(
|
||||
title: String,
|
||||
val query: String,
|
||||
genres: List<String>,
|
||||
) : Filter.Group<GenreCheckBox>(title, genres.map { GenreCheckBox(it) })
|
||||
|
||||
class GenreCheckBox(name: String, val id: String = name) : Filter.CheckBox(name)
|
||||
|
||||
private val all = "Todos"
|
||||
|
||||
private val genresList = arrayOf(
|
||||
all, "+18", "Abuso", "Adulto", "Amor Puro", "Artes Marciais",
|
||||
"Aventura", "Ação", "Comédia", "Crime", "Cultivação", "Drama",
|
||||
"Fantasia", "Gap Girls", "Gore", "Harém", "Histórico", "Horror",
|
||||
"Isekai", "Mistério", "Overpowered", "Psicológico", "Reencarnação",
|
||||
"Romance", "Sistema", "Tragédia", "Viagem no Tempo", "Violência",
|
||||
private val statusList = arrayOf(
|
||||
all,
|
||||
"Em andamento",
|
||||
"Completo",
|
||||
"Cancelado",
|
||||
"Hiato",
|
||||
)
|
||||
|
||||
private val typeList = arrayOf(
|
||||
all,
|
||||
"Mangá",
|
||||
"Manhwa",
|
||||
"Manhua",
|
||||
"Comics",
|
||||
)
|
||||
|
||||
private var genresList: List<String> = listOf(
|
||||
"Ação", "Artes Marciais", "Aventura",
|
||||
"Comédia",
|
||||
"Drama",
|
||||
"Escolar",
|
||||
"Esporte",
|
||||
"Fantasia",
|
||||
"Harém", "Histórico",
|
||||
"Isekai",
|
||||
"Josei",
|
||||
"Mistério",
|
||||
"Reencarnação", "Regressão", "Romance",
|
||||
"Sci-fi", "Seinen", "Shoujo", "Shounen", "Slice of Life", "Sobrenatural", "Super Poderes",
|
||||
"Terror",
|
||||
"Vingança",
|
||||
)
|
||||
|
||||
// ============================== Utilities ===========================
|
||||
|
||||
private inline fun <reified T> Response.parseAs(): T {
|
||||
return json.decodeFromStream(body.byteStream())
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class ChaptersDto(val chapters: String, private val remaining: Int) {
|
||||
fun hasNext() = remaining > 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PREFIX_SEARCH = "id:"
|
||||
val SEARCH_URL_REGEX = "'([^']+)".toRegex()
|
||||
val MANGA_ID_REGEX = """obra_id:\s+(\d+)""".toRegex()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@ class YushukeMangasUrlActivity : Activity() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val id = intent?.data?.getQueryParameter("id")
|
||||
if (id != null) {
|
||||
val pathSegment = intent?.data?.pathSegments
|
||||
if (pathSegment != null && pathSegment.size > 1) {
|
||||
val mainIntent = Intent().apply {
|
||||
action = "eu.kanade.tachiyomi.SEARCH"
|
||||
putExtra("query", "${YushukeMangas.PREFIX_SEARCH}$id")
|
||||
putExtra("query", "${YushukeMangas.PREFIX_SEARCH}${pathSegment[1]}")
|
||||
putExtra("filter", packageName)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue