diff --git a/src/all/globalcomix/AndroidManifest.xml b/src/all/globalcomix/AndroidManifest.xml new file mode 100644 index 000000000..a58173090 --- /dev/null +++ b/src/all/globalcomix/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/all/globalcomix/assets/i18n/messages_en.properties b/src/all/globalcomix/assets/i18n/messages_en.properties new file mode 100644 index 000000000..af9af7967 --- /dev/null +++ b/src/all/globalcomix/assets/i18n/messages_en.properties @@ -0,0 +1,5 @@ +data_saver=Data saver +data_saver_summary=Enables smaller, more compressed images +invalid_manga_id=Not a valid comic ID +show_locked_chapters=Show chapters with pay-walled pages +show_locked_chapters_summary=Display chapters that require an account with a premium subscription diff --git a/src/all/globalcomix/build.gradle b/src/all/globalcomix/build.gradle new file mode 100644 index 000000000..637969339 --- /dev/null +++ b/src/all/globalcomix/build.gradle @@ -0,0 +1,12 @@ +ext { + extName = 'GlobalComix' + extClass = '.GlobalComixFactory' + extVersionCode = 1 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" + +dependencies { + implementation(project(":lib:i18n")) +} diff --git a/src/all/globalcomix/res/mipmap-hdpi/ic_launcher.png b/src/all/globalcomix/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..895825c12 Binary files /dev/null and b/src/all/globalcomix/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/globalcomix/res/mipmap-mdpi/ic_launcher.png b/src/all/globalcomix/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..6d7b6fdb3 Binary files /dev/null and b/src/all/globalcomix/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/globalcomix/res/mipmap-xhdpi/ic_launcher.png b/src/all/globalcomix/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..164d89c6e Binary files /dev/null and b/src/all/globalcomix/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/globalcomix/res/mipmap-xxhdpi/ic_launcher.png b/src/all/globalcomix/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..ef35b26b7 Binary files /dev/null and b/src/all/globalcomix/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/globalcomix/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/globalcomix/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..8cbd24aba Binary files /dev/null and b/src/all/globalcomix/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComix.kt b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComix.kt new file mode 100644 index 000000000..dfa30f1bf --- /dev/null +++ b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComix.kt @@ -0,0 +1,234 @@ +package eu.kanade.tachiyomi.extension.all.globalcomix + +import android.content.SharedPreferences +import androidx.preference.PreferenceScreen +import androidx.preference.SwitchPreferenceCompat +import eu.kanade.tachiyomi.extension.all.globalcomix.dto.ChapterDataDto.Companion.createChapter +import eu.kanade.tachiyomi.extension.all.globalcomix.dto.ChapterDto +import eu.kanade.tachiyomi.extension.all.globalcomix.dto.ChaptersDto +import eu.kanade.tachiyomi.extension.all.globalcomix.dto.EntityDto +import eu.kanade.tachiyomi.extension.all.globalcomix.dto.MangaDataDto.Companion.createManga +import eu.kanade.tachiyomi.extension.all.globalcomix.dto.MangaDto +import eu.kanade.tachiyomi.extension.all.globalcomix.dto.MangasDto +import eu.kanade.tachiyomi.extension.all.globalcomix.dto.UnknownEntity +import eu.kanade.tachiyomi.lib.i18n.Intl +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource +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.getPreferencesLazy +import keiyoushi.utils.parseAs +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.plus +import kotlinx.serialization.modules.polymorphic +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.TimeZone + +abstract class GlobalComix(final override val lang: String, private val extLang: String = lang) : + ConfigurableSource, HttpSource() { + + override val name = "GlobalComix" + override val baseUrl = webUrl + override val supportsLatest = true + + private val preferences: SharedPreferences by getPreferencesLazy() + + private val json = Json { + isLenient = true + ignoreUnknownKeys = true + serializersModule += SerializersModule { + polymorphic(EntityDto::class) { + defaultDeserializer { UnknownEntity.serializer() } + } + } + } + + private val intl = Intl( + language = lang, + baseLanguage = english, + availableLanguages = setOf(english), + classLoader = this::class.java.classLoader!!, + createMessageFileName = { lang -> Intl.createDefaultMessageFileName(lang) }, + ) + + final override fun headersBuilder() = super.headersBuilder().apply { + set("Referer", "$baseUrl/") + set("Origin", baseUrl) + set("x-gc-client", clientId) + set("x-gc-identmode", "cookie") + } + + override val client = network.client.newBuilder() + .rateLimit(3) + .build() + + private fun simpleQueryRequest(page: Int, orderBy: String?, query: String?): Request { + val url = apiSearchUrl.toHttpUrl().newBuilder() + .addQueryParameter("lang_id[]", extLang) + .addQueryParameter("p", page.toString()) + + orderBy?.let { url.addQueryParameter("sort", it) } + query?.let { url.addQueryParameter("q", it) } + + return GET(url.build(), headers) + } + + override fun popularMangaRequest(page: Int): Request = + simpleQueryRequest(page, orderBy = null, query = null) + + override fun popularMangaParse(response: Response): MangasPage = + mangaListParse(response) + + override fun latestUpdatesRequest(page: Int): Request = + simpleQueryRequest(page, "recent", query = null) + + override fun latestUpdatesParse(response: Response): MangasPage = + mangaListParse(response) + + private fun mangaListParse(response: Response): MangasPage { + val isSingleItemLookup = response.request.url.toString().startsWith(apiMangaUrl) + return if (!isSingleItemLookup) { + // Normally, the response is a paginated list of mangas + // The results property will be a JSON array + response.parseAs().payload!!.let { dto -> + MangasPage( + dto.results.map { it -> it.createManga() }, + dto.pagination.hasNextPage, + ) + } + } else { + // However, when using the 'id:' query prefix (via the UrlActivity for example), + // the response is a single manga and the results property will be a JSON object + MangasPage( + listOf( + response.parseAs().payload!! + .results + .createManga(), + ), + false, + ) + } + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + // If the query is a slug ID, return the manga directly + if (query.startsWith(prefixIdSearch)) { + val mangaSlugId = query.removePrefix(prefixIdSearch) + + if (mangaSlugId.isEmpty()) { + throw Exception(intl["invalid_manga_id"]) + } + + val url = apiMangaUrl.toHttpUrl().newBuilder() + .addPathSegment(mangaSlugId) + .build() + + return GET(url, headers) + } + + return simpleQueryRequest(page, orderBy = "relevance", query) + } + + override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response) + + override fun getMangaUrl(manga: SManga): String = "$webComicUrl/${titleToSlug(manga.title)}" + + override fun mangaDetailsRequest(manga: SManga): Request { + val url = apiMangaUrl.toHttpUrl().newBuilder() + .addPathSegment(titleToSlug(manga.title)) + .build() + + return GET(url, headers) + } + + override fun mangaDetailsParse(response: Response): SManga = + response.parseAs().payload!! + .results + .createManga() + + override fun chapterListRequest(manga: SManga): Request { + val url = apiSearchUrl.toHttpUrl().newBuilder() + .addPathSegment(manga.url) // manga.url contains the the comic id + .addPathSegment("releases") + .addQueryParameter("lang_id", extLang) + .addQueryParameter("all", "true") + .toString() + + return GET(url, headers) + } + + override fun chapterListParse(response: Response): List = + response.parseAs().payload!!.results.filterNot { dto -> + dto.isPremium && !preferences.showLockedChapters + }.map { it.createChapter() } + + override fun getChapterUrl(chapter: SChapter): String = + "$baseUrl/read/${chapter.url}" + + override fun pageListRequest(chapter: SChapter): Request { + val chapterKey = chapter.url + val url = "$apiChapterUrl/$chapterKey" + return GET(url, headers) + } + + override fun pageListParse(response: Response): List { + val chapterKey = response.request.url.pathSegments.last() + val chapterWebUrl = "$webChapterUrl/$chapterKey" + + return response.parseAs() + .payload!! + .results + .page_objects!! + .map { dto -> if (preferences.useDataSaver) dto.mobile_image_url else dto.desktop_image_url } + .mapIndexed { index, url -> Page(index, "$chapterWebUrl/$index", url) } + } + + override fun imageUrlParse(response: Response): String = "" + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + val dataSaverPref = SwitchPreferenceCompat(screen.context).apply { + key = getDataSaverPreferenceKey(extLang) + title = intl["data_saver"] + summary = intl["data_saver_summary"] + setDefaultValue(false) + } + + val showLockedChaptersPref = SwitchPreferenceCompat(screen.context).apply { + key = getShowLockedChaptersPreferenceKey(extLang) + title = intl["show_locked_chapters"] + summary = intl["show_locked_chapters_summary"] + setDefaultValue(true) + } + + screen.addPreference(dataSaverPref) + screen.addPreference(showLockedChaptersPref) + } + + private inline fun Response.parseAs(): T = parseAs(json) + + private val SharedPreferences.useDataSaver + get() = getBoolean(getDataSaverPreferenceKey(extLang), false) + + private val SharedPreferences.showLockedChapters + get() = getBoolean(getShowLockedChaptersPreferenceKey(extLang), true) + + companion object { + fun titleToSlug(title: String) = title.trim() + .lowercase(Locale.US) + .replace(titleSpecialCharactersRegex, "-") + + val titleSpecialCharactersRegex = "[^a-z0-9]+".toRegex() + val dateFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) + .apply { timeZone = TimeZone.getTimeZone("UTC") } + } +} diff --git a/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComixConstants.kt b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComixConstants.kt new file mode 100644 index 000000000..93807cff6 --- /dev/null +++ b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComixConstants.kt @@ -0,0 +1,30 @@ +package eu.kanade.tachiyomi.extension.all.globalcomix + +const val lockSymbol = "🔒" + +// Language codes used for translations +const val english = "en" + +// JSON discriminators +const val release = "Release" +const val comic = "Comic" +const val artist = "Artist" +const val releasePage = "ReleasePage" + +// Web requests +const val webUrl = "https://globalcomix.com" +const val webComicUrl = "$webUrl/c" +const val webChapterUrl = "$webUrl/read" +const val apiUrl = "https://api.globalcomix.com/v1" +const val apiMangaUrl = "$apiUrl/read" +const val apiChapterUrl = "$apiUrl/readV2" +const val apiSearchUrl = "$apiUrl/comics" + +const val clientId = "gck_d0f170d5729446dcb3b55e6b3ebc7bf6" + +// Search prefix for title ids +const val prefixIdSearch = "id:" + +// Preferences +fun getDataSaverPreferenceKey(extLang: String): String = "dataSaver_$extLang" +fun getShowLockedChaptersPreferenceKey(extLang: String): String = "showLockedChapters_$extLang" diff --git a/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComixFactory.kt b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComixFactory.kt new file mode 100644 index 000000000..39cf5c950 --- /dev/null +++ b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComixFactory.kt @@ -0,0 +1,92 @@ +package eu.kanade.tachiyomi.extension.all.globalcomix + +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceFactory + +class GlobalComixFactory : SourceFactory { + override fun createSources(): List = listOf( + GlobalComixAlbanian(), + GlobalComixArabic(), + GlobalComixBulgarian(), + GlobalComixBengali(), + GlobalComixBrazilianPortuguese(), + GlobalComixChineseMandarin(), + GlobalComixCzech(), + GlobalComixGerman(), + GlobalComixDanish(), + GlobalComixGreek(), + GlobalComixEnglish(), + GlobalComixSpanish(), + GlobalComixPersian(), + GlobalComixFinnish(), + GlobalComixFilipino(), + GlobalComixFrench(), + GlobalComixHindi(), + GlobalComixHungarian(), + GlobalComixIndonesian(), + GlobalComixItalian(), + GlobalComixHebrew(), + GlobalComixJapanese(), + GlobalComixKorean(), + GlobalComixLatvian(), + GlobalComixMalay(), + GlobalComixDutch(), + GlobalComixNorwegian(), + GlobalComixPolish(), + GlobalComixPortugese(), + GlobalComixRomanian(), + GlobalComixRussian(), + GlobalComixSwedish(), + GlobalComixSlovak(), + GlobalComixSlovenian(), + GlobalComixTamil(), + GlobalComixThai(), + GlobalComixTurkish(), + GlobalComixUkrainian(), + GlobalComixUrdu(), + GlobalComixVietnamese(), + GlobalComixChineseCantonese(), + ) +} + +class GlobalComixAlbanian : GlobalComix("al") +class GlobalComixArabic : GlobalComix("ar") +class GlobalComixBulgarian : GlobalComix("bg") +class GlobalComixBengali : GlobalComix("bn") +class GlobalComixBrazilianPortuguese : GlobalComix("pt-BR", "br") +class GlobalComixChineseMandarin : GlobalComix("zh-Hans", "cn") +class GlobalComixCzech : GlobalComix("cs", "cz") +class GlobalComixGerman : GlobalComix("de") +class GlobalComixDanish : GlobalComix("dk") +class GlobalComixGreek : GlobalComix("el") +class GlobalComixEnglish : GlobalComix("en") +class GlobalComixSpanish : GlobalComix("es") +class GlobalComixPersian : GlobalComix("fa") +class GlobalComixFinnish : GlobalComix("fi") +class GlobalComixFilipino : GlobalComix("fil", "fo") +class GlobalComixFrench : GlobalComix("fr") +class GlobalComixHindi : GlobalComix("hi") +class GlobalComixHungarian : GlobalComix("hu") +class GlobalComixIndonesian : GlobalComix("id") +class GlobalComixItalian : GlobalComix("it") +class GlobalComixHebrew : GlobalComix("he", "iw") +class GlobalComixJapanese : GlobalComix("ja", "jp") +class GlobalComixKorean : GlobalComix("ko", "kr") +class GlobalComixLatvian : GlobalComix("lv") +class GlobalComixMalay : GlobalComix("ms", "my") +class GlobalComixDutch : GlobalComix("nl") +class GlobalComixNorwegian : GlobalComix("no") +class GlobalComixPolish : GlobalComix("pl") +class GlobalComixPortugese : GlobalComix("pt") +class GlobalComixRomanian : GlobalComix("ro") +class GlobalComixRussian : GlobalComix("ru") +class GlobalComixSwedish : GlobalComix("sv", "se") +class GlobalComixSlovak : GlobalComix("sk") +class GlobalComixSlovenian : GlobalComix("sl") +class GlobalComixTamil : GlobalComix("ta") +class GlobalComixThai : GlobalComix("th") +class GlobalComixTurkish : GlobalComix("tr") +class GlobalComixUkrainian : GlobalComix("uk", "ua") +class GlobalComixUrdu : GlobalComix("ur") +class GlobalComixVietnamese : GlobalComix("vi") +class GlobalComixChineseCantonese : GlobalComix("zh-Hant", "zh") diff --git a/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComixUrlActivity.kt b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComixUrlActivity.kt new file mode 100644 index 000000000..5de91fbff --- /dev/null +++ b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/GlobalComixUrlActivity.kt @@ -0,0 +1,45 @@ +package eu.kanade.tachiyomi.extension.all.globalcomix + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.widget.Toast +import kotlin.system.exitProcess + +/** + * Springboard that accepts https://globalcomix.com/c/xxx intents and redirects them to + * the main tachiyomi process. The idea is to not install the intent filter unless + * you have this extension installed, but still let the main tachiyomi app control + * things. + */ +class GlobalComixUrlActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val pathSegments = intent?.data?.pathSegments + + // Supported path: /c/title-slug + if (pathSegments != null && pathSegments.size > 1) { + val titleId = pathSegments[1] + val mainIntent = Intent().apply { + action = "eu.kanade.tachiyomi.SEARCH" + putExtra("query", prefixIdSearch + titleId) + putExtra("filter", packageName) + } + + try { + startActivity(mainIntent) + } catch (e: ActivityNotFoundException) { + Log.e("GlobalComixUrlActivity", e.toString()) + } + } else { + Log.e("GlobalComixUrlActivity", "Received data URL is unsupported: ${intent?.data}") + Toast.makeText(this, "This URL cannot be handled by the GlobalComix extension.", Toast.LENGTH_SHORT).show() + } + + finish() + exitProcess(0) + } +} diff --git a/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/ArtistDto.kt b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/ArtistDto.kt new file mode 100644 index 000000000..96f3da9e3 --- /dev/null +++ b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/ArtistDto.kt @@ -0,0 +1,13 @@ +package eu.kanade.tachiyomi.extension.all.globalcomix.dto + +import eu.kanade.tachiyomi.extension.all.globalcomix.artist +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Suppress("PropertyName") +@Serializable +@SerialName(artist) +class ArtistDto( + val name: String, // Slug + val roman_name: String?, +) : EntityDto() diff --git a/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/ChapterDto.kt b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/ChapterDto.kt new file mode 100644 index 000000000..adb8570f4 --- /dev/null +++ b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/ChapterDto.kt @@ -0,0 +1,63 @@ +package eu.kanade.tachiyomi.extension.all.globalcomix.dto + +import eu.kanade.tachiyomi.extension.all.globalcomix.GlobalComix.Companion.dateFormatter +import eu.kanade.tachiyomi.extension.all.globalcomix.lockSymbol +import eu.kanade.tachiyomi.extension.all.globalcomix.release +import eu.kanade.tachiyomi.source.model.SChapter +import keiyoushi.utils.tryParse +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +typealias ChapterDto = ResponseDto +typealias ChaptersDto = PaginatedResponseDto + +@Suppress("PropertyName") +@Serializable +@SerialName(release) +class ChapterDataDto( + val title: String, + val chapter: String, // Stringified number + val key: String, // UUID, required for /readV2 endpoint + val premium_only: Int? = 0, + val published_time: String, + + // Only available when calling the /readV2 endpoint + val page_objects: List?, +) : EntityDto() { + val isPremium: Boolean + get() = premium_only == 1 + + companion object { + /** + * Create an [SChapter] instance from the JSON DTO element. + */ + fun ChapterDataDto.createChapter(): SChapter { + val chapterName = mutableListOf() + if (isPremium) { + chapterName.add(lockSymbol) + } + + chapter.let { + if (it.isNotEmpty()) { + chapterName.add("Ch.$it") + } + } + + title.let { + if (it.isNotEmpty()) { + if (chapterName.isNotEmpty()) { + chapterName.add("-") + } + chapterName.add(it) + } + } + + return SChapter.create().apply { + url = key + name = chapterName.joinToString(" ") + chapter_number = chapter.toFloatOrNull() ?: 0f + date_upload = dateFormatter.tryParse(published_time) + } + } + } +} diff --git a/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/EntityDto.kt b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/EntityDto.kt new file mode 100644 index 000000000..c74fe9f4f --- /dev/null +++ b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/EntityDto.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.extension.all.globalcomix.dto + +import kotlinx.serialization.Serializable + +@Serializable +sealed class EntityDto { + val id: Long = -1 +} + +@Serializable +class UnknownEntity() : EntityDto() diff --git a/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/MangaDto.kt b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/MangaDto.kt new file mode 100644 index 000000000..5cc54bd98 --- /dev/null +++ b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/MangaDto.kt @@ -0,0 +1,49 @@ +package eu.kanade.tachiyomi.extension.all.globalcomix.dto + +import eu.kanade.tachiyomi.extension.all.globalcomix.comic +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +typealias MangaDto = ResponseDto +typealias MangasDto = PaginatedResponseDto + +@Suppress("PropertyName") +@Serializable +@SerialName(comic) +class MangaDataDto( + val name: String, + val description: String?, + val status_name: String?, + val category_name: String?, + val image_url: String?, + val artist: ArtistDto, +) : EntityDto() { + companion object { + /** + * Create an [SManga] instance from the JSON DTO element. + */ + fun MangaDataDto.createManga(): SManga = + SManga.create().also { + it.initialized = true + it.url = id.toString() + it.description = description + it.author = artist.let { it.roman_name ?: it.name } + it.status = status_name?.let(::convertStatus) ?: SManga.UNKNOWN + it.genre = category_name + it.title = name + it.thumbnail_url = image_url + } + + private fun convertStatus(status: String): Int { + return when (status) { + "Ongoing" -> SManga.ONGOING + "Preview" -> SManga.ONGOING + "Finished" -> SManga.COMPLETED + "On hold" -> SManga.ON_HIATUS + "Cancelled" -> SManga.CANCELLED + else -> SManga.UNKNOWN + } + } + } +} diff --git a/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/PageDataDto.kt b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/PageDataDto.kt new file mode 100644 index 000000000..921c614ee --- /dev/null +++ b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/PageDataDto.kt @@ -0,0 +1,14 @@ +package eu.kanade.tachiyomi.extension.all.globalcomix.dto + +import eu.kanade.tachiyomi.extension.all.globalcomix.releasePage +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Suppress("PropertyName") +@Serializable +@SerialName(releasePage) +class PageDataDto( + val is_page_paid: Boolean, + val desktop_image_url: String, + val mobile_image_url: String, +) : EntityDto() diff --git a/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/PaginatedResponseDto.kt b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/PaginatedResponseDto.kt new file mode 100644 index 000000000..9b3d72bca --- /dev/null +++ b/src/all/globalcomix/src/eu/kanade/tachiyomi/extension/all/globalcomix/dto/PaginatedResponseDto.kt @@ -0,0 +1,36 @@ +package eu.kanade.tachiyomi.extension.all.globalcomix.dto + +import kotlinx.serialization.Serializable + +@Serializable +class PaginatedResponseDto( + val payload: PaginatedPayloadDto? = null, +) + +@Serializable +class PaginatedPayloadDto( + val results: List = emptyList(), + val pagination: PaginationStateDto, +) + +@Serializable +class ResponseDto( + val payload: PayloadDto? = null, +) + +@Serializable +class PayloadDto( + val results: T, +) + +@Suppress("PropertyName") +@Serializable +class PaginationStateDto( + val page: Int = 1, + val per_page: Int = 0, + val total_pages: Int = 0, + val total_results: Int = 0, +) { + val hasNextPage: Boolean + get() = page < total_pages +}