Compare commits

...

45 Commits

Author SHA1 Message Date
bapeey c5c6d77479 HeavenManga: Fix chapter list and add latest tab (#1206)
CI / Prepare job (push) Successful in 4s Details
CI / Build multisrc modules (push) Successful in 3m39s Details
CI / Build individual modules (push) Successful in 37s Details
CI / Publish repo (push) Successful in 39s Details
Fixes
2024-02-13 02:56:17 +00:00
Luka Mamukashvili 2556e117be Update MagusManga.kt (#1208)
* Update MagusManga.kt

* Update MangaThemesiaGenerator.kt
2024-02-13 02:56:17 +00:00
Fermín Cirella 925136fc15 Anchira: Enable image quality preference (#1207)
Enable image quality preference
2024-02-13 02:56:17 +00:00
bapeey c8ca375c1c Atlantis Scan: Move to MangaThemesia (#1205)
* Move to MangaThemesia

* Fixes
2024-02-13 02:56:17 +00:00
bapeey 5b640b1512 InariManga: Update domain (#1204)
Update domain
2024-02-13 02:56:17 +00:00
bapeey 6850a721c0 LectorTMO: Add preference for save the last CF url (#1203)
Add pref
2024-02-13 02:56:17 +00:00
mohamedotaku 8bb508d679 change url Manga Sail "en" (#1194) 2024-02-13 02:56:17 +00:00
mohamedotaku cf5299c188 change url mangaae "ar" (#1191) 2024-02-13 02:56:17 +00:00
mohamedotaku 591c65a534 Add source MangaGezgini "tr" (#1168)
* Add source MangaGezgini "tr"

* fix  problem chapter name

* Update MangaGezgini.kt

* Update MangaGezgini.kt
2024-02-13 02:56:17 +00:00
Cuong M. Tran 731145443b Fix cloudflare un-caught causes app crashing (#1178)
Fix cloudflare uncatch causes app crashing
2024-02-13 02:56:17 +00:00
MikeZeDev bc4df5d008 Mangahub multisrc : change CDN domain (#1169)
* Change Mangahub CDN

* Update MangaHubGenerator.kt version code
2024-02-13 02:56:17 +00:00
beerpsi f71938e357 Comikey: Fix page list (#1161) 2024-02-13 02:56:17 +00:00
Mike 712c3a75be Luminous Scans - update domain (again) (#1158) 2024-02-13 02:56:17 +00:00
beerpsi f09c11a01c Add MangaDig and make ColaManga a multisrc (#1139)
* Add MangaDig and make ColaManga a multisrc

* Make MangaDig NSFW

* Fix linting

* Fix next page selector
2024-02-13 02:56:17 +00:00
Mike 244fd8f4fa StoneScape cleanup (#1156) 2024-02-13 02:56:17 +00:00
beerpsi 3db5f6edc2 Add Truyen Hentai 18+ (#1148)
* Add Truyen Hentai 18+

* fix search
2024-02-13 02:56:17 +00:00
mohamedotaku e54743b6c9 Add source StoneScape "en" (#1149) 2024-02-13 02:56:17 +00:00
mohamedotaku cdd332ee78 update Mangalink url (#1150) 2024-02-13 02:56:17 +00:00
beerpsi 49c3517510 SayHentai: Update base URL (#1147) 2024-02-13 02:56:17 +00:00
beerpsi 4682cc8752 Add Comikey (#1110)
* Add Comikey

* Remove logging

* i18n

* Comikey Brasil, paid chapters toggle, use other chapter endpoint

* Don't parse author/artist in searchMangaFromElement

* makeEpisodeSlug private

* Move gundamUrl outside of class constructor

* paginate latest

* paginate search

* Properly distinguish i18n keys from normal messages in WebView script

* Parse statuses better

* Add genre for entry format

* remove unnecessary getChapterUrl

* Fix status on BR

* ACTUALLY fix status on BR

* Fix more Comikey Brasil stupidity

* Validate that manifestUrl is valid

* Revert "Validate that manifestUrl is valid"

This reverts commit d744fd42b45ae46baf48308ec3f354546d1452af.

* Proper i18n in WebView script

* Add explanation for weird binding

* Move helper functions to bottom

* Support signing in through WebView

* Fix chapter list when signed in

* Properly filter locked chapters

* Remove WebView logging
2024-02-13 02:56:17 +00:00
AwkwardPeak7 879eb629b1 add Manga18 multisrc (#1135)
* add Manga18 multisrc

18 Porn Comic
Comic1000
HANMAN18
Hentai3z.CC
Manga18.Club
TuManhwas.Club

* tag filters

* empty alt name

* lint
2024-02-13 02:56:17 +00:00
Secozzi d8f4f38676 add rawotaku (#1111) 2024-02-13 02:56:17 +00:00
Mike 95d3671f3d NepNep - conditionally add scanlator info (#1131) 2024-02-13 02:56:17 +00:00
Secozzi c8f24dac99 Add Sussyscan (#1133)
* add sussyscan

* remove type
2024-02-13 02:56:17 +00:00
Secozzi aae877a2a3 add ninjascan (#1132) 2024-02-13 02:56:17 +00:00
mohamedotaku bd0b4b0edd Add Source Nirvana Manga "tr" (#1122)
* Add Source Nirvana Manga "tr"

* Update NirvanaManga.kt
2024-02-13 02:56:17 +00:00
beerpsi 47e328dc84 Nekopost: Fix search (#1124)
* Nekopost: Fix search

* Bump version
2024-02-13 02:56:17 +00:00
Mike 8dd884535b AsmHentai - new source (#1106)
* AsmHentai - new source

* cleanup
2024-02-13 02:56:17 +00:00
LLecca 457475fc07 Luminous Scans: Update domain (#1120)
Update domain
2024-02-13 02:56:17 +00:00
mohamedotaku 82983fb1b9 Add source MangaWOW "tr" (#1119)
* Add Source MangaWOW "tr"

* Update MangaWOW.kt
2024-02-13 02:56:17 +00:00
LLecca ea90594583 Earlym: Use the correct CDN (#1118)
🤡
2024-02-13 02:56:17 +00:00
LLecca b78d35db07 Earlym: Fix unexpected JSON token (#1108)
One char fix
2024-02-13 02:56:17 +00:00
AwkwardPeak7 b2ddfd348b Earlym fix pages (#1105) 2024-02-13 02:56:17 +00:00
bapeey 584e00b1dd Add Plot Twist No Fansub (#1099)
* Bruh

* Change filter message

* review changes
2024-02-13 02:56:17 +00:00
beerpsi 2f244a72ff Add Hachiraw (#1096)
* Add Hachiraw

* Add URI intent handler

* Mark as NSFW
2024-02-13 02:56:17 +00:00
bapeey 7746e2a3fa SamuraiScans: Update domain (#1102)
Update domain
2024-02-13 02:56:17 +00:00
AlphaBoom fb88e2ce97 Manga1000: Fix Chapter list and Manga details issue (#1094)
* Manga1000:Fix the chapter list for some manga cannot be found.

* Manga1000:Failed to parse manga details info.
2024-02-13 02:56:17 +00:00
AwkwardPeak7 48c71e342d add Idol. gravureprincess .date (#1093)
add IdolGravureprincessDate
2024-02-13 02:56:17 +00:00
beerpsi c8c4110a55 Clear out MangaThemesia source requests (#1079)
* Add Comicaso (#466)

* Add Comicsekai (#798)

* Add Dragon-Manga (#66)

* Add Iris Scanlator (#630)

* Add Lami-Manga (#74)

* Add Makimaaaaa (#73)

* Add Manga Efendisi (#197)

* Add Manga-Mate (#297)

* Add Manga-Moon (#63)

* Add NTR-Manga (#71)

* Make Thunder Scans multilang and add Thunder Scans EN (#812)

* ThunderScans: Update generator
2024-02-13 02:56:17 +00:00
beerpsi 23e385128e Add MangaFun + LZString library (#1057)
* Add MangaFun + LZString library

* Mark as NSFW

* Reverse using :lib:lzstring on Manhuagui

* Add ending newline

* Replace QuickJS in Manhuagui with LZString + Unpacker

* Bump ManhuaGui version

* remove unncessary .lets

* optimize icons

* Apply suggestion
2024-02-13 02:56:17 +00:00
beerpsi b0b32918e1 Add SadScans and make APairOf2 a multisrc (#1046)
* Add SadScans and make APairOf2 a multisrc

* Formatting

* Trailing comma

* optimize icons

* newline

---------

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
2024-02-13 02:56:17 +00:00
beerpsi c02bd36b43 Komga: Fix latest updates tab (#1076) 2024-02-13 02:56:17 +00:00
Mike fe97867cf1 Jiangzaitoon - update domain (#1074) 2024-02-13 02:56:17 +00:00
Mike fdd2c5c6d1 ReadM - update domain (#1073) 2024-02-13 02:56:17 +00:00
Secozzi 8e610e1ea9 Infinityscans: fix cloudflare issues (#1067)
fix cloudflare issues
2024-02-13 02:56:17 +00:00
286 changed files with 5561 additions and 1009 deletions

View File

@ -0,0 +1,12 @@
plugins {
`java-library`
kotlin("jvm")
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(libs.kotlin.stdlib)
}

View File

@ -0,0 +1,294 @@
package eu.kanade.tachiyomi.lib.lzstring
typealias getCharFromIntFn = (it: Int) -> String
typealias getNextValueFn = (it: Int) -> Int
/**
* Reimplementation of [lz-string](https://github.com/pieroxy/lz-string) compression/decompression.
*/
object LZString {
private fun compress(
uncompressed: String,
bitsPerChar: Int,
getCharFromInt: getCharFromIntFn,
): String {
val context = CompressionContext(uncompressed.length, bitsPerChar, getCharFromInt)
for (ii in uncompressed.indices) {
context.c = uncompressed[ii].toString()
if (!context.dictionary.containsKey(context.c)) {
context.dictionary[context.c] = context.dictSize++
context.dictionaryToCreate[context.c] = true
}
context.wc = context.w + context.c
if (context.dictionary.containsKey(context.wc)) {
context.w = context.wc
continue
}
context.outputCodeForW()
context.decrementEnlargeIn()
context.dictionary[context.wc] = context.dictSize++
context.w = context.c
}
if (context.w.isNotEmpty()) {
context.outputCodeForW()
context.decrementEnlargeIn()
}
// Mark the end of the stream
context.value = 2
for (i in 0 until context.numBits) {
context.dataVal = (context.dataVal shl 1) or (context.value and 1)
context.appendDataOrAdvancePosition()
context.value = context.value shr 1
}
while (true) {
context.dataVal = context.dataVal shl 1
if (context.dataPosition == bitsPerChar - 1) {
context.data.append(getCharFromInt(context.dataVal))
break
}
context.dataPosition++
}
return context.data.toString()
}
private fun decompress(length: Int, resetValue: Int, getNextValue: getNextValueFn): String {
val dictionary = mutableListOf<String>()
val result = StringBuilder()
val data = DecompressionContext(resetValue, getNextValue)
var enlargeIn = 4
var numBits = 3
var entry: String
var c: Char? = null
for (i in 0 until 3) {
dictionary.add(i.toString())
}
data.loopUntilMaxPower()
when (data.bits) {
0 -> {
data.bits = 0
data.maxPower = 1 shl 8
data.power = 1
data.loopUntilMaxPower()
c = data.bits.toChar()
}
1 -> {
data.bits = 0
data.maxPower = 1 shl 16
data.power = 1
data.loopUntilMaxPower()
c = data.bits.toChar()
}
2 -> throw IllegalArgumentException("Invalid LZString")
}
if (c == null) {
throw Exception("No character found")
}
dictionary.add(c.toString())
var w = c.toString()
result.append(c.toString())
while (true) {
if (data.index > length) {
throw IllegalArgumentException("Invalid LZString")
}
data.bits = 0
data.maxPower = 1 shl numBits
data.power = 1
data.loopUntilMaxPower()
var cc = data.bits
when (data.bits) {
0 -> {
data.bits = 0
data.maxPower = 1 shl 8
data.power = 1
data.loopUntilMaxPower()
dictionary.add(data.bits.toChar().toString())
cc = dictionary.size - 1
enlargeIn--
}
1 -> {
data.bits = 0
data.maxPower = 1 shl 16
data.power = 1
data.loopUntilMaxPower()
dictionary.add(data.bits.toChar().toString())
cc = dictionary.size - 1
enlargeIn--
}
2 -> return result.toString()
}
if (enlargeIn == 0) {
enlargeIn = 1 shl numBits
numBits++
}
entry = if (cc < dictionary.size) {
dictionary[cc]
} else {
if (cc == dictionary.size) {
w + w[0]
} else {
throw Exception("Invalid LZString")
}
}
result.append(entry)
dictionary.add(w + entry[0])
enlargeIn--
w = entry
if (enlargeIn == 0) {
enlargeIn = 1 shl numBits
numBits++
}
}
}
private const val base64KeyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
fun compressToBase64(input: String): String =
compress(input, 6) { base64KeyStr[it].toString() }.let {
return when (it.length % 4) {
0 -> it
1 -> "$it==="
2 -> "$it=="
3 -> "$it="
else -> throw IllegalStateException("Modulo of 4 should not exceed 3.")
}
}
fun decompressFromBase64(input: String): String =
decompress(input.length, 32) {
base64KeyStr.indexOf(input[it])
}
}
private data class DecompressionContext(
val resetValue: Int,
val getNextValue: getNextValueFn,
var value: Int = getNextValue(0),
var position: Int = resetValue,
var index: Int = 1,
var bits: Int = 0,
var maxPower: Int = 1 shl 2,
var power: Int = 1,
) {
fun loopUntilMaxPower() {
while (power != maxPower) {
val resb = value and position
position = position shr 1
if (position == 0) {
position = resetValue
value = getNextValue(index++)
}
bits = bits or ((if (resb > 0) 1 else 0) * power)
power = power shl 1
}
}
}
private data class CompressionContext(
val uncompressedLength: Int,
val bitsPerChar: Int,
val getCharFromInt: getCharFromIntFn,
var value: Int = 0,
val dictionary: MutableMap<String, Int> = HashMap(),
val dictionaryToCreate: MutableMap<String, Boolean> = HashMap(),
var c: String = "",
var wc: String = "",
var w: String = "",
var enlargeIn: Int = 2, // Compensate for the first entry which should not count
var dictSize: Int = 3,
var numBits: Int = 2,
val data: StringBuilder = StringBuilder(uncompressedLength / 3),
var dataVal: Int = 0,
var dataPosition: Int = 0,
) {
fun appendDataOrAdvancePosition() {
if (dataPosition == bitsPerChar - 1) {
dataPosition = 0
data.append(getCharFromInt(dataVal))
dataVal = 0
} else {
dataPosition++
}
}
fun decrementEnlargeIn() {
enlargeIn--
if (enlargeIn == 0) {
enlargeIn = 1 shl numBits
numBits++
}
}
// Output the code for W.
fun outputCodeForW() {
if (dictionaryToCreate.containsKey(w)) {
if (w[0].code < 256) {
for (i in 0 until numBits) {
dataVal = dataVal shl 1
appendDataOrAdvancePosition()
}
value = w[0].code
for (i in 0 until 8) {
dataVal = (dataVal shl 1) or (value and 1)
appendDataOrAdvancePosition()
value = value shr 1
}
} else {
value = 1
for (i in 0 until numBits) {
dataVal = (dataVal shl 1) or value
appendDataOrAdvancePosition()
value = 0
}
value = w[0].code
for (i in 0 until 16) {
dataVal = (dataVal shl 1) or (value and 1)
appendDataOrAdvancePosition()
value = value shr 1
}
}
decrementEnlargeIn()
dictionaryToCreate.remove(w)
} else {
value = dictionary[w]!!
for (i in 0 until numBits) {
dataVal = (dataVal shl 1) or (value and 1)
appendDataOrAdvancePosition()
value = value shr 1
}
}
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name="eu.kanade.tachiyomi.multisrc.colamanga.ColaMangaUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:host="${SOURCEHOST}"
android:scheme="${SOURCESCHEME}"
android:pathPattern="/manga-..*/" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,3 @@
dependencies {
implementation(project(":lib:synchrony"))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,123 @@
package eu.kanade.tachiyomi.extension.en.mangadig
import eu.kanade.tachiyomi.multisrc.colamanga.ColaManga
import eu.kanade.tachiyomi.multisrc.colamanga.UriPartFilter
import eu.kanade.tachiyomi.source.model.FilterList
class MangaDig : ColaManga("MangaDig", "https://mangadig.com", "en") {
override fun popularMangaNextPageSelector() = "a:contains(Next):not(.fed-btns-disad)"
override val statusTitle = "Status"
override val authorTitle = "Author"
override val genreTitle = "Category"
override val statusOngoing = "OnGoing"
override val statusCompleted = "Complete"
override fun getFilterList(): FilterList {
val filters = buildList {
addAll(super.getFilterList().list)
add(SortFilter())
add(CategoryFilter())
add(CharFilter())
add(StatusFilter())
}
return FilterList(filters)
}
private class StatusFilter : UriPartFilter(
"Status",
"status",
arrayOf(
Pair("All", ""),
Pair("Ongoing", "1"),
Pair("Complete", "2"),
),
)
private class SortFilter : UriPartFilter(
"Order by",
"orderBy",
arrayOf(
Pair("Last updated", "update"),
Pair("Recently added", "create"),
Pair("Most popular today", "dailyCount"),
Pair("Most popular this week", "weeklyCount"),
Pair("Most popular this month", "monthlyCount"),
),
2,
)
private class CategoryFilter : UriPartFilter(
"Genre",
"mainCategoryId",
arrayOf(
Pair("All", ""),
Pair("Romance", "10008"),
Pair("Drama", "10005"),
Pair("Comedy", "10004"),
Pair("Fantasy", "10006"),
Pair("Action", "10002"),
Pair("CEO", "10142"),
Pair("Webtoons", "10012"),
Pair("Historical", "10021"),
Pair("Adventure", "10003"),
Pair("Josei", "10059"),
Pair("Smut", "10047"),
Pair("Supernatural", "10018"),
Pair("School life", "10017"),
Pair("Completed", "10423"),
Pair("Possessive", "10284"),
Pair("Manhua", "10010"),
Pair("Sweet", "10282"),
Pair("Harem", "10007"),
Pair("Slice of life", "10026"),
Pair("Girl Power", "10144"),
Pair("Martial arts", "10013"),
Pair("Chinese Classic", "10243"),
Pair("BL", "10262"),
Pair("Manhwa", "10039"),
Pair("Adult", "10030"),
Pair("Shounen", "10009"),
Pair("TimeTravel", "10143"),
Pair("Shoujo", "10054"),
Pair("Ecchi", "10027"),
Pair("Revenge", "10556"),
),
)
private class CharFilter : UriPartFilter(
"Alphabet",
"charCategoryId",
arrayOf(
Pair("All", ""),
Pair("A", "10015"),
Pair("B", "10028"),
Pair("C", "10055"),
Pair("D", "10034"),
Pair("E", "10049"),
Pair("F", "10056"),
Pair("G", "10023"),
Pair("H", "10037"),
Pair("I", "10035"),
Pair("J", "10060"),
Pair("K", "10022"),
Pair("L", "10046"),
Pair("M", "10020"),
Pair("N", "10044"),
Pair("O", "10024"),
Pair("P", "10048"),
Pair("Q", "10051"),
Pair("R", "10025"),
Pair("S", "10011"),
Pair("T", "10001"),
Pair("U", "10058"),
Pair("V", "10016"),
Pair("W", "10052"),
Pair("X", "10061"),
Pair("Y", "10036"),
Pair("Z", "10101"),
),
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -0,0 +1,120 @@
package eu.kanade.tachiyomi.extension.zh.onemanhua
import eu.kanade.tachiyomi.multisrc.colamanga.ColaManga
import eu.kanade.tachiyomi.multisrc.colamanga.UriPartFilter
import eu.kanade.tachiyomi.source.model.FilterList
class Onemanhua : ColaManga("COLAMANGA", "https://www.colamanga.com", "zh") {
override val id = 8252565807829914103 // name used to be "One漫画"
override fun popularMangaNextPageSelector() = "a:contains(下页):not(.fed-btns-disad)"
override val statusTitle = "状态"
override val authorTitle = "作者"
override val genreTitle = "类别"
override val statusOngoing = "连载中"
override val statusCompleted = "已完结"
override fun getFilterList(): FilterList {
val filters = buildList {
addAll(super.getFilterList().list)
add(SortFilter())
add(CategoryFilter())
add(CharFilter())
add(StatusFilter())
}
return FilterList(filters)
}
private class StatusFilter : UriPartFilter(
"状态",
"status",
arrayOf(
Pair("全部", ""),
Pair("连载中", "1"),
Pair("已完结", "2"),
),
)
private class SortFilter : UriPartFilter(
"排序",
"orderBy",
arrayOf(
Pair("更新日", "update"),
Pair("日点击", "dailyCount"),
Pair("周点击", "weeklyCount"),
Pair("月点击", "monthlyCount"),
),
1,
)
private class CategoryFilter : UriPartFilter(
"类型",
"mainCategoryId",
arrayOf(
Pair("全部", ""),
Pair("热血", "10023"),
Pair("玄幻", "10024"),
Pair("恋爱", "10126"),
Pair("冒险", "10210"),
Pair("古风", "10143"),
Pair("都市", "10124"),
Pair("穿越", "10129"),
Pair("奇幻", "10242"),
Pair("其他", "10560"),
Pair("少男", "10641"),
Pair("搞笑", "10122"),
Pair("战斗", "10309"),
Pair("冒险热血", "11224"),
Pair("重生", "10461"),
Pair("爆笑", "10201"),
Pair("逆袭", "10943"),
Pair("后宫", "10138"),
Pair("少年", "10321"),
Pair("少女", "10301"),
Pair("熱血", "12044"),
Pair("系统", "10722"),
Pair("动作", "10125"),
Pair("校园", "10131"),
Pair("冒險", "12123"),
Pair("修真", "10133"),
Pair("修仙", "10453"),
Pair("剧情", "10480"),
Pair("霸总", "10127"),
Pair("大女主", "10706"),
Pair("生活", "10142"),
),
)
private class CharFilter : UriPartFilter(
"字母",
"charCategoryId",
arrayOf(
Pair("全部", ""),
Pair("A", "10182"),
Pair("B", "10081"),
Pair("C", "10134"),
Pair("D", "10001"),
Pair("E", "10238"),
Pair("F", "10161"),
Pair("G", "10225"),
Pair("H", "10137"),
Pair("I", "10284"),
Pair("J", "10141"),
Pair("K", "10283"),
Pair("L", "10132"),
Pair("M", "10136"),
Pair("N", "10130"),
Pair("O", "10282"),
Pair("P", "10262"),
Pair("Q", "10164"),
Pair("R", "10240"),
Pair("S", "10121"),
Pair("T", "10123"),
Pair("U", "11184"),
Pair("V", "11483"),
Pair("W", "10135"),
Pair("X", "10061"),
Pair("Y", "10082"),
Pair("Z", "10128"),
),
)
}

View File

@ -13,6 +13,9 @@ import rx.Observable
import java.util.Calendar import java.util.Calendar
class Manga1000 : FMReader("Manga1000", "https://manga1000.top", "ja") { class Manga1000 : FMReader("Manga1000", "https://manga1000.top", "ja") {
override val infoElementSelector = "div.row div.row"
// source is picky about URL format // source is picky about URL format
private fun mangaRequest(sortBy: String, page: Int): Request { private fun mangaRequest(sortBy: String, page: Int): Request {
return GET("$baseUrl/manga-list.html?listType=pagination&page=$page&artist=&author=&group=&m_status=&name=&genre=&ungenre=&magazine=&sort=$sortBy&sort_type=DESC", headers) return GET("$baseUrl/manga-list.html?listType=pagination&page=$page&artist=&author=&group=&m_status=&name=&genre=&ungenre=&magazine=&sort=$sortBy&sort_type=DESC", headers)
@ -25,7 +28,7 @@ class Manga1000 : FMReader("Manga1000", "https://manga1000.top", "ja") {
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
val slug = manga.url.substringAfter("manga-").substringBefore(".html") val slug = manga.url.substringAfter("manga-").substringBefore(".html")
return client.newCall(GET("$baseUrl/app/manga/controllers/cont.Listchapterapi.php?slug=$slug", headers)) return client.newCall(GET("$baseUrl/app/manga/controllers/cont.Listchapter.php?slug=$slug", headers))
.asObservableSuccess() .asObservableSuccess()
.map { res -> .map { res ->
res.asJsoup().select(".at-series a").map { res.asJsoup().select(".at-series a").map {

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: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -0,0 +1,96 @@
package eu.kanade.tachiyomi.extension.ja.idolgravureprincessdate
import eu.kanade.tachiyomi.multisrc.gravureblogger.GravureBlogger
class IdolGravureprincessDate : GravureBlogger(
"Idol. gravureprincess .date",
"https://idol.gravureprincess.date",
"ja",
) {
override val labelFilters = buildMap {
put("Idol", getIdols())
put("Magazines", getMagazine())
}
private fun getIdols() = listOf(
"Nogizaka46",
"AKB48",
"NMB48",
"Keyakizaka46",
"HKT48",
"SKE48",
"NGT48",
"SUPER☆GiRLS",
"Morning Musume",
"Dempagumi.inc",
"Angerme",
"Juice=Juice",
"NijiCon-虹コン",
"Houkago Princess",
"Magical Punchline",
"Idoling!!!",
"Rev. from DVL",
"Link STAR`s",
"LADYBABY",
"℃-ute",
"Country Girls",
"Up Up Girls (Kakko Kari)",
"Yumemiru Adolescence",
"Shiritsu Ebisu Chugaku",
"Tenkoushoujo Kagekidan",
"Drop",
"Steam Girls",
"Kamen Joshi's",
"LinQ",
"Doll☆Element",
"TrySail",
"Akihabara Backstage Pass",
"Palet",
"Passport☆",
"Ange☆Reve",
"BiSH",
"Ciao Bella Cinquetti",
"Gekidanherbest",
"Haraeki Stage Ace",
"Ru:Run",
"SDN48",
)
private fun getMagazine() = listOf(
"FLASH",
"Weekly Playboy",
"FRIDAY Magazine",
"Young Jump",
"Young Magazine",
"BLT",
"ENTAME",
"EX-Taishu",
"SPA! Magazine",
"Young Gangan",
"UTB",
"Young Animal",
"Young Champion",
"Big Comic Spirtis",
"Shonen Magazine",
"BUBKA",
"BOMB",
"Shonen Champion",
"Manga Action",
"Weekly Shonen Sunday",
"Photobooks",
"BRODY",
"Hustle Press",
"ANAN Magazine",
"SMART Magazine",
"Young Sunday",
"Gravure The Television",
"CD&DL My Girl",
"Daily LoGiRL",
"Shukan Taishu",
"Girls! Magazine",
"Soccer Game King",
"Weekly Georgia",
"Sunday Magazine",
"Mery Magazine",
)
}

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -0,0 +1,62 @@
package eu.kanade.tachiyomi.extension.ja.micmicidol
import eu.kanade.tachiyomi.multisrc.gravureblogger.GravureBlogger
class MicMicIdol : GravureBlogger("MIC MIC IDOL", "https://www.micmicidol.club", "ja") {
override val labelFilters = buildMap {
put("Type", getTypes())
put("Japan Magazine", getJapanMagazines())
put("Japan Fashion", getJapanFashion())
}
private fun getJapanMagazines() = listOf(
"cyzo",
"EnTame",
"EX大衆",
"Friday",
"Flash",
"Shonen Magazine",
"Shonen Sunday",
"Weekly Shonen Champion",
"Weekly Big Comic Spirits",
"Weekly Jitsuwa",
"Weekly Playboy",
"Weekly SPA!",
"Young Animal",
"Young Champion",
"Young Gangan",
"Young Jump",
"Young Magazine",
)
private fun getJapanFashion() = listOf(
"andGIRL",
"aR",
"Baila",
"Biteki",
"CanCam",
"Classy",
"ELLE Japan",
"Ginger",
"JJ",
"Maquia",
"Mina",
"MORE",
"Non-no",
"Oggi",
"Ray",
"Scawaii",
"Steady",
"ViVi",
"VoCE",
"With",
)
private fun getTypes() = listOf(
"- Cover",
"- Japan Magazine",
"- Japan Fashion Magazine",
"- Japan Idol Photobook",
"- Asia Idol",
)
}

View File

@ -1,62 +0,0 @@
package eu.kanade.tachiyomi.extension.es.atlantisscan
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 AtlantisScan : Madara(
"Atlantis Scan",
"https://scansatlanticos.com",
"es",
dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.US),
) {
override val id: Long = 2237642340381856331
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(2, 1, 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

@ -8,7 +8,7 @@ import java.util.concurrent.TimeUnit
class Jiangzaitoon : Madara( class Jiangzaitoon : Madara(
"Jiangzaitoon", "Jiangzaitoon",
"https://jiangzaitoon.cc", "https://jiangzaitoon.info",
"tr", "tr",
SimpleDateFormat("d MMM yyy", Locale("tr")), SimpleDateFormat("d MMM yyy", Locale("tr")),
) { ) {

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: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,30 @@
package eu.kanade.tachiyomi.extension.tr.mangagezgini
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.source.model.SChapter
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
class MangaGezgini : Madara(
"MangaGezgini",
"https://mangagezgini.com",
"tr",
SimpleDateFormat("dd/MM/yyy", Locale("tr")),
) {
override fun chapterFromElement(element: Element): SChapter {
val chapter = SChapter.create()
with(element) {
select(chapterUrlSelector).first()?.let { urlElement ->
chapter.url = urlElement.attr("abs:href").let {
it.substringBefore("?style=paged") + if (!it.endsWith(chapterUrlSuffix)) chapterUrlSuffix else ""
}
chapter.name = element.select("li.wp-manga-chapter.has-thumb a").text()
}
chapter.date_upload = select("img:not(.thumb)").firstOrNull()?.attr("alt")?.let { parseRelativeDate(it) }
?: select("span a").firstOrNull()?.attr("title")?.let { parseRelativeDate(it) }
?: parseChapterDate(select(chapterDateSelector()).firstOrNull()?.text())
}
return chapter
}
}

View File

@ -11,11 +11,11 @@ import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class Mangalink : Madara("مانجا لينك", "https://manga-link.net", "ar", SimpleDateFormat("MMMM dd, yyyy", Locale("ar"))) { class Mangalink : Madara("مانجا لينك", "https://manga-link.com", "ar", SimpleDateFormat("MMMM dd, yyyy", Locale("ar"))) {
override val chapterUrlSuffix = "" override val chapterUrlSuffix = ""
private val defaultBaseUrl = "https://manga-link.net" private val defaultBaseUrl = "https://manga-link.com"
override val baseUrl by lazy { getPrefBaseUrl() } override val baseUrl by lazy { getPrefBaseUrl() }
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,12 @@
package eu.kanade.tachiyomi.extension.tr.mangawow
import eu.kanade.tachiyomi.multisrc.madara.Madara
import java.text.SimpleDateFormat
import java.util.Locale
class MangaWOW : Madara(
"MangaWOW",
"https://mangawow.org",
"tr",
SimpleDateFormat("MMMM dd, yyyy", Locale("tr")),
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,21 @@
package eu.kanade.tachiyomi.extension.pt.ninjascan
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import java.text.SimpleDateFormat
import java.util.Locale
class NinjaScan : Madara(
"Ninja Scan",
"https://ninjascan.site",
"pt-BR",
SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")),
) {
override val client = super.client.newBuilder()
.rateLimit(2)
.build()
override val useNewChapterEndpoint = true
override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/"
}

View File

@ -6,10 +6,12 @@ import java.util.Locale
class SamuraiScan : Madara( class SamuraiScan : Madara(
"SamuraiScan", "SamuraiScan",
"https://samuraiscan.com", "https://samuraiscan.org",
"es", "es",
SimpleDateFormat("MMMM d, yyyy", Locale("es")), SimpleDateFormat("d MMMM, yyyy", Locale("es")),
) { ) {
override val mangaSubString = "l"
override val useNewChapterEndpoint = true override val useNewChapterEndpoint = true
override val mangaDetailsSelectorDescription = "div.summary_content div.manga-summary" override val mangaDetailsSelectorDescription = "div.summary_content div.manga-summary"

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,15 @@
package eu.kanade.tachiyomi.extension.en.stonescape
import eu.kanade.tachiyomi.multisrc.madara.Madara
import java.text.SimpleDateFormat
import java.util.Locale
class StoneScape : Madara(
"StoneScape",
"https://stonescape.xyz",
"en",
SimpleDateFormat("MMMM dd, yyyy", Locale("en")),
) {
override val mangaSubString = "series"
override val chapterUrlSelector = "div + a"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 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: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,21 @@
package eu.kanade.tachiyomi.extension.pt.sussyscan
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import java.text.SimpleDateFormat
import java.util.Locale
class SussyScan : Madara(
"Sussy Scan",
"https://sussyscan.com",
"pt-BR",
SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
) {
override val client = super.client.newBuilder()
.rateLimit(2)
.build()
override val useNewChapterEndpoint = true
override val mangaSubString = "sus"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 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,8 @@
package eu.kanade.tachiyomi.extension.zh.hanman18
import eu.kanade.tachiyomi.multisrc.manga18.Manga18
class HANMAN18 : Manga18("HANMAN18", "https://hanman18.com", "zh") {
// tag filter doesn't work on site
override val getAvailableTags = false
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,16 @@
package eu.kanade.tachiyomi.extension.en.hentai3zcc
import eu.kanade.tachiyomi.multisrc.manga18.Manga18
import eu.kanade.tachiyomi.source.model.SManga
import org.jsoup.nodes.Element
class Hentai3zCC : Manga18("Hentai3z.CC", "https://hentai3z.cc", "en") {
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
title = element.selectFirst("div.mg_info > div.mg_name a")!!.text()
thumbnail_url = element.selectFirst("img")?.absUrl("src")
?.replace("cover_thumb_2.webp", "cover_250x350.jpg")
?.replace("admin.manga18.us", "bk.18porncomic.com")
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 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: 18 KiB

View File

@ -0,0 +1,251 @@
package eu.kanade.tachiyomi.extension.ja.rawotaku
import eu.kanade.tachiyomi.multisrc.mangareader.MangaReader
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
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.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.nodes.TextNode
import org.jsoup.select.Evaluator
import rx.Observable
import java.net.URLEncoder
class RawOtaku : MangaReader() {
override val name = "Raw Otaku"
override val lang = "ja"
override val baseUrl = "https://rawotaku.com"
override val client = network.cloudflareClient.newBuilder()
.rateLimit(2)
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
// ============================== Popular ===============================
override fun popularMangaRequest(page: Int) =
GET("$baseUrl/filter/?type=all&status=all&language=all&sort=most-viewed&p=$page", headers)
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int) =
GET("$baseUrl/filter/?type=all&status=all&language=all&sort=latest-updated&p=$page", headers)
// =============================== Search ===============================
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = baseUrl.toHttpUrl().newBuilder().apply {
if (query.isNotBlank()) {
addQueryParameter("q", query)
} else {
addPathSegment("filter")
addPathSegment("")
filters.ifEmpty(::getFilterList).forEach { filter ->
when (filter) {
is TypeFilter -> {
addQueryParameter(filter.param, filter.selection)
}
is StatusFilter -> {
addQueryParameter(filter.param, filter.selection)
}
is LanguageFilter -> {
addQueryParameter(filter.param, filter.selection)
}
is SortFilter -> {
addQueryParameter(filter.param, filter.selection)
}
is GenresFilter -> {
filter.state.forEach {
if (it.state) {
addQueryParameter(filter.param, it.id)
}
}
}
else -> { }
}
}
}
addQueryParameter("p", page.toString())
}.build()
return GET(url, headers)
}
override fun searchMangaSelector() = ".manga_list-sbs .manga-poster"
override fun searchMangaFromElement(element: Element) =
SManga.create().apply {
setUrlWithoutDomain(element.attr("href"))
element.selectFirst(Evaluator.Tag("img"))!!.let {
title = it.attr("alt")
thumbnail_url = it.imgAttr()
}
}
override fun searchMangaNextPageSelector() = "ul.pagination > li.active + li"
// =============================== Filters ==============================
override fun getFilterList() =
FilterList(
Note,
Filter.Separator(),
TypeFilter(),
StatusFilter(),
LanguageFilter(),
SortFilter(),
GenresFilter(),
)
// =========================== Manga Details ============================
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
val root = document.selectFirst(Evaluator.Id("ani_detail"))!!
val mangaTitle = root.selectFirst(Evaluator.Class("manga-name"))!!.ownText()
title = mangaTitle
description = buildString {
root.selectFirst(".description")?.ownText()?.let { append(it) }
append("\n\n")
root.selectFirst(".manga-name-or")?.ownText()?.let {
if (it.isNotEmpty() && it != mangaTitle) {
append("Alternative Title: ")
append(it)
}
}
}.trim()
thumbnail_url = root.selectFirst(Evaluator.Tag("img"))!!.imgAttr()
genre = root.selectFirst(Evaluator.Class("genres"))!!.children().joinToString { it.ownText() }
for (item in root.selectFirst(Evaluator.Class("anisc-info"))!!.children()) {
if (item.hasClass("item").not()) continue
when (item.selectFirst(Evaluator.Class("item-head"))!!.ownText()) {
"著者:" -> item.parseAuthorsTo(this)
"地位:" -> status = when (item.selectFirst(Evaluator.Class("name"))!!.ownText().lowercase()) {
"ongoing" -> SManga.ONGOING
"completed" -> SManga.COMPLETED
"on-hold" -> SManga.ON_HIATUS
"canceled" -> SManga.CANCELLED
else -> SManga.UNKNOWN
}
}
}
}
private fun Element.parseAuthorsTo(manga: SManga) {
val authors = select(Evaluator.Tag("a"))
val text = authors.map { it.ownText().replace(",", "") }
val count = authors.size
when (count) {
0 -> return
1 -> {
manga.author = text[0]
return
}
}
val authorList = ArrayList<String>(count)
val artistList = ArrayList<String>(count)
for ((index, author) in authors.withIndex()) {
val textNode = author.nextSibling() as? TextNode
val list = if (textNode != null && "(Art)" in textNode.wholeText) artistList else authorList
list.add(text[index])
}
if (authorList.isEmpty().not()) manga.author = authorList.joinToString()
if (artistList.isEmpty().not()) manga.artist = artistList.joinToString()
}
// ============================== Chapters ==============================
override fun chapterListRequest(mangaUrl: String, type: String): Request =
GET(baseUrl + mangaUrl, headers)
override fun parseChapterElements(response: Response, isVolume: Boolean): List<Element> {
TODO("Not yet implemented")
}
override val chapterType = ""
override val volumeType = ""
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return client.newCall(chapterListRequest(manga))
.asObservableSuccess()
.map(::parseChapterList)
}
private fun parseChapterList(response: Response): List<SChapter> {
val document = response.use { it.asJsoup() }
return document.select(chapterListSelector())
.map(::chapterFromElement)
}
private fun chapterListSelector(): String = "#ja-chaps > .chapter-item"
private fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
val id = element.attr("data-id")
element.selectFirst("a")!!.run {
setUrlWithoutDomain(attr("href") + "#$id")
name = selectFirst(".name")?.text() ?: text()
}
}
// =============================== Pages ================================
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> = Observable.fromCallable {
val id = chapter.url.substringAfterLast("#")
val ajaxHeaders = super.headersBuilder().apply {
add("Accept", "application/json, text/javascript, */*; q=0.01")
add("Referer", URLEncoder.encode(baseUrl + chapter.url.substringBeforeLast("#"), "utf-8"))
add("X-Requested-With", "XMLHttpRequest")
}.build()
val ajaxUrl = "$baseUrl/json/chapter?mode=vertical&id=$id"
client.newCall(GET(ajaxUrl, ajaxHeaders)).execute().let(::pageListParse)
}
override fun pageListParse(response: Response): List<Page> {
val document = response.use { it.parseHtmlProperty() }
val pageList = document.select(".container-reader-chapter > div > img").map {
val index = it.attr("alt").toInt()
val imgUrl = it.imgAttr()
Page(index, imageUrl = imgUrl)
}
return pageList
}
// ============================= Utilities ==============================
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 fun Response.parseHtmlProperty(): Document {
val html = Json.parseToJsonElement(body.string()).jsonObject["html"]!!.jsonPrimitive.content
return Jsoup.parseBodyFragment(html)
}
}

View File

@ -0,0 +1,110 @@
package eu.kanade.tachiyomi.extension.ja.rawotaku
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 TypeFilter(
values: Array<String> = types.keys.toTypedArray(),
) : Select("タイプ", "type", values) {
override val selection: String
get() = types[values[state]]!!
companion object {
private val types = mapOf(
"全て" to "all",
"Raw Manga" to "Raw Manga",
"BLコミック" to "BLコミック",
"TLコミック" to "TLコミック",
"オトナコミック" to "オトナコミック",
"女性マンガ" to "女性マンガ",
"少女マンガ" to "少女マンガ",
"少年マンガ" to "少年マンガ",
"青年マンガ" to "青年マンガ",
)
}
}
class StatusFilter(
values: Array<String> = statuses.keys.toTypedArray(),
) : Select("地位", "status", values) {
override val selection: String
get() = statuses[values[state]]!!
companion object {
private val statuses = mapOf(
"全て" to "all",
"Publishing" to "Publishing",
"Finished" to "Finished",
)
}
}
class LanguageFilter(
values: Array<String> = languages.keys.toTypedArray(),
) : Select("言語", "language", values) {
override val selection: String
get() = languages[values[state]]!!
companion object {
private val languages = mapOf(
"全て" to "all",
"Japanese" to "ja",
"English" to "en",
)
}
}
class SortFilter(
values: Array<String> = sort.keys.toTypedArray(),
) : Select("選別", "sort", values) {
override val selection: String
get() = sort[values[state]]!!
companion object {
private val sort = mapOf(
"デフォルト" to "default",
"最新の更新" to "latest-updated",
"最も見られました" to "most-viewed",
"Title [A-Z]" to "title-az",
"Title [Z-A]" to "title-za",
)
}
}
class Genre(name: String, val id: String) : Filter.CheckBox(name)
class GenresFilter(
values: List<Genre> = genres,
) : Filter.Group<Genre>("ジャンル", values) {
val param = "genre[]"
companion object {
private val genres: List<Genre>
get() = listOf(
Genre("アクション", "55"),
Genre("エッチ", "15706"),
Genre("コメディ", "91"),
Genre("ドラマ", "56"),
Genre("ハーレム", "20"),
Genre("ファンタジー", "1"),
Genre("冒険", "54"),
Genre("悪魔", "6820"),
Genre("武道", "1064"),
Genre("歴史的", "9600"),
Genre("警察・特殊部隊", "6089"),
Genre("車・バイク", "4329"),
Genre("音楽", "473"),
Genre("魔法", "1416"),
)
}
}

View File

@ -0,0 +1,22 @@
package eu.kanade.tachiyomi.extension.es.atlantisscan
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 AtlantisScan : MangaThemesia(
"Atlantis Scan",
"https://scansatlanticos.com",
"es",
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US),
) {
// Site moved from Madara to MangaThemesia
override val versionId = 2
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(2, 1, TimeUnit.SECONDS)
.build()
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,12 @@
package eu.kanade.tachiyomi.extension.id.comicaso
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import java.text.SimpleDateFormat
import java.util.Locale
class Comicaso : MangaThemesia(
"Comicaso",
"https://comicaso.com",
"id",
dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("id")),
)

View File

@ -0,0 +1,27 @@
package eu.kanade.tachiyomi.extension.id.comicsekai
import android.util.Base64
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.source.model.Page
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonPrimitive
import org.jsoup.nodes.Document
class Comicsekai : MangaThemesia("Comicsekai", "http://www.comicsekai.com", "id") {
override fun pageListParse(document: Document): List<Page> {
// "ts_reader.run({" in base64
val script = document.selectFirst("script[src^=data:text/javascript;base64,dHNfcmVhZGVyLnJ1bih7]")
?: return super.pageListParse(document)
val data = Base64.decode(script.attr("src").substringAfter("base64,"), Base64.DEFAULT).toString(Charsets.UTF_8)
val imageListJson = JSON_IMAGE_LIST_REGEX.find(data)?.destructured?.toList()?.get(0).orEmpty()
val imageList = try {
json.parseToJsonElement(imageListJson).jsonArray
} catch (_: IllegalArgumentException) {
emptyList()
}
return imageList.mapIndexed { i, jsonEl ->
Page(i, imageUrl = jsonEl.jsonPrimitive.content)
}
}
}

View File

@ -0,0 +1,12 @@
package eu.kanade.tachiyomi.extension.th.dragonmanga
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import java.text.SimpleDateFormat
import java.util.Locale
class DragonManga : MangaThemesia(
"DragonManga",
"https://www.dragon-manga.com",
"th",
dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("th")),
)

View File

@ -8,7 +8,7 @@ import java.util.Locale
class InariManga : MangaThemesia( class InariManga : MangaThemesia(
"InariManga", "InariManga",
"https://inarimanga.com", "https://inarimanga.net",
"es", "es",
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("en")), dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("en")),
) { ) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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