A Pair Of 2+ : move to individual extension (#15757)

* Remove from Madara

* APairOf2: rewrite for new site
This commit is contained in:
mobi2002 2023-03-19 02:01:16 +05:00 committed by GitHub
parent 8bf06c53b9
commit 47d888e120
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 224 additions and 1 deletions

View File

@ -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),

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.kanade.tachiyomi.extension">
<application>
<activity
android:name=".en.apairof2.APairOf2UrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="po2scans.com"
android:pathPattern="/series/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -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"

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -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<MangasPage> {
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<Page> {
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:"
}
}

View File

@ -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)
}
}