diff --git a/src/tr/uzaymanga/AndroidManifest.xml b/src/tr/uzaymanga/AndroidManifest.xml
new file mode 100644
index 000000000..87114e76d
--- /dev/null
+++ b/src/tr/uzaymanga/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tr/uzaymanga/build.gradle b/src/tr/uzaymanga/build.gradle
index c55a8626f..ae6caa5bb 100644
--- a/src/tr/uzaymanga/build.gradle
+++ b/src/tr/uzaymanga/build.gradle
@@ -1,9 +1,7 @@
ext {
extName = 'Uzay Manga'
extClass = '.UzayManga'
- themePkg = 'mangathemesia'
- baseUrl = 'https://uzaymanga.com'
- overrideVersionCode = 6
+ extVersionCode = 37
}
apply from: "$rootDir/common.gradle"
diff --git a/src/tr/uzaymanga/res/mipmap-hdpi/ic_launcher.png b/src/tr/uzaymanga/res/mipmap-hdpi/ic_launcher.png
index 695237093..0d1da0e91 100644
Binary files a/src/tr/uzaymanga/res/mipmap-hdpi/ic_launcher.png and b/src/tr/uzaymanga/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/tr/uzaymanga/res/mipmap-mdpi/ic_launcher.png b/src/tr/uzaymanga/res/mipmap-mdpi/ic_launcher.png
index 4d3c38a74..675e53238 100644
Binary files a/src/tr/uzaymanga/res/mipmap-mdpi/ic_launcher.png and b/src/tr/uzaymanga/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/tr/uzaymanga/res/mipmap-xhdpi/ic_launcher.png b/src/tr/uzaymanga/res/mipmap-xhdpi/ic_launcher.png
index 55828020d..80d0c8c8a 100644
Binary files a/src/tr/uzaymanga/res/mipmap-xhdpi/ic_launcher.png and b/src/tr/uzaymanga/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/tr/uzaymanga/res/mipmap-xxhdpi/ic_launcher.png b/src/tr/uzaymanga/res/mipmap-xxhdpi/ic_launcher.png
index ac9a51d4e..bdb534078 100644
Binary files a/src/tr/uzaymanga/res/mipmap-xxhdpi/ic_launcher.png and b/src/tr/uzaymanga/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/tr/uzaymanga/res/mipmap-xxxhdpi/ic_launcher.png b/src/tr/uzaymanga/res/mipmap-xxxhdpi/ic_launcher.png
index 2d77ac42a..fac9f6252 100644
Binary files a/src/tr/uzaymanga/res/mipmap-xxxhdpi/ic_launcher.png and b/src/tr/uzaymanga/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/tr/uzaymanga/src/eu/kanade/tachiyomi/extension/tr/uzaymanga/UzayManga.kt b/src/tr/uzaymanga/src/eu/kanade/tachiyomi/extension/tr/uzaymanga/UzayManga.kt
index a1c083e7e..4e4b0b6a7 100644
--- a/src/tr/uzaymanga/src/eu/kanade/tachiyomi/extension/tr/uzaymanga/UzayManga.kt
+++ b/src/tr/uzaymanga/src/eu/kanade/tachiyomi/extension/tr/uzaymanga/UzayManga.kt
@@ -1,12 +1,166 @@
package eu.kanade.tachiyomi.extension.tr.uzaymanga
-import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
+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.ParsedHttpSource
+import eu.kanade.tachiyomi.util.asJsoup
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+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 uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
-class UzayManga : MangaThemesia(
- "Uzay Manga",
- "https://uzaymanga.com",
- "tr",
- dateFormat = SimpleDateFormat("MMM d, yyyy", Locale("tr")),
-)
+class UzayManga : ParsedHttpSource() {
+ override val name = "Uzay Manga"
+
+ override val baseUrl = "https://uzaymanga.com"
+
+ override val lang = "tr"
+
+ override val supportsLatest = true
+
+ override val versionId = 2
+
+ override val client = network.cloudflareClient.newBuilder()
+ .rateLimit(3)
+ .build()
+
+ private val json: Json by injectLazy()
+
+ override fun headersBuilder() = super.headersBuilder()
+ .set("Referer", "$baseUrl/")
+
+ override fun popularMangaRequest(page: Int): Request =
+ GET("$baseUrl/search?page=$page&search=&order=4")
+
+ override fun popularMangaNextPageSelector() =
+ "section[aria-label='navigation'] li:has(a[class~='!text-gray-800']) + li > a:not([href~='#'])"
+
+ override fun popularMangaSelector() = "section[aria-label='series area'] .card"
+
+ override 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 latestUpdatesRequest(page: Int) =
+ GET("$baseUrl/search?page=$page&search=&order=3")
+
+ override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
+
+ override fun latestUpdatesSelector() = popularMangaSelector()
+
+ override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
+
+ 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(document)), 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 = json.decodeFromString>(response.body.string())
+ 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)
+ }
+
+ override fun searchMangaFromElement(element: Element) = throw UnsupportedOperationException()
+
+ override fun searchMangaNextPageSelector() = throw UnsupportedOperationException()
+
+ override fun searchMangaSelector() = throw UnsupportedOperationException()
+
+ override fun mangaDetailsParse(document: Document) = SManga.create().apply {
+ 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 chapterListSelector() = "div.list-episode a"
+
+ override fun chapterFromElement(element: Element) = SChapter.create().apply {
+ name = element.selectFirst("h3")!!.text()
+ date_upload = element.selectFirst("span")?.text()?.toDate() ?: 0L
+ setUrlWithoutDomain(element.absUrl("href"))
+ }
+
+ override fun pageListParse(document: Document): List {
+ val script = document.select("script")
+ .map { it.html() }.firstOrNull { pageRegex.find(it) != null }
+ ?: return emptyList()
+
+ return pageRegex.findAll(script).mapIndexed { index, result ->
+ val url = result.groups.get("path")!!.value
+ Page(index, document.location(), "$CDN_URL/upload/series/$url")
+ }.toList()
+ }
+
+ override fun imageUrlParse(document: Document) = ""
+
+ private fun isMangaPage(document: Document): Boolean =
+ document.selectFirst("div.grid h2 + p") != null
+
+ private fun String.toDate(): Long =
+ try { dateFormat.parse(this)!!.time } catch (_: Exception) { 0L }
+
+ private fun String.contains(vararg fragment: String): Boolean =
+ fragment.any { trim().contains(it, ignoreCase = true) }
+
+ companion object {
+ const val CDN_URL = "https://cdn1.uzaymanga.com"
+ 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/uzaymanga/src/eu/kanade/tachiyomi/extension/tr/uzaymanga/UzayMangaUrlActivity.kt b/src/tr/uzaymanga/src/eu/kanade/tachiyomi/extension/tr/uzaymanga/UzayMangaUrlActivity.kt
new file mode 100644
index 000000000..0fd6e6194
--- /dev/null
+++ b/src/tr/uzaymanga/src/eu/kanade/tachiyomi/extension/tr/uzaymanga/UzayMangaUrlActivity.kt
@@ -0,0 +1,37 @@
+package eu.kanade.tachiyomi.extension.tr.uzaymanga
+
+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 UzayMangaUrlActivity : 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", "${UzayManga.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)
+ }
+}