Add SadScans and make APairOf2 a multisrc (#1046)

* Add SadScans and make APairOf2 a multisrc

* Formatting

* Trailing comma

* optimize icons

* newline

---------

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
This commit is contained in:
beerpsi 2024-02-07 15:15:50 +07:00 committed by Draff
parent c02bd36b43
commit b0b32918e1
22 changed files with 98 additions and 96 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,12 @@
package eu.kanade.tachiyomi.extension.en.apairof2
import eu.kanade.tachiyomi.multisrc.po2scans.PO2Scans
import eu.kanade.tachiyomi.network.interceptor.rateLimit
class APairOf2 : PO2Scans("A Pair Of 2+", "https://po2scans.com", "en") {
override val versionId = 2
override val client = super.client.newBuilder()
.rateLimit(4)
.build()
}

View File

@ -3,7 +3,7 @@
<application> <application>
<activity <activity
android:name=".en.apairof2.APairOf2UrlActivity" android:name="eu.kanade.tachiyomi.multisrc.po2scans.PO2ScansUrlActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:exported="true" android:exported="true"
android:theme="@android:style/Theme.NoDisplay"> android:theme="@android:style/Theme.NoDisplay">
@ -14,9 +14,9 @@
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data <data
android:host="po2scans.com" android:host="${SOURCEHOST}"
android:pathPattern="/series/..*" android:pathPattern="/series/..*"
android:scheme="https" /> android:scheme="${SOURCESCHEME}" />
</intent-filter> </intent-filter>
</activity> </activity>
</application> </application>

View File

@ -0,0 +1,3 @@
dependencies {
implementation(project(":lib:dataimage"))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -1,80 +1,64 @@
package eu.kanade.tachiyomi.extension.en.apairof2 package eu.kanade.tachiyomi.multisrc.po2scans
import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor
import eu.kanade.tachiyomi.lib.dataimage.dataImageAsUrl import eu.kanade.tachiyomi.lib.dataimage.dataImageAsUrl
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource 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.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class APairOf2 : ParsedHttpSource() { abstract class PO2Scans(
override val name: String,
override val name = "A Pair of 2+" override val baseUrl: String,
override val lang: String,
override val baseUrl = "https://po2scans.com" private val dateFormat: SimpleDateFormat = SimpleDateFormat("dd MMMM, yy", Locale.ENGLISH),
) : ParsedHttpSource() {
override val lang = "en"
override val supportsLatest = true override val supportsLatest = true
override val versionId = 2 override val client = network.cloudflareClient.newBuilder()
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor(DataImageInterceptor()) .addInterceptor(DataImageInterceptor())
.rateLimit(4)
.build() .build()
override fun headersBuilder(): Headers.Builder = super.headersBuilder() override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/") .add("Referer", "$baseUrl/")
// popular // popular
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int) = GET("$baseUrl/series", headers)
return GET("$baseUrl/series", headers)
}
override fun popularMangaSelector() = "div.series-list" override fun popularMangaSelector() = "div.series-list"
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element) = SManga.create().apply {
return SManga.create().apply { setUrlWithoutDomain(element.selectFirst("div > a")!!.absUrl("href"))
title = element.select("div > h2").text() title = element.selectFirst("div > h2")!!.text()
url = element.select("div > a").attr("href").let { "/$it" } thumbnail_url = element.selectFirst("img")?.absUrl("data-src")
thumbnail_url = element.select("img").attr("abs:data-src")
}
} }
// TODO: add page selectors & url parameters when site have enough series for pagination // TODO: add page selectors & url parameters when site have enough series for pagination
override fun popularMangaNextPageSelector() = null override fun popularMangaNextPageSelector() = null
// latest // latest
override fun latestUpdatesRequest(page: Int): Request { override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers)
return GET(baseUrl, headers)
}
override fun latestUpdatesSelector() = "div.chap" override fun latestUpdatesSelector() = "div.chap"
override fun latestUpdatesFromElement(element: Element): SManga { override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
return SManga.create().apply { element.selectFirst("div.chap-title a")!!.let {
element.select("div.chap-title a").let { it -> setUrlWithoutDomain(it.absUrl("href"))
url = it.attr("href").let { "/$it" }
title = it.text() title = it.text()
} }
thumbnail_url = element.select("img").attr("abs:data-src") thumbnail_url = element.selectFirst("img")?.absUrl("data-src")
}
} }
override fun latestUpdatesNextPageSelector() = popularMangaSelector() override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
// search // search
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
@ -83,15 +67,15 @@ class APairOf2 : ParsedHttpSource() {
} }
val url = "/series/${query.substringAfter(SLUG_SEARCH_PREFIX)}" val url = "/series/${query.substringAfter(SLUG_SEARCH_PREFIX)}"
return fetchMangaDetails(SManga.create().apply { this.url = url }).map { return fetchMangaDetails(SManga.create().apply { this.url = url })
.map {
it.url = url it.url = url
MangasPage(listOf(it), false) MangasPage(listOf(it), false)
} }
} }
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
return GET("$baseUrl/series?search=$query", headers) GET("$baseUrl/series?search=$query", headers)
}
override fun searchMangaSelector() = popularMangaSelector() override fun searchMangaSelector() = popularMangaSelector()
@ -111,39 +95,33 @@ class APairOf2 : ParsedHttpSource() {
} }
} }
private fun String.parseStatus(): Int {
return when {
this.contains("ongoing", true) -> SManga.ONGOING
this.contains("complete", true) -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
}
// chapter list // chapter list
override fun chapterListSelector() = "div.chap" override fun chapterListSelector() = "div.chap"
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element) = SChapter.create().apply {
return SChapter.create().apply { element.selectFirst("a")!!.let {
element.select("a").let { a -> setUrlWithoutDomain(it.absUrl("href"))
url = a.attr("href").let { "/$it" } name = it.text()
name = a.text()
} }
date_upload = parseDate(element.select("div > div > span:nth-child(2)").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 // page list
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document) =
return document.select(".swiper-slide img").mapIndexed { index, img -> document.select(".swiper-slide img").mapIndexed { index, img ->
Page( Page(index, imageUrl = img.imgAttr())
index = index, }
imageUrl = img.imgAttr(),
) override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
private val statusOngoing = listOf("ongoing", "devam ediyor")
private val statusCompleted = listOf("complete", "tamamlandı", "bitti")
private fun String.parseStatus(): Int {
return when (this.lowercase()) {
in statusOngoing -> SManga.ONGOING
in statusCompleted -> SManga.COMPLETED
else -> SManga.UNKNOWN
} }
} }
@ -153,15 +131,11 @@ class APairOf2 : ParsedHttpSource() {
else -> dataImageAsUrl("src") else -> dataImageAsUrl("src")
} }
override fun imageUrlParse(document: Document): String { private fun parseDate(dateStr: String) =
throw UnsupportedOperationException() runCatching { dateFormat.parse(dateStr)!!.time }
} .getOrDefault(0L)
companion object { companion object {
private val DATE_FORMATTER by lazy {
SimpleDateFormat("dd MMMM, yy", Locale.ENGLISH)
}
const val SLUG_SEARCH_PREFIX = "slug:" const val SLUG_SEARCH_PREFIX = "slug:"
} }
} }

