Compare commits
	
		
			No commits in common. "c5c6d77479d473b7acd5251d603d224de1d31132" and "1699cece9f9f17f1033819a005c68b403f0d703c" have entirely different histories.
		
	
	
		
			c5c6d77479
			...
			1699cece9f
		
	
		
| @ -1,12 +0,0 @@ | ||||
| plugins { | ||||
|     `java-library` | ||||
|     kotlin("jvm") | ||||
| } | ||||
| 
 | ||||
| repositories { | ||||
|     mavenCentral() | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     compileOnly(libs.kotlin.stdlib) | ||||
| } | ||||
| @ -1,294 +0,0 @@ | ||||
| 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 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| <?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> | ||||
| @ -1,3 +0,0 @@ | ||||
| dependencies { | ||||
|     implementation(project(":lib:synchrony")) | ||||
| } | ||||
| Before Width: | Height: | Size: 2.2 KiB | 
| Before Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 2.7 KiB | 
| Before Width: | Height: | Size: 5.0 KiB | 
| Before Width: | Height: | Size: 6.0 KiB | 
| @ -1,123 +0,0 @@ | ||||
| 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"), | ||||
|         ), | ||||
|     ) | ||||
| } | ||||
| Before Width: | Height: | Size: 2.3 KiB | 
| Before Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 3.4 KiB | 
| Before Width: | Height: | Size: 6.2 KiB | 
| Before Width: | Height: | Size: 8.6 KiB | 
| @ -1,120 +0,0 @@ | ||||
| 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,9 +13,6 @@ import rx.Observable | ||||
| import java.util.Calendar | ||||
| 
 | ||||
| class Manga1000 : FMReader("Manga1000", "https://manga1000.top", "ja") { | ||||
| 
 | ||||
|     override val infoElementSelector = "div.row div.row" | ||||
| 
 | ||||
|     // source is picky about URL format | ||||
|     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) | ||||
| @ -28,7 +25,7 @@ class Manga1000 : FMReader("Manga1000", "https://manga1000.top", "ja") { | ||||
|     override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { | ||||
|         val slug = manga.url.substringAfter("manga-").substringBefore(".html") | ||||
| 
 | ||||
|         return client.newCall(GET("$baseUrl/app/manga/controllers/cont.Listchapter.php?slug=$slug", headers)) | ||||
|         return client.newCall(GET("$baseUrl/app/manga/controllers/cont.Listchapterapi.php?slug=$slug", headers)) | ||||
|             .asObservableSuccess() | ||||
|             .map { res -> | ||||
|                 res.asJsoup().select(".at-series a").map { | ||||
|  | ||||
| Before Width: | Height: | Size: 3.1 KiB | 
| Before Width: | Height: | Size: 1.6 KiB | 
| Before Width: | Height: | Size: 3.4 KiB | 
| Before Width: | Height: | Size: 6.3 KiB | 
| Before Width: | Height: | Size: 8.6 KiB | 
| @ -1,96 +0,0 @@ | ||||
| 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", | ||||
|     ) | ||||
| } | ||||
| @ -1,62 +0,0 @@ | ||||
| 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", | ||||
|     ) | ||||
| } | ||||
| 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 | 
							
								
								
									
										62
									
								
								multisrc/overrides/madara/atlantisscan/src/AtlantisScan.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,62 @@ | ||||
