Compare commits
29 Commits
480cb9d780
...
1b722d308d
Author | SHA1 | Date |
---|---|---|
AwkwardPeak7 | 1b722d308d | |
Secozzi | 6b238a0e09 | |
Secozzi | 1d19c2c688 | |
AwkwardPeak7 | af9b767d1b | |
AwkwardPeak7 | 5d3348bd5b | |
AwkwardPeak7 | ce1b255bcd | |
AwkwardPeak7 | ff7408266e | |
AwkwardPeak7 | ef36432ffc | |
KirinRaikage | 8bbc841b7d | |
Vetle Ledaal | 74d3bda48e | |
Vetle Ledaal | 951b3c9d00 | |
morallkat | 200ac6fd32 | |
AwkwardPeak7 | e8f8e9e8a9 | |
WarmSeeker6 | ea833d9401 | |
Barrell Titor | c406609d33 | |
WarmSeeker6 | f69e16b3bb | |
bapeey | 69624b4193 | |
Barrell Titor | 461846edaa | |
AwkwardPeak7 | 01ab21ab4a | |
bapeey | 63c1a534a2 | |
Vetle Ledaal | a4c0faaac8 | |
mohamedotaku | 2967ffe0dc | |
bapeey | 49ccd590b8 | |
bapeey | 49b3b90db3 | |
mohamedotaku | 7bb2113b37 | |
mohamedotaku | 7217df6180 | |
mohamedotaku | 631272428e | |
haruki-takeshi | 71fa6bdd37 | |
bapeey | 08b7ec34bd |
|
@ -2,4 +2,4 @@ plugins {
|
|||
id("lib-multisrc")
|
||||
}
|
||||
|
||||
baseVersionCode = 5
|
||||
baseVersionCode = 6
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package eu.kanade.tachiyomi.multisrc.flixscans
|
||||
|
||||
import android.util.Log
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
|
@ -14,18 +14,17 @@ import kotlinx.serialization.decodeFromString
|
|||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.IOException
|
||||
|
||||
abstract class FlixScans(
|
||||
override val name: String,
|
||||
override val baseUrl: String,
|
||||
override val lang: String,
|
||||
protected val apiUrl: String = "$baseUrl/api/__api_party/noxApi",
|
||||
protected val apiUrl: String = "$baseUrl/api/v1",
|
||||
protected val cdnUrl: String = baseUrl.replace("://", "://media.").plus("/"),
|
||||
) : HttpSource() {
|
||||
|
||||
|
@ -38,22 +37,10 @@ abstract class FlixScans(
|
|||
.build()
|
||||
|
||||
override fun headersBuilder() = super.headersBuilder()
|
||||
.add("Referer", baseUrl)
|
||||
|
||||
protected open fun postPath(path: String): Request {
|
||||
val payload = """{"path":"$path","headers":{}}""".toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
return POST(apiUrl, headers, payload)
|
||||
}
|
||||
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||
runCatching { fetchGenre() }
|
||||
|
||||
return super.fetchPopularManga(page)
|
||||
}
|
||||
.add("Referer", "$baseUrl/")
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
return postPath("webtoon/pages/home/romance")
|
||||
return GET("$apiUrl/webtoon/pages/home/romance", headers)
|
||||
}
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
|
@ -66,21 +53,15 @@ abstract class FlixScans(
|
|||
return MangasPage(entries, false)
|
||||
}
|
||||
|
||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
||||
runCatching { fetchGenre() }
|
||||
|
||||
return super.fetchLatestUpdates(page)
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request {
|
||||
return postPath("search/advance?page=$page&serie_type=webtoon")
|
||||
return GET("$apiUrl/search/advance?page=$page&serie_type=webtoon", headers)
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val result = response.parseAs<ApiResponse<BrowseSeries>>()
|
||||
|
||||
val entries = result.data.map { it.toSManga(cdnUrl) }
|
||||
val hasNextPage = result.meta.lastPage > result.meta.currentPage
|
||||
val hasNextPage = result.lastPage > result.currentPage
|
||||
|
||||
return MangasPage(entries, hasNextPage)
|
||||
}
|
||||
|
@ -100,7 +81,7 @@ abstract class FlixScans(
|
|||
}
|
||||
|
||||
private val fetchGenreCallback = object : Callback {
|
||||
override fun onFailure(call: Call, e: okio.IOException) {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
fetchGenreAttempt++
|
||||
fetchGenreFailed = true
|
||||
fetchGenreCallOngoing = false
|
||||
|
@ -132,7 +113,7 @@ abstract class FlixScans(
|
|||
}
|
||||
|
||||
private fun fetchGenreRequest(): Request {
|
||||
return postPath("search/genres")
|
||||
return GET("$apiUrl/search/genres", headers)
|
||||
}
|
||||
|
||||
private fun fetchGenreParse(response: Response): List<GenreHolder> {
|
||||
|
@ -140,6 +121,8 @@ abstract class FlixScans(
|
|||
}
|
||||
|
||||
override fun getFilterList(): FilterList {
|
||||
fetchGenre()
|
||||
|
||||
val filters: MutableList<Filter<*>> = mutableListOf(
|
||||
Filter.Header("Ignored when using Text Search"),
|
||||
MainGenreFilter(),
|
||||
|
@ -161,60 +144,59 @@ abstract class FlixScans(
|
|||
return FilterList(filters)
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
runCatching { fetchGenre() }
|
||||
|
||||
return super.fetchSearchManga(page, query, filters)
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
if (query.isNotEmpty()) {
|
||||
return postPath("search/serie/${query.trim()}?page=$page")
|
||||
val url = "$apiUrl/search/serie".toHttpUrl().newBuilder()
|
||||
.addPathSegment(query.trim())
|
||||
.addQueryParameter("page", page.toString())
|
||||
.build()
|
||||
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
val advSearchBody = buildString {
|
||||
append("search/advance")
|
||||
append("?page=", page)
|
||||
append("&serie_type=webtoon")
|
||||
val advSearchUrl = apiUrl.toHttpUrl().newBuilder().apply {
|
||||
addPathSegments("search/advance")
|
||||
addQueryParameter("page", page.toString())
|
||||
addQueryParameter("serie_type", "webtoon")
|
||||
|
||||
filters.forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreFilter -> {
|
||||
filter.checked.let {
|
||||
if (it.isNotEmpty()) {
|
||||
append("&genres=", it.joinToString(","))
|
||||
addQueryParameter("genres", it.joinToString(","))
|
||||
}
|
||||
}
|
||||
}
|
||||
is MainGenreFilter -> {
|
||||
if (filter.state > 0) {
|
||||
append("&main_genres=", filter.selected)
|
||||
addQueryParameter("main_genres", filter.selected)
|
||||
}
|
||||
}
|
||||
is TypeFilter -> {
|
||||
if (filter.state > 0) {
|
||||
append("&type=", filter.selected)
|
||||
addQueryParameter("type", filter.selected)
|
||||
}
|
||||
}
|
||||
is StatusFilter -> {
|
||||
if (filter.state > 0) {
|
||||
append("&status=", filter.selected)
|
||||
addQueryParameter("status", filter.selected)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.build()
|
||||
|
||||
return postPath(advSearchBody)
|
||||
return GET(advSearchUrl, headers)
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||
val id = manga.url.split("-")[1]
|
||||
val (prefix, id) = getPrefixIdFromUrl(manga.url)
|
||||
|
||||
return postPath("webtoon/series/$id")
|
||||
return GET("$apiUrl/webtoon/series/$id/$prefix", headers)
|
||||
}
|
||||
|
||||
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url
|
||||
|
@ -226,23 +208,30 @@ abstract class FlixScans(
|
|||
}
|
||||
|
||||
override fun chapterListRequest(manga: SManga): Request {
|
||||
val id = manga.url.split("-")[1]
|
||||
val (prefix, id) = getPrefixIdFromUrl(manga.url)
|
||||
|
||||
return postPath("webtoon/chapters/$id-desc")
|
||||
return GET("$apiUrl/webtoon/chapters/$id-desc#$prefix", headers)
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val chapters = response.parseAs<List<Chapter>>()
|
||||
val prefix = response.request.url.fragment!!
|
||||
|
||||
return chapters.map(Chapter::toSChapter)
|
||||
return chapters.map { it.toSChapter(prefix) }
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
val id = chapter.url
|
||||
.substringAfterLast("/")
|
||||
.substringBefore("-")
|
||||
val (prefix, id) = getPrefixIdFromUrl(chapter.url)
|
||||
|
||||
return postPath("webtoon/chapters/chapter/$id")
|
||||
return GET("$apiUrl/webtoon/chapters/chapter/$id/$prefix", headers)
|
||||
}
|
||||
|
||||
protected fun getPrefixIdFromUrl(url: String): Pair<String, String> {
|
||||
return with(url.substringAfterLast("/")) {
|
||||
val split = split("-")
|
||||
|
||||
split[0] to split[1]
|
||||
}
|
||||
}
|
||||
|
||||
override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url
|
||||
|
@ -259,8 +248,4 @@ abstract class FlixScans(
|
|||
|
||||
protected inline fun <reified T> Response.parseAs(): T =
|
||||
use { body.string() }.let(json::decodeFromString)
|
||||
|
||||
companion object {
|
||||
private val JSON_MEDIA_TYPE = "application/json".toMediaTypeOrNull()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,8 @@ import java.util.Locale
|
|||
@Serializable
|
||||
data class ApiResponse<T>(
|
||||
val data: List<T>,
|
||||
val meta: PageInfo,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PageInfo(
|
||||
@SerialName("last_page") val lastPage: Int,
|
||||
@SerialName("current_page") val currentPage: Int,
|
||||
@SerialName("last_page") val lastPage: Int,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
@ -120,8 +115,8 @@ data class Chapter(
|
|||
val slug: String,
|
||||
val createdAt: String? = null,
|
||||
) {
|
||||
fun toSChapter() = SChapter.create().apply {
|
||||
url = "/read/webtoon/$id-$slug"
|
||||
fun toSChapter(prefix: String) = SChapter.create().apply {
|
||||
url = "/read/webtoon/$prefix-$id-$slug"
|
||||
name = this@Chapter.name
|
||||
date_upload = runCatching { dateFormat.parse(createdAt!!)!!.time }.getOrDefault(0L)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ abstract class Gmanga(
|
|||
override val name: String,
|
||||
override val baseUrl: String,
|
||||
final override val lang: String,
|
||||
protected val cdnUrl: String = baseUrl,
|
||||
protected open val cdnUrl: String = baseUrl,
|
||||
) : HttpSource() {
|
||||
|
||||
override val supportsLatest = true
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
plugins {
|
||||
id("lib-multisrc")
|
||||
}
|
||||
|
||||
baseVersionCode = 1
|
||||
|
||||
dependencies {
|
||||
api(project(":lib:cookieinterceptor"))
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package eu.kanade.tachiyomi.multisrc.hotcomics
|
||||
|
||||
import eu.kanade.tachiyomi.lib.cookieinterceptor.CookieInterceptor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
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.HttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Element
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
abstract class HotComics(
|
||||
final override val name: String,
|
||||
final override val lang: String,
|
||||
final override val baseUrl: String,
|
||||
) : HttpSource() {
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client = network.cloudflareClient.newBuilder()
|
||||
.addNetworkInterceptor(
|
||||
CookieInterceptor(baseUrl.removePrefix("https://"), "hc_vfs" to "Y"),
|
||||
)
|
||||
.build()
|
||||
|
||||
override fun headersBuilder() = super.headersBuilder()
|
||||
.set("Referer", "$baseUrl/")
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/en", headers)
|
||||
override fun popularMangaParse(response: Response) = searchMangaParse(response)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/en/new", headers)
|
||||
override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = baseUrl.toHttpUrl().newBuilder().apply {
|
||||
if (query.isNotEmpty()) {
|
||||
addEncodedPathSegments("en/search")
|
||||
addQueryParameter("keyword", query.trim())
|
||||
} else {
|
||||
val filter = filters.filterIsInstance<BrowseFilter>().first()
|
||||
addEncodedPathSegments(filter.selected)
|
||||
addQueryParameter("page", page.toString())
|
||||
}
|
||||
}.build()
|
||||
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
abstract class SelectFilter(
|
||||
name: String,
|
||||
private val options: List<Pair<String, String>>,
|
||||
) : Filter.Select<String>(
|
||||
name,
|
||||
options.map { it.first }.toTypedArray(),
|
||||
) {
|
||||
val selected get() = options[state].second
|
||||
}
|
||||
|
||||
abstract val browseList: List<Pair<String, String>>
|
||||
|
||||
class BrowseFilter(browseList: List<Pair<String, String>>) : SelectFilter("Browse", browseList)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
Filter.Header("Doesn't work with Text search"),
|
||||
Filter.Separator(),
|
||||
BrowseFilter(browseList),
|
||||
)
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val document = response.asJsoup()
|
||||
|
||||
val entries = document.select("li[itemtype*=ComicSeries]:not(.no-comic) > a").map { element ->
|
||||
SManga.create().apply {
|
||||
setUrlWithoutDomain(element.absUrl("href"))
|
||||
thumbnail_url = element.selectFirst("div.visual img")?.imgAttr()
|
||||
title = element.selectFirst("div.main-text > h4.title")!!.text()
|
||||
}
|
||||
}.distinctBy { it.url }
|
||||
val hasNextPage = document.selectFirst("div.pagination a.vnext:not(.disabled)") != null
|
||||
|
||||
return MangasPage(entries, hasNextPage)
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(response: Response) = SManga.create().apply {
|
||||
val document = response.asJsoup()
|
||||
|
||||
title = document.selectFirst("h2.episode-title")!!.text()
|
||||
with(document.selectFirst("p.type_box")!!) {
|
||||
author = selectFirst("span.writer")?.text()
|
||||
?.substringAfter("ⓒ")?.trim()
|
||||
genre = selectFirst("span.type")?.text()
|
||||
?.split("/")?.joinToString { it.trim() }
|
||||
status = when (selectFirst("span.date")?.text()) {
|
||||
"End", "Ende" -> SManga.COMPLETED
|
||||
null -> SManga.UNKNOWN
|
||||
else -> SManga.ONGOING
|
||||
}
|
||||
}
|
||||
description = buildString {
|
||||
document.selectFirst("div.episode-contents header")
|
||||
?.text()?.let {
|
||||
append(it)
|
||||
append("\n\n")
|
||||
}
|
||||
document.selectFirst("div.title_content > h2:not(.episode-title)")
|
||||
?.text()?.let { append(it) }
|
||||
}.trim()
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
return response.asJsoup().select("#tab-chapter a").map { element ->
|
||||
SChapter.create().apply {
|
||||
setUrlWithoutDomain(element.absUrl("href"))
|
||||
name = element.selectFirst(".cell-num")!!.text()
|
||||
date_upload = parseDate(element.selectFirst(".cell-time")?.text())
|
||||
}
|
||||
}.reversed()
|
||||
}
|
||||
|
||||
private val dateFormat = SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH)
|
||||
|
||||
private fun parseDate(date: String?): Long {
|
||||
date ?: return 0L
|
||||
|
||||
return try {
|
||||
dateFormat.parse(date)!!.time
|
||||
} catch (_: ParseException) {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
return response.asJsoup().select("#viewer-img img").mapIndexed { idx, img ->
|
||||
Page(idx, imageUrl = img.imgAttr())
|
||||
}
|
||||
}
|
||||
|
||||
private fun Element.imgAttr(): String {
|
||||
return when {
|
||||
hasAttr("data-src") -> absUrl("data-src")
|
||||
else -> absUrl("src")
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlParse(response: Response): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
|
@ -2,4 +2,4 @@ plugins {
|
|||
id("lib-multisrc")
|
||||
}
|
||||
|
||||
baseVersionCode = 8
|
||||
baseVersionCode = 9
|
||||
|
|
|
@ -27,7 +27,7 @@ abstract class ZeistManga(
|
|||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
protected val json: Json by injectLazy()
|
||||
|
||||
private val intl by lazy { ZeistMangaIntl(lang) }
|
||||
|
||||
|
@ -63,23 +63,7 @@ abstract class ZeistManga(
|
|||
return GET(url, headers)
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val jsonString = response.body.string()
|
||||
val result = json.decodeFromString<ZeistMangaDto>(jsonString)
|
||||
|
||||
val mangas = result.feed?.entry.orEmpty()
|
||||
.filter { it.category.orEmpty().any { category -> category.term == "Series" } }
|
||||
.filter { !it.category.orEmpty().any { category -> category.term == "Anime" } }
|
||||
.map { it.toSManga(baseUrl) }
|
||||
|
||||
val mangalist = mangas.toMutableList()
|
||||
if (mangas.size == maxMangaResults + 1) {
|
||||
mangalist.removeLast()
|
||||
return MangasPage(mangalist, true)
|
||||
}
|
||||
|
||||
return MangasPage(mangalist, false)
|
||||
}
|
||||
override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val startIndex = maxMangaResults * (page - 1) + 1
|
||||
|
@ -123,7 +107,25 @@ abstract class ZeistManga(
|
|||
return GET(url.build(), headers)
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
|
||||
protected open val excludedCategories: List<String> = listOf("Anime")
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val jsonString = response.body.string()
|
||||
val result = json.decodeFromString<ZeistMangaDto>(jsonString)
|
||||
|
||||
val mangas = result.feed?.entry.orEmpty()
|
||||
.filter { it.category.orEmpty().any { category -> category.term == "Series" } } // Default category for all series
|
||||
.filterNot { it.category.orEmpty().any { category -> excludedCategories.contains(category.term) } }
|
||||
.map { it.toSManga(baseUrl) }
|
||||
|
||||
val mangalist = mangas.toMutableList()
|
||||
if (mangas.size == maxMangaResults + 1) {
|
||||
mangalist.removeLast()
|
||||
return MangasPage(mangalist, true)
|
||||
}
|
||||
|
||||
return MangasPage(mangalist, false)
|
||||
}
|
||||
|
||||
protected open val statusSelectorList = listOf(
|
||||
"Status",
|
||||
|
@ -199,13 +201,9 @@ abstract class ZeistManga(
|
|||
val document = response.asJsoup()
|
||||
|
||||
val url = getChapterFeedUrl(document)
|
||||
val res = client.newCall(GET(url, headers)).execute()
|
||||
|
||||
val req = GET(url, headers)
|
||||
val res = client.newCall(req).execute()
|
||||
|
||||
val jsonString = res.body.string()
|
||||
val result = json.decodeFromString<ZeistMangaDto>(jsonString)
|
||||
|
||||
val result = json.decodeFromString<ZeistMangaDto>(res.body.string())
|
||||
return result.feed?.entry?.filter { it.category.orEmpty().any { category -> category.term == chapterCategory } }
|
||||
?.map { it.toSChapter(baseUrl) }
|
||||
?: throw Exception("Failed to parse from chapter API")
|
||||
|
@ -392,6 +390,7 @@ abstract class ZeistManga(
|
|||
"ongoing",
|
||||
"en curso",
|
||||
"en emisión",
|
||||
"activo",
|
||||
"ativo",
|
||||
"lançando",
|
||||
"مستمر",
|
||||
|
@ -400,10 +399,12 @@ abstract class ZeistManga(
|
|||
protected open val statusCompletedList = listOf(
|
||||
"completed",
|
||||
"completo",
|
||||
"finalizado",
|
||||
)
|
||||
|
||||
protected open val statusHiatusList = listOf(
|
||||
"hiatus",
|
||||
"pausado",
|
||||
)
|
||||
|
||||
protected open val statusCancelledList = listOf(
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
ext {
|
||||
extName = 'HNI-Scantrad'
|
||||
extClass = '.HNIScantradFactory'
|
||||
themePkg = 'foolslide'
|
||||
baseUrl = 'https://hni-scantrad.com'
|
||||
overrideVersionCode = 1
|
||||
extClass = '.HNIScantrad'
|
||||
themePkg = 'pizzareader'
|
||||
baseUrl = 'https://hni-scantrad.net'
|
||||
overrideVersionCode = 5
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package eu.kanade.tachiyomi.extension.all.hniscantrad
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.pizzareader.PizzaReader
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class HNIScantrad : PizzaReader("HNI-Scantrad", "https://hni-scantrad.net", "all") {
|
||||
override val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
coerceInputValues = true
|
||||
}
|
||||
|
||||
override fun String.toStatus(): Int {
|
||||
return if (isEmpty()) {
|
||||
SManga.UNKNOWN
|
||||
} else {
|
||||
when (substring(0, 7)) {
|
||||
"In cors" -> SManga.ONGOING
|
||||
"On goin" -> SManga.ONGOING
|
||||
"Complet" -> SManga.COMPLETED
|
||||
"Conclus" -> SManga.COMPLETED
|
||||
"Conclud" -> SManga.COMPLETED
|
||||
"Licenzi" -> SManga.LICENSED
|
||||
"License" -> SManga.LICENSED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.all.hniscantrad
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.foolslide.FoolSlide
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
import okhttp3.Response
|
||||
|
||||
class HNIScantradFactory : SourceFactory {
|
||||
override fun createSources() = listOf(HNIScantradFR(), HNIScantradEN())
|
||||
}
|
||||
|
||||
class HNIScantradFR : FoolSlide("HNI-Scantrad", "https://hni-scantrad.com", "fr", "/lel") {
|
||||
override fun chapterListParse(response: Response) =
|
||||
super.chapterListParse(response).filter { "/fr/" in it.url }
|
||||
}
|
||||
|
||||
class HNIScantradEN : FoolSlide("HNI-Scantrad", "https://hni-scantrad.com", "en", "/lel") {
|
||||
override fun chapterListParse(response: Response) =
|
||||
super.chapterListParse(response).filter { "/en-us/" in it.url }
|
||||
}
|
|
@ -2,7 +2,7 @@ ext {
|
|||
extName = 'Dilar'
|
||||
extClass = '.Dilar'
|
||||
themePkg = 'gmanga'
|
||||
overrideVersionCode = 0
|
||||
overrideVersionCode = 2
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,15 +1,30 @@
|
|||
package eu.kanade.tachiyomi.extension.ar.dilar
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.widget.Toast
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.multisrc.gmanga.Gmanga
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class Dilar : Gmanga(
|
||||
private const val MIRROR_PREF_KEY = "MIRROR"
|
||||
private const val MIRROR_PREF_TITLE = "Dilar : Mirror Urls"
|
||||
private val MIRROR_PREF_ENTRY_VALUES = arrayOf("https://dilar.tube", "https://golden.rest")
|
||||
private val MIRROR_PREF_DEFAULT_VALUE = MIRROR_PREF_ENTRY_VALUES[0]
|
||||
private const val RESTART_TACHIYOMI = ".لتطبيق الإعدادات الجديدة Tachiyomi أعد تشغيل"
|
||||
|
||||
class Dilar :
|
||||
ConfigurableSource, Gmanga(
|
||||
"Dilar",
|
||||
"https://dilar.tube",
|
||||
MIRROR_PREF_DEFAULT_VALUE,
|
||||
"ar",
|
||||
) {
|
||||
override fun chaptersRequest(manga: SManga): Request {
|
||||
|
@ -23,4 +38,34 @@ class Dilar : Gmanga(
|
|||
|
||||
return releases.map { it.toSChapter() }
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
val mirrorPref = ListPreference(screen.context).apply {
|
||||
key = MIRROR_PREF_KEY
|
||||
title = MIRROR_PREF_TITLE
|
||||
entries = MIRROR_PREF_ENTRY_VALUES
|
||||
entryValues = MIRROR_PREF_ENTRY_VALUES
|
||||
setDefaultValue(MIRROR_PREF_DEFAULT_VALUE)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, _ ->
|
||||
Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show()
|
||||
true
|
||||
}
|
||||
}
|
||||
screen.addPreference(mirrorPref)
|
||||
}
|
||||
|
||||
private fun mirrorPref() = when {
|
||||
System.getenv("CI") == "true" -> MIRROR_PREF_ENTRY_VALUES.joinToString("#, ")
|
||||
else -> preferences.getString(MIRROR_PREF_KEY, MIRROR_PREF_DEFAULT_VALUE)!!
|
||||
}
|
||||
|
||||
override val baseUrl by lazy { mirrorPref() }
|
||||
|
||||
override val cdnUrl by lazy { baseUrl }
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,11 @@ package eu.kanade.tachiyomi.extension.ar.galaxymanga
|
|||
|
||||
import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans
|
||||
|
||||
class GalaxyManga : FlixScans("جالاكسي مانجا", "https://flixscans.com", "ar") {
|
||||
class GalaxyManga : FlixScans(
|
||||
"جالاكسي مانجا",
|
||||
"https://flixscans.com",
|
||||
"ar",
|
||||
"https://ar.flixscans.site/api/v1",
|
||||
) {
|
||||
override val versionId = 2
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@ ext {
|
|||
extClass = '.Mangalek'
|
||||
themePkg = 'madara'
|
||||
baseUrl = 'https://lekmanga.net'
|
||||
overrideVersionCode = 5
|
||||
overrideVersionCode = 6
|
||||
isNsfw = false
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.extension.ar.mangalek
|
|||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.widget.Toast
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.extension.BuildConfig
|
||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
|
@ -18,14 +18,25 @@ import okhttp3.Request
|
|||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
private const val mangalekUrl = "https://lekmanga.net"
|
||||
private const val MIRROR_PREF_KEY = "MIRROR"
|
||||
private const val MIRROR_PREF_TITLE = "تعديل رابط مانجا ليك"
|
||||
internal val MIRROR_PREF_ENTRY_VALUES = arrayOf(
|
||||
"https://lekmanga.net",
|
||||
"https://lekmanga.org",
|
||||
"https://like-manga.net",
|
||||
"https://lekmanga.com",
|
||||
)
|
||||
private val MIRROR_PREF_DEFAULT_VALUE = MIRROR_PREF_ENTRY_VALUES[0]
|
||||
private const val RESTART_TACHIYOMI = ".لتطبيق الإعدادات الجديدة Tachiyomi أعد تشغيل"
|
||||
|
||||
class Mangalek :
|
||||
Madara(
|
||||
"مانجا ليك",
|
||||
mangalekUrl,
|
||||
MIRROR_PREF_DEFAULT_VALUE,
|
||||
"ar",
|
||||
SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
||||
),
|
||||
|
@ -35,38 +46,50 @@ class Mangalek :
|
|||
override val useLoadMoreRequest = LoadMoreStrategy.Always
|
||||
override val chapterUrlSuffix = ""
|
||||
|
||||
private val defaultBaseUrl = mangalekUrl
|
||||
override val baseUrl by lazy { getPrefBaseUrl() }
|
||||
override val baseUrl by lazy {
|
||||
when {
|
||||
System.getenv("CI") == "true" -> MIRROR_PREF_ENTRY_VALUES.joinToString("#, ")
|
||||
else -> preferences.getString(MIRROR_PREF_KEY, MIRROR_PREF_DEFAULT_VALUE)!!
|
||||
}
|
||||
}
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val RESTART_TACHIYOMI = ".لتطبيق الإعدادات الجديدة Tachiyomi أعد تشغيل"
|
||||
private const val BASE_URL_PREF_TITLE = "تعديل الرابط"
|
||||
private const val BASE_URL_PREF = "overrideBaseUrl_v${BuildConfig.VERSION_CODE}"
|
||||
private const val BASE_URL_PREF_SUMMARY = ".للاستخدام المؤقت. تحديث التطبيق سيؤدي الى حذف الإعدادات"
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
val baseUrlPref = androidx.preference.EditTextPreference(screen.context).apply {
|
||||
key = BASE_URL_PREF
|
||||
title = BASE_URL_PREF_TITLE
|
||||
summary = BASE_URL_PREF_SUMMARY
|
||||
this.setDefaultValue(defaultBaseUrl)
|
||||
dialogTitle = BASE_URL_PREF_TITLE
|
||||
dialogMessage = "Default: $defaultBaseUrl"
|
||||
val mirrorPref = ListPreference(screen.context).apply {
|
||||
key = MIRROR_PREF_KEY
|
||||
title = MIRROR_PREF_TITLE
|
||||
entries = MIRROR_PREF_ENTRY_VALUES
|
||||
entryValues = MIRROR_PREF_ENTRY_VALUES
|
||||
setDefaultValue(MIRROR_PREF_DEFAULT_VALUE)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, _ ->
|
||||
Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show()
|
||||
true
|
||||
}
|
||||
}
|
||||
screen.addPreference(baseUrlPref)
|
||||
screen.addPreference(mirrorPref)
|
||||
}
|
||||
|
||||
private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, defaultBaseUrl)!!
|
||||
private val formatOne = SimpleDateFormat("MMMM dd, yyyy", Locale("ar"))
|
||||
private val formatTwo = SimpleDateFormat("yyyy-MM-dd", Locale.US)
|
||||
|
||||
override fun parseChapterDate(date: String?): Long {
|
||||
date ?: return 0L
|
||||
|
||||
return try {
|
||||
formatOne.parse(date)!!.time
|
||||
} catch (_: ParseException) {
|
||||
try {
|
||||
formatTwo.parse(date)!!.time
|
||||
} catch (_: ParseException) {
|
||||
0L
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
|
||||
POST(
|
||||
|
|
|
@ -5,13 +5,10 @@ import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistMangaDto
|
|||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class Yokai : ZeistManga("Yokai", "https://yokai-team.blogspot.com", "ar") {
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
// ============================== Chapters ==============================
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
ext {
|
||||
extName = 'Toomics.Top'
|
||||
extClass = '.ToomicsTop'
|
||||
themePkg = 'hotcomics'
|
||||
overrideVersionCode = 0
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,44 @@
|
|||
package eu.kanade.tachiyomi.extension.de.toomicstop
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.hotcomics.HotComics
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import okhttp3.Response
|
||||
|
||||
class ToomicsTop : HotComics(
|
||||
"Toomics.Top",
|
||||
"de",
|
||||
"https://toomics.top",
|
||||
) {
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val mangasPage = super.searchMangaParse(response)
|
||||
mangasPage.mangas.apply {
|
||||
for (i in indices) {
|
||||
this[i].url = this[i].url.replace(urlIdRegex, ".html")
|
||||
}
|
||||
}
|
||||
|
||||
return mangasPage
|
||||
}
|
||||
|
||||
private val urlIdRegex = Regex("""(/\w+).html""")
|
||||
|
||||
override val browseList = listOf(
|
||||
Pair("Home", "en"),
|
||||
Pair("Weekly", "en/weekly"),
|
||||
Pair("New", "en/new"),
|
||||
Pair("Genre: All", "en/genres"),
|
||||
Pair("Genre: Romantik", "en/genres/Romantik"),
|
||||
Pair("Genre: Drama", "en/genres/Drama"),
|
||||
Pair("Genre: BL", "en/genres/BL"),
|
||||
Pair("Genre: Action", "en/genres/Action"),
|
||||
Pair("Genre: Schulleben", "en/genres/Schulleben"),
|
||||
Pair("Genre: Fantasy", "en/genres/Fantasy"),
|
||||
Pair("Genre: Comedy", "en/genres/Comedy"),
|
||||
Pair("Genre: Historisch", "en/genres/Historisch"),
|
||||
Pair("Genre: Sci-Fi", "en/genres/Sci-Fi"),
|
||||
Pair("Genre: Thriller", "en/genres/Thriller"),
|
||||
Pair("Genre: Horror", "en/genres/Horror"),
|
||||
Pair("Genre: Sport", "en/genres/Sport"),
|
||||
Pair("Genre: GL", "en/genres/GL"),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
ext {
|
||||
extName = 'DAYcomics.me'
|
||||
extClass = '.DAYcomicsMe'
|
||||
themePkg = 'hotcomics'
|
||||
overrideVersionCode = 0
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,30 @@
|
|||
package eu.kanade.tachiyomi.extension.en.daycomicsme
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.hotcomics.HotComics
|
||||
|
||||
class DAYcomicsMe : HotComics(
|
||||
"DAYcomics.me",
|
||||
"en",
|
||||
"https://daycomics.me",
|
||||
) {
|
||||
override val browseList = listOf(
|
||||
Pair("Home", "en"),
|
||||
Pair("Weekly", "en/weekly"),
|
||||
Pair("New", "en/new"),
|
||||
Pair("Genre: All", "en/genres"),
|
||||
Pair("Genre: Romance", "en/genres/Romance"),
|
||||
Pair("Genre: Office", "en/genres/Office"),
|
||||
Pair("Genre: College", "en/genres/College"),
|
||||
Pair("Genre: Drama", "en/genres/Drama"),
|
||||
Pair("Genre: Isekai", "en/genres/Isekai"),
|
||||
Pair("Genre: UNCENSORED", "en/genres/UNCENSORED"),
|
||||
Pair("Genre: Action", "en/genres/Action"),
|
||||
Pair("Genre: BL", "en/genres/BL"),
|
||||
Pair("Genre: New", "en/genres/New"),
|
||||
Pair("Genre: Slice of Life", "en/genres/Slice_of_Life"),
|
||||
Pair("Genre: Supernatural", "en/genres/Supernatural"),
|
||||
Pair("Genre: Historical", "en/genres/Historical"),
|
||||
Pair("Genre: School Life", "en/genres/School_Life"),
|
||||
Pair("Genre: Horror Thriller", "en/genres/Horror_Thriller"),
|
||||
)
|
||||
}
|
|
@ -2,4 +2,9 @@ package eu.kanade.tachiyomi.extension.en.flixscans
|
|||
|
||||
import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans
|
||||
|
||||
class FlixScansNet : FlixScans("Flix Scans", "https://flixscans.org", "en")
|
||||
class FlixScansNet : FlixScans(
|
||||
"Flix Scans",
|
||||
"https://flixscans.org",
|
||||
"en",
|
||||
"https://flixscans.site/api/v1",
|
||||
)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
ext {
|
||||
extName = 'HotComics'
|
||||
extClass = '.HotComics'
|
||||
themePkg = 'hotcomics'
|
||||
overrideVersionCode = 0
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,29 @@
|
|||
package eu.kanade.tachiyomi.extension.en.hotcomics
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.hotcomics.HotComics
|
||||
|
||||
class HotComics : HotComics(
|
||||
"HotComics",
|
||||
"en",
|
||||
"https://hotcomics.me",
|
||||
) {
|
||||
override val browseList = listOf(
|
||||
Pair("Home", "en"),
|
||||
Pair("Weekly", "en/weekly"),
|
||||
Pair("New", "en/new"),
|
||||
Pair("Genre: All", "en/genres"),
|
||||
Pair("Genre: Sports", "en/genres/Sports"),
|
||||
Pair("Genre: Historical", "en/genres/Historical"),
|
||||
Pair("Genre: Drama", "en/genres/Drama"),
|
||||
Pair("Genre: BL", "en/genres/BL"),
|
||||
Pair("Genre: Thriller", "en/genres/Thriller"),
|
||||
Pair("Genre: School life", "en/genres/School_life"),
|
||||
Pair("Genre: Comedy", "en/genres/Comedy"),
|
||||
Pair("Genre: GL", "en/genres/GL"),
|
||||
Pair("Genre: Action", "en/genres/Action"),
|
||||
Pair("Genre: Sci-fi", "en/genres/Sci-fi"),
|
||||
Pair("Genre: Horror", "en/genres/Horror"),
|
||||
Pair("Genre: Fantasy", "en/genres/Fantasy"),
|
||||
Pair("Genre: Romance", "en/genres/Romance"),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
ext {
|
||||
extName = 'Laid Back Scans'
|
||||
extClass = '.LaidBackScans'
|
||||
themePkg = 'keyoapp'
|
||||
baseUrl = 'https://laidbackscans.org'
|
||||
overrideVersionCode = 0
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,5 @@
|
|||
package eu.kanade.tachiyomi.extension.en.laidbackscans
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.keyoapp.Keyoapp
|
||||
|
||||
class LaidBackScans : Keyoapp("Laid Back Scans", "https://laidbackscans.org", "en")
|
|
@ -2,8 +2,8 @@ ext {
|
|||
extName = 'Magus Manga'
|
||||
extClass = '.MagusManga'
|
||||
themePkg = 'mangathemesia'
|
||||
baseUrl = 'https://vofeg.com'
|
||||
overrideVersionCode = 3
|
||||
baseUrl = 'https://neroscans.com'
|
||||
overrideVersionCode = 4
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -8,7 +8,7 @@ import java.util.concurrent.TimeUnit
|
|||
|
||||
class MagusManga : MangaThemesia(
|
||||
"Magus Manga",
|
||||
"https://vofeg.com",
|
||||
"https://neroscans.com",
|
||||
"en",
|
||||
mangaUrlDirectory = "/series",
|
||||
dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("en")),
|
||||
|
|
|
@ -2,8 +2,8 @@ ext {
|
|||
extName = 'Read Attack on Titan Shingeki no Kyojin Manga'
|
||||
extClass = '.ReadAttackOnTitanShingekiNoKyojinManga'
|
||||
themePkg = 'mangacatalog'
|
||||
baseUrl = 'https://ww8.readsnk.com'
|
||||
overrideVersionCode = 4
|
||||
baseUrl = 'https://ww9.readsnk.com'
|
||||
overrideVersionCode = 5
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.multisrc.mangacatalog.MangaCatalog
|
|||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class ReadAttackOnTitanShingekiNoKyojinManga : MangaCatalog("Read Attack on Titan Shingeki no Kyojin Manga", "https://ww8.readsnk.com", "en") {
|
||||
class ReadAttackOnTitanShingekiNoKyojinManga : MangaCatalog("Read Attack on Titan Shingeki no Kyojin Manga", "https://ww9.readsnk.com", "en") {
|
||||
override val sourceList = listOf(
|
||||
Pair("Shingeki No Kyojin", "$baseUrl/manga/shingeki-no-kyojin/"),
|
||||
Pair("Colored", "$baseUrl/manga/shingeki-no-kyojin-colored/"),
|
||||
|
@ -20,7 +20,7 @@ class ReadAttackOnTitanShingekiNoKyojinManga : MangaCatalog("Read Attack on Tita
|
|||
Pair("No Regrets Colored", "$baseUrl/manga/attack-on-titan-no-regrets-colored/"),
|
||||
).sortedBy { it.first }.distinctBy { it.second }
|
||||
|
||||
override fun chapterListSelector(): String = "div.w-full div.grid div.col-span-3"
|
||||
override fun chapterListSelector(): String = "div.w-full div.grid div.col-span-4"
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
|
||||
val urlElement = element.selectFirst("a")!!
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
ext {
|
||||
extName = 'ReadComic.Top'
|
||||
extClass = '.ReadComicTop'
|
||||
extVersionCode = 1
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 7.7 KiB |
|
@ -0,0 +1,234 @@
|
|||
package eu.kanade.tachiyomi.extension.en.readcomictop
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
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 okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class ReadComicTop : ParsedHttpSource() {
|
||||
|
||||
override val name = "ReadComic.Top"
|
||||
|
||||
override val baseUrl = "https://readcomic.top"
|
||||
|
||||
override val lang = "en"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client: OkHttpClient = network.cloudflareClient
|
||||
|
||||
override fun popularMangaSelector() = "div.eg-box"
|
||||
|
||||
override fun latestUpdatesSelector() = "ul.line-list"
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
val url = "$baseUrl/popular-comics".toHttpUrl().newBuilder().apply {
|
||||
if (page > 1) addQueryParameter("page", page.toString())
|
||||
}.build()
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request {
|
||||
val url = "$baseUrl/comic-updates".toHttpUrl().newBuilder().apply {
|
||||
if (page > 1) addQueryParameter("page", page.toString())
|
||||
}.build()
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/advanced-search".toHttpUrl().newBuilder().apply {
|
||||
addQueryParameter("key", query)
|
||||
filters.forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreFilter -> {
|
||||
addQueryParameter("wg", filter.included.joinToString("%2C"))
|
||||
addQueryParameter("wog", filter.excluded.joinToString("%2C"))
|
||||
}
|
||||
is StatusFilter -> if (filter.toUriPart().isNotBlank()) {
|
||||
addQueryParameter("status", filter.toUriPart())
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
if (page > 1) addQueryParameter("page", page.toString())
|
||||
}.build()
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
|
||||
setUrlWithoutDomain(element.select("div.egb-right > a.egb-serie").attr("href"))
|
||||
title = element.select("div.egb-right > a.egb-serie").text()
|
||||
thumbnail_url = element.select("a.eg-image > img").attr("src")
|
||||
}
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
|
||||
with(element.select("ul.line-list > li > a.big-link")) {
|
||||
setUrlWithoutDomain(attr("href"))
|
||||
title = text()
|
||||
}
|
||||
thumbnail_url = "https://fakeimg.pl/200x300/?text=No%20Cover&font_size=62"
|
||||
}
|
||||
override fun searchMangaFromElement(element: Element) = SManga.create().apply {
|
||||
with(element.select("div.dlb-right > a.dlb-title")) {
|
||||
setUrlWithoutDomain(attr("href"))
|
||||
title = text()
|
||||
}
|
||||
thumbnail_url = element.select("a.dlb-image > img").attr("src")
|
||||
}
|
||||
|
||||
override fun popularMangaNextPageSelector() = "div.general-nav > a:contains(Next)"
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun searchMangaSelector() = "div.dl-box"
|
||||
|
||||
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga {
|
||||
return SManga.create().apply {
|
||||
title = document.select("h1.title").text()
|
||||
thumbnail_url = document.select("div.anime-image > img").attr("src")
|
||||
status = parseStatus(document.select("ul.anime-genres li.status").text())
|
||||
author = document.select("td:contains(Author:) + td").text()
|
||||
description = document.select(".detail-desc-content > p").text()
|
||||
genre = document.select("ul.anime-genres > li > a[href*='genre']").joinToString { it.text() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseStatus(element: String): Int = when {
|
||||
element.contains("Completed") -> SManga.COMPLETED
|
||||
element.contains("Ongoing") -> SManga.ONGOING
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = "ul.basic-list > li"
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter {
|
||||
return SChapter.create().apply {
|
||||
with(element.select("a.ch-name")) {
|
||||
setUrlWithoutDomain(attr("href"))
|
||||
name = text()
|
||||
}
|
||||
date_upload = dateParse(element.select("span").text())
|
||||
}
|
||||
}
|
||||
|
||||
private val dateFormat by lazy { SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH) }
|
||||
|
||||
private fun dateParse(dateStr: String): Long {
|
||||
return try {
|
||||
dateFormat.parse(dateStr)!!.time
|
||||
} catch (_: Exception) {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
return GET(baseUrl + chapter.url + "/full", headers)
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
return document.select("div.chapter-container img").mapIndexed { index, img ->
|
||||
Page(index, "", img.attr("abs:src"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
|
||||
|
||||
// Filters
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
Filter.Header("Note: can't leave both filters as Any with a blank search string"),
|
||||
Filter.Separator(),
|
||||
GenreFilter(getGenreList),
|
||||
StatusFilter(getStatusList),
|
||||
)
|
||||
|
||||
private class Genre(name: String, val toUriPart: String) : Filter.TriState(name)
|
||||
private class GenreFilter(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres) {
|
||||
val included: List<String>
|
||||
get() = state.filter { it.isIncluded() }.map { it.toUriPart }
|
||||
|
||||
val excluded: List<String>
|
||||
get() = state.filter { it.isExcluded() }.map { it.toUriPart }
|
||||
}
|
||||
private class StatusFilter(statusPairs: Array<Pair<String, String>>) : UriPartFilter("Status", statusPairs)
|
||||
|
||||
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
|
||||
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
private val getStatusList = arrayOf(
|
||||
Pair("Any", ""), // You might want an option for any status
|
||||
Pair("Ongoing", "ONG"),
|
||||
Pair("Completed", "CMP"),
|
||||
)
|
||||
private val getGenreList = listOf(
|
||||
Genre("Any", ""),
|
||||
Genre("Marvel", "Marvel"),
|
||||
Genre("DC Comics", "DC%20Comics"),
|
||||
Genre("Action", "Action"),
|
||||
Genre("Adventure", "Adventure"),
|
||||
Genre("Anthology", "Anthology"),
|
||||
Genre("Anthropomorphic", "Anthropomorphic"),
|
||||
Genre("Biography", "Biography"),
|
||||
Genre("Children", "Children"),
|
||||
Genre("Comedy", "Comedy"),
|
||||
Genre("Crime", "Crime"),
|
||||
Genre("Cyborgs", "Cyborgs"),
|
||||
Genre("Dark Horse", "Dark%20Horse"),
|
||||
Genre("Demons", "Demons"),
|
||||
Genre("Drama", "Drama"),
|
||||
Genre("Fantasy", "Fantasy"),
|
||||
Genre("Family", "Family"),
|
||||
Genre("Fighting", "Fighting"),
|
||||
Genre("Gore", "Gore"),
|
||||
Genre("Graphic Novels", "Graphic%20Novels"),
|
||||
Genre("Historical", "Historical"),
|
||||
Genre("Horror", "Horror"),
|
||||
Genre("Leading Ladies", "Leading%20Ladies"),
|
||||
Genre("Literature", "Literature"),
|
||||
Genre("Magic", "Magic"),
|
||||
Genre("Manga", "Manga"),
|
||||
Genre("Martial Arts", "Martial%20Arts"),
|
||||
Genre("Mature", "Mature"),
|
||||
Genre("Mecha", "Mecha"),
|
||||
Genre("Military", "Military"),
|
||||
Genre("Movie Cinematic Link", "Movie%20Cinematic%20Link"),
|
||||
Genre("Mystery", "Mystery"),
|
||||
Genre("Mythology", "Mythology"),
|
||||
Genre("Psychological", "Psychological"),
|
||||
Genre("Personal", "Personal"),
|
||||
Genre("Political", "Political"),
|
||||
Genre("Post-Apocalyptic", "Post-Apocalyptic"),
|
||||
Genre("Pulp", "Pulp"),
|
||||
Genre("Robots", "Robots"),
|
||||
Genre("Romance", "Romance"),
|
||||
Genre("Sci-Fi", "Sci-Fi"),
|
||||
Genre("Slice of Life", "Slice%20of%20Life"),
|
||||
Genre("Science Fiction", "Science%20Fiction"),
|
||||
Genre("Sports", "Sports"),
|
||||
Genre("Spy", "Spy"),
|
||||
Genre("Superhero", "Superhero"),
|
||||
Genre("Supernatural", "Supernatural"),
|
||||
Genre("Suspense", "Suspense"),
|
||||
Genre("Thriller", "Thriller"),
|
||||
Genre("Tragedy", "Tragedy"),
|
||||
Genre("Vampires", "Vampires"),
|
||||
Genre("Vertigo", "Vertigo"),
|
||||
Genre("Video Games", "Video%20Games"),
|
||||
Genre("War", "War"),
|
||||
Genre("Western", "Western"),
|
||||
Genre("Zombies", "Zombies"),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
ext {
|
||||
extName = 'ToomicsFree.com'
|
||||
extClass = '.ToomicsFree'
|
||||
themePkg = 'hotcomics'
|
||||
overrideVersionCode = 0
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,29 @@
|
|||
package eu.kanade.tachiyomi.extension.en.toomicsfree
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.hotcomics.HotComics
|
||||
|
||||
class ToomicsFree : HotComics(
|
||||
"ToomicsFree.com",
|
||||
"en",
|
||||
"https://toomicsfree.com",
|
||||
) {
|
||||
override val browseList = listOf(
|
||||
Pair("Home", "en"),
|
||||
Pair("Weekly", "en/weekly"),
|
||||
Pair("New", "en/new"),
|
||||
Pair("Genre: All", "en/genres"),
|
||||
Pair("Genre: Sports", "en/genres/Sports"),
|
||||
Pair("Genre: Historical", "en/genres/Historical"),
|
||||
Pair("Genre: Drama", "en/genres/Drama"),
|
||||
Pair("Genre: BL", "en/genres/BL"),
|
||||
Pair("Genre: Thriller", "en/genres/Thriller"),
|
||||
Pair("Genre: School life", "en/genres/School_life"),
|
||||
Pair("Genre: Comedy", "en/genres/Comedy"),
|
||||
Pair("Genre: GL", "en/genres/GL"),
|
||||
Pair("Genre: Action", "en/genres/Action"),
|
||||
Pair("Genre: Sci-fi", "en/genres/Sci-fi"),
|
||||
Pair("Genre: Horror", "en/genres/Horror"),
|
||||
Pair("Genre: Fantasy", "en/genres/Fantasy"),
|
||||
Pair("Genre: Romance", "en/genres/Romance"),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
ext {
|
||||
extName = 'ToomicsFree.info'
|
||||
extClass = '.ToomicsFreeInfo'
|
||||
themePkg = 'hotcomics'
|
||||
overrideVersionCode = 0
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,29 @@
|
|||
package eu.kanade.tachiyomi.extension.en.toomicsfreeinfo
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.hotcomics.HotComics
|
||||
|
||||
class ToomicsFreeInfo : HotComics(
|
||||
"ToomicsFree.info",
|
||||
"en",
|
||||
"https://toomicsfree.info",
|
||||
) {
|
||||
override val browseList = listOf(
|
||||
Pair("Home", "en"),
|
||||
Pair("Weekly", "en/weekly"),
|
||||
Pair("New", "en/new"),
|
||||
Pair("Genre: All", "en/genres"),
|
||||
Pair("Genre: Sports", "en/genres/Sports"),
|
||||
Pair("Genre: Historical", "en/genres/Historical"),
|
||||
Pair("Genre: Drama", "en/genres/Drama"),
|
||||
Pair("Genre: BL", "en/genres/BL"),
|
||||
Pair("Genre: Thriller", "en/genres/Thriller"),
|
||||
Pair("Genre: School life", "en/genres/School_life"),
|
||||
Pair("Genre: Comedy", "en/genres/Comedy"),
|
||||
Pair("Genre: GL", "en/genres/GL"),
|
||||
Pair("Genre: Action", "en/genres/Action"),
|
||||
Pair("Genre: Sci-fi", "en/genres/Sci-fi"),
|
||||
Pair("Genre: Horror", "en/genres/Horror"),
|
||||
Pair("Genre: Fantasy", "en/genres/Fantasy"),
|
||||
Pair("Genre: Romance", "en/genres/Romance"),
|
||||
)
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
ext {
|
||||
extName = 'Cerberus Series'
|
||||
extClass = '.CerberusSeries'
|
||||
extVersionCode = 1
|
||||
themePkg = 'mangathemesia'
|
||||
baseUrl = 'https://cerberuseries.xyz'
|
||||
overrideVersionCode = 0
|
||||
isNsfw = false
|
||||
}
|
||||
|
||||
|
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 50 KiB |
|
@ -1,107 +1,12 @@
|
|||
package eu.kanade.tachiyomi.extension.es.cerberusseries
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
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 okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import java.io.IOException
|
||||
import java.util.Calendar
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
|
||||
class CerberusSeries : ParsedHttpSource() {
|
||||
|
||||
override val name = "Cerberus Series"
|
||||
|
||||
override val baseUrl = "https://cerberuseries.xyz"
|
||||
|
||||
override val lang = "es"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client: OkHttpClient = network.client.newBuilder()
|
||||
.rateLimitHost(baseUrl.toHttpUrl(), 2)
|
||||
.build()
|
||||
|
||||
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
||||
.add("Referer", baseUrl)
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/comics?page=$page", headers)
|
||||
|
||||
override fun popularMangaSelector(): String = "div.grid > div:has(> div.c-iZMlIN)"
|
||||
|
||||
override fun popularMangaNextPageSelector(): String = "nav[role=navigation] a:contains(»), nav[role=navigation] a:contains(Next)"
|
||||
|
||||
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
|
||||
setUrlWithoutDomain(element.select("div.c-hCLgme a").attr("href"))
|
||||
title = element.select("div.c-hCLgme a").text()
|
||||
thumbnail_url = element.selectFirst("div.c-iZMlIN img")?.attr("abs:src")
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request = GET(baseUrl, headers)
|
||||
|
||||
override fun latestUpdatesSelector(): String = popularMangaSelector()
|
||||
|
||||
override fun latestUpdatesNextPageSelector(): String? = null
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = throw IOException("Esta funcionalidad aún no esta implementada.")
|
||||
|
||||
override fun searchMangaSelector(): String = throw UnsupportedOperationException()
|
||||
|
||||
override fun searchMangaNextPageSelector(): String = throw UnsupportedOperationException()
|
||||
|
||||
override fun searchMangaFromElement(element: Element): SManga = throw UnsupportedOperationException()
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
||||
thumbnail_url = document.selectFirst("div.thumb-wrapper img")!!.attr("abs:src")
|
||||
title = document.selectFirst("div.series-title")!!.text()
|
||||
genre = document.select("div.tags-container span").joinToString { it.text() }
|
||||
description = document.selectFirst("div.description-container")!!.text()
|
||||
author = document.select("div.useful-container p:containsOwn(Autor) strong").text()
|
||||
}
|
||||
|
||||
override fun chapterListSelector(): String = "div.chapters-list-wrapper ul a"
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
name = element.selectFirst("li span")!!.text()
|
||||
date_upload = parseRelativeDate(element.selectFirst("li p")!!.text())
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
return document.select("div.main-content p > img").mapIndexed { i, element ->
|
||||
Page(i, "", element.attr("abs:src"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
|
||||
|
||||
private fun parseRelativeDate(date: String): Long {
|
||||
val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0
|
||||
val cal = Calendar.getInstance()
|
||||
|
||||
return when {
|
||||
WordSet("segundo").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis
|
||||
WordSet("minuto").anyWordIn(date) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis
|
||||
WordSet("hora").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
|
||||
WordSet("día", "dia").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
|
||||
WordSet("semana").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number * 7) }.timeInMillis
|
||||
WordSet("mes").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
|
||||
WordSet("año").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
class WordSet(private vararg val words: String) {
|
||||
fun anyWordIn(dateString: String): Boolean = words.any { dateString.contains(it, ignoreCase = true) }
|
||||
}
|
||||
class CerberusSeries : MangaThemesia(
|
||||
"Cerberus Series",
|
||||
"https://cerberuseries.xyz",
|
||||
"es",
|
||||
) {
|
||||
// Moved from custom to MangaThemesia
|
||||
override val versionId = 2
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
ext {
|
||||
extName = 'De Todo Un Poco Scan'
|
||||
extClass = '.DtupScan'
|
||||
themePkg = 'mangathemesia'
|
||||
baseUrl = 'https://dtupscan.com'
|
||||
overrideVersionCode = 0
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 31 KiB |
|
@ -0,0 +1,18 @@
|
|||
package eu.kanade.tachiyomi.extension.es.dtupscan
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class DtupScan : MangaThemesia(
|
||||
"De Todo Un Poco Scan",
|
||||
"https://dtupscan.com",
|
||||
"es",
|
||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
||||
) {
|
||||
override val client = super.client.newBuilder()
|
||||
.rateLimitHost(baseUrl.toHttpUrl(), 3, 1)
|
||||
.build()
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
ext {
|
||||
extName = 'Foy Scan'
|
||||
extClass = '.FoyScan'
|
||||
themePkg = 'mangaesp'
|
||||
baseUrl = 'https://foyscan.xyz'
|
||||
overrideVersionCode = 0
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 18 KiB |
|
@ -0,0 +1,5 @@
|
|||
package eu.kanade.tachiyomi.extension.es.foyscan
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangaesp.MangaEsp
|
||||
|
||||
class FoyScan : MangaEsp("Foy Scan", "https://foyscan.xyz", "es")
|
|
@ -0,0 +1,10 @@
|
|||
ext {
|
||||
extName = 'Gistamis House'
|
||||
extClass = '.GistamisHouse'
|
||||
themePkg = 'zeistmanga'
|
||||
baseUrl = 'https://gistamishousefansub.blogspot.com'
|
||||
overrideVersionCode = 0
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 39 KiB |
|
@ -0,0 +1,154 @@
|
|||
package eu.kanade.tachiyomi.extension.es.gistamishouse
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.zeistmanga.Genre
|
||||
import eu.kanade.tachiyomi.multisrc.zeistmanga.Status
|
||||
import eu.kanade.tachiyomi.multisrc.zeistmanga.Type
|
||||
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
|
||||
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistMangaDto
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import okhttp3.Response
|
||||
|
||||
class GistamisHouse : ZeistManga(
|
||||
"Gistamis House",
|
||||
"https://gistamishousefansub.blogspot.com",
|
||||
"es",
|
||||
) {
|
||||
override val useNewChapterFeed = true
|
||||
override val hasFilters = true
|
||||
override val hasLanguageFilter = false
|
||||
|
||||
override val client = super.client.newBuilder()
|
||||
.rateLimit(2)
|
||||
.build()
|
||||
|
||||
override val excludedCategories = listOf("Anime", "Novela")
|
||||
|
||||
override val popularMangaSelector = "div.PopularPosts div.grid > figure:not(:has(span[data=Capitulo]))"
|
||||
|
||||
override val authorSelectorList = listOf(
|
||||
"Author",
|
||||
"Autor",
|
||||
"Mangaka",
|
||||
)
|
||||
|
||||
override val mangaDetailsSelectorAltName = "div.y6x11p:contains(Otros Nombres) > span.dt"
|
||||
override val mangaDetailsSelectorInfoTitle = ""
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga {
|
||||
val document = response.asJsoup()
|
||||
val profileManga = document.selectFirst(mangaDetailsSelector)!!
|
||||
return SManga.create().apply {
|
||||
thumbnail_url = profileManga.selectFirst("img")!!.attr("abs:src")
|
||||
description = buildString {
|
||||
append(profileManga.select(mangaDetailsSelectorDescription).text())
|
||||
append("\n\n")
|
||||
profileManga.selectFirst(mangaDetailsSelectorAltName)?.text()?.takeIf { it.isNotBlank() }?.let {
|
||||
append("Otros Nombres: ")
|
||||
append(it)
|
||||
}
|
||||
}.trim()
|
||||
genre = profileManga.select(mangaDetailsSelectorGenres)
|
||||
.joinToString { it.text() }
|
||||
|
||||
val infoElement = profileManga.select(mangaDetailsSelectorInfo)
|
||||
var statusFound = false
|
||||
infoElement.forEach { element ->
|
||||
val infoText = element.ownText().trim().ifEmpty { element.selectFirst(mangaDetailsSelectorInfoTitle)?.text()?.trim() ?: "" }
|
||||
val descText = element.select(mangaDetailsSelectorInfoDescription).text().trim()
|
||||
when {
|
||||
statusSelectorList.any { infoText.contains(it) } -> {
|
||||
if (!statusFound) status = parseStatus(descText)
|
||||
statusFound = true
|
||||
}
|
||||
|
||||
authorSelectorList.any { infoText.contains(it) } -> {
|
||||
author = descText
|
||||
}
|
||||
|
||||
artisSelectorList.any { infoText.contains(it) } -> {
|
||||
artist = descText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val chapterCategory = ""
|
||||
private val chapterCategories = listOf("Capitulo", "Cap")
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
|
||||
val url = getChapterFeedUrl(document)
|
||||
val res = client.newCall(GET(url, headers)).execute()
|
||||
|
||||
val result = json.decodeFromString<ZeistMangaDto>(res.body.string())
|
||||
return result.feed?.entry?.filter { it.category.orEmpty().any { category -> chapterCategories.contains(category.term) } }
|
||||
?.map { it.toSChapter(baseUrl) }
|
||||
?: throw Exception("Failed to parse from chapter API")
|
||||
}
|
||||
|
||||
override val pageListSelector = "article.oh div.post p"
|
||||
|
||||
override fun getGenreList(): List<Genre> = listOf(
|
||||
Genre("Acción", "Acción"),
|
||||
Genre("Aventura", "Aventura"),
|
||||
Genre("Comedia", "Comedia"),
|
||||
Genre("Dementia", "Dementia"),
|
||||
Genre("Demonios", "Demonios"),
|
||||
Genre("Drama", "Drama"),
|
||||
Genre("Ecchi", "Ecchi"),
|
||||
Genre("Fantasía", "Fantasía"),
|
||||
Genre("Videojuegos", "Videojuegos"),
|
||||
Genre("Harem", "Harem"),
|
||||
Genre("Histórico", "Histórico"),
|
||||
Genre("Horror", "Horror"),
|
||||
Genre("Josei", "Josei"),
|
||||
Genre("Magia", "Magia"),
|
||||
Genre("Arte marcial", "Arte marcial"),
|
||||
Genre("Mecha", "Mecha"),
|
||||
Genre("Militar", "Militar"),
|
||||
Genre("Música", "Música"),
|
||||
Genre("Misterio", "Misterio"),
|
||||
Genre("Parody", "Parody"),
|
||||
Genre("Policia", "Policia"),
|
||||
Genre("Filosófico", "Filosófico"),
|
||||
Genre("Romance", "Romance"),
|
||||
Genre("Samurai", "Samurai"),
|
||||
Genre("Escolar", "Escolar"),
|
||||
Genre("Sci-Fi", "Sci-Fi"),
|
||||
Genre("Seinen", "Seinen"),
|
||||
Genre("Shoujo", "Shoujo"),
|
||||
Genre("GL", "GL"),
|
||||
Genre("BL", "BL"),
|
||||
Genre("HET", "HET"),
|
||||
Genre("Shounen", "Shounen"),
|
||||
Genre("Vida cotidiana", "Vida cotidiana"),
|
||||
Genre("Espacio", "Espacio"),
|
||||
Genre("Deportes", "Deportes"),
|
||||
Genre("Super poderes", "Super poderes"),
|
||||
Genre("Sobrenatural", "Sobrenatural"),
|
||||
Genre("Thriller", "Thriller"),
|
||||
Genre("Vampiro", "Vampiro"),
|
||||
Genre("Vida laboral", "Vida laboral"),
|
||||
)
|
||||
|
||||
override fun getStatusList(): List<Status> = listOf(
|
||||
Status("Activo", "Activo"),
|
||||
Status("Completo", "Completo"),
|
||||
Status("Cancelado", "Cancelado"),
|
||||
Status("Futuro", "Futuro"),
|
||||
Status("Pausado", "Pausado"),
|
||||
)
|
||||
|
||||
override fun getTypeList(): List<Type> = listOf(
|
||||
Type("Manga", "Manga"),
|
||||
Type("Manhua", "Manhua"),
|
||||
Type("Manhwa", "Manhwa"),
|
||||
)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Ikigai Mangas'
|
||||
extClass = '.IkigaiMangas'
|
||||
extVersionCode = 3
|
||||
extVersionCode = 4
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|