Compare commits

..

8 Commits

Author SHA1 Message Date
Zakaria aourzag 504342706e Multporn: fix popular listing (#154)
CI / Prepare job (push) Successful in 18s Details
CI / Build multisrc modules (push) Successful in 6m8s Details
CI / Build individual modules (push) Successful in 1m9s Details
CI / Publish repo (push) Successful in 2m50s Details
Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com>
2024-01-12 03:35:06 +00:00
Vetle Ledaal 3523acfe90 Fix popular selector for Mundo Hentai (#155) 2024-01-12 03:35:04 +00:00
Vetle Ledaal 2ce5e88163 Update domain for TonizuToon (#152) 2024-01-12 03:35:01 +00:00
Vetle Ledaal 283c117b49 Update domain for Gekkou Scans (#150) 2024-01-12 03:34:59 +00:00
bapeey c72814cc9a Fix XOXO Comics (#148) 2024-01-12 03:34:49 +00:00
stevenyomi 34c46abf9d Roumanwu: update URL (#146) 2024-01-12 03:34:42 +00:00
stevenyomi defa788dd5 DMZJ: fix comment page (Brotli issue) (#145) 2024-01-12 03:34:39 +00:00
kooper100 abc5afb00f Updated parsing of manga title from URL. (#59) 2024-01-12 03:34:30 +00:00
17 changed files with 63 additions and 53 deletions

View File

@ -3,12 +3,15 @@ package eu.kanade.tachiyomi.extension.pt.gekkouscan
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class GekkouScans : Madara( class GekkouScans : Madara(
"Gekkou Scans", "Gekkou Scans",
"https://gekkouscans.top", "https://gekkou.site",
"pt-BR", "pt-BR",
dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("pt", "BR")),
) { ) {
// Theme changed from MMRCMS to Madara, again. // Theme changed from MMRCMS to Madara, again.
@ -22,4 +25,10 @@ class GekkouScans : Madara(
.build() .build()
override val useNewChapterEndpoint: Boolean = true override val useNewChapterEndpoint: Boolean = true
override val mangaDetailsSelectorTitle = "#manga-title"
override val mangaDetailsSelectorStatus = ".summary-heading:contains(Status) ~ .summary-content"
override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/"
} }

View File

@ -6,7 +6,17 @@ import java.util.Locale
class TonizuToon : Madara( class TonizuToon : Madara(
"TonizuToon", "TonizuToon",
"https://tonizutoon.com", "https://tonizu.com",
"tr", "tr",
SimpleDateFormat("MMMMM d, yyyy", Locale("tr")), dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ROOT),
) ) {
override val useNewChapterEndpoint = true
override val mangaDetailsSelectorTitle = "#manga-title"
override val mangaDetailsSelectorAuthor = ".summary-heading:contains(Yazar) ~ .summary-content"
override val mangaDetailsSelectorStatus = ".summary-heading:contains(Durumu) ~ .summary-content"
override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/"
}

View File

@ -14,7 +14,8 @@ import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class XoxoComics : WPComics("XOXO Comics", "https://xoxocomic.com", "en", SimpleDateFormat("MM/dd/yyyy", Locale.US), null) { class XoxoComics : WPComics("XOXO Comics", "https://xoxocomic.com", "en", SimpleDateFormat("MM/dd/yyyy", Locale.US), null) {
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/comic-updates?page=$page", headers) override val popularPath = "hot-comic"
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/comic-update?page=$page", headers)
override fun latestUpdatesSelector() = "li.row" override fun latestUpdatesSelector() = "li.row"
override fun latestUpdatesFromElement(element: Element): SManga { override fun latestUpdatesFromElement(element: Element): SManga {
return SManga.create().apply { return SManga.create().apply {
@ -26,7 +27,7 @@ class XoxoComics : WPComics("XOXO Comics", "https://xoxocomic.com", "en", Simple
} }
} }
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
return GET("$baseUrl/search?keyword=$query&page=$page", headers) return GET("$baseUrl/search-comic?keyword=$query&page=$page", headers)
} }
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {

View File

@ -467,24 +467,27 @@ abstract class Madara(
"مكتملة", "مكتملة",
"مكتمل", "مكتمل",
"已完结", "已完结",
"Tamamlandı",
) )
protected val ongoingStatusList: Array<String> = arrayOf( protected val ongoingStatusList: Array<String> = arrayOf(
"OnGoing", "Продолжается", "Updating", "Em Lançamento", "Em lançamento", "Em andamento", "OnGoing", "Продолжается", "Updating", "Em Lançamento", "Em lançamento", "Em andamento",
"Em Andamento", "En cours", "En Cours", "En cours de publication", "Ativo", "Lançando", "Đang Tiến Hành", "Devam Ediyor", "Em Andamento", "En cours", "En Cours", "En cours de publication", "Ativo", "Lançando", "Đang Tiến Hành", "Devam Ediyor",
"Devam ediyor", "In Corso", "In Arrivo", "مستمرة", "مستمر", "En Curso", "En curso", "Emision", "Devam ediyor", "In Corso", "In Arrivo", "مستمرة", "مستمر", "En Curso", "En curso", "Emision",
"Curso", "En marcha", "Publicandose", "En emision", "连载中", "Em Lançamento", "Curso", "En marcha", "Publicandose", "En emision", "连载中", "Em Lançamento", "Devam Ediyo",
) )
protected val hiatusStatusList: Array<String> = arrayOf( protected val hiatusStatusList: Array<String> = arrayOf(
"On Hold", "On Hold",
"Pausado", "Pausado",
"En espera", "En espera",
"Durduruldu",
) )
protected val canceledStatusList: Array<String> = arrayOf( protected val canceledStatusList: Array<String> = arrayOf(
"Canceled", "Canceled",
"Cancelado", "Cancelado",
"İptal Edildi",
) )
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {

View File

@ -112,7 +112,7 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("FreeWebtoonCoins", "https://freewebtooncoins.com", "en", overrideVersionCode = 1), SingleLang("FreeWebtoonCoins", "https://freewebtooncoins.com", "en", overrideVersionCode = 1),
SingleLang("GalaxyDegenScans", "https://gdscans.com", "en", overrideVersionCode = 4), SingleLang("GalaxyDegenScans", "https://gdscans.com", "en", overrideVersionCode = 4),
SingleLang("Gatemanga", "https://gatemanga.com", "ar", overrideVersionCode = 1), SingleLang("Gatemanga", "https://gatemanga.com", "ar", overrideVersionCode = 1),
SingleLang("Gekkou Scans", "https://gekkouscans.top", "pt-BR", isNsfw = true, pkgName = "gekkouscan", overrideVersionCode = 1), SingleLang("Gekkou Scans", "https://gekkou.site", "pt-BR", isNsfw = true, pkgName = "gekkouscan", overrideVersionCode = 2),
SingleLang("Ghost Scan", "https://ghostscan.com.br", "pt-BR", isNsfw = true), SingleLang("Ghost Scan", "https://ghostscan.com.br", "pt-BR", isNsfw = true),
SingleLang("Girls Love Manga!", "https://glmanga.com", "en", isNsfw = true, className = "GirlsLoveManga"), SingleLang("Girls Love Manga!", "https://glmanga.com", "en", isNsfw = true, className = "GirlsLoveManga"),
SingleLang("Glory Manga", "https://glorymanga.com", "tr"), SingleLang("Glory Manga", "https://glorymanga.com", "tr"),
@ -456,7 +456,7 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("The Guild", "https://theguildscans.com", "en"), SingleLang("The Guild", "https://theguildscans.com", "en"),
SingleLang("Time Naight", "https://timenaight.com", "tr"), SingleLang("Time Naight", "https://timenaight.com", "tr"),
SingleLang("Todaymic", "https://todaymic.com", "en", overrideVersionCode = 1), SingleLang("Todaymic", "https://todaymic.com", "en", overrideVersionCode = 1),
SingleLang("TonizuToon", "https://tonizutoon.com", "tr", isNsfw = true), SingleLang("TonizuToon", "https://tonizu.com", "tr", isNsfw = true, overrideVersionCode = 1),
SingleLang("ToonChill", "https://toonchill.com", "en", overrideVersionCode = 1), SingleLang("ToonChill", "https://toonchill.com", "en", overrideVersionCode = 1),
SingleLang("ToonGod", "https://www.toongod.org", "en", isNsfw = true, overrideVersionCode = 5), SingleLang("ToonGod", "https://www.toongod.org", "en", isNsfw = true, overrideVersionCode = 5),
SingleLang("Toonily.net", "https://toonily.net", "en", isNsfw = true, className = "Toonilynet", overrideVersionCode = 2), SingleLang("Toonily.net", "https://toonily.net", "en", isNsfw = true, className = "Toonilynet", overrideVersionCode = 2),

View File

@ -128,7 +128,7 @@ abstract class WPComics(
open fun String?.toStatus(): Int { open fun String?.toStatus(): Int {
val ongoingWords = listOf("Ongoing", "Updating", "Đang tiến hành") val ongoingWords = listOf("Ongoing", "Updating", "Đang tiến hành")
val completedWords = listOf("Complete", "Hoàn thành") val completedWords = listOf("Complete", "Completed", "Hoàn thành")
return when { return when {
this == null -> SManga.UNKNOWN this == null -> SManga.UNKNOWN
ongoingWords.doesInclude(this) -> SManga.ONGOING ongoingWords.doesInclude(this) -> SManga.ONGOING

View File

@ -9,7 +9,7 @@ class WPComicsGenerator : ThemeSourceGenerator {
override val themeClass = "WPComics" override val themeClass = "WPComics"
override val baseVersionCode: Int = 2 override val baseVersionCode: Int = 3
override val sources = listOf( override val sources = listOf(
SingleLang("NetTruyen", "https://www.nettruyenclub.com", "vi", overrideVersionCode = 21), SingleLang("NetTruyen", "https://www.nettruyenclub.com", "vi", overrideVersionCode = 21),

View File

@ -6,7 +6,7 @@ ext {
extName = 'Madokami' extName = 'Madokami'
pkgNameSuffix = 'en.madokami' pkgNameSuffix = 'en.madokami'
extClass = '.Madokami' extClass = '.Madokami'
extVersionCode = 7 extVersionCode = 8
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -67,7 +67,10 @@ class Madokami : ConfigurableSource, ParsedHttpSource() {
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.url = element.attr("href") manga.url = element.attr("href")
manga.title = URLDecoder.decode(element.attr("href").split("/").last(), "UTF-8").trimStart('!') val pathSegments = element.attr("href").split("/")
var i = pathSegments.size
manga.description = URLDecoder.decode(pathSegments[i - 1], "UTF-8")
do { i--; manga.title = URLDecoder.decode(pathSegments[i], "UTF-8") } while (URLDecoder.decode(pathSegments[i], "UTF-8").startsWith("!"))
return manga return manga
} }
@ -109,8 +112,7 @@ class Madokami : ConfigurableSource, ParsedHttpSource() {
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.author = document.select("a[itemprop=\"author\"]").joinToString(", ") { it.text() } manga.author = document.select("a[itemprop=\"author\"]").joinToString(", ") { it.text() }
manga.description = "Tags: " + document.select("div.genres[itemprop=\"keywords\"] a.tag.tag-category").joinToString(", ") { it.text() } manga.genre = document.select("div.genres a.tag").joinToString(", ") { it.text() }
manga.genre = document.select("div.genres a.tag[itemprop=\"genre\"]").joinToString(", ") { it.text() }
manga.status = if (document.select("span.scanstatus").text() == "Yes") SManga.COMPLETED else SManga.UNKNOWN manga.status = if (document.select("span.scanstatus").text() == "Yes") SManga.COMPLETED else SManga.UNKNOWN
manga.thumbnail_url = document.select("div.manga-info img[itemprop=\"image\"]").attr("src") manga.thumbnail_url = document.select("div.manga-info img[itemprop=\"image\"]").attr("src")
return manga return manga

View File

@ -6,7 +6,7 @@ ext {
extName = 'Multporn' extName = 'Multporn'
pkgNameSuffix = 'en.multporn' pkgNameSuffix = 'en.multporn'
extClass = '.Multporn' extClass = '.Multporn'
extVersionCode = 3 extVersionCode = 4
isNsfw = true isNsfw = true
} }

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.extension.en.multporn package eu.kanade.tachiyomi.extension.en.multporn
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
@ -12,18 +11,11 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.FormBody
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
@ -47,36 +39,22 @@ class Multporn : ParsedHttpSource() {
// Popular // Popular
private fun buildPopularMangaRequest(page: Int, filters: FilterList = FilterList()): Request { private fun buildPopularMangaRequest(page: Int, filters: FilterList = FilterList()): Request {
val body = FormBody.Builder() val url = "$baseUrl/best".toHttpUrl().newBuilder()
.addEncoded("page", page.toString()) .addQueryParameter("page", page.toString())
.addEncoded("view_name", "top")
.addEncoded("view_display_id", "page")
(if (filters.isEmpty()) getFilterList(POPULAR_DEFAULT_SORT_BY_FILTER_STATE) else filters).forEach { (if (filters.isEmpty()) getFilterList(POPULAR_DEFAULT_SORT_BY_FILTER_STATE) else filters).forEach {
when (it) { when (it) {
is SortBySelectFilter -> body.addEncoded("sort_by", it.selected.uri) is SortBySelectFilter -> url.addQueryParameter("sort_by", it.selected.uri)
is SortOrderSelectFilter -> body.addEncoded("sort_order", it.selected.uri) is SortOrderSelectFilter -> url.addQueryParameter("sort_order", it.selected.uri)
is PopularTypeSelectFilter -> body.addEncoded("type", it.selected.uri) is PopularTypeSelectFilter -> url.addQueryParameter("type", it.selected.uri)
else -> { } else -> { }
} }
} }
return POST("$baseUrl/views/ajax", headers, body.build()) return GET(url.build(), headers)
} }
override fun popularMangaRequest(page: Int) = buildPopularMangaRequest(page - 1) override fun popularMangaRequest(page: Int) = buildPopularMangaRequest(page - 1)
override fun popularMangaParse(response: Response): MangasPage {
val html = json.decodeFromString<JsonArray>(response.body.string())
.last { it.jsonObject["command"]!!.jsonPrimitive.content == "insert" }.jsonObject["data"]!!.jsonPrimitive.content
return super.popularMangaParse(
response.newBuilder()
.body(html.toResponseBody("text/html; charset=UTF-8".toMediaTypeOrNull()))
.build(),
)
}
override fun popularMangaSelector() = ".masonry-item" override fun popularMangaSelector() = ".masonry-item"
override fun popularMangaNextPageSelector() = ".pager-next a" override fun popularMangaNextPageSelector() = ".pager-next a"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
@ -88,7 +66,7 @@ class Multporn : ParsedHttpSource() {
// Latest // Latest
private fun buildLatestMangaRequest(page: Int, filters: FilterList = FilterList()): Request { private fun buildLatestMangaRequest(page: Int, filters: FilterList = FilterList()): Request {
val url = "$baseUrl/new".toHttpUrlOrNull()!!.newBuilder() val url = "$baseUrl/new".toHttpUrl().newBuilder()
.addQueryParameter("page", page.toString()) .addQueryParameter("page", page.toString())
(if (filters.isEmpty()) getFilterList(LATEST_DEFAULT_SORT_BY_FILTER_STATE) else filters).forEach { (if (filters.isEmpty()) getFilterList(LATEST_DEFAULT_SORT_BY_FILTER_STATE) else filters).forEach {
@ -126,7 +104,7 @@ class Multporn : ParsedHttpSource() {
} }
private fun buildSearchMangaRequest(page: Int, query: String, filtersArg: FilterList = FilterList()): Request { private fun buildSearchMangaRequest(page: Int, query: String, filtersArg: FilterList = FilterList()): Request {
val url = "$baseUrl/search".toHttpUrlOrNull()!!.newBuilder() val url = "$baseUrl/search".toHttpUrl().newBuilder()
.addQueryParameter("page", page.toString()) .addQueryParameter("page", page.toString())
.addQueryParameter("views_fulltext", query) .addQueryParameter("views_fulltext", query)

View File

@ -5,7 +5,7 @@ ext {
extName = 'Mundo Hentai' extName = 'Mundo Hentai'
pkgNameSuffix = 'pt.mundohentai' pkgNameSuffix = 'pt.mundohentai'
extClass = '.MundoHentai' extClass = '.MundoHentai'
extVersionCode = 7 extVersionCode = 8
isNsfw = true isNsfw = true
} }

View File

@ -56,7 +56,7 @@ class MundoHentai : ParsedHttpSource() {
return GET("$baseUrl/category/doujinshi/$pageStr", newHeaders) return GET("$baseUrl/category/doujinshi/$pageStr", newHeaders)
} }
override fun popularMangaSelector(): String = "div.lista > ul > li div.thumb-conteudo:has(a[href^=$baseUrl]:has(span.thumb-imagem)):not(:contains(Tufos))" override fun popularMangaSelector(): String = "div.lista > ul > li div.thumb-conteudo:has(a[href^=$baseUrl]):not(:contains(Tufos))"
override fun popularMangaFromElement(element: Element): SManga = genericMangaFromElement(element) override fun popularMangaFromElement(element: Element): SManga = genericMangaFromElement(element)

View File

@ -6,7 +6,7 @@ ext {
extName = 'DMZJ' extName = 'DMZJ'
pkgNameSuffix = 'zh.dmzj' pkgNameSuffix = 'zh.dmzj'
extClass = '.Dmzj' extClass = '.Dmzj'
extVersionCode = 41 extVersionCode = 42
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -27,7 +27,7 @@ class Dmzj : ConfigurableSource, HttpSource() {
override val lang = "zh" override val lang = "zh"
override val supportsLatest = true override val supportsLatest = true
override val name = "动漫之家" override val name = "动漫之家"
override val baseUrl = "https://m.dmzj.com" override val baseUrl = "https://m.idmzj.com"
private val preferences: SharedPreferences = private val preferences: SharedPreferences =
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
@ -36,6 +36,13 @@ class Dmzj : ConfigurableSource, HttpSource() {
.addInterceptor(ImageUrlInterceptor) .addInterceptor(ImageUrlInterceptor)
.addInterceptor(CommentsInterceptor) .addInterceptor(CommentsInterceptor)
.rateLimit(4) .rateLimit(4)
.apply {
val interceptors = interceptors()
val index = interceptors.indexOfFirst { "Brotli" in it.javaClass.simpleName }
if (index >= 0) {
interceptors.add(interceptors.removeAt(index))
}
}
.build() .build()
// API v4 randomly fails // API v4 randomly fails

View File

@ -6,7 +6,7 @@ ext {
extName = 'Roumanwu' extName = 'Roumanwu'
pkgNameSuffix = 'zh.roumanwu' pkgNameSuffix = 'zh.roumanwu'
extClass = '.Roumanwu' extClass = '.Roumanwu'
extVersionCode = 6 extVersionCode = 7
isNsfw = true isNsfw = true
} }

View File

@ -116,7 +116,7 @@ class Roumanwu : HttpSource(), ConfigurableSource {
private const val MIRROR_PREF_SUMMARY = "使用镜像网址。重启软件生效。" private const val MIRROR_PREF_SUMMARY = "使用镜像网址。重启软件生效。"
// 地址: https://rou.pub/dizhi // 地址: https://rou.pub/dizhi
private val MIRRORS = arrayOf("https://rouman5.com", "https://roum2.xyz") private val MIRRORS = arrayOf("https://rouman5.com", "https://roum10.xyz")
private val MIRRORS_DESC = arrayOf("主站", "镜像") private val MIRRORS_DESC = arrayOf("主站", "镜像")
private const val MIRROR_DEFAULT = 1.toString() // use mirror private const val MIRROR_DEFAULT = 1.toString() // use mirror