fix lint
This commit is contained in:
parent
e2c7543d3e
commit
2e45708568
|
@ -109,7 +109,9 @@ abstract class Madara(
|
|||
protected open val useLoadMoreRequest = LoadMoreStrategy.AutoDetect
|
||||
|
||||
enum class LoadMoreStrategy {
|
||||
AutoDetect, Always, Never
|
||||
AutoDetect,
|
||||
Always,
|
||||
Never,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,7 +120,9 @@ abstract class Madara(
|
|||
private var loadMoreRequestDetected = LoadMoreDetection.Pending
|
||||
|
||||
private enum class LoadMoreDetection {
|
||||
Pending, True, False
|
||||
Pending,
|
||||
True,
|
||||
False,
|
||||
}
|
||||
|
||||
protected fun detectLoadMore(document: Document) {
|
||||
|
@ -132,12 +136,10 @@ abstract class Madara(
|
|||
}
|
||||
}
|
||||
|
||||
protected fun useLoadMoreRequest(): Boolean {
|
||||
return when (useLoadMoreRequest) {
|
||||
LoadMoreStrategy.Always -> true
|
||||
LoadMoreStrategy.Never -> false
|
||||
else -> loadMoreRequestDetected == LoadMoreDetection.True
|
||||
}
|
||||
protected fun useLoadMoreRequest(): Boolean = when (useLoadMoreRequest) {
|
||||
LoadMoreStrategy.Always -> true
|
||||
LoadMoreStrategy.Never -> false
|
||||
else -> loadMoreRequestDetected == LoadMoreDetection.True
|
||||
}
|
||||
|
||||
// Popular Manga
|
||||
|
@ -176,19 +178,17 @@ abstract class Madara(
|
|||
return manga
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request =
|
||||
if (useLoadMoreRequest()) {
|
||||
loadMoreRequest(page, popular = true)
|
||||
} else {
|
||||
GET("$baseUrl/$mangaSubString/${searchPage(page)}?m_orderby=views", headers)
|
||||
}
|
||||
override fun popularMangaRequest(page: Int): Request = if (useLoadMoreRequest()) {
|
||||
loadMoreRequest(page, popular = true)
|
||||
} else {
|
||||
GET("$baseUrl/$mangaSubString/${searchPage(page)}?m_orderby=views", headers)
|
||||
}
|
||||
|
||||
override fun popularMangaNextPageSelector(): String? =
|
||||
if (useLoadMoreRequest()) {
|
||||
"body:not(:has(.no-posts))"
|
||||
} else {
|
||||
"div.nav-previous, nav.navigation-ajax, a.nextpostslink"
|
||||
}
|
||||
override fun popularMangaNextPageSelector(): String? = if (useLoadMoreRequest()) {
|
||||
"body:not(:has(.no-posts))"
|
||||
} else {
|
||||
"div.nav-previous, nav.navigation-ajax, a.nextpostslink"
|
||||
}
|
||||
|
||||
// Latest Updates
|
||||
|
||||
|
@ -199,12 +199,11 @@ abstract class Madara(
|
|||
return popularMangaFromElement(element)
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request =
|
||||
if (useLoadMoreRequest()) {
|
||||
loadMoreRequest(page, popular = false)
|
||||
} else {
|
||||
GET("$baseUrl/$mangaSubString/${searchPage(page)}?m_orderby=latest", headers)
|
||||
}
|
||||
override fun latestUpdatesRequest(page: Int): Request = if (useLoadMoreRequest()) {
|
||||
loadMoreRequest(page, popular = false)
|
||||
} else {
|
||||
GET("$baseUrl/$mangaSubString/${searchPage(page)}?m_orderby=latest", headers)
|
||||
}
|
||||
|
||||
override fun latestUpdatesNextPageSelector(): String? = popularMangaNextPageSelector()
|
||||
|
||||
|
@ -251,20 +250,16 @@ abstract class Madara(
|
|||
return super.fetchSearchManga(page, query, filters)
|
||||
}
|
||||
|
||||
protected open fun searchPage(page: Int): String {
|
||||
return if (page == 1) {
|
||||
""
|
||||
} else {
|
||||
"page/$page/"
|
||||
}
|
||||
protected open fun searchPage(page: Int): String = if (page == 1) {
|
||||
""
|
||||
} else {
|
||||
"page/$page/"
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
return if (useLoadMoreRequest()) {
|
||||
searchLoadMoreRequest(page, query, filters)
|
||||
} else {
|
||||
searchRequest(page, query, filters)
|
||||
}
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = if (useLoadMoreRequest()) {
|
||||
searchLoadMoreRequest(page, query, filters)
|
||||
} else {
|
||||
searchRequest(page, query, filters)
|
||||
}
|
||||
|
||||
protected open fun searchRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
|
@ -310,7 +305,9 @@ abstract class Madara(
|
|||
filter.state
|
||||
.filter { it.state }
|
||||
.let { list ->
|
||||
if (list.isNotEmpty()) { list.forEach { genre -> url.addQueryParameter("genre[]", genre.id) } }
|
||||
if (list.isNotEmpty()) {
|
||||
list.forEach { genre -> url.addQueryParameter("genre[]", genre.id) }
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
|
@ -489,8 +486,7 @@ abstract class Madara(
|
|||
intl["adult_content_filter_only"] to "1",
|
||||
)
|
||||
|
||||
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>, state: Int = 0) :
|
||||
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray(), state) {
|
||||
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>, state: Int = 0) : Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray(), state) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
|
@ -499,21 +495,21 @@ abstract class Madara(
|
|||
protected class AuthorFilter(title: String) : Filter.Text(title)
|
||||
protected class ArtistFilter(title: String) : Filter.Text(title)
|
||||
protected class YearFilter(title: String) : Filter.Text(title)
|
||||
protected class StatusFilter(title: String, status: List<Tag>) :
|
||||
Filter.Group<Tag>(title, status)
|
||||
protected class StatusFilter(title: String, status: List<Tag>) : Filter.Group<Tag>(title, status)
|
||||
|
||||
protected class OrderByFilter(title: String, options: List<Pair<String, String>>, state: Int = 0) :
|
||||
UriPartFilter(title, options.toTypedArray(), state)
|
||||
protected class OrderByFilter(title: String, options: List<Pair<String, String>>, state: Int = 0) : UriPartFilter(title, options.toTypedArray(), state)
|
||||
|
||||
protected class GenreConditionFilter(title: String, options: List<Pair<String, String>>) : UriPartFilter(
|
||||
title,
|
||||
options.toTypedArray(),
|
||||
)
|
||||
protected class GenreConditionFilter(title: String, options: List<Pair<String, String>>) :
|
||||
UriPartFilter(
|
||||
title,
|
||||
options.toTypedArray(),
|
||||
)
|
||||
|
||||
protected class AdultContentFilter(title: String, options: List<Pair<String, String>>) : UriPartFilter(
|
||||
title,
|
||||
options.toTypedArray(),
|
||||
)
|
||||
protected class AdultContentFilter(title: String, options: List<Pair<String, String>>) :
|
||||
UriPartFilter(
|
||||
title,
|
||||
options.toTypedArray(),
|
||||
)
|
||||
|
||||
protected class GenreList(title: String, genres: List<Genre>) : Filter.Group<GenreCheckBox>(title, genres.map { GenreCheckBox(it.name, it.id) })
|
||||
class GenreCheckBox(name: String, val id: String = name) : Filter.CheckBox(name)
|
||||
|
@ -757,32 +753,24 @@ abstract class Madara(
|
|||
open val altName = intl["alt_names_heading"]
|
||||
open val updatingRegex = "Updating|Atualizando".toRegex(RegexOption.IGNORE_CASE)
|
||||
|
||||
fun String.notUpdating(): Boolean {
|
||||
return this.contains(updatingRegex).not()
|
||||
}
|
||||
fun String.notUpdating(): Boolean = this.contains(updatingRegex).not()
|
||||
|
||||
private fun String.containsIn(array: Array<String>): Boolean {
|
||||
return this.lowercase() in array.map { it.lowercase() }
|
||||
}
|
||||
private fun String.containsIn(array: Array<String>): Boolean = this.lowercase() in array.map { it.lowercase() }
|
||||
|
||||
protected open fun imageFromElement(element: Element): String? {
|
||||
return when {
|
||||
element.hasAttr("data-src") -> element.attr("abs:data-src")
|
||||
element.hasAttr("data-lazy-src") -> element.attr("abs:data-lazy-src")
|
||||
element.hasAttr("srcset") -> element.attr("abs:srcset").getSrcSetImage()
|
||||
element.hasAttr("data-cfsrc") -> element.attr("abs:data-cfsrc")
|
||||
else -> element.attr("abs:src")
|
||||
}
|
||||
protected open fun imageFromElement(element: Element): String? = when {
|
||||
element.hasAttr("data-src") -> element.attr("abs:data-src")
|
||||
element.hasAttr("data-lazy-src") -> element.attr("abs:data-lazy-src")
|
||||
element.hasAttr("srcset") -> element.attr("abs:srcset").getSrcSetImage()
|
||||
element.hasAttr("data-cfsrc") -> element.attr("abs:data-cfsrc")
|
||||
else -> element.attr("abs:src")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the best image quality available from srcset
|
||||
*/
|
||||
protected fun String.getSrcSetImage(): String? {
|
||||
return this.split(" ")
|
||||
.filter(URL_REGEX::matches)
|
||||
.maxOfOrNull(String::toString)
|
||||
}
|
||||
protected fun String.getSrcSetImage(): String? = this.split(" ")
|
||||
.filter(URL_REGEX::matches)
|
||||
.maxOfOrNull(String::toString)
|
||||
|
||||
/**
|
||||
* Set it to true if the source uses the new AJAX endpoint to
|
||||
|
@ -807,9 +795,7 @@ abstract class Madara(
|
|||
return POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, form)
|
||||
}
|
||||
|
||||
protected open fun xhrChaptersRequest(mangaUrl: String): Request {
|
||||
return POST("$mangaUrl/ajax/chapters", xhrHeaders)
|
||||
}
|
||||
protected open fun xhrChaptersRequest(mangaUrl: String): Request = POST("$mangaUrl/ajax/chapters", xhrHeaders)
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
|
@ -880,12 +866,10 @@ abstract class Madara(
|
|||
open fun parseChapterDate(date: String?): Long {
|
||||
date ?: return 0
|
||||
|
||||
fun SimpleDateFormat.tryParse(string: String): Long {
|
||||
return try {
|
||||
parse(string)?.time ?: 0
|
||||
} catch (_: ParseException) {
|
||||
0
|
||||
}
|
||||
fun SimpleDateFormat.tryParse(string: String): Long = try {
|
||||
parse(string)?.time ?: 0
|
||||
} catch (_: ParseException) {
|
||||
0
|
||||
}
|
||||
|
||||
return when {
|
||||
|
@ -1006,9 +990,7 @@ abstract class Madara(
|
|||
}
|
||||
}
|
||||
|
||||
override fun imageRequest(page: Page): Request {
|
||||
return GET(page.imageUrl!!, headers.newBuilder().set("Referer", page.url).build())
|
||||
}
|
||||
override fun imageRequest(page: Page): Request = GET(page.imageUrl!!, headers.newBuilder().set("Referer", page.url).build())
|
||||
|
||||
override fun imageUrlParse(document: Document) = ""
|
||||
|
||||
|
@ -1083,26 +1065,22 @@ abstract class Madara(
|
|||
/**
|
||||
* The request to the search page (or another one) that have the genres list.
|
||||
*/
|
||||
protected open fun genresRequest(): Request {
|
||||
return GET("$baseUrl/?s=genre&post_type=wp-manga", headers)
|
||||
}
|
||||
protected open fun genresRequest(): Request = GET("$baseUrl/?s=genre&post_type=wp-manga", headers)
|
||||
|
||||
/**
|
||||
* Get the genres from the search page document.
|
||||
*
|
||||
* @param document The search page document
|
||||
*/
|
||||
protected open fun parseGenres(document: Document): List<Genre> {
|
||||
return document.selectFirst("div.checkbox-group")
|
||||
?.select("div.checkbox")
|
||||
.orEmpty()
|
||||
.map { li ->
|
||||
Genre(
|
||||
li.selectFirst("label")!!.text(),
|
||||
li.selectFirst("input[type=checkbox]")!!.`val`(),
|
||||
)
|
||||
}
|
||||
}
|
||||
protected open fun parseGenres(document: Document): List<Genre> = document.selectFirst("div.checkbox-group")
|
||||
?.select("div.checkbox")
|
||||
.orEmpty()
|
||||
.map { li ->
|
||||
Genre(
|
||||
li.selectFirst("label")!!.text(),
|
||||
li.selectFirst("input[type=checkbox]")!!.`val`(),
|
||||
)
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/66614516
|
||||
protected fun String.decodeHex(): ByteArray {
|
||||
|
|
|
@ -6,13 +6,11 @@ import org.jsoup.nodes.Element
|
|||
class CoffeeManga : Madara("Coffee Manga", "https://coffeemanga.io", "en") {
|
||||
override val useNewChapterEndpoint = false
|
||||
|
||||
override fun imageFromElement(element: Element): String? {
|
||||
return when {
|
||||
element.attr("data-src").isNotBlank() -> element.attr("abs:data-src")
|
||||
element.attr("data-lazy-src").isNotBlank() -> element.attr("abs:data-lazy-src")
|
||||
element.attr("srcset").isNotBlank() -> element.attr("abs:srcset").getSrcSetImage()
|
||||
element.attr("data-cfsrc").isNotBlank() -> element.attr("abs:data-cfsrc")
|
||||
else -> element.attr("abs:src")
|
||||
}
|
||||
override fun imageFromElement(element: Element): String? = when {
|
||||
element.attr("data-src").isNotBlank() -> element.attr("abs:data-src")
|
||||
element.attr("data-lazy-src").isNotBlank() -> element.attr("abs:data-lazy-src")
|
||||
element.attr("srcset").isNotBlank() -> element.attr("abs:srcset").getSrcSetImage()
|
||||
element.attr("data-cfsrc").isNotBlank() -> element.attr("abs:data-cfsrc")
|
||||
else -> element.attr("abs:src")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@ package eu.kanade.tachiyomi.extension.en.luascans
|
|||
|
||||
import eu.kanade.tachiyomi.multisrc.heancms.HeanCms
|
||||
|
||||
class LuaScans : HeanCms(
|
||||
"Lua Scans",
|
||||
"https://luacomic.org",
|
||||
"en",
|
||||
) {
|
||||
class LuaScans :
|
||||
HeanCms(
|
||||
"Lua Scans",
|
||||
"https://luacomic.org",
|
||||
"en",
|
||||
) {
|
||||
// Moved from Keyoapp to HeanCms
|
||||
override val versionId = 3
|
||||
|
||||
|
|
|
@ -2,11 +2,12 @@ package eu.kanade.tachiyomi.extension.en.retsu
|
|||
|
||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||
|
||||
class Retsu : Madara(
|
||||
"Retsu",
|
||||
"https://retsu.org",
|
||||
"en",
|
||||
) {
|
||||
class Retsu :
|
||||
Madara(
|
||||
"Retsu",
|
||||
"https://retsu.org",
|
||||
"en",
|
||||
) {
|
||||
override fun popularMangaSelector() = "div.manga__item"
|
||||
override val popularMangaUrlSelector = "h4 a"
|
||||
|
||||
|
|
|
@ -12,12 +12,13 @@ import java.io.IOException
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class SirenKomik : MangaThemesia(
|
||||
"Siren Komik",
|
||||
"https://sirenkomik.my.id",
|
||||
"id",
|
||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("id")),
|
||||
) {
|
||||
class SirenKomik :
|
||||
MangaThemesia(
|
||||
"Siren Komik",
|
||||
"https://sirenkomik.my.id",
|
||||
"id",
|
||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("id")),
|
||||
) {
|
||||
override val id = 8457447675410081142
|
||||
|
||||
override val hasProjectPage = true
|
||||
|
|
|
@ -32,7 +32,9 @@ import uy.kohesive.injekt.injectLazy
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class SussyToons : HttpSource(), ConfigurableSource {
|
||||
class SussyToons :
|
||||
HttpSource(),
|
||||
ConfigurableSource {
|
||||
|
||||
override val name = "Sussy Toons"
|
||||
|
||||
|
@ -102,9 +104,7 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
|||
|
||||
// ============================= Popular ==================================
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
return GET("$apiUrl/obras/top5", headers)
|
||||
}
|
||||
override fun popularMangaRequest(page: Int): Request = GET("$apiUrl/obras/top5", headers)
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val dto = response.parseAs<WrapperDto<List<MangaDto>>>()
|
||||
|
@ -152,8 +152,7 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
|||
return GET(url, headers)
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(response: Response) =
|
||||
response.parseAs<WrapperDto<MangaDto>>().results.toSManga()
|
||||
override fun mangaDetailsParse(response: Response) = response.parseAs<WrapperDto<MangaDto>>().results.toSManga()
|
||||
|
||||
private val SManga.id: String get() {
|
||||
val mangaUrl = apiUrl.toHttpUrl().newBuilder()
|
||||
|
@ -166,18 +165,16 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
|||
|
||||
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
return response.parseAs<WrapperDto<WrapperChapterDto>>().results.chapters.map {
|
||||
SChapter.create().apply {
|
||||
name = it.name
|
||||
it.chapterNumber?.let {
|
||||
chapter_number = it
|
||||
}
|
||||
setUrlWithoutDomain("$baseUrl/capitulo/${it.id}")
|
||||
date_upload = it.updateAt.toDate()
|
||||
override fun chapterListParse(response: Response): List<SChapter> = response.parseAs<WrapperDto<WrapperChapterDto>>().results.chapters.map {
|
||||
SChapter.create().apply {
|
||||
name = it.name
|
||||
it.chapterNumber?.let {
|
||||
chapter_number = it
|
||||
}
|
||||
}.sortedBy(SChapter::chapter_number).reversed()
|
||||
}
|
||||
setUrlWithoutDomain("$baseUrl/capitulo/${it.id}")
|
||||
date_upload = it.updateAt.toDate()
|
||||
}
|
||||
}.sortedBy(SChapter::chapter_number).reversed()
|
||||
|
||||
// ============================= Pages ====================================
|
||||
|
||||
|
@ -208,30 +205,22 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
|||
Page(index, imageUrl = imageUrl.toString())
|
||||
}
|
||||
}
|
||||
private fun pageListParse(document: Document): List<Page> {
|
||||
return document.select(pageUrlSelector).mapIndexed { index, element ->
|
||||
Page(index, document.location(), element.absUrl("src"))
|
||||
}
|
||||
}
|
||||
private fun extractScriptData(document: Document): String {
|
||||
return document.select("script").map(Element::data)
|
||||
.firstOrNull(pageRegex::containsMatchIn)
|
||||
?: throw Exception("Failed to load pages: Script data not found")
|
||||
private fun pageListParse(document: Document): List<Page> = document.select(pageUrlSelector).mapIndexed { index, element ->
|
||||
Page(index, document.location(), element.absUrl("src"))
|
||||
}
|
||||
private fun extractScriptData(document: Document): String = document.select("script").map(Element::data)
|
||||
.firstOrNull(pageRegex::containsMatchIn)
|
||||
?: throw Exception("Failed to load pages: Script data not found")
|
||||
|
||||
private fun extractJsonContent(scriptData: String): String {
|
||||
return pageRegex.find(scriptData)
|
||||
?.groups?.get(1)?.value
|
||||
?.let { json.decodeFromString<String>("\"$it\"") }
|
||||
?: throw Exception("Failed to extract JSON from script")
|
||||
}
|
||||
private fun extractJsonContent(scriptData: String): String = pageRegex.find(scriptData)
|
||||
?.groups?.get(1)?.value
|
||||
?.let { json.decodeFromString<String>("\"$it\"") }
|
||||
?: throw Exception("Failed to extract JSON from script")
|
||||
|
||||
private fun parseJsonToChapterPageDto(jsonContent: String): ChapterPageDto {
|
||||
return try {
|
||||
jsonContent.parseAs<WrapperDto<ChapterPageDto>>().results
|
||||
} catch (e: Exception) {
|
||||
throw Exception("Failed to load pages: ${e.message}")
|
||||
}
|
||||
private fun parseJsonToChapterPageDto(jsonContent: String): ChapterPageDto = try {
|
||||
jsonContent.parseAs<WrapperDto<ChapterPageDto>>().results
|
||||
} catch (e: Exception) {
|
||||
throw Exception("Failed to load pages: ${e.message}")
|
||||
}
|
||||
|
||||
override fun imageUrlParse(response: Response): String = ""
|
||||
|
@ -345,12 +334,13 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
|||
return json.decodeFromStream(body.byteStream())
|
||||
}
|
||||
|
||||
private inline fun <reified T> String.parseAs(): T {
|
||||
return json.decodeFromString(this)
|
||||
}
|
||||
private inline fun <reified T> String.parseAs(): T = json.decodeFromString(this)
|
||||
|
||||
private fun String.toDate() =
|
||||
try { dateFormat.parse(this)!!.time } catch (_: Exception) { 0L }
|
||||
private fun String.toDate() = try {
|
||||
dateFormat.parse(this)!!.time
|
||||
} catch (_: Exception) {
|
||||
0L
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes path segments:
|
||||
|
|
|
@ -41,13 +41,11 @@ class MangaDto(
|
|||
@SerialName("stt_nome")
|
||||
val value: String?,
|
||||
) {
|
||||
fun toStatus(): Int {
|
||||
return when (value?.lowercase()) {
|
||||
"em andamento" -> SManga.ONGOING
|
||||
"completo" -> SManga.COMPLETED
|
||||
"hiato" -> SManga.ON_HIATUS
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
fun toStatus(): Int = when (value?.lowercase()) {
|
||||
"em andamento" -> SManga.ONGOING
|
||||
"completo" -> SManga.COMPLETED
|
||||
"hiato" -> SManga.ON_HIATUS
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,8 +93,7 @@ class HattoriManga : HttpSource() {
|
|||
csrfToken = document.selectFirst("meta[name=csrf-token]")!!.attr("content")
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response) =
|
||||
throw UnsupportedOperationException()
|
||||
override fun chapterListParse(response: Response) = throw UnsupportedOperationException()
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
val slug = manga.url.substringAfterLast('/')
|
||||
|
@ -116,10 +115,9 @@ class HattoriManga : HttpSource() {
|
|||
return Observable.just(chapters)
|
||||
}
|
||||
|
||||
private fun fetchChapterPageableList(slug: String, page: Int, manga: SManga): HMChapterDto =
|
||||
client.newCall(GET("$baseUrl/load-more-chapters/$slug?page=$page", headers))
|
||||
.execute()
|
||||
.parseAs<HMChapterDto>()
|
||||
private fun fetchChapterPageableList(slug: String, page: Int, manga: SManga): HMChapterDto = client.newCall(GET("$baseUrl/load-more-chapters/$slug?page=$page", headers))
|
||||
.execute()
|
||||
.parseAs<HMChapterDto>()
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/latest-chapters")
|
||||
|
||||
|
@ -149,24 +147,20 @@ class HattoriManga : HttpSource() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
return response.asJsoup().select(".image-wrapper img").mapIndexed { index, element ->
|
||||
Page(index, imageUrl = "$baseUrl${element.attr("data-src")}")
|
||||
}.takeIf { it.isNotEmpty() } ?: throw Exception("Oturum açmanız, WebView'ı açmanız ve oturum açmanız gerekir")
|
||||
}
|
||||
override fun pageListParse(response: Response): List<Page> = response.asJsoup().select(".image-wrapper img").mapIndexed { index, element ->
|
||||
Page(index, imageUrl = "$baseUrl${element.attr("data-src")}")
|
||||
}.takeIf { it.isNotEmpty() } ?: throw Exception("Oturum açmanız, WebView'ı açmanız ve oturum açmanız gerekir")
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
return response.use {
|
||||
val mangas = it.parseAs<HMLatestUpdateDto>().chapters.map {
|
||||
SManga.create().apply {
|
||||
val manga = it.manga
|
||||
title = manga.title
|
||||
thumbnail_url = "$baseUrl/storage/${manga.thumbnail}"
|
||||
url = "/manga/${manga.slug}"
|
||||
}
|
||||
}.distinctBy { manga -> manga.title }
|
||||
MangasPage(mangas, false)
|
||||
}
|
||||
override fun latestUpdatesParse(response: Response): MangasPage = response.use {
|
||||
val mangas = it.parseAs<HMLatestUpdateDto>().chapters.map {
|
||||
SManga.create().apply {
|
||||
val manga = it.manga
|
||||
title = manga.title
|
||||
thumbnail_url = "$baseUrl/storage/${manga.thumbnail}"
|
||||
url = "/manga/${manga.slug}"
|
||||
}
|
||||
}.distinctBy { manga -> manga.title }
|
||||
MangasPage(mangas, false)
|
||||
}
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
|
@ -263,19 +257,18 @@ class HattoriManga : HttpSource() {
|
|||
setUrlWithoutDomain(REGEX_MANGA_URL.find(script)!!.groups[1]!!.value)
|
||||
}
|
||||
|
||||
private fun parseGenres(document: Document): List<Genre> {
|
||||
return document.select(".tags-blog a")
|
||||
.map { element -> Genre(element.text()) }
|
||||
}
|
||||
private fun parseGenres(document: Document): List<Genre> = document.select(".tags-blog a")
|
||||
.map { element -> Genre(element.text()) }
|
||||
|
||||
private inline fun <reified T> Response.parseAs(): T {
|
||||
return json.decodeFromString(body.string())
|
||||
}
|
||||
private inline fun <reified T> Response.parseAs(): T = json.decodeFromString(body.string())
|
||||
|
||||
private fun Response.isPageExpired() = code == 419
|
||||
|
||||
private fun String.toDate(): Long =
|
||||
try { dateFormat.parse(trim())!!.time } catch (_: Exception) { 0L }
|
||||
private fun String.toDate(): Long = try {
|
||||
dateFormat.parse(trim())!!.time
|
||||
} catch (_: Exception) {
|
||||
0L
|
||||
}
|
||||
|
||||
class GenreList(title: String, genres: List<Genre>) : Filter.Group<GenreCheckBox>(title, genres.map { GenreCheckBox(it.name, it.id) })
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ import uy.kohesive.injekt.api.get
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class LxHentai : ParsedHttpSource(), ConfigurableSource {
|
||||
class LxHentai :
|
||||
ParsedHttpSource(),
|
||||
ConfigurableSource {
|
||||
|
||||
override val name = "LXHentai"
|
||||
|
||||
|
@ -41,38 +43,30 @@ class LxHentai : ParsedHttpSource(), ConfigurableSource {
|
|||
|
||||
override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl)
|
||||
|
||||
override fun popularMangaRequest(page: Int) =
|
||||
searchMangaRequest(page, "", FilterList(SortBy(3)))
|
||||
override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", FilterList(SortBy(3)))
|
||||
|
||||
override fun popularMangaSelector() = searchMangaSelector()
|
||||
|
||||
override fun popularMangaFromElement(element: Element) =
|
||||
searchMangaFromElement(element)
|
||||
override fun popularMangaFromElement(element: Element) = searchMangaFromElement(element)
|
||||
|
||||
override fun popularMangaNextPageSelector() =
|
||||
searchMangaNextPageSelector()
|
||||
override fun popularMangaNextPageSelector() = searchMangaNextPageSelector()
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) =
|
||||
searchMangaRequest(page, "", FilterList(SortBy(0)))
|
||||
override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", FilterList(SortBy(0)))
|
||||
|
||||
override fun latestUpdatesSelector() = searchMangaSelector()
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) =
|
||||
searchMangaFromElement(element)
|
||||
override fun latestUpdatesFromElement(element: Element) = searchMangaFromElement(element)
|
||||
|
||||
override fun latestUpdatesNextPageSelector() =
|
||||
searchMangaNextPageSelector()
|
||||
override fun latestUpdatesNextPageSelector() = searchMangaNextPageSelector()
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
return when {
|
||||
query.startsWith(PREFIX_ID_SEARCH) -> {
|
||||
val slug = query.substringAfter(PREFIX_ID_SEARCH)
|
||||
val mangaUrl = "/truyen/$slug"
|
||||
fetchMangaDetails(SManga.create().apply { url = mangaUrl })
|
||||
.map { MangasPage(listOf(it), false) }
|
||||
}
|
||||
else -> super.fetchSearchManga(page, query, filters)
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> = when {
|
||||
query.startsWith(PREFIX_ID_SEARCH) -> {
|
||||
val slug = query.substringAfter(PREFIX_ID_SEARCH)
|
||||
val mangaUrl = "/truyen/$slug"
|
||||
fetchMangaDetails(SManga.create().apply { url = mangaUrl })
|
||||
.map { MangasPage(listOf(it), false) }
|
||||
}
|
||||
else -> super.fetchSearchManga(page, query, filters)
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
|
@ -168,32 +162,33 @@ class LxHentai : ParsedHttpSource(), ConfigurableSource {
|
|||
|
||||
private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
|
||||
|
||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>, state: Int = 0) :
|
||||
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray(), state) {
|
||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>, state: Int = 0) : Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray(), state) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
private class SortBy(state: Int = 0) : UriPartFilter(
|
||||
"Sắp xếp theo",
|
||||
arrayOf(
|
||||
Pair("Mới cập nhật", "-updated_at"),
|
||||
Pair("Mới nhất", "-created_at"),
|
||||
Pair("Cũ nhất", "created_at"),
|
||||
Pair("Xem nhiều", "-views"),
|
||||
Pair("A-Z", "name"),
|
||||
Pair("Z-A", "-name"),
|
||||
),
|
||||
state,
|
||||
)
|
||||
private class SortBy(state: Int = 0) :
|
||||
UriPartFilter(
|
||||
"Sắp xếp theo",
|
||||
arrayOf(
|
||||
Pair("Mới cập nhật", "-updated_at"),
|
||||
Pair("Mới nhất", "-created_at"),
|
||||
Pair("Cũ nhất", "created_at"),
|
||||
Pair("Xem nhiều", "-views"),
|
||||
Pair("A-Z", "name"),
|
||||
Pair("Z-A", "-name"),
|
||||
),
|
||||
state,
|
||||
)
|
||||
|
||||
private class Status : UriPartFilter(
|
||||
"Trạng thái",
|
||||
arrayOf(
|
||||
Pair("Tất cả", "1,2"),
|
||||
Pair("Đang tiến hành", "2"),
|
||||
Pair("Đã hoàn thành", "1"),
|
||||
),
|
||||
)
|
||||
private class Status :
|
||||
UriPartFilter(
|
||||
"Trạng thái",
|
||||
arrayOf(
|
||||
Pair("Tất cả", "1,2"),
|
||||
Pair("Đang tiến hành", "2"),
|
||||
Pair("Đã hoàn thành", "1"),
|
||||
),
|
||||
)
|
||||
|
||||
private class Genre(name: String, val id: Int) : Filter.TriState(name)
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Thể loại", genres)
|
||||
|
|
|
@ -34,39 +34,31 @@ class TruyenTranh3Q : ParsedHttpSource() {
|
|||
.rateLimit(3)
|
||||
.build()
|
||||
|
||||
override fun headersBuilder(): Headers.Builder {
|
||||
return super.headersBuilder().add("Referer", "$baseUrl/")
|
||||
}
|
||||
override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", "$baseUrl/")
|
||||
|
||||
private val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.US)
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
return GET("$baseUrl/danh-sach/truyen-yeu-thich?page=$page", headers)
|
||||
}
|
||||
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/danh-sach/truyen-yeu-thich?page=$page", headers)
|
||||
|
||||
override fun popularMangaSelector(): String = "ul.list_grid.grid > li"
|
||||
|
||||
override fun popularMangaFromElement(element: Element): SManga {
|
||||
return SManga.create().apply {
|
||||
element.select("h3 a").let {
|
||||
title = it.text()
|
||||
setUrlWithoutDomain(it.attr("abs:href"))
|
||||
}
|
||||
thumbnail_url = element.selectFirst(".book_avatar a img")
|
||||
?.absUrl("src")
|
||||
?.let { url ->
|
||||
url.toHttpUrlOrNull()
|
||||
?.queryParameter("url")
|
||||
?: url
|
||||
}
|
||||
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
|
||||
element.select("h3 a").let {
|
||||
title = it.text()
|
||||
setUrlWithoutDomain(it.attr("abs:href"))
|
||||
}
|
||||
thumbnail_url = element.selectFirst(".book_avatar a img")
|
||||
?.absUrl("src")
|
||||
?.let { url ->
|
||||
url.toHttpUrlOrNull()
|
||||
?.queryParameter("url")
|
||||
?: url
|
||||
}
|
||||
}
|
||||
|
||||
override fun popularMangaNextPageSelector(): String? = ".page_redirect > a:last-child > p:not(.active)"
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request {
|
||||
return GET("$baseUrl/danh-sach/truyen-moi-cap-nhat?page=$page", headers)
|
||||
}
|
||||
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/danh-sach/truyen-moi-cap-nhat?page=$page", headers)
|
||||
|
||||
// same as popularManga
|
||||
override fun latestUpdatesSelector(): String = popularMangaSelector()
|
||||
|
@ -121,21 +113,19 @@ class TruyenTranh3Q : ParsedHttpSource() {
|
|||
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
override fun searchMangaNextPageSelector(): String? = popularMangaNextPageSelector()
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga {
|
||||
return SManga.create().apply {
|
||||
document.selectFirst(".book_info > .book_other")?.let { info ->
|
||||
title = info.selectFirst("h1[itemprop=name]")!!.text()
|
||||
author = info.selectFirst("ul.list-info li.author p.col-xs-9")?.text()
|
||||
status = when (info.selectFirst("ul.list-info li.status p.col-xs-9")?.text()) {
|
||||
"Đang Cập Nhật" -> SManga.ONGOING
|
||||
"Hoàn Thành" -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
genre = info.select(".list01 li a").joinToString { it.text() }
|
||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
||||
document.selectFirst(".book_info > .book_other")?.let { info ->
|
||||
title = info.selectFirst("h1[itemprop=name]")!!.text()
|
||||
author = info.selectFirst("ul.list-info li.author p.col-xs-9")?.text()
|
||||
status = when (info.selectFirst("ul.list-info li.status p.col-xs-9")?.text()) {
|
||||
"Đang Cập Nhật" -> SManga.ONGOING
|
||||
"Hoàn Thành" -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
description = document.selectFirst(".book_detail > .story-detail-info")?.text()
|
||||
thumbnail_url = document.selectFirst(".book_detail > .book_info > .book_avatar > img")?.attr("abs:src")
|
||||
genre = info.select(".list01 li a").joinToString { it.text() }
|
||||
}
|
||||
description = document.selectFirst(".book_detail > .story-detail-info")?.text()
|
||||
thumbnail_url = document.selectFirst(".book_detail > .book_info > .book_avatar > img")?.attr("abs:src")
|
||||
}
|
||||
|
||||
// chapters
|
||||
|
@ -183,23 +173,19 @@ class TruyenTranh3Q : ParsedHttpSource() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter {
|
||||
return SChapter.create().apply {
|
||||
element.selectFirst(".name-chap > a")?.let {
|
||||
name = it.text()
|
||||
setUrlWithoutDomain(it.attr("abs:href"))
|
||||
}
|
||||
date_upload = parseChapterDate(element.selectFirst(".time-chap")?.text() ?: "")
|
||||
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
|
||||
element.selectFirst(".name-chap > a")?.let {
|
||||
name = it.text()
|
||||
setUrlWithoutDomain(it.attr("abs:href"))
|
||||
}
|
||||
date_upload = parseChapterDate(element.selectFirst(".time-chap")?.text() ?: "")
|
||||
}
|
||||
|
||||
// parse pages
|
||||
private val pageListSelector = ".chapter_content .page-chapter img"
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
return document.select(pageListSelector).mapIndexed { idx, it ->
|
||||
Page(idx, imageUrl = it.absUrl("data-src"))
|
||||
}
|
||||
override fun pageListParse(document: Document): List<Page> = document.select(pageListSelector).mapIndexed { idx, it ->
|
||||
Page(idx, imageUrl = it.absUrl("data-src"))
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
|
||||
|
@ -248,10 +234,8 @@ class TruyenTranh3Q : ParsedHttpSource() {
|
|||
|
||||
private fun genresRequest() = GET("$baseUrl/$searchPath", headers)
|
||||
|
||||
private fun parseGenres(document: Document): List<Genre> {
|
||||
return document.select(".genre-item").mapIndexed { index, element ->
|
||||
Genre(element.text(), index + 1)
|
||||
}
|
||||
private fun parseGenres(document: Document): List<Genre> = document.select(".genre-item").mapIndexed { index, element ->
|
||||
Genre(element.text(), index + 1)
|
||||
}
|
||||
|
||||
private fun fetchGenres() {
|
||||
|
|
|
@ -36,7 +36,9 @@ import java.io.IOException
|
|||
import java.net.URLDecoder
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class YuriNeko : HttpSource(), ConfigurableSource {
|
||||
class YuriNeko :
|
||||
HttpSource(),
|
||||
ConfigurableSource {
|
||||
|
||||
override val name = "YuriNeko"
|
||||
|
||||
|
@ -107,22 +109,20 @@ class YuriNeko : HttpSource(), ConfigurableSource {
|
|||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException()
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
return when {
|
||||
query.startsWith(PREFIX_ID_SEARCH) -> {
|
||||
val id = query.removePrefix(PREFIX_ID_SEARCH).trim()
|
||||
if (id.toIntOrNull() == null) {
|
||||
throw Exception("ID tìm kiếm không hợp lệ (phải là một số).")
|
||||
}
|
||||
fetchMangaDetails(
|
||||
SManga.create().apply {
|
||||
url = "/manga/$id"
|
||||
},
|
||||
)
|
||||
.map { MangasPage(listOf(it), false) }
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> = when {
|
||||
query.startsWith(PREFIX_ID_SEARCH) -> {
|
||||
val id = query.removePrefix(PREFIX_ID_SEARCH).trim()
|
||||
if (id.toIntOrNull() == null) {
|
||||
throw Exception("ID tìm kiếm không hợp lệ (phải là một số).")
|
||||
}
|
||||
else -> super.fetchSearchManga(page, query, filters)
|
||||
fetchMangaDetails(
|
||||
SManga.create().apply {
|
||||
url = "/manga/$id"
|
||||
},
|
||||
)
|
||||
.map { MangasPage(listOf(it), false) }
|
||||
}
|
||||
else -> super.fetchSearchManga(page, query, filters)
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
|
@ -182,15 +182,13 @@ class YuriNeko : HttpSource(), ConfigurableSource {
|
|||
|
||||
override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response)
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
|
||||
client.newCall(GET("$apiUrl${manga.url}"))
|
||||
.asObservableSuccess()
|
||||
.map { mangaDetailsParse(it) }
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> = client.newCall(GET("$apiUrl${manga.url}"))
|
||||
.asObservableSuccess()
|
||||
.map { mangaDetailsParse(it) }
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request = GET("$baseUrl${manga.url}")
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga =
|
||||
response.parseAs<MangaDto>().toSManga(storageUrl)
|
||||
override fun mangaDetailsParse(response: Response): SManga = response.parseAs<MangaDto>().toSManga(storageUrl)
|
||||
|
||||
override fun chapterListRequest(manga: SManga): Request = GET("$apiUrl${manga.url}")
|
||||
|
||||
|
@ -202,13 +200,11 @@ class YuriNeko : HttpSource(), ConfigurableSource {
|
|||
|
||||
override fun pageListRequest(chapter: SChapter): Request = GET("$apiUrl${chapter.url}")
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> =
|
||||
response.parseAs<ReadResponseDto>().toPageList(storageUrl)
|
||||
override fun pageListParse(response: Response): List<Page> = response.parseAs<ReadResponseDto>().toPageList(storageUrl)
|
||||
|
||||
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
|
||||
|
||||
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
|
||||
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) : Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue