Home Hero Scans: Add support for deeplinking + fix bug where webview wouldn't launch (#7003)

This commit is contained in:
h-hyuuga 2021-05-16 12:06:09 -04:00 committed by GitHub
parent 56d9d913f3
commit 3a90e498b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 123 additions and 9 deletions

View File

@ -1,2 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.kanade.tachiyomi.extension">
<application>
<activity
android:name=".en.homeheroscans.HomeHeroScansUrlActivity"
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="hhs.vercel.app"
android:pathPattern="/chapter"
android:scheme="https" />
</intent-filter>
<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="hhs.vercel.app"
android:pathPattern="/series"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -5,7 +5,7 @@ ext {
extName = 'Home Hero Scans'
pkgNameSuffix = "en.homeheroscans"
extClass = '.HomeHeroScans'
extVersionCode = 1
extVersionCode = 2
libVersion = '1.2'
}

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.extension.en.homeheroscans
import android.util.Log
import com.github.salomonbrys.kotson.forEach
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
@ -28,6 +27,23 @@ open class HomeHeroScans : HttpSource() {
// { seriesId |---> chapter |---> numPages }
private val chapterNumberCache: MutableMap<String, MutableMap<String, Int>> = mutableMapOf()
/**
* Given function f which returns an observable, returns a memoized version of f which'll
* cache values emitted by the observable. Future calls will return an observable that
* emits the cached values
*/
private fun <P, R> memoizeObservable(f: (arg: P) -> Observable<R>): (P) -> Observable<R> {
val cache = mutableMapOf<P, MutableList<R>>()
fun decorated(arg: P) = cache[arg]?.let { Observable.from(it) } ?: f(arg).map {
cache.getOrPut(arg, ::mutableListOf).add(it)
it
}
return ::decorated
}
val memoizedFetchPopularManga = memoizeObservable { page: Int -> super.fetchPopularManga(page) }
// reduce number of times we call their api, user can force a call to api by relaunching the app
override fun fetchPopularManga(page: Int) = memoizedFetchPopularManga(page)
override fun popularMangaRequest(page: Int) = GET("$baseUrl/series.json", headers)
override fun popularMangaParse(response: Response): MangasPage {
val res = JsonParser.parseString(response.body?.string()).asJsonObject
@ -56,13 +72,35 @@ open class HomeHeroScans : HttpSource() {
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used")
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException("Not used")
// search - site doesn't have a search, so just return the popular page
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = popularMangaRequest(page)
override fun searchMangaParse(response: Response) = popularMangaParse(response)
// search
private fun getMangaId(s: String): String? {
return s.toHttpUrlOrNull()?.let { url ->
// allow for trailing slash
if (url.pathSegments.size == 1 && url.pathSegments.last().isNotEmpty() || url.pathSegments.size == 2 && url.pathSegments.last().isEmpty())
return url.queryParameter("series")
return null
}
}
private fun fetchBySeriesId(id: String): Observable<List<SManga>> = fetchPopularManga(1).map { mp ->
mp.mangas.filter { "$baseUrl${it.url}".toHttpUrlOrNull()?.queryParameter("series") == id }
}
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
if (!query.startsWith(URL_SEARCH_PREFIX))
// site doesn't have a search, so just return the popular page
return fetchPopularManga(page)
return getMangaId(query.substringAfter(URL_SEARCH_PREFIX))?.let { id ->
fetchBySeriesId(id).map { MangasPage(it, false) }
} ?: Observable.just(MangasPage(emptyList(), false))
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException("Not used")
override fun searchMangaParse(response: Response) = throw UnsupportedOperationException("Not used")
// chapter list (is paginated),
override fun chapterListParse(response: Response): List<SChapter> {
Log.e("Genkan", "hello")
return JsonParser.parseString(response.body?.string()!!).asJsonObject["data"].asJsonArray.map {
val chapterData = it.asJsonObject["data"].asJsonObject
fun get(k: String) = chapterData[k].asString
@ -87,9 +125,18 @@ open class HomeHeroScans : HttpSource() {
"$baseUrl/api/chapters", headers, """{"series":"$series"}""".toRequestBody("text/plain;charset=utf-8".toMediaTypeOrNull())
)
}
override fun fetchMangaDetails(manga: SManga): Observable<SManga> = Observable.just(manga.apply { initialized = true }) // details already filled out by whatever originally fetched manga
override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
"$baseUrl${manga.url}".toHttpUrlOrNull()?.queryParameter("series")?.let { id ->
fetchBySeriesId(id).map {
(it.getOrNull(0) ?: manga).apply { initialized = true }
}
} ?: Observable.just(manga)
override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException("Not used")
override fun mangaDetailsRequest(manga: SManga) = throw UnsupportedOperationException("Not used")
// default implementation of mangaDetailsRequest has to exist for webview to work
// override fun mangaDetailsRequest(manga: SManga) = throw UnsupportedOperationException("Not used")
// Pages
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
val url = "$baseUrl${chapter.url}".toHttpUrlOrNull()!!
@ -116,4 +163,8 @@ open class HomeHeroScans : HttpSource() {
override fun pageListParse(response: Response) = throw UnsupportedOperationException("Not used")
override fun pageListRequest(chapter: SChapter) = throw UnsupportedOperationException("Not Used")
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used")
companion object {
const val URL_SEARCH_PREFIX = "url:"
}
}

View File

@ -0,0 +1,33 @@
package eu.kanade.tachiyomi.extension.en.homeheroscans
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 HomeHeroScansUrlActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
if (pathSegments?.size == 1) {
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH"
putExtra("query", "${HomeHeroScans.URL_SEARCH_PREFIX}${intent?.data?.toString()}")
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e("HomeHeroScansUrl", e.toString())
}
} else {
Log.e("HomeHeroScansUrl", "could not parse uri from intent $intent")
}
finish()
exitProcess(0)
}
}