From 141b80aa19db1f60586107f804fa2d51dc7768c9 Mon Sep 17 00:00:00 2001 From: SupremeDeity <29257166+SupremeDeity@users.noreply.github.com> Date: Wed, 22 Oct 2025 10:27:06 +0500 Subject: [PATCH] Add WeebDex (#11113) * Add WeebDex * fixes and add scanlator info * add placeholder icons * Refactoring with reviewed changes * minor refactoring to fix build * Url and Chapter title fixes fix: Add manga URL correctly. feat: Improve chapter title format by including volume and chapter numbers, and a "Oneshot" fallback. fix: An issue where oneshot chapters show Chapter null * Remove extraneous json field --- src/en/weebdex/build.gradle | 7 + .../weebdex/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3805 bytes .../weebdex/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2235 bytes .../weebdex/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 5197 bytes .../weebdex/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 10009 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 14211 bytes .../tachiyomi/extension/en/weebdex/WeebDex.kt | 173 ++++++++++++++++++ .../extension/en/weebdex/WeebDexConstants.kt | 109 +++++++++++ .../extension/en/weebdex/WeebDexFilters.kt | 113 ++++++++++++ .../extension/en/weebdex/WeebDexHelper.kt | 31 ++++ .../extension/en/weebdex/dto/ChapterDto.kt | 101 ++++++++++ .../extension/en/weebdex/dto/MangaDto.kt | 65 +++++++ 12 files changed, 599 insertions(+) create mode 100644 src/en/weebdex/build.gradle create mode 100644 src/en/weebdex/res/mipmap-hdpi/ic_launcher.png create mode 100644 src/en/weebdex/res/mipmap-mdpi/ic_launcher.png create mode 100644 src/en/weebdex/res/mipmap-xhdpi/ic_launcher.png create mode 100644 src/en/weebdex/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 src/en/weebdex/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDex.kt create mode 100644 src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexConstants.kt create mode 100644 src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexFilters.kt create mode 100644 src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexHelper.kt create mode 100644 src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/dto/ChapterDto.kt create mode 100644 src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/dto/MangaDto.kt diff --git a/src/en/weebdex/build.gradle b/src/en/weebdex/build.gradle new file mode 100644 index 000000000..664f1abff --- /dev/null +++ b/src/en/weebdex/build.gradle @@ -0,0 +1,7 @@ +ext { + extName = 'WeebDex' + extClass = '.WeebDex' + extVersionCode = 1 + isNsfw = true +} +apply from: "$rootDir/common.gradle" diff --git a/src/en/weebdex/res/mipmap-hdpi/ic_launcher.png b/src/en/weebdex/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..705665d32d67debe0886ab335158762c676c55d1 GIT binary patch literal 3805 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 TjVDbg00000NkvXXu0mjf+O`f7 literal 0 HcmV?d00001 diff --git a/src/en/weebdex/res/mipmap-mdpi/ic_launcher.png b/src/en/weebdex/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..be9f3ba8014b01a986930219f5018fedcc78cdaf GIT binary patch literal 2235 zcmV;s2t@aZP)Px-bxA})RA@u(ntg0l@Rf=V|wCG_Xj2eNXmBd0+_V8n-7%=zEq}4spe%j z*^=e#_*yHg1u$F=pk{G5A9UE*hwnWV@^PM*<%-qQYppB};A~ldZ+7!R+h<-u6!B@< znsNKzRq*9%=JhcHlnCBWeU!^?fqhCOhT!}IJma#EteKxImI=gGO7dwr|Bnga?q?(m zU5MZ%xr%lgAGY+ZfCZ4hS>8*W=65$@=EsNZa~$gq&hbX=P59&Fuj8Nb zo4{BgzX0OE>uo;U=pqw9*EQ%`2HRGL&=3!ao9;M}!pm<51VB@L4qNY`Y#HOut)tLc zg@A6G&(Lyxg#mCljVJc%0$|I*M=*2bh?!csiu3u%K)`E zqxLPtkY&f9z@1M!E!4XvjD|W>fieKPsel_` z6Dhzs+W+$?W{nsY+FkcG^jz};us|5Pmc`OzFX7$~b{PPn8Ur8{GwOgb{}yrpWK9L! z0HTO9=#&&#Xjw?jp>3g4flYfzuFhLX04OmL3<1oOEVSuW<2j@_fQltYe^&sYgkquw#-xA^ zVAg;P@GG)VF@Vj(ur1^PK!w<0DZn{)*O3D2!Gr=TmJ-0JR~$7kK7cMhD2mu@qkWPB zG5~d4e^Is&X zg68@(x>irLyU>EoXGI$&3q2qdFebSJhf=UC)N$~Ud=1pi#j{Q8p;!RILZ$+t8myeN z{Q>~wQs`St0LDZ{zM>M*h&fXNI5ni&hjeZ`oC^8iK{>Ub{? zu!}4-ZM`UBP0Qiit{NCOz(P`hrX5;JAoWJX6a%~4-%^k;dT{76MZ zJV-TXkG0(cIF*z?Hy9UM&&}jm2UCgR#`W$YU zlEB90jfhqli#mDyuWmI>(1+)7Y?M04GLkpoF8sS{(;}j@i~)NMyPEVn zhMFG5oW>M3uDBYNkpTMk4dBH$RVbkfXqhA)URsS?X2tQp;S4tHIE7>9su2iJ5Hl?e zEq5Lr2RC9i&BR4Azdw2n_SSwCZ(lVJfk+h;TFjY#94CPB6eS?EG_-6I(`u3k1%M-G zDgb);P=erE78NXssWllSG69@C7c(+X!P8=lo-Ncg8qfS2mFGVK=Ty`}2ICK7U{G*$ z;J94zBgQ0V4&MOtpK?AEJCx9}V%#7KtItx=MduE}F z#i1%}MCVgN1c3Fl$|%tR``01ND2WJ5Xi{DS!gxOGeBghEZch&3*D;35t!oBYc|kw!n?2d=oe?fu?p z-L&PUGUJ(5j~Bmwvs>=C)Zl+#e6ziYJ~54!`aVF{iA3VfSKn~Y3y)XUOkDtlUJK-h zcU%2r-KAurbTyS8KECgr->%q@8SFm+Amiw`9w$=+r=TYR;tQqmO77B)oSAj{w3Ko; zw_@LkX*;c(>Bl*pUVISxMoe!6=%>H{pZ-^ai!X@tj+eax{|DMfO?;N(!Z82<002ov JPDHLkV1l@r7hwPZ literal 0 HcmV?d00001 diff --git a/src/en/weebdex/res/mipmap-xhdpi/ic_launcher.png b/src/en/weebdex/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..bc2249daa6a9c80247c5ec2039841673f4acc7f2 GIT binary patch literal 5197 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 Hu0mjfp{K3U literal 0 HcmV?d00001 diff --git a/src/en/weebdex/res/mipmap-xxhdpi/ic_launcher.png b/src/en/weebdex/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..eb4e78b07bd21dbb89bbc34139ba256b037d02ab GIT binary patch literal 10009 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 O2Pi#Lm#>yJ3;91a@ed~e literal 0 HcmV?d00001 diff --git a/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDex.kt b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDex.kt new file mode 100644 index 000000000..0d1eddac0 --- /dev/null +++ b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDex.kt @@ -0,0 +1,173 @@ +package eu.kanade.tachiyomi.extension.en.weebdex + +import eu.kanade.tachiyomi.extension.en.weebdex.dto.ChapterDto +import eu.kanade.tachiyomi.extension.en.weebdex.dto.ChapterListDto +import eu.kanade.tachiyomi.extension.en.weebdex.dto.MangaDto +import eu.kanade.tachiyomi.extension.en.weebdex.dto.MangaListDto +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimit +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 keiyoushi.utils.parseAs +import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response + +class WeebDex : HttpSource() { + override val name = "WeebDex" + override val baseUrl = "https://weebdex.org" + override val lang = "en" + override val supportsLatest = true + + override val client = network.cloudflareClient.newBuilder() + .rateLimit(WeebDexConstants.RATE_LIMIT) + .build() + + override fun headersBuilder(): Headers.Builder = super.headersBuilder() + .add("Referer", "$baseUrl/") + + // -------------------- Popular -------------------- + + override fun popularMangaRequest(page: Int): Request { + val url = WeebDexConstants.API_MANGA_URL.toHttpUrl().newBuilder() + .addQueryParameter("page", page.toString()) + .addQueryParameter("sort", "views") + .addQueryParameter("order", "desc") + .addQueryParameter("hasChapters", "1") + .build() + return GET(url, headers) + } + + override fun popularMangaParse(response: Response): MangasPage { + val mangaListDto = response.parseAs() + val mangas = mangaListDto.toSMangaList() + return MangasPage(mangas, mangaListDto.hasNextPage) + } + + // -------------------- Latest -------------------- + override fun latestUpdatesRequest(page: Int): Request { + val url = WeebDexConstants.API_MANGA_URL.toHttpUrl().newBuilder() + .addQueryParameter("page", page.toString()) + .addQueryParameter("sort", "updatedAt") + .addQueryParameter("order", "desc") + .addQueryParameter("hasChapters", "1") + .build() + return GET(url, headers) + } + + override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response) + + // -------------------- Search -------------------- + override fun getFilterList(): FilterList = buildFilterList() + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val urlBuilder = WeebDexConstants.API_MANGA_URL.toHttpUrl().newBuilder() + .addQueryParameter("page", page.toString()) + + if (query.isNotBlank()) { + urlBuilder.addQueryParameter("title", query) + } else { + filters.forEach { filter -> + when (filter) { + is TagList -> { + filter.state.forEach { tag -> + if (tag.state) { + WeebDexConstants.tags[tag.name]?.let { tagId -> + urlBuilder.addQueryParameter("tag", tagId) + } + } + } + } + is TagsExcludeFilter -> { + filter.state.forEach { tag -> + if (tag.state) { + WeebDexConstants.tags[tag.name]?.let { tagId -> + urlBuilder.addQueryParameter("tagx", tagId) + } + } + } + } + is TagModeFilter -> urlBuilder.addQueryParameter("tmod", filter.state.toString()) + is TagExcludeModeFilter -> urlBuilder.addQueryParameter("txmod", filter.state.toString()) + else -> { /* Do Nothing */ } + } + } + } + + // Separated explicitly to be applied even when a search query is applied. + filters.forEach { filter -> + when (filter) { + is SortFilter -> urlBuilder.addQueryParameter("sort", filter.selected) + is OrderFilter -> urlBuilder.addQueryParameter("order", filter.selected) + is StatusFilter -> filter.selected?.let { urlBuilder.addQueryParameter("status", it) } + is DemographicFilter -> filter.selected?.let { urlBuilder.addQueryParameter("demographic", it) } + is ContentRatingFilter -> filter.selected?.let { urlBuilder.addQueryParameter("contentRating", it) } + is LangFilter -> filter.query?.let { urlBuilder.addQueryParameter("lang", it) } + is HasChaptersFilter -> if (filter.state) urlBuilder.addQueryParameter("hasChapters", "1") + is YearFromFilter -> filter.state.takeIf { it.isNotEmpty() }?.let { urlBuilder.addQueryParameter("yearFrom", it) } + is YearToFilter -> filter.state.takeIf { it.isNotEmpty() }?.let { urlBuilder.addQueryParameter("yearTo", it) } + else -> { /* Do Nothing */ } + } + } + + return GET(urlBuilder.build(), headers) + } + + override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response) + + // -------------------- Manga details -------------------- + + override fun mangaDetailsRequest(manga: SManga): Request { + return GET("${WeebDexConstants.API_URL}${manga.url}", headers) + } + + override fun mangaDetailsParse(response: Response): SManga { + val manga = response.parseAs() + return manga.toSManga() + } + + // -------------------- Chapters -------------------- + + override fun chapterListRequest(manga: SManga): Request { + // chapter list is paginated; get all pages + return GET("${WeebDexConstants.API_URL}${manga.url}/chapters?order=desc", headers) + } + + override fun chapterListParse(response: Response): List { + val chapters = mutableListOf() + + // Recursively parse pages + fun parsePage(chapterListDto: ChapterListDto) { + chapters.addAll(chapterListDto.toSChapterList()) + if (chapterListDto.hasNextPage) { + val nextUrl = response.request.url.newBuilder() + .setQueryParameter("page", (chapterListDto.page + 1).toString()) + .build() + val nextResponse = client.newCall(GET(nextUrl, headers)).execute() + val nextChapterListDto = nextResponse.parseAs() + parsePage(nextChapterListDto) + } + } + + parsePage(response.parseAs()) + return chapters + } + + // -------------------- Pages -------------------- + + override fun pageListRequest(chapter: SChapter): Request { + return GET("${WeebDexConstants.API_URL}${chapter.url}", headers) + } + + override fun pageListParse(response: Response): List { + val chapter = response.parseAs() + return chapter.toPageList() + } + + override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used") +} diff --git a/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexConstants.kt b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexConstants.kt new file mode 100644 index 000000000..8a98869fd --- /dev/null +++ b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexConstants.kt @@ -0,0 +1,109 @@ +package eu.kanade.tachiyomi.extension.en.weebdex + +object WeebDexConstants { + + // API Base URLs + const val API_URL = "https://api.weebdex.org" + const val CDN_URL = "https://srv.notdelta.xyz" + + // API Endpoints + const val API_MANGA_URL = "$API_URL/manga" + + // CDN Endpoints + const val CDN_COVER_URL = "$CDN_URL/covers" + const val CDN_DATA_URL = "$CDN_URL/data" + + // Rate Limit (API is 5 req/s, using conservative value) + const val RATE_LIMIT = 3 + + // Tags Map + val tags = mapOf( + // Formats + "Oneshot" to "99q3m1plnt", + "Web Comic" to "1utcekkc70", + "Doujinshi" to "fnvjk3jg1b", + "Adaptation" to "pbst9p8bd4", + "Full Color" to "6amsrv3w16", + "4-Koma" to "jnqtucy8q3", + + // Genres + "Action" to "g0eao31zjw", + "Adventure" to "pjl8oxd1ld", + "Boys' Love" to "1cnfhxwshb", + "Comedy" to "onj03z2gnf", + "Crime" to "bwec51tbms", + "Drama" to "00xq9oqthh", + "Fantasy" to "3lhj8r7s6n", + "Girls' Love" to "i9w6sjikyd", + "Historical" to "mmf28hr2co", + "Horror" to "rclreo8b25", + "Magical Girls" to "hy189x450f", + "Mystery" to "hv0hsu8kje", + "Romance" to "o0rm4pweru", + "Slice of Life" to "13x7xvq10k", + "Sports" to "zsvyg4whkp", + "Tragedy" to "85hmqw16y9", + + // Themes + "Cooking" to "9wm2j2zl1e", + "Crossdressing" to "arjr4qdpgc", + "Delinquents" to "h5ioz14hix", + "Genderswap" to "25k4gcfnfp", + "Magic" to "evt7r78scn", + "Monster Girls" to "ddjrvi8vsu", + "School Life" to "hobsiukk71", + "Shota" to "lu0sbwbs3r", + "Supernatural" to "c4rnaci8q6", + "Traditional Games" to "aqfqkul8rg", + "Vampires" to "djs29flsq6", + "Video Games" to "axstzcu7pc", + "Office Workers" to "6uytt2873o", + "Martial Arts" to "577a4hd52b", + "Zombies" to "szg24cwbrm", + "Survival" to "mt4vdanhfc", + "Police" to "acai4usl79", + "Mafia" to "qjuief8bi1", + + // Content Tags + "Gore" to "hceia50cf9", + "Sexual Violence" to "xh9k4t31ll", + ) + + // Demographics + val demographics = listOf( + "Any" to null, + "Shounen" to "shounen", + "Shoujo" to "shoujo", + "Josei" to "josei", + "Seinen" to "seinen", + ) + + // Publication Status + val statusList = listOf( + "Any" to null, + "Ongoing" to "ongoing", + "Completed" to "completed", + "Hiatus" to "hiatus", + "Cancelled" to "cancelled", + ) + + // Languages + val langList = listOf( + "Any" to null, + "English" to "en", + "Japanese" to "ja", + ) + + // Sort Options + val sortList = listOf( + "Views" to "views", + "Updated At" to "updatedAt", + "Created At" to "createdAt", + "Chapter Update" to "lastUploadedChapterAt", + "Title" to "title", + "Year" to "year", + "Rating" to "rating", + "Follows" to "follows", + "Chapters" to "chapters", + ) +} diff --git a/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexFilters.kt b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexFilters.kt new file mode 100644 index 000000000..09a9b66db --- /dev/null +++ b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexFilters.kt @@ -0,0 +1,113 @@ +package eu.kanade.tachiyomi.extension.en.weebdex + +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList + +internal fun buildFilterList() = FilterList( + Filter.Header("NOTE: Not all filters work on all sources."), + Filter.Separator(), + SortFilter(), + OrderFilter(), + StatusFilter(), + LangFilter(), + DemographicFilter(), + HasChaptersFilter(), + Filter.Header("Tags (ignored if searching by text)"), + TagModeFilter(), + TagList(WeebDexConstants.tags.keys.toTypedArray()), + Filter.Header("Tags to exclude (ignored if searching by text)"), + TagExcludeModeFilter(), + TagsExcludeFilter(WeebDexConstants.tags.keys.toTypedArray()), + ContentRatingFilter(), + YearFromFilter(), + YearToFilter(), +) + +internal class SortFilter : Filter.Select( + "Sort by", + WeebDexConstants.sortList.map { it.first }.toTypedArray(), + 0, +) { + val selected: String + get() = WeebDexConstants.sortList[state].second +} + +internal class OrderFilter : Filter.Select( + "Order", + arrayOf("Descending", "Ascending"), + 0, +) { + val selected: String + get() = if (state == 0) "desc" else "asc" +} + +internal class StatusFilter : Filter.Select( + "Status", + WeebDexConstants.statusList.map { it.first }.toTypedArray(), + 0, +) { + val selected: String? + get() = WeebDexConstants.statusList[state].second +} + +class TagCheckBox(name: String) : Filter.CheckBox(name, false) +class TagList(tags: Array) : Filter.Group("Tags", tags.map { TagCheckBox(it) }) +class TagsExcludeFilter(tags: Array) : Filter.Group( + "Tags to Exclude", + tags.map { TagCheckBox(it) }, +) + +class TagModeFilter : Filter.Select( + "Tag mode", + arrayOf("AND", "OR"), // what user sees + 0, +) { + val selected: String + get() = if (state == 0) "0" else "1" // backend wants 0=AND, 1=OR +} + +class TagExcludeModeFilter : Filter.Select( + "Exclude tag mode", + arrayOf("OR", "AND"), // what user sees + 0, +) { + val selected: String + get() = if (state == 0) "0" else "1" // backend wants 0=OR, 1=AND +} + +internal class DemographicFilter : Filter.Select( + "Demographic", + WeebDexConstants.demographics.map { it.first }.toTypedArray(), + 0, +) { + val selected: String? + get() = WeebDexConstants.demographics[state].second +} + +internal class ContentRatingFilter : Filter.Select( + "Content Rating", + arrayOf("Any", "Safe", "Suggestive", "Erotica", "Pornographic"), + 0, +) { + private val apiValues = arrayOf(null, "safe", "suggestive", "erotica", "pornographic") + + val selected: String? + get() = apiValues[state] +} + +internal class LangFilter : Filter.Select( + "Original Language", + WeebDexConstants.langList.map { it.first }.toTypedArray(), + 0, +) { + val query: String? + get() = WeebDexConstants.langList[state].second +} + +internal class HasChaptersFilter : Filter.CheckBox("Has Chapters", true) { + val selected: String? + get() = if (state) "1" else null +} + +internal class YearFromFilter : Filter.Text("Year (from)") +internal class YearToFilter : Filter.Text("Year (to)") diff --git a/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexHelper.kt b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexHelper.kt new file mode 100644 index 000000000..49d6db75b --- /dev/null +++ b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/WeebDexHelper.kt @@ -0,0 +1,31 @@ +import eu.kanade.tachiyomi.extension.en.weebdex.WeebDexConstants +import eu.kanade.tachiyomi.source.model.SManga +import keiyoushi.utils.tryParse +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.TimeZone + +class WeebDexHelper { + private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH).apply { + timeZone = TimeZone.getTimeZone("UTC") + } + fun parseStatus(status: String?): Int { + return when (status?.lowercase(Locale.ROOT)) { + "ongoing" -> SManga.ONGOING + "completed" -> SManga.COMPLETED + "hiatus" -> SManga.ON_HIATUS + "cancelled" -> SManga.CANCELLED + else -> SManga.UNKNOWN + } + } + + fun buildCoverUrl(mangaId: String, cover: eu.kanade.tachiyomi.extension.en.weebdex.dto.CoverDto?): String? { + if (cover == null) return null + val ext = cover.ext + return "${WeebDexConstants.CDN_COVER_URL}/$mangaId/${cover.id}$ext" + } + + fun parseDate(dateStr: String): Long { + return dateFormat.tryParse(dateStr) + } +} diff --git a/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/dto/ChapterDto.kt b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/dto/ChapterDto.kt new file mode 100644 index 000000000..72c680ab0 --- /dev/null +++ b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/dto/ChapterDto.kt @@ -0,0 +1,101 @@ +package eu.kanade.tachiyomi.extension.en.weebdex.dto + +import WeebDexHelper +import eu.kanade.tachiyomi.extension.en.weebdex.WeebDexConstants +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.jsoup.parser.Parser + +@Serializable +class ChapterListDto( + private val data: List = emptyList(), + val page: Int = 1, + val limit: Int = 0, + val total: Int = 0, +) { + val hasNextPage: Boolean + get() = page * limit < total + fun toSChapterList(): List { + return data.map { it.toSChapter() } + } +} + +@Serializable +class ChapterDto( + private val id: String, + private val title: String? = null, + private val chapter: String? = null, + private val volume: String? = null, + @SerialName("published_at") private val publishedAt: String = "", + private val data: List? = null, + @SerialName("data_optimized") private val dataOptimized: List? = null, + private val relationships: ChapterRelationshipsDto? = null, +) { + @Contextual + private val helper = WeebDexHelper() + + fun toSChapter(): SChapter { + val chapterName = mutableListOf() + // Build chapter name + volume?.let { + if (it.isNotEmpty()) { + chapterName.add("Vol.$it") + } + } + + chapter?.let { + if (it.isNotEmpty()) { + chapterName.add("Ch.$it") + } + } + + title?.let { + if (it.isNotEmpty()) { + if (chapterName.isNotEmpty()) { + chapterName.add("-") + } + chapterName.add(it) + } + } + + // if volume, chapter and title is empty its a oneshot + if (chapterName.isEmpty()) { + chapterName.add("Oneshot") + } + + return SChapter.create().apply { + url = "/chapter/$id" + name = Parser.unescapeEntities(chapterName.joinToString(" "), false) + chapter_number = chapter?.toFloat() ?: -2F + date_upload = helper.parseDate(publishedAt) + scanlator = relationships?.groups?.joinToString(", ") { it.name } + } + } + fun toPageList(): List { + val pagesArray = dataOptimized ?: data ?: emptyList() + val pages = mutableListOf() + + pagesArray.forEachIndexed { index, pageData -> + // pages in spec have 'name' field and images served from /data/{id}/{filename} + val filename = pageData.name + val chapterId = id + val imageUrl = filename?.takeIf { it.isNotBlank() && chapterId.isNotBlank() } + ?.let { "${WeebDexConstants.CDN_DATA_URL}/$chapterId/$it" } + pages.add(Page(index, imageUrl = imageUrl)) + } + return pages + } +} + +@Serializable +class ChapterRelationshipsDto( + val groups: List = emptyList(), +) + +@Serializable +class PageData( + val name: String? = null, +) diff --git a/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/dto/MangaDto.kt b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/dto/MangaDto.kt new file mode 100644 index 000000000..88d3cca2b --- /dev/null +++ b/src/en/weebdex/src/eu/kanade/tachiyomi/extension/en/weebdex/dto/MangaDto.kt @@ -0,0 +1,65 @@ +package eu.kanade.tachiyomi.extension.en.weebdex.dto + +import WeebDexHelper +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable + +@Serializable +class MangaListDto( + private val data: List = emptyList(), + val page: Int = 1, + val limit: Int = 0, + val total: Int = 0, +) { + val hasNextPage: Boolean + get() = page * limit < total + fun toSMangaList(): List { + return data.map { it.toSManga() } + } +} + +@Serializable +class MangaDto( + private val id: String, + private val title: String, + private val description: String = "", + private val status: String? = null, + val relationships: RelationshipsDto? = null, +) { + @Contextual + private val helper = WeebDexHelper() + fun toSManga(): SManga { + return SManga.create().apply { + title = this@MangaDto.title + description = this@MangaDto.description + status = helper.parseStatus(this@MangaDto.status) + thumbnail_url = helper.buildCoverUrl(id, relationships?.cover) + url = "/manga/$id" + relationships?.let { rel -> + author = rel.authors.joinToString(", ") { it.name } + artist = rel.artists.joinToString(", ") { it.name } + genre = rel.tags.joinToString(", ") { it.name } + } + } + } +} + +@Serializable +class RelationshipsDto( + val authors: List = emptyList(), + val artists: List = emptyList(), + val tags: List = emptyList(), + val cover: CoverDto? = null, +) + +@Serializable +class NamedEntity( + val name: String, +) + +@Serializable +class CoverDto( + val id: String, + val ext: String = ".jpg", +)