Compare commits
35 Commits
bbb2922515
...
9b7a772823
Author | SHA1 | Date |
---|---|---|
Knightblood | 9b7a772823 | |
mohamedotaku | 98b02fa9eb | |
sinkableShip | cffcadaf92 | |
mohamedotaku | 3e0a38da57 | |
sinkableShip | 239a90665d | |
Vetle Ledaal | 84fd56dcc1 | |
bapeey | 88783a5221 | |
Secozzi | dc3135aede | |
Secozzi | da85b27a94 | |
bapeey | 86cacd6f59 | |
Chopper | c47be31508 | |
Chopper | 9713ac2bcf | |
Vetle Ledaal | 7ec1b79d3c | |
Vetle Ledaal | 54e9a630e9 | |
Vetle Ledaal | 6e3c338d82 | |
Vetle Ledaal | 1cbecae3f7 | |
Vetle Ledaal | c1185fdd78 | |
bapeey | 7fd04cd006 | |
bapeey | b5a49071b7 | |
devil6venom | ebcb2cd055 | |
bapeey | d3a34b5693 | |
Chopper | ecb0606bb4 | |
Chopper | 0bc65ac48a | |
Chaos Pjeles | c7b5b45670 | |
bapeey | e465778aec | |
Chopper | eb8350da2d | |
mohamedotaku | f68c0a5c5d | |
bapeey | a6c35d3175 | |
renovate[bot] | 3ea687e183 | |
mohamedotaku | dc56cd1b81 | |
KirinRaikage | 653b4af574 | |
Chopper | 848168f3ae | |
bapeey | adbcbd33ef | |
Secozzi | 4030cebbbf | |
mohamedotaku | 0e20357684 |
|
@ -45,7 +45,7 @@ jobs:
|
||||||
echo ${{ secrets.SIGNING_KEY }} | base64 -d > signingkey.jks
|
echo ${{ secrets.SIGNING_KEY }} | base64 -d > signingkey.jks
|
||||||
|
|
||||||
- name: Set up Gradle
|
- name: Set up Gradle
|
||||||
uses: gradle/actions/setup-gradle@6cec5d49d4d6d4bb982fbed7047db31ea6d38f11 # v3.3.0
|
uses: gradle/actions/setup-gradle@750cdda3edd6d51b7fdfc069d2e2818cf3c44f4c # v3.3.1
|
||||||
|
|
||||||
- name: Build extensions
|
- name: Build extensions
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -2,4 +2,4 @@ plugins {
|
||||||
id("lib-multisrc")
|
id("lib-multisrc")
|
||||||
}
|
}
|
||||||
|
|
||||||
baseVersionCode = 1
|
baseVersionCode = 3
|
||||||
|
|
|
@ -322,9 +322,17 @@ abstract class Liliana(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
return document.select("div.separator").mapIndexed { i, page ->
|
return if (document.selectFirst("div.separator[data-index]") == null) {
|
||||||
val url = page.selectFirst("a")!!.attr("abs:href")
|
document.select("div.separator").mapIndexed { i, page ->
|
||||||
Page(i, document.location(), url)
|
val url = page.selectFirst("a")!!.attr("abs:href")
|
||||||
|
Page(i, document.location(), url)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.select("div.separator[data-index]").map { page ->
|
||||||
|
val index = page.attr("data-index").toInt()
|
||||||
|
val url = page.selectFirst("a")!!.attr("abs:href")
|
||||||
|
Page(index, document.location(), url)
|
||||||
|
}.sortedBy { it.index }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,6 +342,7 @@ abstract class Liliana(
|
||||||
val imgHeaders = headersBuilder().apply {
|
val imgHeaders = headersBuilder().apply {
|
||||||
add("Accept", "image/avif,image/webp,*/*")
|
add("Accept", "image/avif,image/webp,*/*")
|
||||||
add("Host", page.imageUrl!!.toHttpUrl().host)
|
add("Host", page.imageUrl!!.toHttpUrl().host)
|
||||||
|
removeAll("Referer")
|
||||||
}.build()
|
}.build()
|
||||||
return GET(page.imageUrl!!, imgHeaders)
|
return GET(page.imageUrl!!, imgHeaders)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@ open class MCCMSConfig(
|
||||||
hasCategoryPage: Boolean = true,
|
hasCategoryPage: Boolean = true,
|
||||||
val textSearchOnlyPageOne: Boolean = false,
|
val textSearchOnlyPageOne: Boolean = false,
|
||||||
val useMobilePageList: Boolean = false,
|
val useMobilePageList: Boolean = false,
|
||||||
private val lazyLoadImageAttr: String = "data-original",
|
protected val lazyLoadImageAttr: String = "data-original",
|
||||||
) {
|
) {
|
||||||
val genreData = GenreData(hasCategoryPage)
|
val genreData = GenreData(hasCategoryPage)
|
||||||
|
|
||||||
fun pageListParse(response: Response): List<Page> {
|
open fun pageListParse(response: Response): List<Page> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
|
|
||||||
return if (useMobilePageList) {
|
return if (useMobilePageList) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'CosplayTele'
|
extName = 'CosplayTele'
|
||||||
extClass = '.CosplayTele'
|
extClass = '.CosplayTele'
|
||||||
extVersionCode = 1
|
extVersionCode = 2
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ class CosplayTele : ParsedHttpSource() {
|
||||||
|
|
||||||
override fun chapterFromElement(element: Element): SChapter {
|
override fun chapterFromElement(element: Element): SChapter {
|
||||||
val chapter = SChapter.create()
|
val chapter = SChapter.create()
|
||||||
chapter.setUrlWithoutDomain(element.select("link[rel=\"canonical\"]").attr("href"))
|
chapter.setUrlWithoutDomain(element.select("[rel=\"bookmark\"]").attr("href"))
|
||||||
chapter.name = "Gallery"
|
chapter.name = "Gallery"
|
||||||
chapter.date_upload = getDate(element.select("time.updated").attr("datetime"))
|
chapter.date_upload = getDate(element.select("time.updated").attr("datetime"))
|
||||||
return chapter
|
return chapter
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'NineManga'
|
extName = 'NineManga'
|
||||||
extClass = '.NineMangaFactory'
|
extClass = '.NineMangaFactory'
|
||||||
extVersionCode = 19
|
extVersionCode = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":lib:cookieinterceptor"))
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package eu.kanade.tachiyomi.extension.all.ninemanga
|
package eu.kanade.tachiyomi.extension.all.ninemanga
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.lib.cookieinterceptor.CookieInterceptor
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
@ -26,6 +27,8 @@ open class NineManga(
|
||||||
|
|
||||||
override val supportsLatest: Boolean = true
|
override val supportsLatest: Boolean = true
|
||||||
|
|
||||||
|
private val cookieInterceptor = CookieInterceptor(baseUrl.substringAfter("://"), "ninemanga_list_num" to "1")
|
||||||
|
|
||||||
override val client: OkHttpClient = network.client.newBuilder()
|
override val client: OkHttpClient = network.client.newBuilder()
|
||||||
.addInterceptor { chain ->
|
.addInterceptor { chain ->
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
|
@ -37,7 +40,9 @@ open class NineManga(
|
||||||
return@addInterceptor chain.proceed(newRequest)
|
return@addInterceptor chain.proceed(newRequest)
|
||||||
}
|
}
|
||||||
chain.proceed(request)
|
chain.proceed(request)
|
||||||
}.build()
|
}
|
||||||
|
.addNetworkInterceptor(cookieInterceptor)
|
||||||
|
.build()
|
||||||
|
|
||||||
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
||||||
.add("Accept-Language", "es-ES,es;q=0.9,en;q=0.8,gl;q=0.7")
|
.add("Accept-Language", "es-ES,es;q=0.9,en;q=0.8,gl;q=0.7")
|
||||||
|
@ -97,7 +102,6 @@ open class NineManga(
|
||||||
element.select("a.chapter_list_a").let {
|
element.select("a.chapter_list_a").let {
|
||||||
name = it.text().replace(mangaTitleForCleaning, "", true)
|
name = it.text().replace(mangaTitleForCleaning, "", true)
|
||||||
url = it.attr("href").substringAfter(baseUrl).replace("%20", " ")
|
url = it.attr("href").substringAfter(baseUrl).replace("%20", " ")
|
||||||
.substringBeforeLast(".html") + "-1-1.html"
|
|
||||||
}
|
}
|
||||||
date_upload = parseChapterDate(element.select("span").text())
|
date_upload = parseChapterDate(element.select("span").text())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
||||||
extClass = '.ThunderScansFactory'
|
extClass = '.ThunderScansFactory'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://thunderscans.com'
|
baseUrl = 'https://thunderscans.com'
|
||||||
overrideVersionCode = 0
|
overrideVersionCode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.kanade.tachiyomi.extension.all.thunderscans
|
package eu.kanade.tachiyomi.extension.all.thunderscans
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesiaAlt
|
||||||
import eu.kanade.tachiyomi.source.SourceFactory
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
@ -12,7 +13,7 @@ class ThunderScansFactory : SourceFactory {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ThunderScansAR : MangaThemesia(
|
class ThunderScansAR : MangaThemesiaAlt(
|
||||||
"Thunder Scans",
|
"Thunder Scans",
|
||||||
"https://thunderscans.com",
|
"https://thunderscans.com",
|
||||||
"ar",
|
"ar",
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
<data android:host="https://unionmangas.xyz" />
|
<data android:host="unionmangasbr.org" />
|
||||||
|
|
||||||
<data android:scheme="https"/>
|
<data android:scheme="https"/>
|
||||||
<data android:pathPattern="/manga-br/..*"/>
|
<data android:pathPattern="/manga-br/..*"/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Union Mangas'
|
extName = 'Union Mangas'
|
||||||
extClass = '.UnionMangasFactory'
|
extClass = '.UnionMangasFactory'
|
||||||
extVersionCode = 1
|
extVersionCode = 2
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ class UnionMangas(private val langOption: LanguageOption) : HttpSource() {
|
||||||
|
|
||||||
override val name: String = "Union Mangas"
|
override val name: String = "Union Mangas"
|
||||||
|
|
||||||
override val baseUrl: String = "https://unionmangas.xyz"
|
override val baseUrl: String = "https://unionmangasbr.org"
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
ext {
|
||||||
|
extName = 'Crow Scans'
|
||||||
|
extClass = '.CrowScans'
|
||||||
|
themePkg = 'mangathemesia'
|
||||||
|
baseUrl = 'https://crowscans.com'
|
||||||
|
overrideVersionCode = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 21 KiB |
|
@ -0,0 +1,12 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.ar.crowscans
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class CrowScans : MangaThemesia(
|
||||||
|
"Crow Scans",
|
||||||
|
"https://crowscans.com",
|
||||||
|
"ar",
|
||||||
|
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
||||||
|
)
|
|
@ -2,8 +2,8 @@ ext {
|
||||||
extName = 'MangaNoon'
|
extName = 'MangaNoon'
|
||||||
extClass = '.MangaNoon'
|
extClass = '.MangaNoon'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://manjanoon.net'
|
baseUrl = 'https://manjanoon.org'
|
||||||
overrideVersionCode = 1
|
overrideVersionCode = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import java.util.Locale
|
||||||
|
|
||||||
class MangaNoon : MangaThemesia(
|
class MangaNoon : MangaThemesia(
|
||||||
"مانجا نون",
|
"مانجا نون",
|
||||||
"https://manjanoon.net",
|
"https://manjanoon.org",
|
||||||
"ar",
|
"ar",
|
||||||
dateFormat = SimpleDateFormat("MMM d, yyy", Locale("ar")),
|
dateFormat = SimpleDateFormat("MMM d, yyy", Locale("ar")),
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,8 +2,8 @@ ext {
|
||||||
extName = 'Manga Pro'
|
extName = 'Manga Pro'
|
||||||
extClass = '.MangaPro'
|
extClass = '.MangaPro'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://mangapro.pro'
|
baseUrl = 'https://mangapro.club'
|
||||||
overrideVersionCode = 0
|
overrideVersionCode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import java.util.Locale
|
||||||
|
|
||||||
class MangaPro : MangaThemesia(
|
class MangaPro : MangaThemesia(
|
||||||
"Manga Pro",
|
"Manga Pro",
|
||||||
"https://mangapro.pro",
|
"https://mangapro.club",
|
||||||
"ar",
|
"ar",
|
||||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -2,8 +2,8 @@ ext {
|
||||||
extName = 'MangaSwat'
|
extName = 'MangaSwat'
|
||||||
extClass = '.MangaSwat'
|
extClass = '.MangaSwat'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://swatmanhua.com'
|
baseUrl = 'https://normoyun.com'
|
||||||
overrideVersionCode = 17
|
overrideVersionCode = 18
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -22,7 +22,7 @@ import uy.kohesive.injekt.api.get
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
private const val swatUrl = "https://swatmanhua.com"
|
private const val swatUrl = "https://normoyun.com"
|
||||||
|
|
||||||
class MangaSwat :
|
class MangaSwat :
|
||||||
MangaThemesia(
|
MangaThemesia(
|
||||||
|
|
|
@ -1,142 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.comickiba
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
|
||||||
|
|
||||||
object Note : Filter.Header("NOTE: Ignored if using text search!")
|
|
||||||
|
|
||||||
sealed class Select(
|
|
||||||
name: String,
|
|
||||||
val param: String,
|
|
||||||
values: Array<String>,
|
|
||||||
) : Filter.Select<String>(name, values) {
|
|
||||||
open val selection: String
|
|
||||||
get() = if (state == 0) "" else state.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
class StatusFilter(
|
|
||||||
values: Array<String> = statuses.keys.toTypedArray(),
|
|
||||||
) : Select("Status", "status", values) {
|
|
||||||
override val selection: String
|
|
||||||
get() = statuses[values[state]]!!
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val statuses = mapOf(
|
|
||||||
"All" to "",
|
|
||||||
"Completed" to "completed",
|
|
||||||
"OnGoing" to "on-going",
|
|
||||||
"On-Hold" to "on-hold",
|
|
||||||
"Canceled" to "canceled",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SortFilter(
|
|
||||||
values: Array<String> = orders.keys.toTypedArray(),
|
|
||||||
) : Select("Sort", "sort", values) {
|
|
||||||
override val selection: String
|
|
||||||
get() = orders[values[state]]!!
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val orders = mapOf(
|
|
||||||
"Default" to "default",
|
|
||||||
"Latest Updated" to "latest-updated",
|
|
||||||
"Most Viewed" to "views",
|
|
||||||
"Most Viewed Month" to "views_month",
|
|
||||||
"Most Viewed Week" to "views_week",
|
|
||||||
"Most Viewed Day" to "views_day",
|
|
||||||
"Score" to "score",
|
|
||||||
"Name A-Z" to "az",
|
|
||||||
"Name Z-A" to "za",
|
|
||||||
"The highest chapter count" to "chapters",
|
|
||||||
"Newest" to "new",
|
|
||||||
"Oldest" to "old",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Genre(name: String, val id: String) : Filter.CheckBox(name)
|
|
||||||
|
|
||||||
class GenresFilter(
|
|
||||||
values: List<Genre> = genres,
|
|
||||||
) : Filter.Group<Genre>("Genres", values) {
|
|
||||||
val param = "genres"
|
|
||||||
|
|
||||||
val selection: String
|
|
||||||
get() = state.filter { it.state }.joinToString(",") { it.id }
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val genres: List<Genre>
|
|
||||||
get() = listOf(
|
|
||||||
Genre("Action", "37"),
|
|
||||||
Genre("Adaptation", "19"),
|
|
||||||
Genre("Adult", "5310"),
|
|
||||||
Genre("Adventure", "38"),
|
|
||||||
Genre("Aliens", "5436"),
|
|
||||||
Genre("Animals", "1552"),
|
|
||||||
Genre("Award Winning", "39"),
|
|
||||||
Genre("Comedy", "202"),
|
|
||||||
Genre("Comic", "287"),
|
|
||||||
Genre("Cooking", "277"),
|
|
||||||
Genre("Crime", "2723"),
|
|
||||||
Genre("Delinquents", "4438"),
|
|
||||||
Genre("Demons", "379"),
|
|
||||||
Genre("Drama", "3"),
|
|
||||||
Genre("Ecchi", "17"),
|
|
||||||
Genre("Fantasy", "197"),
|
|
||||||
Genre("Full Color", "13"),
|
|
||||||
Genre("Gender Bender", "221"),
|
|
||||||
Genre("Genderswap", "2290"),
|
|
||||||
Genre("Ghosts", "2866"),
|
|
||||||
Genre("Gore", "42"),
|
|
||||||
Genre("Harem", "222"),
|
|
||||||
Genre("Historical", "4"),
|
|
||||||
Genre("Horror", "5"),
|
|
||||||
Genre("Isekai", "259"),
|
|
||||||
Genre("Josei", "292"),
|
|
||||||
Genre("Loli", "5449"),
|
|
||||||
Genre("Long Strip", "7"),
|
|
||||||
Genre("Magic", "272"),
|
|
||||||
Genre("Manhwa", "266"),
|
|
||||||
Genre("Martial Arts", "40"),
|
|
||||||
Genre("Mature", "5311"),
|
|
||||||
Genre("Mecha", "2830"),
|
|
||||||
Genre("Medical", "1598"),
|
|
||||||
Genre("Military", "43"),
|
|
||||||
Genre("Monster Girls", "2307"),
|
|
||||||
Genre("Monsters", "298"),
|
|
||||||
Genre("Music", "3182"),
|
|
||||||
Genre("Mystery", "6"),
|
|
||||||
Genre("Office Workers", "14"),
|
|
||||||
Genre("Official Colored", "1046"),
|
|
||||||
Genre("Philosophical", "2776"),
|
|
||||||
Genre("Post-Apocalyptic", "1059"),
|
|
||||||
Genre("Psychological", "493"),
|
|
||||||
Genre("Reincarnation", "204"),
|
|
||||||
Genre("Reverse", "280"),
|
|
||||||
Genre("Reverse Harem", "199"),
|
|
||||||
Genre("Romance", "186"),
|
|
||||||
Genre("School Life", "601"),
|
|
||||||
Genre("Sci-Fi", "1845"),
|
|
||||||
Genre("Sexual Violence", "731"),
|
|
||||||
Genre("Shoujo", "254"),
|
|
||||||
Genre("Slice of Life", "10"),
|
|
||||||
Genre("Sports", "4066"),
|
|
||||||
Genre("Superhero", "481"),
|
|
||||||
Genre("Supernatural", "198"),
|
|
||||||
Genre("Survival", "44"),
|
|
||||||
Genre("Thriller", "1058"),
|
|
||||||
Genre("Time Travel", "299"),
|
|
||||||
Genre("Tragedy", "41"),
|
|
||||||
Genre("Video Games", "1846"),
|
|
||||||
Genre("Villainess", "278"),
|
|
||||||
Genre("Virtual Reality", "1847"),
|
|
||||||
Genre("Web Comic", "12"),
|
|
||||||
Genre("Webtoon", "279"),
|
|
||||||
Genre("Webtoons", "267"),
|
|
||||||
Genre("Wuxia", "203"),
|
|
||||||
Genre("Yaoi", "18"),
|
|
||||||
Genre("Yuri", "11"),
|
|
||||||
Genre("Zombies", "1060"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ ext {
|
||||||
extClass = '.ElarcPage'
|
extClass = '.ElarcPage'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://elarctoons.com'
|
baseUrl = 'https://elarctoons.com'
|
||||||
overrideVersionCode = 6
|
overrideVersionCode = 7
|
||||||
isNsfw = false
|
isNsfw = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.en.elarcpage
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
@ -63,12 +64,10 @@ class ElarcPage : MangaThemesia(
|
||||||
request.url.toString(),
|
request.url.toString(),
|
||||||
)
|
)
|
||||||
|
|
||||||
document.select("#menu-item-14 > a, a:contains(All Series), #main-menu a, .mm a")
|
document.selectFirst(".serieslist > ul > li a.series")
|
||||||
.reversed()
|
|
||||||
.map { it.attr("href") }
|
|
||||||
.lastOrNull { it.length >= 2 && it[0] == '/' }
|
|
||||||
?.let {
|
?.let {
|
||||||
setMangaUrlDirectory(it)
|
val mangaUrlDirectory = it.attr("abs:href").toHttpUrl().pathSegments.first()
|
||||||
|
setMangaUrlDirectory("/$mangaUrlDirectory")
|
||||||
dynamicUrlUpdated = timeNow
|
dynamicUrlUpdated = timeNow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Manga Demon'
|
extName = 'Manga Demon'
|
||||||
extClass = '.MangaDemon'
|
extClass = '.MangaDemon'
|
||||||
extVersionCode = 10
|
extVersionCode = 11
|
||||||
isNsfw = false
|
isNsfw = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ class MangaDemon : ParsedHttpSource() {
|
||||||
override val lang = "en"
|
override val lang = "en"
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
override val name = "Manga Demon"
|
override val name = "Manga Demon"
|
||||||
override val baseUrl = "https://demonreader.org"
|
override val baseUrl = "https://comicdemons.com"
|
||||||
|
|
||||||
override val client = network.cloudflareClient.newBuilder()
|
override val client = network.cloudflareClient.newBuilder()
|
||||||
.rateLimit(1)
|
.rateLimit(1)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Manga Sect'
|
extName = 'Manga Sect'
|
||||||
extClass = '.MangaSect'
|
extClass = '.MangaSect'
|
||||||
extVersionCode = 1
|
themePkg = 'liliana'
|
||||||
|
baseUrl = 'https://mangasect.net'
|
||||||
|
overrideVersionCode = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -1,238 +1,15 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.mangasect
|
package eu.kanade.tachiyomi.extension.en.mangasect
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.multisrc.liliana.Liliana
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
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.ParsedHttpSource
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import org.jsoup.Jsoup
|
|
||||||
import org.jsoup.nodes.Document
|
|
||||||
import org.jsoup.nodes.Element
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
|
|
||||||
class MangaSect : ParsedHttpSource() {
|
class MangaSect : Liliana(
|
||||||
|
"Manga Sect",
|
||||||
override val name = "Manga Sect"
|
"https://mangasect.net",
|
||||||
|
"en",
|
||||||
override val baseUrl = "https://mangasect.com"
|
usesPostSearch = true,
|
||||||
|
) {
|
||||||
override val lang = "en"
|
override val client = super.client.newBuilder()
|
||||||
|
|
||||||
override val supportsLatest = true
|
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
|
||||||
|
|
||||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
|
||||||
.rateLimit(1)
|
.rateLimit(1)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun headersBuilder() = super.headersBuilder()
|
|
||||||
.add("Referer", "$baseUrl/")
|
|
||||||
|
|
||||||
// Popular
|
|
||||||
|
|
||||||
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/ranking/week/$page", headers)
|
|
||||||
|
|
||||||
override fun popularMangaSelector(): String = "div#main div.grid > div"
|
|
||||||
|
|
||||||
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
|
|
||||||
thumbnail_url = element.selectFirst("img")?.imgAttr()
|
|
||||||
element.selectFirst(".text-center a")!!.run {
|
|
||||||
title = text().trim()
|
|
||||||
setUrlWithoutDomain(attr("href"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun popularMangaNextPageSelector(): String = ".blog-pager > span.pagecurrent + span"
|
|
||||||
|
|
||||||
// Latest
|
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request =
|
|
||||||
GET("$baseUrl/all-manga/$page/?sort=1", headers)
|
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
|
|
||||||
|
|
||||||
override fun latestUpdatesSelector(): String =
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
override fun latestUpdatesFromElement(element: Element): SManga =
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
override fun latestUpdatesNextPageSelector(): String =
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
// Search
|
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
|
||||||
val url = baseUrl.toHttpUrl().newBuilder().apply {
|
|
||||||
if (query.isNotBlank()) {
|
|
||||||
addPathSegment("search")
|
|
||||||
addQueryParameter("keyword", query)
|
|
||||||
} else {
|
|
||||||
addPathSegment("filter")
|
|
||||||
filters.forEach { filter ->
|
|
||||||
when (filter) {
|
|
||||||
is GenreFilter -> {
|
|
||||||
if (filter.checked.isNotEmpty()) {
|
|
||||||
addQueryParameter("genres", filter.checked.joinToString(","))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is StatusFilter -> {
|
|
||||||
if (filter.selected.isNotBlank()) {
|
|
||||||
addQueryParameter("status", filter.selected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is SortFilter -> {
|
|
||||||
addQueryParameter("sort", filter.selected)
|
|
||||||
}
|
|
||||||
is ChapterCountFilter -> {
|
|
||||||
addQueryParameter("chapter_count", filter.selected)
|
|
||||||
}
|
|
||||||
is GenderFilter -> {
|
|
||||||
addQueryParameter("sex", filter.selected)
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addPathSegment(page.toString())
|
|
||||||
addPathSegment("")
|
|
||||||
}
|
|
||||||
|
|
||||||
return GET(url.build(), headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response)
|
|
||||||
|
|
||||||
override fun searchMangaSelector(): String =
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
override fun searchMangaFromElement(element: Element): SManga =
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
override fun searchMangaNextPageSelector(): String =
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
|
|
||||||
override fun getFilterList(): FilterList = FilterList(
|
|
||||||
Filter.Header("Ignored when using text search"),
|
|
||||||
Filter.Separator(),
|
|
||||||
GenreFilter(),
|
|
||||||
ChapterCountFilter(),
|
|
||||||
GenderFilter(),
|
|
||||||
StatusFilter(),
|
|
||||||
SortFilter(),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Details
|
|
||||||
|
|
||||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
|
||||||
description = document.selectFirst("div#syn-target")?.text()
|
|
||||||
thumbnail_url = document.selectFirst(".a1 > figure img")?.imgAttr()
|
|
||||||
title = document.selectFirst(".a2 header h1")?.text()?.trim() ?: "N/A"
|
|
||||||
genre = document.select(".a2 div > a[rel='tag'].label").joinToString(", ") { it.text() }
|
|
||||||
|
|
||||||
document.selectFirst(".a1 > aside")?.run {
|
|
||||||
author = select("div:contains(Authors) > span a")
|
|
||||||
.joinToString(", ") { it.text().trim() }
|
|
||||||
.takeUnless { it.isBlank() || it.equals("Updating", true) }
|
|
||||||
status = selectFirst("div:contains(Status) > span")?.text().let(::parseStatus)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseStatus(status: String?): Int = when {
|
|
||||||
status.equals("ongoing", true) -> SManga.ONGOING
|
|
||||||
status.equals("completed", true) -> SManga.COMPLETED
|
|
||||||
status.equals("on-hold", true) -> SManga.ON_HIATUS
|
|
||||||
status.equals("canceled", true) -> SManga.CANCELLED
|
|
||||||
else -> SManga.UNKNOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chapters
|
|
||||||
|
|
||||||
override fun chapterListSelector() = "ul > li.chapter"
|
|
||||||
|
|
||||||
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
|
|
||||||
element.selectFirst("time[datetime]")?.also {
|
|
||||||
date_upload = it.attr("datetime").toLongOrNull()?.let { it * 1000L } ?: 0L
|
|
||||||
}
|
|
||||||
element.selectFirst("a")!!.run {
|
|
||||||
text().trim().also {
|
|
||||||
name = it
|
|
||||||
chapter_number = it.substringAfter("hapter ").toFloatOrNull() ?: 0F
|
|
||||||
}
|
|
||||||
setUrlWithoutDomain(attr("href"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pages
|
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter): Request {
|
|
||||||
val pageHeaders = headersBuilder().apply {
|
|
||||||
add("Accept", "application/json, text/javascript, */*; q=0.01")
|
|
||||||
add("Host", baseUrl.toHttpUrl().host)
|
|
||||||
add("Referer", baseUrl + chapter.url)
|
|
||||||
add("X-Requested-With", "XMLHttpRequest")
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
val id = chapter.url.split("/").last()
|
|
||||||
return GET("$baseUrl/ajax/image/list/chap/$id", pageHeaders)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class PageListResponseDto(val html: String)
|
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
|
||||||
val data = response.parseAs<PageListResponseDto>().html
|
|
||||||
return pageListParse(
|
|
||||||
Jsoup.parseBodyFragment(
|
|
||||||
data,
|
|
||||||
response.request.header("Referer")!!,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
|
||||||
return document.select("div.separator").map { page ->
|
|
||||||
val index = page.attr("data-index").toInt()
|
|
||||||
val url = page.selectFirst("a")!!.attr("abs:href")
|
|
||||||
Page(index, document.location(), url)
|
|
||||||
}.sortedBy { it.index }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun imageUrlParse(document: Document) = ""
|
|
||||||
|
|
||||||
override fun imageRequest(page: Page): Request {
|
|
||||||
val imgHeaders = headersBuilder().apply {
|
|
||||||
add("Accept", "image/avif,image/webp,*/*")
|
|
||||||
add("Host", page.imageUrl!!.toHttpUrl().host)
|
|
||||||
}.build()
|
|
||||||
return GET(page.imageUrl!!, imgHeaders)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utilities
|
|
||||||
|
|
||||||
// From mangathemesia
|
|
||||||
private fun Element.imgAttr(): String = when {
|
|
||||||
hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
|
|
||||||
hasAttr("data-src") -> attr("abs:data-src")
|
|
||||||
else -> attr("abs:src")
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <reified T> Response.parseAs(): T {
|
|
||||||
return json.decodeFromString(body.string())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,167 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.mangasect
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
class CheckBoxFilter(
|
|
||||||
name: String,
|
|
||||||
val value: String,
|
|
||||||
) : Filter.CheckBox(name)
|
|
||||||
|
|
||||||
class ChapterCountFilter : SelectFilter("Chapter count", chapterCount) {
|
|
||||||
companion object {
|
|
||||||
private val chapterCount = listOf(
|
|
||||||
Pair(">= 0", "0"),
|
|
||||||
Pair(">= 10", "10"),
|
|
||||||
Pair(">= 30", "30"),
|
|
||||||
Pair(">= 50", "50"),
|
|
||||||
Pair(">= 100", "100"),
|
|
||||||
Pair(">= 200", "200"),
|
|
||||||
Pair(">= 300", "300"),
|
|
||||||
Pair(">= 400", "400"),
|
|
||||||
Pair(">= 500", "500"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GenderFilter : SelectFilter("Manga Gender", gender) {
|
|
||||||
companion object {
|
|
||||||
private val gender = listOf(
|
|
||||||
Pair("All", "All"),
|
|
||||||
Pair("Boy", "Boy"),
|
|
||||||
Pair("Girl", "Girl"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StatusFilter : SelectFilter("Status", status) {
|
|
||||||
companion object {
|
|
||||||
private val status = listOf(
|
|
||||||
Pair("All", ""),
|
|
||||||
Pair("Completed", "completed"),
|
|
||||||
Pair("OnGoing", "on-going"),
|
|
||||||
Pair("On-Hold", "on-hold"),
|
|
||||||
Pair("Canceled", "canceled"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SortFilter : SelectFilter("Sort", sort) {
|
|
||||||
companion object {
|
|
||||||
private val sort = listOf(
|
|
||||||
Pair("Default", "default"),
|
|
||||||
Pair("Latest Updated", "latest-updated"),
|
|
||||||
Pair("Most Viewed", "most-viewd"),
|
|
||||||
Pair("Score", "score"),
|
|
||||||
Pair("Name A-Z", "az"),
|
|
||||||
Pair("Name Z-A", "za"),
|
|
||||||
Pair("Newest", "new"),
|
|
||||||
Pair("Oldest", "old"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GenreFilter : Filter.Group<CheckBoxFilter>(
|
|
||||||
"Genre",
|
|
||||||
genres.map { CheckBoxFilter(it.first, it.second) },
|
|
||||||
) {
|
|
||||||
val checked get() = state.filter { it.state }.map { it.value }
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val genres = listOf(
|
|
||||||
Pair("Action", "29"),
|
|
||||||
Pair("Adaptation", "66"),
|
|
||||||
Pair("Adult", "108"),
|
|
||||||
Pair("Adventure", "33"),
|
|
||||||
Pair("Aliens", "2326"),
|
|
||||||
Pair("Animals", "199"),
|
|
||||||
Pair("Comedy", "35"),
|
|
||||||
Pair("Comic", "109"),
|
|
||||||
Pair("Cooking", "26"),
|
|
||||||
Pair("Crime", "274"),
|
|
||||||
Pair("Delinquents", "234"),
|
|
||||||
Pair("Demons", "136"),
|
|
||||||
Pair("Drama", "39"),
|
|
||||||
Pair("Dungeons", "204"),
|
|
||||||
Pair("Ecchi", "54"),
|
|
||||||
Pair("Fantasy", "30"),
|
|
||||||
Pair("Full Color", "27"),
|
|
||||||
Pair("Genderswap", "1441"),
|
|
||||||
Pair("Genius MC", "209"),
|
|
||||||
Pair("Ghosts", "1527"),
|
|
||||||
Pair("Gore", "1678"),
|
|
||||||
Pair("Harem", "43"),
|
|
||||||
Pair("Historical", "49"),
|
|
||||||
Pair("Horror", "69"),
|
|
||||||
Pair("Incest", "1189"),
|
|
||||||
Pair("Isekai", "40"),
|
|
||||||
Pair("Loli", "198"),
|
|
||||||
Pair("Long Strip", "233"),
|
|
||||||
Pair("Magic", "212"),
|
|
||||||
Pair("Magical Girls", "1676"),
|
|
||||||
Pair("Manhua", "58"),
|
|
||||||
Pair("Manhwa", "80"),
|
|
||||||
Pair("Martial Arts", "32"),
|
|
||||||
Pair("Mature", "34"),
|
|
||||||
Pair("Mecha", "70"),
|
|
||||||
Pair("Medical", "2113"),
|
|
||||||
Pair("Military", "1531"),
|
|
||||||
Pair("Monster", "218"),
|
|
||||||
Pair("Monster Girls", "201"),
|
|
||||||
Pair("Monsters", "63"),
|
|
||||||
Pair("Murim", "208"),
|
|
||||||
Pair("Music", "412"),
|
|
||||||
Pair("Mystery", "31"),
|
|
||||||
Pair("One shot", "155"),
|
|
||||||
Pair("Overpowered", "206"),
|
|
||||||
Pair("Police", "275"),
|
|
||||||
Pair("Post-Apocalyptic", "197"),
|
|
||||||
Pair("Psychological", "36"),
|
|
||||||
Pair("Rebirth", "1435"),
|
|
||||||
Pair("Recarnation", "67"),
|
|
||||||
Pair("Regression", "205"),
|
|
||||||
Pair("Reincarnation", "64"),
|
|
||||||
Pair("Return", "1454"),
|
|
||||||
Pair("Returner", "211"),
|
|
||||||
Pair("Revenge", "219"),
|
|
||||||
Pair("Romance", "37"),
|
|
||||||
Pair("School Life", "44"),
|
|
||||||
Pair("Sci fi", "42"),
|
|
||||||
Pair("Sci-fi", "216"),
|
|
||||||
Pair("Seinen", "52"),
|
|
||||||
Pair("Sexual Violence", "2325"),
|
|
||||||
Pair("Shota", "2327"),
|
|
||||||
Pair("Shoujo", "92"),
|
|
||||||
Pair("Shounen", "38"),
|
|
||||||
Pair("Shounen ai", "103"),
|
|
||||||
Pair("Slice of Life", "68"),
|
|
||||||
Pair("Super power", "213"),
|
|
||||||
Pair("Superhero", "1630"),
|
|
||||||
Pair("Supernatural", "41"),
|
|
||||||
Pair("Survival", "463"),
|
|
||||||
Pair("System", "203"),
|
|
||||||
Pair("Thriller", "462"),
|
|
||||||
Pair("Time travel", "65"),
|
|
||||||
Pair("tower", "207"),
|
|
||||||
Pair("Tragedy", "51"),
|
|
||||||
Pair("Transmigration", "217"),
|
|
||||||
Pair("Uncategorized", "55"),
|
|
||||||
Pair("Vampires", "200"),
|
|
||||||
Pair("Video Games", "1606"),
|
|
||||||
Pair("Virtual Reality", "757"),
|
|
||||||
Pair("Web comic", "98"),
|
|
||||||
Pair("Webtoons", "77"),
|
|
||||||
Pair("Wuxia", "202"),
|
|
||||||
Pair("Zombies", "464"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
ext {
|
|
||||||
extName = 'MangaStic'
|
|
||||||
extClass = '.MangaStic'
|
|
||||||
themePkg = 'madara'
|
|
||||||
baseUrl = 'https://mangastic9.com'
|
|
||||||
overrideVersionCode = 3
|
|
||||||
isNsfw = true
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 18 KiB |
|
@ -1,5 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.mangastic
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
|
||||||
|
|
||||||
class MangaStic : Madara("MangaStic", "https://mangastic9.com", "en")
|
|
|
@ -2,8 +2,8 @@ ext {
|
||||||
extName = 'Manga Weebs'
|
extName = 'Manga Weebs'
|
||||||
extClass = '.MangaWeebs'
|
extClass = '.MangaWeebs'
|
||||||
themePkg = 'madara'
|
themePkg = 'madara'
|
||||||
baseUrl = 'https://mangaweebs.in'
|
baseUrl = 'https://mangaweebs.org'
|
||||||
overrideVersionCode = 8
|
overrideVersionCode = 9
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -7,7 +7,7 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class MangaWeebs : Madara("Manga Weebs", "https://mangaweebs.in", "en", dateFormat = SimpleDateFormat("dd MMMM HH:mm", Locale.US)) {
|
class MangaWeebs : Madara("Manga Weebs", "https://mangaweebs.org", "en", dateFormat = SimpleDateFormat("dd MMMM HH:mm", Locale.US)) {
|
||||||
|
|
||||||
override val client: OkHttpClient = super.client.newBuilder()
|
override val client: OkHttpClient = super.client.newBuilder()
|
||||||
.rateLimit(20, 4, TimeUnit.SECONDS)
|
.rateLimit(20, 4, TimeUnit.SECONDS)
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
ext {
|
|
||||||
extName = 'Phantom Scans'
|
|
||||||
extClass = '.PhantomScans'
|
|
||||||
themePkg = 'mangathemesia'
|
|
||||||
baseUrl = 'https://phantomscans.com'
|
|
||||||
overrideVersionCode = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 24 KiB |
|
@ -1,5 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.phantomscans
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
|
||||||
|
|
||||||
class PhantomScans : MangaThemesia("Phantom Scans", "https://phantomscans.com", "en")
|
|
|
@ -1,9 +0,0 @@
|
||||||
ext {
|
|
||||||
extName = 'TeenManhua'
|
|
||||||
extClass = '.TeenManhua'
|
|
||||||
themePkg = 'madara'
|
|
||||||
baseUrl = 'https://teenmanhua.com'
|
|
||||||
overrideVersionCode = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 22 KiB |
|
@ -1,14 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.teenmanhua
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class TeenManhua : Madara(
|
|
||||||
"TeenManhua",
|
|
||||||
"https://teenmanhua.com",
|
|
||||||
"en",
|
|
||||||
dateFormat = SimpleDateFormat("dd/MM/yy", Locale.US),
|
|
||||||
) {
|
|
||||||
override val filterNonMangaItems = false
|
|
||||||
}
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
ext {
|
||||||
|
extName = 'Edens Fairy'
|
||||||
|
extClass = '.Eflee'
|
||||||
|
themePkg = 'zeistmanga'
|
||||||
|
baseUrl = 'https://www.eflee.co'
|
||||||
|
overrideVersionCode = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,79 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.es.eflee
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.zeistmanga.Genre
|
||||||
|
import eu.kanade.tachiyomi.multisrc.zeistmanga.GenreList
|
||||||
|
import eu.kanade.tachiyomi.multisrc.zeistmanga.Type
|
||||||
|
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
|
||||||
|
class Eflee : ZeistManga(
|
||||||
|
"Edens Fairy",
|
||||||
|
"https://www.eflee.co",
|
||||||
|
"es",
|
||||||
|
) {
|
||||||
|
override val popularMangaSelector = "#PopularPosts3 article"
|
||||||
|
override val popularMangaSelectorTitle = ".post-title a"
|
||||||
|
override val popularMangaSelectorUrl = popularMangaSelectorTitle
|
||||||
|
|
||||||
|
override val useNewChapterFeed = true
|
||||||
|
override val chapterCategory = "Cap"
|
||||||
|
|
||||||
|
override val hasFilters = true
|
||||||
|
override val hasLanguageFilter = false
|
||||||
|
override val hasGenreFilter = false
|
||||||
|
override val hasStatusFilter = false
|
||||||
|
|
||||||
|
private var genresList: List<Genre> = emptyList()
|
||||||
|
private var fetchGenresAttempts: Int = 0
|
||||||
|
|
||||||
|
override fun getFilterList(): FilterList {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch { fetchGenres() }
|
||||||
|
val filters = super.getFilterList().list.toMutableList()
|
||||||
|
if (genresList.isNotEmpty()) {
|
||||||
|
filters += GenreList(
|
||||||
|
title = "Generos",
|
||||||
|
genres = genresList,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
filters += listOf(
|
||||||
|
Filter.Separator(),
|
||||||
|
Filter.Header("Presione 'Restablecer' para intentar mostrar los géneros"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return FilterList(filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTypeList(): List<Type> = listOf(
|
||||||
|
Type("Todos", ""),
|
||||||
|
Type("Manga", "Manga"),
|
||||||
|
Type("Manhua", "Manhua"),
|
||||||
|
Type("Manhwa", "Manhwa"),
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun fetchGenres() {
|
||||||
|
if (fetchGenresAttempts < 3 && genresList.isEmpty()) {
|
||||||
|
try {
|
||||||
|
genresList = client.newCall(GET(baseUrl, headers)).execute()
|
||||||
|
.use { parseGenres(it.asJsoup()) }
|
||||||
|
.sortedBy { it.value }
|
||||||
|
} catch (_: Exception) {
|
||||||
|
} finally {
|
||||||
|
fetchGenresAttempts++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseGenres(document: Document): List<Genre> {
|
||||||
|
return document.select(".filters .filter:first-child input:not(.hidden)")
|
||||||
|
.map { element ->
|
||||||
|
Genre(element.attr("id"), element.attr("value"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Ikigai Mangas'
|
extName = 'Ikigai Mangas'
|
||||||
extClass = '.IkigaiMangas'
|
extClass = '.IkigaiMangas'
|
||||||
extVersionCode = 4
|
extVersionCode = 7
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":lib:cookieinterceptor"))
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package eu.kanade.tachiyomi.extension.es.ikigaimangas
|
package eu.kanade.tachiyomi.extension.es.ikigaimangas
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.lib.cookieinterceptor.CookieInterceptor
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
@ -9,6 +10,7 @@ import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
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.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
@ -30,9 +32,12 @@ class IkigaiMangas : HttpSource() {
|
||||||
|
|
||||||
override val supportsLatest: Boolean = true
|
override val supportsLatest: Boolean = true
|
||||||
|
|
||||||
|
private val cookieInterceptor = CookieInterceptor(baseUrl.substringAfter("://"), "data-saving" to "0")
|
||||||
|
|
||||||
override val client = network.cloudflareClient.newBuilder()
|
override val client = network.cloudflareClient.newBuilder()
|
||||||
.rateLimitHost(baseUrl.toHttpUrl(), 1, 2)
|
.rateLimitHost(baseUrl.toHttpUrl(), 1, 2)
|
||||||
.rateLimitHost(apiBaseUrl.toHttpUrl(), 2, 1)
|
.rateLimitHost(apiBaseUrl.toHttpUrl(), 2, 1)
|
||||||
|
.addNetworkInterceptor(cookieInterceptor)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun headersBuilder() = super.headersBuilder()
|
override fun headersBuilder() = super.headersBuilder()
|
||||||
|
@ -102,7 +107,7 @@ class IkigaiMangas : HttpSource() {
|
||||||
return MangasPage(mangaList, result.hasNextPage())
|
return MangasPage(mangaList, result.hasNextPage())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url.substringBefore("#")
|
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url.substringBefore("#").replace("/series/comic-", "/series/")
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
val slug = manga.url
|
val slug = manga.url
|
||||||
|
@ -141,14 +146,13 @@ class IkigaiMangas : HttpSource() {
|
||||||
return mangas
|
return mangas
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter): Request {
|
override fun pageListRequest(chapter: SChapter): Request =
|
||||||
val id = chapter.url.substringAfter("/capitulo/")
|
GET(baseUrl + chapter.url.substringBefore("#"), headers)
|
||||||
return GET("$apiBaseUrl/api/swf/chapters/$id", headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
return json.decodeFromString<PayloadPagesDto>(response.body.string()).chapter.pages.mapIndexed { i, img ->
|
val document = response.asJsoup()
|
||||||
Page(i, "", img)
|
return document.select("section > div.img > img").mapIndexed { i, element ->
|
||||||
|
Page(i, imageUrl = element.attr("abs:src"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,16 +109,6 @@ class ChapterMetaDto(
|
||||||
fun hasNextPage() = currentPage < lastPage
|
fun hasNextPage() = currentPage < lastPage
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class PayloadPagesDto(
|
|
||||||
val chapter: PageDto,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class PageDto(
|
|
||||||
val pages: List<String>,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SeriesStatusDto(
|
class SeriesStatusDto(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
ext {
|
|
||||||
extName = 'KenhuaScan'
|
|
||||||
extClass = '.KenhuaScan'
|
|
||||||
themePkg = 'madara'
|
|
||||||
baseUrl = 'https://kenhuav2scan.com'
|
|
||||||
overrideVersionCode = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 31 KiB |
|
@ -1,22 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.es.kenhuascan
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class KenhuaScan : Madara(
|
|
||||||
"KenhuaScan",
|
|
||||||
"https://kenhuav2scan.com",
|
|
||||||
"es",
|
|
||||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
|
||||||
) {
|
|
||||||
override val client: OkHttpClient = super.client.newBuilder()
|
|
||||||
.rateLimit(2, 1, TimeUnit.SECONDS)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override val useNewChapterEndpoint = true
|
|
||||||
override val useLoadMoreRequest = LoadMoreStrategy.Always
|
|
||||||
}
|
|
|
@ -1,9 +1,10 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'MangaShiina'
|
extName = 'Manga Mukai'
|
||||||
extClass = '.MangaShiina'
|
extClass = '.MangaMukai'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://mangashiina.com'
|
baseUrl = 'https://mangamukai.com'
|
||||||
overrideVersionCode = 0
|
overrideVersionCode = 1
|
||||||
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 42 KiB |
|
@ -4,9 +4,11 @@ import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class MangaShiina : MangaThemesia(
|
class MangaMukai : MangaThemesia(
|
||||||
"MangaShiina",
|
"Manga Mukai",
|
||||||
"https://mangashiina.com",
|
"https://mangamukai.com",
|
||||||
"es",
|
"es",
|
||||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
||||||
)
|
) {
|
||||||
|
override val id: Long = 711368877221654433
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Olympus Scanlation'
|
extName = 'Olympus Scanlation'
|
||||||
extClass = '.OlympusScanlation'
|
extClass = '.OlympusScanlation'
|
||||||
extVersionCode = 7
|
extVersionCode = 8
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -24,8 +24,8 @@ class OlympusScanlation : HttpSource() {
|
||||||
|
|
||||||
override val versionId = 2
|
override val versionId = 2
|
||||||
|
|
||||||
override val baseUrl: String = "https://visorolym.com"
|
override val baseUrl: String = "https://leelolympus.com"
|
||||||
private val apiBaseUrl: String = "https://dashboard.visorolym.com"
|
private val apiBaseUrl: String = "https://dashboard.leelolympus.com"
|
||||||
|
|
||||||
override val lang: String = "es"
|
override val lang: String = "es"
|
||||||
override val name: String = "Olympus Scanlation"
|
override val name: String = "Olympus Scanlation"
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Plot Twist No Fansub'
|
extName = 'Plot Twist No Fansub'
|
||||||
extClass = '.PlotTwistNoFansub'
|
extClass = '.PlotTwistNoFansub'
|
||||||
extVersionCode = 3
|
extVersionCode = 4
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(':lib:randomua'))
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
package eu.kanade.tachiyomi.extension.es.plottwistnofansub
|
package eu.kanade.tachiyomi.extension.es.plottwistnofansub
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen
|
||||||
|
import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA
|
||||||
|
import eu.kanade.tachiyomi.lib.randomua.getPrefUAType
|
||||||
|
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||||
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
|
@ -24,9 +32,11 @@ import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import org.jsoup.nodes.Entities
|
import org.jsoup.nodes.Entities
|
||||||
import org.jsoup.select.Elements
|
import org.jsoup.select.Elements
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class PlotTwistNoFansub : ParsedHttpSource() {
|
class PlotTwistNoFansub : ParsedHttpSource(), ConfigurableSource {
|
||||||
|
|
||||||
override val name = "Plot Twist No Fansub"
|
override val name = "Plot Twist No Fansub"
|
||||||
|
|
||||||
|
@ -38,10 +48,21 @@ class PlotTwistNoFansub : ParsedHttpSource() {
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
override val client: OkHttpClient = network.client.newBuilder()
|
private val preferences: SharedPreferences =
|
||||||
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x000)
|
||||||
|
|
||||||
|
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||||
|
.setRandomUserAgent(
|
||||||
|
preferences.getPrefUAType(),
|
||||||
|
preferences.getPrefCustomUA(),
|
||||||
|
)
|
||||||
.rateLimitHost(baseUrl.toHttpUrl(), 1)
|
.rateLimitHost(baseUrl.toHttpUrl(), 1)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
addRandomUAPreferenceToScreen(screen)
|
||||||
|
}
|
||||||
|
|
||||||
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
||||||
.add("Referer", "$baseUrl/")
|
.add("Referer", "$baseUrl/")
|
||||||
|
|
||||||
|
@ -117,7 +138,7 @@ class PlotTwistNoFansub : ParsedHttpSource() {
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val mangaId = document.selectFirst("div.td-ss-main-content > article[id^=post-]")!!.id().substringAfter("-")
|
val mangaId = document.selectFirst(".chapters-container .row.itemlist p[data-mangaid]")!!.attr("data-mangaid")
|
||||||
|
|
||||||
val key = getKey(document)
|
val key = getKey(document)
|
||||||
val url = "$baseUrl/wp-admin/admin-ajax.php"
|
val url = "$baseUrl/wp-admin/admin-ajax.php"
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
ext {
|
||||||
|
extName = 'SapphireScan'
|
||||||
|
extClass = '.SapphireScan'
|
||||||
|
themePkg = 'madara'
|
||||||
|
baseUrl = 'https://sapphirescan.com'
|
||||||
|
overrideVersionCode = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 8.0 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 22 KiB |
|
@ -0,0 +1,45 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.es.sapphirescan
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||||
|
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||||
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class SapphireScan : Madara(
|
||||||
|
"SapphireScan",
|
||||||
|
"https://sapphirescan.com",
|
||||||
|
"es",
|
||||||
|
SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
||||||
|
) {
|
||||||
|
override val client = super.client.newBuilder()
|
||||||
|
.rateLimitHost(baseUrl.toHttpUrl(), 3)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
override val useNewChapterEndpoint = true
|
||||||
|
override val useLoadMoreRequest = LoadMoreStrategy.Always
|
||||||
|
|
||||||
|
override fun chapterFromElement(element: Element): SChapter {
|
||||||
|
return super.chapterFromElement(element).apply {
|
||||||
|
if (element.select("span.required-login").isNotEmpty()) {
|
||||||
|
name = "🔒 $name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
|
val pageList = super.pageListParse(document)
|
||||||
|
|
||||||
|
if (
|
||||||
|
pageList.isEmpty() &&
|
||||||
|
document.select(".content-blocked, .login-required").isNotEmpty()
|
||||||
|
) {
|
||||||
|
throw Exception("Inicie sesión en WebView para ver este capítulo")
|
||||||
|
}
|
||||||
|
return pageList
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
ext {
|
||||||
|
extName = 'Senshi Manga'
|
||||||
|
extClass = '.SenshiManga'
|
||||||
|
extVersionCode = 1
|
||||||
|
isNsfw = false
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 16 KiB |
|
@ -0,0 +1,145 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.es.senshimanga
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||||
|
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 kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.Headers
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
class SenshiManga : HttpSource() {
|
||||||
|
|
||||||
|
override val name = "Senshi Manga"
|
||||||
|
|
||||||
|
override val baseUrl = "https://senshimanga.com"
|
||||||
|
|
||||||
|
override val lang = "es"
|
||||||
|
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
private val apiBaseUrl = "https://lat-manga.com"
|
||||||
|
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||||
|
.rateLimitHost(baseUrl.toHttpUrl(), 3)
|
||||||
|
.rateLimitHost(apiBaseUrl.toHttpUrl(), 3)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
|
||||||
|
.add("Referer", "$baseUrl/")
|
||||||
|
|
||||||
|
private val apiHeaders: Headers = headersBuilder()
|
||||||
|
.add("Organization-Domain", "senshimanga.com")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
override fun popularMangaRequest(page: Int): Request =
|
||||||
|
GET("$apiBaseUrl/api/manga-custom?page=$page&limit=$PAGE_LIMIT&order=popular", apiHeaders)
|
||||||
|
|
||||||
|
override fun popularMangaParse(response: Response): MangasPage = searchMangaParse(response)
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request =
|
||||||
|
GET("$apiBaseUrl/api/manga-custom?page=$page&limit=$PAGE_LIMIT&order=latest", apiHeaders)
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response): MangasPage = searchMangaParse(response)
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
val url = "$apiBaseUrl/api/manga-custom".toHttpUrl().newBuilder()
|
||||||
|
|
||||||
|
url.setQueryParameter("page", page.toString())
|
||||||
|
url.setQueryParameter("limit", PAGE_LIMIT.toString())
|
||||||
|
|
||||||
|
filters.forEach { filter ->
|
||||||
|
when (filter) {
|
||||||
|
is SortByFilter -> url.setQueryParameter("order", filter.toUriPart())
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.isNotBlank()) url.setQueryParameter("q", query)
|
||||||
|
|
||||||
|
return GET(url.build(), apiHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
|
val page = response.request.url.queryParameter("page")!!.toInt()
|
||||||
|
val result = json.decodeFromString<Data<SeriesListDataDto>>(response.body.string())
|
||||||
|
|
||||||
|
val mangas = result.data.series.map { it.toSManga() }
|
||||||
|
val hasNextPage = page < result.data.maxPage
|
||||||
|
|
||||||
|
return MangasPage(mangas, hasNextPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFilterList() = FilterList(
|
||||||
|
SortByFilter("Ordenar por", getSortList()),
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getSortList() = arrayOf(
|
||||||
|
Pair("Popularidad", "popular"),
|
||||||
|
Pair("Recientes", "latest"),
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun getMangaUrl(manga: SManga): String = "$baseUrl/manga/${manga.url}"
|
||||||
|
|
||||||
|
override fun mangaDetailsRequest(manga: SManga): Request =
|
||||||
|
GET("$apiBaseUrl/api/manga-custom/${manga.url}", apiHeaders)
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(response: Response): SManga {
|
||||||
|
val result = json.decodeFromString<Data<SeriesDto>>(response.body.string())
|
||||||
|
return result.data.toSMangaDetails()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChapterUrl(chapter: SChapter): String {
|
||||||
|
val seriesSlug = chapter.url.substringBefore("/")
|
||||||
|
val chapterSlug = chapter.url.substringAfter("/")
|
||||||
|
|
||||||
|
return "$baseUrl/manga/$seriesSlug/chapters/$chapterSlug"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListRequest(manga: SManga): Request = mangaDetailsRequest(manga)
|
||||||
|
|
||||||
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
|
val result = json.decodeFromString<Data<SeriesDto>>(response.body.string())
|
||||||
|
val seriesSlug = result.data.slug
|
||||||
|
return result.data.chapters?.map { it.toSChapter(seriesSlug) } ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListRequest(chapter: SChapter): Request {
|
||||||
|
val seriesSlug = chapter.url.substringBefore("/")
|
||||||
|
val chapterSlug = chapter.url.substringAfter("/")
|
||||||
|
|
||||||
|
return GET("$apiBaseUrl/api/manga-custom/$seriesSlug/chapter/$chapterSlug/pages", apiHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
val result = json.decodeFromString<Data<List<PageDto>>>(response.body.string())
|
||||||
|
return result.data.mapIndexed { i, page ->
|
||||||
|
Page(i, imageUrl = page.imageUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
class SortByFilter(title: String, list: Array<Pair<String, String>>) : UriPartFilter(title, list)
|
||||||
|
|
||||||
|
open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||||
|
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||||
|
fun toUriPart() = vals[state].second
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val PAGE_LIMIT = 36
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.es.senshimanga
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Data<T>(val data: T)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class SeriesListDataDto(
|
||||||
|
@SerialName("data") val series: List<SeriesDto> = emptyList(),
|
||||||
|
val maxPage: Int = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class SeriesDto(
|
||||||
|
val slug: String,
|
||||||
|
private val imageUrl: String,
|
||||||
|
private val title: String,
|
||||||
|
private val status: String? = null,
|
||||||
|
private val description: String? = null,
|
||||||
|
private val authors: List<SeriesAuthorDto>? = emptyList(),
|
||||||
|
val chapters: List<SeriesChapterDto>? = emptyList(),
|
||||||
|
) {
|
||||||
|
fun toSManga() = SManga.create().apply {
|
||||||
|
title = this@SeriesDto.title
|
||||||
|
thumbnail_url = imageUrl
|
||||||
|
url = slug
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toSMangaDetails() = toSManga().apply {
|
||||||
|
status = parseStatus(this@SeriesDto.status)
|
||||||
|
description = this@SeriesDto.description
|
||||||
|
title = this@SeriesDto.title
|
||||||
|
author = authors?.joinToString { it.name }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseStatus(status: String?) = when (status) {
|
||||||
|
"ongoing" -> SManga.ONGOING
|
||||||
|
"hiatus" -> SManga.ON_HIATUS
|
||||||
|
"finished" -> SManga.COMPLETED
|
||||||
|
else -> SManga.UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class SeriesAuthorDto(
|
||||||
|
val name: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class SeriesChapterDto(
|
||||||
|
private val title: String,
|
||||||
|
private val number: Float,
|
||||||
|
private val createdAt: String,
|
||||||
|
) {
|
||||||
|
fun toSChapter(seriesSlug: String) = SChapter.create().apply {
|
||||||
|
name = "Capítulo ${number.toString().removeSuffix(".0")} - $title"
|
||||||
|
date_upload = try {
|
||||||
|
dateFormat.parse(createdAt)?.time ?: 0L
|
||||||
|
} catch (_: ParseException) {
|
||||||
|
0L
|
||||||
|
}
|
||||||
|
url = "$seriesSlug/$number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class PageDto(
|
||||||
|
val imageUrl: String,
|
||||||
|
)
|