RizzComic/RealmOasis: domain change + support random urls (#7487)

* rizz: random urls go brrr

* bump, rename, get*Url

* icon

* 0ms?

* remove comments

* regex init in object, comment unused function for future

* lint

* suggestions

* pref
This commit is contained in:
AwkwardPeak7 2025-02-04 08:04:39 +05:00 committed by Draff
parent 6e7fcda20c
commit 42d3545a0a
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
8 changed files with 170 additions and 31 deletions

View File

@ -1,9 +1,9 @@
ext {
extName = 'Rizz Comic'
extClass = '.RizzComic'
extName = 'Realm Oasis'
extClass = '.RealmOasis'
themePkg = 'mangathemesia'
baseUrl = 'https://rizzfables.com'
overrideVersionCode = 5
baseUrl = 'https://realmoasis.com'
overrideVersionCode = 6
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,36 +1,42 @@
package eu.kanade.tachiyomi.extension.en.rizzcomic
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesiaAlt
import android.app.Application
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
import eu.kanade.tachiyomi.source.model.Filter
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.util.asJsoup
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat
import java.util.Locale
class RizzComic : MangaThemesiaAlt(
"Rizz Comic",
"https://rizzfables.com",
class RealmOasis : MangaThemesia(
"Realm Oasis",
"https://realmoasis.com",
"en",
mangaUrlDirectory = "/series",
mangaUrlDirectory = "/comics",
dateFormat = SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH),
) {
override val client = super.client.newBuilder()
.rateLimit(1, 3)
.rateLimitHost(baseUrl.toHttpUrl(), 1, 3)
.addInterceptor { chain ->
val request = chain.request()
val isApiRequest = request.header("X-API-Request") != null
@ -52,15 +58,22 @@ class RizzComic : MangaThemesiaAlt(
.build()
}
override val versionId = 2
override val versionId = 3
override val slugRegex = Regex("""^(r\d+-)""")
private val preferences =
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
// don't allow disabling random part setting
override fun setupPreferenceScreen(screen: PreferenceScreen) = Unit
override val listUrl = mangaUrlDirectory
override val listSelector = "div.bsx a"
private val mangaPath by lazy {
client.newCall(GET(baseUrl, headers))
.execute().asJsoup()
.selectFirst(".listupd a")!!
.absUrl("href")
.toHttpUrl()
.pathSegments[0]
.also {
mangaPathCache = it
}
}
override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", SortFilter.POPULAR)
override fun popularMangaParse(response: Response) = searchMangaParse(response)
@ -98,6 +111,7 @@ class RizzComic : MangaThemesiaAlt(
@Serializable
class Comic(
val id: String,
val title: String,
@SerialName("image_url") val cover: String? = null,
@SerialName("long_description") val synopsis: String? = null,
@ -108,16 +122,7 @@ class RizzComic : MangaThemesiaAlt(
val serialization: String? = null,
@SerialName("genre_id") val genres: String? = null,
) {
val slug get() = title.trim().lowercase()
.replace(slugRegex, "-")
.replace("-s-", "s-")
.replace("-ll-", "ll-")
val genreIds get() = genres?.split(",")?.map(String::trim)
companion object {
private val slugRegex = Regex("""[^a-z0-9]+""")
}
}
override fun searchMangaParse(response: Response): MangasPage {
@ -125,13 +130,13 @@ class RizzComic : MangaThemesiaAlt(
val entries = result.map { comic ->
SManga.create().apply {
url = "$mangaUrlDirectory/${comic.slug}/"
url = comic.id
title = comic.title
description = comic.synopsis
author = listOfNotNull(comic.author, comic.serialization).joinToString()
artist = comic.artist
status = comic.status.parseStatus()
thumbnail_url = comic.cover?.let { "$baseUrl/assets/images/$it" }
thumbnail_url = comic.cover?.let { "https://x.0ms.dev/q70/$baseUrl/assets/images/$it" }
genre = buildList {
add(comic.type?.capitalize())
comic.genreIds?.onEach { gId ->
@ -145,19 +150,82 @@ class RizzComic : MangaThemesiaAlt(
return MangasPage(entries, false)
}
override fun mangaDetailsRequest(manga: SManga): Request {
val url = baseUrl.toHttpUrl().newBuilder()
.addPathSegment(mangaPath)
.addPathSegment(
UrlUtils.generateSeriesLink(manga.url.toInt()),
).build()
return GET(url, headers)
}
override fun getMangaUrl(manga: SManga): String {
return buildString {
append(baseUrl)
append("/")
append(mangaPathCache)
append("/")
append(
UrlUtils.generateSeriesLink(manga.url.toInt()),
)
}
}
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsRequest(manga))
.asObservableSuccess()
.map { mangaDetailsParse(it).apply { description = manga.description } }
}
override fun chapterListRequest(manga: SManga): Request {
return mangaDetailsRequest(manga)
}
override fun chapterFromElement(element: Element): SChapter {
return super.chapterFromElement(element).apply {
val chapUrl = element.selectFirst("a")!!.absUrl("href")
val (seriesId, chapterId) = UrlUtils.extractChapterIds(chapUrl)
?: throw Exception("unable find chapter id from url")
url = "$seriesId/$chapterId"
}
}
override fun pageListRequest(chapter: SChapter): Request {
val (seriesId, chapterId) = chapter.url.split("/").take(2).map(String::toInt)
val url = baseUrl.toHttpUrl().newBuilder()
.addPathSegment(mangaPath)
.addPathSegment(
UrlUtils.generateChapterLink(seriesId, chapterId),
).build()
return GET(url, headers)
}
override fun getChapterUrl(chapter: SChapter): String {
val (seriesId, chapterId) = chapter.url.split("/").take(2).map(String::toInt)
return buildString {
append(baseUrl)
append("/")
append(mangaPathCache)
append("/")
append(
UrlUtils.generateChapterLink(seriesId, chapterId),
)
}
}
override fun imageRequest(page: Page): Request {
val newHeaders = headersBuilder()
.set("Accept", "image/avif,image/webp,image/png,image/jpeg,*/*")
.set("Referer", "$baseUrl/")
.build()
return GET(page.imageUrl!!, newHeaders)
return GET("https://x.0ms.dev/q70/" + page.imageUrl!!, newHeaders)
}
private inline fun <reified T> Response.parseAs(): T =
@ -175,4 +243,19 @@ class RizzComic : MangaThemesiaAlt(
val charPool = ('a'..'z') + ('A'..'Z')
return List(length) { charPool.random() }.joinToString("")
}
private var mangaPathCache: String = ""
get() {
if (field.isBlank()) {
field = preferences.getString(mangaPathPrefCache, "comics")!!
}
return field
}
set(newVal) {
preferences.edit().putString(mangaPathPrefCache, newVal).apply()
field = newVal
}
}
private const val mangaPathPrefCache = "manga_path"

View File

@ -0,0 +1,56 @@
package eu.kanade.tachiyomi.extension.en.rizzcomic
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
object UrlUtils {
private fun randomString(length: Int): String {
val charPool = ('a'..'z') + ('0'..'9')
return List(length) { charPool.random() }.joinToString("")
}
fun generateSeriesLink(seriesId: Int): String {
return buildString {
append(randomString(4))
append("s")
append(randomString(4))
append(seriesId.toString().padStart(5, '0'))
append(randomString(12))
}
}
fun generateChapterLink(seriesId: Int, chapterId: Int): String {
return buildString {
append(randomString(4))
append("c")
append(randomString(4))
append(seriesId.toString().padStart(5, '0'))
append(randomString(4))
append(chapterId.toString().padStart(6, '0'))
append(randomString(4))
}
}
// private val seriesRegex = """^[a-z0-9]{4}s[a-z0-9]{4}([0-9]{5})[a-z0-9]+$""".toRegex()
//
// fun extractSeriesId(url: String): Int? {
// val path = url.toHttpUrlOrNull()?.pathSegments?.lastOrNull()
// ?: return null
// return seriesRegex.find(path)?.groupValues?.get(1)?.toIntOrNull()
// }
private val ChaptersRegex = """^[a-z0-9]{4}c[a-z0-9]{4}([0-9]{5})[a-z0-9]{4}([0-9]{6})[a-z0-9]+$""".toRegex()
fun extractChapterIds(url: String): Pair<Int, Int>? {
val path = url.toHttpUrlOrNull()?.pathSegments?.lastOrNull()
?: return null
return ChaptersRegex.find(path)?.let { matchResult ->
val seriesId = matchResult.groupValues[1].toIntOrNull()
val chapterId = matchResult.groupValues[2].toIntOrNull()
if (seriesId != null && chapterId != null) {
seriesId to chapterId
} else {
null
}
}
}
}