Replace JsonParser with kotlinx.serialization in some extensions (#7620)

* Replace JsonParser with kotlinx.serialization.

* Remove wildcard import.

* Remove more usages of JsonParser.
This commit is contained in:
Alessandro Jean 2021-06-14 07:20:05 -03:00 committed by GitHub
parent 71986a1c6e
commit 3f91c5f75e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 638 additions and 519 deletions

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Hitomi.la'
pkgNameSuffix = 'all.hitomi'
extClass = '.HitomiFactory'
extVersionCode = 5
extVersionCode = 6
libVersion = '1.2'
containsNsfw = true
}

View File

@ -2,10 +2,6 @@ package eu.kanade.tachiyomi.extension.all.hitomi
import android.app.Application
import android.content.SharedPreferences
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.ConfigurableSource
@ -16,6 +12,10 @@ 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.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
@ -24,6 +24,7 @@ import rx.Single
import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.util.Locale
import androidx.preference.CheckBoxPreference as AndroidXCheckBoxPreference
import androidx.preference.PreferenceScreen as AndroidXPreferenceScreen
@ -41,6 +42,8 @@ open class Hitomi(override val lang: String, private val nozomiLang: String) : H
override val baseUrl = BASE_URL
private val json: Json by injectLazy()
// Popular
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
@ -271,21 +274,25 @@ open class Hitomi(override val lang: String, private val nozomiLang: String) : H
return GET("$LTN_BASE_URL/galleries/${hlIdFromUrl(chapter.url)}.js")
}
private val jsonParser = JsonParser()
override fun pageListParse(response: Response): List<Page> {
val str = response.body!!.string()
val json = jsonParser.parse(str.removePrefix("var galleryinfo = "))
return json["files"].array.mapIndexed { i, jsonElement ->
val hash = jsonElement["hash"].string
val ext = if (jsonElement["haswebp"].string == "0" || !hitomiAlwaysWebp()) jsonElement["name"].string.split('.').last() else "webp"
val path = if (jsonElement["haswebp"].string == "0" || !hitomiAlwaysWebp()) "images" else "webp"
val jsonRaw = response.body!!.string().removePrefix("var galleryinfo = ")
val jsonResult = json.parseToJsonElement(jsonRaw).jsonObject
return jsonResult["files"]!!.jsonArray.mapIndexed { i, jsonEl ->
val jsonObj = jsonEl.jsonObject
val hash = jsonObj["hash"]!!.jsonPrimitive.content
val hasWebp = jsonObj["haswebp"]!!.jsonPrimitive.content == "0"
val hasAvif = jsonObj["hasavif"]!!.jsonPrimitive.content == "0"
val ext = if (hasWebp || !hitomiAlwaysWebp())
jsonObj["name"]!!.jsonPrimitive.content.substringAfterLast(".") else "webp"
val path = if (hasWebp || !hitomiAlwaysWebp())
"images" else "webp"
val hashPath1 = hash.takeLast(1)
val hashPath2 = hash.takeLast(3).take(2)
// https://ltn.hitomi.la/reader.js
// function make_image_element()
val secondSubdomain = if (jsonElement["haswebp"].string == "0" && jsonElement["hasavif"].string == "0") "b" else "a"
val secondSubdomain = if (hasWebp && hasAvif) "b" else "a"
Page(i, "", "https://${firstSubdomainFromGalleryId(hashPath2)}$secondSubdomain.hitomi.la/$path/$hashPath1/$hashPath2/$hash.$ext")
}

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Home Hero Scans'
pkgNameSuffix = "en.homeheroscans"
extClass = '.HomeHeroScans'
extVersionCode = 3
extVersionCode = 4
libVersion = '1.2'
}

View File

@ -1,29 +1,36 @@
package eu.kanade.tachiyomi.extension.en.homeheroscans
import com.github.salomonbrys.kotson.forEach
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
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 kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
open class HomeHeroScans : HttpSource() {
override val lang = "en"
final override val name = "Home Hero Scans"
override val lang = "en"
final override val baseUrl = "https://hhs.vercel.app"
final override val supportsLatest = false
private val json: Json by injectLazy()
// { seriesId |---> chapter |---> numPages }
private val chapterNumberCache: MutableMap<String, MutableMap<String, Int>> = mutableMapOf()
@ -42,38 +49,39 @@ open class HomeHeroScans : HttpSource() {
}
val memoizedFetchPopularManga = memoizeObservable { page: Int -> super.fetchPopularManga(page) }
// reduce number of times we call their api, user can force a call to api by relaunching the app
override fun fetchPopularManga(page: Int) = memoizedFetchPopularManga(page)
override fun popularMangaRequest(page: Int) = GET("$baseUrl/series.json", headers)
override fun popularMangaParse(response: Response): MangasPage {
val res = JsonParser.parseString(response.body?.string()).asJsonObject
val manga = mutableListOf<SManga>()
res.forEach { s, jsonElement ->
val data = jsonElement.asJsonObject
fun get(k: String) = data[k]?.asString
manga.add(
SManga.create().apply {
artist = get("artist")
author = get("author")
description = get("description")
genre = get("genre")
title = get("title")!!
thumbnail_url = "$baseUrl${get("cover")}"
url = "/series?series=$s"
status = SManga.ONGOING // isn't reported
}
)
// Reduce number of times we call their api, user can force a call to api by relaunching the app
override fun fetchPopularManga(page: Int) = memoizedFetchPopularManga(page)
override fun popularMangaRequest(page: Int) = GET("$baseUrl/series.json", headers)
override fun popularMangaParse(response: Response): MangasPage {
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonObject
val mangaList = jsonResult.entries.map { entry ->
val jsonObj = entry.value.jsonObject
SManga.create().apply {
artist = jsonObj["artist"]!!.jsonPrimitive.content
author = jsonObj["author"]!!.jsonPrimitive.content
description = jsonObj["description"]!!.jsonPrimitive.content
genre = jsonObj["genre"]!!.jsonPrimitive.content
title = jsonObj["title"]!!.jsonPrimitive.content
thumbnail_url = baseUrl + jsonObj["cover"]!!.jsonPrimitive.content
url = "/series?series=" + entry.key
status = SManga.ONGOING
}
}
return MangasPage(manga, false)
return MangasPage(mangaList, hasNextPage = false)
}
// latest
// Latest
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used")
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException("Not used")
// search
// Search
private fun getMangaId(s: String): String? {
return s.toHttpUrlOrNull()?.let { url ->
// allow for trailing slash
@ -89,34 +97,43 @@ open class HomeHeroScans : HttpSource() {
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
if (!query.startsWith(URL_SEARCH_PREFIX))
// site doesn't have a search, so just return the popular page
// Site doesn't have a search, so just return the popular page
return fetchPopularManga(page)
return getMangaId(query.substringAfter(URL_SEARCH_PREFIX))?.let { id ->
fetchBySeriesId(id).map { MangasPage(it, false) }
} ?: Observable.just(MangasPage(emptyList(), false))
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException("Not used")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
throw UnsupportedOperationException("Not used")
override fun searchMangaParse(response: Response) = throw UnsupportedOperationException("Not used")
// chapter list (is paginated),
// Chapter list (is paginated)
override fun chapterListParse(response: Response): List<SChapter> {
return JsonParser.parseString(response.body?.string()!!).asJsonObject["data"].asJsonArray.map {
val chapterData = it.asJsonObject["data"].asJsonObject
fun get(k: String) = chapterData[k].asString
if (chapterNumberCache[get("series")] == null)
chapterNumberCache[get("series")] = mutableMapOf()
chapterNumberCache[get("series")]!![get("chapter")] = get("numPages").toInt()
SChapter.create().apply {
url = "/chapter?series=${get("series")}&ch=${get("chapter")}"
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonObject
name = "Ch. ${get("chapter")} ${get("title")}"
return jsonResult["data"]!!.jsonArray
.map { jsonEl ->
val jsonObj = jsonEl.jsonObject
val chapterData = jsonObj["data"]!!.jsonObject
val series = chapterData["series"]!!.jsonPrimitive.content
val chapter = chapterData["chapter"]!!.jsonPrimitive.content
date_upload = SimpleDateFormat("MM/dd/yyyy").parse(get("date")).time
if (chapterNumberCache[series] == null) {
chapterNumberCache[series] = mutableMapOf()
}
chapter_number = get("chapter").toFloat()
chapterNumberCache[series]!![chapter] = chapterData["numPages"]!!.jsonPrimitive.content.toInt()
SChapter.create().apply {
name = "Ch. $chapter ${chapterData["title"]!!.jsonPrimitive.content}"
chapter_number = chapter.toFloatOrNull() ?: -1f
date_upload = DATE_FORMATTER.parse(chapterData["date"]!!.jsonPrimitive.content)?.time ?: 0L
url = "/chapter?series=$series&ch=$chapter"
}
}
}.sortedByDescending { it.chapter_number }
.sortedByDescending { it.chapter_number }
}
override fun chapterListRequest(manga: SManga): Request {
@ -134,7 +151,8 @@ open class HomeHeroScans : HttpSource() {
} ?: Observable.just(manga)
override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException("Not used")
// default implementation of mangaDetailsRequest has to exist for webview to work
// Default implementation of mangaDetailsRequest has to exist for webview to work
// override fun mangaDetailsRequest(manga: SManga) = throw UnsupportedOperationException("Not used")
// Pages
@ -146,9 +164,9 @@ open class HomeHeroScans : HttpSource() {
return if (chapterPages() != null) {
Observable.just(chapterPages()!!)
} else {
// has side effect of setting numPages in cache
// Has side effect of setting numPages in cache
fetchChapterList(
// super hacky, url is wrong but has query parameter we need
// Super hacky, url is wrong but has query parameter we need
SManga.create().apply { this.url = chapter.url }
).map {
chapterPages()
@ -161,10 +179,14 @@ open class HomeHeroScans : HttpSource() {
}
override fun pageListParse(response: Response) = throw UnsupportedOperationException("Not used")
override fun pageListRequest(chapter: SChapter) = throw UnsupportedOperationException("Not Used")
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used")
override fun imageUrlParse(response: Response): String = ""
companion object {
private val DATE_FORMATTER = SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH)
const val URL_SEARCH_PREFIX = "url:"
}
}

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'HonkaiImpact3'
pkgNameSuffix = 'en.honkaiimpact'
extClass = '.Honkaiimpact'
extVersionCode = 1
extVersionCode = 2
libVersion = '1.2'
}

View File

@ -1,34 +1,39 @@
package eu.kanade.tachiyomi.extension.en.honkaiimpact
import com.github.salomonbrys.kotson.float
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
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.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.float
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.OkHttpClient
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
// Info - Based of BH3
// This is the english version of the site
class Honkaiimpact : ParsedHttpSource() {
// Info - Based of BH3
// This is the english version of the site
override val name = "Honkai Impact 3rd"
override val baseUrl = "https://manga.honkaiimpact3.com"
override val lang = "en"
override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
@ -36,25 +41,33 @@ class Honkaiimpact : ParsedHttpSource() {
.followRedirects(true)
.build()
private val json: Json by injectLazy()
// Popular
override fun popularMangaSelector() = "a[href*=book]"
override fun popularMangaNextPageSelector(): String? = null
override fun popularMangaRequest(page: Int) = GET("$baseUrl/book", headers)
override fun popularMangaFromElement(element: Element) = mangaFromElement(element)
// Latest
override fun latestUpdatesSelector() = throw Exception("Not Used")
override fun latestUpdatesNextPageSelector(): String? = null
override fun latestUpdatesRequest(page: Int) = throw Exception("Not Used")
override fun latestUpdatesFromElement(element: Element) = mangaFromElement(element)
// Search
override fun searchMangaSelector() = throw Exception("Not Used")
override fun searchMangaNextPageSelector(): String? = null
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw Exception("No search")
override fun searchMangaFromElement(element: Element) = mangaFromElement(element)
private fun mangaFromElement(element: Element): SManga {
@ -78,22 +91,20 @@ class Honkaiimpact : ParsedHttpSource() {
override fun chapterListSelector() = throw Exception("Not Used")
override fun chapterFromElement(element: Element) = throw Exception("Not Used")
override fun chapterListRequest(manga: SManga) = GET(baseUrl + manga.url + "/get_chapter", headers)
override fun chapterListParse(response: Response): List<SChapter> {
val jsondata = response.body!!.string()
val json = JsonParser().parse(jsondata).asJsonArray
val chapters = mutableListOf<SChapter>()
json.forEach {
chapters.add(createChapter(it))
}
return chapters
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonArray
return jsonResult.map { jsonEl -> createChapter(jsonEl.jsonObject) }
}
private fun createChapter(json: JsonElement) = SChapter.create().apply {
name = json["title"].string
url = "/book/${json["bookid"].int}/${json["chapterid"].int}"
date_upload = parseDate(json["timestamp"].string)
chapter_number = json["chapterid"].float
private fun createChapter(jsonObj: JsonObject) = SChapter.create().apply {
name = jsonObj["title"]!!.jsonPrimitive.content
url = "/book/${jsonObj["bookid"]!!.jsonPrimitive.int}/${jsonObj["chapterid"]!!.jsonPrimitive.int}"
date_upload = parseDate(jsonObj["timestamp"]!!.jsonPrimitive.content)
chapter_number = jsonObj["chapterid"]!!.jsonPrimitive.float
}
private fun parseDate(date: String): Long {
@ -101,13 +112,12 @@ class Honkaiimpact : ParsedHttpSource() {
}
// Manga Pages
override fun pageListParse(response: Response): List<Page> = mutableListOf<Page>().apply {
val body = response.asJsoup()
body.select("img.lazy.comic_img")?.forEach {
add(Page(size, "", it.attr("data-original")))
override fun pageListParse(document: Document): List<Page> {
return document.select("img.lazy.comic_img").mapIndexed { i, el ->
Page(i, "", el.attr("data-original"))
}
}
override fun pageListParse(document: Document) = throw Exception("Not Used")
override fun imageUrlParse(document: Document) = throw Exception("Not Used")
override fun imageUrlParse(document: Document) = ""
}

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'MangaDog'
pkgNameSuffix = 'en.mangadog'
extClass = '.Mangadog'
extVersionCode = 1
extVersionCode = 2
libVersion = '1.2'
}

View File

@ -1,10 +1,6 @@
package eu.kanade.tachiyomi.extension.en.mangadog
import android.net.Uri
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
@ -13,23 +9,39 @@ 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.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.float
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
class Mangadog : HttpSource() {
override val name = "MangaDog"
override val baseUrl = "https://mangadog.club"
private val cdn = "https://cdn.mangadog.club"
override val lang = "en"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val json: Json by injectLazy()
override fun popularMangaRequest(page: Int) = GET("$baseUrl/index/classification/search_test?page=$page&state=all&demographic=all&genre=all", headers)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/index/latestupdate/getUpdateResult?page=$page", headers)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val uri = Uri.parse("$baseUrl/index/keywordsearch/index").buildUpon()
.appendQueryParameter("query", query)
@ -37,61 +49,49 @@ class Mangadog : HttpSource() {
}
override fun popularMangaParse(response: Response): MangasPage {
// val page = response.request.url.queryParameterValues("page").toString().toInt()
val jsonData = response.body!!.string()
val results = JsonParser().parse(jsonData)
val data = results["data"]["data"]
val mangas = mutableListOf<SManga>()
for (i in 0 until data.asJsonArray.size()) {
mangas.add(popularMangaFromjson(data[i]))
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonObject
val mangaList = jsonResult["data"]!!.jsonObject["data"]!!.jsonArray.map { jsonEl ->
popularMangaFromJson(jsonEl.jsonObject)
}
val hasNextPage = true // page < results["data"]["pageNum"].int
return MangasPage(mangas, hasNextPage)
return MangasPage(mangaList, hasNextPage = true)
}
private fun popularMangaFromjson(json: JsonElement): SManga {
val manga = SManga.create()
manga.title = json["name"].string.trim()
manga.thumbnail_url = cdn + json["image"].string.replace("\\/", "/")
val searchname = json["search_name"].string
val id = json["id"].string
manga.url = "/detail/$searchname/$id.html"
return manga
private fun popularMangaFromJson(jsonObj: JsonObject): SManga = SManga.create().apply {
title = jsonObj["name"]!!.jsonPrimitive.content.trim()
thumbnail_url = cdn + jsonObj["image"]!!.jsonPrimitive.content.replace("\\/", "/")
val searchName = jsonObj["search_name"]!!.jsonPrimitive.content
val id = jsonObj["id"]!!.jsonPrimitive.content
url = "/detail/$searchName/$id.html"
}
override fun latestUpdatesParse(response: Response): MangasPage {
val jsonData = response.body!!.string()
val results = JsonParser().parse(jsonData)
val data = results["data"]
val mangas = mutableListOf<SManga>()
for (i in 0 until data.asJsonArray.size()) {
mangas.add(popularMangaFromjson(data[i]))
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonObject
val mangaList = jsonResult["data"]!!.jsonArray.map { jsonEl ->
popularMangaFromJson(jsonEl.jsonObject)
}
val hasNextPage = true // data.asJsonArray.size()>18
return MangasPage(mangas, hasNextPage)
return MangasPage(mangaList, hasNextPage = true)
}
override fun searchMangaParse(response: Response): MangasPage {
val jsonData = response.body!!.string()
val results = JsonParser().parse(jsonData)
val data = results["suggestions"]
val mangas = mutableListOf<SManga>()
for (i in 0 until 1) {
mangas.add(searchMangaFromjson(data[i]))
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonObject
val mangaList = jsonResult["suggestions"]!!.jsonArray.map { jsonEl ->
searchMangaFromJson(jsonEl.jsonObject)
}
val hasNextPage = false
return MangasPage(mangas, hasNextPage)
return MangasPage(mangaList, hasNextPage = false)
}
private fun searchMangaFromjson(json: JsonElement): SManga {
val manga = SManga.create()
manga.title = json["value"].string.trim()
val data = json["data"].string.replace("\\/", "/")
manga.url = "/detail/$data.html"
return manga
private fun searchMangaFromJson(jsonObj: JsonObject): SManga = SManga.create().apply{
title = jsonObj["value"]!!.jsonPrimitive.content.trim()
val dataValue = jsonObj["data"]!!.jsonPrimitive.content.replace("\\/", "/")
url = "/detail/$dataValue.html"
}
override fun chapterListRequest(manga: SManga): Request {
@ -100,57 +100,51 @@ class Mangadog : HttpSource() {
}
override fun chapterListParse(response: Response): List<SChapter> {
val jsonData = response.body!!.string()
val results = JsonParser().parse(jsonData)
val data = results["data"]["data"]
val chapters = mutableListOf<SChapter>()
for (i in 0 until data.asJsonArray.size()) {
chapters.add(chapterFromjson(data[i]))
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonObject
return jsonResult["data"]!!.jsonObject["data"]!!.jsonArray.map { jsonEl ->
chapterFromJson(jsonEl.jsonObject)
}
return chapters
}
private fun chapterFromjson(json: JsonElement): SChapter {
val chapter = SChapter.create()
val searchname = json["search_name"].string
val id = json["comic_id"].string
chapter.url = "/read/read/$searchname/$id.html" // The url should include the manga name but it doesn't seem to matter
chapter.name = json["name"].string.trim()
chapter.chapter_number = json["obj_id"].asFloat
chapter.date_upload = parseDate(json["create_date"].string)
return chapter
private fun chapterFromJson(jsonObj: JsonObject): SChapter = SChapter.create().apply {
// The url should include the manga name but it doesn't seem to matter
val searchname = jsonObj["search_name"]!!.jsonPrimitive.content
val id = jsonObj["comic_id"]!!.jsonPrimitive.content
url = "/read/read/$searchname/$id.html"
name = jsonObj["name"]!!.jsonPrimitive.content.trim()
chapter_number = jsonObj["obj_id"]!!.jsonPrimitive.float
date_upload = parseDate(jsonObj["create_date"]!!.jsonPrimitive.content)
}
private fun parseDate(date: String): Long {
return SimpleDateFormat("yyyy-MM-dd", Locale.US).parse(date)?.time ?: 0L
}
override fun mangaDetailsParse(response: Response): SManga {
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply{
val document = response.asJsoup()
val manga = SManga.create()
manga.thumbnail_url = document.select("img.detail-post-img").attr("abs:src")
manga.description = document.select("h2.fs15 + p").text().trim()
manga.author = document.select("a[href*=artist]").text()
manga.artist = document.select("a[href*=artist]").text()
val glist = document.select("div.col-sm-10.col-xs-9.text-left.toe.mlr0.text-left-m a[href*=genre]").map { it.text().substringAfter(",").capitalize() }
manga.genre = glist.joinToString(", ")
manga.status = when (document.select("span.label.label-success").first().text()) {
thumbnail_url = document.select("img.detail-post-img").attr("abs:src")
description = document.select("h2.fs15 + p").text().trim()
author = document.select("a[href*=artist]").text()
artist = document.select("a[href*=artist]").text()
genre = document.select("div.col-sm-10.col-xs-9.text-left.toe.mlr0.text-left-m a[href*=genre]")
.joinToString { it.text().substringAfter(",").capitalize(Locale.ROOT) }
status = when (document.select("span.label.label-success").first().text()) {
"update" -> SManga.ONGOING
"finished" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
return manga
}
override fun pageListParse(response: Response): List<Page> {
val body = response.asJsoup()
val pages = mutableListOf<Page>()
val elements = body.select("img[data-src]")
for (i in 0 until elements.size) {
pages.add(Page(i, "", elements[i].select("img").attr("data-src")))
val document = response.asJsoup()
return document.select("img[data-src]").mapIndexed { i, el ->
Page(i, "", el.select("img").attr("data-src"))
}
return pages
}
override fun imageUrlParse(response: Response) = throw Exception("Not used")
override fun imageUrlParse(response: Response) = ""
}

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Mangahasu'
pkgNameSuffix = 'en.mangahasu'
extClass = '.Mangahasu'
extVersionCode = 11
extVersionCode = 12
libVersion = '1.2'
}

View File

@ -1,11 +1,6 @@
package eu.kanade.tachiyomi.extension.en.mangahasu
import android.util.Base64
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
@ -13,12 +8,18 @@ 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.ParsedHttpSource
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
@ -34,9 +35,10 @@ class Mangahasu : ParsedHttpSource() {
override val client: OkHttpClient = network.cloudflareClient
override fun headersBuilder(): Headers.Builder {
return super.headersBuilder().add("Referer", baseUrl)
}
private val json: Json by injectLazy()
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Referer", baseUrl)
override fun popularMangaRequest(page: Int): Request =
GET("$baseUrl/directory.html?page=$page", headers)
@ -143,34 +145,40 @@ class Mangahasu : ParsedHttpSource() {
}
override fun pageListParse(document: Document): List<Page> {
// Grab All Pages from site
// Some images are place holders on new chapters.
val pages = mutableListOf<Page>().apply {
document.select("div.img img").forEach {
val pageNumber = it.attr("class").substringAfter("page").toInt()
add(Page(pageNumber, "", it.attr("src")))
val pageList = document.select("div.img img")
.mapIndexed { i, el ->
val pageNumber = el.attr("class").substringAfter("page").toInt()
Page(pageNumber, "", el.attr("src"))
}
}
.toMutableList()
// Some images are not yet loaded onto Mangahasu's image server.
// Decode temporary URLs and replace placeholder images.
val lstDUrls =
document.select("script:containsData(lstDUrls)").html().substringAfter("lstDUrls")
.substringAfter("\"").substringBefore("\"")
if (lstDUrls != "W10=") { // Base64 = [] or empty file
val decoded = String(Base64.decode(lstDUrls, Base64.DEFAULT))
val json = JsonParser().parse(decoded).array
json.forEach {
val pageNumber = it["page"].int
pages.removeAll { page -> page.index == pageNumber }
pages.add(Page(pageNumber, "", it["url"].string))
val lstDUrls = document.select("script:containsData(lstDUrls)")
.html()
.substringAfter("lstDUrls")
.substringAfter("\"")
.substringBefore("\"")
// Base64 = [] or empty file
if (lstDUrls != "W10=") {
val jsonRaw = String(Base64.decode(lstDUrls, Base64.DEFAULT))
val jsonArr = json.parseToJsonElement(jsonRaw).jsonArray
jsonArr.forEach { jsonEl ->
val jsonObj = jsonEl.jsonObject
val pageNumber = jsonObj["page"]!!.jsonPrimitive.int
pageList.removeAll { page -> page.index == pageNumber }
pageList.add(Page(pageNumber, "", jsonObj["url"]!!.jsonPrimitive.content))
}
}
pages.sortBy { page -> page.index }
return pages
return pageList.sortedBy { page -> page.index }
}
override fun imageUrlParse(document: Document) = ""

View File

@ -1,17 +1,13 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Kumanga'
pkgNameSuffix = 'es.kumanga'
extClass = '.Kumanga'
extVersionCode = 6
extVersionCode = 7
libVersion = '1.2'
}
dependencies {
compileOnly 'com.google.code.gson:gson:2.8.5'
compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0'
}
apply from: "$rootDir/common.gradle"

View File

@ -1,12 +1,7 @@
package eu.kanade.tachiyomi.extension.es.kumanga
import android.util.Base64
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import com.github.salomonbrys.kotson.jsonObject
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.Filter
@ -17,18 +12,33 @@ 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.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.math.roundToInt
class Kumanga : HttpSource() {
override val name = "Kumanga"
override val baseUrl = "https://www.kumanga.com"
override val lang = "es"
override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
.newBuilder()
.followRedirects(true)
@ -45,13 +55,7 @@ class Kumanga : HttpSource() {
}
.build()
override val name = "Kumanga"
override val baseUrl = "https://www.kumanga.com"
override val lang = "es"
override val supportsLatest = false
private val json: Json by injectLazy()
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0")
@ -72,8 +76,10 @@ class Kumanga : HttpSource() {
private fun getKumangaToken(): String {
val body = client.newCall(GET("$baseUrl/mangalist?&page=1", headers)).execute().asJsoup()
var dt = body.select("#searchinput").attr("dt").toString()
var kumangaTokenKey = encodeAndReverse(encodeAndReverse(dt)).replace("=", "k").toLowerCase()
val dt = body.select("#searchinput").attr("dt").toString()
val kumangaTokenKey = encodeAndReverse(encodeAndReverse(dt))
.replace("=", "k")
.toLowerCase(Locale.ROOT)
kumangaToken = body.select("div.input-group [type=hidden]").attr(kumangaTokenKey)
return kumangaToken
}
@ -82,40 +88,28 @@ class Kumanga : HttpSource() {
private fun getMangaUrl(mangaId: String, mangaSlug: String, page: Int) = "/manga/$mangaId/p/$page/$mangaSlug#cl"
private fun parseMangaFromJson(json: JsonElement) = SManga.create().apply {
title = json["name"].string
description = json["description"].string.replace("\\", "")
url = getMangaUrl(json["id"].string, json["slug"].string, 1)
thumbnail_url = getMangaCover(json["id"].string)
val genresArray = json["categories"].array
genre = genresArray.joinToString { jsonObject ->
parseGenresFromJson(jsonObject)
}
private fun parseMangaFromJson(jsonObj: JsonObject) = SManga.create().apply {
title = jsonObj["name"]!!.jsonPrimitive.content
description = jsonObj["description"]!!.jsonPrimitive.content.replace("\\", "")
url = getMangaUrl(jsonObj["id"]!!.jsonPrimitive.content, jsonObj["slug"]!!.jsonPrimitive.content, 1)
thumbnail_url = getMangaCover(jsonObj["id"]!!.jsonPrimitive.content)
genre = jsonObj["categories"]!!.jsonArray
.joinToString { it.jsonObject["name"]!!.jsonPrimitive.content }
}
private fun parseJson(json: String): JsonElement {
return JsonParser().parse(json)
}
private fun parseGenresFromJson(json: JsonElement) = json["name"].string
override fun popularMangaRequest(page: Int): Request {
return POST("$baseUrl/backend/ajax/searchengine.php?page=$page&perPage=10&keywords=&retrieveCategories=true&retrieveAuthors=false&contentType=manga&token=$kumangaToken", headers)
}
override fun popularMangaParse(response: Response): MangasPage {
val res = response.body!!.string()
val json = parseJson(res)
val data = json["contents"].array
val retrievedCount = json["retrievedCount"].int
val hasNextPage = retrievedCount == 10
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonObject
val mangas = data.map { jsonObject ->
parseMangaFromJson(jsonObject)
}
val mangaList = jsonResult["contents"]!!.jsonArray
.map { jsonEl -> parseMangaFromJson(jsonEl.jsonObject) }
return MangasPage(mangas, hasNextPage)
val hasNextPage = jsonResult["retrievedCount"]!!.jsonPrimitive.int == 10
return MangasPage(mangaList, hasNextPage)
}
override fun latestUpdatesRequest(page: Int) = throw Exception("Not Used")
@ -174,20 +168,22 @@ class Kumanga : HttpSource() {
} else throw Exception("No fue posible obtener los capítulos")
}
override fun pageListParse(response: Response): List<Page> = mutableListOf<Page>().apply {
override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup()
var imagesJsonListStr = document.select("script:containsData(var pUrl=)").firstOrNull()?.data()
val imagesJsonRaw = document.select("script:containsData(var pUrl=)").firstOrNull()
?.data()
?.substringAfter("var pUrl=")
?.substringBefore(";")
?.let { decodeBase64(decodeBase64(it).reversed().dropLast(10).drop(10)) }
?: throw Exception("imagesJsonListStr null")
imagesJsonListStr = decodeBase64(decodeBase64(imagesJsonListStr).reversed().dropLast(10).drop(10))
val imagesJsonList = parseJson(imagesJsonListStr).array
imagesJsonList.forEach {
val fakeImageUrl = it["imgURL"].string.replace("\\", "")
val imageUrl = baseUrl + fakeImageUrl
val jsonResult = json.parseToJsonElement(imagesJsonRaw).jsonArray
add(Page(size, "", imageUrl))
return jsonResult.mapIndexed { i, jsonEl ->
val jsonObj = jsonEl.jsonObject
val imagePath = jsonObj["imgURL"]!!.jsonPrimitive.content.replace("\\", "")
Page(i, "", baseUrl + imagePath)
}
}

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'MangaMx'
pkgNameSuffix = 'es.mangamx'
extClass = '.MangaMx'
extVersionCode = 10
extVersionCode = 11
libVersion = '1.2'
}

View File

@ -4,10 +4,6 @@ import android.app.Application
import android.content.SharedPreferences
import android.net.Uri
import android.util.Base64
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.ConfigurableSource
@ -19,6 +15,11 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.FormBody
import okhttp3.Request
import okhttp3.Response
@ -26,6 +27,7 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.nio.charset.Charset
import java.text.SimpleDateFormat
import java.util.Locale
@ -44,6 +46,8 @@ open class MangaMx : ConfigurableSource, ParsedHttpSource() {
private var csrfToken = ""
private val json: Json by injectLazy()
override fun popularMangaRequest(page: Int) = GET(
"$baseUrl/directorio?genero=false" +
"&estado=false&filtro=visitas&tipo=false&adulto=${if (hideNSFWContent()) "0" else "false"}&orden=desc&p=$page",
@ -154,20 +158,22 @@ open class MangaMx : ConfigurableSource, ParsedHttpSource() {
return MangasPage(mangas, hasNextPage)
} else {
val body = response.body!!.string()
if (body == "[]") throw Exception("Término de búsqueda demasiado corto")
val json = JsonParser().parse(body)["mangas"].asJsonArray
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonArray
val mangas = json.map { jsonElement -> searchMangaFromJson(jsonElement) }
val hasNextPage = false
return MangasPage(mangas, hasNextPage)
if (jsonResult.isEmpty()) {
throw Exception("Término de búsqueda demasiado corto")
}
val mangaList = jsonResult.map { jsonEl -> searchMangaFromJson(jsonEl.jsonObject) }
return MangasPage(mangaList, hasNextPage = false)
}
}
private fun searchMangaFromJson(jsonElement: JsonElement): SManga = SManga.create().apply {
title = jsonElement["nombre"].string
setUrlWithoutDomain(jsonElement["url"].string)
thumbnail_url = jsonElement["img"].string.replace("/thumb", "/cover")
private fun searchMangaFromJson(jsonObj: JsonObject): SManga = SManga.create().apply {
title = jsonObj["nombre"]!!.jsonPrimitive.content
thumbnail_url = jsonObj["img"]!!.jsonPrimitive.content.replace("/thumb", "/cover")
setUrlWithoutDomain(jsonObj["url"]!!.jsonPrimitive.content)
}
override fun mangaDetailsParse(document: Document): SManga {

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Japanread'
pkgNameSuffix = 'fr.japanread'
extClass = '.Japanread'
extVersionCode = 7
extVersionCode = 8
libVersion = '1.2'
containsNsfw = true
}

View File

@ -1,8 +1,6 @@
package eu.kanade.tachiyomi.extension.fr.japanread
import android.net.Uri
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.Filter
@ -12,11 +10,16 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.util.Calendar
class Japanread : ParsedHttpSource() {
@ -29,6 +32,8 @@ class Japanread : ParsedHttpSource() {
override val supportsLatest = true
private val json: Json by injectLazy()
// Generic (used by popular/latest/search)
private fun mangaListFromElement(element: Element): SManga {
return SManga.create().apply {
@ -224,20 +229,19 @@ class Japanread : ParsedHttpSource() {
override fun pageListParse(document: Document): List<Page> {
val chapterId = document.select("meta[data-chapter-id]").attr("data-chapter-id")
val apiResponse = client.newCall(GET("$baseUrl/api/?id=$chapterId&type=chapter", apiHeaders())).execute()
val apiRequest = GET("$baseUrl/api/?id=$chapterId&type=chapter", apiHeaders())
val apiResponse = client.newCall(apiRequest).execute()
val jsonData = apiResponse.body!!.string()
val json = JsonParser.parseString(jsonData).asJsonObject
val jsonResult = json.parseToJsonElement(apiResponse.body!!.string()).jsonObject
val baseImagesUrl = json["baseImagesUrl"].string
val baseImagesUrl = jsonResult["baseImagesUrl"]!!.jsonPrimitive.content
return json["page_array"].asJsonArray.mapIndexed { idx, it ->
val imgUrl = "$baseUrl$baseImagesUrl/${it.asString}"
Page(idx, baseUrl, imgUrl)
return jsonResult["page_array"]!!.jsonArray.mapIndexed { i, jsonEl ->
Page(i, baseUrl, "$baseUrl$baseImagesUrl/${jsonEl.jsonPrimitive.content}")
}
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used")
override fun imageUrlParse(document: Document) = ""
override fun imageRequest(page: Page): Request {
return GET(page.imageUrl!!, headers)

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Japscan'
pkgNameSuffix = 'fr.japscan'
extClass = '.Japscan'
extVersionCode = 28
extVersionCode = 29
libVersion = '1.2'
}

View File

@ -18,13 +18,6 @@ import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.string
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.ConfigurableSource
@ -36,6 +29,11 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.FormBody
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
@ -46,6 +44,7 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.text.ParseException
@ -66,6 +65,8 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
override val supportsLatest = true
private val json: Json by injectLazy()
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
@ -173,7 +174,9 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
}
override fun popularMangaNextPageSelector(): String? = null
override fun popularMangaSelector() = "#top_mangas_week li > span"
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
element.select("a").first().let {
@ -201,10 +204,10 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
}
override fun latestUpdatesNextPageSelector(): String? = null
override fun latestUpdatesSelector() = "#chapters > div > h3.text-truncate"
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
private val gson = Gson()
override fun latestUpdatesSelector() = "#chapters > div > h3.text-truncate"
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
// Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
@ -227,20 +230,22 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
.build()
try {
return client.newCall(POST("$baseUrl/live-search/", searchHeaders, formBody)).execute().use { response ->
if (!response.isSuccessful) throw Exception("Unexpected code $response")
val searchRequest = POST("$baseUrl/live-search/", searchHeaders, formBody)
val searchResponse = client.newCall(searchRequest).execute()
val jsonObject = gson.fromJson<JsonObject>(response.body!!.string())
if (jsonObject.asJsonArray.size() === 0) {
Log.d("japscan", "Search not returning anything, using duckduckgo")
throw Exception("No data")
}
return response.request
if (!searchResponse.isSuccessful) {
throw Exception("Unexpected code ${searchResponse.code}")
}
} finally {
val jsonResult = json.parseToJsonElement(searchResponse.body!!.string()).jsonArray
if (jsonResult.isEmpty()) {
Log.d("japscan", "Search not returning anything, using duckduckgo")
throw Exception("No data")
}
return searchRequest
} catch (e: Exception) {
// Fallback to duckduckgo if the search does not return any result
val uri = Uri.parse("https://duckduckgo.com/lite/").buildUpon()
.appendQueryParameter("q", "$query site:$baseUrl/manga/")
@ -250,42 +255,30 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
}
}
override fun searchMangaNextPageSelector(): String? = "li.page-item:last-child:not(li.active),.next_form .navbutton"
override fun searchMangaNextPageSelector(): String = "li.page-item:last-child:not(li.active),.next_form .navbutton"
override fun searchMangaSelector(): String = "div.card div.p-2, a.result-link"
override fun searchMangaParse(response: Response): MangasPage {
if ("live-search" in response.request.url.toString()) {
val body = response.body!!.string()
val json = JsonParser().parse(body).asJsonArray
val mangas = json.map { jsonElement ->
searchMangaFromJson(jsonElement)
}
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonArray
val hasNextPage = false
val mangaList = jsonResult.map { jsonEl -> searchMangaFromJson(jsonEl.jsonObject) }
return MangasPage(mangas, hasNextPage)
} else {
val document = response.asJsoup()
val mangas = document.select(searchMangaSelector()).map { element ->
searchMangaFromElement(element)
}
val hasNextPage = searchMangaNextPageSelector()?.let { selector ->
document.select(selector).first()
} != null
return MangasPage(mangas, hasNextPage)
return MangasPage(mangaList, hasNextPage = false)
}
return super.searchMangaParse(response)
}
override fun searchMangaFromElement(element: Element): SManga {
if (element.attr("class") == "result-link") {
return SManga.create().apply {
return if (element.attr("class") == "result-link") {
SManga.create().apply {
title = element.text().substringAfter(" ").substringBefore(" | JapScan")
setUrlWithoutDomain(element.attr("abs:href"))
}
} else {
return SManga.create().apply {
SManga.create().apply {
thumbnail_url = element.select("img").attr("abs:src")
element.select("p a").let {
title = it.text()
@ -295,9 +288,9 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
}
}
private fun searchMangaFromJson(jsonElement: JsonElement): SManga = SManga.create().apply {
title = jsonElement["name"].string
url = jsonElement["url"].string
private fun searchMangaFromJson(jsonObj: JsonObject): SManga = SManga.create().apply {
title = jsonObj["name"]!!.jsonPrimitive.content
url = jsonObj["url"]!!.jsonPrimitive.content
}
override fun mangaDetailsParse(document: Document): SManga {
@ -365,7 +358,7 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
val checkNew = ArrayList<String>(pagecount)
var maxIter = document.getElementsByTag("option").size
var isSinglePage = false
if ((zjs.toLowerCase().split("new image").size - 1) == 1) {
if ((zjs.toLowerCase(Locale.ROOT).split("new image").size - 1) == 1) {
isSinglePage = true
maxIter = 1
}

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Scan-Manga'
pkgNameSuffix = 'fr.scanmanga'
extClass = '.ScanManga'
extVersionCode = 4
extVersionCode = 5
libVersion = '1.2'
containsNsfw = true
}

View File

@ -1,9 +1,5 @@
package eu.kanade.tachiyomi.extension.fr.scanmanga
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.string
import com.google.gson.Gson
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.FilterList
@ -13,6 +9,10 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
@ -21,6 +21,7 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.parser.Parser
import rx.Observable
import uy.kohesive.injekt.injectLazy
import kotlin.random.Random
class ScanManga : ParsedHttpSource() {
@ -44,7 +45,7 @@ class ScanManga : ParsedHttpSource() {
chain.proceed(newReq)
}.build()!!
private val gson = Gson()
private val json: Json by injectLazy()
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Accept-Language", "fr-FR")
@ -93,7 +94,7 @@ class ScanManga : ParsedHttpSource() {
override fun searchMangaParse(response: Response): MangasPage = parseMangaFromJson(response)
fun shuffle(s: String?): String? {
private fun shuffle(s: String?): String {
val result = StringBuffer(s!!)
var n = result.length
while (n > 1) {
@ -106,10 +107,11 @@ class ScanManga : ParsedHttpSource() {
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val searchHeaders = headersBuilder().apply {
add("Referer", "$baseUrl/scanlation/liste_series.html")
add("x-requested-with", "XMLHttpRequest")
}.build()
val searchHeaders = headersBuilder()
.add("Referer", "$baseUrl/scanlation/liste_series.html")
.add("x-requested-with", "XMLHttpRequest")
.build()
return GET("$baseUrl/scanlation/scan.data.json", searchHeaders)
}
@ -126,58 +128,58 @@ class ScanManga : ParsedHttpSource() {
}
private fun parseMangaFromJson(response: Response): MangasPage {
val jsonData = response.body!!.string()!!
if (jsonData == "") {
return MangasPage(listOf<SManga>(), false)
val jsonRaw = response.body!!.string()
if (jsonRaw.isEmpty()) {
return MangasPage(emptyList(), hasNextPage = false)
}
val jsonObject = JsonParser().parse(jsonData).asJsonObject
val jsonObj = json.parseToJsonElement(jsonRaw).jsonObject
val mangas = jsonObject.keySet()
.map { key ->
// "95","%24100-is-Too-Cheap","0","3","One Shot","","2 avril 2010","","335","178","4010",""
SManga.create().apply {
url = "/" + jsonObject[key][0].string + "/" + jsonObject[key][1].string + ".html"
title = Parser.unescapeEntities(key, false)
genre = jsonObject[key][2].string.let {
when {
it.contains("0") -> "Shōnen"
it.contains("1") -> "Shōjo"
it.contains("2") -> "Seinen"
it.contains("3") -> "Josei"
else -> null
}
}
status = jsonObject[key][3].string.let {
when {
it.contains("0") -> SManga.ONGOING // En cours
it.contains("1") -> SManga.ONGOING // En pause
it.contains("2") -> SManga.COMPLETED // Terminé
it.contains("3") -> SManga.COMPLETED // One shot
else -> SManga.UNKNOWN
}
val mangaList = jsonObj.entries.map { entry ->
SManga.create().apply {
title = Parser.unescapeEntities(entry.key, false)
genre = entry.value.jsonArray[2].jsonPrimitive.content.let {
when {
it.contains("0") -> "Shōnen"
it.contains("1") -> "Shōjo"
it.contains("2") -> "Seinen"
it.contains("3") -> "Josei"
else -> null
}
}
status = entry.value.jsonArray[3].jsonPrimitive.content.let {
when {
it.contains("0") -> SManga.ONGOING // En cours
it.contains("1") -> SManga.ONGOING // En pause
it.contains("2") -> SManga.COMPLETED // Terminé
it.contains("3") -> SManga.COMPLETED // One shot
else -> SManga.UNKNOWN
}
}
url = "/" + entry.value.jsonArray[0].jsonPrimitive.content + "/" +
entry.value.jsonArray[1].jsonPrimitive.content + ".html"
}
}
return MangasPage(mangas, false)
return MangasPage(mangaList, hasNextPage = false)
}
override fun searchMangaSelector() = throw UnsupportedOperationException("Not used")
// Details
override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply {
title = document.select("h2[itemprop=\"name\"]").text()
author = document.select("li[itemprop=\"author\"] a").joinToString { it.text() }
description = document.select("p[itemprop=\"description\"]").text()
thumbnail_url = document.select(".contenu_fiche_technique .image_manga img").attr("src")
}
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
title = document.select("h2[itemprop=\"name\"]").text()
author = document.select("li[itemprop=\"author\"] a").joinToString { it.text() }
description = document.select("p[itemprop=\"description\"]").text()
thumbnail_url = document.select(".contenu_fiche_technique .image_manga img").attr("src")
}
// Chapters
override fun chapterListSelector() = throw Exception("Not used")
override fun chapterFromElement(element: Element): SChapter = throw Exception("Not used")
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
@ -207,9 +209,10 @@ class ScanManga : ParsedHttpSource() {
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used")
override fun imageRequest(page: Page): Request {
val imgHeaders = headersBuilder().apply {
add("Referer", page.url)
}.build()
val imgHeaders = headersBuilder()
.add("Referer", page.url)
.build()
return GET(page.imageUrl!!, imgHeaders)
}
}

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'DigitalTeam'
pkgNameSuffix = 'it.digitalteam'
extClass = '.DigitalTeam'
extVersionCode = 1
extVersionCode = 2
libVersion = '1.2'
containsNsfw = false
}

View File

@ -1,7 +1,5 @@
package eu.kanade.tachiyomi.extension.it.digitalteam
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.FilterList
@ -10,12 +8,18 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Locale
@ -23,117 +27,152 @@ import java.util.Locale
class DigitalTeam : ParsedHttpSource() {
override val name = "DigitalTeam"
override val baseUrl = "https://dgtread.com"
override val lang = "it"
override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val json: Json by injectLazy()
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/reader/series", headers)
}
override fun latestUpdatesRequest(page: Int): Request = popularMangaRequest(page)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = throw Exception("La ricerca è momentaneamente disabilitata.")
// LIST SELECTOR
override fun latestUpdatesRequest(page: Int): Request = popularMangaRequest(page)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
throw Exception("La ricerca è momentaneamente disabilitata.")
// LIST SELECTOR
override fun popularMangaSelector() = "ul li.manga_block"
override fun latestUpdatesSelector() = popularMangaSelector()
override fun searchMangaSelector() = popularMangaSelector()
// ELEMENT
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
manga.thumbnail_url = element.select("img").attr("src")
manga.setUrlWithoutDomain(element.select(".manga_title a").first().attr("href"))
manga.title = element.select(".manga_title a").text()
return manga
// ELEMENT
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
title = element.select(".manga_title a").text()
thumbnail_url = element.select("img").attr("src")
setUrlWithoutDomain(element.select(".manga_title a").first().attr("href"))
}
override fun searchMangaFromElement(element: Element): SManga = throw Exception("Not Used")
override fun latestUpdatesFromElement(element: Element): SManga = throw Exception("Not Used")
// NEXT SELECTOR
// NEXT SELECTOR
// Not needed
override fun popularMangaNextPageSelector(): String? = null
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
// ////////////////
override fun mangaDetailsParse(document: Document): SManga {
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply{
val infoElement = document.select("#manga_left")
val manga = SManga.create()
manga.author = infoElement.select(".info_name:contains(Autore)").next()?.text()
manga.artist = infoElement.select(".info_name:contains(Artista)").next()?.text()
manga.genre = infoElement.select(".info_name:contains(Genere)").next()?.text()
manga.status = parseStatus(infoElement.select(".info_name:contains(Status)").next().text())
manga.description = document.select("div.plot")?.text()
manga.thumbnail_url = infoElement.select(".cover img").attr("src")
return manga
author = infoElement.select(".info_name:contains(Autore)").next()?.text()
artist = infoElement.select(".info_name:contains(Artista)").next()?.text()
genre = infoElement.select(".info_name:contains(Genere)").next()?.text()
status = parseStatus(infoElement.select(".info_name:contains(Status)").next().text())
description = document.select("div.plot")?.text()
thumbnail_url = infoElement.select(".cover img").attr("src")
}
private fun parseStatus(element: String): Int = when {
element.toLowerCase().contains("in corso") -> SManga.ONGOING
element.toLowerCase().contains("completo") -> SManga.COMPLETED
element.toLowerCase(Locale.ROOT).contains("in corso") -> SManga.ONGOING
element.toLowerCase(Locale.ROOT).contains("completo") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun chapterListSelector() = ".chapter_list ul li"
override fun chapterFromElement(element: Element): SChapter {
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
val urlElement = element.select("a").first()
val chapter = SChapter.create()
chapter.setUrlWithoutDomain(urlElement.attr("href"))
chapter.name = urlElement.text()
chapter.date_upload = element.select(".ch_bottom").first()?.text()?.replace("Pubblicato il ", "")?.let {
try {
SimpleDateFormat("dd-MM-yyyy", Locale.ITALY).parse(it).time
} catch (e: ParseException) {
SimpleDateFormat("H", Locale.ITALY).parse(it).time
}
} ?: 0
return chapter
setUrlWithoutDomain(urlElement.attr("href"))
name = urlElement.text()
date_upload = element.select(".ch_bottom").first()?.text()
?.replace("Pubblicato il ", "")
?.let {
try {
DATE_FORMAT_FULL.parse(it)?.time
} catch (e: ParseException) {
DATE_FORMAT_SIMPLE.parse(it)?.time
}
} ?: 0
}
protected fun getXhrPages(script_content: String, title: String): String {
val xhrHeaders = headersBuilder().add("Content-Type: application/x-www-form-urlencoded; charset=UTF-8")
private fun getXhrPages(script_content: String, title: String): String {
val infoManga = script_content.substringAfter("m='").substringBefore("'")
val infoChapter = script_content.substringAfter("ch='").substringBefore("'")
val infoChSub = script_content.substringAfter("chs='").substringBefore("'")
val formBody = FormBody.Builder()
.add("info[manga]", infoManga)
.add("info[chapter]", infoChapter)
.add("info[ch_sub]", infoChSub)
.add("info[title]", title)
.build()
// This can be improved, i don't know how to do it with Regex
var infomanga = script_content.substringAfter("m='").substringBefore("'")
var infochapter = script_content.substringAfter("ch='").substringBefore("'")
var infoch_sub = script_content.substringAfter("chs='").substringBefore("'")
val body = RequestBody.create(null, "info[manga]=$infomanga&info[chapter]=$infochapter&info[ch_sub]=$infoch_sub&info[title]=$title")
return client.newCall(POST("$baseUrl/reader/c_i", xhrHeaders, body))
.execute()
.asJsoup().select("body").text()
.replace("\\", "").removeSurrounding("\"")
val xhrHeaders = headersBuilder()
.add("Content-Length", formBody.contentLength().toString())
.add("Content-Type", formBody.contentType().toString())
.build()
val request = POST("$baseUrl/reader/c_i", xhrHeaders, formBody)
val response = client.newCall(request).execute()
return response.asJsoup()
.select("body")
.text()
.replace("\\", "")
.removeSurrounding("\"")
}
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
val script_content = document.body()!!.toString().substringAfter("current_page=").substringBefore(";")
val scriptContent = document.body().toString()
.substringAfter("current_page=")
.substringBefore(";")
val title = document.select("title").first().text()
val imagesJsonList = JsonParser().parse(getXhrPages(script_content, title)).asJsonArray
val image_url = imagesJsonList.get(2).string
val images_name = imagesJsonList.get(1).asJsonArray
val images_data = imagesJsonList.get(0).asJsonArray
images_name.forEachIndexed { index, imagename ->
val imageUrl =
"$baseUrl/reader$image_url" +
"${images_data.get(index).asJsonObject.get("name").string}" +
"${imagename.string}" +
"${images_data.get(index).asJsonObject.get("ex").string}"
pages.add(Page(index, "", imageUrl))
val xhrPages = getXhrPages(scriptContent, title)
val jsonResult = json.parseToJsonElement(xhrPages).jsonArray
val imageData = jsonResult[0].jsonArray
val imagePath = jsonResult[2].jsonPrimitive.content
return jsonResult[1].jsonArray.mapIndexed { i, jsonEl ->
val imageUrl = "$baseUrl/reader$imagePath" +
imageData[i].jsonObject["name"]!!.jsonPrimitive.content +
jsonEl.jsonPrimitive.content +
imageData[i].jsonObject["ex"]!!.jsonPrimitive.content
Page(i, "", imageUrl)
}
return pages
}
override fun imageUrlParse(document: Document) = ""
override fun imageRequest(page: Page): Request {
val imgHeader = Headers.Builder().apply {
add("User-Agent", "Mozilla/5.0 (Linux; U; Android 4.1.1; en-gb; Build/KLP) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30")
add("Referer", baseUrl)
}.build()
val imgHeader = Headers.Builder()
.add("User-Agent", USER_AGENT)
.add("Referer", baseUrl)
.build()
return GET(page.imageUrl!!, imgHeader)
}
companion object {
private const val USER_AGENT = "Mozilla/5.0 (Linux; U; Android 4.1.1; en-gb; Build/KLP) " +
"AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30"
private val DATE_FORMAT_FULL = SimpleDateFormat("dd-MM-yyyy", Locale.ITALY)
private val DATE_FORMAT_SIMPLE = SimpleDateFormat("H", Locale.ITALY)
}
}

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Mangahub'
pkgNameSuffix = 'ru.mangahub'
extClass = '.Mangahub'
extVersionCode = 9
extVersionCode = 10
libVersion = '1.2'
}

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.extension.ru.mangahub
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.FilterList
@ -8,11 +7,16 @@ 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.ParsedHttpSource
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
@ -26,12 +30,11 @@ open class Mangahub : ParsedHttpSource() {
override val supportsLatest = true
private val rateLimitInterceptor = RateLimitInterceptor(2)
private val jsonParser = JsonParser()
override val client: OkHttpClient = network.client.newBuilder()
.addNetworkInterceptor(rateLimitInterceptor).build()
.addNetworkInterceptor(RateLimitInterceptor(2))
.build()
private val json: Json by injectLazy()
override fun popularMangaRequest(page: Int): Request =
GET("$baseUrl/explore?filter[sort]=rating&filter[dateStart][left_number]=1900&filter[dateStart][right_number]=2099&page=$page", headers)
@ -114,25 +117,25 @@ open class Mangahub : ParsedHttpSource() {
}
override fun pageListParse(document: Document): List<Page> {
val chapInfo = document.select("reader").attr("data-store").replace("&quot;", "\"").replace("\\/", "/")
val chapter = jsonParser.parse(chapInfo).asJsonObject
val scans = chapter["scans"].asJsonArray
val chapInfo = document.select("reader")
.attr("data-store")
.replace("&quot;", "\"")
.replace("\\/", "/")
val chapter = json.parseToJsonElement(chapInfo).jsonObject
val pages = mutableListOf<Page>()
scans.mapIndexed { i, page ->
pages.add(Page(i, "", "https:${page.asJsonObject.get("src").asString}"))
return chapter["scans"]!!.jsonArray.mapIndexed { i, jsonEl ->
Page(i, "", "https:" + jsonEl.jsonObject["src"]!!.jsonPrimitive.content)
}
return pages
}
override fun imageUrlParse(document: Document) = ""
override fun imageRequest(page: Page): Request {
val imgHeader = Headers.Builder().apply {
add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
add("Referer", baseUrl)
}.build()
val imgHeader = Headers.Builder()
.add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
.add("Referer", baseUrl)
.build()
return GET(page.imageUrl!!, imgHeader)
}
}

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'BH3'
pkgNameSuffix = 'zh.bh3'
extClass = '.BH3'
extVersionCode = 2
extVersionCode = 3
libVersion = '1.2'
}

View File

@ -1,22 +1,23 @@
package eu.kanade.tachiyomi.extension.zh.bh3
import com.github.salomonbrys.kotson.float
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
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.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.float
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.OkHttpClient
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
@ -24,35 +25,48 @@ import java.util.concurrent.TimeUnit
class BH3 : ParsedHttpSource() {
override val name = "《崩坏3》IP站"
override val baseUrl = "https://comic.bh3.com"
override val lang = "zh"
override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.retryOnConnectionFailure(true)
.followRedirects(true)
.build()!!
.build()
private val json: Json by injectLazy()
override fun popularMangaSelector() = "a[href*=book]"
override fun latestUpdatesSelector() = throw Exception("Not Used")
override fun searchMangaSelector() = throw Exception("Not Used")
override fun chapterListSelector() = throw Exception("Not Used")
override fun popularMangaNextPageSelector() = "none"
override fun latestUpdatesNextPageSelector() = "none"
override fun searchMangaNextPageSelector() = "none"
override fun latestUpdatesSelector() = ""
override fun searchMangaSelector() = ""
override fun chapterListSelector() = ""
override fun popularMangaNextPageSelector(): String? = null
override fun latestUpdatesNextPageSelector(): String? = null
override fun searchMangaNextPageSelector(): String? = null
override fun popularMangaRequest(page: Int) = GET("$baseUrl/book", headers)
override fun latestUpdatesRequest(page: Int) = throw Exception("Not Used")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw Exception("No search")
// override fun mangaDetailsRequest(manga: SManga) = GET(baseUrl + manga.url, headers)
// override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers)
override fun chapterListRequest(manga: SManga) = GET(baseUrl + manga.url + "/get_chapter", headers)
override fun popularMangaFromElement(element: Element) = mangaFromElement(element)
override fun latestUpdatesFromElement(element: Element) = mangaFromElement(element)
override fun searchMangaFromElement(element: Element) = mangaFromElement(element)
private fun mangaFromElement(element: Element): SManga {
@ -66,41 +80,33 @@ class BH3 : ParsedHttpSource() {
override fun chapterFromElement(element: Element) = throw Exception("Not Used")
override fun chapterListParse(response: Response): List<SChapter> {
val jsondata = response.body!!.string()
val json = JsonParser().parse(jsondata).asJsonArray
val chapters = mutableListOf<SChapter>()
json.forEach {
chapters.add(createChapter(it))
}
return chapters
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonArray
return jsonResult.map { jsonEl -> createChapter(jsonEl.jsonObject) }
}
private fun createChapter(json: JsonElement) = SChapter.create().apply {
name = json["title"].string
url = "/book/${json["bookid"].int}/${json["chapterid"].int}"
date_upload = parseDate(json["timestamp"].string)
chapter_number = json["chapterid"].float
private fun createChapter(jsonObj: JsonObject) = SChapter.create().apply {
name = jsonObj["title"]!!.jsonPrimitive.content
url = "/book/${jsonObj["bookid"]!!.jsonPrimitive.int}/${jsonObj["chapterid"]!!.jsonPrimitive.int}"
date_upload = parseDate(jsonObj["timestamp"]!!.jsonPrimitive.content)
chapter_number = jsonObj["chapterid"]!!.jsonPrimitive.float
}
private fun parseDate(date: String): Long {
return SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).parse(date)?.time ?: 0L
}
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
manga.thumbnail_url = document.select("img.cover").attr("abs:src")
manga.description = document.select("div.detail_info1").text().trim()
manga.title = document.select("div.title").text().trim()
return manga
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
thumbnail_url = document.select("img.cover").attr("abs:src")
description = document.select("div.detail_info1").text().trim()
title = document.select("div.title").text().trim()
}
override fun pageListParse(response: Response): List<Page> = mutableListOf<Page>().apply {
val body = response.asJsoup()
body.select("img.lazy.comic_img")?.forEach {
add(Page(size, "", it.attr("data-original")))
override fun pageListParse(document: Document): List<Page> {
return document.select("img.lazy.comic_img").mapIndexed { i, el ->
Page(i, "", el.attr("data-original"))
}
}
override fun pageListParse(document: Document) = throw Exception("Not Used")
override fun imageUrlParse(document: Document) = throw Exception("Not Used")
override fun imageUrlParse(document: Document) = ""
}

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Qimiaomh'
pkgNameSuffix = 'zh.qimiaomh'
extClass = '.Qimiaomh'
extVersionCode = 1
extVersionCode = 2
libVersion = '1.2'
}

View File

@ -1,40 +1,53 @@
package eu.kanade.tachiyomi.extension.zh.qimiaomh
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
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.source.online.ParsedHttpSource
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.Random
import java.util.concurrent.TimeUnit
class Qimiaomh : ParsedHttpSource() {
override val name: String = "奇妙漫画"
override val lang: String = "zh"
override val baseUrl: String = "https://www.qimiaomh.com"
override val supportsLatest: Boolean = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.retryOnConnectionFailure(true)
.followRedirects(true)
.build()!!
.build()
private val json: Json by injectLazy()
// Popular
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/list-1------hits--$page.html", headers)
}
override fun popularMangaNextPageSelector(): String? = "a:contains(下一页)"
override fun popularMangaSelector(): String = "div.classification"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
url = element.select("a").first().attr("href")
thumbnail_url = element.select("img.lazyload").attr("abs:data-src")
@ -45,8 +58,11 @@ class Qimiaomh : ParsedHttpSource() {
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/list-1------updatetime--$page.html", headers)
}
override fun latestUpdatesNextPageSelector(): String? = popularMangaNextPageSelector()
override fun latestUpdatesSelector(): String = popularMangaSelector()
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
// Search
@ -54,8 +70,11 @@ class Qimiaomh : ParsedHttpSource() {
throw Exception("不管用 (T_T)")
// Todo Filters
}
override fun searchMangaNextPageSelector(): String? = popularMangaNextPageSelector()
override fun searchMangaSelector(): String = popularMangaSelector()
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
// Details
@ -78,31 +97,31 @@ class Qimiaomh : ParsedHttpSource() {
// Chapters
override fun chapterListSelector(): String = "div.comic-content-list ul.comic-content-c"
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
url = element.select("a").first().attr("href")
name = element.select("li.tit").text().trim()
}
private fun parseDate(date: String): Long {
return SimpleDateFormat("dd/MM/yyyy", Locale.US).parse(date)?.time ?: 0L
}
// Pages
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply {
override fun pageListParse(document: Document): List<Page> {
val script = document.select("script:containsData(var did =)").html()
val did = script.substringAfter("var did = ").substringBefore(";")
val sid = script.substringAfter("var sid = ").substringBefore(";")
val url = "$baseUrl/Action/Play/AjaxLoadImgUrl?did=$did&sid=$sid&tmp=${Random().nextFloat()}"
val body = client.newCall(GET(url, headers)).execute().body!!.string()
val json = JsonParser().parse(body).asJsonObject
val images = json["listImg"].asJsonArray
images.forEachIndexed { index, jsonElement ->
add(Page(index, "", jsonElement.string))
val response = client.newCall(GET(url, headers)).execute()
val result = json.parseToJsonElement(response.body!!.string()).jsonObject
return result["listImg"]!!.jsonArray.mapIndexed { i, jsonEl ->
Page(i, "", jsonEl.jsonPrimitive.content)
}
}
override fun imageUrlParse(document: Document): String {
throw Exception("Not Used")
}
// Not Used
override fun imageUrlParse(document: Document): String = ""
}