Webtoons: Add Search By Url + Deeplinking (#7026)
This commit is contained in:
parent
759b614000
commit
640b5f035d
|
@ -0,0 +1,43 @@
|
||||||
|
<?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.webtoons.WebtoonsUrlActivity"
|
||||||
|
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="webtoons.com"
|
||||||
|
android:pathPattern="/.*/.*/.*/..*"
|
||||||
|
android:scheme="https" />
|
||||||
|
<data
|
||||||
|
android:host="www.webtoons.com"
|
||||||
|
android:pathPattern="/.*/.*/.*/..*"
|
||||||
|
android:scheme="https" />
|
||||||
|
<data
|
||||||
|
android:host="m.webtoons.com"
|
||||||
|
android:pathPattern="/.*/.*/.*/..*"
|
||||||
|
android:scheme="https" />
|
||||||
|
<data
|
||||||
|
android:host="webtoons.com"
|
||||||
|
android:pathPattern="/.*/.*/.*/.*/..*"
|
||||||
|
android:scheme="https" />
|
||||||
|
<data
|
||||||
|
android:host="www.webtoons.com"
|
||||||
|
android:pathPattern="/.*/.*/.*/.*/..*"
|
||||||
|
android:scheme="https" />
|
||||||
|
<data
|
||||||
|
android:host="m.webtoons.com"
|
||||||
|
android:pathPattern="/.*/.*/.*/.*/..*"
|
||||||
|
android:scheme="https" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -22,6 +22,7 @@ import okhttp3.Response
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
|
import rx.Observable
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
@ -129,6 +130,35 @@ open class Webtoons(
|
||||||
|
|
||||||
override fun latestUpdatesNextPageSelector(): String? = null
|
override fun latestUpdatesNextPageSelector(): String? = null
|
||||||
|
|
||||||
|
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||||
|
if (!query.startsWith(URL_SEARCH_PREFIX))
|
||||||
|
return super.fetchSearchManga(page, query, filters)
|
||||||
|
|
||||||
|
val emptyResult = Observable.just(MangasPage(emptyList(), false))
|
||||||
|
|
||||||
|
// given a url to either a webtoon or an episode, returns a url path to corresponding webtoon
|
||||||
|
fun webtoonPath(u: HttpUrl) = when {
|
||||||
|
langCode == u.pathSegments[0] -> "/${u.pathSegments[0]}/${u.pathSegments[1]}/${u.pathSegments[2]}/list"
|
||||||
|
else -> "/${u.pathSegments[0]}/${u.pathSegments[1]}/list" // dongmanmanhua doesn't include langCode
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.substringAfter(URL_SEARCH_PREFIX).toHttpUrlOrNull()?.let { url ->
|
||||||
|
val title_no = url.queryParameter("title_no")
|
||||||
|
val couldBeWebtoonOrEpisode = title_no != null && (url.pathSegments.size >= 3 && url.pathSegments.last().isNotEmpty())
|
||||||
|
val isThisLang = "$url".startsWith("$baseUrl/$langCode")
|
||||||
|
if (! (couldBeWebtoonOrEpisode && isThisLang))
|
||||||
|
emptyResult
|
||||||
|
else{
|
||||||
|
val potentialUrl = "${webtoonPath(url)}?title_no=$title_no"
|
||||||
|
fetchMangaDetails(SManga.create().apply { this.url = potentialUrl }).map {
|
||||||
|
it.url = potentialUrl
|
||||||
|
MangasPage(listOf(it), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: emptyResult
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
val url = "$baseUrl/$langCode/search?keyword=$query".toHttpUrlOrNull()?.newBuilder()!!
|
val url = "$baseUrl/$langCode/search?keyword=$query".toHttpUrlOrNull()?.newBuilder()!!
|
||||||
val uriPart = (filters.find { it is SearchType } as? SearchType)?.toUriPart() ?: ""
|
val uriPart = (filters.find { it is SearchType } as? SearchType)?.toUriPart() ?: ""
|
||||||
|
@ -156,6 +186,7 @@ open class Webtoons(
|
||||||
val infoElement = document.select("#_asideDetail")
|
val infoElement = document.select("#_asideDetail")
|
||||||
|
|
||||||
val manga = SManga.create()
|
val manga = SManga.create()
|
||||||
|
manga.title = document.selectFirst("h1.subj").text()
|
||||||
manga.author = detailElement.select(".author:nth-of-type(1)").first()?.ownText()
|
manga.author = detailElement.select(".author:nth-of-type(1)").first()?.ownText()
|
||||||
manga.artist = detailElement.select(".author:nth-of-type(2)").first()?.ownText() ?: manga.author
|
manga.artist = detailElement.select(".author:nth-of-type(2)").first()?.ownText() ?: manga.author
|
||||||
manga.genre = detailElement.select(".genre").joinToString(", ") { it.text() }
|
manga.genre = detailElement.select(".genre").joinToString(", ") { it.text() }
|
||||||
|
@ -241,4 +272,8 @@ open class Webtoons(
|
||||||
Page(i, "", motiontoonPath + motiontoonJson.getString(key))
|
Page(i, "", motiontoonPath + motiontoonJson.getString(key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val URL_SEARCH_PREFIX = "url:"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ class WebtoonsGenerator : ThemeSourceGenerator {
|
||||||
override val baseVersionCode: Int = 1
|
override val baseVersionCode: Int = 1
|
||||||
|
|
||||||
override val sources = listOf(
|
override val sources = listOf(
|
||||||
MultiLang("Webtoons.com", "https://www.webtoons.com", listOf("en", "fr", "es", "id", "th", "zh"), className = "WebtoonsFactory", pkgName = "webtoons", overrideVersionCode = 27),
|
MultiLang("Webtoons.com", "https://www.webtoons.com", listOf("en", "fr", "es", "id", "th", "zh"), className = "WebtoonsFactory", pkgName = "webtoons", overrideVersionCode = 28),
|
||||||
SingleLang("Dongman Manhua", "https://www.dongmanmanhua.cn", "zh")
|
SingleLang("Dongman Manhua", "https://www.dongmanmanhua.cn", "zh")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package eu.kanade.tachiyomi.multisrc.webtoons
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Springboard that accepts https://mangadex.com/title/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.
|
||||||
|
*
|
||||||
|
* Main goal was to make it easier to open manga in Tachiyomi in spite of the DDoS blocking
|
||||||
|
* the usual search screen from working.
|
||||||
|
*/
|
||||||
|
class WebtoonsUrlActivity : Activity() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val pathSegments = intent?.data?.pathSegments
|
||||||
|
val title_no = intent?.data?.getQueryParameter("title_no")
|
||||||
|
if (pathSegments != null && pathSegments.size >= 3 && title_no != null) {
|
||||||
|
val mainIntent = Intent().apply {
|
||||||
|
action = "eu.kanade.tachiyomi.SEARCH"
|
||||||
|
putExtra("query", "${Webtoons.URL_SEARCH_PREFIX}${intent?.data?.toString()}")
|
||||||
|
putExtra("filter", packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
startActivity(mainIntent)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
Log.e("WebtoonsUrlActivity", e.toString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e("WebtoonsUrlActivity", "could not parse uri from intent $intent")
|
||||||
|
}
|
||||||
|
|
||||||
|
finish()
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue