parent
f93f28cbd4
commit
ee394dc2a8
@ -5,7 +5,7 @@ ext {
|
||||
appName = 'Tachiyomi: LectorManga'
|
||||
pkgNameSuffix = 'es.lectormanga'
|
||||
extClass = '.LectorManga'
|
||||
extVersionCode = 9
|
||||
extVersionCode = 10
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
|
@ -7,16 +7,28 @@ import android.support.v7.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.source.model.*
|
||||
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.Page
|
||||
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 okhttp3.*
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
@ -24,26 +36,24 @@ import java.util.concurrent.TimeUnit
|
||||
*/
|
||||
class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
//Info
|
||||
|
||||
override val name = "LectorManga"
|
||||
|
||||
override val baseUrl = "https://lectormanga.com/"
|
||||
|
||||
override val baseUrl = "https://lectormanga.com"
|
||||
override val lang = "es"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val rateLimitInterceptor = RateLimitInterceptor(1)
|
||||
//Client
|
||||
|
||||
private val rateLimitInterceptor = RateLimitInterceptor(1)
|
||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||
.addNetworkInterceptor(rateLimitInterceptor)
|
||||
.connectTimeout(1, TimeUnit.MINUTES)
|
||||
.readTimeout(1, TimeUnit.MINUTES)
|
||||
.retryOnConnectionFailure(true)
|
||||
.followRedirects(true)
|
||||
.build()!!
|
||||
|
||||
.build()
|
||||
private val userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"
|
||||
|
||||
override fun headersBuilder(): Headers.Builder {
|
||||
return Headers.Builder()
|
||||
.add("User-Agent", userAgent)
|
||||
@ -64,24 +74,23 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
.toString()
|
||||
}
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override fun popularMangaSelector() = ".col-6 .card"
|
||||
|
||||
override fun latestUpdatesSelector() = "div.table-responsive:first-child td[scope=row]:nth-child(5n-3)"
|
||||
//Popular
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/library?order_item=likes_count&order_dir=desc&type=&filter_by=title&page=$page", headers)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers)
|
||||
|
||||
override fun popularMangaNextPageSelector() = ".pagination .page-item:not(.disabled) a[rel='next']"
|
||||
override fun popularMangaSelector() = ".col-6 .card"
|
||||
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
|
||||
setUrlWithoutDomain(element.select("a").attr("href"))
|
||||
title = element.select("a").text()
|
||||
thumbnail_url = element.select("img").attr("src")
|
||||
}
|
||||
|
||||
|
||||
//Latest
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers)
|
||||
override fun latestUpdatesNextPageSelector(): String? = null
|
||||
override fun latestUpdatesSelector() = "div.table-responsive:first-child td[scope=row]:nth-child(5n-3)"
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val document = response.asJsoup()
|
||||
val mangas = document.select(latestUpdatesSelector())
|
||||
@ -99,25 +108,7 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
return manga
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
|
||||
genre = document.select("a.py-2").joinToString(", ") {
|
||||
it.text()
|
||||
}
|
||||
|
||||
description = document.select(".col-12.mt-2")?.text()
|
||||
status = parseStatus(document.select(".status-publishing")?.text().orEmpty())
|
||||
thumbnail_url = document.select(".text-center img.img-fluid").attr("src")
|
||||
}
|
||||
|
||||
private fun parseStatus(status: String) = when {
|
||||
status.contains("Publicándose") -> SManga.ONGOING
|
||||
status.contains("Finalizado") -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
override fun popularMangaNextPageSelector() = ".pagination .page-item:not(.disabled) a[rel='next']"
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = "none"
|
||||
//Search
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = HttpUrl.parse("$baseUrl/library")!!.newBuilder()
|
||||
@ -182,21 +173,37 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = popularMangaSelector()
|
||||
|
||||
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
|
||||
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||
//Details
|
||||
|
||||
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
|
||||
genre = document.select("a.py-2").joinToString(", ") {
|
||||
it.text()
|
||||
}
|
||||
description = document.select(".col-12.mt-2")?.text()
|
||||
status = parseStatus(document.select(".status-publishing")?.text().orEmpty())
|
||||
thumbnail_url = document.select(".text-center img.img-fluid").attr("src")
|
||||
}
|
||||
|
||||
private fun parseStatus(status: String) = when {
|
||||
status.contains("Publicándose") -> SManga.ONGOING
|
||||
status.contains("Finalizado") -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
//Chapters
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
time = serverTime() //Get time when the chapter page is opened
|
||||
|
||||
val document = response.asJsoup()
|
||||
val chapterurl = response.request().url().toString()
|
||||
val chapteridselector = ""
|
||||
val chapterUrl = response.request().url().toString()
|
||||
|
||||
// One-shot
|
||||
if (document.select("#chapters").isEmpty()) {
|
||||
return document.select(oneShotChapterListSelector()).map { oneShotChapterFromElement(it, chapterurl, chapteridselector) }
|
||||
return document.select(oneShotChapterListSelector()).map { oneShotChapterFromElement(it, chapterUrl) }
|
||||
}
|
||||
|
||||
// Regular list of chapters
|
||||
@ -208,9 +215,9 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
chapterNames.forEachIndexed { index, _ ->
|
||||
val scanlator = chapterInfos[index].select("li")
|
||||
if (dupselect == "one") {
|
||||
scanlator.last { chapters.add(regularChapterFromElement(chapterNames[index].text(), it , chapterNumbers[index], chapterurl, chapteridselector)) }
|
||||
scanlator.last { chapters.add(regularChapterFromElement(chapterNames[index].text(), it, chapterNumbers[index], chapterUrl)) }
|
||||
} else {
|
||||
scanlator.forEach { chapters.add(regularChapterFromElement(chapterNames[index].text(), it ,chapterNumbers[index], chapterurl, chapteridselector)) }
|
||||
scanlator.forEach { chapters.add(regularChapterFromElement(chapterNames[index].text(), it, chapterNumbers[index], chapterUrl)) }
|
||||
}
|
||||
}
|
||||
return chapters
|
||||
@ -221,24 +228,30 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
private fun oneShotChapterListSelector() = "div.chapter-list-element > ul.list-group li.list-group-item"
|
||||
|
||||
private fun oneShotChapterFromElement(element: Element, chapterurl: String, chapteridselector: String) = SChapter.create().apply {
|
||||
url = "$chapterurl#${element.select("div.row > .text-right > form").attr("id")}"
|
||||
private fun oneShotChapterFromElement(element: Element, chapterUrl: String) = SChapter.create().apply {
|
||||
url = "$chapterUrl#${element.select("div.row > .text-right > form").attr("id")}"
|
||||
name = "One Shot"
|
||||
scanlator = element.select("div.col-md-6.text-truncate")?.text()
|
||||
date_upload = element.select("span.badge.badge-primary.p-2").first()?.text()?.let { parseChapterDate(it) } ?: 0
|
||||
date_upload = element.select("span.badge.badge-primary.p-2").first()?.text()?.let { parseChapterDate(it) }
|
||||
?: 0
|
||||
}
|
||||
|
||||
private fun regularChapterFromElement(chapterName: String, info: Element, number: Float, chapterurl: String, chapteridselector: String): SChapter {
|
||||
private fun regularChapterFromElement(chapterName: String, info: Element, number: Float, chapterUrl: String): SChapter {
|
||||
val chapter = SChapter.create()
|
||||
chapter.url = "$chapterurl#${info.select("div.row > .text-right > form").attr("id")}"
|
||||
chapter.url = "$chapterUrl#${info.select("div.row > .text-right > form").attr("id")}"
|
||||
chapter.name = chapterName
|
||||
chapter.scanlator = info.select("div.col-md-6.text-truncate")?.text()
|
||||
chapter.date_upload = info.select("span.badge.badge-primary.p-2").first()?.text()?.let { parseChapterDate(it) } ?: 0
|
||||
chapter.date_upload = info.select("span.badge.badge-primary.p-2").first()?.text()?.let { parseChapterDate(it) }
|
||||
?: 0
|
||||
chapter.chapter_number = number
|
||||
return chapter
|
||||
}
|
||||
|
||||
private fun parseChapterDate(date: String): Long = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse(date)?.time ?: 0
|
||||
private fun parseChapterDate(date: String): Long = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse(date)?.time
|
||||
?: 0
|
||||
|
||||
//Utilities
|
||||
|
||||
private var time = serverTime() //Grab time at app launch, can be updated
|
||||
private fun serverTime(): String {
|
||||
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
|
||||
@ -246,7 +259,10 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
return formatter.format(Date())
|
||||
}
|
||||
|
||||
//Pages
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
|
||||
val (chapterURL, chapterID) = chapter.url.split("#")
|
||||
val response = client.newCall(GET(chapterURL, headers)).execute() //Get chapter page for current token
|
||||
if (!response.isSuccessful) throw Exception("Lector Manga HTTP Error ${response.code()}")
|
||||
@ -271,6 +287,8 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
}
|
||||
|
||||
val newurl = getBuilder(geturl, getHeaders, formBody, method)
|
||||
|
||||
// Get /cascade instead of /paginate to get all pages at once
|
||||
val url = if (getPageMethod() == "cascade" && newurl.contains("paginated")) {
|
||||
newurl.substringBefore("paginated") + "cascade"
|
||||
} else if (getPageMethod() == "paginated" && newurl.contains("cascade")) {
|
||||
@ -282,15 +300,16 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
.add("Referer", newurl)
|
||||
.build()
|
||||
|
||||
// Get /cascade instead of /paginate to get all pages at once
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply {
|
||||
if (getPageMethod() == "cascade") {
|
||||
val style = document.select("style:containsData(height)").html()
|
||||
document.select( "img[id]").filterNot { "#${it.attr("id")}," in style || "#${it.attr("id")}{" in style }.forEach {
|
||||
add(Page(size, "", it.attr("src")))
|
||||
document.select("img.viewer-img").forEach {
|
||||
add(Page(size, "", it.let {
|
||||
if (it.hasAttr("data-src"))
|
||||
it.attr("abs:data-src") else it.attr("abs:src")
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
val pageList = document.select("#viewer-pages-select").first().select("option").map { it.attr("value").toInt() }
|
||||
@ -303,6 +322,8 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
override fun imageUrlParse(document: Document): String = document.select("img.viewer-image").attr("src")
|
||||
|
||||
//Filters
|
||||
|
||||
private class Types : UriPartFilter("Tipo", arrayOf(
|
||||
Pair("Ver todo", ""),
|
||||
Pair("Manga", "manga"),
|
||||
@ -423,7 +444,11 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
// Preferences Code
|
||||
// Preferences
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
|
||||
val deduppref = androidx.preference.ListPreference(screen.context).apply {
|
||||
@ -436,7 +461,7 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues.get(index) as String
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(DEDUP_PREF, entry).commit()
|
||||
}
|
||||
}
|
||||
@ -451,7 +476,7 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues.get(index) as String
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(PAGEGET_PREF, entry).commit()
|
||||
}
|
||||
}
|
||||
@ -470,7 +495,7 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues.get(index) as String
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(DEDUP_PREF, entry).commit()
|
||||
}
|
||||
}
|
||||
@ -485,7 +510,7 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues.get(index) as String
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(PAGEGET_PREF, entry).commit()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user