From 272d00b3520e75acb56717d51c20d3dd199fc0d0 Mon Sep 17 00:00:00 2001 From: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> Date: Sun, 1 May 2022 18:13:08 -0300 Subject: [PATCH] Migrate HC to Madara. (#11689) --- .../hipercool/res/mipmap-hdpi/ic_launcher.png | Bin .../hipercool/res/mipmap-mdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher.png | Bin .../madara}/hipercool/res/web_hi_res_512.png | Bin .../madara/hipercool/src/Hipercool.kt | 16 ++ .../multisrc/madara/MadaraGenerator.kt | 1 + src/pt/hipercool/AndroidManifest.xml | 2 - src/pt/hipercool/build.gradle | 17 -- .../extension/pt/hipercool/Hipercool.kt | 227 ------------------ .../extension/pt/hipercool/HipercoolDto.kt | 44 ---- 12 files changed, 17 insertions(+), 290 deletions(-) rename {src/pt => multisrc/overrides/madara}/hipercool/res/mipmap-hdpi/ic_launcher.png (100%) rename {src/pt => multisrc/overrides/madara}/hipercool/res/mipmap-mdpi/ic_launcher.png (100%) rename {src/pt => multisrc/overrides/madara}/hipercool/res/mipmap-xhdpi/ic_launcher.png (100%) rename {src/pt => multisrc/overrides/madara}/hipercool/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {src/pt => multisrc/overrides/madara}/hipercool/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {src/pt => multisrc/overrides/madara}/hipercool/res/web_hi_res_512.png (100%) create mode 100644 multisrc/overrides/madara/hipercool/src/Hipercool.kt delete mode 100644 src/pt/hipercool/AndroidManifest.xml delete mode 100644 src/pt/hipercool/build.gradle delete mode 100644 src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/Hipercool.kt delete mode 100644 src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/HipercoolDto.kt diff --git a/src/pt/hipercool/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/hipercool/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from src/pt/hipercool/res/mipmap-hdpi/ic_launcher.png rename to multisrc/overrides/madara/hipercool/res/mipmap-hdpi/ic_launcher.png diff --git a/src/pt/hipercool/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/hipercool/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from src/pt/hipercool/res/mipmap-mdpi/ic_launcher.png rename to multisrc/overrides/madara/hipercool/res/mipmap-mdpi/ic_launcher.png diff --git a/src/pt/hipercool/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/hipercool/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from src/pt/hipercool/res/mipmap-xhdpi/ic_launcher.png rename to multisrc/overrides/madara/hipercool/res/mipmap-xhdpi/ic_launcher.png diff --git a/src/pt/hipercool/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/hipercool/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from src/pt/hipercool/res/mipmap-xxhdpi/ic_launcher.png rename to multisrc/overrides/madara/hipercool/res/mipmap-xxhdpi/ic_launcher.png diff --git a/src/pt/hipercool/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/hipercool/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from src/pt/hipercool/res/mipmap-xxxhdpi/ic_launcher.png rename to multisrc/overrides/madara/hipercool/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/src/pt/hipercool/res/web_hi_res_512.png b/multisrc/overrides/madara/hipercool/res/web_hi_res_512.png similarity index 100% rename from src/pt/hipercool/res/web_hi_res_512.png rename to multisrc/overrides/madara/hipercool/res/web_hi_res_512.png diff --git a/multisrc/overrides/madara/hipercool/src/Hipercool.kt b/multisrc/overrides/madara/hipercool/src/Hipercool.kt new file mode 100644 index 000000000..97fbc938a --- /dev/null +++ b/multisrc/overrides/madara/hipercool/src/Hipercool.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.pt.hipercool + +import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor +import eu.kanade.tachiyomi.multisrc.madara.Madara +import okhttp3.OkHttpClient +import java.util.concurrent.TimeUnit + +class Hipercool : Madara("HipercooL", "https://hipercool.xyz", "pt-BR") { + + // Migrated from a custom CMS to Madara. + override val versionId = 2 + + override val client: OkHttpClient = super.client.newBuilder() + .addInterceptor(RateLimitInterceptor(1, 2, TimeUnit.SECONDS)) + .build() +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt index 83172646b..715066468 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt @@ -131,6 +131,7 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("HentaiZone", "https://hentaizone.xyz", "fr", isNsfw = true), SingleLang("Hentaidexy", "https://hentaidexy.com", "en", isNsfw = true, overrideVersionCode = 2), SingleLang("Heroz Scanlation", "https://herozscans.com", "en", overrideVersionCode = 1), + SingleLang("HipercooL", "https://hipercool.xyz", "pt-BR", isNsfw = true, className = "Hipercool"), SingleLang("Hiperdex", "https://hiperdex.com", "en", isNsfw = true, overrideVersionCode = 5), SingleLang("Hizomanga", "https://hizomanga.com", "ar", overrideVersionCode = 1), SingleLang("Hscans", "https://hscans.com", "en", overrideVersionCode = 2), diff --git a/src/pt/hipercool/AndroidManifest.xml b/src/pt/hipercool/AndroidManifest.xml deleted file mode 100644 index 30deb7f79..000000000 --- a/src/pt/hipercool/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/pt/hipercool/build.gradle b/src/pt/hipercool/build.gradle deleted file mode 100644 index bec809c15..000000000 --- a/src/pt/hipercool/build.gradle +++ /dev/null @@ -1,17 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlinx-serialization' - -ext { - extName = 'HipercooL' - pkgNameSuffix = 'pt.hipercool' - extClass = '.Hipercool' - extVersionCode = 9 - isNsfw = true -} - -dependencies { - implementation project(':lib-ratelimit') -} - -apply from: "$rootDir/common.gradle" diff --git a/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/Hipercool.kt b/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/Hipercool.kt deleted file mode 100644 index 2acbc6eb0..000000000 --- a/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/Hipercool.kt +++ /dev/null @@ -1,227 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.hipercool - -import eu.kanade.tachiyomi.lib.ratelimit.SpecificHostRateLimitInterceptor -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.network.asObservableSuccess -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.online.HttpSource -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonPrimitive -import okhttp3.Headers -import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.Response -import rx.Observable -import uy.kohesive.injekt.injectLazy -import java.text.SimpleDateFormat -import java.util.Locale - -class Hipercool : HttpSource() { - - // Hardcode the id because the language wasn't specific. - override val id: Long = 5898568703656160 - - override val name = "HipercooL" - - override val baseUrl = "https://hiper.cool" - - override val lang = "pt-BR" - - override val supportsLatest = true - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .addInterceptor(SpecificHostRateLimitInterceptor(baseUrl.toHttpUrl(), 1, 2)) - .addInterceptor(SpecificHostRateLimitInterceptor(STATIC_URL.toHttpUrl(), 1, 1)) - .build() - - override fun headersBuilder(): Headers.Builder = Headers.Builder() - .add("User-Agent", USER_AGENT) - .add("Referer", baseUrl) - .add("X-Requested-With", "XMLHttpRequest") - - private val json: Json by injectLazy() - - private fun genericMangaListParse(response: Response): MangasPage { - val chapters = response.parseAs>() - - if (chapters.isEmpty()) - return MangasPage(emptyList(), false) - - val mangaList = chapters - .distinctBy { it.book!!.title } - .map(::genericMangaFromObject) - - val hasNextPage = chapters.size == DEFAULT_COUNT - - return MangasPage(mangaList, hasNextPage) - } - - private fun genericMangaFromObject(chapter: HipercoolChapterDto): SManga = SManga.create().apply { - title = chapter.book!!.title - thumbnail_url = chapter.book.slug.toThumbnailUrl(chapter.book.revision) - url = "/books/" + chapter.book.slug - } - - // The source does not have popular mangas, so use latest instead. - override fun popularMangaRequest(page: Int): Request = latestUpdatesRequest(page) - - override fun popularMangaParse(response: Response): MangasPage = genericMangaListParse(response) - - override fun latestUpdatesRequest(page: Int): Request { - val start = (page - 1) * DEFAULT_COUNT - return GET("$baseUrl/api/books/chapters?start=$start&count=$DEFAULT_COUNT", headers) - } - - override fun latestUpdatesParse(response: Response): MangasPage = genericMangaListParse(response) - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val searchPayload = HipercoolSearchDto( - start = (page - 1) * DEFAULT_COUNT, - count = DEFAULT_COUNT, - text = query, - type = "text" - ) - - val body = json.encodeToString(searchPayload).toRequestBody(JSON_MEDIA_TYPE) - - return POST("$baseUrl/api/books/chapters/search", headers, body) - } - - override fun searchMangaParse(response: Response): MangasPage = genericMangaListParse(response) - - // Workaround to allow "Open in browser" use the real URL. - override fun fetchMangaDetails(manga: SManga): Observable { - return client.newCall(mangaDetailsApiRequest(manga)) - .asObservableSuccess() - .map { response -> - mangaDetailsParse(response).apply { initialized = true } - } - } - - private fun mangaDetailsApiRequest(manga: SManga): Request { - val slug = manga.url.substringAfterLast("/") - - return GET("$baseUrl/api/books/$slug", headers) - } - - override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply { - val book = response.parseAs() - - title = book.title - thumbnail_url = book.slug.toThumbnailUrl(book.revision) - description = book.synopsis.orEmpty() - artist = book.fixedTags["artista"].orEmpty().joinToString("; ") - author = book.fixedTags["autor"].orEmpty().joinToString("; ") - genre = book.fixedTags["tags"].orEmpty().joinToString() - } - - // Chapters are available in the same url of the manga details. - override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga) - - override fun chapterListParse(response: Response): List { - val book = response.parseAs() - - if (book.chapters is JsonPrimitive) - return emptyList() - - return json.decodeFromString>(book.chapters.toString()) - .map { chapterListItemParse(book, it) } - .reversed() - } - - private fun chapterListItemParse(book: HipercoolBookDto, chapter: HipercoolChapterDto): SChapter = - SChapter.create().apply { - name = "Cap. " + chapter.title - chapter_number = chapter.title.toFloatOrNull() ?: -1f - date_upload = chapter.publishedAt.toDate() - scanlator = book.fixedTags["tradutor"]?.joinToString(" & ") - - val fullUrl = "$baseUrl/books".toHttpUrl().newBuilder() - .addPathSegment(book.slug) - .addPathSegment(chapter.slug) - .addQueryParameter("images", chapter.images.toString()) - .addQueryParameter("revision", book.revision.toString()) - .toString() - - setUrlWithoutDomain(fullUrl) - } - - override fun fetchPageList(chapter: SChapter): Observable> { - val chapterUrl = (baseUrl + chapter.url).toHttpUrlOrNull()!! - - val bookSlug = chapterUrl.pathSegments[1] - val chapterSlug = chapterUrl.pathSegments[2] - val images = chapterUrl.queryParameter("images")!!.toInt() - val revision = chapterUrl.queryParameter("revision")!! - - val pages = List(images) { i -> - val imageUrl = "$STATIC_URL/books".toHttpUrl().newBuilder() - .addPathSegment(bookSlug) - .addPathSegment(chapterSlug) - .addPathSegment("$bookSlug-chapter-$chapterSlug-page-${i + 1}.jpg") - .addQueryParameter("revision", revision) - .toString() - - Page(i, chapter.url, imageUrl) - } - - return Observable.just(pages) - } - - override fun pageListParse(response: Response): List = - throw Exception("This method should not be called!") - - override fun fetchImageUrl(page: Page): Observable = Observable.just(page.imageUrl!!) - - override fun imageUrlParse(response: Response): String = "" - - override fun imageRequest(page: Page): Request { - val newHeaders = headersBuilder() - .set("Referer", baseUrl + page.url) - .build() - - return GET(page.imageUrl!!, newHeaders) - } - - private inline fun Response.parseAs(): T = use { - json.decodeFromString(it.body?.string().orEmpty()) - } - - private fun String.toDate(): Long { - return runCatching { DATE_FORMATTER.parse(this)?.time } - .getOrNull() ?: 0L - } - - private fun String.toThumbnailUrl(revision: Int): String = - "$STATIC_URL/books".toHttpUrlOrNull()!!.newBuilder() - .addPathSegment(this) - .addPathSegment("$this-cover.jpg") - .addQueryParameter("revision", revision.toString()) - .toString() - - companion object { - private const val STATIC_URL = "https://static.hiper.cool" - - private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaType() - - private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + - "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36" - - private const val DEFAULT_COUNT = 40 - - private val DATE_FORMATTER by lazy { - SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH) - } - } -} diff --git a/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/HipercoolDto.kt b/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/HipercoolDto.kt deleted file mode 100644 index 69fd82e6d..000000000 --- a/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/HipercoolDto.kt +++ /dev/null @@ -1,44 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.hipercool - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonElement - -@Serializable -data class HipercoolBookDto( - val chapters: JsonElement, - val revision: Int = 1, - val slug: String, - val synopsis: String? = null, - val tags: List = emptyList(), - val title: String -) { - val fixedTags: Map> - get() = tags - .groupBy(HipercoolTagDto::slug, HipercoolTagDto::values) - .mapValues { it.value.flatten().map(HipercoolTagDto::label) } -} - -@Serializable -data class HipercoolTagDto( - val label: String, - val values: List = emptyList(), - val slug: String -) - -@Serializable -data class HipercoolChapterDto( - @SerialName("_book") val book: HipercoolBookDto? = null, - val images: Int = 0, - @SerialName("publishied_at") val publishedAt: String, - val slug: String, - val title: String -) - -@Serializable -data class HipercoolSearchDto( - val start: Int, - val count: Int, - val text: String, - val type: String -)