View File

@ -0,0 +1,24 @@
package eu.kanade.tachiyomi.multisrc.po2scans
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
class PO2ScansGenerator : ThemeSourceGenerator {
override val themePkg = "po2scans"
override val themeClass = "PO2Scans"
override val baseVersionCode = 1
override val sources = listOf(
SingleLang("A Pair Of 2+", "https://po2scans.com", "en", className = "APairOf2", overrideVersionCode = 31),
SingleLang("Sadscans", "https://sadscans.com", "tr"),
)
companion object {
@JvmStatic
fun main(args: Array<String>) {
PO2ScansGenerator().createAll()
}
}
}

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.extension.en.apairof2 package eu.kanade.tachiyomi.multisrc.po2scans
import android.app.Activity import android.app.Activity
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
@ -7,7 +7,7 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import kotlin.system.exitProcess import kotlin.system.exitProcess
class APairOf2UrlActivity : Activity() { class PO2ScansUrlActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments val pathSegments = intent?.data?.pathSegments
@ -15,17 +15,17 @@ class APairOf2UrlActivity : Activity() {
val slug = pathSegments[1] val slug = pathSegments[1]
val mainIntent = Intent().apply { val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH" action = "eu.kanade.tachiyomi.SEARCH"
putExtra("query", "${APairOf2.SLUG_SEARCH_PREFIX}$slug") putExtra("query", "${PO2Scans.SLUG_SEARCH_PREFIX}$slug")
putExtra("filter", packageName) putExtra("filter", packageName)
} }
try { try {
startActivity(mainIntent) startActivity(mainIntent)
} catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) {
Log.e("APairOf2UrlActivity", e.toString()) Log.e("PO2ScansUrlActivity", "Could not start activity", e)
} }
} else { } else {
Log.e("APairOf2UrlActivity", "could not parse uri from intent $intent") Log.e("PO2ScansUrlActivity", "could not parse URI from intent $intent")
} }
finish() finish()

View File

@ -1,11 +0,0 @@
ext {
extName = 'A Pair of 2+'
extClass = '.APairOf2'
extVersionCode = 31
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation project(':lib:dataimage')
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB