diff --git a/src/tr/eldermanga/AndroidManifest.xml b/src/tr/eldermanga/AndroidManifest.xml
new file mode 100644
index 000000000..96ed7fec4
--- /dev/null
+++ b/src/tr/eldermanga/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tr/eldermanga/build.gradle b/src/tr/eldermanga/build.gradle
new file mode 100644
index 000000000..0d453c9df
--- /dev/null
+++ b/src/tr/eldermanga/build.gradle
@@ -0,0 +1,7 @@
+ext {
+ extName = 'Elder Manga'
+ extClass = '.ElderManga'
+ extVersionCode = 1
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/tr/eldermanga/res/mipmap-hdpi/ic_launcher.png b/src/tr/eldermanga/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..412b4b0a8
Binary files /dev/null and b/src/tr/eldermanga/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/tr/eldermanga/res/mipmap-mdpi/ic_launcher.png b/src/tr/eldermanga/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..9e98eb274
Binary files /dev/null and b/src/tr/eldermanga/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/tr/eldermanga/res/mipmap-xhdpi/ic_launcher.png b/src/tr/eldermanga/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..0c737a388
Binary files /dev/null and b/src/tr/eldermanga/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/tr/eldermanga/res/mipmap-xxhdpi/ic_launcher.png b/src/tr/eldermanga/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..501773a6c
Binary files /dev/null and b/src/tr/eldermanga/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/tr/eldermanga/res/mipmap-xxxhdpi/ic_launcher.png b/src/tr/eldermanga/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..b07cdfb99
Binary files /dev/null and b/src/tr/eldermanga/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/tr/eldermanga/src/eu/kanade/tachiyomi/extension/tr/eldermanga/ElderManga.kt b/src/tr/eldermanga/src/eu/kanade/tachiyomi/extension/tr/eldermanga/ElderManga.kt
new file mode 100644
index 000000000..4cdc1b3ef
--- /dev/null
+++ b/src/tr/eldermanga/src/eu/kanade/tachiyomi/extension/tr/eldermanga/ElderManga.kt
@@ -0,0 +1,192 @@
+package eu.kanade.tachiyomi.extension.tr.eldermanga
+
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.asObservableSuccess
+import eu.kanade.tachiyomi.network.interceptor.rateLimit
+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 eu.kanade.tachiyomi.util.asJsoup
+import keiyoushi.utils.parseAs
+import keiyoushi.utils.tryParse
+import kotlinx.serialization.Serializable
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.Request
+import okhttp3.Response
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import rx.Observable
+import java.text.SimpleDateFormat
+import java.util.Locale
+
+class ElderManga : HttpSource() {
+ override val name = "Elder Manga"
+
+ override val baseUrl = "https://eldermanga.com"
+
+ // CDN used for search API responses and images
+ private val CDN_URL = "https://cdn1.eldermanga.com"
+
+ override val lang = "tr"
+
+ override val supportsLatest = true
+
+ override val client = network.cloudflareClient.newBuilder()
+ .rateLimit(3)
+ .build()
+
+ override fun headersBuilder() = super.headersBuilder()
+ .set("Referer", "$baseUrl/")
+
+ override fun popularMangaRequest(page: Int): Request =
+ GET("$baseUrl/search?page=$page&search=&order=4")
+
+ private fun popularMangaSelector() = "section[aria-label='series area'] .card"
+
+ private fun popularMangaFromElement(element: Element) = SManga.create().apply {
+ title = element.selectFirst("h2")!!.text()
+ thumbnail_url = element.selectFirst("img")?.absUrl("src")
+ setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
+ }
+
+ override fun popularMangaParse(response: Response): MangasPage {
+ val document = response.asJsoup()
+ val mangas = document.select(popularMangaSelector()).map { element ->
+ popularMangaFromElement(element)
+ }
+
+ val hasNextPage = hasNextPage(document)
+ return MangasPage(mangas, hasNextPage)
+ }
+
+ override fun latestUpdatesRequest(page: Int) =
+ GET("$baseUrl/search?page=$page&search=&order=3")
+
+ private fun latestUpdatesSelector() = popularMangaSelector()
+ private fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
+
+ override fun latestUpdatesParse(response: Response): MangasPage {
+ val document = response.asJsoup()
+ val mangas = document.select(latestUpdatesSelector()).map { element ->
+ latestUpdatesFromElement(element)
+ }
+
+ val hasNextPage = hasNextPage(document)
+ return MangasPage(mangas, hasNextPage)
+ }
+
+ override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
+ if (query.startsWith(URL_SEARCH_PREFIX)) {
+ val url = "$baseUrl/manga/${query.substringAfter(URL_SEARCH_PREFIX)}"
+ return client.newCall(GET(url, headers)).asObservableSuccess().map { response ->
+ val document = response.asJsoup()
+ when {
+ isMangaPage(document) -> MangasPage(listOf(mangaDetailsParse(response)), false)
+ else -> MangasPage(emptyList(), false)
+ }
+ }
+ }
+ return super.fetchSearchManga(page, query, filters)
+ }
+
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+ val url = "$CDN_URL/series/search/navbar".toHttpUrl().newBuilder()
+ .addQueryParameter("search", query)
+ .build()
+ return GET(url, headers)
+ }
+
+ override fun searchMangaParse(response: Response): MangasPage {
+ val dto = response.parseAs>()
+ val mangas = dto.map {
+ SManga.create().apply {
+ title = it.name
+ thumbnail_url = CDN_URL + it.image
+ url = "/manga/${it.id}/${title.lowercase().trim().replace(" ", "-")}"
+ }
+ }
+
+ return MangasPage(mangas, false)
+ }
+
+ // Not used (JSON-based search)
+
+ override fun mangaDetailsParse(response: Response) = SManga.create().apply {
+ val document = response.asJsoup()
+ with(document.selectFirst("#content")!!) {
+ title = selectFirst("h1")!!.text()
+ thumbnail_url = selectFirst("img")?.absUrl("src")
+ genre = select("a[href^='search?categories']").joinToString { it.text() }
+ description = selectFirst("div.grid h2 + p")?.text()
+ val pageStatus = selectFirst("span:contains(Durum) + span")?.text() ?: ""
+ status = when {
+ pageStatus.contains("Devam Ediyor", "Birakildi") -> SManga.ONGOING
+ pageStatus.contains("Tamamlandi") -> SManga.COMPLETED
+ pageStatus.contains("Ara Veridi") -> SManga.ON_HIATUS
+ else -> SManga.UNKNOWN
+ }
+
+ setUrlWithoutDomain(document.location())
+ }
+ }
+
+ override fun chapterListParse(response: Response): List {
+ val document = response.asJsoup()
+ return document.select("div.list-episode a").map { element ->
+ SChapter.create().apply {
+ name = element.selectFirst("h3")!!.text()
+ date_upload = dateFormat.tryParse(element.selectFirst("span")?.text())
+ setUrlWithoutDomain(element.absUrl("href"))
+ }
+ }
+ }
+
+ override fun pageListParse(response: Response): List {
+ val document = response.asJsoup()
+ val script = document.select("script")
+ .map { it.html() }.firstOrNull { pageRegex.find(it) != null }
+ ?: return emptyList()
+
+ val results = pageRegex.findAll(script).toList()
+ return results.mapIndexed { index, result ->
+ val url = result.groups.get(1)!!.value
+ Page(index, imageUrl = "$CDN_URL/upload/series/$url")
+ }
+ }
+
+ override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
+
+ private fun isMangaPage(document: Document): Boolean =
+ document.selectFirst("div.grid h2 + p") != null
+
+ private fun hasNextPage(document: Document): Boolean {
+ val navigation = document.selectFirst("section[aria-label='navigation']") ?: return false
+
+ // Mevcut aktif sayfa numarasını bul (!bg-gray-200 !text-gray-800 class'larına sahip)
+ val currentPageElement = navigation.selectFirst("a[class*='!bg-gray-200'][class*='!text-gray-800']")
+ val currentPage = currentPageElement?.text()?.toIntOrNull() ?: return false
+
+ // Tüm sayfa numaralarını topla
+ val pageNumbers = navigation.select("a[href*='page=']")
+ .mapNotNull { it.text().toIntOrNull() }
+ .filter { it > 0 }
+
+ // Eğer mevcut sayfadan büyük sayfa numarası varsa, sonraki sayfa var demektir
+ return pageNumbers.any { it > currentPage }
+ }
+
+ private fun String.contains(vararg fragment: String): Boolean =
+ fragment.any { trim().contains(it, ignoreCase = true) }
+
+ companion object {
+ const val URL_SEARCH_PREFIX = "slug:"
+ val dateFormat = SimpleDateFormat("MMM d ,yyyy", Locale("tr"))
+ val pageRegex = """\\"path\\":\\"([^"]+)\\""".trimIndent().toRegex()
+ }
+}
+
+@Serializable
+class SearchDto(val id: Int, val name: String, val image: String)
diff --git a/src/tr/eldermanga/src/eu/kanade/tachiyomi/extension/tr/eldermanga/ElderMangaUrlActivity.kt b/src/tr/eldermanga/src/eu/kanade/tachiyomi/extension/tr/eldermanga/ElderMangaUrlActivity.kt
new file mode 100644
index 000000000..a2ddeb395
--- /dev/null
+++ b/src/tr/eldermanga/src/eu/kanade/tachiyomi/extension/tr/eldermanga/ElderMangaUrlActivity.kt
@@ -0,0 +1,37 @@
+package eu.kanade.tachiyomi.extension.tr.eldermanga
+
+import android.app.Activity
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import kotlin.system.exitProcess
+
+class ElderMangaUrlActivity : Activity() {
+
+ private val tag = javaClass.simpleName
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val pathSegments = intent?.data?.pathSegments
+ if (pathSegments != null && pathSegments.size > 2) {
+ val item = "${pathSegments[1]}/${pathSegments[2]}"
+ val mainIntent = Intent().apply {
+ action = "eu.kanade.tachiyomi.SEARCH"
+ putExtra("query", "${ElderManga.URL_SEARCH_PREFIX}$item")
+ putExtra("filter", packageName)
+ }
+
+ try {
+ startActivity(mainIntent)
+ } catch (e: ActivityNotFoundException) {
+ Log.e(tag, e.toString())
+ }
+ } else {
+ Log.e(tag, "could not parse uri from intent $intent")
+ }
+
+ finish()
+ exitProcess(0)
+ }
+}