Compare commits

...

22 Commits

Author SHA1 Message Date
Vetle Ledaal c353c9a780 Update domain for NhatTruyen (#220)
CI / Prepare job (push) Successful in 14s Details
CI / Build multisrc modules (push) Successful in 6m9s Details
CI / Build individual modules (push) Successful in 1m12s Details
CI / Publish repo (push) Successful in 3m2s Details
2024-01-14 00:33:07 +00:00
Vetle Ledaal 76a7c8c54a Nicomanga: fix cover and image fetching (#214)
* Nicomanga: fix cover and image fetching

* not extlib 1.5 yet
2024-01-14 00:33:04 +00:00
beerpsi d88fb5f595 Batoto: Merge English and English (US) (#212)
* Batoto: Fix popular/latest in en_US

* Batoto: Merge English with English (US)
2024-01-14 00:32:56 +00:00
beerpsi c6276c8a7d Ler Manga: Escape HTML entities in titles (#211) 2024-01-14 00:32:11 +00:00
beerpsi 9d74ec6b34 Imperio Scans: Update base URL (#209)
* Imperio Scans: Update base URL

* uncomment other sources

* actually bump overrideVersionCode
2024-01-14 00:32:10 +00:00
beerpsi 85f8bd3917 OnePieceEx: Fix Brotli issue (#206) 2024-01-14 00:32:09 +00:00
Eshlender 555896d9a4 [RU]Nudemoon fix images and lock cloudflareClient (#205) 2024-01-14 00:32:07 +00:00
Wackery 43a2a09e4b Add Reset Scans (#202)
* Add Reset Scans

* Add Reset Scans (Madara)

* Remove initial override version code
2024-01-14 00:32:06 +00:00
beerpsi ddb46a88ce Add Constellar Scans (#203) 2024-01-14 00:32:05 +00:00
az4521 e0d5742b09 create SUPER MEGA extension (#201)
* create super mega extension

* change SUPER MEGA ext name

* fix build errors in SUPER MEGA extension

* reverse chapter order super mega ext

* revert settings.gradle.kts file

---------

Co-authored-by: az4521 <az4521@users.noreply.github.com>
2024-01-14 00:32:03 +00:00
Fermín Cirella 6651c0421b Anchira - Remove response decrypt (#200) 2024-01-14 00:31:32 +00:00
bapeey e2dc0795c3 Mangas.in: Optimize unescaping (#193)
Optimize unescaping
2024-01-14 00:31:23 +00:00
Vetle Ledaal 8f55b1e8d4 Remove Toonily.net (#192) 2024-01-14 00:31:05 +00:00
Fermín Cirella c2555b3e44 Anchira - Apply API changes (#191)
* Anchira - Apply API changes

* Anchira - Update version code
2024-01-14 00:30:05 +00:00
stevenyomi f612bc1c3f Clean up some code to follow best practices (#178)
* toHttpUrlOrNull()!! -> toHttpUrl()
* Jsoup.parse(response.body.string()) -> response.asJsoup()
* Some useless HttpUrl.newBuilder() calls
* extlib v1.4 GET(HttpUrl)
2024-01-14 00:29:56 +00:00
Eshlender 2b9b92ceaa [RU]GroupLe fix error pages and fake loads (#184) 2024-01-14 00:29:47 +00:00
Fermín Cirella a9faf70d33 Anchira - Apply API changes (#180) 2024-01-14 00:29:39 +00:00
CodeSpoof 84418c477f Add Nicomanga (#46)
Co-authored-by: FourTOne5 <107297513+FourTOne5@users.noreply.github.com>
2024-01-14 00:29:30 +00:00
AwkwardPeak7 df0e9cb736 add Setsu Scans (#169)
Setsu Scans
2024-01-14 00:29:06 +00:00
AwkwardPeak7 0c197ded02 YMO: fix selectors (#166) 2024-01-14 00:28:58 +00:00
Fermín Cirella cafe12c736 Add Anchira (#162)
* Add Anchira

* Encode API decryption key

* Apply corrections

* Remove unused MessagePack library
2024-01-14 00:28:53 +00:00
bapeey 6cc9041f10 Add Mangas.in to MMRCMS (#160)
* Add Mangas.in

* Bump

* Is NSFW

* Change messages

* Lint
2024-01-14 00:28:36 +00:00
181 changed files with 1523 additions and 361 deletions

View File

@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.multisrc.grouple.GroupLe
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -46,15 +45,13 @@ class AllHentai : GroupLe("AllHentai", "https://z.allhen.online", "ru") {
is OrderBy -> {
if (filter.state > 0) {
val ord = arrayOf("not", "year", "rate", "popularity", "votes", "created", "updated")[filter.state]
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
return GET(ordUrl.toString(), headers)
return GET("$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}", headers)
}
}
is Tags -> {
if (filter.state > 0) {
val tagName = getTagsList()[filter.state].url
val tagUrl = "$baseUrl/list/tag/$tagName?offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
return GET(tagUrl.toString(), headers)
return GET("$baseUrl/list/tag/$tagName?offset=${70 * (page - 1)}", headers)
}
}
else -> {}

View File

@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.multisrc.grouple.GroupLe
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -57,8 +56,7 @@ class MintManga : GroupLe("MintManga", "https://mintmanga.com", "ru") {
url.addQueryParameter("sortType", arrayOf("RATING", "POPULARITY", "YEAR", "NAME", "DATE_CREATE", "DATE_UPDATE")[filter.state])
} else {
val ord = arrayOf("rate", "popularity", "year", "name", "created", "updated", "votes")[filter.state]
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
return GET(ordUrl.toString(), headers)
return GET("$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}", headers)
}
}
else -> {}

View File

@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.multisrc.grouple.GroupLe
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -57,8 +56,7 @@ class ReadManga : GroupLe("ReadManga", "https://readmanga.live", "ru") {
url.addQueryParameter("sortType", arrayOf("RATING", "POPULARITY", "YEAR", "NAME", "DATE_CREATE", "DATE_UPDATE")[filter.state])
} else {
val ord = arrayOf("rate", "popularity", "year", "name", "created", "updated", "votes")[filter.state]
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
return GET(ordUrl.toString(), headers)
return GET("$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}", headers)
}
}
else -> {}

View File

@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.multisrc.grouple.GroupLe
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -31,8 +30,7 @@ class RuMIX : GroupLe("RuMIX", "https://rumix.me", "ru") {
url.addQueryParameter("sortType", arrayOf("RATING", "POPULARITY", "YEAR", "NAME", "DATE_CREATE", "DATE_UPDATE")[filter.state])
} else {
val ord = arrayOf("rate", "popularity", "year", "name", "created", "updated", "votes")[filter.state]
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
return GET(ordUrl.toString(), headers)
return GET("$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}", headers)
}
}
else -> return@forEach

View File

@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.multisrc.grouple.GroupLe
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -43,8 +42,7 @@ class SelfManga : GroupLe("SelfManga", "https://selfmanga.live", "ru") {
url.addQueryParameter("sortType", arrayOf("RATING", "POPULARITY", "YEAR", "NAME", "DATE_CREATE", "DATE_UPDATE")[filter.state])
} else {
val ord = arrayOf("rate", "popularity", "year", "name", "created", "updated", "votes")[filter.state]
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
return GET(ordUrl.toString(), headers)
return GET("$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}", headers)
}
}
else -> return@forEach

View File

@ -52,7 +52,7 @@ class DoujinHentai : Madara(
}
}
}
return GET(url.build().toString(), headers)
return GET(url.build(), headers)
}
override fun searchMangaSelector() = "div.c-tabs-item__content > div.c-tabs-item__content, ${popularMangaSelector()}"

View File

@ -9,7 +9,7 @@ import java.util.concurrent.TimeUnit
class ImperioScans : Madara(
"Império Scans",
"https://imperioscans.com.br",
"https://neroxus.com.br",
"pt-BR",
SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")),
) {

View File

@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import java.text.SimpleDateFormat
@ -23,7 +23,7 @@ class PojokManga : Madara("Pojok Manga", "https://pojokmanga.net", "id", SimpleD
override val mangaSubString = "komik"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$baseUrl/${searchPage(page)}".toHttpUrlOrNull()!!.newBuilder()
var url = "$baseUrl/${searchPage(page)}".toHttpUrl().newBuilder()
url.addQueryParameter("s", query)
url.addQueryParameter("post_type", "wp-manga")
filters.forEach { filter ->
@ -70,13 +70,13 @@ class PojokManga : Madara("Pojok Manga", "https://pojokmanga.net", "id", SimpleD
}
is ProjectFilter -> {
if (filter.toUriPart() == "project-filter-on") {
url = "$baseUrl/project/page/$page".toHttpUrlOrNull()!!.newBuilder()
url = "$baseUrl/project/page/$page".toHttpUrl().newBuilder()
}
}
else -> {}
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun searchMangaSelector() = "div.c-tabs-item__content, div.page-item-detail"

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

View File

@ -0,0 +1,7 @@
package eu.kanade.tachiyomi.extension.en.resetscans
import eu.kanade.tachiyomi.multisrc.madara.Madara
class ResetScans : Madara("Reset Scans", "https://reset-scans.us", "en") {
override val useNewChapterEndpoint = true
override val chapterUrlSelector = ".li__text > a"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

View File

@ -0,0 +1,62 @@
package eu.kanade.tachiyomi.extension.en.setsuscans
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.MangasPage
import okhttp3.Response
class SetsuScans : Madara(
"Setsu Scans",
"https://setsuscans.com",
"en",
) {
override val client = super.client.newBuilder()
.addNetworkInterceptor { chain ->
val request = chain.request()
val url = request.url
if (url.host == "i0.wp.com") {
val newUrl = url.newBuilder()
.removeAllQueryParameters("fit")
.build()
return@addNetworkInterceptor chain.proceed(
request.newBuilder()
.url(newUrl)
.build(),
)
}
return@addNetworkInterceptor chain.proceed(request)
}
.rateLimit(2)
.build()
override val useNewChapterEndpoint = true
override fun searchPage(page: Int): String {
return if (page > 1) {
"page/$page/"
} else {
""
}
}
override fun popularMangaParse(response: Response) =
super.popularMangaParse(response).fixNextPage()
override fun latestUpdatesParse(response: Response) =
super.latestUpdatesParse(response).fixNextPage()
override fun searchMangaParse(response: Response) =
super.searchMangaParse(response).fixNextPage()
private fun MangasPage.fixNextPage(): MangasPage {
return if (mangas.size < 12) {
MangasPage(mangas, false)
} else {
this
}
}
override val mangaDetailsSelectorStatus = "div.summary-heading:contains(status) + div.summary-content"
}

View File

@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.extension.en.toonily
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.FilterList
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import java.text.SimpleDateFormat
@ -51,7 +51,7 @@ class Toonily : Madara(
val queries = request.url.queryParameterNames
.filterNot { it == "s" }
val newUrl = "$baseUrl/${searchPage(page, query)}".toHttpUrlOrNull()!!.newBuilder().apply {
val newUrl = "$baseUrl/${searchPage(page, query)}".toHttpUrl().newBuilder().apply {
queries.map { q ->
request.url.queryParameterValues(q).map {
this.addQueryParameter(q, it)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

View File

@ -0,0 +1,95 @@
package eu.kanade.tachiyomi.extension.en.constellarscans
import android.app.Application
import android.content.SharedPreferences
import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA
import eu.kanade.tachiyomi.lib.randomua.getPrefUAType
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.CacheControl
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarcomic.com", "en") {
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override val client: OkHttpClient by lazy {
network.cloudflareClient.newBuilder()
.setRandomUserAgent(
preferences.getPrefUAType(),
preferences.getPrefCustomUA(),
)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(1, 1)
.build()
}
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("Referer", "$baseUrl/")
.add("Accept-Language", "en-US,en;q=0.9")
.add("DNT", "1")
.add("Upgrade-Insecure-Requests", "1")
override val seriesStatusSelector = ".status"
override fun pageListRequest(chapter: SChapter): Request =
super.pageListRequest(chapter).newBuilder()
.header(
"Accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
)
.header("Sec-Fetch-Site", "same-origin")
.header("Sec-Fetch-Mode", "navigate")
.header("Sec-Fetch-Dest", "document")
.header("Sec-Fetch-User", "?1")
.cacheControl(CacheControl.FORCE_NETWORK)
.build()
override fun pageListParse(document: Document): List<Page> {
val html = document.toString()
if (!html.contains("ts_rea_der_._run(\"")) {
return super.pageListParse(document)
}
val tsReaderRawData = html
.substringAfter("ts_rea_der_._run(\"")
.substringBefore("\")")
.replace(Regex("""\D"""), "")
.chunked(4)
.map {
val tenthsAndOnes = it.chunked(2).map {
val num = it.toInt()
num / 10 + num % 10
}
(tenthsAndOnes[0] * 10 + tenthsAndOnes[1] + 32).toChar()
}
.joinToString("")
countViews(document)
return json.parseToJsonElement(tsReaderRawData).jsonObject["sources"]!!.jsonArray[0].jsonObject["images"]!!.jsonArray.mapIndexed { idx, it ->
Page(idx, imageUrl = it.jsonPrimitive.content)
}
}
override fun imageRequest(page: Page): Request = super.imageRequest(page).newBuilder()
.header("Accept", "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8")
.header("Sec-Fetch-Dest", "image")
.header("Sec-Fetch-Mode", "no-cors")
.header("Sec-Fetch-Site", "same-origin")
.build()
}

View File

@ -38,7 +38,7 @@ class CosmicScansID : MangaThemesia("CosmicScans.id", "https://cosmicscans.id",
else -> { /* Do Nothing */ }
}
}
return GET(url.toString())
return GET(url.build())
}
override fun searchMangaSelector() = ".bixbox:not(.hothome):has(.hpage) .utao .uta .imgu, .bixbox:not(.hothome) .listupd .bs .bsx"

View File

@ -201,7 +201,7 @@ class KomikCast : MangaThemesia("Komik Cast", "https://komikcast.lol", "id", "/d
else -> { /* Do Nothing */ }
}
}
return GET(url.toString())
return GET(url.build())
}
private class StatusFilter : SelectFilter(

View File

@ -55,7 +55,7 @@ class Ngomik : MangaThemesia("Ngomik", "https://ngomik.net", "id", "/manga") {
else -> { /* Do Nothing */ }
}
}
return GET(url.toString())
return GET(url.build())
}
override fun headersBuilder(): Headers.Builder = Headers.Builder()

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@ -0,0 +1,154 @@
package eu.kanade.tachiyomi.extension.es.mangasin
import android.net.Uri
import android.util.Base64
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
import eu.kanade.tachiyomi.lib.synchrony.Deobfuscator
import eu.kanade.tachiyomi.multisrc.mmrcms.MMRCMS
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
class MangasIn : MMRCMS("Mangas.in", "https://mangas.in", "es") {
private val json: Json by injectLazy()
override val client = super.client.newBuilder()
.rateLimitHost(baseUrl.toHttpUrl(), 1, 1)
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
private var key = ""
private fun getKey(): String {
val script = client.newCall(GET("$baseUrl/js/datachs.js")).execute().body.string()
val deobfuscatedScript = Deobfuscator.deobfuscateScript(script)
?: throw Exception("No se pudo desofuscar el script")
return KEY_REGEX.find(deobfuscatedScript)?.groupValues?.get(1)
?: throw Exception("No se pudo encontrar la clave")
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url: Uri.Builder
when {
query.isNotBlank() -> {
url = Uri.parse("$baseUrl/search")!!.buildUpon()
url.appendQueryParameter("q", query)
}
else -> {
url = Uri.parse("$baseUrl/filterList?page=$page")!!.buildUpon()
filters.filterIsInstance<UriFilter>()
.forEach { it.addToUri(url) }
}
}
return GET(url.toString(), headers)
}
override fun searchMangaParse(response: Response): MangasPage {
return if (listOf("query", "q").any { it in response.request.url.queryParameterNames }) {
val searchResult = json.decodeFromString<List<SearchResult>>(response.body.string())
MangasPage(
searchResult
.map {
SManga.create().apply {
url = getUrlWithoutBaseUrl(itemUrl + it.slug)
title = it.name
thumbnail_url = "$baseUrl/uploads/manga/${it.slug}/cover/cover_250x350.jpg"
}
},
false,
)
} else {
internalMangaParse(response)
}
}
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
val mangaUrl = document.location().removeSuffix("/")
val receivedData = RECEIVED_DATA_REGEX.find(document.html())?.groupValues?.get(1) ?: throw Exception("No se pudo encontrar la lista de capítulos")
val unescapedReceivedData = receivedData.unescape()
val chapterData = json.decodeFromString<CDT>(unescapedReceivedData)
val salt = chapterData.s.decodeHex()
val unsaltedCipherText = Base64.decode(chapterData.ct, Base64.DEFAULT)
val cipherText = SALTED + salt + unsaltedCipherText
val decrypted = CryptoAES.decrypt(Base64.encodeToString(cipherText, Base64.DEFAULT), key).ifEmpty {
key = getKey()
CryptoAES.decrypt(Base64.encodeToString(cipherText, Base64.DEFAULT), key)
}
val unescaped = decrypted.unescapeJava().removeSurrounding("\"").unescape()
val chapters = json.decodeFromString<List<Chapter>>(unescaped)
return chapters.map {
SChapter.create().apply {
name = "Capítulo ${it.number}: ${it.name}"
date_upload = it.createdAt.parseDate()
setUrlWithoutDomain("$mangaUrl/${it.slug}")
}
}
}
private fun String.unescape(): String {
return UNESCAPE_REGEX.replace(this, "$1")
}
private fun String.unescapeJava(): String {
var escaped = this
if (!escaped.contains("\\u")) return escaped
val builder = StringBuilder()
var position = escaped.indexOf("\\u")
while (position != -1) {
if (position != 0) {
builder.append(escaped, 0, position)
}
val token = escaped.substring(position + 2, position + 6)
escaped = escaped.substring(position + 6)
builder.append(Integer.parseInt(token, 16).toChar())
position = escaped.indexOf("\\u")
}
builder.append(escaped)
return builder.toString()
}
private fun String.parseDate(): Long {
return dateFormat.parse(this)?.time ?: 0L
}
private fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}
companion object {
val UNESCAPE_REGEX = """\\(.)""".toRegex()
val RECEIVED_DATA_REGEX = """receivedData\s*=\s*["'](.*)["']\s*;""".toRegex()
val KEY_REGEX = """decrypt\(.*'(.*)'.*\)""".toRegex()
val SALTED = "Salted__".toByteArray(Charsets.UTF_8)
val dateFormat by lazy {
SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
}
}
}

View File

@ -0,0 +1,21 @@
package eu.kanade.tachiyomi.extension.es.mangasin
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class CDT(val ct: String, val s: String)
@Serializable
data class Chapter(
val slug: String,
val name: String,
val number: String,
@SerialName("created_at") val createdAt: String,
)
@Serializable
data class SearchResult(
@SerialName("value") val name: String,
@SerialName("data") val slug: String,
)

View File

@ -9,7 +9,7 @@ import okhttp3.Request
import java.text.SimpleDateFormat
import java.util.Locale
class NhatTruyen : WPComics("NhatTruyen", "https://nhattruyenmax.com", "vi", SimpleDateFormat("dd/MM/yy", Locale.getDefault()), null) {
class NhatTruyen : WPComics("NhatTruyen", "https://nhattruyento.com", "vi", SimpleDateFormat("dd/MM/yy", Locale.getDefault()), null) {
override fun headersBuilder(): Headers.Builder = Headers.Builder()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/the-loai?keyword=$query&page=$page", headers)
override fun imageRequest(page: Page): Request = GET(page.imageUrl!!, headersBuilder().add("Referer", baseUrl).build())

View File

@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
@ -38,7 +38,7 @@ class Komikita : ZManga(
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$baseUrl/${pagePathSegment(page)}".toHttpUrlOrNull()!!.newBuilder()
var url = "$baseUrl/${pagePathSegment(page)}".toHttpUrl().newBuilder()
url.addQueryParameter("s", query)
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
@ -51,13 +51,13 @@ class Komikita : ZManga(
is ProjectFilter -> {
if (filter.toUriPart() == "project-filter-on") {
url =
"$baseUrl$projectPageString/page/$page".toHttpUrlOrNull()!!.newBuilder()
"$baseUrl$projectPageString/page/$page".toHttpUrl().newBuilder()
}
}
else -> {}
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun getFilterList(): FilterList {

View File

@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
@ -32,20 +32,20 @@ class KomikPlay : ZManga("KomikPlay", "https://komikplay.com", "id", SimpleDateF
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$baseUrl/${pagePathSegment(page)}".toHttpUrlOrNull()!!.newBuilder()
var url = "$baseUrl/${pagePathSegment(page)}".toHttpUrl().newBuilder()
url.addQueryParameter("s", query)
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
// if site has project page, default value "hasProjectPage" = false
is ProjectFilter -> {
if (filter.toUriPart() == "project-filter-on") {
url = "$baseUrl$projectPageString/page/$page".toHttpUrlOrNull()!!.newBuilder()
url = "$baseUrl$projectPageString/page/$page".toHttpUrl().newBuilder()
}
}
else -> {}
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun getFilterList() = FilterList(

View File

@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Element
@ -58,20 +58,20 @@ class SekteKomik : ZManga("Sekte Komik", "https://sektekomik.com", "id") {
// search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$baseUrl/${pagePathSegment(page)}".toHttpUrlOrNull()!!.newBuilder()
var url = "$baseUrl/${pagePathSegment(page)}".toHttpUrl().newBuilder()
url.addQueryParameter("s", query)
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
// if site has project page, default value "hasProjectPage" = false
is ProjectFilter -> {
if (filter.toUriPart() == "project-filter-on") {
url = "$baseUrl$projectPageString/${pagePathSegment(page)}".toHttpUrlOrNull()!!.newBuilder()
url = "$baseUrl$projectPageString/${pagePathSegment(page)}".toHttpUrl().newBuilder()
}
}
else -> {}
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun searchMangaSelector() = "div.flexbox2-item"

View File

@ -42,7 +42,7 @@ abstract class BakaManga(
return if (query.isNotEmpty()) {
val url = "$baseUrl/page/$page".toHttpUrl().newBuilder()
.addQueryParameter("s", query)
GET(url.toString(), headers)
GET(url.build(), headers)
} else {
val filterList = if (filters.isEmpty()) getFilterList() else filters
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter

View File

@ -10,7 +10,7 @@ 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 okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -59,7 +59,7 @@ open class EroMuse(override val name: String, override val baseUrl: String) : Ht
if (url.contains(pageQueryRegex)) {
url.replace(pageQueryRegex, "page=$int")
} else {
val httpUrl = url.toHttpUrlOrNull()!!
val httpUrl = url.toHttpUrl()
val builder = if (httpUrl.pathSegments.last().toIntOrNull() is Int) {
httpUrl.newBuilder().removePathSegment(httpUrl.pathSegments.lastIndex)
} else {
@ -165,7 +165,7 @@ open class EroMuse(override val name: String, override val baseUrl: String) : Ht
protected fun stackRequest(): Request {
stackItem = pageStack.removeLast()
val url = if (stackItem.pageType == AUTHOR && currentSortingMode.isNotEmpty() && !stackItem.url.contains("sort")) {
stackItem.url.toHttpUrlOrNull()!!.newBuilder().addQueryParameter("sort", currentSortingMode).toString()
stackItem.url.toHttpUrl().newBuilder().addQueryParameter("sort", currentSortingMode).toString()
} else {
stackItem.url
}
@ -208,14 +208,14 @@ open class EroMuse(override val name: String, override val baseUrl: String) : Ht
currentSortingMode = filterList.filterIsInstance<SortFilter>().first().toQueryValue()
if (query.isNotBlank()) {
val url = "$baseUrl/search?q=$query".toHttpUrlOrNull()!!.newBuilder().apply {
val url = "$baseUrl/search?q=$query".toHttpUrl().newBuilder().apply {
if (currentSortingMode.isNotEmpty()) addQueryParameter("sort", currentSortingMode)
addQueryParameter("page", "1")
}
pageStack.addLast(StackItem(url.toString(), SEARCH_RESULTS_OR_BASE))
} else {
val albumFilter = filterList.filterIsInstance<AlbumFilter>().first().selection()
val url = "$baseUrl/comics/${albumFilter.pathSegments}".toHttpUrlOrNull()!!.newBuilder().apply {
val url = "$baseUrl/comics/${albumFilter.pathSegments}".toHttpUrl().newBuilder().apply {
if (currentSortingMode.isNotEmpty()) addQueryParameter("sort", currentSortingMode)
if (albumFilter.pageType != AUTHOR) addQueryParameter("page", "1")
}

View File

@ -122,7 +122,7 @@ abstract class FansubsCat(
if (query.isNotBlank()) {
builder.addQueryParameter("query", query)
}
return GET(builder.toString(), headers)
return GET(builder.build(), headers)
}
override fun searchMangaParse(response: Response): MangasPage = parseMangaFromJson(response)

View File

@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -65,7 +65,7 @@ abstract class FMReader(
GET("$baseUrl/$requestPath?listType=pagination&page=$page&$popularSort&sort_type=DESC", headers)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/$requestPath?".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/$requestPath?".toHttpUrl().newBuilder()
.addQueryParameter("name", query)
.addQueryParameter("page", page.toString())
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
@ -101,7 +101,7 @@ abstract class FMReader(
else -> {}
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun latestUpdatesRequest(page: Int): Request =

View File

@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -58,7 +58,7 @@ abstract class Gattsu(
override fun latestUpdatesNextPageSelector(): String = "ul.paginacao li.next > a"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val searchUrl = "$baseUrl/page/$page/".toHttpUrlOrNull()!!.newBuilder()
val searchUrl = "$baseUrl/page/$page/".toHttpUrl().newBuilder()
.addQueryParameter("s", query)
.addQueryParameter("post_type", "post")
.toString()

View File

@ -21,6 +21,7 @@ import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Call
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
@ -92,10 +93,10 @@ abstract class GigaViewer(
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotEmpty()) {
val url = "$baseUrl/search".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/search".toHttpUrl().newBuilder()
.addQueryParameter("q", query)
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
val collectionSelected = (filters[0] as CollectionFilter).selected
@ -138,7 +139,7 @@ abstract class GigaViewer(
val document = response.asJsoup()
val readableProductList = document.selectFirst("div.js-readable-product-list")!!
val firstListEndpoint = readableProductList.attr("data-first-list-endpoint")
.toHttpUrlOrNull()!!
.toHttpUrl()
val latestListEndpoint = readableProductList.attr("data-latest-list-endpoint")
.toHttpUrlOrNull() ?: firstListEndpoint
val numberSince = latestListEndpoint.queryParameter("number_since")!!.toFloat()
@ -214,7 +215,7 @@ abstract class GigaViewer(
return episode.readableProduct.pageStructure.pages
.filter { it.type == "main" }
.mapIndexed { i, page ->
val imageUrl = page.src.toHttpUrlOrNull()!!.newBuilder()
val imageUrl = page.src.toHttpUrl().newBuilder()
.addQueryParameter("width", page.width.toString())
.addQueryParameter("height", page.height.toString())
.toString()

View File

@ -15,7 +15,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -101,7 +101,7 @@ abstract class GroupLe(
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url =
"$baseUrl/search/advancedResults?offset=${50 * (page - 1)}".toHttpUrlOrNull()!!
"$baseUrl/search/advancedResults?offset=${50 * (page - 1)}".toHttpUrl()
.newBuilder()
if (query.isNotEmpty()) {
url.addQueryParameter("q", query)
@ -286,11 +286,7 @@ abstract class GroupLe(
val html = document.html()
var readerMark = "rm_h.initReader( ["
if (!html.contains(readerMark)) {
readerMark = "rm_h.readerInit( 0,["
}
var readerMark = "rm_h.readerDoInit(["
if (!html.contains(readerMark)) {
if (document.select(".input-lg").isNotEmpty() || (document.select(".user-avatar").isNullOrEmpty() && document.select("img.logo").first()?.attr("title")?.contains("Allhentai") == true)) {

View File

@ -9,7 +9,7 @@ class GroupLeGenerator : ThemeSourceGenerator {
override val themeClass = "GroupLe"
override val baseVersionCode = 20
override val baseVersionCode = 21
override val sources = listOf(
SingleLang("ReadManga", "https://readmanga.live", "ru", overrideVersionCode = 46),

View File

@ -24,7 +24,7 @@ import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
@ -79,7 +79,7 @@ abstract class HentaiHand(
}
override fun popularMangaRequest(page: Int): Request {
val url = "$baseUrl/api/comics".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/api/comics".toHttpUrl().newBuilder()
.addQueryParameter("page", page.toString())
.addQueryParameter("sort", "popularity")
.addQueryParameter("order", "desc")
@ -88,7 +88,7 @@ abstract class HentaiHand(
url.addQueryParameter("languages[${-index - 1}]", it.toString())
}
// if (altLangId != null) url.addQueryParameter("languages", altLangId.toString())
return GET(url.toString())
return GET(url.build())
}
// Latest
@ -96,7 +96,7 @@ abstract class HentaiHand(
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
override fun latestUpdatesRequest(page: Int): Request {
val url = "$baseUrl/api/comics".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/api/comics".toHttpUrl().newBuilder()
.addQueryParameter("page", page.toString())
.addQueryParameter("sort", "uploaded_at")
.addQueryParameter("order", "desc")
@ -104,7 +104,7 @@ abstract class HentaiHand(
hhLangId.forEachIndexed { index, it ->
url.addQueryParameter("languages[${-index - 1}]", it.toString())
}
return GET(url.toString())
return GET(url.build())
}
// Search
@ -130,7 +130,7 @@ abstract class HentaiHand(
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/api/comics".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/api/comics".toHttpUrl().newBuilder()
.addQueryParameter("page", page.toString())
.addQueryParameter("q", query)
@ -162,7 +162,7 @@ abstract class HentaiHand(
}
}
return GET(url.toString())
return GET(url.build())
}
// Details

View File

@ -31,7 +31,7 @@ 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.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
@ -544,7 +544,7 @@ abstract class LibGroup(
val resBody = tokenResponse.body.string()
csrfToken = "_token\" content=\"(.*)\"".toRegex().find(resBody)!!.groups[1]!!.value
}
val url = "$baseUrl/filterlist?page=$page&chapters[min]=1".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/filterlist?page=$page&chapters[min]=1".toHttpUrl().newBuilder()
if (query.isNotEmpty()) {
url.addQueryParameter("name", query)
}

View File

@ -27,7 +27,7 @@ import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.CacheControl
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -209,7 +209,7 @@ abstract class Madara(
protected open fun searchPage(page: Int): String = "page/$page/"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/${searchPage(page)}".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/${searchPage(page)}".toHttpUrl().newBuilder()
url.addQueryParameter("s", query)
url.addQueryParameter("post_type", "wp-manga")
filters.forEach { filter ->
@ -257,7 +257,7 @@ abstract class Madara(
else -> {}
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
protected open val authorFilterTitle: String = when (lang) {

View File

@ -152,7 +152,7 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("I Love Manhwa", "https://ilovemanhwa.com", "en", isNsfw = true),
SingleLang("Illusion Scan", "https://illusionscan.com", "pt-BR", isNsfw = true),
SingleLang("Immortal Updates", "https://immortalupdates.com", "en", overrideVersionCode = 6),
SingleLang("Império Scans", "https://imperioscans.com.br", "pt-BR", className = "ImperioScans", overrideVersionCode = 1),
SingleLang("Império Scans", "https://neroxus.com.br", "pt-BR", className = "ImperioScans", overrideVersionCode = 2),
SingleLang("InfraFandub", "https://infrafandub.com", "es", overrideVersionCode = 1),
SingleLang("Inmortal Scan", "https://manga.mundodrama.site", "es"),
SingleLang("InstaManhwa", "https://www.instamanhwa.com", "en", isNsfw = true, overrideVersionCode = 1),
@ -414,6 +414,7 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("ReadFreeComics", "https://readfreecomics.com", "en"),
SingleLang("ReadMangaFree", "https://readmangafree.net", "en", isNsfw = true),
SingleLang("ReadManhua", "https://readmanhua.net", "en", overrideVersionCode = 2),
SingleLang("Reset Scans", "https://reset-scans.us", "en"),
SingleLang("Rh2PlusManga", "https://www.rh2plusmanga.com", "th", isNsfw = true, overrideVersionCode = 5),
SingleLang("RichtoScan", "https://richtoscan.com", "es"),
SingleLang("Rightdark Scan", "https://rightdark-scan.com", "es"),
@ -429,6 +430,7 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("Scan Hentai Menu", "https://scan.hentai.menu", "fr", isNsfw = true, overrideVersionCode = 1),
SingleLang("Scantrad-VF", "https://scantrad-vf.co", "fr", className = "ScantradVF"),
SingleLang("Sdl scans", "https://sdlscans.com", "es", className = "SdlScans"),
SingleLang("Setsu Scans", "https://setsuscans.com", "en"),
SingleLang("Shadowtrad", "https://shadowtrad.net", "fr"),
SingleLang("ShavelProiection", "https://www.shavelproiection.com", "it", true),
SingleLang("Shayami", "https://shayami.com", "es"),
@ -459,7 +461,6 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("TonizuToon", "https://tonizu.com", "tr", isNsfw = true, overrideVersionCode = 1),
SingleLang("ToonChill", "https://toonchill.com", "en", overrideVersionCode = 1),
SingleLang("ToonGod", "https://www.toongod.org", "en", isNsfw = true, overrideVersionCode = 5),
SingleLang("Toonily.net", "https://toonily.net", "en", isNsfw = true, className = "Toonilynet", overrideVersionCode = 2),
SingleLang("Toonily", "https://toonily.com", "en", isNsfw = true, overrideVersionCode = 11),
SingleLang("Toonizy", "https://toonizy.com", "en", isNsfw = true),
SingleLang("ToonMany", "https://toonmany.com", "en", isNsfw = true),

View File

@ -114,7 +114,7 @@ abstract class MadTheme(
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun searchMangaSelector(): String = ".book-detailed-item"

View File

@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -80,7 +80,7 @@ abstract class MangaBox(
return if (query.isNotBlank() && getAdvancedGenreFilters().isEmpty()) {
GET("$baseUrl/$simpleQueryPath${normalizeSearchQuery(query)}?page=$page", headers)
} else {
val url = baseUrl.toHttpUrlOrNull()!!.newBuilder()
val url = baseUrl.toHttpUrl().newBuilder()
if (getAdvancedGenreFilters().isNotEmpty()) {
url.addPathSegment("advanced_search")
url.addQueryParameter("page", page.toString())
@ -113,7 +113,7 @@ abstract class MangaBox(
}
}
}
GET(url.toString(), headers)
GET(url.build(), headers)
}
}
@ -210,7 +210,7 @@ abstract class MangaBox(
url = it.attr("abs:href").substringAfter(baseUrl) // intentionally not using setUrlWithoutDomain
name = it.text()
scanlator =
it.attr("abs:href").toHttpUrlOrNull()!!.host // show where chapters are actually from
it.attr("abs:href").toHttpUrl().host // show where chapters are actually from
}
date_upload = parseChapterDate(element.selectDateFromElement().text(), scanlator!!) ?: 0
}

View File

@ -163,7 +163,7 @@ abstract class MangaHub(
// search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/search/page/$page".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/search/page/$page".toHttpUrl().newBuilder()
url.addQueryParameter("q", query)
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
@ -178,7 +178,7 @@ abstract class MangaHub(
else -> {}
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun searchMangaSelector() = popularMangaSelector()

View File

@ -31,6 +31,7 @@ class MangaThemesiaGenerator : ThemeSourceGenerator {
SingleLang("BirdManga", "https://birdmanga.com", "en"),
SingleLang("Boosei", "https://boosei.net", "id", overrideVersionCode = 2),
SingleLang("Cartel de Manhwas", "https://carteldemanhwas.com", "es", overrideVersionCode = 5),
SingleLang("Constellar Scans", "https://constellarcomic.com", "en", isNsfw = true, overrideVersionCode = 16),
SingleLang("Cosmic Scans", "https://cosmic-scans.com", "en", overrideVersionCode = 2),
SingleLang("CosmicScans.id", "https://cosmicscans.id", "id", overrideVersionCode = 3, className = "CosmicScansID"),
SingleLang("Cypher Scans", "https://cypherscans.xyz", "en"),

View File

@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -78,7 +78,7 @@ abstract class MangaWorld(
override fun latestUpdatesParse(response: Response): MangasPage = searchMangaParse(response)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/archive?page=$page".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/archive?page=$page".toHttpUrl().newBuilder()
url.addQueryParameter("keyword", query)
filters.forEach { filter ->
@ -100,7 +100,7 @@ abstract class MangaWorld(
else -> {}
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun mangaDetailsParse(document: Document): SManga {

View File

@ -186,14 +186,14 @@ abstract class MMRCMS(
.let { elements ->
when {
// List layout (most sources)
elements.select("a").firstOrNull()?.hasText() == true -> elements.map { latestUpdatesFromElement(it, "a") }
elements.select("a[href]").firstOrNull()?.hasText() == true -> elements.map { latestUpdatesFromElement(it, "a[href]") }
// Grid layout (e.g. MangaID)
else -> document.select(gridLatestUpdatesSelector()).map { gridLatestUpdatesFromElement(it) }
}
}
.filterNotNull()
return MangasPage(mangas, document.select(latestUpdatesNextPageSelector()) != null)
return MangasPage(mangas, document.selectFirst(latestUpdatesNextPageSelector()) != null)
}
private fun latestUpdatesSelector() = "div.mangalist div.manga-item"
private fun latestUpdatesNextPageSelector() = "a[rel=next]"

View File

@ -9,7 +9,7 @@ class MMRCMSGenerator : ThemeSourceGenerator {
override val themeClass = "MMRCMS"
override val baseVersionCode = 6
override val baseVersionCode = 7
override val sources = listOf(
SingleLang("مانجا اون لاين", "https://onma.top", "ar", className = "onma"),
@ -19,6 +19,7 @@ class MMRCMSGenerator : ThemeSourceGenerator {
SingleLang("Scan VF", "https://www.scan-vf.net", "fr", overrideVersionCode = 1),
SingleLang("Komikid", "https://www.komikid.com", "id"),
SingleLang("Mangadoor", "https://mangadoor.com", "es", overrideVersionCode = 1),
SingleLang("Mangas.in", "https://mangas.in", "es", isNsfw = true, className = "MangasIn", overrideVersionCode = 1),
SingleLang("Utsukushii", "https://manga.utsukushii-bg.com", "bg", overrideVersionCode = 1),
SingleLang("Phoenix-Scans", "https://phoenix-scans.pl", "pl", className = "PhoenixScans", overrideVersionCode = 1),
SingleLang("Lelscan-VF", "https://lelscanvf.cc", "fr", className = "LelscanVF", overrideVersionCode = 2),

View File

@ -14,7 +14,7 @@ object SourceData {
"https://www.komikid.com" -> """{"name":"Komikid","base_url":"https://www.komikid.com","supports_latest":true,"item_url":"https://www.komikid.com/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Fantasy"},{"id":"7","name":"Gender Bender"},{"id":"8","name":"Historical"},{"id":"9","name":"Horror"},{"id":"10","name":"Josei"},{"id":"11","name":"Martial Arts"},{"id":"12","name":"Mature"},{"id":"13","name":"Mecha"},{"id":"14","name":"Mystery"},{"id":"15","name":"One Shot"},{"id":"16","name":"Psychological"},{"id":"17","name":"Romance"},{"id":"18","name":"School Life"},{"id":"19","name":"Sci-fi"},{"id":"20","name":"Seinen"},{"id":"21","name":"Shoujo"},{"id":"22","name":"Shoujo Ai"},{"id":"23","name":"Shounen"},{"id":"24","name":"Shounen Ai"},{"id":"25","name":"Slice of Life"},{"id":"26","name":"Sports"},{"id":"27","name":"Supernatural"},{"id":"28","name":"Tragedy"},{"id":"29","name":"Yaoi"},{"id":"30","name":"Yuri"}]}"""
"http://azbivo.webd.pro" -> """{"name":"Nikushima","base_url":"http://azbivo.webd.pro","supports_latest":false,"item_url":"\u003chtml\u003e \n \u003chead\u003e \n \u003cmeta http-equiv\u003d\"Content-Language\" content\u003d\"pl\"\u003e \n \u003cmeta http-equiv name\u003d\"pragma\" content\u003d\"no-cache\"\u003e \n \u003clink href\u003d\"style/style.css\" rel\u003d\"stylesheet\" type\u003d\"text/css\"\u003e \n \u003cmeta http-equiv\u003d\"Refresh\" content\u003d\"0; url\u003dhttps://www.webd.pl/_errnda.php?utm_source\u003dwn07\u0026amp;utm_medium\u003dwww\u0026amp;utm_campaign\u003dblock\"\u003e \n \u003cmeta name\u003d\"Robots\" content\u003d\"index, follow\"\u003e \n \u003cmeta name\u003d\"revisit-after\" content\u003d\"2 days\"\u003e \n \u003cmeta name\u003d\"rating\" content\u003d\"general\"\u003e \n \u003cmeta name\u003d\"keywords\" content\u003d\"STRONA ZAWIESZONA, WEBD, DOMENY, DOMENA, HOSTING, SERWER, INTERNET, PHP, MySQL, FTP, WEBMASTER, SERWERY WIRTUALNE, WWW, KONTO, MAIL, POCZTA, E-MAIL, NET, .COM, .ORG, TANIE, PHP+MySQL, DOMENY, DOMENA, HOSTING, SERWER, INTERNET, PHP, MySQL, FTP, WEBMASTER, SERWERY WIRTUALNE, WWW, KONTO, MAIL, POCZTA, E-MAIL, DOMENY, DOMENA, NET, .COM, .ORG, TANIE, PHP+MySQL, HOSTING, SERWER, INTERNET, PHP, MySQL, FTP, WEBMASTER, SERWERY WIRTUALNE, WWW, KONTO, MAIL, POCZTA, E-MAIL, NET, .COM, .ORG, TANIE, PHP+MySQL\"\u003e \n \u003cmeta name\u003d\"description\" content\u003d\"STRONA ZAWIESZONA - Oferujemy profesjonalny hosting z PHP + MySQL, rejestrujemy domeny. Sprawdz nasz hosting i przetestuj nasze serwery. Kupuj tanio domeny i serwery!\"\u003e \n \u003ctitle\u003eSTRONA ZAWIESZONA - WEBD.PL - Tw<54>j profesjonalny hosting za jedyne 4.99PLN! Serwery z PHP+MySQL, tanie domeny, serwer + domena .pl - taniej sie nie da!\u003c/title\u003e \n \u003cscript type\u003d\"text/javascript\"\u003e\nfunction init() {\n if (!document.getElementById) return\n var imgOriginSrc;\n var imgTemp \u003d new Array();\n var imgarr \u003d document.getElementsByTagName(\u0027img\u0027);\n for (var i \u003d 0; i \u003c imgarr.length; i++) {\n if (imgarr[i].getAttribute(\u0027hsrc\u0027)) {\n imgTemp[i] \u003d new Image();\n imgTemp[i].src \u003d imgarr[i].getAttribute(\u0027hsrc\u0027);\n imgarr[i].onmouseover \u003d function() {\n imgOriginSrc \u003d this.getAttribute(\u0027src\u0027);\n this.setAttribute(\u0027src\u0027,this.getAttribute(\u0027hsrc\u0027))\n }\n imgarr[i].onmouseout \u003d function() {\n this.setAttribute(\u0027src\u0027,imgOriginSrc)\n }\n }\n }\n}\nonload\u003dinit;\n\u003c/script\u003e \n \u003c/head\u003e \n \u003cbody\u003e\n Trwa przekierowanie .... \u0026gt;\u0026gt;\u0026gt;\u0026gt; \u003c!--\n--\u003e \n \u003c/body\u003e\n\u003c/html\u003e/","categories":[]}"""
"https://mangadoor.com" -> """{"name":"Mangadoor","base_url":"https://mangadoor.com","supports_latest":true,"item_url":"https://mangadoor.com/manga/","categories":[{"id":"1","name":"Acción"},{"id":"2","name":"Aventura"},{"id":"3","name":"Comedia"},{"id":"4","name":"Drama"},{"id":"5","name":"Ecchi"},{"id":"6","name":"Fantasía"},{"id":"7","name":"Gender Bender"},{"id":"8","name":"Harem"},{"id":"9","name":"Histórico"},{"id":"10","name":"Horror"},{"id":"11","name":"Josei"},{"id":"12","name":"Artes Marciales"},{"id":"13","name":"Maduro"},{"id":"14","name":"Mecha"},{"id":"15","name":"Misterio"},{"id":"16","name":"One Shot"},{"id":"17","name":"Psicológico"},{"id":"18","name":"Romance"},{"id":"19","name":"Escolar"},{"id":"20","name":"Ciencia Ficción"},{"id":"21","name":"Seinen"},{"id":"22","name":"Shoujo"},{"id":"23","name":"Shoujo Ai"},{"id":"24","name":"Shounen"},{"id":"25","name":"Shounen Ai"},{"id":"26","name":"Recuentos de la vida"},{"id":"27","name":"Deportes"},{"id":"28","name":"Supernatural"},{"id":"29","name":"Tragedia"},{"id":"30","name":"Yaoi"},{"id":"31","name":"Yuri"},{"id":"32","name":"Demonios"},{"id":"33","name":"Juegos"},{"id":"34","name":"Policial"},{"id":"35","name":"Militar"},{"id":"36","name":"Thriller"},{"id":"37","name":"Autos"},{"id":"38","name":"Música"},{"id":"39","name":"Vampiros"},{"id":"40","name":"Magia"},{"id":"41","name":"Samurai"},{"id":"42","name":"Boys love"},{"id":"43","name":"Hentai"}]}"""
"https://mangas.in" -> """{"name":"Mangas.pw","base_url":"https://mangas.in","supports_latest":true,"item_url":"https://mangas.in/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"},{"id":"33","name":"Hentai"},{"id":"34","name":"Smut"}]}"""
"https://mangas.in" -> """{"name":"Mangas.in","base_url":"https://mangas.in","supports_latest":true,"item_url":"https://mangas.in/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"},{"id":"33","name":"Hentai"},{"id":"34","name":"Smut"}]}"""
"https://manga.utsukushii-bg.com" -> """{"name":"Utsukushii","base_url":"https://manga.utsukushii-bg.com","supports_latest":true,"item_url":"https://manga.utsukushii-bg.com/manga/","categories":[{"id":"1","name":"Екшън"},{"id":"2","name":"Приключенски"},{"id":"3","name":"Комедия"},{"id":"4","name":"Драма"},{"id":"5","name":"Фентъзи"},{"id":"6","name":"Исторически"},{"id":"7","name":"Ужаси"},{"id":"8","name":"Джосей"},{"id":"9","name":"Бойни изкуства"},{"id":"10","name":"Меха"},{"id":"11","name":"Мистерия"},{"id":"12","name":"Самостоятелна/Пилотна глава"},{"id":"13","name":"Психологически"},{"id":"14","name":"Романтика"},{"id":"15","name":"Училищни"},{"id":"16","name":"Научна фантастика"},{"id":"17","name":"Сейнен"},{"id":"18","name":"Шоджо"},{"id":"19","name":"Реализъм"},{"id":"20","name":"Спорт"},{"id":"21","name":"Свръхестествено"},{"id":"22","name":"Трагедия"},{"id":"23","name":"Йокаи"},{"id":"24","name":"Паралелна вселена"},{"id":"25","name":"Супер сили"},{"id":"26","name":"Пародия"},{"id":"27","name":"Шонен"}]}"""
"https://phoenix-scans.pl" -> """{"name":"Phoenix-Scans","base_url":"https://phoenix-scans.pl","supports_latest":true,"item_url":"https://phoenix-scans.pl/manga/","categories":[{"id":"1","name":"Shounen"},{"id":"2","name":"Tragedia"},{"id":"3","name":"Szkolne życie"},{"id":"4","name":"Romans"},{"id":"5","name":"Zagadka"},{"id":"6","name":"Horror"},{"id":"7","name":"Dojrzałe"},{"id":"8","name":"Psychologiczne"},{"id":"9","name":"Przygodowe"},{"id":"10","name":"Akcja"},{"id":"11","name":"Komedia"},{"id":"12","name":"Zboczone"},{"id":"13","name":"Fantasy"},{"id":"14","name":"Harem"},{"id":"15","name":"Historyczne"},{"id":"16","name":"Manhua"},{"id":"17","name":"Manhwa"},{"id":"18","name":"Sztuki walki"},{"id":"19","name":"One shot"},{"id":"20","name":"Sci fi"},{"id":"21","name":"Seinen"},{"id":"22","name":"Shounen ai"},{"id":"23","name":"Spokojne życie"},{"id":"24","name":"Sport"},{"id":"25","name":"Nadprzyrodzone"},{"id":"26","name":"Webtoons"},{"id":"27","name":"Dramat"},{"id":"28","name":"Hentai"},{"id":"29","name":"Mecha"},{"id":"30","name":"Gender Bender"},{"id":"31","name":"Gry"},{"id":"32","name":"Yaoi"}],"tags":[{"id":"aktywne","name":"aktywne"},{"id":"zakonczone","name":"zakończone"},{"id":"porzucone","name":"porzucone"},{"id":"zawieszone","name":"zawieszone"},{"id":"zlicencjonowane","name":"zlicencjonowane"},{"id":"hentai","name":"Hentai"}]}"""
"https://lelscanvf.cc" -> """{"name":"Lelscan-VF","base_url":"https://lelscanvf.cc","supports_latest":true,"item_url":"https://lelscanvf.cc/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"}]}"""

View File

@ -8,7 +8,7 @@ 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 okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -67,7 +67,7 @@ abstract class Paprika(
return if (query.isNotBlank()) {
GET("$baseUrl/search?q=$query&page=$page")
} else {
val url = "$baseUrl/mangas/".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/mangas/".toHttpUrl().newBuilder()
filters.forEach { filter ->
when (filter) {
is GenreFilter -> url.addPathSegment(filter.toUriPart())
@ -76,7 +76,7 @@ abstract class Paprika(
}
}
url.addQueryParameter("page", page.toString())
GET(url.toString(), headers)
GET(url.build(), headers)
}
}

View File

@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
@ -36,7 +36,7 @@ abstract class PaprikaAlt(
return if (query.isNotBlank()) {
GET("$baseUrl/search?s=$query&post_type=manga&page=$page")
} else {
val url = "$baseUrl/genres/".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/genres/".toHttpUrl().newBuilder()
filters.forEach { filter ->
when (filter) {
is GenreFilter -> url.addPathSegment(filter.toUriPart())
@ -45,7 +45,7 @@ abstract class PaprikaAlt(
}
}
url.addQueryParameter("page", page.toString())
GET(url.toString(), headers)
GET(url.build(), headers)
}
}

View File

@ -18,6 +18,7 @@ import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.OkHttpClient
@ -178,13 +179,13 @@ open class Webtoons(
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/$langCode/search?keyword=$query".toHttpUrlOrNull()?.newBuilder()!!
val url = "$baseUrl/$langCode/search?keyword=$query".toHttpUrl().newBuilder()
val uriPart = (filters.find { it is SearchType } as? SearchType)?.toUriPart() ?: ""
url.addQueryParameter("searchType", uriPart)
if (uriPart != "WEBTOON" && page > 1) url.addQueryParameter("page", page.toString())
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun searchMangaSelector() = "#content > div.card_wrap.search ul:not(#filterLayer) li a"

View File

@ -17,7 +17,7 @@ import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
@ -34,8 +34,8 @@ open class WebtoonsTranslate(
// popularMangaRequest already returns manga sorted by latest update
override val supportsLatest = false
private val apiBaseUrl = "https://global.apis.naver.com".toHttpUrlOrNull()!!
private val mobileBaseUrl = "https://m.webtoons.com".toHttpUrlOrNull()!!
private val apiBaseUrl = "https://global.apis.naver.com".toHttpUrl()
private val mobileBaseUrl = "https://m.webtoons.com".toHttpUrl()
private val thumbnailBaseUrl = "https://mwebtoon-phinf.pstatic.net"
private val pageSize = 24
@ -53,7 +53,7 @@ open class WebtoonsTranslate(
.addQueryParameter("size", "$requeztSize")
.addQueryParameter("languageCode", translateLangCode)
.build()
return GET(url.toString(), headers)
return GET(url, headers)
}
// Webtoons translations doesn't really have a "popular" sort; just "UPDATE", "TITLE_ASC",
@ -161,7 +161,7 @@ open class WebtoonsTranslate(
override fun pageListParse(document: Document): List<Page> = throw Exception("Not used")
override fun chapterListRequest(manga: SManga): Request {
val mangaUrl = manga.url.toHttpUrlOrNull()!!
val mangaUrl = manga.url.toHttpUrl()
val titleNo = mangaUrl.queryParameter("titleNo")
val teamVersion = mangaUrl.queryParameter("teamVersion")
val chapterListUrl = apiBaseUrl
@ -210,7 +210,7 @@ open class WebtoonsTranslate(
}
override fun pageListRequest(chapter: SChapter): Request {
return GET(apiBaseUrl.resolve(chapter.url).toString(), headers)
return GET(apiBaseUrl.resolve(chapter.url)!!, headers)
}
override fun pageListParse(response: Response): List<Page> {

View File

@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
@ -78,7 +78,7 @@ abstract class WPComics(
return if (filterList.isEmpty()) {
GET("$baseUrl/?s=$query&post_type=comics&page=$page")
} else {
val url = "$baseUrl/$searchPath".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/$searchPath".toHttpUrl().newBuilder()
filterList.forEach { filter ->
when (filter) {

View File

@ -13,7 +13,7 @@ class WPComicsGenerator : ThemeSourceGenerator {
override val sources = listOf(
SingleLang("NetTruyen", "https://www.nettruyenclub.com", "vi", overrideVersionCode = 21),
SingleLang("NhatTruyen", "https://nhattruyenmax.com", "vi", overrideVersionCode = 13),
SingleLang("NhatTruyen", "https://nhattruyento.com", "vi", overrideVersionCode = 14),
SingleLang("TruyenChon", "http://truyenchon.com", "vi", overrideVersionCode = 3),
SingleLang("XOXO Comics", "https://xoxocomic.com", "en", className = "XoxoComics", overrideVersionCode = 3),
)

View File

@ -7,7 +7,7 @@ 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 okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
@ -58,7 +58,7 @@ abstract class ZManga(
// search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$baseUrl/advanced-search/${pagePathSegment(page)}".toHttpUrlOrNull()!!.newBuilder()
var url = "$baseUrl/advanced-search/${pagePathSegment(page)}".toHttpUrl().newBuilder()
url.addQueryParameter("title", query)
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
@ -90,13 +90,13 @@ abstract class ZManga(
// if site has project page, default value "hasProjectPage" = false
is ProjectFilter -> {
if (filter.toUriPart() == "project-filter-on") {
url = "$baseUrl$projectPageString/page/$page".toHttpUrlOrNull()!!.newBuilder()
url = "$baseUrl$projectPageString/page/$page".toHttpUrl().newBuilder()
}
}
else -> {}
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
open val projectPageString = "/project-list"

View File

@ -6,7 +6,7 @@ ext {
extName = 'Bato.to'
pkgNameSuffix = 'all.batoto'
extClass = '.BatoToFactory'
extVersionCode = 32
extVersionCode = 34
isNsfw = true
}

View File

@ -27,7 +27,6 @@ import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -110,7 +109,7 @@ open class BatoTo(
override fun latestUpdatesSelector(): String {
return when (siteLang) {
"" -> "div#series-list div.col"
"en" -> "div#series-list div.col.no-flag"
"en,en_us" -> "div#series-list div.col.no-flag"
else -> "div#series-list div.col:has([data-lang=\"$siteLang\"])"
}
}
@ -160,13 +159,13 @@ open class BatoTo(
else -> { /* Do Nothing */ }
}
}
client.newCall(GET(url.build().toString(), headers)).asObservableSuccess()
client.newCall(GET(url.build(), headers)).asObservableSuccess()
.map { response ->
queryParse(response)
}
}
else -> {
val url = "$baseUrl/browse".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/browse".toHttpUrl().newBuilder()
var min = ""
var max = ""
filters.forEach { filter ->
@ -232,7 +231,7 @@ open class BatoTo(
url.addQueryParameter("chapters", "$min-$max")
}
client.newCall(GET(url.build().toString(), headers)).asObservableSuccess()
client.newCall(GET(url.build(), headers)).asObservableSuccess()
.map { response ->
queryParse(response)
}

View File

@ -11,7 +11,7 @@ class LanguageOption(val lang: String, val siteLang: String = lang)
private val languages = listOf(
LanguageOption("all", ""),
// Lang options from publish.bato.to
LanguageOption("en"),
LanguageOption("en", "en,en_us"),
LanguageOption("ar"),
LanguageOption("bg"),
LanguageOption("zh"),
@ -58,7 +58,6 @@ private val languages = listOf(
LanguageOption("zh-Hans", "zh_hk"),
LanguageOption("zh-Hant", "zh_tw"),
LanguageOption("hr"),
LanguageOption("en-US", "en_us"),
LanguageOption("eo"),
LanguageOption("et"),
LanguageOption("fo"),

View File

@ -109,7 +109,7 @@ class Hennojin(override val lang: String, suffix: String) : ParsedHttpSource() {
private inline fun HttpUrl.request(
block: HttpUrl.Builder.() -> HttpUrl.Builder,
) = GET(newBuilder().block().toString(), headers)
) = GET(newBuilder().block().build(), headers)
private inline val Response.date: Long
get() = headers["Last-Modified"]?.run(httpDate::parse)?.time ?: 0L

View File

@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -111,14 +111,14 @@ class IMHentai(override val lang: String, private val imhLang: String) : ParsedH
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (filters.any { it is LanguageFilters && it.state.any { it.name == LANGUAGE_SPEECHLESS && it.state } }) { // edge case for language = speechless
val url = "$baseUrl/language/speechless/".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/language/speechless/".toHttpUrl().newBuilder()
if ((if (filters.isEmpty()) getFilterList() else filters).filterIsInstance<SortOrderFilter>()[0].state == 0) {
url.addPathSegment("popular")
}
return GET(url.toString())
return GET(url.build())
} else {
val url = "$baseUrl/search".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/search".toHttpUrl().newBuilder()
.addQueryParameter("key", query)
.addQueryParameter("page", page.toString())
.addQueryParameter(getLanguageURIByName(imhLang).uri, toBinary(true)) // main language always enabled
@ -143,7 +143,7 @@ class IMHentai(override val lang: String, private val imhLang: String) : ParsedH
else -> {}
}
}
return GET(url.toString())
return GET(url.build())
}
}

View File

@ -29,7 +29,7 @@ import kotlinx.serialization.json.long
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray
import kotlinx.serialization.json.putJsonObject
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
@ -206,7 +206,7 @@ abstract class Luscious(
private fun buildAlbumListRequest(page: Int, filters: FilterList, query: String = ""): Request {
val input = buildAlbumListRequestInput(page, filters, query)
val url = apiBaseUrl.toHttpUrlOrNull()!!.newBuilder()
val url = apiBaseUrl.toHttpUrl().newBuilder()
.addQueryParameter("operationName", "AlbumList")
.addQueryParameter("query", ALBUM_LIST_REQUEST_GQL)
.addQueryParameter("variables", input.toString())
@ -238,7 +238,7 @@ abstract class Luscious(
private fun buildAlbumInfoRequest(id: String): Request {
val input = buildAlbumInfoRequestInput(id)
val url = apiBaseUrl.toHttpUrlOrNull()!!.newBuilder()
val url = apiBaseUrl.toHttpUrl().newBuilder()
.addQueryParameter("operationName", "AlbumGet")
.addQueryParameter("query", albumInfoQuery)
.addQueryParameter("variables", input.toString())
@ -337,7 +337,7 @@ abstract class Luscious(
private fun buildAlbumPicturesPageUrl(id: String, page: Int): String {
val input = buildAlbumPicturesRequestInput(id, page)
return apiBaseUrl.toHttpUrlOrNull()!!.newBuilder()
return apiBaseUrl.toHttpUrl().newBuilder()
.addQueryParameter("operationName", "AlbumListOwnPictures")
.addQueryParameter("query", ALBUM_PICTURES_REQUEST_GQL)
.addQueryParameter("variables", input.toString())

View File

@ -27,7 +27,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -146,13 +146,13 @@ open class NHentai(
filterList.findInstance<OffsetPageFilter>()?.state?.toIntOrNull()?.plus(page) ?: page
if (favoriteFilter?.state == true) {
val url = "$baseUrl/favorites".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/favorites".toHttpUrl().newBuilder()
.addQueryParameter("q", "$fixedQuery $advQuery")
.addQueryParameter("page", offsetPage.toString())
return GET(url.toString(), headers)
return GET(url.build(), headers)
} else {
val url = "$baseUrl/search".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/search".toHttpUrl().newBuilder()
.addQueryParameter("q", "$fixedQuery $nhLangSearch$advQuery")
.addQueryParameter("page", offsetPage.toString())
@ -162,7 +162,7 @@ open class NHentai(
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
}

View File

@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
@ -137,7 +137,7 @@ open class NineManga(
override fun imageUrlParse(document: Document) = document.select("div.pic_box img.manga_pic").first()!!.attr("src").orEmpty()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/search/".toHttpUrlOrNull()!!.newBuilder()
val url = "$baseUrl/search/".toHttpUrl().newBuilder()
url.addQueryParameter("wd", query)
url.addQueryParameter("page", page.toString())
@ -166,7 +166,7 @@ open class NineManga(
url.addQueryParameter("type", "high")
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun searchMangaSelector() = popularMangaSelector()

View File

@ -76,7 +76,7 @@ class Photos18 : HttpSource(), ConfigurableSource {
if (filter is QueryFilter) filter.addQueryTo(url)
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun searchMangaParse(response: Response) = popularMangaParse(response)

View File

@ -6,6 +6,7 @@ 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.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Headers
@ -13,7 +14,6 @@ import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import rx.Observable
import uy.kohesive.injekt.injectLazy
@ -191,7 +191,7 @@ class Pixiv(override val lang: String) : HttpSource() {
for (p in countUp(start = 1)) {
searchUsers.url.setEncodedQueryParameter("p", p.toString())
val userIds = Jsoup.parse(searchUsers.execute().body.string())
val userIds = searchUsers.execute().asJsoup()
.select(".user-recommendation-item > a").eachAttr("href")
.map { it.substringAfterLast('/') }

View File

@ -5,7 +5,7 @@ ext {
extName = 'Yaoi Manga Online'
pkgNameSuffix = 'all.yaoimangaonline'
extClass = '.YaoiMangaOnline'
extVersionCode = 2
extVersionCode = 3
isNsfw = true
}

View File

@ -67,26 +67,25 @@ class YaoiMangaOnline : ParsedHttpSource() {
SManga.create().apply {
title = element.attr("title")
setUrlWithoutDomain(element.attr("href"))
thumbnail_url = element.selectFirst("img")!!.attr("src")
thumbnail_url = element.selectFirst("img")?.attr("src")
}
override fun mangaDetailsParse(document: Document) =
SManga.create().apply {
title = document.selectFirst(".entry-title")!!.text()
thumbnail_url = document.head()
.selectFirst("meta[property=og:image]")!!.attr("content")
description = document.selectFirst(".entry-content > p")!!
.html().replace("<br> ", "\n")
title = document.select("h1.entry-title").text()
thumbnail_url = document
.selectFirst(".herald-post-thumbnail img")?.attr("src")
description = document.select(".entry-content > p").text()
genre = document.select(".meta-tags > a").joinToString { it.text() }
}
override fun chapterListSelector() = "#acp_paging_menu > li"
override fun chapterListSelector() = ".mpp-toc a"
override fun chapterFromElement(element: Element) =
SChapter.create().apply {
name = element.selectFirst(".acp_title")!!.text()
name = element.ownText()
setUrlWithoutDomain(
element.selectFirst("a")?.attr("href") ?: element.baseUri(),
element.attr("href") ?: element.baseUri(),
)
}
@ -96,10 +95,10 @@ class YaoiMangaOnline : ParsedHttpSource() {
name = "Chapter"
url = response.request.url.encodedPath
}.let(::listOf)
}
}.reversed()
override fun pageListParse(document: Document) =
document.select(".size-full").mapIndexed { idx, img ->
document.select(".entry-content img").mapIndexed { idx, img ->
Page(idx, "", img.attr("src"))
}

View File

@ -80,7 +80,7 @@ class REManga : ParsedHttpSource() {
else -> {}
}
}
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun searchMangaSelector() = popularMangaSelector()

View File

@ -45,7 +45,7 @@ class MangaRok : ParsedHttpSource() {
.addQueryParameter("q", query)
.addQueryParameter("page", page.toString())
return GET(url.toString(), headers)
return GET(url.build(), headers)
}
override fun searchMangaSelector(): String =
".is-half > a.box"

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -0,0 +1,13 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Anchira'
pkgNameSuffix = 'en.anchira'
extClass = '.Anchira'
extVersionCode = 4
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

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