Compare commits

...

54 Commits

Author SHA1 Message Date
Sofie eb4c7f00be komiktap/id: added sucuriInterceptor (#454)
CI / Prepare job (push) Successful in 4s Details
CI / Build multisrc modules (push) Failing after 12s Details
CI / Build individual modules (push) Failing after 11s Details
CI / Publish repo (push) Has been skipped Details
* komiktap/id: added sucuriInterceptor

* fix

---------

Co-authored-by: ghost <ghost@gmail.com>
2024-01-22 20:38:32 +00:00
Yush0DAN 53dd6255f2 KataKomik: Update url (#478)
* clear generator

* Revert "clear generator"

This reverts commit 05be5fe5d9342486b58e7ceb4a8490f8aa516e39.

* update url

* update url
2024-01-22 20:38:30 +00:00
beerpsi 54e5dd9469 AnimaRegia: Remove from MMRCMS generator (#484) 2024-01-22 20:38:28 +00:00
Deivid Gabriel Pereira de Oliveira ee11eaa24f Daily Update Portugues (#447)
* Hipercool change url

* Fix Url HiperCool

* Arkham Scan -> HuntersScan (Fusion)

* CeriseScans Change Url

* animaregia (Down)

* SssScanlatorHentai (is closed)

* Update multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt

Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>

* Update multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt

Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>

---------

Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
2024-01-22 20:38:24 +00:00
Mike 11e66824c4 Update C&H (#473) 2024-01-22 20:38:19 +00:00
Mike 640344e5e2 Pururin - fix pages (#470)
Pururin fix pages
2024-01-22 20:38:14 +00:00
Claudemirovsky f5d9d57008 New source: tr/Tempest Fansub (#448)
* feat(multisrc/tr): New source: Tempest Fansub

* fix: Fix manga status

* chore: Add source (horrible) icon
2024-01-22 20:38:04 +00:00
AwkwardPeak7 d49c20a530 add Hentai Cosplay (#444)
* HentaiCosplay

* trim name correctly from tag

* change return statement
2024-01-22 20:38:02 +00:00
beerpsi 93df09d758 Wicked Witch Scan: Add new website (#440)
* Wicked Witch Scan: Add new website

* use pt-BR locale for parsing dates

* fix: Use the original source name in ID calculation

* move new site to individual extension

* translate some more stuff

* remove import

* use parseBodyFragment

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>

* chop down the forest

---------

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
2024-01-22 20:38:00 +00:00
Luqman 40fa020e0e Madara, WPMangaThemesia: add CloudFlare lazy-load images parsing (#441)
* Madara, WPMangaThemesia: add CloudFlare lazy-load images parsing

> The data-cfsrc is used by CloudFlare's Mirage feature to lazy-load images to improve website performance.

* fix lint
2024-01-22 20:37:56 +00:00
beerpsi f9a2e87f6b Split Asura Scans to Asura Scans (EN) and Armoni Scans (#439)
* Split Asura Scans to Asura Scans (EN) and Armoni Scans

* Uncomment other sources
2024-01-22 20:37:52 +00:00
Draff 34dbd2d59a Pururin refactor (#438) 2024-01-22 20:37:35 +00:00
beerpsi 19a1dd9133 Shinigami: Fix chapter URL selector (#437) 2024-01-22 20:36:07 +00:00
beerpsi 198c7f053f ManhuaRock: FMReader to individual ext (#433)
* ManhuaRock: FMReader to individual ext

* Add trailing newline

* fix: replace hardcoded base URL
2024-01-22 20:36:03 +00:00
beerpsi 1549a6ad52 SayHentai: Fix filtering by genre (#435) 2024-01-22 20:35:56 +00:00
beerpsi 5fa08faf08 Add SayHentai (#431) 2024-01-22 20:35:49 +00:00
beerpsi b377580e3b Surya Scans: Fix base URL (#432) 2024-01-22 20:35:48 +00:00
bapeey 15b9714c54 SlimeRead: Fix unexpected JSON token (#430)
Fix edge case
2024-01-22 20:35:45 +00:00
beerpsi e96323105e Add XXManhwa (#425) 2024-01-22 20:35:40 +00:00
AwkwardPeak7 77a1336c15 RawZ icon (#421) 2024-01-22 20:35:35 +00:00
AwkwardPeak7 b49c4c5378 add RawZ (#419)
* RawZ

* nsfw flag

* webview urls
2024-01-22 20:35:29 +00:00
Mike 846e783195 ComicFury refactor chapters and dates (#412) 2024-01-22 20:35:26 +00:00
Secozzi c3f277e342 Add mangabtt (#398)
* add mangabtt

* replace tab with spaces

* minor cleanup
2024-01-22 20:35:23 +00:00
Eshlender a14c354a5b [RU]Remanga fix lost id and page num (#410) 2024-01-22 20:28:21 +00:00
Luqman d51448d967 MGKomik: fix cloudflare, remove ratelimit, tweak ext setting (#407)
* MGKomik: fix cloudflare, remove ratelimit, tweak ext setting

- remove ratelimit
- use random ua (on by default)
- custom ua will override random ua

* fix useless lint
2024-01-22 20:27:34 +00:00
KirinRaikage 537adadea0 Manga-Scan: Fix manga name with useless words (#390)
* Manga-Scan: Fix manga name with useless words

* Add missing imports
2024-01-22 20:27:30 +00:00
Secozzi 544d28e4c4 Add 2 sources to madara (#388)
add 2 sources to madara
2024-01-22 20:27:22 +00:00
AwkwardPeak7 1f1753d047 Flix: update api (#384)
* Flix multisrc: update api

* fix search
2024-01-22 20:26:45 +00:00
KirinRaikage f45bc4cc4d Manga-Scan: Update base URL (#385) 2024-01-22 20:26:44 +00:00
Claudemirovsky 3d1006e244 New source: HikariScan (#379)
* feat: Add HikariScan

* chore: Add source icon

* chore: Add rate limit
2024-01-22 20:26:42 +00:00
bapeey bcb3c31237 PicaComic: Fix bad base64 (#382)
Maybe fix bad base64
2024-01-22 20:26:41 +00:00
Mike 3922547fa3 Junmeitu cleanup (#380) 2024-01-22 20:26:39 +00:00
AwkwardPeak7 b26afd2269 OPSCANS: rewrite for new site (#343)
* OpScans: rewrite for new site

* remove from MangaThemesiaGenerator

* chapter order and webview urls

* mangaid and chapter id to find chapter
2024-01-22 20:26:29 +00:00
Secozzi 64d59377c6 Add Gourmet scans to madara (#374)
* Add source to madara

* Commit changes

* fix capacity
2024-01-22 20:25:30 +00:00
Mike 23f8d95b1d Update pageListParse for DBZ Multiverse (#371)
Update pageListParse
2024-01-22 20:25:28 +00:00
Secozzi 29697c086a Shinigami: Fix cloudflare + pageListParse (#354)
* Fix cloudflare + pageListParse

* remove comment

* fix lint
2024-01-22 20:25:26 +00:00
Vetle Ledaal 27bed17520 Mangaku: use `by lazy` for loading assets (#372) 2024-01-22 20:25:13 +00:00
Vetle Ledaal 8a55ca5d6f Mangaku: update selectors, fix URL decryption (#360)
* Mangaku: update selectors, fix URL decryption

* Translate error message to ID

Co-authored-by: Luqman <riyanluqman@gmail.com>

---------

Co-authored-by: Luqman <riyanluqman@gmail.com>
2024-01-22 20:25:05 +00:00
bapeey b6e923ac49 MNS: Fix no chapters found (#366)
Fix no chapters found
2024-01-22 20:24:03 +00:00
Secozzi 8194dd0096 Add ezmanga to madara (#356) 2024-01-22 20:24:00 +00:00
NotBlankyu af69ff235f Add hunters scans (#355)
* Add hunters scans

* reorder imports

* reorder more imports...
2024-01-22 20:23:58 +00:00
NotBlankyu cf699e5331 Update Domain for SSSScanlator (#353)
update domain
2024-01-22 20:23:55 +00:00
bapeey f9aa8a1c93 TMO & LectorManga: Ignore ssl in the problematic CDN (#336)
Ignore ssl errors
2024-01-22 20:23:48 +00:00
bapeey 39431c31b1 Add MHScans to Madara (#333)
* Add to generator

* Add classes

* I hate generators

* Fix package name

* Fixes for MHScans

* I really hate generators

This reverts commit b85087946e26070d539d46b2b8ca1fd593ee3872.

* Ok TMOManga is a copycat, not Madara
2024-01-22 20:23:45 +00:00
bapeey 7390f88f2c Add Celestial Moon to ZeistManga (#332)
* Add Celestial Moon

* Remove sources

* Revert "Remove sources"

This reverts commit 0b14b8d1e3a5f50702d9b243033b77f0228200a2.
2024-01-22 20:23:44 +00:00
beerpsi 0d92662eec HentaiCB: Update base URL (#331) 2024-01-22 20:23:42 +00:00
Claudemirovsky 3616d62946 New source: Blackout Comics (#325)
* feat: Create Blackout Comics base

* feat: Implement popular manga page

* feat: Implement latest updates page

* feat: Implement search page

* feat: Implement manga details page

* feat: Implement chapter list page

* feat: Implement page list

* chore: Add source icon
2024-01-22 20:23:28 +00:00
NotBlankyu 9a571a9625 Update Pull Request checklist on CONTRIBUTING.md (#324)
Update CONTRIBUTING.md
2024-01-22 20:23:22 +00:00
AwkwardPeak7 2829141f0f Earlym v2 (#323) 2024-01-22 20:22:40 +00:00
NotBlankyu 602493510f Update domain for Seri Manga (#322) 2024-01-22 20:22:37 +00:00
Umair Yousif f3f816f956 MangaHub: update API URL (#318)
* Update MangaHub.kt

Updated the MangaHub API from "mghubcdn.com" to "mghcdn.com" to resolve the issue "hostname api.mghubcbdn.com not verified" as the URL had moved over to "mghcdn.com"

* Update MangaHubGenerator.kt

Updated "overrideVersionCode" as needed for all multisrc extensions

* Update MangaHubGenerator.kt

Updated "overrideVersionCode" as needed for all multisrc extensions and reverted the change made in the previous commit for "baseVersionCode".
2024-01-22 20:22:34 +00:00
Claudemirovsky 2e410da03b New source: SlimeRead (#317)
* feat: Create SlimeRead base

* feat: Implement popular manga page

* feat: Implement latest manga page

* feat: Implement search manga page

* feat: Implement manga details page

* feat: Implement chapter list

* feat: Parse page list

* fix: Revert chapter list

* chore: Apply rate-limit in the source API

* chore: Add Origin header to API requests

* chore: Add source icon

* chore: Add isNsfw flag
2024-01-22 20:22:28 +00:00
AwkwardPeak7 598baf532c [skip ci] switch to keiyoushi random UA url (#316) 2024-01-22 20:22:15 +00:00
Fermín Cirella c311614a2e Anchira - Add tag grouping, allow to get the source URL without the keys (#306) 2024-01-22 20:22:10 +00:00
244 changed files with 4737 additions and 1416 deletions

View File

@ -769,9 +769,11 @@ Please **do test your changes by compiling it through Android Studio** before su
### Pull Request checklist
- Update `extVersionCode` value in `build.gradle` for individual extensions
- Update `overrideVersionCode` or `baseVersionCode` as needed for all multisrc extensions
- Reference all related issues in the PR body (e.g. "Closes #xyz")
- Add the `isNsfw = true` flag in `build.gradle` when appropriate
- Explicitly kept the `id` if a source's name or language were changed
- Test the modifications by compiling and running the extension through Android Studio
- Updated `extVersionCode` value in `build.gradle` for individual extensions
- Updated `overrideVersionCode` or `baseVersionCode` as needed for all multisrc extensions
- Referenced all related issues in the PR body (e.g. "Closes #xyz")
- Added the `isNsfw = true` flag in `build.gradle` when appropriate
- Have not changed source names
- Have explicitly kept the `id` if a source's name or language were changed
- Have tested the modifications by compiling and running the extension through Android Studio
- Have removed `web_hi_res_512.png` when adding a new extension

View File

@ -85,7 +85,7 @@ private class RandomUserAgentInterceptor(
}
companion object {
private const val UA_DB_URL = "https://tachiyomiorg.github.io/user-agents/user-agents.json"
private const val UA_DB_URL = "https://keiyoushi.github.io/user-agents/user-agents.json"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,5 +0,0 @@
package eu.kanade.tachiyomi.extension.en.flixscans
import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans
class FlixScansNet : FlixScans("Flix Scans", "https://flixscans.org", "en", cdnUrl = "https://media.flixscans.org/")

View File

@ -1,13 +1,7 @@
package eu.kanade.tachiyomi.extension.ar.galaxymanga
import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans
import eu.kanade.tachiyomi.network.GET
import okhttp3.Request
class GalaxyManga : FlixScans("جالاكسي مانجا", "https://flixscans.com", "ar") {
override val versionId = 2
override fun popularMangaRequest(page: Int): Request {
return GET("$apiUrl/webtoon/pages/home/action", headers)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -13,7 +13,7 @@ import java.util.concurrent.TimeUnit
class CeriseScan : Madara(
"Cerise Scan",
"https://cerisescan.com",
"https://cerisescan.net",
"pt-BR",
SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")),
) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,9 @@
package eu.kanade.tachiyomi.extension.en.darkscan
import eu.kanade.tachiyomi.multisrc.madara.Madara
class DarkScan : Madara("Dark-scan", "https://dark-scan.com", "en") {
override val useNewChapterEndpoint = true
override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,19 @@
package eu.kanade.tachiyomi.extension.en.ezmanga
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import java.text.SimpleDateFormat
import java.util.Locale
class EZmanga : Madara(
"EZmanga",
"https://ezmanga.net",
"en",
dateFormat = SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH),
) {
override val useNewChapterEndpoint = true
override val client = super.client.newBuilder()
.rateLimit(1)
.build()
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -0,0 +1,112 @@
package eu.kanade.tachiyomi.extension.en.gourmetscans
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SChapter
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
class GourmetScans : Madara(
"Gourmet Scans",
"https://gourmetsupremacy.com",
"en",
) {
override val mangaSubString = "project"
override val useNewChapterEndpoint = false
// Search
override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = baseUrl.toHttpUrl().newBuilder()
val filterList = if (filters.isEmpty()) getFilterList() else filters
val yearFilter = filterList.find { it is YearFilter } as YearFilter
val orderByFilter = filterList.find { it is OrderByFilter } as OrderByFilter
val genreFilter = filterList.find { it is GenreFilter } as? GenreFilter
when {
yearFilter.state.isNotBlank() -> {
url.addPathSegment("release-year")
url.addPathSegment(yearFilter.state)
}
genreFilter?.state?.equals(0)?.not() ?: false -> {
url.addPathSegment("genre")
url.addPathSegment(genreFilter!!.toUriPart())
}
else -> {
url.addPathSegment(mangaSubString)
}
}
if (orderByFilter.toUriPart().isNotBlank()) {
url.addQueryParameter("m_orderby", orderByFilter.toUriPart())
}
url.addPathSegments(searchPage(page))
return GET(url.build(), headers)
}
override fun searchMangaSelector(): String = ".page-listing-item .page-item-detail"
override fun searchMangaNextPageSelector(): String = ".navigation-ajax > #navigation-ajax"
// Filters
override fun genresRequest(): Request = GET("$baseUrl/$mangaSubString", headers)
override fun parseGenres(document: Document): List<Genre> {
genresList = document.select("div.row.genres ul li a")
.orEmpty()
.map { li ->
Pair(
li.text(),
li.attr("href").split("/").last { it.isNotBlank() },
)
}
return emptyList()
}
private var genresList: List<Pair<String, String>> = emptyList()
class GenreFilter(val vals: List<Pair<String, String>>) :
UriPartFilter("Genre", vals.toTypedArray())
override fun getFilterList(): FilterList {
val filters = buildList(4) {
add(YearFilter(yearFilterTitle))
add(
OrderByFilter(
title = orderByFilterTitle,
options = orderByFilterOptions.zip(orderByFilterOptionsValues),
state = 0,
),
)
add(Filter.Separator())
if (genresList.isEmpty()) {
add(Filter.Header(genresMissingWarning))
} else {
add(GenreFilter(listOf(Pair("<select>", "")) + genresList))
}
}
return FilterList(filters)
}
// Chapters
override fun chapterFromElement(element: Element): SChapter {
return super.chapterFromElement(element).apply {
url = this.url.substringBefore("?style=list")
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -6,8 +6,14 @@ import org.jsoup.nodes.Document
import java.text.SimpleDateFormat
import java.util.Locale
class HentaiCB : Madara("Hentai CB", "https://hentaicube.net", "vi", SimpleDateFormat("dd/MM/yyyy", Locale("vi"))) {
class HentaiCB : Madara("CBHentai", "https://cbhentai.net", "vi", SimpleDateFormat("dd/MM/yyyy", Locale("vi"))) {
override val id: Long = 823638192569572166
override val mangaSubString = "read"
override val filterNonMangaItems = false
override fun pageListParse(document: Document): List<Page> {
return super.pageListParse(document).distinctBy { it.imageUrl }
}

View File

@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class Hipercool : Madara("HipercooL", "https://hipercool.xyz", "pt-BR") {
class Hipercool : Madara("HipercooL", "https://hiper.cool", "pt-BR") {
// Migrated from a custom CMS to Madara.
override val versionId = 2

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -0,0 +1,59 @@
package eu.kanade.tachiyomi.extension.pt.huntersscans
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class HuntersScans : Madara(
"Hunters Scan",
"https://huntersscan.xyz/",
"pt-BR",
SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")),
) {
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS)
.build()
override val useNewChapterEndpoint = true
private fun loadMoreRequest(page: Int, metaKey: String): Request {
val formBody = FormBody.Builder().apply {
add("action", "madara_load_more")
add("page", page.toString())
add("template", "madara-core/content/content-archive")
add("vars[paged]", "1")
add("vars[orderby]", "meta_value_num")
add("vars[template]", "archive")
add("vars[sidebar]", "right")
add("vars[post_type]", "wp-manga")
add("vars[post_status]", "publish")
add("vars[meta_key]", metaKey)
add("vars[order]", "desc")
add("vars[meta_query][relation]", "AND")
add("vars[manga_archives_item_layout]", "default")
}.build()
val xhrHeaders = headersBuilder()
.add("Content-Length", formBody.contentLength().toString())
.add("Content-Type", formBody.contentType().toString())
.add("X-Requested-With", "XMLHttpRequest")
.build()
return POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, formBody)
}
override fun popularMangaRequest(page: Int): Request {
return loadMoreRequest(page - 1, "_wp_manga_views")
}
override fun latestUpdatesRequest(page: Int): Request {
return loadMoreRequest(page - 1, "_latest_update")
}
}

View File

@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
@ -62,14 +61,4 @@ class LectorMangaLat : Madara(
}
override val pageListParseSelector = "div.reading-content div.page-break > img"
override fun imageFromElement(element: Element): String? {
return when {
element.hasAttr("data-cfsrc") -> element.attr("abs:data-cfsrc")
element.hasAttr("data-src") -> element.attr("abs:data-src")
element.hasAttr("data-lazy-src") -> element.attr("abs:data-lazy-src")
element.hasAttr("srcset") -> element.attr("abs:srcset").substringBefore(" ")
else -> element.attr("abs:src")
}
}
}

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.extension.en.mangareadorg
import eu.kanade.tachiyomi.multisrc.madara.Madara
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
@ -10,14 +9,4 @@ class MangaReadOrg : Madara(
"https://www.mangaread.org",
"en",
SimpleDateFormat("dd.MM.yyy", Locale.US),
) {
override fun imageFromElement(element: Element): String? {
return when {
element.hasAttr("data-cfsrc") -> element.attr("abs:data-cfsrc")
element.hasAttr("data-src") -> element.attr("abs:data-src")
element.hasAttr("data-lazy-src") -> element.attr("abs:data-lazy-src")
element.hasAttr("srcset") -> element.attr("abs:srcset").substringBefore(" ")
else -> element.attr("abs:src")
}
}
}
)

View File

@ -2,11 +2,16 @@ package eu.kanade.tachiyomi.extension.es.mangasnosekai
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.CacheControl
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
@ -72,13 +77,14 @@ class MangasNoSekai : Madara(
override fun searchMangaNextPageSelector() = "nav.navigation a.next"
override val mangaDetailsSelectorTitle = "div.summary-content h1.titleManga"
override val mangaDetailsSelectorThumbnail = "div.tab-summary img.img-responsive"
override val mangaDetailsSelectorDescription = "div.summary-content div.artist-content"
override val mangaDetailsSelectorStatus = "div.summary-content ul.general-List li:has(span:contains(Estado))"
override val mangaDetailsSelectorAuthor = "div.summary-content ul.general-List li:has(span:contains(Autor))"
override val mangaDetailsSelectorArtist = "div.summary-content ul.general-List li:has(span:contains(Dibujante))"
override val seriesTypeSelector = "div.summary-content ul.general-List li:has(span:contains(Tipo))"
override val mangaDetailsSelectorTitle = "div.thumble-container p.titleMangaSingle"
override val mangaDetailsSelectorThumbnail = "div.thumble-container img.img-responsive"
override val mangaDetailsSelectorDescription = "section#section-sinopsis > p"
override val mangaDetailsSelectorStatus = "section#section-sinopsis div.d-flex:has(div:contains(Estado)) p"
override val mangaDetailsSelectorAuthor = "section#section-sinopsis div.d-flex:has(div:contains(Autor)) p"
override val mangaDetailsSelectorGenre = "section#section-sinopsis div.d-flex:has(div:contains(Generos)) p a"
override val altNameSelector = "section#section-sinopsis div.d-flex:has(div:contains(Otros nombres)) p"
override val altName = "Otros nombres: "
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
@ -89,9 +95,6 @@ class MangasNoSekai : Madara(
selectFirst(mangaDetailsSelectorAuthor)?.ownText()?.let {
manga.author = it
}
selectFirst(mangaDetailsSelectorArtist)?.ownText()?.let {
manga.artist = it
}
select(mangaDetailsSelectorDescription).let {
manga.description = it.text()
}
@ -111,13 +114,6 @@ class MangasNoSekai : Madara(
.map { element -> element.text().lowercase(Locale.ROOT) }
.toMutableSet()
// add manga/manhwa/manhua thinggy to genre
document.select(seriesTypeSelector).firstOrNull()?.ownText()?.let {
if (it.isEmpty().not() && it.notUpdating() && it != "-" && genres.contains(it).not()) {
genres.add(it.lowercase(Locale.ROOT))
}
}
manga.genre = genres.toList().joinToString(", ") { genre ->
genre.replaceFirstChar {
if (it.isLowerCase()) {
@ -130,7 +126,6 @@ class MangasNoSekai : Madara(
}
}
// add alternative name to manga description
document.select(altNameSelector).firstOrNull()?.ownText()?.let {
if (it.isBlank().not() && it.notUpdating()) {
manga.description = when {
@ -153,4 +148,64 @@ class MangasNoSekai : Madara(
"views2",
"new-manga",
)
private fun altChapterRequest(mangaId: String, page: Int): Request {
val form = FormBody.Builder()
.add("action", "load_chapters")
.add("mangaid", mangaId)
.add("page", page.toString())
.build()
val xhrHeaders = headersBuilder()
.add("Content-Length", form.contentLength().toString())
.add("Content-Type", form.contentType().toString())
.add("X-Requested-With", "XMLHttpRequest")
.build()
return POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, form)
}
private val altChapterListSelector = "div.wp-manga-chapter"
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
val mangaUrl = document.location().removeSuffix("/")
var xhrRequest = xhrChaptersRequest(mangaUrl)
var xhrResponse = client.newCall(xhrRequest).execute()
val chapterElements = xhrResponse.asJsoup().select(chapterListSelector())
if (chapterElements.isEmpty()) {
val mangaId = document.selectFirst("div.tab-summary > script:containsData(manga_id)")?.data()
?.let { MANGA_ID_REGEX.find(it)?.groupValues?.get(1) }
?: throw Exception("No se pudo obtener el id del manga")
var page = 1
do {
xhrRequest = altChapterRequest(mangaId, page)
xhrResponse = client.newCall(xhrRequest).execute()
val xhrDocument = xhrResponse.asJsoup()
chapterElements.addAll(xhrDocument.select(altChapterListSelector))
page++
} while (xhrDocument.select(altChapterListSelector).isNotEmpty())
countViews(document)
return chapterElements.map(::altChapterFromElement)
}
countViews(document)
return chapterElements.map(::chapterFromElement)
}
private fun altChapterFromElement(element: Element) = SChapter.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href"))
name = element.select("div.text-sm").text()
date_upload = element.select("time").firstOrNull()?.text()?.let {
parseChapterDate(it)
} ?: 0
}
companion object {
val MANGA_ID_REGEX = """manga_id\s*=\s*(.*)\s*;""".toRegex()
}
}

View File

@ -1,46 +1,148 @@
package eu.kanade.tachiyomi.extension.id.mgkomik
import android.app.Application
import android.content.SharedPreferences
import android.util.Base64
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.network.GET
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
import kotlin.random.Random
class MGKomik : Madara("MG Komik", "https://mgkomik.id", "id", SimpleDateFormat("dd MMM yy", Locale.US)) {
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(20, 5, TimeUnit.SECONDS)
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
private val encodedString = "AAAAaAAA AHQAAAG 0AAAAcAAAAHM AAAA6AAA ALwAAAC8 AAAByAAAAYQAA AHcAQuAZwAA" + "AGkAAAG0AA AAaAAAAHUAAA BiAAAAdQAAAHMAA ABlyAtAAAcgAAAG MAAABvAAAAbgAA AHQAAABlAAAA bgAAAHQAAAAuA " + "AAAYwAAAG 8AAABtAAAALwAAA GsAAABlyAtAAA aQAAAHkAAABv AAAAdQAAAHMAAAD oAiBuAAAaQAAAC8 AAAB1AAAAcwA" + " AAGUAAABy AAAALQAAAGE AAABnAAA AZQAAAG 4AAAG0AAAAe gAAAC8AAABnAAAAaA AAAC0AAABwAAAA YQAAAGcAAABlAA " + " AAegAAAC8AAAB 1AAAAcwAAAGU AAAByAAAALQA AAGEAAABnA AAAZQAAAG 4AAAG0A AAAegAAAC4A AABtAAAAa QAAAG4AQ " + " uAagAAAHM AAABvAAAAbg=="
private val keiListUaUrl = Base64.decode(encodedString.replace("\\s".toRegex(), "").replace("DoAiBu", "BoA").replace("G0A", "B0A").replace("BlyAt", "BlA").replace("AQuA", "AAAAuAAAA"), Base64.DEFAULT).toString(Charsets.UTF_32).replace("z", "s")
private var secChUaMP: List<String>? = null
private var userAgent: String? = null
private var checkedUa = false
private val uaIntercept = object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val customUa = preferences.getString(PREF_KEY_CUSTOM_UA, "")
try {
if (customUa!!.isNotBlank()) userAgent = customUa
if (userAgent.isNullOrBlank() && checkedUa.not()) {
val uaResponse = chain.proceed(GET(keiListUaUrl))
if (uaResponse.isSuccessful) {
val parseTachiUa = uaResponse.use { json.decodeFromString<TachiUaResponse>(it.body.string()) }
var listUserAgentString = parseTachiUa.desktop + parseTachiUa.mobile
listUserAgentString = listUserAgentString!!.filter {
listOf("windows", "android").any { filter ->
it.contains(filter, ignoreCase = true)
}
}
userAgent = listUserAgentString!!.random()
checkedUa = true
}
uaResponse.close()
}
if (userAgent.isNullOrBlank().not()) {
secChUaMP = if (userAgent!!.contains("Windows")) {
listOf("?0", "Windows")
} else {
listOf("?1", "Android")
}
val newRequest = chain.request().newBuilder()
.header("User-Agent", userAgent!!.trim())
.header("Sec-CH-UA-Mobile", secChUaMP!![0])
.header("Sec-CH-UA-Platform", secChUaMP!![1])
.removeHeader("X-Requested-With")
.build()
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
.add("Accept-Language", "en-US,en;q=0.9,id;q=0.8")
return chain.proceed(newRequest)
}
return chain.proceed(chain.request())
} catch (e: Exception) {
throw IOException(e.message)
}
}
}
@Serializable
data class TachiUaResponse(
val desktop: List<String> = emptyList(),
val mobile: List<String> = emptyList(),
)
// disable random ua in ext setting from multisrc (.setRandomUserAgent)
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor(uaIntercept)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
override fun headersBuilder(): Headers.Builder {
val builder = super.headersBuilder()
.add("Sec-Fetch-Dest", "document")
.add("Sec-Fetch-Mode", "navigate")
.add("Sec-Fetch-Site", "same-origin")
.add("Sec-Fetch-User", "?1")
.add("Upgrade-Insecure-Requests", "1")
.add("X-Requested-With", randomString)
.add("X-Requested-With", "") // added for webview, and removed in interceptor for normal use
private fun generateRandomString(length: Int): String {
val charset = "HALOGaES.BCDFHIJKMNPQRTUVWXYZ.bcdefghijklmnopqrstuvwxyz0123456789"
return (1..length)
.map { charset.random() }
.joinToString("")
// used to flush tachi custom ua in webview and use system ua instead
if (userAgent.isNullOrBlank()) builder.removeAll("User-Agent")
return builder
}
override fun searchPage(page: Int): String = if (page > 1) "page/$page/" else ""
private val randomLength = Random.Default.nextInt(13, 21)
private val randomString = generateRandomString(randomLength)
override val mangaSubString = "komik"
override fun searchMangaNextPageSelector() = "a.page.larger"
override val chapterUrlSuffix = ""
// remove random ua in setting ext from multisrc and use custom one
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val prefCustomUserAgent = EditTextPreference(screen.context).apply {
key = PREF_KEY_CUSTOM_UA
title = TITLE_CUSTOM_UA
summary = (preferences.getString(PREF_KEY_CUSTOM_UA, "")!!.trim() + SUMMARY_STRING_CUSTOM_UA).trim()
setOnPreferenceChangeListener { _, newValue ->
val customUa = newValue as String
preferences.edit().putString(PREF_KEY_CUSTOM_UA, customUa).apply()
if (customUa.isNullOrBlank()) {
Toast.makeText(screen.context, RESTART_APP_STRING, Toast.LENGTH_LONG).show()
} else {
userAgent = null
}
summary = (customUa.trim() + SUMMARY_STRING2_CUSTOM_UA).trim()
true
}
}
screen.addPreference(prefCustomUserAgent)
}
companion object {
const val TITLE_CUSTOM_UA = "Custom User-Agent"
const val PREF_KEY_CUSTOM_UA = "pref_key_custom_ua"
const val SUMMARY_STRING_CUSTOM_UA = "\n\nBiarkan kosong untuk menggunakan User-Agent secara random" // leave empty to use random UA
const val SUMMARY_STRING2_CUSTOM_UA = "\n\nKosongkan untuk menggunakan User-Agent secara random" // make it blank/empty to use random UA
const val RESTART_APP_STRING = "Restart Aplikasi untuk menggunakan pengaturan baru." // restart app to use new settings
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -0,0 +1,60 @@
package eu.kanade.tachiyomi.extension.es.mhscans
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class MHScans : Madara(
"MHScans",
"https://mhscans.com",
"es",
dateFormat = SimpleDateFormat("dd 'de' MMMM 'de' yyyy", Locale("es")),
) {
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 3, TimeUnit.SECONDS)
.build()
override val useNewChapterEndpoint = true
override fun popularMangaNextPageSelector() = "body:not(:has(.no-posts))"
private fun loadMoreRequest(page: Int, metaKey: String): Request {
val formBody = FormBody.Builder().apply {
add("action", "madara_load_more")
add("page", page.toString())
add("template", "madara-core/content/content-archive")
add("vars[paged]", "1")
add("vars[orderby]", "meta_value_num")
add("vars[template]", "archive")
add("vars[sidebar]", "full")
add("vars[post_type]", "wp-manga")
add("vars[post_status]", "publish")
add("vars[meta_key]", metaKey)
add("vars[order]", "desc")
add("vars[meta_query][relation]", "AND")
add("vars[manga_archives_item_layout]", "big_thumbnail")
}.build()
val xhrHeaders = headersBuilder()
.add("Content-Length", formBody.contentLength().toString())
.add("Content-Type", formBody.contentType().toString())
.add("X-Requested-With", "XMLHttpRequest")
.build()
return POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, formBody)
}
override fun popularMangaRequest(page: Int): Request {
return loadMoreRequest(page - 1, "_wp_manga_views")
}
override fun latestUpdatesRequest(page: Int): Request {
return loadMoreRequest(page - 1, "_latest_update")
}
}

View File

@ -1,28 +1,17 @@
package eu.kanade.tachiyomi.extension.id.shinigami
import android.app.Application
import android.content.SharedPreferences
import android.util.Base64
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
import eu.kanade.tachiyomi.lib.synchrony.Deobfuscator
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.IOException
import java.util.concurrent.TimeUnit
class Shinigami : Madara("Shinigami", "https://shinigamitoon.com", "id") {
@ -33,97 +22,37 @@ class Shinigami : Madara("Shinigami", "https://shinigamitoon.com", "id") {
override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/"
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
override fun headersBuilder() = super.headersBuilder().apply {
add("Sec-Fetch-Dest", "document")
add("Sec-Fetch-Mode", "navigate")
add("Sec-Fetch-Site", "same-origin")
add("Upgrade-Insecure-Requests", "1")
add("X-Requested-With", "") // added for webview, and removed in interceptor for normal use
}
private val encodedString = "AAA AaAAAAH QAAAB0 AAAAcA AAAHMAA AA6AAA ALwAAAC8AA " + "AB0AAAAYQA AAGM AAADoAAAAaQAAAH kAAABvAA AAbQAAA GkAAABvAAAA cgAAAGcAAAAuAAA AZwAAAGk " + "AAAB0AAAA aAAAAHUAA ABiAAAALgAAAGkAA ABvAAAAL wAAAHUAAABzA AAAZQAAAHIAAAAtA AAAYQAAAGcA " + "AABlyAtAAAbgA AAHQAAAB6AAAA LwAAAHUAAA BcAAAAZQ AAAHIAAAAtAAA AYQAAAGcAAABl AAAAbgAA AHQAAAB6AAAALgAAAG" + " oAhAntUAABzAA AAbwAAAG4="
private val tachiUaUrl = Base64.decode(encodedString.replace("\\s".toRegex(), "").replace("DoA", "BoA").replace("GoAhAntU", "GoA").replace("BlyAt", "BlA").replace("BcA", "BzA"), Base64.DEFAULT).toString(Charsets.UTF_32).replace("z", "s")
private var secChUaMP: List<String>? = null
private var userAgent: String? = null
private var checkedUa = false
private val uaIntercept = object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val customUa = preferences.getString(PREF_KEY_CUSTOM_UA, "")
try {
if (customUa!!.isNotBlank()) userAgent = customUa
if (userAgent.isNullOrBlank() && checkedUa.not()) {
val uaResponse = chain.proceed(GET(tachiUaUrl))
if (uaResponse.isSuccessful) {
val parseTachiUa = uaResponse.use { json.decodeFromString<TachiUaResponse>(it.body.string()) }
var listUserAgentString = parseTachiUa.desktop + parseTachiUa.mobile
listUserAgentString = listUserAgentString!!.filter {
listOf("windows", "android").any { filter ->
it.contains(filter, ignoreCase = true)
}
}
userAgent = listUserAgentString!!.random()
checkedUa = true
}
uaResponse.close()
}
if (userAgent.isNullOrBlank().not()) {
secChUaMP = if (userAgent!!.contains("Windows")) {
listOf("?0", "Windows")
} else {
listOf("?1", "Android")
}
val newRequest = chain.request().newBuilder()
.header("User-Agent", userAgent!!.trim())
.header("Sec-CH-UA-Mobile", secChUaMP!![0])
.header("Sec-CH-UA-Platform", secChUaMP!![1])
.removeHeader("X-Requested-With")
.build()
return chain.proceed(newRequest)
}
return chain.proceed(chain.request())
} catch (e: Exception) {
throw IOException(e.message)
}
}
}
@Serializable
data class TachiUaResponse(
val desktop: List<String> = emptyList(),
val mobile: List<String> = emptyList(),
)
// disable random ua in ext setting from multisrc (.setRandomUserAgent)
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor(uaIntercept)
.addInterceptor { chain ->
val request = chain.request()
val headers = request.headers.newBuilder().apply {
if (request.header("X-Requested-With")?.isBlank() == true) {
removeAll("X-Requested-With")
}
}.build()
chain.proceed(request.newBuilder().headers(headers).build())
}
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(2)
.build()
override fun headersBuilder(): Headers.Builder {
val builder = super.headersBuilder()
.add("Sec-Fetch-Dest", "document")
.add("Sec-Fetch-Mode", "navigate")
.add("Sec-Fetch-Site", "same-origin")
.add("Upgrade-Insecure-Requests", "1")
.add("X-Requested-With", "") // added for webview, and removed in interceptor for normal use
// used to flush tachi custom ua in webview and use system ua instead
if (userAgent.isNullOrBlank()) builder.removeAll("User-Agent")
return builder
}
override val mangaSubString = "semua-series"
// Tags are useless as they are just SEO keywords.
override val mangaDetailsSelectorTag = ""
override val chapterUrlSelector = "a:not([href*=troll-page])"
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
val urlElement = element.selectFirst(chapterUrlSelector)!!
@ -148,13 +77,15 @@ class Shinigami : Madara("Shinigami", "https://shinigamitoon.com", "id") {
val deobfuscated = Deobfuscator.deobfuscateScript(script)
?: throw Exception("Unable to deobfuscate chapter_data script")
val postId = script.substringAfter("var post_id = '").substringBefore("'")
val chapterData = json.decodeFromString<CDT>(
script.substringAfter("var chapter_data = '").substringBefore("'"),
)
val keyMatch = KEY_REGEX.find(deobfuscated)?.groupValues
?: throw Exception("Unable to find key")
val keyMatch = KEY_REGEX.find(deobfuscated)!!.groupValues
val key = postId + keyMatch[1] + postId + keyMatch[2] + postId
val chapterData = json.decodeFromString<CDT>(
CHAPTER_DATA_REGEX.find(script)?.groupValues?.get(1) ?: throw Exception("Unable to get chapter data"),
)
val postId = POST_ID_REGEX.find(script)?.groupValues?.get(1) ?: throw Exception("Unable to get post_id")
val otherId = OTHER_ID_REGEX.findAll(script).firstOrNull { it.groupValues[1] != "post" }?.groupValues?.get(2) ?: throw Exception("Unable to get other id")
val key = otherId + keyMatch[1] + postId + keyMatch[2] + postId
val salt = chapterData.s.decodeHex()
val unsaltedCiphertext = Base64.decode(chapterData.ct, Base64.DEFAULT)
@ -176,36 +107,10 @@ class Shinigami : Madara("Shinigami", "https://shinigamitoon.com", "id") {
.toByteArray()
}
// remove random ua in setting ext from multisrc and use custom one
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val prefCustomUserAgent = EditTextPreference(screen.context).apply {
key = PREF_KEY_CUSTOM_UA
title = TITLE_CUSTOM_UA
summary = (preferences.getString(PREF_KEY_CUSTOM_UA, "")!!.trim() + SUMMARY_STRING_CUSTOM_UA).trim()
setOnPreferenceChangeListener { _, newValue ->
val customUa = newValue as String
preferences.edit().putString(PREF_KEY_CUSTOM_UA, customUa).apply()
if (customUa.isNullOrBlank()) {
Toast.makeText(screen.context, RESTART_APP_STRING, Toast.LENGTH_LONG).show()
} else {
userAgent = null
}
summary = (customUa.trim() + SUMMARY_STRING2_CUSTOM_UA).trim()
true
}
}
screen.addPreference(prefCustomUserAgent)
}
companion object {
const val TITLE_CUSTOM_UA = "Custom User-Agent"
const val PREF_KEY_CUSTOM_UA = "pref_key_custom_ua"
const val SUMMARY_STRING_CUSTOM_UA = "\n\nBiarkan kosong untuk menggunakan User-Agent secara random"
const val SUMMARY_STRING2_CUSTOM_UA = "\n\nKosongkan untuk menggunakan User-Agent secara random"
const val RESTART_APP_STRING = "Restart Tachiyomi untuk menggunakan pengaturan baru."
private val KEY_REGEX by lazy { Regex("""post_id\s+\+\s+'(.*?)'\s+\+\s+post_id\s+\+\s+'(.*?)'\s+\+\s+post_id""") }
private val KEY_REGEX by lazy { Regex("""_id\s+\+\s+'(.*?)'\s+\+\s+post_id\s+\+\s+'(.*?)'\s+\+\s+post_id""") }
private val CHAPTER_DATA_REGEX by lazy { Regex("""var chapter_data\s*=\s*'(.*?)'""") }
private val POST_ID_REGEX by lazy { Regex("""var post_id\s*=\s*'(.*?)'""") }
private val OTHER_ID_REGEX by lazy { Regex("""var (\w+)_id\s*=\s*'(.*?)'""") }
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,9 @@
package eu.kanade.tachiyomi.extension.en.whalemanga
import eu.kanade.tachiyomi.multisrc.madara.Madara
class WhaleManga : Madara("WhaleManga", "https://whalemanga.com", "en") {
override val useNewChapterEndpoint = true
override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/"
}

View File

@ -1,22 +0,0 @@
package eu.kanade.tachiyomi.extension.pt.arkhamscan
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class ArkhamScan : MangaThemesia(
"Arkham Scan",
"https://arkhamscan.com",
"pt-BR",
dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
) {
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS)
.build()
override val altNamePrefix = "Nomes alternativos: "
}

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.extension.all.asurascans
package eu.kanade.tachiyomi.extension.en.asurascans
import android.app.Application
import android.content.SharedPreferences
@ -18,7 +18,6 @@ import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -27,7 +26,7 @@ import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class AsuraScansEn : MangaThemesia(
class AsuraScans : MangaThemesia(
"Asura Scans",
"https://asuratoon.com",
"en",
@ -115,13 +114,6 @@ class AsuraScansEn : MangaThemesia(
.mapIndexed { i, img -> Page(i, document.location(), img.attr("abs:src")) }
}
override fun Element.imgAttr(): String = when {
hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
hasAttr("data-src") -> attr("abs:data-src")
hasAttr("data-cfsrc") -> attr("abs:data-cfsrc")
else -> attr("abs:src")
}
private fun Observable<MangasPage>.tempUrlToPermIfNeeded(): Observable<MangasPage> {
return this.map { mangasPage ->
MangasPage(

View File

@ -1,10 +0,0 @@
package eu.kanade.tachiyomi.extension.all.asurascans
import eu.kanade.tachiyomi.source.SourceFactory
class AsuraScansFactory : SourceFactory {
override fun createSources() = listOf(
AsuraScansEn(),
AsuraScansTr(),
)
}

View File

@ -1,66 +0,0 @@
package eu.kanade.tachiyomi.extension.all.asurascans
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import okhttp3.OkHttpClient
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class AsuraScansTr : MangaThemesia(
"Asura Scans",
"https://armoniscans.com",
"tr",
dateFormat = SimpleDateFormat("MMM d, yyyy", Locale("tr")),
) {
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 3, TimeUnit.SECONDS)
.build()
override val seriesArtistSelector = ".fmed b:contains(Çizer)+span"
override val seriesAuthorSelector = ".fmed b:contains(Yazar)+span"
override val seriesStatusSelector = ".imptdt:contains(Durum) i"
override val seriesTypeSelector = ".imptdt:contains(Tür) a"
override val altNamePrefix: String = "Alternatif isim: "
override fun String?.parseStatus(): Int = when {
this == null -> SManga.UNKNOWN
this.contains("Devam Ediyor", ignoreCase = true) -> SManga.ONGOING
this.contains("Tamamlandı", ignoreCase = true) -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun Element.imgAttr(): String = when {
hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
hasAttr("data-src") -> attr("abs:data-src")
hasAttr("data-cfsrc") -> attr("abs:data-cfsrc")
else -> attr("abs:src")
}
override fun pageListParse(document: Document): List<Page> {
val scriptContent = document.selectFirst("script:containsData(ts_reader)")?.data()
?: return super.pageListParse(document)
val jsonString = scriptContent.substringAfter("ts_reader.run(").substringBefore(");")
val tsReader = json.decodeFromString<TSReader>(jsonString)
val imageUrls = tsReader.sources.firstOrNull()?.images ?: return emptyList()
return imageUrls.mapIndexed { index, imageUrl -> Page(index, document.location(), imageUrl) }
}
@Serializable
data class TSReader(
val sources: List<ReaderImageSource>,
)
@Serializable
data class ReaderImageSource(
val source: String,
val images: List<String>,
)
}

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.extension.es.carteldemanhwas
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
@ -15,13 +14,6 @@ class CarteldeManhwas : MangaThemesia(
override val hasProjectPage = true
override val projectPageString = "/proyectos"
override fun Element.imgAttr(): String = when {
hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
hasAttr("data-cfsrc") -> attr("abs:data-cfsrc")
hasAttr("data-src") -> attr("abs:data-src")
else -> attr("abs:src")
}
override fun searchMangaSelector() = ".utao .uta .imgu:not(:has(span.novelabel)), " +
".listupd .bs .bsx:not(:has(span.novelabel)), " +
".listo .bs .bsx:not(:has(span.novelabel))"

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 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,22 @@
package eu.kanade.tachiyomi.extension.pt.hikariscan
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
import okhttp3.HttpUrl.Companion.toHttpUrl
import java.text.SimpleDateFormat
import java.util.Locale
class HikariScan : MangaThemesia(
"Hikari Scan",
"https://hikariscan.org",
"pt-BR",
dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
) {
override val client = super.client.newBuilder()
.rateLimitHost(baseUrl.toHttpUrl(), 1, 2)
.build()
// =========================== Manga Details ============================
override val altNamePrefix = "Títulos alternativos: "
override val seriesAuthorSelector = ".tsinfo .imptdt:contains(autor) i"
}

View File

@ -1,11 +1,47 @@
package eu.kanade.tachiyomi.extension.id.komiktap
import app.cash.quickjs.QuickJs
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Cookie
import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
class Komiktap : MangaThemesia("Komiktap", "https://komiktap.me", "id") {
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(4)
.build()
override val client = super.client.newBuilder().addInterceptor(::sucuriInterceptor).build()
// Taken from es/ManhwasNet
private fun sucuriInterceptor(chain: Interceptor.Chain): Response {
val request = chain.request()
val url = request.url
val response = try {
chain.proceed(request)
} catch (e: Exception) {
// Try to clear cookies and retry
client.cookieJar.saveFromResponse(url, emptyList())
val clearHeaders = request.headers.newBuilder().removeAll("Cookie").build()
chain.proceed(request.newBuilder().headers(clearHeaders).build())
}
if (response.headers["x-sucuri-cache"].isNullOrEmpty() && response.headers["x-sucuri-id"] != null && url.toString().startsWith(baseUrl)) {
val script = response.use { it.asJsoup() }.selectFirst("script")?.data()
if (script != null) {
val patchedScript = script.split("(r)")[0].dropLast(1) + "r=r.replace('document.cookie','cookie');"
QuickJs.create().use {
val result = (it.evaluate(patchedScript) as String)
.replace("location.", "")
.replace("reload();", "")
val sucuriCookie = (it.evaluate(result) as String).split("=", limit = 2)
val cookieName = sucuriCookie.first()
val cookieValue = sucuriCookie.last().replace(";path", "")
client.cookieJar.saveFromResponse(url, listOf(Cookie.parse(url, "$cookieName=$cookieValue")!!))
}
val newResponse = chain.proceed(request)
if (!newResponse.headers["x-sucuri-cache"].isNullOrEmpty()) return newResponse
}
throw IOException("Situs yang dilindungi - Buka di WebView untuk mencoba membuka blokir.")
}
return response
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,36 +0,0 @@
package eu.kanade.tachiyomi.extension.pt.ssshentais
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Page
import okhttp3.OkHttpClient
import okhttp3.Request
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class SssHentais : MangaThemesia(
"SSS Hentais",
"https://hentais.sssscanlator.com",
"pt-BR",
dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
) {
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS)
.build()
override fun imageRequest(page: Page): Request {
val newHeaders = headersBuilder()
.set("Referer", page.url)
.set("Accept", "image/avif,image/webp,*/*")
.set("Accept-Language", "pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3")
.set("Sec-Fetch-Dest", "image")
.set("Sec-Fetch-Mode", "no-cors")
.set("Sec-Fetch-Site", "same-origin")
.build()
return GET(page.imageUrl!!, newHeaders)
}
}

View File

@ -12,7 +12,7 @@ import java.util.concurrent.TimeUnit
class SSSScanlator : MangaThemesia(
"SSSScanlator",
"https://sssscanlator.com",
"https://sssscanlator.com.br",
"pt-BR",
dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
) {

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.5 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,27 @@
package eu.kanade.tachiyomi.extension.tr.tempestfansub
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.HttpUrl.Companion.toHttpUrl
class TempestFansub : MangaThemesia(
"Tempest Fansub",
"https://tempestfansub.com",
"tr",
) {
override val client = super.client.newBuilder()
.rateLimitHost(baseUrl.toHttpUrl(), 2)
.build()
// =========================== Manga Details ============================
override val seriesArtistSelector = ".tsinfo .imptdt:contains(İllüstratör) i"
override val seriesAuthorSelector = ".tsinfo .imptdt:contains(Yazar) i"
override val seriesStatusSelector = ".tsinfo .imptdt:contains(Seri Durumu) i"
override fun String?.parseStatus(): Int = when (this?.trim()?.lowercase()) {
"devam ediyor" -> SManga.ONGOING
"bitti" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

View File

@ -1,115 +0,0 @@
package eu.kanade.tachiyomi.extension.pt.animaregia
import eu.kanade.tachiyomi.multisrc.mmrcms.MMRCMS
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
import okhttp3.Response
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class AnimaRegia : MMRCMS("AnimaRegia", "https://animaregia.net", "pt-BR") {
override val id: Long = 4378659695320121364
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS)
.build()
// Remove the language tag from the title name.
override fun internalMangaParse(response: Response): MangasPage {
return super.internalMangaParse(response).let {
it.copy(
mangas = it.mangas.map { manga ->
manga.apply { title = title.removeSuffix(LANGUAGE_SUFFIX) }
},
)
}
}
override fun latestUpdatesFromElement(element: Element, urlSelector: String): SManga? {
return super.latestUpdatesFromElement(element, urlSelector)
?.apply { title = title.removeSuffix(LANGUAGE_SUFFIX) }
}
override fun gridLatestUpdatesFromElement(element: Element): SManga {
return super.gridLatestUpdatesFromElement(element)
.apply { title = title.removeSuffix(LANGUAGE_SUFFIX) }
}
// Override searchMangaParse with same body from internalMangaParse since
// it can use the other endpoint instead.
override fun searchMangaParse(response: Response): MangasPage {
return super.searchMangaParse(response).let {
it.copy(
mangas = it.mangas.map { manga ->
manga.apply { title = title.removeSuffix(LANGUAGE_SUFFIX) }
},
)
}
}
// The website modified the information panel.
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
val document = response.asJsoup()
title = document.selectFirst("h1.widget-title")!!.text()
thumbnail_url = coverGuess(
document.select("div.col-sm-5 img.img-thumbnail").firstOrNull()?.attr("abs:src"),
document.location(),
)
description = document.select("div.row div.well p")!!.text().trim()
for (element in document.select("div.col-sm-5 ul.list-group li.list-group-item")) {
when (element.text().trim().lowercase(BRAZILIAN_LOCALE).substringBefore(":")) {
"autor(es)" -> author = element.select("a")
.joinToString(", ") { it.text().trim() }
"artist(s)" -> artist = element.select("a")
.joinToString(", ") { it.text().trim() }
"categorias" -> genre = element.select("a")
.joinToString(", ") { it.text().trim() }
"status" -> status = when (element.select("span.label").text()) {
"Completo", "Concluído" -> SManga.COMPLETED
"Ativo" -> SManga.ONGOING
else -> SManga.UNKNOWN
}
}
}
}
override fun chapterListSelector(): String = "div.row ul.chapters > li"
override fun chapterListParse(response: Response): List<SChapter> {
return response.asJsoup()
.select(chapterListSelector())
.map { el ->
SChapter.create().apply {
name = el.select("h5.chapter-title-rtl").text()
scanlator = el.select("div.col-md-3 ul li")
.joinToString(" & ") { it.text().trim() }
date_upload = el.select("div.col-md-4").firstOrNull()
?.text()?.removeSuffix("Download")?.toDate() ?: 0L
setUrlWithoutDomain(el.select("h5.chapter-title-rtl a").first()!!.attr("href"))
}
}
}
private fun String.toDate(): Long {
return runCatching { DATE_FORMAT.parse(trim())?.time }
.getOrNull() ?: 0L
}
companion object {
private const val LANGUAGE_SUFFIX = " (pt-br)"
private val BRAZILIAN_LOCALE = Locale("pt", "BR")
private val DATE_FORMAT by lazy {
SimpleDateFormat("dd MMM. yyyy", Locale.ENGLISH)
}
}
}

View File

@ -3,9 +3,18 @@ package eu.kanade.tachiyomi.extension.fr.mangascan
import eu.kanade.tachiyomi.multisrc.mmrcms.MMRCMS
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.Request
import okhttp3.Response
class MangaScan : MMRCMS("Manga-Scan", "https://mangascan-fr.com", "fr") {
override fun mangaDetailsParse(response: Response): SManga {
return super.mangaDetailsParse(response).apply {
title = title.substringBefore("Chapitres en ligne").substringAfter("Scan").trim()
}
}
class MangaScan : MMRCMS("Manga-Scan", "https://mangascan.cc", "fr") {
override fun imageRequest(page: Page): Request {
val newHeaders = headersBuilder()
.set("Referer", baseUrl)

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.multisrc.flixscans
import android.util.Log
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter
@ -16,7 +15,6 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.Call
import okhttp3.Callback
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
@ -28,8 +26,8 @@ abstract class FlixScans(
override val name: String,
override val baseUrl: String,
override val lang: String,
protected val apiUrl: String = baseUrl.replace("://", "://api.").plus("/api/v1"),
protected val cdnUrl: String = baseUrl.replace("://", "://api.").plus("/storage/"),
protected val apiUrl: String = "$baseUrl/api/__api_party/noxApi",
protected val cdnUrl: String = baseUrl.replace("://", "://media.").plus("/"),
) : HttpSource() {
override val supportsLatest = true
@ -43,6 +41,12 @@ abstract class FlixScans(
override fun headersBuilder() = super.headersBuilder()
.add("Referer", baseUrl)
protected open fun postPath(path: String): Request {
val payload = """{"path":"$path","headers":{}}""".toRequestBody(JSON_MEDIA_TYPE)
return POST(apiUrl, headers, payload)
}
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
runCatching { fetchGenre() }
@ -50,7 +54,7 @@ abstract class FlixScans(
}
override fun popularMangaRequest(page: Int): Request {
return GET("$apiUrl/webtoon/homepage/home", headers)
return postPath("webtoon/pages/home/romance")
}
override fun popularMangaParse(response: Response): MangasPage {
@ -70,16 +74,14 @@ abstract class FlixScans(
}
override fun latestUpdatesRequest(page: Int): Request {
return GET("$apiUrl/search/advance?page=$page&serie_type=webtoon", headers)
return postPath("search/advance?page=$page&serie_type=webtoon")
}
override fun latestUpdatesParse(response: Response): MangasPage {
val result = response.parseAs<ApiResponse<BrowseSeries>>()
val currentPage = response.request.url.queryParameter("page")
?.toIntOrNull() ?: 1
val entries = result.data.map { it.toSManga(cdnUrl) }
val hasNextPage = result.meta.lastPage > currentPage
val hasNextPage = result.meta.lastPage > result.meta.currentPage
return MangasPage(entries, hasNextPage)
}
@ -131,7 +133,7 @@ abstract class FlixScans(
}
private fun fetchGenreRequest(): Request {
return GET("$apiUrl/search/genres", headers)
return postPath("search/genres")
}
private fun fetchGenreParse(response: Response): List<GenreHolder> {
@ -168,53 +170,55 @@ abstract class FlixScans(
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotEmpty()) {
val requestBody = SearchInput(query.trim())
val searchBody = SearchInput(query.trim())
.let(json::encodeToString)
.toRequestBody(JSON_MEDIA_TYPE)
.replace("\"", "\\\"")
val newHeaders = headersBuilder()
.add("Content-Length", requestBody.contentLength().toString())
.add("Content-Type", requestBody.contentType().toString())
.build()
val requestBody = """{
|"path":"search/serie?page=$page",
|"headers":{"Content-type":"application/json"},
|"method":"POST","body":"$searchBody"
|}
""".trimMargin().toRequestBody(JSON_MEDIA_TYPE)
return POST("$apiUrl/search/serie?page=$page", newHeaders, requestBody)
return POST(apiUrl, headers, requestBody)
}
val advSearchUrl = apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("search/advance")
addQueryParameter("page", page.toString())
addQueryParameter("serie_type", "webtoon")
val advSearchBody = buildString {
append("search/advance")
append("?page=", page)
append("&serie_type=webtoon")
filters.forEach { filter ->
when (filter) {
is GenreFilter -> {
filter.checked.let {
if (it.isNotEmpty()) {
addQueryParameter("genres", it.joinToString(","))
append("&genres=", it.joinToString(","))
}
}
}
is MainGenreFilter -> {
if (filter.state > 0) {
addQueryParameter("main_genres", filter.selected)
append("&main_genres=", filter.selected)
}
}
is TypeFilter -> {
if (filter.state > 0) {
addQueryParameter("type", filter.selected)
append("&type=", filter.selected)
}
}
is StatusFilter -> {
if (filter.state > 0) {
addQueryParameter("status", filter.selected)
append("&status=", filter.selected)
}
}
else -> {}
}
}
}.build()
}
return GET(advSearchUrl, headers)
return postPath(advSearchBody)
}
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
@ -222,7 +226,7 @@ abstract class FlixScans(
override fun mangaDetailsRequest(manga: SManga): Request {
val id = manga.url.split("-")[1]
return GET("$apiUrl/webtoon/series/$id", headers)
return postPath("webtoon/series/$id")
}
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url
@ -236,7 +240,7 @@ abstract class FlixScans(
override fun chapterListRequest(manga: SManga): Request {
val id = manga.url.split("-")[1]
return GET("$apiUrl/webtoon/chapters/$id-desc", headers)
return postPath("webtoon/chapters/$id-desc")
}
override fun chapterListParse(response: Response): List<SChapter> {
@ -250,7 +254,7 @@ abstract class FlixScans(
.substringAfterLast("/")
.substringBefore("-")
return GET("$apiUrl/webtoon/chapters/chapter/$id", headers)
return postPath("webtoon/chapters/chapter/$id")
}
override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url
@ -269,6 +273,6 @@ abstract class FlixScans(
use { body.string() }.let(json::decodeFromString)
companion object {
private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull()
private val JSON_MEDIA_TYPE = "application/json".toMediaTypeOrNull()
}
}

View File

@ -17,6 +17,7 @@ data class ApiResponse<T>(
@Serializable
data class PageInfo(
@SerialName("last_page") val lastPage: Int,
@SerialName("current_page") val currentPage: Int,
)
@Serializable

View File

@ -9,7 +9,7 @@ class FlixScansGenerator : ThemeSourceGenerator {
override val themeClass = "FlixScans"
override val baseVersionCode: Int = 3
override val baseVersionCode: Int = 4
override val sources = listOf(
SingleLang("Flix Scans", "https://flixscans.org", "en", className = "FlixScansNet", pkgName = "flixscans"),

View File

@ -15,7 +15,6 @@ class FMReaderGenerator : ThemeSourceGenerator {
SingleLang("Epik Manga", "https://www.epikmanga.com", "tr"),
SingleLang("KissLove", "https://klz9.com", "ja", isNsfw = true, overrideVersionCode = 4),
SingleLang("Manga-TR", "https://manga-tr.com", "tr", className = "MangaTR", overrideVersionCode = 3),
SingleLang("ManhuaRock", "https://manhuarock.net", "vi", overrideVersionCode = 1),
SingleLang("Say Truyen", "https://saytruyenvip.com", "vi", overrideVersionCode = 3),
SingleLang("WeLoveManga", "https://weloma.art", "ja", pkgName = "rawlh", isNsfw = true, overrideVersionCode = 5),
SingleLang("Manga1000", "https://manga1000.top", "ja"),

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