diff --git a/multisrc/overrides/madara/olympusscanlation/src/OlympusScanlationFactory.kt b/multisrc/overrides/madara/olympusscanlation/src/OlympusScanlationFactory.kt
deleted file mode 100644
index 9c00dfaf7..000000000
--- a/multisrc/overrides/madara/olympusscanlation/src/OlympusScanlationFactory.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package eu.kanade.tachiyomi.extension.all.olympusscanlation
-
-import eu.kanade.tachiyomi.multisrc.madara.Madara
-import eu.kanade.tachiyomi.network.interceptor.rateLimit
-import eu.kanade.tachiyomi.source.SourceFactory
-import okhttp3.OkHttpClient
-import java.text.SimpleDateFormat
-import java.util.Locale
-import java.util.concurrent.TimeUnit
-
-class OlympusScanlationFactory : SourceFactory {
- override fun createSources() = listOf(
- OlympusScanlationBr(),
- OlympusScanlationEs(),
- )
-}
-
-abstract class OlympusScanlation(
- override val baseUrl: String,
- lang: String,
- dateFormat: SimpleDateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale.US),
-) : Madara("Olympus Scanlation", baseUrl, lang, dateFormat)
-
-class OlympusScanlationEs : OlympusScanlation("https://olympusscanlation.com", "es")
-
-class OlympusScanlationBr : OlympusScanlation(
- "https://br.olympusscanlation.com",
- "pt-BR",
- SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
-) {
-
- override val client: OkHttpClient = super.client.newBuilder()
- .rateLimit(1, 2, TimeUnit.SECONDS)
- .build()
-}
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 178f32c3c..73ff11a6c 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
@@ -16,7 +16,6 @@ class MadaraGenerator : ThemeSourceGenerator {
MultiLang("Leviatan Scans", "https://leviatanscans.com", listOf("en", "es"), className = "LeviatanScansFactory", overrideVersionCode = 13),
MultiLang("MangaForFree.net", "https://mangaforfree.net", listOf("en", "ko", "all"), isNsfw = true, className = "MangaForFreeFactory", pkgName = "mangaforfree", overrideVersionCode = 1),
MultiLang("Manhwa18.cc", "https://manhwa18.cc", listOf("en", "ko", "all"), isNsfw = true, className = "Manhwa18CcFactory", pkgName = "manhwa18cc", overrideVersionCode = 4),
- MultiLang("Olympus Scanlation", "https://olympusscanlation.com", listOf("es", "pt-BR")),
MultiLang("Reaper Scans", "https://reaperscans.com", listOf("fr", "tr"), className = "ReaperScansFactory", pkgName = "reaperscans", overrideVersionCode = 12),
SingleLang("1st Kiss Manga.love", "https://1stkissmanga.love", "en", className = "FirstKissMangaLove", overrideVersionCode = 2),
SingleLang("1st Kiss Manhua", "https://1stkissmanhua.com", "en", className = "FirstKissManhua", overrideVersionCode = 4),
diff --git a/src/es/olympusscanlation/AndroidManifest.xml b/src/es/olympusscanlation/AndroidManifest.xml
new file mode 100644
index 000000000..b4571bfa8
--- /dev/null
+++ b/src/es/olympusscanlation/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/src/es/olympusscanlation/build.gradle b/src/es/olympusscanlation/build.gradle
new file mode 100644
index 000000000..0d79d83f8
--- /dev/null
+++ b/src/es/olympusscanlation/build.gradle
@@ -0,0 +1,13 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlinx-serialization'
+
+
+ext {
+ extName = 'Olympus Scanlation'
+ pkgNameSuffix = 'es.olympusscanlation'
+ extClass = '.OlympusScanlation'
+ extVersionCode = 2
+}
+
+apply from: "$rootDir/common.gradle"
\ No newline at end of file
diff --git a/multisrc/overrides/madara/olympusscanlation/res/mipmap-hdpi/ic_launcher.png b/src/es/olympusscanlation/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/olympusscanlation/res/mipmap-hdpi/ic_launcher.png
rename to src/es/olympusscanlation/res/mipmap-hdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/olympusscanlation/res/mipmap-mdpi/ic_launcher.png b/src/es/olympusscanlation/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/olympusscanlation/res/mipmap-mdpi/ic_launcher.png
rename to src/es/olympusscanlation/res/mipmap-mdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/olympusscanlation/res/mipmap-xhdpi/ic_launcher.png b/src/es/olympusscanlation/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/olympusscanlation/res/mipmap-xhdpi/ic_launcher.png
rename to src/es/olympusscanlation/res/mipmap-xhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/olympusscanlation/res/mipmap-xxhdpi/ic_launcher.png b/src/es/olympusscanlation/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/olympusscanlation/res/mipmap-xxhdpi/ic_launcher.png
rename to src/es/olympusscanlation/res/mipmap-xxhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/olympusscanlation/res/mipmap-xxxhdpi/ic_launcher.png b/src/es/olympusscanlation/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/olympusscanlation/res/mipmap-xxxhdpi/ic_launcher.png
rename to src/es/olympusscanlation/res/mipmap-xxxhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/olympusscanlation/res/web_hi_res_512.png b/src/es/olympusscanlation/res/web_hi_res_512.png
similarity index 100%
rename from multisrc/overrides/madara/olympusscanlation/res/web_hi_res_512.png
rename to src/es/olympusscanlation/res/web_hi_res_512.png
diff --git a/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt b/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt
new file mode 100644
index 000000000..fa2bde9ea
--- /dev/null
+++ b/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt
@@ -0,0 +1,165 @@
+package eu.kanade.tachiyomi.extension.es.olympusscanlation
+
+import eu.kanade.tachiyomi.extension.es.olympusscanlation.dto.OlympusScanlationDto.ChapterDto
+import eu.kanade.tachiyomi.extension.es.olympusscanlation.dto.OlympusScanlationDto.MangaDetailDto
+import eu.kanade.tachiyomi.extension.es.olympusscanlation.dto.OlympusScanlationDto.MangaDto
+import eu.kanade.tachiyomi.extension.es.olympusscanlation.dto.OlympusScanlationDto.PayloadChapterDto
+import eu.kanade.tachiyomi.extension.es.olympusscanlation.dto.OlympusScanlationDto.PayloadHomeDto
+import eu.kanade.tachiyomi.extension.es.olympusscanlation.dto.OlympusScanlationDto.PayloadMangaDto
+import eu.kanade.tachiyomi.extension.es.olympusscanlation.dto.OlympusScanlationDto.PayloadPagesDto
+import eu.kanade.tachiyomi.network.GET
+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.HttpUrl.Companion.toHttpUrl
+import okhttp3.Request
+import okhttp3.Response
+import uy.kohesive.injekt.injectLazy
+class OlympusScanlation : HttpSource() {
+
+ override val baseUrl: String = "https://olympusscans.com"
+ private val apiBaseUrl: String = "https://dashboard.olympusscans.com"
+ override val lang: String = "es"
+ override val name: String = "Olympus Scanlation"
+ override val versionId = 2
+ override val supportsLatest: Boolean = true
+ private val json: Json by injectLazy()
+
+ // Search
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+ val apiUrl = "$apiBaseUrl/api/search".toHttpUrl().newBuilder()
+ .addQueryParameter("name", query)
+ .build()
+ return GET(apiUrl, headers)
+ }
+
+ override fun searchMangaParse(response: Response): MangasPage {
+ val result = json.decodeFromString(response.body.string())
+ val mangaList = result.data.map {
+ SManga.create().apply {
+ url = "/series/comic-${it.slug}"
+ title = it.name
+ thumbnail_url = it.cover
+ }
+ }
+ return MangasPage(mangaList, hasNextPage = false)
+ }
+
+ // Latest
+ override fun latestUpdatesRequest(page: Int): Request = popularMangaRequest(1)
+ override fun latestUpdatesParse(response: Response): MangasPage {
+ val result = json.decodeFromString(response.body.string())
+ val mangaList = result.data.new_chapters.map {
+ SManga.create().apply {
+ url = "/series/comic-${it.slug}"
+ title = it.name
+ thumbnail_url = it.cover
+ }
+ }
+ return MangasPage(mangaList, hasNextPage = false)
+ }
+
+ // Details
+ override fun mangaDetailsParse(response: Response): SManga {
+ val slug = response.request.url
+ .toString()
+ .substringAfter("/series/comic-")
+ .substringBefore("/chapters")
+ val urla = "$apiBaseUrl/api/series/$slug?type=comic"
+ val newRequest = GET(url = urla, headers = headers)
+ val newResponse = client.newCall(newRequest).execute()
+ val result = json.decodeFromString(newResponse.body.string())
+ return SManga.create().apply {
+ url = "/series/comic-$slug"
+ title = result.data.name
+ thumbnail_url = result.data.cover
+ description = result.data.summary
+ }
+ }
+ override fun imageUrlParse(response: Response): String = throw Exception("Not used")
+
+ // Chapters
+
+ override fun chapterListRequest(manga: SManga): Request {
+ return paginatedChapterListRequest(
+ manga.url
+ .substringAfter("/series/comic-")
+ .substringBefore("/chapters"),
+ 1,
+ )
+ }
+
+ private fun paginatedChapterListRequest(mangaUrl: String, page: Int): Request {
+ return GET(
+ url = "$apiBaseUrl/api/series/$mangaUrl/chapters?page=$page&direction=desc&type=comic",
+ headers = headers,
+ )
+ }
+
+ override fun chapterListParse(response: Response): List {
+ val slug = response.request.url
+ .toString()
+ .substringAfter("/series/")
+ .substringBefore("/chapters")
+ var data = json.decodeFromString(response.body.string())
+ var resultSize = data.data.size
+ var page = 2
+ while (data.meta.total > resultSize) {
+ val newRequest = paginatedChapterListRequest("$slug", page)
+ val newResponse = client.newCall(newRequest).execute()
+ var newData = json.decodeFromString(newResponse.body.string())
+ data.data += newData.data
+ resultSize += newData.data.size
+ page += 1
+ }
+ return data.data.map { chap -> chapterFromObject(chap, slug) }
+ }
+
+ private fun chapterFromObject(chapter: ChapterDto, slug: String) = SChapter.create().apply {
+ url = "/capitulo/${chapter.id}/comic-$slug"
+ name = "Capitulo ${chapter.name}"
+ chapter_number = chapter.name.toFloatOrNull() ?: -1f
+ }
+ // Pages
+
+ override fun pageListRequest(chapter: SChapter): Request {
+ var id = chapter.url
+ .substringAfter("/capitulo/")
+ .substringBefore("/chapters")
+ .substringBefore("/comic")
+ val slug = chapter.url
+ .substringAfter("comic-")
+ .substringBefore("/chapters")
+ .substringBefore("/comic")
+ return GET("$apiBaseUrl/api/series/$slug/chapters/$id?type=comic")
+ }
+
+ override fun pageListParse(response: Response): List =
+ json.decodeFromString(response.body.string()).chapter.pages.mapIndexed { i, img ->
+ Page(i, "", img)
+ }
+
+ // Popular
+ override fun popularMangaParse(response: Response): MangasPage {
+ val result = json.decodeFromString(response.body.string())
+ val resultMangaList = json.decodeFromString>(result.data.popular_comics)
+ val mangaList = resultMangaList.map {
+ SManga.create().apply {
+ url = "/series/comic-${it.slug}"
+ title = it.name
+ thumbnail_url = it.cover
+ }
+ }
+ return MangasPage(mangaList, hasNextPage = false)
+ }
+ override fun popularMangaRequest(page: Int): Request {
+ val apiUrl = "$apiBaseUrl/api/home".toHttpUrl().newBuilder()
+ .build()
+ return GET(apiUrl, headers)
+ }
+}
diff --git a/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/dto/OlympusScanlationDto.kt b/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/dto/OlympusScanlationDto.kt
new file mode 100644
index 000000000..6e73d9811
--- /dev/null
+++ b/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/dto/OlympusScanlationDto.kt
@@ -0,0 +1,48 @@
+package eu.kanade.tachiyomi.extension.es.olympusscanlation.dto
+import kotlinx.serialization.Serializable
+@Serializable
+object OlympusScanlationDto {
+ @Serializable
+ data class ChapterDto(val id: Int, val name: String)
+
+ @Serializable
+ data class MangaDto(
+ val id: Int,
+ val name: String,
+ val slug: String?,
+ val cover: String?,
+ val type: String?,
+ val summary: String?,
+ )
+
+ @Serializable
+ data class MangaDetailDto(
+ var data: MangaDto,
+ )
+
+ @Serializable
+ data class MetaDto(val total: Int)
+
+ @Serializable
+ data class PageDto(val pages: List)
+
+ @Serializable
+ data class PayloadChapterDto(var data: List, val meta: MetaDto)
+
+ @Serializable
+ data class PayloadMangaDto(val data: List)
+
+ @Serializable
+ data class PayloadPagesDto(val chapter: PageDto)
+
+ @Serializable
+ data class HomeDto(
+ val popular_comics: String,
+ val new_chapters: List,
+ )
+
+ @Serializable
+ data class PayloadHomeDto(
+ val data: HomeDto,
+ )
+}