EternalMangas: Move to Multi and fix pages not found (#3258)
* Move to Multi and fix pages * skill issue * mmmm * remode id
@ -2,7 +2,7 @@ plugins {
|
|||||||
id("lib-multisrc")
|
id("lib-multisrc")
|
||||||
}
|
}
|
||||||
|
|
||||||
baseVersionCode = 1
|
baseVersionCode = 2
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":lib:i18n"))
|
api(project(":lib:i18n"))
|
||||||
|
@ -73,7 +73,7 @@ abstract class MangaEsp(
|
|||||||
return MangasPage(mangas, false)
|
return MangasPage(mangas, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var comicsList = mutableListOf<SeriesDto>()
|
protected var comicsList = mutableListOf<SeriesDto>()
|
||||||
|
|
||||||
override fun fetchSearchManga(
|
override fun fetchSearchManga(
|
||||||
page: Int,
|
page: Int,
|
||||||
@ -93,7 +93,7 @@ abstract class MangaEsp(
|
|||||||
|
|
||||||
override fun searchMangaParse(response: Response): MangasPage = throw UnsupportedOperationException()
|
override fun searchMangaParse(response: Response): MangasPage = throw UnsupportedOperationException()
|
||||||
|
|
||||||
private fun searchMangaParse(response: Response, page: Int, query: String, filters: FilterList): MangasPage {
|
protected open fun searchMangaParse(response: Response, page: Int, query: String, filters: FilterList): MangasPage {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val script = document.select("script:containsData(self.__next_f.push)").joinToString { it.data() }
|
val script = document.select("script:containsData(self.__next_f.push)").joinToString { it.data() }
|
||||||
val jsonString = MANGA_LIST_REGEX.find(script)?.groupValues?.get(1)
|
val jsonString = MANGA_LIST_REGEX.find(script)?.groupValues?.get(1)
|
||||||
@ -105,7 +105,7 @@ abstract class MangaEsp(
|
|||||||
|
|
||||||
private var filteredList = mutableListOf<SeriesDto>()
|
private var filteredList = mutableListOf<SeriesDto>()
|
||||||
|
|
||||||
private fun parseComicsList(page: Int, query: String, filterList: FilterList): MangasPage {
|
protected open fun parseComicsList(page: Int, query: String, filterList: FilterList): MangasPage {
|
||||||
if (page == 1) {
|
if (page == 1) {
|
||||||
filteredList.clear()
|
filteredList.clear()
|
||||||
|
|
||||||
@ -228,21 +228,21 @@ abstract class MangaEsp(
|
|||||||
|
|
||||||
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
|
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
|
||||||
|
|
||||||
private fun Element.imgAttr(): String = when {
|
protected open fun Element.imgAttr(): String = when {
|
||||||
hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
|
hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
|
||||||
hasAttr("data-src") -> attr("abs:data-src")
|
hasAttr("data-src") -> attr("abs:data-src")
|
||||||
hasAttr("data-cfsrc") -> attr("abs:data-cfsrc")
|
hasAttr("data-cfsrc") -> attr("abs:data-cfsrc")
|
||||||
else -> attr("abs:src")
|
else -> attr("abs:src")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.unescape(): String {
|
fun String.unescape(): String {
|
||||||
return UNESCAPE_REGEX.replace(this, "$1")
|
return UNESCAPE_REGEX.replace(this, "$1")
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val UNESCAPE_REGEX = """\\(.)""".toRegex()
|
private val UNESCAPE_REGEX = """\\(.)""".toRegex()
|
||||||
private val MANGA_LIST_REGEX = """self\.__next_f\.push\(.*data\\":(\[.*trending.*])\}""".toRegex()
|
val MANGA_LIST_REGEX = """self\.__next_f\.push\(.*data\\":(\[.*trending.*])\}""".toRegex()
|
||||||
private val MANGA_DETAILS_REGEX = """self\.__next_f\.push\(.*data\\":(\{.*lastChapters.*\}).*\\"numFollow""".toRegex()
|
private val MANGA_DETAILS_REGEX = """self\.__next_f\.push\(.*data\\":(\{.*lastChapters.*\}).*\\"numFollow""".toRegex()
|
||||||
private const val MANGAS_PER_PAGE = 15
|
const val MANGAS_PER_PAGE = 15
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,9 @@ class SeriesDto(
|
|||||||
val trending: TrendingDto? = null,
|
val trending: TrendingDto? = null,
|
||||||
@SerialName("autors") private val authors: List<AuthorDto> = emptyList(),
|
@SerialName("autors") private val authors: List<AuthorDto> = emptyList(),
|
||||||
private val artists: List<ArtistDto> = emptyList(),
|
private val artists: List<ArtistDto> = emptyList(),
|
||||||
|
@Suppress("unused") // Used in some sources
|
||||||
|
@SerialName("idioma")
|
||||||
|
val language: String? = null,
|
||||||
) {
|
) {
|
||||||
fun toSManga(): SManga {
|
fun toSManga(): SManga {
|
||||||
return SManga.create().apply {
|
return SManga.create().apply {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'EternalMangas'
|
extName = 'EternalMangas'
|
||||||
extClass = '.EternalMangas'
|
extClass = '.EternalMangasFactory'
|
||||||
themePkg = 'mangaesp'
|
themePkg = 'mangaesp'
|
||||||
baseUrl = 'https://eternalmangas.com'
|
baseUrl = 'https://eternalmangas.com'
|
||||||
overrideVersionCode = 0
|
overrideVersionCode = 0
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
@ -0,0 +1,82 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.all.eternalmangas
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.mangaesp.MangaEsp
|
||||||
|
import eu.kanade.tachiyomi.multisrc.mangaesp.SeriesDto
|
||||||
|
import eu.kanade.tachiyomi.multisrc.mangaesp.TopSeriesDto
|
||||||
|
import eu.kanade.tachiyomi.network.POST
|
||||||
|
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.util.asJsoup
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import okhttp3.FormBody
|
||||||
|
import okhttp3.Response
|
||||||
|
|
||||||
|
open class EternalMangas(
|
||||||
|
lang: String,
|
||||||
|
private val internalLang: String,
|
||||||
|
) : MangaEsp(
|
||||||
|
"EternalMangas",
|
||||||
|
"https://eternalmangas.com",
|
||||||
|
lang,
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun popularMangaParse(response: Response): MangasPage {
|
||||||
|
val body = response.body.string()
|
||||||
|
val responseData = json.decodeFromString<TopSeriesDto>(body)
|
||||||
|
|
||||||
|
val topDaily = responseData.response.topDaily.flatten().map { it.data }
|
||||||
|
val topWeekly = responseData.response.topWeekly.flatten().map { it.data }
|
||||||
|
val topMonthly = responseData.response.topMonthly.flatten().map { it.data }
|
||||||
|
|
||||||
|
val mangas = (topDaily + topWeekly + topMonthly).distinctBy { it.slug }
|
||||||
|
.filter { it.language == internalLang }
|
||||||
|
.map { it.toSManga() }
|
||||||
|
|
||||||
|
return MangasPage(mangas, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||||
|
val responseData = json.decodeFromString<LatestUpdatesDto>(response.body.string())
|
||||||
|
val mangas = responseData.updates[internalLang]?.flatten()?.map { it.toSManga() } ?: emptyList()
|
||||||
|
return MangasPage(mangas, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response, page: Int, query: String, filters: FilterList): MangasPage {
|
||||||
|
val document = response.asJsoup()
|
||||||
|
val script = document.select("script:containsData(self.__next_f.push)").joinToString { it.data() }
|
||||||
|
val jsonString = MANGA_LIST_REGEX.find(script)?.groupValues?.get(1)
|
||||||
|
?: throw Exception(intl["comics_list_error"])
|
||||||
|
val unescapedJson = jsonString.unescape()
|
||||||
|
comicsList = json.decodeFromString<List<SeriesDto>>(unescapedJson)
|
||||||
|
.filter { it.language == internalLang }
|
||||||
|
.toMutableList()
|
||||||
|
return parseComicsList(page, query, filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
var document = response.asJsoup()
|
||||||
|
|
||||||
|
document.selectFirst("body > form[method=post]")?.let {
|
||||||
|
val action = it.attr("action")
|
||||||
|
val inputs = it.select("input")
|
||||||
|
|
||||||
|
val form = FormBody.Builder()
|
||||||
|
inputs.forEach { input ->
|
||||||
|
form.add(input.attr("name"), input.attr("value"))
|
||||||
|
}
|
||||||
|
|
||||||
|
document = client.newCall(POST(action, headers, form.build())).execute().asJsoup()
|
||||||
|
}
|
||||||
|
|
||||||
|
return document.select("main > img").mapIndexed { i, img ->
|
||||||
|
Page(i, imageUrl = img.imgAttr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class LatestUpdatesDto(
|
||||||
|
val updates: Map<String, List<List<SeriesDto>>>,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.all.eternalmangas
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
|
|
||||||
|
class EternalMangasFactory : SourceFactory {
|
||||||
|
override fun createSources() = listOf(
|
||||||
|
EternalMangasES(),
|
||||||
|
EternalMangasEN(),
|
||||||
|
EternalMangasPTBR(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class EternalMangasES : EternalMangas("es", "es")
|
||||||
|
class EternalMangasEN : EternalMangas("en", "en")
|
||||||
|
class EternalMangasPTBR : EternalMangas("pt-BR", "pt")
|
@ -1,5 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.es.eternalmangas
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangaesp.MangaEsp
|
|
||||||
|
|
||||||
class EternalMangas : MangaEsp("EternalMangas", "https://eternalmangas.com", "es")
|
|