Move MT to multisrc and add a new source. (#8478)
@ -0,0 +1,4 @@
|
||||
|
||||
dependencies {
|
||||
implementation project(':lib-ratelimit')
|
||||
}
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 14 KiB |
BIN
multisrc/overrides/mangasar/default/res/web_hi_res_512.png
Normal file
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
18
multisrc/overrides/mangasar/mangatube/src/MangaTube.kt
Normal file
@ -0,0 +1,18 @@
|
||||
package eu.kanade.tachiyomi.extension.pt.mangatube
|
||||
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
||||
import eu.kanade.tachiyomi.multisrc.mangasar.MangaSar
|
||||
import okhttp3.OkHttpClient
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class MangaTube : MangaSar(
|
||||
"MangaTube",
|
||||
"https://mangatube.site",
|
||||
"pt-BR"
|
||||
) {
|
||||
|
||||
override val client: OkHttpClient = super.client.newBuilder()
|
||||
.addInterceptor(::searchIntercept)
|
||||
.addInterceptor(RateLimitInterceptor(1, 2, TimeUnit.SECONDS))
|
||||
.build()
|
||||
}
|
52
multisrc/overrides/mangasar/meusmangas/src/MeusMangas.kt
Normal file
@ -0,0 +1,52 @@
|
||||
package eu.kanade.tachiyomi.extension.pt.meusmangas
|
||||
|
||||
import eu.kanade.tachiyomi.annotations.Nsfw
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
||||
import eu.kanade.tachiyomi.multisrc.mangasar.MangaSar
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Element
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Nsfw
|
||||
class MeusMangas : MangaSar(
|
||||
"Meus Mangás",
|
||||
"https://meusmangas.net",
|
||||
"pt-BR"
|
||||
) {
|
||||
|
||||
override val client: OkHttpClient = super.client.newBuilder()
|
||||
.addInterceptor(::searchIntercept)
|
||||
.addInterceptor(RateLimitInterceptor(1, 2, TimeUnit.SECONDS))
|
||||
.build()
|
||||
|
||||
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 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.touchcarousel a.widget-btn").joinToString { it.text() }
|
||||
status = infoElement.selectFirst("span.mdq").text().toStatus()
|
||||
thumbnail_url = infoElement.selectFirst("div.thumb img").attr("abs:src")
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.toStatus(): Int = when (this) {
|
||||
"Em andamento" -> SManga.ONGOING
|
||||
"Completo" -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package eu.kanade.tachiyomi.extension.pt.mangatube
|
||||
package eu.kanade.tachiyomi.multisrc.mangasar
|
||||
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
@ -24,27 +23,22 @@ import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Element
|
||||
import org.jsoup.parser.Parser
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class MangaTube : HttpSource() {
|
||||
|
||||
override val name = "MangaTube"
|
||||
|
||||
override val baseUrl = "https://mangatube.site"
|
||||
|
||||
override val lang = "pt-BR"
|
||||
abstract class MangaSar(
|
||||
override val name: String,
|
||||
override val baseUrl: String,
|
||||
override val lang: String
|
||||
) : HttpSource() {
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||
.addInterceptor(RateLimitInterceptor(1, 2, TimeUnit.SECONDS))
|
||||
.addInterceptor(::searchIntercept)
|
||||
.build()
|
||||
override val client: OkHttpClient = network.cloudflareClient
|
||||
|
||||
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
||||
.add("Accept", ACCEPT_HTML)
|
||||
@ -66,13 +60,16 @@ class MangaTube : HttpSource() {
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val document = response.asJsoup()
|
||||
|
||||
val mangas = document.select("div:contains(Populares) ~ ul.mangasList li div.gridbox")
|
||||
val mangas = document.select(popularMangaSelector())
|
||||
.map(::popularMangaFromElement)
|
||||
|
||||
return MangasPage(mangas, hasNextPage = false)
|
||||
}
|
||||
|
||||
private fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
|
||||
protected open fun popularMangaSelector(): String =
|
||||
"div:contains(Populares) ~ ul.mangasList li div.gridbox"
|
||||
|
||||
protected open fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
|
||||
title = element.select("div.title a").first()!!.text()
|
||||
thumbnail_url = element.select("div.thumb img").first()!!.attr("abs:src")
|
||||
setUrlWithoutDomain(element.select("a").first()!!.attr("href"))
|
||||
@ -92,18 +89,19 @@ class MangaTube : HttpSource() {
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val result = json.decodeFromString<MangaTubeLatestDto>(response.body!!.string())
|
||||
val result = json.decodeFromString<MangaSarLatestDto>(response.body!!.string())
|
||||
|
||||
val latestMangas = result.releases
|
||||
.map(::latestUpdatesFromObject)
|
||||
.distinctBy { it.url }
|
||||
|
||||
val hasNextPage = result.page.toInt() < result.totalPage
|
||||
|
||||
return MangasPage(latestMangas, hasNextPage)
|
||||
}
|
||||
|
||||
private fun latestUpdatesFromObject(release: MangaTubeReleaseDto) = SManga.create().apply {
|
||||
title = release.name
|
||||
private fun latestUpdatesFromObject(release: MangaSarReleaseDto) = SManga.create().apply {
|
||||
title = release.name.withoutEntities()
|
||||
thumbnail_url = release.image
|
||||
url = release.link
|
||||
}
|
||||
@ -118,14 +116,14 @@ class MangaTube : HttpSource() {
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val result = json.decodeFromString<Map<String, MangaTubeTitleDto>>(response.body!!.string())
|
||||
val result = json.decodeFromString<Map<String, MangaSarTitleDto>>(response.body!!.string())
|
||||
|
||||
val searchResults = result.values.map(::searchMangaFromObject)
|
||||
|
||||
return MangasPage(searchResults, hasNextPage = false)
|
||||
}
|
||||
|
||||
private fun searchMangaFromObject(manga: MangaTubeTitleDto) = SManga.create().apply {
|
||||
private fun searchMangaFromObject(manga: MangaSarTitleDto) = SManga.create().apply {
|
||||
title = manga.title
|
||||
thumbnail_url = manga.image
|
||||
setUrlWithoutDomain(manga.url)
|
||||
@ -164,7 +162,7 @@ class MangaTube : HttpSource() {
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val mangaUrl = response.request.header("Referer")!!.substringAfter(baseUrl)
|
||||
|
||||
var result = json.decodeFromString<MangaTubePaginatedChaptersDto>(response.body!!.string())
|
||||
var result = json.decodeFromString<MangaSarPaginatedChaptersDto>(response.body!!.string())
|
||||
|
||||
if (result.chapters.isNullOrEmpty()) {
|
||||
return emptyList()
|
||||
@ -191,7 +189,7 @@ class MangaTube : HttpSource() {
|
||||
return chapters
|
||||
}
|
||||
|
||||
private fun chapterFromObject(chapter: MangaTubeChapterDto): SChapter = SChapter.create().apply {
|
||||
private fun chapterFromObject(chapter: MangaSarChapterDto): SChapter = SChapter.create().apply {
|
||||
name = "Cap. " + (if (chapter.number.booleanOrNull != null) "0" else chapter.number.content) +
|
||||
(if (chapter.name.isString) " - " + chapter.name.content else "")
|
||||
chapter_number = chapter.number.floatOrNull ?: -1f
|
||||
@ -224,7 +222,7 @@ class MangaTube : HttpSource() {
|
||||
|
||||
val apiRequest = pageListApiRequest(chapterUrl, serieId, token)
|
||||
val apiResponse = client.newCall(apiRequest).execute().let {
|
||||
json.decodeFromString<MangaTubeReaderDto>(it.body!!.string())
|
||||
json.decodeFromString<MangaSarReaderDto>(it.body!!.string())
|
||||
}
|
||||
|
||||
return apiResponse.images
|
||||
@ -245,7 +243,7 @@ class MangaTube : HttpSource() {
|
||||
return GET(page.imageUrl!!, newHeaders)
|
||||
}
|
||||
|
||||
private fun searchIntercept(chain: Interceptor.Chain): Response {
|
||||
protected fun searchIntercept(chain: Interceptor.Chain): Response {
|
||||
if (chain.request().url.toString().contains("/search/")) {
|
||||
val homeRequest = popularMangaRequest(1)
|
||||
val document = chain.proceed(homeRequest).asJsoup()
|
||||
@ -278,6 +276,10 @@ class MangaTube : HttpSource() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.withoutEntities(): String {
|
||||
return Parser.unescapeEntities(this, true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ACCEPT = "application/json, text/plain, */*"
|
||||
private const val ACCEPT_HTML = "text/html,application/xhtml+xml,application/xml;q=0.9," +
|
@ -1,39 +1,39 @@
|
||||
package eu.kanade.tachiyomi.extension.pt.mangatube
|
||||
package eu.kanade.tachiyomi.multisrc.mangasar
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
|
||||
@Serializable
|
||||
data class MangaTubeLatestDto(
|
||||
data class MangaSarLatestDto(
|
||||
val page: String,
|
||||
val releases: List<MangaTubeReleaseDto> = emptyList(),
|
||||
val releases: List<MangaSarReleaseDto> = emptyList(),
|
||||
@SerialName("total_page") val totalPage: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangaTubeReleaseDto(
|
||||
data class MangaSarReleaseDto(
|
||||
val image: String,
|
||||
val link: String,
|
||||
val name: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangaTubeTitleDto(
|
||||
data class MangaSarTitleDto(
|
||||
@SerialName("img") val image: String,
|
||||
val title: String,
|
||||
val url: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangaTubePaginatedChaptersDto(
|
||||
val chapters: List<MangaTubeChapterDto>? = emptyList(),
|
||||
data class MangaSarPaginatedChaptersDto(
|
||||
val chapters: List<MangaSarChapterDto>? = emptyList(),
|
||||
@SerialName("pagina") val page: Int,
|
||||
@SerialName("total_pags") val totalPages: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangaTubeChapterDto(
|
||||
data class MangaSarChapterDto(
|
||||
@SerialName("date_created") val dateCreated: String,
|
||||
val link: String,
|
||||
@SerialName("chapter_name") val name: JsonPrimitive,
|
||||
@ -41,11 +41,11 @@ data class MangaTubeChapterDto(
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangaTubeReaderDto(
|
||||
val images: List<MangaTubePageDto> = emptyList()
|
||||
data class MangaSarReaderDto(
|
||||
val images: List<MangaSarPageDto> = emptyList()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangaTubePageDto(
|
||||
data class MangaSarPageDto(
|
||||
val url: String
|
||||
)
|
@ -0,0 +1,26 @@
|
||||
package eu.kanade.tachiyomi.multisrc.mangasar
|
||||
|
||||
import generator.ThemeSourceData.SingleLang
|
||||
import generator.ThemeSourceGenerator
|
||||
|
||||
class MangaSarGenerator : ThemeSourceGenerator {
|
||||
|
||||
override val themePkg = "mangasar"
|
||||
|
||||
override val themeClass = "MangaSar"
|
||||
|
||||
override val baseVersionCode: Int = 4
|
||||
|
||||
override val sources = listOf(
|
||||
SingleLang("MangaTube", "https://mangatube.site", "pt-BR"),
|
||||
SingleLang("Meus Mangás", "https://meusmangas.net", "pt-BR", isNsfw = true, className = "MeusMangas")
|
||||
)
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
MangaSarGenerator().createAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="eu.kanade.tachiyomi.extension" />
|
@ -1,18 +0,0 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'MangaTube'
|
||||
pkgNameSuffix = 'pt.mangatube'
|
||||
extClass = '.MangaTube'
|
||||
extVersionCode = 3
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':lib-ratelimit')
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|