Compare commits

...

35 Commits

Author SHA1 Message Date
Knightblood 9b7a772823 Manga Demon: Update domain (#2549)
CI / Prepare job (push) Successful in 5s Details
CI / Build individual modules (push) Successful in 2m54s Details
CI / Publish repo (push) Successful in 1m10s Details
2024-04-26 01:48:09 +01:00
mohamedotaku 98b02fa9eb update Url MangaNoon "ar" (#2573) 2024-04-26 01:48:09 +01:00
sinkableShip cffcadaf92 Miaoshang: Fix page parsing (#2556)
* Fix page parsing

* Add default function to fall back if the website get back to normal

calling super.pageListParse(response) will cause java.lang.IllegalStateException: closed since response.asJsoup() will be called twice, so resolve to copying the code
2024-04-26 01:48:09 +01:00
mohamedotaku 3e0a38da57 thunderscans "ar" use random url (#2554) 2024-04-26 01:48:09 +01:00
sinkableShip 239a90665d SeriManga: Fix date locale (#2517)
* SeriManga: fix date locale

* bump extVersionCode

* add try-catch in date parse

* add another description selector

some mangas use the first selector, example:
https://serimangas.com/manga/kara-yonca

and some mangas use the second one, example:
https://serimangas.com/manga/chainsaw-manrenkli

* Fix status selector

* Apply suggestions from code review
2024-04-26 01:48:09 +01:00
Vetle Ledaal 84fd56dcc1 Add Atikrost (#2552) 2024-04-26 01:48:09 +01:00
bapeey 88783a5221 NineManga: Fix wrong pages (#2545)
* Fix images

* Stop changing the url
2024-04-26 01:48:09 +01:00
Secozzi dc3135aede Move mangasect to liliana theme (#2534)
* fix page request + move mangasect to liliana

* remove unused file

* Update src/en/mangasect/build.gradle
2024-04-26 01:48:09 +01:00
Secozzi da85b27a94 update extraction for mangaUrlDirectory (#2535) 2024-04-26 01:48:09 +01:00
bapeey 86cacd6f59 Olympus Scanlation: Update domain (#2558)
Update domain
2024-04-26 01:48:09 +01:00
Chopper c47be31508 Add Edens Fairy (#2530)
* Add Edens Fairy

* Add type filter

* Fix message

* Change message

* Sort genres
2024-04-26 01:48:09 +01:00
Chopper 9713ac2bcf Plot Twist No Fansub: Fix chapters loading (#2516)
* Fix loading chapters

* Change attribute

* Cleanup

* Update src/es/plottwistnofansub/build.gradle
2024-04-26 01:48:09 +01:00
Vetle Ledaal 7ec1b79d3c Add Hikari Ga Nai (#2528) 2024-04-26 01:48:09 +01:00
Vetle Ledaal 54e9a630e9 Remove MangaStic (#2526) 2024-04-26 01:48:09 +01:00
Vetle Ledaal 6e3c338d82 Remove Phantom Scans (#2525) 2024-04-26 01:48:09 +01:00
Vetle Ledaal 1cbecae3f7 Remove TeenManhua (#2524) 2024-04-26 01:48:09 +01:00
Vetle Ledaal c1185fdd78 Remove KenhuaScan (#2523) 2024-04-26 01:48:09 +01:00
bapeey 7fd04cd006 Add SapphireScan (#2519)
Add sapphire
2024-04-26 01:48:09 +01:00
bapeey b5a49071b7 IkigaiMangas: Add cookie interceptor (#2514)
* Add cookie

* Add line
2024-04-26 01:48:09 +01:00
devil6venom ebcb2cd055 Update MangaWeebs domain (#2435)
* Update MangaWeebs.kt

Domain changed of mangaweebs.

* Update build.gradle

Updated version code

* Update build.gradle

Fix.. and its working fine to me!
2024-04-26 01:48:09 +01:00
bapeey d3a34b5693 IkigaiMangas: Fix http 404 when reading a chapter (#2511)
* fix pages

* remove dto
2024-04-26 01:48:09 +01:00
Chopper ecb0606bb4 Add GalaxScanlator (#2509)
* Add GalaxScanlator

* Add isNsfw

* Setup useNewChapterFeed

* Setup filters

* Simplifies genres selector

* Fix popularManga
2024-04-26 01:48:09 +01:00
Chopper 0bc65ac48a MangaShiina: Update baseUrl and rebrand (#2495)
* Update baseUrl and rebrand

* Add isNsfw

* Fix source name

* Keeps package name
2024-04-26 01:48:09 +01:00
Chaos Pjeles c7b5b45670 Fix wrong chapter url in CosplayTele (#2501)
* fix broken chapter url parsing

* Update build.gradle

Change version code
2024-04-26 01:48:09 +01:00
bapeey e465778aec IkigaiMangas: Fix webview (#2494)
Fix webview
2024-04-26 01:48:09 +01:00
Chopper eb8350da2d Remove BurningScans (#2491) 2024-04-26 01:48:09 +01:00
mohamedotaku f68c0a5c5d Add source CrowScans "ar" (#2490) 2024-04-26 01:48:09 +01:00
bapeey a6c35d3175 Add SenshiManga (#2484)
* Add SenshiManga

* Move to individual

* Today I learned template class
2024-04-26 01:48:09 +01:00
renovate[bot] 3ea687e183 Update github-actions dependencies (#2402)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-26 01:48:04 +01:00
mohamedotaku dc56cd1b81 Update Url MangaPro "ar" (#2488) 2024-04-26 01:46:50 +01:00
KirinRaikage 653b4af574 Scan Hentai Menu -> X-Manga: Update logo and domain (#2487)
Scan Hentai Menu: Update logo and domain
2024-04-26 01:46:50 +01:00
Chopper 848168f3ae UnionMangas: Fix domain changed (#2478)
* Fix domain

* Fix AndroidManifest host
2024-04-26 01:46:50 +01:00
bapeey adbcbd33ef SlimeRead: Update api list (#2476)
update api list
2024-04-26 01:46:50 +01:00
Secozzi 4030cebbbf fix liliana page order (#2473) 2024-04-26 01:46:50 +01:00
mohamedotaku 0e20357684 Update Url MangaSwat "ar" (#2469) 2024-04-26 01:46:50 +01:00
143 changed files with 669 additions and 764 deletions

View File

@ -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:

View File

@ -2,4 +2,4 @@ plugins {
id("lib-multisrc") id("lib-multisrc")
} }
baseVersionCode = 1 baseVersionCode = 3

View File

@ -322,10 +322,18 @@ 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) {
document.select("div.separator").mapIndexed { i, page ->
val url = page.selectFirst("a")!!.attr("abs:href") val url = page.selectFirst("a")!!.attr("abs:href")
Page(i, document.location(), url) 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 }
}
} }
override fun imageUrlParse(document: Document) = "" override fun imageUrlParse(document: Document) = ""
@ -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)
} }

View File

@ -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) {

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'CosplayTele' extName = 'CosplayTele'
extClass = '.CosplayTele' extClass = '.CosplayTele'
extVersionCode = 1 extVersionCode = 2
isNsfw = true isNsfw = true
} }

View File

@ -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

View File

@ -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"))
}

View File

@ -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())
} }

View File

@ -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"

View File

@ -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",

View File

@ -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/..*"/>

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'Union Mangas' extName = 'Union Mangas'
extClass = '.UnionMangasFactory' extClass = '.UnionMangasFactory'
extVersionCode = 1 extVersionCode = 2
isNsfw = true isNsfw = true
} }

View File

@ -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

View File

@ -0,0 +1,9 @@
ext {
extName = 'Crow Scans'
extClass = '.CrowScans'
themePkg = 'mangathemesia'
baseUrl = 'https://crowscans.com'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -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")),
)

View File

@ -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"

View File

@ -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")),
) )

View File

@ -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"

View File

@ -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")),
) { ) {

View File

@ -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"

View File

@ -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(

View File

@ -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"),
)
}
}

View File

@ -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
} }

View File

@ -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
} }

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'Manga Demon' extName = 'Manga Demon'
extClass = '.MangaDemon' extClass = '.MangaDemon'
extVersionCode = 10 extVersionCode = 11
isNsfw = false isNsfw = false
} }

View File

@ -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)

View File

@ -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"

View File

@ -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())
}
} }

View File

@ -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"),
)
}
}

View File

@ -1,10 +0,0 @@
ext {
extName = 'MangaStic'
extClass = '.MangaStic'
themePkg = 'madara'
baseUrl = 'https://mangastic9.com'
overrideVersionCode = 3
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -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")

View File

@ -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"

View File

@ -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)

View File

@ -1,9 +0,0 @@
ext {
extName = 'Phantom Scans'
extClass = '.PhantomScans'
themePkg = 'mangathemesia'
baseUrl = 'https://phantomscans.com'
overrideVersionCode = 1
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -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")

View File

@ -1,9 +0,0 @@
ext {
extName = 'TeenManhua'
extClass = '.TeenManhua'
themePkg = 'madara'
baseUrl = 'https://teenmanhua.com'
overrideVersionCode = 1
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -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
}

View File

@ -0,0 +1,9 @@
ext {
extName = 'Edens Fairy'
extClass = '.Eflee'
themePkg = 'zeistmanga'
baseUrl = 'https://www.eflee.co'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -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"))
}
}
}

View File

@ -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"))
}

View File

@ -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"))
} }
} }

View File

@ -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,

View File

@ -1,9 +0,0 @@
ext {
extName = 'KenhuaScan'
extClass = '.KenhuaScan'
themePkg = 'madara'
baseUrl = 'https://kenhuav2scan.com'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@ -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
}

View File

@ -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"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -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
}

View File

@ -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"

View File

@ -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"

View File

@ -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'))
}

View File

@ -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"

View File

@ -0,0 +1,9 @@
ext {
extName = 'SapphireScan'
extClass = '.SapphireScan'
themePkg = 'madara'
baseUrl = 'https://sapphirescan.com'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -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
}
}

View File

@ -0,0 +1,8 @@
ext {
extName = 'Senshi Manga'
extClass = '.SenshiManga'
extVersionCode = 1
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -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
}
}

View File

@ -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,
)

Some files were not shown because too many files have changed in this diff Show More