| 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( | ||||
|     "Jiangzaitoon", | ||||
|     "https://jiangzaitoon.info", | ||||
|     "https://jiangzaitoon.cc", | ||||
|     "tr", | ||||
|     SimpleDateFormat("d MMM yyy", Locale("tr")), | ||||
| ) { | ||||
|  | ||||
| Before Width: | Height: | Size: 6.7 KiB | 
| Before Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 9.7 KiB | 
| Before Width: | Height: | Size: 19 KiB | 
| Before Width: | Height: | Size: 28 KiB | 
| @ -1,30 +0,0 @@ | ||||
| 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.util.Locale | ||||
| 
 | ||||
| class Mangalink : Madara("مانجا لينك", "https://manga-link.com", "ar", SimpleDateFormat("MMMM dd, yyyy", Locale("ar"))) { | ||||
| class Mangalink : Madara("مانجا لينك", "https://manga-link.net", "ar", SimpleDateFormat("MMMM dd, yyyy", Locale("ar"))) { | ||||
| 
 | ||||
|     override val chapterUrlSuffix = "" | ||||
| 
 | ||||
|     private val defaultBaseUrl = "https://manga-link.com" | ||||
|     private val defaultBaseUrl = "https://manga-link.net" | ||||
|     override val baseUrl by lazy { getPrefBaseUrl() } | ||||
| 
 | ||||
|     private val preferences: SharedPreferences by lazy { | ||||
|  | ||||
| Before Width: | Height: | Size: 7.5 KiB | 
| Before Width: | Height: | Size: 3.8 KiB | 
| Before Width: | Height: | Size: 11 KiB | 
| Before Width: | Height: | Size: 22 KiB | 
| Before Width: | Height: | Size: 36 KiB | 
| @ -1,12 +0,0 @@ | ||||
| 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")), | ||||
| ) | ||||
| Before Width: | Height: | Size: 4.6 KiB | 
| Before Width: | Height: | Size: 2.7 KiB | 
| Before Width: | Height: | Size: 7.0 KiB | 
| Before Width: | Height: | Size: 13 KiB | 
| Before Width: | Height: | Size: 20 KiB | 
| @ -1,21 +0,0 @@ | ||||
| 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,12 +6,10 @@ import java.util.Locale | ||||
| 
 | ||||
| class SamuraiScan : Madara( | ||||
|     "SamuraiScan", | ||||
|     "https://samuraiscan.org", | ||||
|     "https://samuraiscan.com", | ||||
|     "es", | ||||
|     SimpleDateFormat("d MMMM, yyyy", Locale("es")), | ||||
|     SimpleDateFormat("MMMM d, yyyy", Locale("es")), | ||||
| ) { | ||||
| 
 | ||||
|     override val mangaSubString = "l" | ||||
|     override val useNewChapterEndpoint = true | ||||
| 
 | ||||
|     override val mangaDetailsSelectorDescription = "div.summary_content div.manga-summary" | ||||
|  | ||||
| Before Width: | Height: | Size: 7.0 KiB | 
| Before Width: | Height: | Size: 3.6 KiB | 
| Before Width: | Height: | Size: 11 KiB | 
| Before Width: | Height: | Size: 22 KiB | 
| Before Width: | Height: | Size: 35 KiB | 
| @ -1,15 +0,0 @@ | ||||
| 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" | ||||
| } | ||||
| Before Width: | Height: | Size: 5.7 KiB | 
| Before Width: | Height: | Size: 3.0 KiB | 
| Before Width: | Height: | Size: 8.7 KiB | 
| Before Width: | Height: | Size: 17 KiB | 
| Before Width: | Height: | Size: 28 KiB | 
| @ -1,21 +0,0 @@ | ||||
| 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" | ||||
| } | ||||
| Before Width: | Height: | Size: 6.2 KiB | 
| Before Width: | Height: | Size: 3.4 KiB | 
| Before Width: | Height: | Size: 9.9 KiB | 
| Before Width: | Height: | Size: 19 KiB | 
| Before Width: | Height: | Size: 29 KiB | 
| @ -1,8 +0,0 @@ | ||||
| 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 | ||||
| } | ||||
| Before Width: | Height: | Size: 9.6 KiB | 
| Before Width: | Height: | Size: 4.6 KiB | 
| Before Width: | Height: | Size: 16 KiB | 
| Before Width: | Height: | Size: 32 KiB | 
| Before Width: | Height: | Size: 53 KiB | 
| @ -1,16 +0,0 @@ | ||||
| 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") | ||||
|     } | ||||
| } | ||||
| Before Width: | Height: | Size: 5.4 KiB | 
| Before Width: | Height: | Size: 3.0 KiB | 
| Before Width: | Height: | Size: 7.1 KiB | 
| Before Width: | Height: | Size: 13 KiB | 
| Before Width: | Height: | Size: 18 KiB | 
| @ -1,251 +0,0 @@ | ||||
| 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) | ||||
|     } | ||||
| } | ||||
| @ -1,110 +0,0 @@ | ||||
| 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"), | ||||
|             ) | ||||
|     } | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| 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() | ||||
| } | ||||
| Before Width: | Height: | Size: 2.9 KiB | 
| Before Width: | Height: | Size: 1.6 KiB | 
| Before Width: | Height: | Size: 3.9 KiB | 
| Before Width: | Height: | Size: 6.7 KiB | 
| Before Width: | Height: | Size: 9.4 KiB | 
| @ -1,12 +0,0 @@ | ||||
| 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")), | ||||
| ) | ||||
| @ -1,27 +0,0 @@ | ||||
| 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) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,12 +0,0 @@ | ||||
| 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( | ||||
|     "InariManga", | ||||
|     "https://inarimanga.net", | ||||
|     "https://inarimanga.com", | ||||
|     "es", | ||||
|     dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("en")), | ||||
| ) { | ||||
|  | ||||
| Before Width: | Height: | Size: 1.8 KiB | 
| Before Width: | Height: | Size: 978 B | 
| Before Width: | Height: | Size: 2.3 KiB | 
| Before Width: | Height: | Size: 4.3 KiB | 
| Before Width: | Height: | Size: 6.0 KiB | 
| @ -1,12 +0,0 @@ | ||||
| package eu.kanade.tachiyomi.extension.pt.irisscanlator | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia | ||||
| import java.text.SimpleDateFormat | ||||
| import java.util.Locale | ||||
| 
 | ||||
| class IrisScanlator : MangaThemesia( | ||||
|     "Iris Scanlator", | ||||
|     "https://irisscanlator.com.br", | ||||
|     "pt-BR", | ||||
|     dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("pt", "BR")), | ||||
| ) | ||||
| Before Width: | Height: | Size: 3.7 KiB | 
| Before Width: | Height: | Size: 2.0 KiB |