Update four "all" sources (#1314)
* ComiCake: Some cleanup work Signed-off-by: TacoTheDank <SkytkRSfan3895@gmail.com> * E-Hentai: Some cleanup work Signed-off-by: TacoTheDank <SkytkRSfan3895@gmail.com> * FoolSlide: Some cleanup work and source updates Signed-off-by: TacoTheDank <SkytkRSfan3895@gmail.com> * Genkan: Some cleanup work Signed-off-by: TacoTheDank <SkytkRSfan3895@gmail.com>
This commit is contained in:
parent
27175b3dee
commit
c94cc58e7d
|
@ -3,9 +3,9 @@ apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
appName = 'Tachiyomi: ComiCake'
|
appName = 'Tachiyomi: ComiCake'
|
||||||
pkgNameSuffix = "all.comicake"
|
pkgNameSuffix = 'all.comicake'
|
||||||
extClass = '.ComiCakeFactory'
|
extClass = '.ComiCakeFactory'
|
||||||
extVersionCode = 3
|
extVersionCode = 4
|
||||||
libVersion = '1.2'
|
libVersion = '1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,24 @@ package eu.kanade.tachiyomi.extension.all.comicake
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import eu.kanade.tachiyomi.extension.BuildConfig
|
import eu.kanade.tachiyomi.extension.BuildConfig
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.source.model.*
|
import eu.kanade.tachiyomi.source.model.*
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
const val COMICAKE_DEFAULT_API_ENDPOINT = "/api" // Highly unlikely to change
|
const val COMICAKE_DEFAULT_API_ENDPOINT = "/api" // Highly unlikely to change
|
||||||
const val COMICAKE_DEFAULT_READER_ENDPOINT = "/r" // Can change based on CC config
|
const val COMICAKE_DEFAULT_READER_ENDPOINT = "/r" // Can change based on CC config
|
||||||
|
|
||||||
open class ComiCake(override val name: String, override val baseUrl: String, override val lang: String, val readerEndpoint: String = COMICAKE_DEFAULT_READER_ENDPOINT, val apiEndpoint: String = COMICAKE_DEFAULT_API_ENDPOINT) : HttpSource() {
|
open class ComiCake(override val name: String,
|
||||||
|
final override val baseUrl: String,
|
||||||
|
override val lang: String,
|
||||||
|
readerEndpoint: String = COMICAKE_DEFAULT_READER_ENDPOINT,
|
||||||
|
apiEndpoint: String = COMICAKE_DEFAULT_API_ENDPOINT) : HttpSource() {
|
||||||
override val versionId = 1
|
override val versionId = 1
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
private val readerBase = baseUrl + readerEndpoint
|
private val readerBase = baseUrl + readerEndpoint
|
||||||
|
@ -40,18 +43,18 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
||||||
return getMangasPageFromComicsResponse(res)
|
return getMangasPageFromComicsResponse(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMangasPageFromComicsResponse(json: String, nested: Boolean = false) : MangasPage {
|
private fun getMangasPageFromComicsResponse(json: String, nested: Boolean = false): MangasPage {
|
||||||
var response = JSONObject(json)
|
val response = JSONObject(json)
|
||||||
var results = response.getJSONArray("results")
|
val results = response.getJSONArray("results")
|
||||||
val mangas = ArrayList<SManga>()
|
val mangas = ArrayList<SManga>()
|
||||||
val ids = mutableListOf<Int>();
|
val ids = mutableListOf<Int>()
|
||||||
|
|
||||||
for (i in 0 until results.length()) {
|
for (i in 0 until results.length()) {
|
||||||
val obj = results.getJSONObject(i)
|
val obj = results.getJSONObject(i)
|
||||||
if(nested) {
|
if (nested) {
|
||||||
val nestedComic = obj.getJSONObject("comic");
|
val nestedComic = obj.getJSONObject("comic")
|
||||||
val id = nestedComic.getInt("id")
|
val id = nestedComic.getInt("id")
|
||||||
if(ids.contains(id))
|
if (ids.contains(id))
|
||||||
continue
|
continue
|
||||||
ids.add(id)
|
ids.add(id)
|
||||||
val manga = SManga.create()
|
val manga = SManga.create()
|
||||||
|
@ -60,13 +63,13 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
||||||
mangas.add(manga)
|
mangas.add(manga)
|
||||||
} else {
|
} else {
|
||||||
val id = obj.getInt("id")
|
val id = obj.getInt("id")
|
||||||
if(ids.contains(id))
|
if (ids.contains(id))
|
||||||
continue
|
continue
|
||||||
ids.add(id)
|
ids.add(id)
|
||||||
mangas.add(parseComicJson(obj))
|
mangas.add(parseComicJson(obj))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MangasPage(mangas, if (response.getString("next").isNullOrEmpty() || response.getString("next") == "null") false else true)
|
return MangasPage(mangas, !(response.getString("next").isNullOrEmpty() || response.getString("next") == "null"))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
|
@ -78,11 +81,11 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
||||||
return parseComicJson(comicJson, true)
|
return parseComicJson(comicJson, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseComicJson(obj: JSONObject, human: Boolean = false) = SManga.create().apply {
|
private fun parseComicJson(obj: JSONObject, human: Boolean = false) = SManga.create().apply {
|
||||||
if(human) {
|
url = if (human) {
|
||||||
url = "$readerBase/series/${obj.getString("slug")}/"
|
"$readerBase/series/${obj.getString("slug")}/"
|
||||||
} else {
|
} else {
|
||||||
url = obj.getInt("id").toString() // Yeah, I know... Feel free to improve on this
|
obj.getInt("id").toString() // Yeah, I know... Feel free to improve on this
|
||||||
}
|
}
|
||||||
title = obj.getString("name")
|
title = obj.getString("name")
|
||||||
thumbnail_url = obj.getString("cover")
|
thumbnail_url = obj.getString("cover")
|
||||||
|
@ -93,9 +96,9 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
||||||
status = SManga.UNKNOWN
|
status = SManga.UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseListNames(arr: JSONArray) : String {
|
private fun parseListNames(arr: JSONArray): String {
|
||||||
var hold = ArrayList<String>(arr.length())
|
val hold = ArrayList<String>(arr.length())
|
||||||
for(i in 0 until arr.length())
|
for (i in 0 until arr.length())
|
||||||
hold.add(arr.getJSONObject(i).getString("name"))
|
hold.add(arr.getJSONObject(i).getString("name"))
|
||||||
return hold.sorted().joinToString(", ")
|
return hold.sorted().joinToString(", ")
|
||||||
}
|
}
|
||||||
|
@ -133,7 +136,7 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
val chapterJson = JSONObject(response.body()!!.string())
|
val chapterJson = JSONObject(response.body()!!.string())
|
||||||
var results = chapterJson.getJSONArray("results")
|
val results = chapterJson.getJSONArray("results")
|
||||||
val ret = ArrayList<SChapter>()
|
val ret = ArrayList<SChapter>()
|
||||||
for (i in 0 until results.length()) {
|
for (i in 0 until results.length()) {
|
||||||
ret.add(parseChapterJson(results.getJSONObject(i)))
|
ret.add(parseChapterJson(results.getJSONObject(i)))
|
||||||
|
@ -144,13 +147,13 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
val webPub = JSONObject(response.body()!!.string())
|
val webPub = JSONObject(response.body()!!.string())
|
||||||
val readingOrder = webPub.getJSONArray("readingOrder")
|
val readingOrder = webPub.getJSONArray("readingOrder")
|
||||||
val ret = ArrayList<Page>();
|
val ret = ArrayList<Page>()
|
||||||
for (i in 0 until readingOrder.length()) {
|
for (i in 0 until readingOrder.length()) {
|
||||||
var pageUrl = readingOrder.getJSONObject(i).getString("href")
|
val pageUrl = readingOrder.getJSONObject(i).getString("href")
|
||||||
ret.add(Page(i, "", pageUrl))
|
ret.add(Page(i, "", pageUrl))
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("This method should not be called!")
|
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("This method should not be called!")
|
||||||
}
|
}
|
|
@ -9,11 +9,11 @@ class ComiCakeFactory : SourceFactory {
|
||||||
|
|
||||||
fun getAllComiCake(): List<Source> {
|
fun getAllComiCake(): List<Source> {
|
||||||
return listOf(
|
return listOf(
|
||||||
WhimSubs(),
|
WhimSubs(),
|
||||||
ChampionScans()
|
ChampionScans()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhimSubs : ComiCake("WhimSubs", "https://whimsubs.xyz", "en")
|
class WhimSubs : ComiCake("WhimSubs", "https://whimsubs.xyz", "en")
|
||||||
|
|
||||||
class ChampionScans : ComiCake("Champion Scans", "https://reader.championscans.com", "en", "/")
|
class ChampionScans : ComiCake("Champion Scans", "https://reader.championscans.com", "en", "/")
|
|
@ -4,8 +4,8 @@ apply plugin: 'kotlin-android'
|
||||||
ext {
|
ext {
|
||||||
appName = 'Tachiyomi: E-Hentai'
|
appName = 'Tachiyomi: E-Hentai'
|
||||||
pkgNameSuffix = 'all.ehentai'
|
pkgNameSuffix = 'all.ehentai'
|
||||||
extClass = '.EHJapanese; .EHEnglish; .EHChinese; .EHDutch; .EHFrench; .EHGerman; .EHHungarian; .EHItalian; .EHKorean; .EHPolish; .EHPolish; .EHPortuguese; .EHRussian; .EHSpanish; .EHThai; .EHVietnamese; .EHSpeechless; .EHOther'
|
extClass = '.EHJapanese; .EHEnglish; .EHChinese; .EHDutch; .EHFrench; .EHGerman; .EHHungarian; .EHItalian; .EHKorean; .EHPolish; .EHPortuguese; .EHRussian; .EHSpanish; .EHThai; .EHVietnamese; .EHSpeechless; .EHOther'
|
||||||
extVersionCode = 4
|
extVersionCode = 5
|
||||||
libVersion = '1.2'
|
libVersion = '1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,23 +3,23 @@ package eu.kanade.tachiyomi.extension.all.ehentai
|
||||||
/**
|
/**
|
||||||
* E-Hentai languages
|
* E-Hentai languages
|
||||||
*/
|
*/
|
||||||
class EHJapanese: EHentai("ja", "japanese")
|
class EHJapanese : EHentai("ja", "japanese")
|
||||||
class EHEnglish: EHentai("en", "english")
|
class EHEnglish : EHentai("en", "english")
|
||||||
class EHChinese: EHentai("zh", "chinese")
|
class EHChinese : EHentai("zh", "chinese")
|
||||||
class EHDutch: EHentai("nl", "dutch")
|
class EHDutch : EHentai("nl", "dutch")
|
||||||
class EHFrench: EHentai("fr", "french")
|
class EHFrench : EHentai("fr", "french")
|
||||||
class EHGerman: EHentai("de", "german")
|
class EHGerman : EHentai("de", "german")
|
||||||
class EHHungarian: EHentai("hu", "hungarian")
|
class EHHungarian : EHentai("hu", "hungarian")
|
||||||
class EHItalian: EHentai("it", "italian")
|
class EHItalian : EHentai("it", "italian")
|
||||||
class EHKorean: EHentai("ko", "korean")
|
class EHKorean : EHentai("ko", "korean")
|
||||||
class EHPolish: EHentai("pl", "polish")
|
class EHPolish : EHentai("pl", "polish")
|
||||||
class EHPortuguese: EHentai("pt", "portuguese")
|
class EHPortuguese : EHentai("pt", "portuguese")
|
||||||
class EHRussian: EHentai("ru", "russian")
|
class EHRussian : EHentai("ru", "russian")
|
||||||
class EHSpanish: EHentai("es", "spanish")
|
class EHSpanish : EHentai("es", "spanish")
|
||||||
class EHThai: EHentai("th", "thai")
|
class EHThai : EHentai("th", "thai")
|
||||||
class EHVietnamese: EHentai("vi", "vietnamese")
|
class EHVietnamese : EHentai("vi", "vietnamese")
|
||||||
class EHSpeechless: EHentai("none", "n/a")
|
class EHSpeechless : EHentai("none", "n/a")
|
||||||
class EHOther: EHentai("other", "other")
|
class EHOther : EHentai("other", "other")
|
||||||
|
|
||||||
fun getAllEHentaiLanguages() = listOf(
|
fun getAllEHentaiLanguages() = listOf(
|
||||||
EHJapanese(),
|
EHJapanese(),
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package eu.kanade.tachiyomi.extension.all.ehentai
|
package eu.kanade.tachiyomi.extension.all.ehentai
|
||||||
|
|
||||||
|
import kotlin.math.ln
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Various utility methods used in the E-Hentai source
|
* Various utility methods used in the E-Hentai source
|
||||||
*/
|
*/
|
||||||
|
@ -36,18 +39,18 @@ operator fun StringBuilder.plusAssign(other: String) {
|
||||||
*/
|
*/
|
||||||
fun humanReadableByteCount(bytes: Long, si: Boolean): String {
|
fun humanReadableByteCount(bytes: Long, si: Boolean): String {
|
||||||
val unit = if (si) 1000 else 1024
|
val unit = if (si) 1000 else 1024
|
||||||
if (bytes < unit) return bytes.toString() + " B"
|
if (bytes < unit) return "$bytes B"
|
||||||
val exp = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt()
|
val exp = (ln(bytes.toDouble()) / ln(unit.toDouble())).toInt()
|
||||||
val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i"
|
val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i"
|
||||||
return String.format("%.1f %sB", bytes / Math.pow(unit.toDouble(), exp.toDouble()), pre)
|
return String.format("%.1f %sB", bytes / unit.toDouble().pow(exp.toDouble()), pre)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val KB_FACTOR = 1000
|
private const val KB_FACTOR = 1000
|
||||||
private val KIB_FACTOR = 1024
|
private const val KIB_FACTOR = 1024
|
||||||
private val MB_FACTOR = 1000 * KB_FACTOR
|
private const val MB_FACTOR = 1000 * KB_FACTOR
|
||||||
private val MIB_FACTOR = 1024 * KIB_FACTOR
|
private const val MIB_FACTOR = 1024 * KIB_FACTOR
|
||||||
private val GB_FACTOR = 1000 * MB_FACTOR
|
private const val GB_FACTOR = 1000 * MB_FACTOR
|
||||||
private val GIB_FACTOR = 1024 * MIB_FACTOR
|
private const val GIB_FACTOR = 1024 * MIB_FACTOR
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse human readable size Strings
|
* Parse human readable size Strings
|
||||||
|
@ -64,4 +67,4 @@ fun parseHumanReadableByteCount(arg0: String): Double? {
|
||||||
"KiB" -> return ret * KIB_FACTOR
|
"KiB" -> return ret * KIB_FACTOR
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,15 @@ package eu.kanade.tachiyomi.extension.all.ehentai
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.*
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import okhttp3.CacheControl
|
import okhttp3.*
|
||||||
import okhttp3.CookieJar
|
|
||||||
import okhttp3.Headers
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
|
|
||||||
open class EHentai(override val lang: String, val ehLang: String) : HttpSource() {
|
open class EHentai(override val lang: String, private val ehLang: String) : HttpSource() {
|
||||||
|
|
||||||
override val name = "E-Hentai"
|
override val name = "E-Hentai"
|
||||||
|
|
||||||
|
@ -51,15 +42,13 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
return MangasPage(parsedMangas, hasNextPage)
|
return MangasPage(parsedMangas, hasNextPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>>
|
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = Observable.just(listOf(SChapter.create().apply {
|
||||||
= Observable.just(listOf(SChapter.create().apply {
|
|
||||||
url = manga.url
|
url = manga.url
|
||||||
name = "Chapter"
|
name = "Chapter"
|
||||||
chapter_number = 1f
|
chapter_number = 1f
|
||||||
}))
|
}))
|
||||||
|
|
||||||
override fun fetchPageList(chapter: SChapter)
|
override fun fetchPageList(chapter: SChapter) = fetchChapterPage(chapter, "$baseUrl/${chapter.url}").map {
|
||||||
= fetchChapterPage(chapter, "$baseUrl/${chapter.url}").map {
|
|
||||||
it.mapIndexed { i, s ->
|
it.mapIndexed { i, s ->
|
||||||
Page(i, s)
|
Page(i, s)
|
||||||
}
|
}
|
||||||
|
@ -80,8 +69,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseChapterPage(response: Element)
|
private fun parseChapterPage(response: Element) = with(response) {
|
||||||
= with(response) {
|
|
||||||
select(".gdtm a").map {
|
select(".gdtm a").map {
|
||||||
Pair(it.child(0).attr("alt").toInt(), it.attr("href"))
|
Pair(it.child(0).attr("alt").toInt(), it.attr("href"))
|
||||||
}.sortedBy(Pair<Int, String>::first).map { it.second }
|
}.sortedBy(Pair<Int, String>::first).map { it.second }
|
||||||
|
@ -90,8 +78,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
private fun chapterPageCall(np: String) = client.newCall(chapterPageRequest(np)).asObservableSuccess()
|
private fun chapterPageCall(np: String) = client.newCall(chapterPageRequest(np)).asObservableSuccess()
|
||||||
private fun chapterPageRequest(np: String) = exGet(np, null, headers)
|
private fun chapterPageRequest(np: String) = exGet(np, null, headers)
|
||||||
|
|
||||||
private fun nextPageUrl(element: Element)
|
private fun nextPageUrl(element: Element) = element.select("a[onclick=return false]").last()?.let {
|
||||||
= element.select("a[onclick=return false]").last()?.let {
|
|
||||||
if (it.text() == ">") it.attr("href") else null
|
if (it.text() == ">") it.attr("href") else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,8 +102,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
override fun searchMangaParse(response: Response) = genericMangaParse(response)
|
override fun searchMangaParse(response: Response) = genericMangaParse(response)
|
||||||
override fun latestUpdatesParse(response: Response) = genericMangaParse(response)
|
override fun latestUpdatesParse(response: Response) = genericMangaParse(response)
|
||||||
|
|
||||||
private fun exGet(url: String, page: Int? = null, additionalHeaders: Headers? = null, cache: Boolean = true)
|
private fun exGet(url: String, page: Int? = null, additionalHeaders: Headers? = null, cache: Boolean = true) = GET(page?.let {
|
||||||
= GET(page?.let {
|
|
||||||
addParam(url, "page", (it - 1).toString())
|
addParam(url, "page", (it - 1).toString())
|
||||||
} ?: url, additionalHeaders?.let {
|
} ?: url, additionalHeaders?.let {
|
||||||
val headers = headers.newBuilder()
|
val headers = headers.newBuilder()
|
||||||
|
@ -204,7 +190,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
Tag(it.text().trim(),
|
Tag(it.text().trim(),
|
||||||
it.hasClass("gtl"))
|
it.hasClass("gtl"))
|
||||||
}
|
}
|
||||||
tags.put(namespace, currentTags)
|
tags[namespace] = currentTags
|
||||||
}
|
}
|
||||||
|
|
||||||
//Copy metadata to manga
|
//Copy metadata to manga
|
||||||
|
@ -214,19 +200,15 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListParse(response: Response)
|
override fun chapterListParse(response: Response) = throw UnsupportedOperationException("Unused method was called somehow!")
|
||||||
= throw UnsupportedOperationException("Unused method was called somehow!")
|
|
||||||
|
|
||||||
override fun pageListParse(response: Response)
|
override fun pageListParse(response: Response) = throw UnsupportedOperationException("Unused method was called somehow!")
|
||||||
= throw UnsupportedOperationException("Unused method was called somehow!")
|
|
||||||
|
|
||||||
override fun fetchImageUrl(page: Page)
|
override fun fetchImageUrl(page: Page) = client.newCall(imageUrlRequest(page))
|
||||||
= client.newCall(imageUrlRequest(page))
|
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { realImageUrlParse(it, page) }!!
|
.map { realImageUrlParse(it, page) }!!
|
||||||
|
|
||||||
private fun realImageUrlParse(response: Response, page: Page)
|
private fun realImageUrlParse(response: Response, page: Page) = with(response.asJsoup()) {
|
||||||
= with(response.asJsoup()) {
|
|
||||||
val currentImage = getElementById("img").attr("src")
|
val currentImage = getElementById("img").attr("src")
|
||||||
//TODO We cannot currently do this as page.url is immutable
|
//TODO We cannot currently do this as page.url is immutable
|
||||||
//Each press of the retry button will choose another server
|
//Each press of the retry button will choose another server
|
||||||
|
@ -236,8 +218,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
currentImage
|
currentImage
|
||||||
}!!
|
}!!
|
||||||
|
|
||||||
override fun imageUrlParse(response: Response)
|
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Unused method was called somehow!")
|
||||||
= throw UnsupportedOperationException("Unused method was called somehow!")
|
|
||||||
|
|
||||||
private val cookiesHeader by lazy {
|
private val cookiesHeader by lazy {
|
||||||
val cookies = mutableMapOf<String, String>()
|
val cookies = mutableMapOf<String, String>()
|
||||||
|
@ -253,25 +234,21 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
.flatMap { it.second }
|
.flatMap { it.second }
|
||||||
.joinToString("x")
|
.joinToString("x")
|
||||||
|
|
||||||
cookies.put("uconfig", buildSettings(settings))
|
cookies["uconfig"] = buildSettings(settings)
|
||||||
|
|
||||||
buildCookies(cookies)
|
buildCookies(cookies)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Headers
|
//Headers
|
||||||
override fun headersBuilder()
|
override fun headersBuilder() = super.headersBuilder().add("Cookie", cookiesHeader)!!
|
||||||
= super.headersBuilder().add("Cookie", cookiesHeader)!!
|
|
||||||
|
|
||||||
private fun buildSettings(settings: List<String?>)
|
private fun buildSettings(settings: List<String?>) = settings.filterNotNull().joinToString(separator = "-")
|
||||||
= settings.filterNotNull().joinToString(separator = "-")
|
|
||||||
|
|
||||||
private fun buildCookies(cookies: Map<String, String>)
|
private fun buildCookies(cookies: Map<String, String>) = cookies.entries.joinToString(separator = "; ", postfix = ";") {
|
||||||
= cookies.entries.map {
|
|
||||||
"${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}"
|
"${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}"
|
||||||
}.joinToString(separator = "; ", postfix = ";")
|
}
|
||||||
|
|
||||||
private fun addParam(url: String, param: String, value: String)
|
private fun addParam(url: String, param: String, value: String) = Uri.parse(url)
|
||||||
= Uri.parse(url)
|
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.appendQueryParameter(param, value)
|
.appendQueryParameter(param, value)
|
||||||
.toString()
|
.toString()
|
||||||
|
@ -295,9 +272,9 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
AdvancedGroup()
|
AdvancedGroup()
|
||||||
)
|
)
|
||||||
|
|
||||||
class GenreOption(name: String, val genreId: String) : Filter.CheckBox(name, false), UriFilter {
|
class GenreOption(name: String, private val genreId: String) : Filter.CheckBox(name, false), UriFilter {
|
||||||
override fun addToUri(builder: Uri.Builder) {
|
override fun addToUri(builder: Uri.Builder) {
|
||||||
builder.appendQueryParameter("f_" + genreId, if (state) "1" else "0")
|
builder.appendQueryParameter("f_$genreId", if (state) "1" else "0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +291,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
GenreOption("Misc", "misc")
|
GenreOption("Misc", "misc")
|
||||||
))
|
))
|
||||||
|
|
||||||
class AdvancedOption(name: String, val param: String, defValue: Boolean = false) : Filter.CheckBox(name, defValue), UriFilter {
|
class AdvancedOption(name: String, private val param: String, defValue: Boolean = false) : Filter.CheckBox(name, defValue), UriFilter {
|
||||||
override fun addToUri(builder: Uri.Builder) {
|
override fun addToUri(builder: Uri.Builder) {
|
||||||
if (state)
|
if (state)
|
||||||
builder.appendQueryParameter(param, "on")
|
builder.appendQueryParameter(param, "on")
|
||||||
|
@ -329,7 +306,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
||||||
"5 stars"
|
"5 stars"
|
||||||
)), UriFilter {
|
)), UriFilter {
|
||||||
override fun addToUri(builder: Uri.Builder) {
|
override fun addToUri(builder: Uri.Builder) {
|
||||||
if (state > 0) builder.appendQueryParameter("f_srdd", Integer.toString(state + 1))
|
if (state > 0) builder.appendQueryParameter("f_srdd", (state + 1).toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.extension.all.ehentai;
|
package eu.kanade.tachiyomi.extension.all.ehentai
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
|
||||||
|
@ -32,25 +32,21 @@ class ExGalleryMetadata {
|
||||||
val tags: MutableMap<String, List<Tag>> = mutableMapOf()
|
val tags: MutableMap<String, List<Tag>> = mutableMapOf()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun splitGalleryUrl(url: String)
|
private fun splitGalleryUrl(url: String) = url.let {
|
||||||
= url.let {
|
|
||||||
//Only parse URL if is full URL
|
//Only parse URL if is full URL
|
||||||
val pathSegments = if(it.startsWith("http"))
|
val pathSegments = if (it.startsWith("http"))
|
||||||
Uri.parse(it).pathSegments
|
Uri.parse(it).pathSegments
|
||||||
else
|
else
|
||||||
it.split('/')
|
it.split('/')
|
||||||
pathSegments.filterNot(String::isNullOrBlank)
|
pathSegments.filterNot(String::isNullOrBlank)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun galleryId(url: String) = splitGalleryUrl(url)[1]
|
private fun galleryId(url: String) = splitGalleryUrl(url)[1]
|
||||||
|
|
||||||
fun galleryToken(url: String) =
|
private fun galleryToken(url: String) = splitGalleryUrl(url)[2]
|
||||||
splitGalleryUrl(url)[2]
|
|
||||||
|
|
||||||
fun normalizeUrl(id: String, token: String)
|
private fun normalizeUrl(id: String, token: String) = "/g/$id/$token/?nw=always"
|
||||||
= "/g/$id/$token/?nw=always"
|
|
||||||
|
|
||||||
fun normalizeUrl(url: String)
|
fun normalizeUrl(url: String) = normalizeUrl(galleryId(url), galleryToken(url))
|
||||||
= normalizeUrl(galleryId(url), galleryToken(url))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,8 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
|
||||||
manga.status = SManga.COMPLETED
|
manga.status = SManga.COMPLETED
|
||||||
title?.let { t ->
|
title?.let { t ->
|
||||||
if (ONGOING_SUFFIX.any {
|
if (ONGOING_SUFFIX.any {
|
||||||
t.endsWith(it, ignoreCase = true)
|
t.endsWith(it, ignoreCase = true)
|
||||||
}) manga.status = SManga.ONGOING
|
}) manga.status = SManga.ONGOING
|
||||||
}
|
}
|
||||||
|
|
||||||
//Build a nice looking description out of what we know
|
//Build a nice looking description out of what we know
|
||||||
|
@ -71,8 +71,7 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
|
||||||
.joinToString(separator = "\n")
|
.joinToString(separator = "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildTagsDescription(metadata: ExGalleryMetadata)
|
private fun buildTagsDescription(metadata: ExGalleryMetadata) = StringBuilder("Tags:\n").apply {
|
||||||
= StringBuilder("Tags:\n").apply {
|
|
||||||
//BiConsumer only available in Java 8, we have to use destructuring here
|
//BiConsumer only available in Java 8, we have to use destructuring here
|
||||||
metadata.tags.forEach { (namespace, tags) ->
|
metadata.tags.forEach { (namespace, tags) ->
|
||||||
if (tags.isNotEmpty()) {
|
if (tags.isNotEmpty()) {
|
||||||
|
|
|
@ -3,9 +3,9 @@ apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
appName = 'Tachiyomi: FoolSlide'
|
appName = 'Tachiyomi: FoolSlide'
|
||||||
pkgNameSuffix = "all.foolslide"
|
pkgNameSuffix = 'all.foolslide'
|
||||||
extClass = '.FoolSlideFactory'
|
extClass = '.FoolSlideFactory'
|
||||||
extVersionCode = 21
|
extVersionCode = 22
|
||||||
libVersion = '1.2'
|
libVersion = '1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,10 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
open class FoolSlide(override val name: String, override val baseUrl: String, override val lang: String, val urlModifier: String = "") : ParsedHttpSource() {
|
open class FoolSlide(override val name: String,
|
||||||
|
override val baseUrl: String,
|
||||||
|
override val lang: String,
|
||||||
|
val urlModifier: String = "") : ParsedHttpSource() {
|
||||||
|
|
||||||
protected open val dedupeLatestUpdates = true
|
protected open val dedupeLatestUpdates = true
|
||||||
|
|
||||||
|
@ -32,7 +35,7 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||||
val mp = super.latestUpdatesParse(response)
|
val mp = super.latestUpdatesParse(response)
|
||||||
return if(dedupeLatestUpdates) {
|
return if (dedupeLatestUpdates) {
|
||||||
val mangas = mp.mangas.distinctBy { it.url }.filterNot { latestUpdatesUrls.contains(it.url) }
|
val mangas = mp.mangas.distinctBy { it.url }.filterNot { latestUpdatesUrls.contains(it.url) }
|
||||||
latestUpdatesUrls.addAll(mangas.map { it.url })
|
latestUpdatesUrls.addAll(mangas.map { it.url })
|
||||||
MangasPage(mangas, mp.hasNextPage)
|
MangasPage(mangas, mp.hasNextPage)
|
||||||
|
@ -96,8 +99,7 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
||||||
|
|
||||||
override fun searchMangaNextPageSelector() = "a:has(span.next)"
|
override fun searchMangaNextPageSelector() = "a:has(span.next)"
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga)
|
override fun mangaDetailsRequest(manga: SManga) = allowAdult(super.mangaDetailsRequest(manga))
|
||||||
= allowAdult(super.mangaDetailsRequest(manga))
|
|
||||||
|
|
||||||
open val mangaDetailsInfoSelector = "div.info"
|
open val mangaDetailsInfoSelector = "div.info"
|
||||||
open val mangaDetailsThumbnailSelector = "div.thumbnail img"
|
open val mangaDetailsThumbnailSelector = "div.thumbnail img"
|
||||||
|
@ -119,14 +121,13 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
||||||
*/
|
*/
|
||||||
private fun allowAdult(request: Request) = allowAdult(request.url().toString())
|
private fun allowAdult(request: Request) = allowAdult(request.url().toString())
|
||||||
|
|
||||||
protected fun allowAdult(url: String): Request {
|
private fun allowAdult(url: String): Request {
|
||||||
return POST(url, body = FormBody.Builder()
|
return POST(url, body = FormBody.Builder()
|
||||||
.add("adult", "true")
|
.add("adult", "true")
|
||||||
.build())
|
.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga)
|
override fun chapterListRequest(manga: SManga) = allowAdult(super.chapterListRequest(manga))
|
||||||
= allowAdult(super.chapterListRequest(manga))
|
|
||||||
|
|
||||||
override fun chapterListSelector() = "div.group div.element, div.list div.element"
|
override fun chapterListSelector() = "div.group div.element, div.list div.element"
|
||||||
|
|
||||||
|
@ -140,7 +141,8 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
||||||
val chapter = SChapter.create()
|
val chapter = SChapter.create()
|
||||||
chapter.setUrlWithoutDomain(urlElement.attr("href"))
|
chapter.setUrlWithoutDomain(urlElement.attr("href"))
|
||||||
chapter.name = urlElement.text()
|
chapter.name = urlElement.text()
|
||||||
chapter.date_upload = dateElement.text()?.let { parseChapterDate(it.substringAfter(", ")) } ?: 0
|
chapter.date_upload = dateElement.text()?.let { parseChapterDate(it.substringAfter(", ")) }
|
||||||
|
?: 0
|
||||||
return chapter
|
return chapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,18 +174,18 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
||||||
|
|
||||||
var result = DATE_FORMAT_1.parseOrNull(date)
|
var result = DATE_FORMAT_1.parseOrNull(date)
|
||||||
|
|
||||||
for(dateFormat in DATE_FORMATS_WITH_ORDINAL_SUFFIXES) {
|
for (dateFormat in DATE_FORMATS_WITH_ORDINAL_SUFFIXES) {
|
||||||
if (result == null)
|
if (result == null)
|
||||||
result = dateFormat.parseOrNull(date)
|
result = dateFormat.parseOrNull(date)
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
for(dateFormat in DATE_FORMATS_WITH_ORDINAL_SUFFIXES_NO_YEAR) {
|
for (dateFormat in DATE_FORMATS_WITH_ORDINAL_SUFFIXES_NO_YEAR) {
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = dateFormat.parseOrNull(date)
|
result = dateFormat.parseOrNull(date)
|
||||||
|
|
||||||
if(result != null) {
|
if (result != null) {
|
||||||
// Result parsed but no year, copy current year over
|
// Result parsed but no year, copy current year over
|
||||||
result = Calendar.getInstance().apply {
|
result = Calendar.getInstance().apply {
|
||||||
time = result
|
time = result
|
||||||
|
@ -230,13 +232,12 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
||||||
private fun SimpleDateFormat.parseOrNull(string: String): Date? {
|
private fun SimpleDateFormat.parseOrNull(string: String): Date? {
|
||||||
return try {
|
return try {
|
||||||
parse(string)
|
parse(string)
|
||||||
} catch(e: ParseException) {
|
} catch (e: ParseException) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter)
|
override fun pageListRequest(chapter: SChapter) = allowAdult(super.pageListRequest(chapter))
|
||||||
= allowAdult(super.pageListRequest(chapter))
|
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
val doc = document.toString()
|
val doc = document.toString()
|
||||||
|
@ -266,4 +267,4 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
||||||
SimpleDateFormat("dd'$it' MMMM", Locale.US)
|
SimpleDateFormat("dd'$it' MMMM", Locale.US)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,8 @@ import com.google.gson.JsonParser
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceFactory
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
import eu.kanade.tachiyomi.source.model.*
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
|
@ -17,34 +18,33 @@ class FoolSlideFactory : SourceFactory {
|
||||||
|
|
||||||
fun getAllFoolSlide(): List<Source> {
|
fun getAllFoolSlide(): List<Source> {
|
||||||
return listOf(
|
return listOf(
|
||||||
JaminisBox(),
|
JaminisBox(),
|
||||||
HelveticaScans(),
|
HelveticaScans(),
|
||||||
SenseScans(),
|
SenseScans(),
|
||||||
KireiCake(),
|
KireiCake(),
|
||||||
SilentSky(),
|
SilentSky(),
|
||||||
Mangatellers(),
|
Mangatellers(),
|
||||||
IskultripScans(),
|
IskultripScans(),
|
||||||
AnataNoMotokare(),
|
AnataNoMotokare(),
|
||||||
DeathTollScans(),
|
DeathTollScans(),
|
||||||
DKThias(),
|
DKThias(),
|
||||||
WorldThree(),
|
WorldThree(),
|
||||||
DokiFansubs(),
|
DokiFansubs(),
|
||||||
YuriIsm(),
|
YuriIsm(),
|
||||||
AjiaNoScantrad(),
|
AjiaNoScantrad(),
|
||||||
OneTimeScans(),
|
OneTimeScans(),
|
||||||
TsubasaSociety(),
|
TsubasaSociety(),
|
||||||
MangaScouts(),
|
MangaScouts(),
|
||||||
StormInHeaven(),
|
StormInHeaven(),
|
||||||
Lilyreader(),
|
Lilyreader(),
|
||||||
MidnightHaven(),
|
Russification(),
|
||||||
Russification(),
|
EvilFlowers(),
|
||||||
EvilFlowers(),
|
AkaiYuhiMunTeam(),
|
||||||
AkaiYuhiMunTeam(),
|
LupiTeam(),
|
||||||
LupiTeam(),
|
HentaiCafe(),
|
||||||
HentaiCafe(),
|
ShoujoSense(),
|
||||||
ShoujoSense(),
|
TheCatScans(),
|
||||||
TheCatScans(),
|
ShoujoHearts()
|
||||||
ShoujoHearts()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ class SenseScans : FoolSlide("Sense-Scans", "https://sensescans.com", "en", "/re
|
||||||
|
|
||||||
class KireiCake : FoolSlide("Kirei Cake", "https://reader.kireicake.com", "en")
|
class KireiCake : FoolSlide("Kirei Cake", "https://reader.kireicake.com", "en")
|
||||||
|
|
||||||
class SilentSky : FoolSlide("Silent Sky", "http://reader.silentsky-scans.net", "en")
|
class SilentSky : FoolSlide("Silent Sky", "https://reader.silentsky-scans.net", "en")
|
||||||
|
|
||||||
class Mangatellers : FoolSlide("Mangatellers", "http://www.mangatellers.gr", "en", "/reader/reader") {
|
class Mangatellers : FoolSlide("Mangatellers", "http://www.mangatellers.gr", "en", "/reader/reader") {
|
||||||
override fun popularMangaRequest(page: Int): Request {
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
|
@ -108,12 +108,10 @@ class TsubasaSociety : FoolSlide("Tsubasa Society", "https://www.tsubasasociety.
|
||||||
|
|
||||||
class MangaScouts : FoolSlide("MangaScouts", "http://onlinereader.mangascouts.org", "de")
|
class MangaScouts : FoolSlide("MangaScouts", "http://onlinereader.mangascouts.org", "de")
|
||||||
|
|
||||||
class StormInHeaven : FoolSlide("Storm in Heaven", "http://www.storm-in-heaven.net", "it", "/reader-sih")
|
class StormInHeaven : FoolSlide("Storm in Heaven", "https://www.storm-in-heaven.net", "it", "/reader-sih")
|
||||||
|
|
||||||
class Lilyreader : FoolSlide("Lilyreader", "https://manga.smuglo.li", "en")
|
class Lilyreader : FoolSlide("Lilyreader", "https://manga.smuglo.li", "en")
|
||||||
|
|
||||||
class MidnightHaven : FoolSlide("Midnight Haven", "http://midnighthaven.shounen-ai.net", "en", "/reader")
|
|
||||||
|
|
||||||
class Russification : FoolSlide("Русификация", "https://rusmanga.ru", "ru")
|
class Russification : FoolSlide("Русификация", "https://rusmanga.ru", "ru")
|
||||||
|
|
||||||
class EvilFlowers : FoolSlide("Evil Flowers", "http://reader.evilflowers.com", "en")
|
class EvilFlowers : FoolSlide("Evil Flowers", "http://reader.evilflowers.com", "en")
|
||||||
|
@ -158,5 +156,3 @@ class ShoujoHearts : FoolSlide("ShoujoHearts", "http://shoujohearts.com", "en",
|
||||||
return manga
|
return manga
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
||||||
}
|
}
|
||||||
|
|
||||||
filters.findInstance<ArtistFilter>()?.let { f ->
|
filters.findInstance<ArtistFilter>()?.let { f ->
|
||||||
if(f.state.isNotBlank()) {
|
if (f.state.isNotBlank()) {
|
||||||
requireNoUrl()
|
requireNoUrl()
|
||||||
url = "/artist/${f.state
|
url = "/artist/${f.state
|
||||||
.trim()
|
.trim()
|
||||||
|
@ -72,19 +72,19 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filters.findInstance<BookFilter>()?.let { f ->
|
filters.findInstance<BookFilter>()?.let { f ->
|
||||||
if(f.state) {
|
if (f.state) {
|
||||||
requireNoUrl()
|
requireNoUrl()
|
||||||
url = "/category/book/"
|
url = "/category/book/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filters.findInstance<TagFilter>()?.let { f ->
|
filters.findInstance<TagFilter>()?.let { f ->
|
||||||
if(f.state != 0) {
|
if (f.state != 0) {
|
||||||
requireNoUrl()
|
requireNoUrl()
|
||||||
url = "/tag/${f.values[f.state].name}/"
|
url = "/tag/${f.values[f.state].name}/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(query.isNotBlank()) {
|
if (query.isNotBlank()) {
|
||||||
requireNoUrl()
|
requireNoUrl()
|
||||||
url = "/"
|
url = "/"
|
||||||
queryString = "s=" + URLEncoder.encode(query, "UTF-8")
|
queryString = "s=" + URLEncoder.encode(query, "UTF-8")
|
||||||
|
@ -97,8 +97,8 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
||||||
|
|
||||||
private fun pagedRequest(url: String, page: Int, queryString: String? = null): Request {
|
private fun pagedRequest(url: String, page: Int, queryString: String? = null): Request {
|
||||||
// The site redirects page 1 -> url-without-page so we do this redirect early for optimization
|
// The site redirects page 1 -> url-without-page so we do this redirect early for optimization
|
||||||
val builtUrl = if(page == 1) url else "${url}page/$page/"
|
val builtUrl = if (page == 1) url else "${url}page/$page/"
|
||||||
return GET(if(queryString != null) "$builtUrl?$queryString" else builtUrl)
|
return GET(if (queryString != null) "$builtUrl?$queryString" else builtUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
|
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
|
||||||
|
@ -106,7 +106,7 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||||
return client.newCall(searchMangaRequest(page, query, filters))
|
return client.newCall(searchMangaRequest(page, query, filters))
|
||||||
.asObservable().doOnNext { response ->
|
.asObservable().doOnNext { response ->
|
||||||
if(!response.isSuccessful) {
|
if (!response.isSuccessful) {
|
||||||
response.close()
|
response.close()
|
||||||
// Better error message for invalid artist
|
// Better error message for invalid artist
|
||||||
if (response.code() == 404
|
if (response.code() == 404
|
||||||
|
@ -132,7 +132,6 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
||||||
class ArtistFilter : Filter.Text("Artist (must be exact match)")
|
class ArtistFilter : Filter.Text("Artist (must be exact match)")
|
||||||
class BookFilter : Filter.CheckBox("Show books only", false)
|
class BookFilter : Filter.CheckBox("Show books only", false)
|
||||||
class TagFilter : Filter.Select<Tag>("Tag", arrayOf(
|
class TagFilter : Filter.Select<Tag>("Tag", arrayOf(
|
||||||
Tag("all", "All"),
|
|
||||||
Tag("ahegao", "Ahegao"),
|
Tag("ahegao", "Ahegao"),
|
||||||
Tag("anal", "Anal"),
|
Tag("anal", "Anal"),
|
||||||
Tag("big-ass", "Big ass"),
|
Tag("big-ass", "Big ass"),
|
||||||
|
@ -155,9 +154,9 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
||||||
Tag("group", "Group"),
|
Tag("group", "Group"),
|
||||||
Tag("hairy", "Hairy"),
|
Tag("hairy", "Hairy"),
|
||||||
Tag("handjob", "Handjob"),
|
Tag("handjob", "Handjob"),
|
||||||
|
Tag("heart-pupils", "Heart pupils"),
|
||||||
Tag("housewife", "Housewife"),
|
Tag("housewife", "Housewife"),
|
||||||
Tag("incest", "Incest"),
|
Tag("incest", "Incest"),
|
||||||
Tag("large-breast", "Large breast"),
|
|
||||||
Tag("lingerie", "Lingerie"),
|
Tag("lingerie", "Lingerie"),
|
||||||
Tag("loli", "Loli"),
|
Tag("loli", "Loli"),
|
||||||
Tag("masturbation", "Masturbation"),
|
Tag("masturbation", "Masturbation"),
|
||||||
|
@ -167,7 +166,7 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
||||||
Tag("pettanko", "Pettanko"),
|
Tag("pettanko", "Pettanko"),
|
||||||
Tag("rape", "Rape"),
|
Tag("rape", "Rape"),
|
||||||
Tag("schoolgirl", "Schoolgirl"),
|
Tag("schoolgirl", "Schoolgirl"),
|
||||||
Tag("sex-toys", "Sex Toys"),
|
Tag("sex-toys", "Sex toys"),
|
||||||
Tag("shota", "Shota"),
|
Tag("shota", "Shota"),
|
||||||
Tag("socks", "Socks"),
|
Tag("socks", "Socks"),
|
||||||
Tag("stocking", "Stocking"),
|
Tag("stocking", "Stocking"),
|
||||||
|
@ -175,11 +174,12 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
||||||
Tag("swimsuit", "Swimsuit"),
|
Tag("swimsuit", "Swimsuit"),
|
||||||
Tag("teacher", "Teacher"),
|
Tag("teacher", "Teacher"),
|
||||||
Tag("tsundere", "Tsundere"),
|
Tag("tsundere", "Tsundere"),
|
||||||
Tag("uncensored", "uncensored"),
|
Tag("uncensored", "Uncensored"),
|
||||||
Tag("vanilla", "Vanilla"),
|
Tag("vanilla", "Vanilla"),
|
||||||
Tag("x-ray", "X-ray")
|
Tag("x-ray", "X-Ray")
|
||||||
))
|
))
|
||||||
class Tag(val name: String, val displayName: String) {
|
|
||||||
|
class Tag(val name: String, private val displayName: String) {
|
||||||
override fun toString() = displayName
|
override fun toString() = displayName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,4 +189,4 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified T> Iterable<*>.findInstance() = find { it is T } as? T
|
private inline fun <reified T> Iterable<*>.findInstance() = find { it is T } as? T
|
|
@ -5,7 +5,7 @@ ext {
|
||||||
appName = 'Tachiyomi: Genkan (multiple sources)'
|
appName = 'Tachiyomi: Genkan (multiple sources)'
|
||||||
pkgNameSuffix = 'all.genkan'
|
pkgNameSuffix = 'all.genkan'
|
||||||
extClass = '.GenkanFactory'
|
extClass = '.GenkanFactory'
|
||||||
extVersionCode = 2
|
extVersionCode = 3
|
||||||
libVersion = '1.2'
|
libVersion = '1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,9 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
abstract class Genkan(
|
abstract class Genkan(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val baseUrl: String,
|
final override val baseUrl: String,
|
||||||
override val lang: String
|
override val lang: String
|
||||||
) : ParsedHttpSource() {
|
) : ParsedHttpSource() {
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
|
@ -27,7 +27,7 @@ abstract class Genkan(
|
||||||
|
|
||||||
private val popularMangaUrl = "$baseUrl/comics?page=" // Search is also based off this val
|
private val popularMangaUrl = "$baseUrl/comics?page=" // Search is also based off this val
|
||||||
override fun popularMangaRequest(page: Int): Request {
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
return GET("$popularMangaUrl$page")
|
return GET("$popularMangaUrl$page")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun latestUpdatesSelector() = popularMangaSelector()
|
override fun latestUpdatesSelector() = popularMangaSelector()
|
||||||
|
@ -36,8 +36,8 @@ abstract class Genkan(
|
||||||
private val latestUpdatesTitles = mutableSetOf<String>()
|
private val latestUpdatesTitles = mutableSetOf<String>()
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request {
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
if (page == 1) latestUpdatesTitles.clear()
|
if (page == 1) latestUpdatesTitles.clear()
|
||||||
return GET("$baseUrl/latest?page=$page")
|
return GET("$baseUrl/latest?page=$page")
|
||||||
}
|
}
|
||||||
|
|
||||||
// To prevent dupes
|
// To prevent dupes
|
||||||
|
@ -165,10 +165,10 @@ abstract class Genkan(
|
||||||
|
|
||||||
// If the date string contains the word "ago" send it off for relative date parsing otherwise use dateFormat
|
// If the date string contains the word "ago" send it off for relative date parsing otherwise use dateFormat
|
||||||
private fun parseChapterDate(string: String): Long? {
|
private fun parseChapterDate(string: String): Long? {
|
||||||
if ("ago" in string) {
|
return if ("ago" in string) {
|
||||||
return parseRelativeDate(string) ?: 0
|
parseRelativeDate(string) ?: 0
|
||||||
} else {
|
} else {
|
||||||
return dateFormat.parse(string).time
|
dateFormat.parse(string).time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,14 +177,14 @@ abstract class Genkan(
|
||||||
val trimmedDate = date.substringBefore(" ago").removeSuffix("s").split(" ")
|
val trimmedDate = date.substringBefore(" ago").removeSuffix("s").split(" ")
|
||||||
|
|
||||||
val calendar = Calendar.getInstance()
|
val calendar = Calendar.getInstance()
|
||||||
when (trimmedDate[1]){
|
when (trimmedDate[1]) {
|
||||||
"month" -> calendar.apply{add(Calendar.MONTH, -trimmedDate[0].toInt())}
|
"month" -> calendar.apply { add(Calendar.MONTH, -trimmedDate[0].toInt()) }
|
||||||
"week" -> calendar.apply{add(Calendar.WEEK_OF_MONTH, -trimmedDate[0].toInt())}
|
"week" -> calendar.apply { add(Calendar.WEEK_OF_MONTH, -trimmedDate[0].toInt()) }
|
||||||
"day" -> calendar.apply{add(Calendar.DAY_OF_MONTH, -trimmedDate[0].toInt())}
|
"day" -> calendar.apply { add(Calendar.DAY_OF_MONTH, -trimmedDate[0].toInt()) }
|
||||||
"hour" -> calendar.apply{add(Calendar.HOUR_OF_DAY, -trimmedDate[0].toInt())}
|
"hour" -> calendar.apply { add(Calendar.HOUR_OF_DAY, -trimmedDate[0].toInt()) }
|
||||||
"minute" -> calendar.apply{add(Calendar.MINUTE, -trimmedDate[0].toInt())}
|
"minute" -> calendar.apply { add(Calendar.MINUTE, -trimmedDate[0].toInt()) }
|
||||||
"second" -> calendar.apply{add(Calendar.SECOND, 0)}
|
"second" -> calendar.apply { add(Calendar.SECOND, 0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return calendar.timeInMillis
|
return calendar.timeInMillis
|
||||||
}
|
}
|
||||||
|
@ -192,10 +192,10 @@ abstract class Genkan(
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
val pages = mutableListOf<Page>()
|
val pages = mutableListOf<Page>()
|
||||||
|
|
||||||
val allImages = document.select("div#pages-container + script").first().data()
|
val allImages = document.select("div#pages-container + script").first().data()
|
||||||
.substringAfter("[").substringBefore("];")
|
.substringAfter("[").substringBefore("];")
|
||||||
.replace(Regex("""["\\]"""), "")
|
.replace(Regex("""["\\]"""), "")
|
||||||
.split(",")
|
.split(",")
|
||||||
|
|
||||||
for (i in 0 until allImages.size) {
|
for (i in 0 until allImages.size) {
|
||||||
pages.add(Page(i, "", allImages[i]))
|
pages.add(Page(i, "", allImages[i]))
|
||||||
|
@ -207,5 +207,4 @@ abstract class Genkan(
|
||||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
|
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
|
||||||
|
|
||||||
override fun getFilterList() = FilterList()
|
override fun getFilterList() = FilterList()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,12 @@ import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
|
|
||||||
class GenkanFactory : SourceFactory {
|
class GenkanFactory : SourceFactory {
|
||||||
override fun createSources(): List<Source> = listOf(
|
override fun createSources(): List<Source> = listOf(
|
||||||
LeviatanScans(),
|
LeviatanScans(),
|
||||||
PsychoPlay(),
|
PsychoPlay(),
|
||||||
OneShotScans(),
|
OneShotScans(),
|
||||||
KaguyaDex(),
|
KaguyaDex(),
|
||||||
KomiScans(),
|
KomiScans(),
|
||||||
HunlightScans())
|
HunlightScans())
|
||||||
}
|
}
|
||||||
|
|
||||||
class LeviatanScans : Genkan("Leviatan Scans", "https://leviatanscans.com", "en")
|
class LeviatanScans : Genkan("Leviatan Scans", "https://leviatanscans.com", "en")
|
||||||
|
|
Loading…
Reference in New Issue