diff --git a/src/en/opscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/opscans/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from src/en/opscans/res/mipmap-hdpi/ic_launcher.png rename to multisrc/overrides/madara/opscans/res/mipmap-hdpi/ic_launcher.png diff --git a/src/en/opscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/opscans/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from src/en/opscans/res/mipmap-mdpi/ic_launcher.png rename to multisrc/overrides/madara/opscans/res/mipmap-mdpi/ic_launcher.png diff --git a/src/en/opscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/opscans/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from src/en/opscans/res/mipmap-xhdpi/ic_launcher.png rename to multisrc/overrides/madara/opscans/res/mipmap-xhdpi/ic_launcher.png diff --git a/src/en/opscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/opscans/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from src/en/opscans/res/mipmap-xxhdpi/ic_launcher.png rename to multisrc/overrides/madara/opscans/res/mipmap-xxhdpi/ic_launcher.png diff --git a/src/en/opscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/opscans/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from src/en/opscans/res/mipmap-xxxhdpi/ic_launcher.png rename to multisrc/overrides/madara/opscans/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/opscans/src/OPSCANS.kt b/multisrc/overrides/madara/opscans/src/OPSCANS.kt new file mode 100644 index 000000000..c35e54f5c --- /dev/null +++ b/multisrc/overrides/madara/opscans/src/OPSCANS.kt @@ -0,0 +1,15 @@ +package eu.kanade.tachiyomi.extension.en.opscans + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class OPSCANS : Madara("OPSCANS", "https://opchapters.com", "en") { + override val versionId = 2 + + override fun searchPage(page: Int): String { + return if (page > 1) { + "page/$page/" + } else { + "" + } + } +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt index 48f72576a..927d22018 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt @@ -371,6 +371,7 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("Oh No Manga", "https://ohnomanga.com", "en", isNsfw = true), SingleLang("Olaoe", "https://olaoe.cyou", "ar"), SingleLang("OnlyManhwa", "https://onlymanhwa.org", "en", isNsfw = true), + SingleLang("OPSCANS", "https://opchapters.com", "en"), SingleLang("Pantheon Scan", "https://pantheon-scan.com", "fr", overrideVersionCode = 1), SingleLang("Paragon Scans", "https://paragonscans.com", "en", isNsfw = true), SingleLang("Passa Mão Scan", "https://passamaoscan.com", "pt-BR", isNsfw = true, className = "PassaMaoScan"), diff --git a/src/en/opscans/build.gradle b/src/en/opscans/build.gradle deleted file mode 100644 index 0746c85eb..000000000 --- a/src/en/opscans/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -ext { - extName = 'OPSCANS' - extClass = '.OpScans' - extVersionCode = 1 -} - -apply from: "$rootDir/common.gradle" - -dependencies { - implementation(project(":lib:dataimage")) -} diff --git a/src/en/opscans/src/eu/kanade/tachiyomi/extension/en/opscans/OpScans.kt b/src/en/opscans/src/eu/kanade/tachiyomi/extension/en/opscans/OpScans.kt deleted file mode 100644 index 55fb9cc1b..000000000 --- a/src/en/opscans/src/eu/kanade/tachiyomi/extension/en/opscans/OpScans.kt +++ /dev/null @@ -1,181 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.opscans - -import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.POST -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.Interceptor -import okhttp3.MultipartBody -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.Response -import uy.kohesive.injekt.injectLazy -import java.lang.UnsupportedOperationException -import java.text.SimpleDateFormat -import java.util.Locale - -class OpScans : HttpSource() { - - override val name = "OPSCANS" - - override val lang = "en" - - override val baseUrl = "https://opchapters.com" - - private val apiUrl = "https://opscanlations.com" - - override val supportsLatest = false - - private val json: Json by injectLazy() - - override val versionId = 2 - - override val client = network.cloudflareClient.newBuilder() - .addInterceptor(::imageInterceptor) - .addInterceptor(DataImageInterceptor()) - .build() - - override fun headersBuilder() = super.headersBuilder() - .set("Referer", "$baseUrl/") - - override fun popularMangaRequest(page: Int): Request { - return GET("$apiUrl/api/mangaData", headers) - } - - override fun popularMangaParse(response: Response): MangasPage { - val mangaData = response.parseAs>() - - return MangasPage( - mangaData.map { it.toSManga() }, - false, - ) - } - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - return GET("$apiUrl/api/mangaData#${query.trim()}", headers) - } - - override fun searchMangaParse(response: Response): MangasPage { - val mangaData = response.parseAs>() - val query = response.request.url.fragment!! - - return MangasPage( - mangaData.filter { - it.name.contains(query, true) || - it.author.contains(query, true) || - it.info.contains(query, true) - }.map { it.toSManga() }, - false, - ) - } - - override fun mangaDetailsRequest(manga: SManga): Request { - return GET("$apiUrl/api/mangaData#${manga.url}", headers) - } - - override fun getMangaUrl(manga: SManga) = "$baseUrl/${manga.url}" - - override fun mangaDetailsParse(response: Response): SManga { - val mangaData = response.parseAs>() - val mangaId = response.request.url.fragment!! - - return mangaData.firstOrNull { it.id == mangaId }!!.toSManga() - } - - override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga) - - override fun getChapterUrl(chapter: SChapter) = "$baseUrl${chapter.url}" - - override fun chapterListParse(response: Response): List { - val mangaData = response.parseAs>() - val mangaId = response.request.url.fragment!! - - return mangaData.firstOrNull { it.id == mangaId } - ?.chapters.orEmpty().map { - SChapter.create().apply { - url = "/$mangaId/${it.id}" - name = it.number + if (it.title.isNullOrEmpty()) "" else ": ${it.title}" - date_upload = runCatching { - dateFormat.parse(it.date!!)!!.time - }.getOrDefault(0L) - } - }.reversed() - } - - override fun pageListRequest(chapter: SChapter): Request { - return GET("$apiUrl/api/mangaData#${chapter.url}", headers) - } - - override fun pageListParse(response: Response): List { - val mangaData = response.parseAs>() - val ids = response.request.url.fragment!!.split("/") - val mangaId = ids[1] - val chapterId = ids[2] - - return mangaData.firstOrNull { it.id == mangaId } - ?.chapters?.firstOrNull { it.id == chapterId } - ?.images.orEmpty().mapIndexed { idx, img -> - Page(idx, "", "https://127.0.0.1/image#${img.source}") - } - } - - private fun imageInterceptor(chain: Interceptor.Chain): Response { - val request = chain.request() - val url = request.url - - if (url.pathSegments.lastOrNull() != "image" || url.fragment.isNullOrEmpty()) { - return chain.proceed(request) - } - - val image = url.fragment!! - - val boundary = buildString { - append((1..9).random()) - repeat(28) { - append((0..9).random()) - } - } - - val form = MultipartBody.Builder("-----------------------------$boundary").apply { - setType(MultipartBody.FORM) - addPart( - Headers.headersOf("Content-Disposition", "form-data; name=\"image\""), - image.toRequestBody(null), - ) - }.build() - - val response = client.newCall( - POST("$apiUrl/api/loadImages", headers, form), - ).execute().parseAs() - - val newUrl = "https://127.0.0.1/?${response.image.substringAfter(":")}" - - return chain.proceed( - request.newBuilder() - .url(newUrl) - .build(), - ) - } - - private inline fun Response.parseAs(): T = use { - json.decodeFromString(it.body.string()) - } - - companion object { - private val dateFormat by lazy { - SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH) - } - } - - override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() - override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException() - override fun imageUrlParse(response: Response) = throw UnsupportedOperationException() -} diff --git a/src/en/opscans/src/eu/kanade/tachiyomi/extension/en/opscans/OpScansDto.kt b/src/en/opscans/src/eu/kanade/tachiyomi/extension/en/opscans/OpScansDto.kt deleted file mode 100644 index 5feed9525..000000000 --- a/src/en/opscans/src/eu/kanade/tachiyomi/extension/en/opscans/OpScansDto.kt +++ /dev/null @@ -1,46 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.opscans - -import eu.kanade.tachiyomi.source.model.SManga -import kotlinx.serialization.Serializable - -@Serializable -data class MangaData( - val id: String, - val name: String, - val author: String, - val info: String, - val genre1: String, - val genre2: String, - val genre3: String, - val cover: String, - val chapters: List, -) { - fun toSManga() = SManga.create().apply { - url = id - title = name - author = this@MangaData.author - description = info - genre = listOf(genre1, genre2, genre3).joinToString() - thumbnail_url = "https://127.0.0.1/image#$cover" - initialized = true - } -} - -@Serializable -data class Chapter( - val id: String, - val title: String? = "", - val date: String? = "", - val number: String, - val images: List? = emptyList(), -) - -@Serializable -data class Image( - val source: String, -) - -@Serializable -data class ImageResponse( - val image: String, -)