From da6869d9b41974fba5eb005dc3c64742472ecc7a Mon Sep 17 00:00:00 2001 From: hatozuki-programmer Date: Sat, 7 Dec 2024 16:01:11 -0500 Subject: [PATCH] Add KadoComi (Comic Walker) (#6473) * KadoComi (Comic Walker) extension Initial working commit for KadoComi (Comic Walker) * Cleanup * Clean up duplicate code * Remove unnecessary dependencies from build.gradle * Use book cover for thumbnail if available * Additional code clean up * Convert to using DTOs * Remove unnecessary fragment handling Fragment isn't sent to the server, so it's not necessary to do any handling here * Use fragment from URL * Fix author/artist names * Fix thumbnails Default of bookCover was interfering with thumbnail determination logic * Implement changes from feedback * Remove unneeded data keyword --- src/ja/kadocomi/build.gradle | 7 + .../kadocomi/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2849 bytes .../kadocomi/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1759 bytes .../kadocomi/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 3685 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6310 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 8823 bytes .../extension/ja/kadocomi/KadoComi.kt | 295 ++++++++++++++++++ .../extension/ja/kadocomi/KadoComiDto.kt | 71 +++++ 8 files changed, 373 insertions(+) create mode 100644 src/ja/kadocomi/build.gradle create mode 100644 src/ja/kadocomi/res/mipmap-hdpi/ic_launcher.png create mode 100644 src/ja/kadocomi/res/mipmap-mdpi/ic_launcher.png create mode 100644 src/ja/kadocomi/res/mipmap-xhdpi/ic_launcher.png create mode 100644 src/ja/kadocomi/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 src/ja/kadocomi/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 src/ja/kadocomi/src/eu/kanade/tachiyomi/extension/ja/kadocomi/KadoComi.kt create mode 100644 src/ja/kadocomi/src/eu/kanade/tachiyomi/extension/ja/kadocomi/KadoComiDto.kt diff --git a/src/ja/kadocomi/build.gradle b/src/ja/kadocomi/build.gradle new file mode 100644 index 000000000..253d371e9 --- /dev/null +++ b/src/ja/kadocomi/build.gradle @@ -0,0 +1,7 @@ +ext { + extName = "KadoComi" + extClass = ".KadoComi" + extVersionCode = 1 +} + +apply from: "$rootDir/common.gradle" diff --git a/src/ja/kadocomi/res/mipmap-hdpi/ic_launcher.png b/src/ja/kadocomi/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..bbf746376aaf49e876c746ed81eb2e4ca5430648 GIT binary patch literal 2849 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 ATmS$7 literal 0 HcmV?d00001 diff --git a/src/ja/kadocomi/res/mipmap-xxhdpi/ic_launcher.png b/src/ja/kadocomi/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..15be9c85586b0cb5f8b74e78e03e035d300035fc GIT binary patch literal 6310 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 Wc&X-Wh)}tml4?#vhPa_ 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 ^yjG?+pF=Uwgj#E6;JzI#nlEVf_d+Mu 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 + val request: Request = chain.request() + val urlString = request.url.toString() + val drmHash = request.url.fragment ?: "" + + val response: Response = chain.proceed(request) + + if (urlString.contains("$cdnUrl/images/") && urlString.contains("&Key-Pair-Id=")) { + val oldBody = response.body.bytes() + val descrambled = descrambleImage(oldBody, drmHash.decodeHex()) + val newBody = descrambled.toResponseBody("image/jpeg".toMediaTypeOrNull()) + response.newBuilder() + .body(newBody) + .build() + } else { + response + } + } + + override val client = network.client.newBuilder() + .addNetworkInterceptor(imageDescrambler) + .build() + + private val json: Json by injectLazy() + + // ============================== Manga Details =============================== + + override fun getMangaUrl(manga: SManga): String { + return "$baseUrl/detail/${getWorkCode(manga)}" + } + + override fun mangaDetailsRequest(manga: SManga): Request { + val url = apiUrl.toHttpUrl().newBuilder().apply { + addPathSegment("contents") + addPathSegment("details") + addPathSegment("work") + addQueryParameter("workCode", getWorkCode(manga)) + } + + return GET(url.build(), headers) + } + + override fun mangaDetailsParse(response: Response): SManga { + val details = json.decodeFromString(response.body.string()) + + var mangaAuthor: String? = null + var mangaArtist: String? = null + + details.work.authors?.forEach { + when (it.role) { + in AUTHOR_ROLES -> { + mangaAuthor = it.name + } + in ARTIST_ROLES -> { + mangaArtist = it.name + } + in COMBINED_ROLES -> { + mangaAuthor = it.name + mangaArtist = it.name + } + } + } + + return SManga.create().apply { + url = "/detail/${details.work.code}" + title = details.work.title + thumbnail_url = getThumbnailUrl(details.work) + author = mangaAuthor + artist = mangaArtist + description = details.work.summary + genre = getGenres(details.work) + status = when (details.work.serializationStatus.lowercase()) { + "ongoing" -> SManga.ONGOING + "unknown" -> SManga.UNKNOWN + else -> SManga.UNKNOWN + } + } + } + + private fun getGenres(work: KadoComiWork): String { + return listOfNotNull(work.genre?.name, work.subGenre?.name) + .plus(work.tags.orEmpty().map { it.name }) + .joinToString() + } + + // ============================== Chapters =============================== + + override fun getChapterUrl(chapter: SChapter): String { + // fragment contains two parameters in the format: #workCode={workCode}&episodeCode={episodeCode}" + // fragment comes from the URL as a single string, so manipulate to acquire the relevant values + val fragment = "$baseUrl${chapter.url}".toHttpUrl().fragment + val params = fragment!!.split("&") + val workCode = params[0].split("=")[1] + val episodeCode = params[1].split("=")[1] + return "$baseUrl/detail/$workCode/episodes/$episodeCode" + } + + override fun chapterListRequest(manga: SManga): Request { + val url = apiUrl.toHttpUrl().newBuilder().apply { + addPathSegment("contents") + addPathSegment("details") + addPathSegment("work") + addQueryParameter("workCode", getWorkCode(manga)) + } + + return GET(url.build(), headers) + } + + override fun chapterListParse(response: Response): List { + val details = json.decodeFromString(response.body.string()) + val workCode = details.work.code + + return details.latestEpisodes?.result.orEmpty().map { episode -> + SChapter.create().apply { + url = "/api/contents/viewer?episodeId=${episode.id}&imageSizeType=width%3A1284#workCode=$workCode&episodeCode=${episode.code}" + name = "${if (!episode.isActive) LOCK else ""} ${episode.title}" + date_upload = parseDate(episode.updateDate) + chapter_number = episode.internal.episodeNo.toFloat() + } + } + } + + // ============================== Pages =============================== + + override fun pageListParse(response: Response): List { + val viewer = json.decodeFromString(response.body.string()) + + val pages = viewer.manuscripts.mapIndexed { idx, manuscript -> + Page(idx, imageUrl = "${manuscript.drmImageUrl.substringAfter(baseUrl)}#${manuscript.drmHash}") + } + + if (pages.isEmpty()) { + throw Exception("このチャプターは非公開です\nChapter is not available!") + } + + return pages + } + + override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException() + + // ============================== Search =============================== + + override fun searchMangaParse(response: Response): MangasPage { + val results = json.decodeFromString(response.body.string()) + return MangasPage(searchResultsParse(results), results.result.size >= SEARCH_LIMIT) + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val offset = (SEARCH_LIMIT * page) - SEARCH_LIMIT + + val url = apiUrl.toHttpUrl().newBuilder().apply { + addPathSegment("search") + addPathSegment("keywords") + addQueryParameter("keywords", query) + addQueryParameter("limit", SEARCH_LIMIT.toString()) + addQueryParameter("offset", offset.toString()) + addQueryParameter("sortBy", "popularity") + } + + return GET(url.build(), headers) + } + + // ============================== Latest =============================== + + override fun latestUpdatesParse(response: Response): MangasPage { + val results = json.decodeFromString(response.body.string()) + return MangasPage(searchResultsParse(results), false) + } + + override fun latestUpdatesRequest(page: Int): Request { + val url = apiUrl.toHttpUrl().newBuilder().apply { + addPathSegment("series") + addPathSegment("new") + addQueryParameter("limit", NEW_LIMIT.toString()) + } + + return GET(url.build(), headers) + } + + // ============================== Popular =============================== + + override fun popularMangaParse(response: Response): MangasPage { + val results = json.decodeFromString(response.body.string()) + return MangasPage(searchResultsParse(results), false) + } + + override fun popularMangaRequest(page: Int): Request { + val url = apiUrl.toHttpUrl().newBuilder().apply { + addPathSegment("ranking") + addQueryParameter("limit", RANKING_LIMIT.toString()) + } + + return GET(url.build(), headers) + } + + // ============================= Utilities ============================== + + private fun getWorkCode(manga: SManga): String { + return manga.url.substringAfterLast("/") + } + + private fun getThumbnailUrl(work: KadoComiWork): String { + return work.bookCover ?: work.thumbnail + } + + // https://stackoverflow.com/a/66614516 + private fun String.decodeHex(): ByteArray { + check(length % 2 == 0) { "Must have an even length" } + + return chunked(2) + .map { it.toInt(16).toByte() } + .toByteArray() + } + + private fun descrambleImage(imageByteArray: ByteArray, hashByteArray: ByteArray): ByteArray { + return imageByteArray.mapIndexed { idx, byte -> + byte xor hashByteArray[idx % hashByteArray.size] + }.toByteArray() + } + + private fun searchResultsParse(results: KadoComiSearchResultsDto): List { + return results.result.map { + SManga.create().apply { + url = "/detail/${it.code}" + title = it.title + thumbnail_url = getThumbnailUrl(it) + } + } + } + + companion object { + // inactive chapter icon + private const val LOCK = "🔒 " + + // date formatting + private fun parseDate(dateStr: String): Long { + return try { + dateFormat.parse(dateStr)!!.time + } catch (_: ParseException) { + 0L + } + } + + private val dateFormat by lazy { + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH) + } + + // search limits, mimics site functionality + private const val SEARCH_LIMIT = 20 + private const val RANKING_LIMIT = 50 + private const val NEW_LIMIT = 100 + + // author/artist roles + private val AUTHOR_ROLES = arrayOf("原作") + private val ARTIST_ROLES = arrayOf("漫画", "作画") + private val COMBINED_ROLES = arrayOf("著者") + } +} diff --git a/src/ja/kadocomi/src/eu/kanade/tachiyomi/extension/ja/kadocomi/KadoComiDto.kt b/src/ja/kadocomi/src/eu/kanade/tachiyomi/extension/ja/kadocomi/KadoComiDto.kt new file mode 100644 index 000000000..02531f008 --- /dev/null +++ b/src/ja/kadocomi/src/eu/kanade/tachiyomi/extension/ja/kadocomi/KadoComiDto.kt @@ -0,0 +1,71 @@ +package eu.kanade.tachiyomi.extension.ja.kadocomi + +import kotlinx.serialization.Serializable + +@Serializable +class KadoComiWorkDto( + val work: KadoComiWork, + val latestEpisodes: KadoComiEpisodesResult?, +) + +@Serializable +class KadoComiSearchResultsDto( + val result: List = emptyList(), +) + +@Serializable +class KadoComiViewerDto( + val manuscripts: List = emptyList(), +) + +@Serializable +class KadoComiWork( + val code: String = "", + val id: String = "", + val thumbnail: String = "", + val bookCover: String?, + val title: String = "", + val serializationStatus: String = "", + val summary: String? = "", + val genre: KadoComiTag?, + val subGenre: KadoComiTag?, + val tags: List? = emptyList(), + val authors: List? = emptyList(), +) + +@Serializable +class KadoComiTag( + val name: String = "", +) + +@Serializable +class KadoComiAuthor( + val name: String = "", + val role: String = "", +) + +@Serializable +class KadoComiEpisodesResult( + val result: List = emptyList(), +) + +@Serializable +class KadoComiEpisode( + val id: String = "", + val code: String = "", + val title: String = "", + val updateDate: String = "", + val isActive: Boolean = false, + val internal: KadoComiEpisodeInternalInfo, +) + +@Serializable +class KadoComiEpisodeInternalInfo( + val episodeNo: Int = 1, +) + +@Serializable +class KadoComiManuscript( + val drmHash: String = "", + val drmImageUrl: String = "", +)