diff --git a/lib-multisrc/lectormoe/build.gradle.kts b/lib-multisrc/lectormoe/build.gradle.kts new file mode 100644 index 000000000..dc076cc37 --- /dev/null +++ b/lib-multisrc/lectormoe/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("lib-multisrc") +} + +baseVersionCode = 1 diff --git a/lib-multisrc/lectormoe/src/eu/kanade/tachiyomi/multisrc/lectormoe/LectorMoe.kt b/lib-multisrc/lectormoe/src/eu/kanade/tachiyomi/multisrc/lectormoe/LectorMoe.kt new file mode 100644 index 000000000..ca9c6c70f --- /dev/null +++ b/lib-multisrc/lectormoe/src/eu/kanade/tachiyomi/multisrc/lectormoe/LectorMoe.kt @@ -0,0 +1,143 @@ +package eu.kanade.tachiyomi.multisrc.lectormoe + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimitHost +import eu.kanade.tachiyomi.source.model.Filter +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.json.Json +import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import uy.kohesive.injekt.injectLazy + +abstract class LectorMoe( + override val name: String, + override val baseUrl: String, + override val lang: String, + private val organizationDomain: String = baseUrl.substringAfter("://"), + private val apiBaseUrl: String = "https://api.lector.moe", +) : HttpSource() { + + override val supportsLatest = true + + private val json: Json by injectLazy() + + override val client: OkHttpClient = network.cloudflareClient.newBuilder() + .rateLimitHost(baseUrl.toHttpUrl(), 3) + .rateLimitHost(apiBaseUrl.toHttpUrl(), 3) + .build() + + final override fun headersBuilder(): Headers.Builder = super.headersBuilder() + .add("Referer", "$baseUrl/") + + private val apiHeaders: Headers = headersBuilder() + .add("Organization-Domain", organizationDomain) + .build() + + override fun popularMangaRequest(page: Int): Request = + GET("$apiBaseUrl/api/manga-custom?page=$page&limit=$PAGE_LIMIT&order=popular", apiHeaders) + + override fun popularMangaParse(response: Response): MangasPage = searchMangaParse(response) + + override fun latestUpdatesRequest(page: Int): Request = + GET("$apiBaseUrl/api/manga-custom?page=$page&limit=$PAGE_LIMIT&order=latest", apiHeaders) + + override fun latestUpdatesParse(response: Response): MangasPage = searchMangaParse(response) + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = "$apiBaseUrl/api/manga-custom".toHttpUrl().newBuilder() + + url.setQueryParameter("page", page.toString()) + url.setQueryParameter("limit", PAGE_LIMIT.toString()) + + filters.forEach { filter -> + when (filter) { + is SortByFilter -> url.setQueryParameter("order", filter.toUriPart()) + else -> {} + } + } + + if (query.isNotBlank()) url.setQueryParameter("title", query) + + return GET(url.build(), apiHeaders) + } + + override fun searchMangaParse(response: Response): MangasPage { + val page = response.request.url.queryParameter("page")!!.toInt() + val result = json.decodeFromString>(response.body.string()) + + val mangas = result.data.series.map { it.toSManga() } + val hasNextPage = page < result.data.maxPage + + return MangasPage(mangas, hasNextPage) + } + + override fun getFilterList() = FilterList( + SortByFilter("Ordenar por", getSortList()), + ) + + private fun getSortList() = arrayOf( + Pair("Popularidad", "popular"), + Pair("Recientes", "latest"), + ) + + override fun getMangaUrl(manga: SManga): String = "$baseUrl/manga/${manga.url}" + + override fun mangaDetailsRequest(manga: SManga): Request = + GET("$apiBaseUrl/api/manga-custom/${manga.url}", apiHeaders) + + override fun mangaDetailsParse(response: Response): SManga { + val result = json.decodeFromString>(response.body.string()) + return result.data.toSMangaDetails() + } + + override fun getChapterUrl(chapter: SChapter): String { + val seriesSlug = chapter.url.substringBefore("/") + val chapterSlug = chapter.url.substringAfter("/") + + return "$baseUrl/manga/$seriesSlug/chapters/$chapterSlug" + } + + override fun chapterListRequest(manga: SManga): Request = mangaDetailsRequest(manga) + + override fun chapterListParse(response: Response): List { + val result = json.decodeFromString>(response.body.string()) + val seriesSlug = result.data.slug + return result.data.chapters?.map { it.toSChapter(seriesSlug) } ?: emptyList() + } + + override fun pageListRequest(chapter: SChapter): Request { + val seriesSlug = chapter.url.substringBefore("/") + val chapterSlug = chapter.url.substringAfter("/") + + return GET("$apiBaseUrl/api/manga-custom/$seriesSlug/chapter/$chapterSlug/pages", apiHeaders) + } + + override fun pageListParse(response: Response): List { + val result = json.decodeFromString>>(response.body.string()) + return result.data.mapIndexed { i, page -> + Page(i, imageUrl = page.imageUrl) + } + } + + override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException() + + class SortByFilter(title: String, list: Array>) : UriPartFilter(title, list) + + open class UriPartFilter(displayName: String, val vals: Array>) : + Filter.Select(displayName, vals.map { it.first }.toTypedArray()) { + fun toUriPart() = vals[state].second + } + + companion object { + private const val PAGE_LIMIT = 36 + } +} diff --git a/src/es/senshimanga/src/eu/kanade/tachiyomi/extension/es/senshimanga/SenshiMangaDto.kt b/lib-multisrc/lectormoe/src/eu/kanade/tachiyomi/multisrc/lectormoe/LectorMoeDto.kt similarity index 97% rename from src/es/senshimanga/src/eu/kanade/tachiyomi/extension/es/senshimanga/SenshiMangaDto.kt rename to lib-multisrc/lectormoe/src/eu/kanade/tachiyomi/multisrc/lectormoe/LectorMoeDto.kt index 78f8e99df..b9ac22513 100644 --- a/src/es/senshimanga/src/eu/kanade/tachiyomi/extension/es/senshimanga/SenshiMangaDto.kt +++ b/lib-multisrc/lectormoe/src/eu/kanade/tachiyomi/multisrc/lectormoe/LectorMoeDto.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.extension.es.senshimanga +package eu.kanade.tachiyomi.multisrc.lectormoe import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga diff --git a/src/es/senshimanga/build.gradle b/src/es/senshimanga/build.gradle index c14ec1afb..b013159b1 100644 --- a/src/es/senshimanga/build.gradle +++ b/src/es/senshimanga/build.gradle @@ -1,7 +1,9 @@ ext { extName = 'Senshi Manga' extClass = '.SenshiManga' - extVersionCode = 5 + themePkg = 'lectormoe' + baseUrl = 'https://visorsenshi.com' + overrideVersionCode = 5 isNsfw = false } diff --git a/src/es/senshimanga/src/eu/kanade/tachiyomi/extension/es/senshimanga/SenshiManga.kt b/src/es/senshimanga/src/eu/kanade/tachiyomi/extension/es/senshimanga/SenshiManga.kt index 5ec8a3a61..41c46a0a5 100644 --- a/src/es/senshimanga/src/eu/kanade/tachiyomi/extension/es/senshimanga/SenshiManga.kt +++ b/src/es/senshimanga/src/eu/kanade/tachiyomi/extension/es/senshimanga/SenshiManga.kt @@ -1,145 +1,9 @@ package eu.kanade.tachiyomi.extension.es.senshimanga -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.interceptor.rateLimitHost -import eu.kanade.tachiyomi.source.model.Filter -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.json.Json -import okhttp3.Headers -import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import uy.kohesive.injekt.injectLazy +import eu.kanade.tachiyomi.multisrc.lectormoe.LectorMoe -class SenshiManga : HttpSource() { - - override val name = "Senshi Manga" - - override val baseUrl = "https://visorsenshi.com" - - private val apiUrl = "https://api.visorsenshi.com" - - override val lang = "es" - - override val supportsLatest = true - - private val json: Json by injectLazy() - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .rateLimitHost(baseUrl.toHttpUrl(), 3) - .rateLimitHost(apiUrl.toHttpUrl(), 3) - .build() - - override fun headersBuilder(): Headers.Builder = super.headersBuilder() - .add("Referer", "$baseUrl/") - - private val apiHeaders: Headers = headersBuilder() - .add("Organization-Domain", "visorsenshi.com") - .build() - - override fun popularMangaRequest(page: Int): Request = - GET("$apiUrl/api/manga-custom?page=$page&limit=$PAGE_LIMIT&order=popular", apiHeaders) - - override fun popularMangaParse(response: Response): MangasPage = searchMangaParse(response) - - override fun latestUpdatesRequest(page: Int): Request = - GET("$apiUrl/api/manga-custom?page=$page&limit=$PAGE_LIMIT&order=latest", apiHeaders) - - override fun latestUpdatesParse(response: Response): MangasPage = searchMangaParse(response) - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val url = "$apiUrl/api/manga-custom".toHttpUrl().newBuilder() - - url.setQueryParameter("page", page.toString()) - url.setQueryParameter("limit", PAGE_LIMIT.toString()) - - filters.forEach { filter -> - when (filter) { - is SortByFilter -> url.setQueryParameter("order", filter.toUriPart()) - else -> {} - } - } - - if (query.isNotBlank()) url.setQueryParameter("title", query) - - return GET(url.build(), apiHeaders) - } - - override fun searchMangaParse(response: Response): MangasPage { - val page = response.request.url.queryParameter("page")!!.toInt() - val result = json.decodeFromString>(response.body.string()) - - val mangas = result.data.series.map { it.toSManga() } - val hasNextPage = page < result.data.maxPage - - return MangasPage(mangas, hasNextPage) - } - - override fun getFilterList() = FilterList( - SortByFilter("Ordenar por", getSortList()), - ) - - private fun getSortList() = arrayOf( - Pair("Popularidad", "popular"), - Pair("Recientes", "latest"), - ) - - override fun getMangaUrl(manga: SManga): String = "$baseUrl/manga/${manga.url}" - - override fun mangaDetailsRequest(manga: SManga): Request = - GET("$apiUrl/api/manga-custom/${manga.url}", apiHeaders) - - override fun mangaDetailsParse(response: Response): SManga { - val result = json.decodeFromString>(response.body.string()) - return result.data.toSMangaDetails() - } - - override fun getChapterUrl(chapter: SChapter): String { - val seriesSlug = chapter.url.substringBefore("/") - val chapterSlug = chapter.url.substringAfter("/") - - return "$baseUrl/manga/$seriesSlug/chapters/$chapterSlug" - } - - override fun chapterListRequest(manga: SManga): Request = mangaDetailsRequest(manga) - - override fun chapterListParse(response: Response): List { - val result = json.decodeFromString>(response.body.string()) - val seriesSlug = result.data.slug - return result.data.chapters?.map { it.toSChapter(seriesSlug) } ?: emptyList() - } - - override fun pageListRequest(chapter: SChapter): Request { - val seriesSlug = chapter.url.substringBefore("/") - val chapterSlug = chapter.url.substringAfter("/") - - return GET("$apiUrl/api/manga-custom/$seriesSlug/chapter/$chapterSlug/pages", apiHeaders) - } - - override fun pageListParse(response: Response): List { - val result = json.decodeFromString>>(response.body.string()) - return result.data.mapIndexed { i, page -> - Page(i, imageUrl = page.imageUrl) - } - } - - override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException() - - class SortByFilter(title: String, list: Array>) : UriPartFilter(title, list) - - open class UriPartFilter(displayName: String, val vals: Array>) : - Filter.Select(displayName, vals.map { it.first }.toTypedArray()) { - fun toUriPart() = vals[state].second - } - - companion object { - private const val PAGE_LIMIT = 36 - } -} +class SenshiManga : LectorMoe( + "Senshi Manga", + "https://visorsenshi.com", + "es", +) diff --git a/src/es/taikutsu/build.gradle b/src/es/taikutsu/build.gradle new file mode 100644 index 000000000..44db1a484 --- /dev/null +++ b/src/es/taikutsu/build.gradle @@ -0,0 +1,10 @@ +ext { + extName = 'Taikutsu' + extClass = '.Taikutsu' + themePkg = 'lectormoe' + baseUrl = 'https://taikutsutl.lector.moe' + overrideVersionCode = 0 + isNsfw = false +} + +apply from: "$rootDir/common.gradle" diff --git a/src/es/taikutsu/res/mipmap-hdpi/ic_launcher.png b/src/es/taikutsu/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..582cdbd29 Binary files /dev/null and b/src/es/taikutsu/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/es/taikutsu/res/mipmap-mdpi/ic_launcher.png b/src/es/taikutsu/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..0a262b5d1 Binary files /dev/null and b/src/es/taikutsu/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/es/taikutsu/res/mipmap-xhdpi/ic_launcher.png b/src/es/taikutsu/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..a69b61143 Binary files /dev/null and b/src/es/taikutsu/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/es/taikutsu/res/mipmap-xxhdpi/ic_launcher.png b/src/es/taikutsu/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..ca9233ad1 Binary files /dev/null and b/src/es/taikutsu/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/es/taikutsu/res/mipmap-xxxhdpi/ic_launcher.png b/src/es/taikutsu/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..89a1fb173 Binary files /dev/null and b/src/es/taikutsu/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/es/taikutsu/src/eu/kanade/tachiyomi/extension/es/taikutsu/Taikutsu.kt b/src/es/taikutsu/src/eu/kanade/tachiyomi/extension/es/taikutsu/Taikutsu.kt new file mode 100644 index 000000000..21a97bb7e --- /dev/null +++ b/src/es/taikutsu/src/eu/kanade/tachiyomi/extension/es/taikutsu/Taikutsu.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.es.taikutsu + +import eu.kanade.tachiyomi.multisrc.lectormoe.LectorMoe + +class Taikutsu : LectorMoe( + "Taikutsu", + "https://taikutsutl.lector.moe", + "es", +)