From 47d888e1202dbf67a578c15df1f97a4493fa4d19 Mon Sep 17 00:00:00 2001
From: mobi2002 <48650614+mobi2002@users.noreply.github.com>
Date: Sun, 19 Mar 2023 02:01:16 +0500
Subject: [PATCH] A Pair Of 2+ : move to individual extension (#15757)
* Remove from Madara
* APairOf2: rewrite for new site
---
.../multisrc/madara/MadaraGenerator.kt | 1 -
src/en/apairof2/AndroidManifest.xml | 24 +++
src/en/apairof2/build.gradle | 11 ++
.../apairof2/res/mipmap-hdpi/ic_launcher.png | Bin
.../apairof2/res/mipmap-mdpi/ic_launcher.png | Bin
.../apairof2/res/mipmap-xhdpi/ic_launcher.png | Bin
.../res/mipmap-xxhdpi/ic_launcher.png | Bin
.../res/mipmap-xxxhdpi/ic_launcher.png | Bin
.../en}/apairof2/res/web_hi_res_512.png | Bin
.../extension/en/apairof2/APairOf2.kt | 155 ++++++++++++++++++
.../en/apairof2/APairOf2UrlActivity.kt | 34 ++++
11 files changed, 224 insertions(+), 1 deletion(-)
create mode 100644 src/en/apairof2/AndroidManifest.xml
create mode 100644 src/en/apairof2/build.gradle
rename {multisrc/overrides/madara => src/en}/apairof2/res/mipmap-hdpi/ic_launcher.png (100%)
rename {multisrc/overrides/madara => src/en}/apairof2/res/mipmap-mdpi/ic_launcher.png (100%)
rename {multisrc/overrides/madara => src/en}/apairof2/res/mipmap-xhdpi/ic_launcher.png (100%)
rename {multisrc/overrides/madara => src/en}/apairof2/res/mipmap-xxhdpi/ic_launcher.png (100%)
rename {multisrc/overrides/madara => src/en}/apairof2/res/mipmap-xxxhdpi/ic_launcher.png (100%)
rename {multisrc/overrides/madara => src/en}/apairof2/res/web_hi_res_512.png (100%)
create mode 100644 src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2.kt
create mode 100644 src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2UrlActivity.kt
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 23e2e6d94..34da225b2 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
@@ -38,7 +38,6 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("Anikiga", "https://anikiga.com", "tr"),
SingleLang("Anisa Manga", "https://anisamanga.com", "tr"),
SingleLang("Ansh Scans", "https://anshscans.org", "en"),
- SingleLang("A Pair of 2+", "https://po2scans.com", "en", className = "APairOf2"),
SingleLang("ApollComics", "https://apollcomics.xyz", "es", isNsfw = true, overrideVersionCode = 2),
SingleLang("Apolltoons", "https://apolltoons.xyz", "es", isNsfw = true),
SingleLang("Aqua Manga", "https://aquamanga.com", "en", overrideVersionCode = 3),
diff --git a/src/en/apairof2/AndroidManifest.xml b/src/en/apairof2/AndroidManifest.xml
new file mode 100644
index 000000000..92fcba232
--- /dev/null
+++ b/src/en/apairof2/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/en/apairof2/build.gradle b/src/en/apairof2/build.gradle
new file mode 100644
index 000000000..f620fcadb
--- /dev/null
+++ b/src/en/apairof2/build.gradle
@@ -0,0 +1,11 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+
+ext {
+ extName = 'A Pair of 2+'
+ pkgNameSuffix = 'en.apairof2'
+ extClass = '.APairOf2'
+ extVersionCode = 29
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/multisrc/overrides/madara/apairof2/res/mipmap-hdpi/ic_launcher.png b/src/en/apairof2/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/apairof2/res/mipmap-hdpi/ic_launcher.png
rename to src/en/apairof2/res/mipmap-hdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/apairof2/res/mipmap-mdpi/ic_launcher.png b/src/en/apairof2/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/apairof2/res/mipmap-mdpi/ic_launcher.png
rename to src/en/apairof2/res/mipmap-mdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/apairof2/res/mipmap-xhdpi/ic_launcher.png b/src/en/apairof2/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/apairof2/res/mipmap-xhdpi/ic_launcher.png
rename to src/en/apairof2/res/mipmap-xhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/apairof2/res/mipmap-xxhdpi/ic_launcher.png b/src/en/apairof2/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/apairof2/res/mipmap-xxhdpi/ic_launcher.png
rename to src/en/apairof2/res/mipmap-xxhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/apairof2/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/apairof2/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/apairof2/res/mipmap-xxxhdpi/ic_launcher.png
rename to src/en/apairof2/res/mipmap-xxxhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/apairof2/res/web_hi_res_512.png b/src/en/apairof2/res/web_hi_res_512.png
similarity index 100%
rename from multisrc/overrides/madara/apairof2/res/web_hi_res_512.png
rename to src/en/apairof2/res/web_hi_res_512.png
diff --git a/src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2.kt b/src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2.kt
new file mode 100644
index 000000000..8e8991dc5
--- /dev/null
+++ b/src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2.kt
@@ -0,0 +1,155 @@
+package eu.kanade.tachiyomi.extension.en.apairof2
+
+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.ParsedHttpSource
+import okhttp3.Headers
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import rx.Observable
+import java.text.SimpleDateFormat
+import java.util.Locale
+
+class APairOf2 : ParsedHttpSource() {
+
+ override val name = "A Pair of 2+"
+
+ override val baseUrl = "https://po2scans.com"
+
+ override val lang = "en"
+
+ override val supportsLatest = true
+
+ override val versionId = 2
+
+ override val client: OkHttpClient = network.cloudflareClient
+
+ override fun headersBuilder(): Headers.Builder = super.headersBuilder()
+ .add("Referer", "$baseUrl/")
+
+ // popular
+ override fun popularMangaRequest(page: Int): Request {
+ return GET("$baseUrl/series", headers)
+ }
+
+ override fun popularMangaSelector() = "div.series-list"
+
+ override fun popularMangaFromElement(element: Element): SManga {
+ return SManga.create().apply {
+ title = element.select("div > h2").text()
+ url = element.select("div > a").attr("href").let { "/$it" }
+ thumbnail_url = element.select("img").attr("data-src").let { "$baseUrl/$it" }
+ }
+ }
+
+ // TODO: add page selectors & url parameters when site have enough series for pagination
+ override fun popularMangaNextPageSelector() = null
+
+ // latest
+ override fun latestUpdatesRequest(page: Int): Request {
+ return GET(baseUrl, headers)
+ }
+
+ override fun latestUpdatesSelector() = "div.chap"
+
+ override fun latestUpdatesFromElement(element: Element): SManga {
+ return SManga.create().apply {
+ element.select("div.chap-title a").let { it ->
+ url = it.attr("href").let { "/$it" }
+ title = it.text()
+ }
+ thumbnail_url = element.select("img").attr("data-src").let { "$baseUrl/$it" }
+ }
+ }
+
+ override fun latestUpdatesNextPageSelector() = popularMangaSelector()
+
+ // search
+ override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
+ if (!query.startsWith(SLUG_SEARCH_PREFIX)) {
+ return super.fetchSearchManga(page, query, filters)
+ }
+
+ val url = "/series/${query.substringAfter(SLUG_SEARCH_PREFIX)}"
+ return fetchMangaDetails(SManga.create().apply { this.url = url }).map {
+ it.url = url
+ MangasPage(listOf(it), false)
+ }
+ }
+
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+ return GET("$baseUrl/series?search=$query", headers)
+ }
+
+ override fun searchMangaSelector() = popularMangaSelector()
+
+ override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
+
+ override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
+
+ // manga details
+ override fun mangaDetailsParse(document: Document): SManga {
+ return SManga.create().apply {
+ title = document.selectFirst(".title")!!.text()
+ author = document.select(".author > span:nth-child(2)").text()
+ artist = author
+ status = document.select(".status > span:nth-child(2)").text().parseStatus()
+ description = document.select(".summary p").text()
+ thumbnail_url = document.select("div.series-image img").attr("src").let { "$baseUrl/$it" }
+ }
+ }
+
+ private fun String.parseStatus(): Int {
+ return when {
+ this.contains("ongoing", true) -> SManga.ONGOING
+ this.contains("complete", true) -> SManga.COMPLETED
+ else -> SManga.UNKNOWN
+ }
+ }
+
+ // chapter list
+ override fun chapterListSelector() = "div.chap"
+
+ override fun chapterFromElement(element: Element): SChapter {
+ return SChapter.create().apply {
+ element.select("a").let { a ->
+ url = a.attr("href").let { "/$it" }
+ name = a.text()
+ }
+ date_upload = parseDate(element.select("div > div > span:nth-child(2)").text())
+ }
+ }
+
+ private fun parseDate(dateStr: String): Long {
+ return runCatching { DATE_FORMATTER.parse(dateStr)?.time }
+ .getOrNull() ?: 0L
+ }
+
+ // page list
+ override fun pageListParse(document: Document): List {
+ return document.select(".swiper-slide img").mapIndexed { index, img ->
+ Page(
+ index = index,
+ imageUrl = img.attr("src").let { "$baseUrl/$it" },
+ )
+ }
+ }
+
+ override fun imageUrlParse(document: Document): String {
+ throw UnsupportedOperationException("not used")
+ }
+
+ companion object {
+ private val DATE_FORMATTER by lazy {
+ SimpleDateFormat("dd MMMM, yy", Locale.ENGLISH)
+ }
+
+ const val SLUG_SEARCH_PREFIX = "slug:"
+ }
+}
diff --git a/src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2UrlActivity.kt b/src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2UrlActivity.kt
new file mode 100644
index 000000000..8ba124982
--- /dev/null
+++ b/src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2UrlActivity.kt
@@ -0,0 +1,34 @@
+package eu.kanade.tachiyomi.extension.en.apairof2
+
+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 APairOf2UrlActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val pathSegments = intent?.data?.pathSegments
+ if (pathSegments != null && pathSegments.size > 1) {
+ val slug = pathSegments[1]
+ val mainIntent = Intent().apply {
+ action = "eu.kanade.tachiyomi.SEARCH"
+ putExtra("query", "${APairOf2.SLUG_SEARCH_PREFIX}$slug")
+ putExtra("filter", packageName)
+ }
+
+ try {
+ startActivity(mainIntent)
+ } catch (e: ActivityNotFoundException) {
+ Log.e("APairOf2UrlActivity", e.toString())
+ }
+ } else {
+ Log.e("APairOf2UrlActivity", "could not parse uri from intent $intent")
+ }
+
+ finish()
+ exitProcess(0)
+ }
+}