Compare commits
45 Commits
1699cece9f
...
c5c6d77479
Author | SHA1 | Date |
---|---|---|
bapeey | c5c6d77479 | |
Luka Mamukashvili | 2556e117be | |
Fermín Cirella | 925136fc15 | |
bapeey | c8ca375c1c | |
bapeey | 5b640b1512 | |
bapeey | 6850a721c0 | |
mohamedotaku | 8bb508d679 | |
mohamedotaku | cf5299c188 | |
mohamedotaku | 591c65a534 | |
Cuong M. Tran | 731145443b | |
MikeZeDev | bc4df5d008 | |
beerpsi | f71938e357 | |
Mike | 712c3a75be | |
beerpsi | f09c11a01c | |
Mike | 244fd8f4fa | |
beerpsi | 3db5f6edc2 | |
mohamedotaku | e54743b6c9 | |
mohamedotaku | cdd332ee78 | |
beerpsi | 49c3517510 | |
beerpsi | 4682cc8752 | |
AwkwardPeak7 | 879eb629b1 | |
Secozzi | d8f4f38676 | |
Mike | 95d3671f3d | |
Secozzi | c8f24dac99 | |
Secozzi | aae877a2a3 | |
mohamedotaku | bd0b4b0edd | |
beerpsi | 47e328dc84 | |
Mike | 8dd884535b | |
LLecca | 457475fc07 | |
mohamedotaku | 82983fb1b9 | |
LLecca | ea90594583 | |
LLecca | b78d35db07 | |
AwkwardPeak7 | b2ddfd348b | |
bapeey | 584e00b1dd | |
beerpsi | 2f244a72ff | |
bapeey | 7746e2a3fa | |
AlphaBoom | fb88e2ce97 | |
AwkwardPeak7 | 48c71e342d | |
beerpsi | c8c4110a55 | |
beerpsi | 23e385128e | |
beerpsi | b0b32918e1 | |
beerpsi | c02bd36b43 | |
Mike | fe97867cf1 | |
Mike | fdd2c5c6d1 | |
Secozzi | 8e610e1ea9 |
|
@ -0,0 +1,12 @@
|
||||||
|
plugins {
|
||||||
|
`java-library`
|
||||||
|
kotlin("jvm")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly(libs.kotlin.stdlib)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -0,0 +1,3 @@
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":lib:synchrony"))
|
||||||
|
}
|
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 6.0 KiB |
|
@ -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"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 8.6 KiB |
|
@ -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"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 8.6 KiB |
|
@ -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",
|
||||||
|
)
|
||||||
|
}
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
@ -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",
|
||||||
|
)
|
||||||
|
}
|
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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")),
|
||||||
) {
|
) {
|
||||||
|
|
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 28 KiB |
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 36 KiB |
|
@ -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")),
|
||||||
|
)
|
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 20 KiB |
|
@ -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/"
|
||||||
|
}
|
|
@ -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"
|
||||||
|
|
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 35 KiB |
|
@ -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"
|
||||||
|
}
|
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 28 KiB |
|
@ -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"
|
||||||
|
}
|
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 29 KiB |
|
@ -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
|
||||||
|
}
|
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 53 KiB |
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 18 KiB |
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
@ -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()
|
||||||
|
}
|
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 9.4 KiB |
|
@ -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")),
|
||||||
|
)
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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")),
|
||||||
|
)
|
|
@ -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")),
|
||||||
) {
|
) {
|
||||||
|
|
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 978 B |
After Width: | Height: | Size: 2.3 KiB |