diff --git a/multisrc/overrides/mangasar/mangasup/src/MangasUp.kt b/multisrc/overrides/mangasar/mangasup/src/MangasUp.kt new file mode 100644 index 000000000..5de6b25fb --- /dev/null +++ b/multisrc/overrides/mangasar/mangasup/src/MangasUp.kt @@ -0,0 +1,32 @@ +package eu.kanade.tachiyomi.extension.pt.mangasup + +import eu.kanade.tachiyomi.multisrc.mangasar.MangaSar +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Element + +class MangasUp : MangaSar( + "MangásUp", + "https://mangasup.net", + "pt-BR" +) { + + override fun chapterListPaginatedRequest(mangaUrl: String, page: Int): Request { + return GET(baseUrl + mangaUrl, headers) + } + + override fun chapterListParse(response: Response): List { + return response.asJsoup() + .select("ul.full-chapters-list > li > a") + .map(::chapterFromElement) + } + + private fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { + name = element.selectFirst("span.cap-text")!!.text() + date_upload = element.selectFirst("span.chapter-date")?.text()?.toDate() ?: 0L + setUrlWithoutDomain(element.attr("href")) + } +} diff --git a/multisrc/overrides/mangasar/meusmangas/src/MeusMangas.kt b/multisrc/overrides/mangasar/meusmangas/src/MeusMangas.kt deleted file mode 100644 index 37d2ce1c4..000000000 --- a/multisrc/overrides/mangasar/meusmangas/src/MeusMangas.kt +++ /dev/null @@ -1,123 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.meusmangas - -import eu.kanade.tachiyomi.multisrc.mangasar.MangaSar -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.Request -import okhttp3.Response -import org.jsoup.nodes.Element - -class MeusMangas : MangaSar( - "Meus Mangás", - "https://meusmangas.net", - "pt-BR" -) { - - override fun popularMangaSelector() = "ul.sidebar-popular li.popular-treending" - - override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { - title = element.selectFirst("h4.title").text() - thumbnail_url = element.selectFirst("div.tumbl img").attr("src") - setUrlWithoutDomain(element.selectFirst("a").attr("abs:href")) - } - - override fun latestUpdatesRequest(page: Int): Request { - val newHeaders = headersBuilder() - .add("X-Requested-With", "XMLHttpRequest") - .build() - - val pagePath = if (page > 1) "page/$page" else "" - - return GET("$baseUrl/$pagePath", newHeaders) - } - - override fun latestUpdatesParse(response: Response): MangasPage { - val document = response.asJsoup() - - val mangaList = document.select("li.item_news-manga") - .map(::latestMangaFromElement) - - val hasNextPage = document.select("div.loadmore.morepage").firstOrNull() != null - - return MangasPage(mangaList, hasNextPage) - } - - private fun latestMangaFromElement(element: Element): SManga = SManga.create().apply { - title = element.select("h3.entry-title a").text() - thumbnail_url = element.select("img.manga").attr("src") - setUrlWithoutDomain(element.select("a").first().attr("abs:href")) - } - - override fun mangaDetailsParse(response: Response): SManga { - val document = response.asJsoup() - val infoElement = document.selectFirst("div.box-single:has(div.mangapage)") - - return SManga.create().apply { - title = infoElement.selectFirst("h1.kw-title").text() - author = infoElement.selectFirst("div.mdq.author").text().trim() - description = infoElement.selectFirst("div.sinopse-page").text() - genre = infoElement.select("div.generos a.widget-btn").joinToString { it.text() } - status = infoElement.selectFirst("span.mdq").text().toStatus() - thumbnail_url = infoElement.selectFirst("div.thumb img").attr("abs:src") - } - } - - override fun chapterListPaginatedRequest(mangaUrl: String, page: Int): Request { - val newHeaders = headersBuilder() - .add("X-Requested-With", "XMLHttpRequest") - .build() - - return GET("$baseUrl$mangaUrl/page/$page", newHeaders) - } - - override fun chapterListParse(response: Response): List { - var document = response.asJsoup() - - val chapterList = document.select(chapterListSelector()) - .map(::chapterFromElement) - .toMutableList() - - val mangaUrl = response.request.url.toString() - .substringAfter(baseUrl) - .substringBefore("/page") - var hasNextPage = document.select(chapterListNextPageSelector()) - .firstOrNull() - - while (hasNextPage != null) { - val page = hasNextPage.attr("href") - .substringAfter("page/") - .toInt() - - val nextRequest = chapterListPaginatedRequest(mangaUrl, page) - val nextResponse = client.newCall(nextRequest).execute() - document = nextResponse.asJsoup() - - chapterList += document.select(chapterListSelector()) - .map(::chapterFromElement) - - hasNextPage = document.select(chapterListNextPageSelector()) - .firstOrNull() - } - - return chapterList - } - - private fun chapterListSelector() = "ul.list-of-chapters li > a" - - private fun chapterListNextPageSelector() = "ul.content-pagination li.active + li:not(.next) a" - - private fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { - name = element.select("span.cap-text").text() - date_upload = element.select("span.chapter-date").text().toDate() - setUrlWithoutDomain(element.attr("abs:href")) - } - - private fun String.toStatus(): Int = when (this) { - "Em andamento" -> SManga.ONGOING - "Completo" -> SManga.COMPLETED - else -> SManga.UNKNOWN - } -} diff --git a/multisrc/overrides/mangasar/seemangas/src/Seemangas.kt b/multisrc/overrides/mangasar/seemangas/src/Seemangas.kt new file mode 100644 index 000000000..015283032 --- /dev/null +++ b/multisrc/overrides/mangasar/seemangas/src/Seemangas.kt @@ -0,0 +1,136 @@ +package eu.kanade.tachiyomi.extension.pt.seemangas + +import eu.kanade.tachiyomi.multisrc.mangasar.MangaSar +import eu.kanade.tachiyomi.multisrc.mangasar.MangaSarLatestDto +import eu.kanade.tachiyomi.multisrc.mangasar.MangaSarReaderDto +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST +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.util.asJsoup +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import okhttp3.FormBody +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Element + +class Seemangas : MangaSar( + "Seemangas", + "https://seemangas.com", + "pt-BR" +) { + + override fun popularMangaSelector() = "ul.sidebar-popular li.popular-treending" + + override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { + title = element.selectFirst("h4.title").text() + thumbnail_url = element.selectFirst("div.tumbl img").attr("data-lazy-src") + setUrlWithoutDomain(element.selectFirst("a").attr("abs:href")) + } + + override fun latestUpdatesRequest(page: Int): Request { + val payload = FormBody.Builder() + .add("action", "get_lancamentos") + .add("pagina", page.toString()) + .build() + + val newHeaders = headersBuilder() + .add("Content-Length", payload.contentLength().toString()) + .add("Content-Type", payload.contentType().toString()) + .add("X-Requested-With", "XMLHttpRequest") + .build() + + return POST("$baseUrl/wp-admin/admin-ajax.php", newHeaders, payload) + } + + override fun latestUpdatesParse(response: Response): MangasPage { + val result = response.parseAs() + + val latestMangas = result.releases + .map(::latestUpdatesFromObject) + .distinctBy { it.url } + + return MangasPage(latestMangas, hasNextPage = result.releases.isNotEmpty()) + } + + override fun mangaDetailsParse(response: Response): SManga { + val document = response.asJsoup() + val infoElement = document.selectFirst("div.box-single:has(div.mangapage)") + + return SManga.create().apply { + title = infoElement.selectFirst("h1.kw-title").text() + author = infoElement.selectFirst("div.mdq.author").text().trim() + description = infoElement.selectFirst("div.sinopse-page").text() + genre = infoElement.select("div.generos a.widget-btn").joinToString { it.text() } + status = infoElement.selectFirst("span.mdq").text().toStatus() + thumbnail_url = infoElement.selectFirst("div.thumb img").attr("abs:data-lazy-src") + } + } + override fun chapterListPaginatedRequest(mangaUrl: String, page: Int): Request { + return GET(baseUrl + mangaUrl, headers) + } + + override fun chapterListParse(response: Response): List { + return response.asJsoup() + .select("ul.full-chapters-list > li > a") + .map(::chapterFromElement) + } + + private fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { + name = element.selectFirst("span.cap-text")!!.text() + date_upload = element.selectFirst("span.chapter-date")?.text()?.toDate() ?: 0L + setUrlWithoutDomain(element.attr("href")) + } + + override fun pageListApiRequest(chapterUrl: String, serieId: String, token: String): Request { + val chapterId = CHAPTER_ID_REGEX.find(chapterUrl)!!.groupValues[1] + + val payload = FormBody.Builder() + .add("action", "get_image_list") + .add("id_serie", chapterId) + .add("secury", token) + .build() + + val newHeaders = apiHeadersBuilder() + .add("Content-Length", payload.contentLength().toString()) + .add("Content-Type", payload.contentType().toString()) + .set("Referer", chapterUrl) + .build() + + return POST("$baseUrl/wp-admin/admin-ajax.php", newHeaders, payload) + } + + override fun pageListParse(response: Response): List { + val document = response.asJsoup() + val apiParams = document.selectFirst("script:containsData(id_serie)")?.data() + ?: throw Exception(TOKEN_NOT_FOUND) + + val chapterUrl = response.request.url.toString() + val infoReader = apiParams + .substringAfter("{") + .substringBeforeLast("}") + val readerParams = json.parseToJsonElement("{$infoReader}").jsonObject + val serieId = readerParams["id_serie"]!!.jsonPrimitive.content + val token = readerParams["token"]!!.jsonPrimitive.content + + val apiRequest = pageListApiRequest(chapterUrl, serieId, token) + val apiResponse = client.newCall(apiRequest).execute().parseAs() + + return apiResponse.images + .filter { it.url.startsWith("http") } + .mapIndexed { i, page -> Page(i, chapterUrl, page.url) } + } + + private fun String.toStatus(): Int = when (this) { + "Em andamento" -> SManga.ONGOING + "Completo" -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + + companion object { + private val CHAPTER_ID_REGEX = "(\\d+)$".toRegex() + } +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSar.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSar.kt index 0fe27bcf1..442337619 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSar.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSar.kt @@ -50,7 +50,7 @@ abstract class MangaSar( .add("Accept-Language", ACCEPT_LANGUAGE) .add("Referer", "$baseUrl/") - private fun apiHeadersBuilder(): Headers.Builder = headersBuilder() + protected fun apiHeadersBuilder(): Headers.Builder = headersBuilder() .set("Accept", ACCEPT) .add("X-Requested-With", "XMLHttpRequest") @@ -100,12 +100,12 @@ abstract class MangaSar( .map(::latestUpdatesFromObject) .distinctBy { it.url } - val hasNextPage = result.page.toInt() < result.totalPage + val hasNextPage = result.page.toInt() < result.totalPage!! return MangasPage(latestMangas, hasNextPage) } - private fun latestUpdatesFromObject(release: MangaSarReleaseDto) = SManga.create().apply { + protected fun latestUpdatesFromObject(release: MangaSarReleaseDto) = SManga.create().apply { title = release.name.withoutEntities() thumbnail_url = release.image url = release.link @@ -200,7 +200,7 @@ abstract class MangaSar( setUrlWithoutDomain(chapter.link) } - private fun pageListApiRequest(chapterUrl: String, serieId: String, token: String): Request { + protected open fun pageListApiRequest(chapterUrl: String, serieId: String, token: String): Request { val newHeaders = apiHeadersBuilder() .set("Referer", chapterUrl) .build() @@ -296,6 +296,6 @@ abstract class MangaSar( private val DATE_FORMATTER by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) } - private const val TOKEN_NOT_FOUND = "Não foi possível obter o token de leitura." + const val TOKEN_NOT_FOUND = "Não foi possível obter o token de leitura." } } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarDto.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarDto.kt index 8ffdfe207..5d540aa3a 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarDto.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarDto.kt @@ -2,13 +2,14 @@ package eu.kanade.tachiyomi.multisrc.mangasar import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonNames import kotlinx.serialization.json.JsonPrimitive @Serializable data class MangaSarLatestDto( val page: String, - val releases: List = emptyList(), - @SerialName("total_page") val totalPage: Int + @JsonNames("lancamentos") val releases: List = emptyList(), + @SerialName("total_page") val totalPage: Int? = 0 ) @Serializable diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarGenerator.kt index 4b7e7468c..ed0351563 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarGenerator.kt @@ -14,7 +14,8 @@ class MangaSarGenerator : ThemeSourceGenerator { override val sources = listOf( SingleLang("Fire Mangás", "https://firemangas.com", "pt-BR", className = "FireMangas"), SingleLang("Mangazim", "https://mangazim.com", "pt-BR"), - SingleLang("Meus Mangás", "https://meusmangas.net", "pt-BR", isNsfw = true, className = "MeusMangas", overrideVersionCode = 2) + SingleLang("MangásUp", "https://mangasup.net", "pt-BR", className = "MangasUp"), + SingleLang("Seemangas", "https://seemangas.com", "pt-BR", isNsfw = true) ) companion object {