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 { ext {
extName = 'Rizz Comic' extName = 'Realm Oasis'
extClass = '.RizzComic' extClass = '.RealmOasis'
themePkg = 'mangathemesia' themePkg = 'mangathemesia'
baseUrl = 'https://rizzfables.com' baseUrl = 'https://realmoasis.com'
overrideVersionCode = 5 overrideVersionCode = 6
} }
apply from: "$rootDir/common.gradle" 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 package eu.kanade.tachiyomi.extension.en.rizzcomic
import androidx.preference.PreferenceScreen import android.app.Application
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesiaAlt import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess 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.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page 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.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class RizzComic : MangaThemesiaAlt( class RealmOasis : MangaThemesia(
"Rizz Comic", "Realm Oasis",
"https://rizzfables.com", "https://realmoasis.com",
"en", "en",
mangaUrlDirectory = "/series", mangaUrlDirectory = "/comics",
dateFormat = SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH), dateFormat = SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH),
) { ) {
override val client = super.client.newBuilder() override val client = super.client.newBuilder()
.rateLimit(1, 3) .rateLimitHost(baseUrl.toHttpUrl(), 1, 3)
.addInterceptor { chain -> .addInterceptor { chain ->
val request = chain.request() val request = chain.request()
val isApiRequest = request.header("X-API-Request") != null val isApiRequest = request.header("X-API-Request") != null
@ -52,15 +58,22 @@ class RizzComic : MangaThemesiaAlt(
.build() .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 private val mangaPath by lazy {
override fun setupPreferenceScreen(screen: PreferenceScreen) = Unit client.newCall(GET(baseUrl, headers))
.execute().asJsoup()
override val listUrl = mangaUrlDirectory .selectFirst(".listupd a")!!
override val listSelector = "div.bsx a" .absUrl("href")
.toHttpUrl()
.pathSegments[0]
.also {
mangaPathCache = it
}
}
override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", SortFilter.POPULAR) override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", SortFilter.POPULAR)
override fun popularMangaParse(response: Response) = searchMangaParse(response) override fun popularMangaParse(response: Response) = searchMangaParse(response)
@ -98,6 +111,7 @@ class RizzComic : MangaThemesiaAlt(
@Serializable @Serializable
class Comic( class Comic(
val id: String,
val title: String, val title: String,
@SerialName("image_url") val cover: String? = null, @SerialName("image_url") val cover: String? = null,
@SerialName("long_description") val synopsis: String? = null, @SerialName("long_description") val synopsis: String? = null,
@ -108,16 +122,7 @@ class RizzComic : MangaThemesiaAlt(
val serialization: String? = null, val serialization: String? = null,
@SerialName("genre_id") val genres: 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) val genreIds get() = genres?.split(",")?.map(String::trim)
companion object {
private val slugRegex = Regex("""[^a-z0-9]+""")
}
} }
override fun searchMangaParse(response: Response): MangasPage { override fun searchMangaParse(response: Response): MangasPage {
@ -125,13 +130,13 @@ class RizzComic : MangaThemesiaAlt(
val entries = result.map { comic -> val entries = result.map { comic ->
SManga.create().apply { SManga.create().apply {
url = "$mangaUrlDirectory/${comic.slug}/" url = comic.id
title = comic.title title = comic.title
description = comic.synopsis description = comic.synopsis
author = listOfNotNull(comic.author, comic.serialization).joinToString() author = listOfNotNull(comic.author, comic.serialization).joinToString()
artist = comic.artist artist = comic.artist
status = comic.status.parseStatus() 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 { genre = buildList {
add(comic.type?.capitalize()) add(comic.type?.capitalize())
comic.genreIds?.onEach { gId -> comic.genreIds?.onEach { gId ->
@ -145,19 +150,82 @@ class RizzComic : MangaThemesiaAlt(
return MangasPage(entries, false) 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> { override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsRequest(manga)) return client.newCall(mangaDetailsRequest(manga))
.asObservableSuccess() .asObservableSuccess()
.map { mangaDetailsParse(it).apply { description = manga.description } } .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 { override fun imageRequest(page: Page): Request {
val newHeaders = headersBuilder() val newHeaders = headersBuilder()
.set("Accept", "image/avif,image/webp,image/png,image/jpeg,*/*") .set("Accept", "image/avif,image/webp,image/png,image/jpeg,*/*")
.set("Referer", "$baseUrl/") .set("Referer", "$baseUrl/")
.build() .build()
return GET(page.imageUrl!!, newHeaders) return GET("https://x.0ms.dev/q70/" + page.imageUrl!!, newHeaders)
} }
private inline fun <reified T> Response.parseAs(): T = private inline fun <reified T> Response.parseAs(): T =
@ -175,4 +243,19 @@ class RizzComic : MangaThemesiaAlt(
val charPool = ('a'..'z') + ('A'..'Z') val charPool = ('a'..'z') + ('A'..'Z')
return List(length) { charPool.random() }.joinToString("") 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
}
}
}
}