Rewrite NyaHentai Filter and add deeplink support (#7618)

* Rewrite NyaHentai Filter and add deeplink support

[Fix] tarUrl not initialized when using filter search

The tagUrl is not initialized and will never get initialized, resulting in the search page not been able to load more than one page. Fixed by rewrite the searchMangaRequest.

[Feat] Enable filter search for parody, character, artist, and group
[Feat] Enable sort by time or popularity search when not in a specific language.
[Feat] Add deeplink support

* Fix filter parody value

* Fix query search URL schema
This commit is contained in:
vulpes310 2021-06-14 04:16:06 -07:00 committed by GitHub
parent 7c1da6d671
commit 1c6acb0eb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 170 additions and 42 deletions

View File

@ -0,0 +1,22 @@
<?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="eu.kanade.tachiyomi.multisrc.nyahentai.NyaHentaiUrlActivity"
android:excludeFromRecents="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="*.${SOURCEHOST}"
android:pathPattern="/g/..*"
android:scheme="${SOURCESCHEME}" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.multisrc.nyahentai
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
@ -14,15 +15,19 @@ import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
abstract class NyaHentai (
abstract class NyaHentai(
override val name: String,
override val baseUrl: String,
override val lang: String
) : ParsedHttpSource() {
) : ParsedHttpSource() {
companion object {
const val TAG = "NyaHentai"
private const val NOT_FOUND_MESSAGE = "If you used a filter, check if the keyword actually exists."
private const val Filter_SEARCH_MESSAGE = "NOTE: Ignored if using text search!"
const val PREFIX_ID_SEARCH = "id:"
}
val nyaLang = when (lang) {
"en" -> "english"
"zh" -> "chinese"
@ -30,7 +35,7 @@ abstract class NyaHentai (
else -> ""
}
val languageUrl = when (nyaLang){
val languageUrl = when (nyaLang) {
"" -> baseUrl
else -> "$baseUrl/language/$nyaLang"
}
@ -149,10 +154,13 @@ abstract class NyaHentai (
throw UnsupportedOperationException("Not used")
override fun popularMangaRequest(page: Int): Request =
GET(when (nyaLang){
"" -> "$languageUrl/page/$page"
else -> "$languageUrl/popular/page/$page"
}, headers)
GET(
when (nyaLang) {
"" -> "$languageUrl/page/$page"
else -> "$languageUrl/popular/page/$page"
},
headers
)
override fun popularMangaFromElement(element: Element) = latestUpdatesFromElement(element)
@ -160,43 +168,55 @@ abstract class NyaHentai (
override fun popularMangaNextPageSelector() = latestUpdatesNextPageSelector()
private lateinit var tagUrl: String
// TODO: Additional filter options, specifically the type[] parameter
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
// todo: remove "english" from the search query in the future
var url = "$baseUrl/search/q_$query $nyaLang/page/$page"
if (query.isBlank()) {
filters.forEach { filter ->
when (filter) {
is Tag -> {
url = if (page == 1) {
"$baseUrl/tag/${filter.state}/$nyaLang" // "Contents" tag
} else {
"$tagUrl/page/$page"
}
}
if (query.isNotBlank()) {
// Normal search
return GET("$baseUrl/search/q_$query $nyaLang/page/$page", headers)
} else {
val type = filters.filterIsInstance<TypeFilter>()
.joinToString("") {
(it as UriPartFilter).toUriPart()
}
val keyword = filters.filterIsInstance<Text>().toString()
.replace("[", "").replace("]", "")
var sort = nyaLang
if (nyaLang == "") {
sort = filters.filterIsInstance<SortFilter>()
.joinToString("") {
(it as UriPartFilter).toUriPart()
}
}
val url = "$baseUrl/$type/$keyword/$sort/page/$page"
return GET(url, headers)
}
}
return GET(url, headers)
// For NyaHentaiUrlActivity
private fun searchMangaByIdRequest(id: String) = GET("$baseUrl/g/$id", headers)
private fun searchMangaByIdParse(response: Response, id: String): MangasPage {
val sManga = mangaDetailsParse(response)
sManga.url = "/g/$id/"
return MangasPage(listOf(sManga), false)
}
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return if (query.startsWith(PREFIX_ID_SEARCH)) {
val id = query.removePrefix(PREFIX_ID_SEARCH)
client.newCall(searchMangaByIdRequest(id))
.asObservableSuccess()
.map { response -> searchMangaByIdParse(response, id) }
} else {
super.fetchSearchManga(page, query, filters)
}
}
override fun searchMangaParse(response: Response): MangasPage {
return if (response.request.url.toString().contains("tag?")) {
response.asJsoup().select("table.table tbody tr a:first-of-type").attr("abs:href").let {
if (it.isNotEmpty()) {
tagUrl = it
super.searchMangaParse(client.newCall(GET(tagUrl, headers)).execute())
} else {
MangasPage(emptyList(), false)
}
}
} else {
super.searchMangaParse(response)
if (!response.isSuccessful) {
response.close()
throw Exception(NOT_FOUND_MESSAGE)
}
return super.searchMangaParse(response)
}
override fun searchMangaSelector() = latestUpdatesSelector()
@ -205,11 +225,55 @@ abstract class NyaHentai (
override fun searchMangaNextPageSelector() = latestUpdatesNextPageSelector()
override fun getFilterList() = FilterList(
Filter.Header("NOTE: Ignored if using text search!"),
Filter.Separator(),
Tag("Tag")
override fun getFilterList(): FilterList {
if (nyaLang == "") {
return FilterList(
Filter.Header(Filter_SEARCH_MESSAGE),
Filter.Separator(),
SortFilter(),
TypeFilter(),
Text("Keyword")
)
} else {
return FilterList(
Filter.Header(Filter_SEARCH_MESSAGE),
Filter.Separator(),
TypeFilter(),
Text("Keyword")
)
}
}
private open class UriPartFilter(
displayName: String,
val pair: Array<Pair<String, String>>,
defaultState: Int = 0
) : Filter.Select<String>(displayName, pair.map { it.first }.toTypedArray(), defaultState) {
open fun toUriPart() = pair[state].second
}
private class TypeFilter : UriPartFilter(
"Type",
arrayOf(
Pair("Tag", "tag"),
Pair("Parody", "parody"),
Pair("Character", "character"),
Pair("Artist", "artist"),
Pair("Group", "group")
)
)
private class Tag(name: String) : Filter.Text(name)
private class SortFilter : UriPartFilter(
"Sort by",
arrayOf(
Pair("Time", ""),
Pair("Popular", "popular"),
)
)
private class Text(name: String) : Filter.Text(name) {
override fun toString(): String {
return state
}
}
}

View File

@ -9,7 +9,7 @@ class NyaHentaiGenerator : ThemeSourceGenerator {
override val themeClass = "NyaHentai"
override val baseVersionCode: Int = 1
override val baseVersionCode: Int = 2
override val sources = listOf(
MultiLang("NyaHentai", "https://nyahentai.com", listOf("en","ja", "zh", "all"), isNsfw = true, className = "NyaHentaiFactory", overrideVersionCode = 3),

View File

@ -0,0 +1,42 @@
package eu.kanade.tachiyomi.multisrc.nyahentai
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.util.Log
import eu.kanade.tachiyomi.multisrc.nyahentai.NyaHentai
import kotlin.system.exitProcess
/**
* Springboard that accepts https://www.manhuagui.com/comic/xxx intents and redirects them to
* the main tachiyomi process. The idea is to not install the intent filter unless
* you have this extension installed, but still let the main tachiyomi app control
* things.
*/
class NyaHentaiUrlActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
if (pathSegments != null && pathSegments.size > 1) {
val titleid = pathSegments[1]
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH"
putExtra("query", "${NyaHentai.PREFIX_ID_SEARCH}$titleid")
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e("NyaHentaiUrlActivity", e.toString())
}
} else {
Log.e("NyaHentaiUrlActivity", "could not parse uri from intent $intent")
}
finish()
exitProcess(0)
}
}