diff --git a/src/en/allanime/build.gradle b/src/en/allanime/build.gradle index 1661c165a..d1b088eb4 100644 --- a/src/en/allanime/build.gradle +++ b/src/en/allanime/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'AllAnime' pkgNameSuffix = 'en.allanime' extClass = '.AllAnime' - extVersionCode = 2 + extVersionCode = 3 } apply from: "$rootDir/common.gradle" diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnime.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnime.kt index 80b1f5244..c06765307 100644 --- a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnime.kt +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnime.kt @@ -10,8 +10,6 @@ import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.interceptor.rateLimitHost import eu.kanade.tachiyomi.source.ConfigurableSource -import eu.kanade.tachiyomi.source.model.Filter.TriState.Companion.STATE_EXCLUDE -import eu.kanade.tachiyomi.source.model.Filter.TriState.Companion.STATE_INCLUDE import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page @@ -20,24 +18,18 @@ import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.asJsoup import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.put -import kotlinx.serialization.json.putJsonObject import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response -import org.jsoup.Jsoup import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy +import java.text.SimpleDateFormat import java.util.Locale class AllAnime : ConfigurableSource, HttpSource() { @@ -48,18 +40,41 @@ class AllAnime : ConfigurableSource, HttpSource() { override val supportsLatest = true - private val json: Json by injectLazy() + private val json: Json = Json { + ignoreUnknownKeys = true + explicitNulls = false + encodeDefaults = true + coerceInputValues = true + } private val preferences: SharedPreferences = Injekt.get().getSharedPreferences("source_$id", 0x0000) - private val domain = preferences.getString(DOMAIN_PREF, "allanime.to") + private val domain = preferences.domainPref override val baseUrl = "https://$domain" private val apiUrl = "https://api.$domain/allanimeapi" override val client: OkHttpClient = network.cloudflareClient.newBuilder() + .addInterceptor { chain -> + val request = chain.request() + val frag = request.url.fragment + val quality = preferences.imageQuality + + if (frag.isNullOrEmpty() || quality == IMAGE_QUALITY_PREF_DEFAULT) { + return@addInterceptor chain.proceed(request) + } + + val oldUrl = request.url.toString() + val newUrl = oldUrl.replace(imageQualityRegex, "$image_cdn/$1?w=$quality") + + return@addInterceptor chain.proceed( + request.newBuilder() + .url(newUrl) + .build(), + ) + } .rateLimitHost(apiUrl.toHttpUrl(), 1) .build() @@ -68,31 +83,22 @@ class AllAnime : ConfigurableSource, HttpSource() { /* Popular */ override fun popularMangaRequest(page: Int): Request { - val showAdult = preferences.getBoolean(SHOW_ADULT_PREF, false) + val payloadObj = ApiPopularPayload( + size = limit, + dateRange = 0, + page = page, + allowAdult = preferences.allowAdult, + ) - val payload = buildJsonObject { - putJsonObject("variables") { - put("type", "manga") - put("size", limit) - put("dateRange", 0) - put("page", page) - put("allowAdult", showAdult) - put("allowUnknown", false) - } - put("query", POPULAR_QUERY) - } - - return apiRequest(payload) + return apiRequest(payloadObj) } override fun popularMangaParse(response: Response): MangasPage { - val result = json.decodeFromString(response.body.string()) + val result = response.parseAs() + val titleStyle = preferences.titlePref - val mangaList = result.data.queryPopular.recommendations - .mapNotNull { it.anyCard } - .map { manga -> - toSManga(manga) - } + val mangaList = result.data.popular.mangas + .mapNotNull { it.manga?.toSManga(titleStyle) } return MangasPage(mangaList, mangaList.size == limit) } @@ -115,58 +121,26 @@ class AllAnime : ConfigurableSource, HttpSource() { } override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val showAdult = preferences.getBoolean(SHOW_ADULT_PREF, false) - var country = "ALL" - val includeGenres = mutableListOf() - val excludeGenres = mutableListOf() + val payloadObj = ApiSearchPayload( + query = query, + size = limit, + page = page, + genres = filters.firstInstanceOrNull()?.included, + excludeGenres = filters.firstInstanceOrNull()?.excluded, + translationType = "sub", + countryOrigin = filters.firstInstanceOrNull()?.getValue() ?: "ALL", + allowAdult = preferences.allowAdult, + ) - filters.forEach { filter -> - when (filter) { - is GenreFilter -> { - filter.state.forEach { genreState -> - when (genreState.state) { - STATE_INCLUDE -> includeGenres.add(genreState.name) - STATE_EXCLUDE -> excludeGenres.add(genreState.name) - } - } - } - is CountryFilter -> { - country = filter.getValue() - } - else -> {} - } - } - - val payload = buildJsonObject { - putJsonObject("variables") { - putJsonObject("search") { - if (includeGenres.isNotEmpty() || excludeGenres.isNotEmpty()) { - put("genres", JsonArray(includeGenres.map { JsonPrimitive(it) })) - put("excludeGenres", JsonArray(excludeGenres.map { JsonPrimitive(it) })) - } - if (query.isNotEmpty()) put("query", query) - put("allowAdult", showAdult) - put("allowUnknown", false) - put("isManga", true) - } - put("limit", limit) - put("page", page) - put("translationType", "sub") - put("countryOrigin", country) - } - put("query", SEARCH_QUERY) - } - - return apiRequest(payload) + return apiRequest(payloadObj) } override fun searchMangaParse(response: Response): MangasPage { - val result = json.decodeFromString(response.body.string()) + val result = response.parseAs() + val titleStyle = preferences.titlePref - val mangaList = result.data.mangas.edges - .map { manga -> - toSManga(manga) - } + val mangaList = result.data.mangas.mangas + .map { it.toSManga(titleStyle) } return MangasPage(mangaList, mangaList.size == limit) } @@ -176,22 +150,15 @@ class AllAnime : ConfigurableSource, HttpSource() { /* Details */ override fun mangaDetailsRequest(manga: SManga): Request { val mangaId = manga.url.split("/")[2] + val payloadObj = ApiIDPayload(mangaId, DETAILS_QUERY) - val payload = buildJsonObject { - putJsonObject("variables") { - put("_id", mangaId) - } - put("query", DETAILS_QUERY) - } - - return apiRequest(payload) + return apiRequest(payloadObj) } override fun mangaDetailsParse(response: Response): SManga { - val result = json.decodeFromString(response.body.string()) - val manga = result.data.manga + val result = response.parseAs() - return toSManga(manga) + return result.data.manga.toSManga(preferences.titlePref) } override fun getMangaUrl(manga: SManga): String { @@ -209,30 +176,44 @@ class AllAnime : ConfigurableSource, HttpSource() { override fun chapterListRequest(manga: SManga): Request { val mangaId = manga.url.split("/")[2] + val payloadObj = ApiIDPayload(mangaId, CHAPTERS_QUERY) - val payload = buildJsonObject { - putJsonObject("variables") { - put("_id", mangaId) - } - put("query", CHAPTERS_QUERY) - } + return apiRequest(payloadObj) + } - return apiRequest(payload) + private fun chapterDetailsRequest(manga: SManga, start: String, end: String): Request { + val mangaId = manga.url.split("/")[2] + val payloadObj = ApiChapterListDetailsPayload(mangaId, start.toFloat(), end.toFloat()) + + return apiRequest(payloadObj) } private fun chapterListParse(response: Response, manga: SManga): List { - val result = json.decodeFromString(response.body.string()) + val result = response.parseAs() + val chapters = result.data.manga.chapters.sub + ?.sortedBy { it.toFloat() } + ?: return emptyList() - val chapters = result.data.manga.availableChaptersDetail.sub + val chapterDetails = client.newCall( + chapterDetailsRequest(manga, chapters.first(), chapters.last()), + ).execute() + .use { + it.parseAs() + }.data.chapterList + ?.sortedBy { it.chapterNum } val mangaUrl = manga.url.substringAfter("/manga/") - return chapters?.map { chapter -> + return chapterDetails?.zip(chapters)?.map { (details, chapterNum) -> SChapter.create().apply { - name = "Chapter $chapter" - url = "/read/$mangaUrl/chapter-$chapter-sub" + name = "Chapter $chapterNum" + if (!details.title.isNullOrEmpty() && !details.title.contains(numberRegex)) { + name += ": ${details.title}" + } + url = "/read/$mangaUrl/chapter-$chapterNum-sub" + date_upload = details.uploadDates?.sub.parseDate() } - } ?: emptyList() + }?.reversed() ?: emptyList() } override fun chapterListParse(response: Response): List { @@ -257,24 +238,21 @@ class AllAnime : ConfigurableSource, HttpSource() { val mangaId = chapterUrl[2] val chapterNo = chapterUrl[4].split("-")[1] - val payload = buildJsonObject { - putJsonObject("variables") { - put("mangaId", mangaId) - put("translationType", "sub") - put("chapterString", chapterNo) - } - put("query", PAGE_QUERY) - } + val payloadObj = ApiPageListPayload( + id = mangaId, + chapterNum = chapterNo, + translationType = "sub", + ) - return apiRequest(payload) + return apiRequest(payloadObj) } private fun pageListParse(response: Response, chapter: SChapter): List { val result = json.decodeFromString(response.body.string()) - val pages = result.data.chapterPages?.edges?.get(0) ?: return emptyList() + val pages = result.data.pageList?.serverList?.get(0) ?: return emptyList() - val imageDomain = if (!pages.pictureUrlHead.isNullOrEmpty()) { - pages.pictureUrlHead.let { server -> + val imageDomain = if (!pages.serverUrl.isNullOrEmpty()) { + pages.serverUrl.let { server -> if (server.matches(urlRegex)) { server } else { @@ -301,7 +279,7 @@ class AllAnime : ConfigurableSource, HttpSource() { return pages.pictureUrls.mapIndexed { index, image -> Page( index = index, - imageUrl = "$imageDomain${image.url}", + imageUrl = "$imageDomain${image.url}#page", ) } } @@ -315,106 +293,99 @@ class AllAnime : ConfigurableSource, HttpSource() { } /* Helpers */ - private fun apiRequest(payload: JsonObject): Request { - val body = payload.toString().toRequestBody(JSON_MEDIA_TYPE) + private inline fun apiRequest(payloadObj: T): Request { + val payload = json.encodeToString(payloadObj) + .toRequestBody(JSON_MEDIA_TYPE) val newHeaders = headersBuilder() - .add("Content-Length", body.contentLength().toString()) - .add("Content-Type", body.contentType().toString()) + .add("Content-Length", payload.contentLength().toString()) + .add("Content-Type", payload.contentType().toString()) .build() - return POST(apiUrl, newHeaders, body) + return POST(apiUrl, newHeaders, payload) } - private fun toSManga(manga: Manga): SManga { - val titleStyle = preferences.getString(TITLE_PREF, "romaji")!! + private inline fun Response.parseAs(): T = json.decodeFromString(body.string()) - return SManga.create().apply { - title = when (titleStyle) { - "romaji" -> manga.name - "eng" -> manga.englishName ?: manga.name - else -> manga.nativeName ?: manga.name - } - url = "/manga/${manga._id}/${manga.name.titleToSlug()}" - thumbnail_url = manga.thumbnail.parseThumbnailUrl() - description = Jsoup.parse( - manga.description?.replace("
", "br2n") ?: "", - ).text().replace("br2n", "\n") - description += if (manga.altNames != null) { - "\n\nAlternative Names: ${manga.altNames.joinToString { it.trim() }}" - } else { - "" - } - if (manga.authors?.isNotEmpty() == true) { - author = manga.authors.first().trim() - artist = author - } - genre = "${manga.genres?.joinToString { it.trim() }}, ${manga.tags?.joinToString { it.trim() }}" - status = manga.status.parseStatus() - } + private inline fun List<*>.firstInstanceOrNull(): R? = + filterIsInstance().firstOrNull() + + private fun String?.parseDate(): Long { + return runCatching { + dateFormat.parse(this!!)!!.time + }.getOrDefault(0L) } - private fun String.parseThumbnailUrl(): String { - return if (this.matches(urlRegex)) { - this - } else { - "$image_cdn$this?w=250" - } - } - - private fun String?.parseStatus(): Int { - if (this == null) { - return SManga.UNKNOWN - } - - return when { - this.contains("releasing", true) -> SManga.ONGOING - this.contains("finished", true) -> SManga.COMPLETED - else -> SManga.UNKNOWN - } - } - - private fun String.titleToSlug() = this.trim() - .lowercase(Locale.US) - .replace(titleSpecialCharactersRegex, "-") - override fun setupPreferenceScreen(screen: PreferenceScreen) { ListPreference(screen.context).apply { key = DOMAIN_PREF title = "Preferred domain" entries = arrayOf("allanime.to", "allanime.co") entryValues = arrayOf("allanime.to", "allanime.co") - setDefaultValue("allanime.to") + setDefaultValue(DOMAIN_PREF_DEFAULT) summary = "Requires App Restart" }.let { screen.addPreference(it) } + ListPreference(screen.context).apply { + key = IMAGE_QUALITY_PREF + title = "Image Quality" + entries = arrayOf("Original", "Wp-800", "Wp-480") + entryValues = arrayOf("original", "800", "480") + setDefaultValue(IMAGE_QUALITY_PREF_DEFAULT) + summary = "Warning: Wp quality servers can be slow and might not work sometimes" + }.let { screen.addPreference(it) } + ListPreference(screen.context).apply { key = TITLE_PREF title = "Preferred Title Style" entries = arrayOf("Romaji", "English", "Native") entryValues = arrayOf("romaji", "eng", "native") - setDefaultValue("romaji") + setDefaultValue(TITLE_PREF_DEFAULT) summary = "%s" }.let { screen.addPreference(it) } SwitchPreferenceCompat(screen.context).apply { key = SHOW_ADULT_PREF title = "Show Adult Content" - setDefaultValue(false) + setDefaultValue(SHOW_ADULT_PREF_DEFAULT) }.let { screen.addPreference(it) } } + private val SharedPreferences.domainPref + get() = getString(DOMAIN_PREF, DOMAIN_PREF_DEFAULT)!! + + private val SharedPreferences.titlePref + get() = getString(TITLE_PREF, TITLE_PREF_DEFAULT) + + private val SharedPreferences.allowAdult + get() = getBoolean(SHOW_ADULT_PREF, SHOW_ADULT_PREF_DEFAULT) + + private val SharedPreferences.imageQuality + get() = getString(IMAGE_QUALITY_PREF, IMAGE_QUALITY_PREF_DEFAULT)!! + companion object { - private const val limit = 26 + private const val limit = 20 + private val numberRegex by lazy { Regex("\\d") } + val whitespace by lazy { Regex("\\s+") } + val dateFormat by lazy { + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH) + } const val SEARCH_PREFIX = "id:" - private const val image_cdn = "https://wp.youtube-anime.com/aln.youtube-anime.com/" + const val thumbnail_cdn = "https://wp.youtube-anime.com/aln.youtube-anime.com/" private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() - private val urlRegex = Regex("^https?://.*") - private val titleSpecialCharactersRegex = Regex("[^a-z\\d]+") + val urlRegex = Regex("^https?://.*") + private const val image_cdn = "https://wp.youtube-anime.com" + private val imageQualityRegex = Regex("^https?://(.*)#.*") + val titleSpecialCharactersRegex = Regex("[^a-z\\d]+") private val imageUrlFromPageRegex = Regex("selectedPicturesServer:\\[\\{.*?url:\"(.*?)\".*?\\}\\]") private const val DOMAIN_PREF = "pref_domain" + private const val DOMAIN_PREF_DEFAULT = "allanime.to" private const val TITLE_PREF = "pref_title" + private const val TITLE_PREF_DEFAULT = "romaji" private const val SHOW_ADULT_PREF = "pref_adult" + private const val SHOW_ADULT_PREF_DEFAULT = false + private const val IMAGE_QUALITY_PREF = "pref_quality" + private const val IMAGE_QUALITY_PREF_DEFAULT = "original" } } diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeDto.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeDto.kt index 10652be5f..ab1c60a79 100644 --- a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeDto.kt +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeDto.kt @@ -1,22 +1,26 @@ package eu.kanade.tachiyomi.extension.en.allanime +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import org.jsoup.Jsoup +import java.util.Locale @Serializable data class ApiPopularResponse( - val data: PopularResultData, + val data: PopularResponseData, ) { @Serializable - data class PopularResultData( - val queryPopular: QueryPopularData, + data class PopularResponseData( + @SerialName("queryPopular") val popular: PopularData, ) { @Serializable - data class QueryPopularData( - val recommendations: List, + data class PopularData( + @SerialName("recommendations") val mangas: List, ) { @Serializable - data class Recommendation( - val anyCard: Manga? = null, + data class Popular( + @SerialName("anyCard") val manga: SearchManga? = null, ) } } @@ -24,19 +28,34 @@ data class ApiPopularResponse( @Serializable data class ApiSearchResponse( - val data: SearchResultData, + val data: SearchResponseData, ) { @Serializable - data class SearchResultData( + data class SearchResponseData( val mangas: SearchResultMangas, ) { @Serializable data class SearchResultMangas( - val edges: List, + @SerialName("edges") val mangas: List, ) } } +@Serializable +data class SearchManga( + @SerialName("_id") val id: String, + val name: String, + val thumbnail: String? = null, + val englishName: String? = null, + val nativeName: String? = null, +) { + fun toSManga(titleStyle: String?) = SManga.create().apply { + title = titleStyle.preferedName(name, englishName, nativeName) + url = "/manga/$id/${name.titleToSlug()}" + thumbnail_url = thumbnail?.parseThumbnailUrl() + } +} + @Serializable data class ApiMangaDetailsResponse( val data: MangaDetailsData, @@ -44,24 +63,43 @@ data class ApiMangaDetailsResponse( @Serializable data class MangaDetailsData( val manga: Manga, - ) + ) { + @Serializable + data class Manga( + @SerialName("_id") val id: String, + val name: String, + val thumbnail: String? = null, + val description: String? = null, + val authors: List? = emptyList(), + val genres: List? = emptyList(), + val tags: List? = emptyList(), + val status: String? = null, + val altNames: List? = emptyList(), + val englishName: String? = null, + val nativeName: String? = null, + ) { + fun toSManga(titleStyle: String?) = SManga.create().apply { + title = titleStyle.preferedName(name, englishName, nativeName) + url = "/manga/$id/${name.titleToSlug()}" + thumbnail_url = thumbnail?.parseThumbnailUrl() + description = this@Manga.description?.parseDescription() + if (!altNames.isNullOrEmpty()) { + description += altNames.joinToString( + prefix = "\n\nAlternative Names:\n* ", + separator = "\n* ", + ) { it.trim() } + } + if (authors?.isNotEmpty() == true) { + author = authors.first().trim() + artist = author + } + genre = "${genres?.joinToString { it.trim() }}, ${tags?.joinToString { it.trim() }}" + status = this@Manga.status.parseStatus() + } + } + } } -@Serializable -data class Manga( - val _id: String, - val name: String, - val thumbnail: String, - val description: String?, - val authors: List?, - val genres: List?, - val tags: List?, - val status: String?, - val altNames: List?, - val englishName: String? = null, - val nativeName: String? = null, -) - @Serializable data class ApiChapterListResponse( val data: ChapterListData, @@ -72,7 +110,7 @@ data class ApiChapterListResponse( ) { @Serializable data class ChapterList( - val availableChaptersDetail: AvailableChapters, + @SerialName("availableChaptersDetail") val chapters: AvailableChapters, ) { @Serializable data class AvailableChapters( @@ -82,21 +120,43 @@ data class ApiChapterListResponse( } } +@Serializable +data class ApiChapterListDetailsResponse( + val data: ChapterListData, +) { + @Serializable + data class ChapterListData( + @SerialName("episodeInfos") val chapterList: List? = emptyList(), + ) { + @Serializable + data class ChapterData( + @SerialName("episodeIdNum") val chapterNum: Float, + @SerialName("notes") val title: String? = null, + val uploadDates: DateDto? = null, + ) { + @Serializable + data class DateDto( + val sub: String? = null, + ) + } + } +} + @Serializable data class ApiPageListResponse( val data: PageListData, ) { @Serializable data class PageListData( - val chapterPages: PageList?, + @SerialName("chapterPages") val pageList: PageList?, ) { @Serializable data class PageList( - val edges: List?, + @SerialName("edges") val serverList: List?, ) { @Serializable data class Servers( - val pictureUrlHead: String? = null, + @SerialName("pictureUrlHead") val serverUrl: String? = null, val pictureUrls: List, ) { @Serializable @@ -107,3 +167,41 @@ data class ApiPageListResponse( } } } + +fun String.parseThumbnailUrl(): String { + return if (this.matches(AllAnime.urlRegex)) { + this + } else { + "${AllAnime.thumbnail_cdn}$this?w=250" + } +} + +fun String?.parseStatus(): Int { + if (this == null) { + return SManga.UNKNOWN + } + + return when { + this.contains("releasing", true) -> SManga.ONGOING + this.contains("finished", true) -> SManga.COMPLETED + else -> SManga.UNKNOWN + } +} + +private fun String.titleToSlug() = this.trim() + .lowercase(Locale.US) + .replace(AllAnime.titleSpecialCharactersRegex, "-") + +private fun String?.preferedName(name: String, englishName: String?, nativeName: String?): String { + return when (this) { + "eng" -> englishName + "native" -> nativeName + else -> name + } ?: name +} + +private fun String.parseDescription(): String { + return Jsoup.parse( + this.replace("
", "br2n"), + ).text().replace("br2n", "\n") +} diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeFiters.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeFiters.kt index fd5d453c6..603ce7ac3 100644 --- a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeFiters.kt +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeFiters.kt @@ -11,7 +11,13 @@ internal class CountryFilter(name: String, private val countries: List) : - Filter.Group(title, genres) + Filter.Group(title, genres) { + val included: List + get() = state.filter { it.isIncluded() }.map { it.name } + + val excluded: List + get() = state.filter { it.isExcluded() }.map { it.name } +} private val genreList: List = listOf( Genre("4 Koma"), diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimePayloadDto.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimePayloadDto.kt new file mode 100644 index 000000000..c481290a6 --- /dev/null +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimePayloadDto.kt @@ -0,0 +1,163 @@ +package eu.kanade.tachiyomi.extension.en.allanime + +import kotlinx.serialization.Serializable + +@Serializable +data class ApiPopularPayload( + val variables: ApiPopularVariables, + val query: String, +) { + @Serializable + data class ApiPopularVariables( + val type: String, + val size: Int, + val dateRange: Int, + val page: Int, + val allowAdult: Boolean, + val allowUnknown: Boolean, + ) + + constructor( + type: String = "manga", + size: Int, + dateRange: Int, + page: Int, + allowAdult: Boolean = false, + allowUnknown: Boolean = false, + ) : this( + ApiPopularVariables( + type = type, + size = size, + dateRange = dateRange, + page = page, + allowAdult = allowAdult, + allowUnknown = allowUnknown, + ), + POPULAR_QUERY, + ) +} + +@Serializable +data class ApiSearchPayload( + val variables: ApiSearchVariables, + val query: String, +) { + @Serializable + data class ApiSearchVariables( + val search: SearchPayload, + val limit: Int, + val page: Int, + val translationType: String, + val countryOrigin: String, + ) + + @Serializable + data class SearchPayload( + val query: String, + val genres: List?, + val excludeGenres: List?, + val isManga: Boolean, + val allowAdult: Boolean, + val allowUnknown: Boolean, + ) + + constructor( + query: String, + size: Int, + page: Int, + genres: List?, + excludeGenres: List?, + translationType: String, + countryOrigin: String, + isManga: Boolean = true, + allowAdult: Boolean = false, + allowUnknown: Boolean = false, + ) : this( + ApiSearchVariables( + search = SearchPayload( + query = query, + genres = genres, + excludeGenres = excludeGenres, + isManga = isManga, + allowAdult = allowAdult, + allowUnknown = allowUnknown, + ), + limit = size, + page = page, + translationType = translationType, + countryOrigin = countryOrigin, + ), + SEARCH_QUERY, + ) +} + +@Serializable +data class ApiIDPayload( + val variables: ApiIDVariables, + val query: String, +) { + @Serializable + data class ApiIDVariables( + val id: String, + ) + + constructor( + id: String, + graphqlQuery: String, + ) : this( + ApiIDVariables(id), + graphqlQuery, + ) +} + +@Serializable +data class ApiChapterListDetailsPayload( + val variables: ApiChapterDetailsVariables, + val query: String, +) { + @Serializable + data class ApiChapterDetailsVariables( + val id: String, + val chapterNumStart: Float, + val chapterNumEnd: Float, + ) + + constructor( + id: String, + chapterNumStart: Float, + chapterNumEnd: Float, + ) : this( + ApiChapterDetailsVariables( + id = "manga@$id", + chapterNumStart = chapterNumStart, + chapterNumEnd = chapterNumEnd, + ), + CHAPTERS_DETAILS_QUERY, + ) +} + +@Serializable +data class ApiPageListPayload( + val variables: ApiPageListVariables, + val query: String, +) { + @Serializable + data class ApiPageListVariables( + val id: String, + val chapterNum: String, + val translationType: String, + ) + + constructor( + id: String, + chapterNum: String, + translationType: String, + ) : this( + ApiPageListVariables( + id = id, + chapterNum = chapterNum, + translationType = translationType, + ), + PAGE_QUERY, + ) +} diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeQueries.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeQueries.kt index afc07c7e6..054503b2b 100644 --- a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeQueries.kt +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeQueries.kt @@ -1,21 +1,24 @@ package eu.kanade.tachiyomi.extension.en.allanime -fun buildQuery(queryAction: () -> String): String { +import eu.kanade.tachiyomi.extension.en.allanime.AllAnime.Companion.whitespace + +private fun buildQuery(queryAction: () -> String): String { return queryAction() .trimIndent() + .replace(whitespace, " ") .replace("%", "$") } val POPULAR_QUERY: String = buildQuery { """ - query( - %type: VaildPopularTypeEnumType! - %size: Int! - %page: Int - %dateRange: Int - %allowAdult: Boolean - %allowUnknown: Boolean - ) { + query ( + %type: VaildPopularTypeEnumType! + %size: Int! + %page: Int + %dateRange: Int + %allowAdult: Boolean + %allowUnknown: Boolean + ) { queryPopular( type: %type size: %size @@ -40,13 +43,13 @@ val POPULAR_QUERY: String = buildQuery { val SEARCH_QUERY: String = buildQuery { """ - query( - %search: SearchInput - %limit: Int - %page: Int - %translationType: VaildTranslationTypeMangaEnumType - %countryOrigin: VaildCountryOriginEnumType - ) { + query ( + %search: SearchInput + %limit: Int + %page: Int + %translationType: VaildTranslationTypeMangaEnumType + %countryOrigin: VaildCountryOriginEnumType + ) { mangas( search: %search limit: %limit @@ -68,10 +71,8 @@ val SEARCH_QUERY: String = buildQuery { val DETAILS_QUERY: String = buildQuery { """ - query (%_id: String!) { - manga( - _id: %_id - ) { + query (%id: String!) { + manga(_id: %id) { _id name thumbnail @@ -90,27 +91,41 @@ val DETAILS_QUERY: String = buildQuery { val CHAPTERS_QUERY: String = buildQuery { """ - query (%_id: String!) { - manga( - _id: %_id - ) { + query (%id: String!) { + manga(_id: %id) { availableChaptersDetail } } """ } +val CHAPTERS_DETAILS_QUERY: String = buildQuery { + """ + query (%id: String!, %chapterNumStart: Float!, %chapterNumEnd: Float!) { + episodeInfos( + showId: %id + episodeNumStart: %chapterNumStart + episodeNumEnd: %chapterNumEnd + ) { + episodeIdNum + notes + uploadDates + } + } + """ +} + val PAGE_QUERY: String = buildQuery { """ - query( - %mangaId: String!, - %translationType: VaildTranslationTypeMangaEnumType!, - %chapterString: String! - ) { + query ( + %id: String! + %translationType: VaildTranslationTypeMangaEnumType! + %chapterNum: String! + ) { chapterPages( - mangaId: %mangaId + mangaId: %id translationType: %translationType - chapterString: %chapterString + chapterString: %chapterNum ) { edges { pictureUrls