Panda Chaika: Add Character Filter, Fix Sort Filters, Add ID search (#4049)
* Add Character Filter, ID search * Fix Sort Filters * Apply Suggestions * * Apply Suggestion * typo fix
This commit is contained in:
parent
2ef807ca07
commit
734c7a1e85
23
src/all/pandachaika/AndroidManifest.xml
Normal file
23
src/all/pandachaika/AndroidManifest.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name=".all.pandachaika.PandaChaikaUrlActivity"
|
||||
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="panda.chaika.moe"
|
||||
android:pathPattern="/archive/..*"
|
||||
android:scheme="https"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'PandaChaika'
|
||||
extClass = '.PandaChaikaFactory'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
package eu.kanade.tachiyomi.extension.all.pandachaika
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservable
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
@ -42,6 +44,9 @@ class PandaChaika(
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val fakkuRegex = Regex("""(?:https?://)?(?:www\.)?fakku\.net/hentai/""")
|
||||
private val ehentaiRegex = Regex("""(?:https?://)?e-hentai\.org/g/""")
|
||||
|
||||
// Popular
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
return GET("$baseSearchUrl/?tags=$searchLang&sort=rating&apply=&json=&page=$page", headers)
|
||||
@ -73,6 +78,76 @@ class PandaChaika(
|
||||
}
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
return when {
|
||||
query.startsWith(PREFIX_ID_SEARCH) -> {
|
||||
val id = query.removePrefix(PREFIX_ID_SEARCH).toInt()
|
||||
client.newCall(GET("$baseUrl/api?archive=$id", headers))
|
||||
.asObservable()
|
||||
.map { response ->
|
||||
searchMangaByIdParse(response, id)
|
||||
}
|
||||
}
|
||||
query.startsWith(PREFIX_EHEN_ID_SEARCH) -> {
|
||||
val id = query.removePrefix(PREFIX_EHEN_ID_SEARCH).replace(ehentaiRegex, "")
|
||||
val baseLink = "https://e-hentai.org/g/"
|
||||
val fullLink = baseSearchUrl.toHttpUrl().newBuilder().apply {
|
||||
addQueryParameter("qsearch", baseLink + id)
|
||||
addQueryParameter("json", "")
|
||||
}.build()
|
||||
client.newCall(GET(fullLink, headers))
|
||||
.asObservableSuccess()
|
||||
.map {
|
||||
val archive = it.parseAs<ArchiveResponse>().archives.getOrNull(0)?.toSManga() ?: throw Exception("Not Found")
|
||||
MangasPage(listOf(archive), false)
|
||||
}
|
||||
}
|
||||
query.startsWith(PREFIX_FAK_ID_SEARCH) -> {
|
||||
val slug = query.removePrefix(PREFIX_FAK_ID_SEARCH).replace(fakkuRegex, "")
|
||||
val baseLink = "https://www.fakku.net/hentai/"
|
||||
val fullLink = baseSearchUrl.toHttpUrl().newBuilder().apply {
|
||||
addQueryParameter("qsearch", baseLink + slug)
|
||||
addQueryParameter("json", "")
|
||||
}.build()
|
||||
client.newCall(GET(fullLink, headers))
|
||||
.asObservableSuccess()
|
||||
.map {
|
||||
val archive = it.parseAs<ArchiveResponse>().archives.getOrNull(0)?.toSManga() ?: throw Exception("Not Found")
|
||||
MangasPage(listOf(archive), false)
|
||||
}
|
||||
}
|
||||
query.startsWith(PREFIX_SOURCE_SEARCH) -> {
|
||||
val url = query.removePrefix(PREFIX_SOURCE_SEARCH)
|
||||
client.newCall(GET("$baseSearchUrl/?qsearch=$url&json=", headers))
|
||||
.asObservableSuccess()
|
||||
.map {
|
||||
val archive = it.parseAs<ArchiveResponse>().archives.getOrNull(0)?.toSManga() ?: throw Exception("Not Found")
|
||||
MangasPage(listOf(archive), false)
|
||||
}
|
||||
}
|
||||
|
||||
else -> super.fetchSearchManga(page, query, filters)
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchMangaByIdParse(response: Response, id: Int = 0): MangasPage {
|
||||
val title = response.parseAs<Archive>().title
|
||||
val fullLink = baseSearchUrl.toHttpUrl().newBuilder().apply {
|
||||
addQueryParameter("qsearch", title)
|
||||
addQueryParameter("json", "")
|
||||
}.build()
|
||||
val archive = client.newCall(GET(fullLink, headers))
|
||||
.execute()
|
||||
.parseAs<ArchiveResponse>().archives
|
||||
.find {
|
||||
it.id == id
|
||||
}
|
||||
?.toSManga()
|
||||
?: throw Exception("Invalid ID")
|
||||
|
||||
return MangasPage(listOf(archive), false)
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val library = response.parseAs<ArchiveResponse>()
|
||||
|
||||
@ -250,4 +325,11 @@ class PandaChaika(
|
||||
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
|
||||
override fun pageListParse(response: Response): List<Page> = throw UnsupportedOperationException()
|
||||
override fun mangaDetailsParse(response: Response): SManga = throw UnsupportedOperationException()
|
||||
|
||||
companion object {
|
||||
const val PREFIX_ID_SEARCH = "id:"
|
||||
const val PREFIX_FAK_ID_SEARCH = "fakku:"
|
||||
const val PREFIX_EHEN_ID_SEARCH = "ehentai:"
|
||||
const val PREFIX_SOURCE_SEARCH = "source:"
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
val dateReformat = SimpleDateFormat("EEEE, d MMM yyyy HH:mm (z)", Locale.ENGLISH)
|
||||
fun filterTags(include: String = "", exclude: List<String> = emptyList(), tags: List<String>): String {
|
||||
fun filterTags(include: String = "", exclude: List<String> = emptyList(), tags: List<String>): String? {
|
||||
return tags.filter { it.startsWith("$include:") && exclude.none { substring -> it.startsWith("$substring:") } }
|
||||
.joinToString {
|
||||
it.substringAfter(":").replace("_", " ").split(" ").joinToString(" ") { s ->
|
||||
@ -16,13 +16,13 @@ fun filterTags(include: String = "", exclude: List<String> = emptyList(), tags:
|
||||
if (sr.isLowerCase()) sr.titlecase(Locale.getDefault()) else sr.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}.takeIf { it.isNotBlank() }
|
||||
}
|
||||
fun getReadableSize(bytes: Double): String {
|
||||
return when {
|
||||
bytes >= 300 * 1024 * 1024 -> "${"%.2f".format(bytes / (1024.0 * 1024.0 * 1024.0))} GB"
|
||||
bytes >= 100 * 1024 -> "${"%.2f".format(bytes / (1024.0 * 1024.0))} MB"
|
||||
bytes >= 1024 -> "${"%.2f".format(bytes / (1024.0))} KB"
|
||||
bytes >= 300 * 1000 * 1000 -> "${"%.2f".format(bytes / (1000.0 * 1000.0 * 1000.0))} GB"
|
||||
bytes >= 100 * 1000 -> "${"%.2f".format(bytes / (1000.0 * 1000.0))} MB"
|
||||
bytes >= 1000 -> "${"%.2f".format(bytes / (1000.0))} kB"
|
||||
else -> "$bytes B"
|
||||
}
|
||||
}
|
||||
@ -31,13 +31,14 @@ fun getReadableSize(bytes: Double): String {
|
||||
class Archive(
|
||||
val download: String,
|
||||
val posted: Long,
|
||||
val title: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class LongArchive(
|
||||
private val thumbnail: String,
|
||||
private val title: String,
|
||||
private val id: Int,
|
||||
val id: Int,
|
||||
private val posted: Long?,
|
||||
private val public_date: Long?,
|
||||
private val filecount: Int,
|
||||
@ -50,35 +51,47 @@ class LongArchive(
|
||||
val groups = filterTags("group", tags = tags)
|
||||
val artists = filterTags("artist", tags = tags)
|
||||
val publishers = filterTags("publisher", tags = tags)
|
||||
val characters = filterTags("character", tags = tags)
|
||||
val male = filterTags("male", tags = tags)
|
||||
val female = filterTags("female", tags = tags)
|
||||
val others = filterTags(exclude = listOf("female", "male", "artist", "publisher", "group", "parody"), tags = tags)
|
||||
val parodies = filterTags("parody", tags = tags)
|
||||
var appended = false
|
||||
|
||||
url = id.toString()
|
||||
title = this@LongArchive.title
|
||||
thumbnail_url = thumbnail
|
||||
author = groups.ifEmpty { artists }
|
||||
author = groups ?: artists
|
||||
artist = artists
|
||||
genre = listOf(male, female, others).joinToString()
|
||||
description = buildString {
|
||||
append("Uploader: ", uploader.ifEmpty { "Anonymous" }, "\n")
|
||||
publishers.takeIf { it.isNotBlank() }?.let {
|
||||
append("Publishers: ", it, "\n\n")
|
||||
publishers?.let {
|
||||
append("Publishers: ", it, "\n")
|
||||
}
|
||||
parodies.takeIf { it.isNotBlank() }?.let {
|
||||
append("Parodies: ", it, "\n\n")
|
||||
append("\n")
|
||||
|
||||
parodies?.let {
|
||||
append("Parodies: ", it, "\n")
|
||||
appended = true
|
||||
}
|
||||
male.takeIf { it.isNotBlank() }?.let {
|
||||
characters?.let {
|
||||
append("Characters: ", it, "\n")
|
||||
appended = true
|
||||
}
|
||||
if (appended) append("\n")
|
||||
|
||||
male?.let {
|
||||
append("Male tags: ", it, "\n\n")
|
||||
}
|
||||
female.takeIf { it.isNotBlank() }?.let {
|
||||
female?.let {
|
||||
append("Female tags: ", it, "\n\n")
|
||||
}
|
||||
others.takeIf { it.isNotBlank() }?.let {
|
||||
others?.let {
|
||||
append("Other tags: ", it, "\n\n")
|
||||
}
|
||||
|
||||
title_jpn?.let { append("Japanese Title: ", it, "\n") }
|
||||
title_jpn?.takeIf { it.isNotEmpty() }?.let { append("Japanese Title: ", it, "\n") }
|
||||
append("Pages: ", filecount, "\n")
|
||||
append("File Size: ", getReadableSize(filesize), "\n")
|
||||
|
||||
|
@ -17,6 +17,7 @@ fun getFilters(): FilterList {
|
||||
TextFilter("Female Tags", "female"),
|
||||
TextFilter("Artists", "artist"),
|
||||
TextFilter("Parodies", "parody"),
|
||||
TextFilter("Characters", "character"),
|
||||
Filter.Separator(),
|
||||
TextFilter("Reason", "reason"),
|
||||
TextFilter("Uploader", "reason"),
|
||||
@ -52,11 +53,11 @@ private val getTypes = listOf(
|
||||
|
||||
private val getSortsList: List<Pair<String, String>> = listOf(
|
||||
Pair("Public Date", "public_date"),
|
||||
Pair("Posted Date", "posted_date"),
|
||||
Pair("Posted Date", "posted"),
|
||||
Pair("Title", "title"),
|
||||
Pair("Japanese Title", "title_jpn"),
|
||||
Pair("Rating", "rating"),
|
||||
Pair("Images", "images"),
|
||||
Pair("File Size", "size"),
|
||||
Pair("Images", "filecount"),
|
||||
Pair("File Size", "filesize"),
|
||||
Pair("Category", "category"),
|
||||
)
|
||||
|
@ -0,0 +1,34 @@
|
||||
package eu.kanade.tachiyomi.extension.all.pandachaika
|
||||
|
||||
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 PandaChaikaUrlActivity : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val pathSegments = intent?.data?.pathSegments
|
||||
if (pathSegments != null && pathSegments.size > 2) {
|
||||
val id = "${pathSegments[1]}/${pathSegments[2]}"
|
||||
val mainIntent = Intent().apply {
|
||||
action = "eu.kanade.tachiyomi.SEARCH"
|
||||
putExtra("query", "${PandaChaika.PREFIX_ID_SEARCH}$id")
|
||||
putExtra("filter", packageName)
|
||||
}
|
||||
|
||||
try {
|
||||
startActivity(mainIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e("KoharuUrlActivity", "Could not start activity", e)
|
||||
}
|
||||
} else {
|
||||
Log.e("KoharuUrlActivity", "Could not parse URI from intent $intent")
|
||||
}
|
||||
|
||||
finish()
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user