Add deeplinking to MangaPlus and Tsuki. (#7597)

This commit is contained in:
Alessandro Jean 2021-06-10 16:22:18 -03:00 committed by GitHub
parent 6fa8cfe804
commit fcb929f4e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 185 additions and 18 deletions

View File

@ -1,2 +1,27 @@
<?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=".all.mangaplus.MangaPlusUrlActivity"
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="mangaplus.shueisha.co.jp"
android:pathPattern="/titles/..*"
android:scheme="https" />
<data
android:host="www.mangaplus.shueisha.co.jp"
android:pathPattern="/titles/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -6,7 +6,7 @@ ext {
extName = 'MANGA Plus by SHUEISHA'
pkgNameSuffix = 'all.mangaplus'
extClass = '.MangaPlusFactory'
extVersionCode = 19
extVersionCode = 20
libVersion = '1.2'
}

View File

@ -172,10 +172,21 @@ abstract class MangaPlus(
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return super.fetchSearchManga(page, query, filters)
.map { MangasPage(it.mangas.filter { m -> m.title.contains(query, true) }, it.hasNextPage) }
.map {
if (it.mangas.size == 1) {
return@map it
}
val filteredResult = it.mangas.filter { m -> m.title.contains(query, true) }
MangasPage(filteredResult, it.hasNextPage)
}
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.startsWith(PREFIX_ID_SEARCH) && query.matches(ID_SEARCH_PATTERN)) {
return titleDetailsRequest(query.removePrefix(PREFIX_ID_SEARCH))
}
val newHeaders = headersBuilder()
.set("Referer", "$baseUrl/manga_list/all")
.build()
@ -189,6 +200,25 @@ abstract class MangaPlus(
if (result.success == null)
throw Exception(result.error!!.langPopup.body)
if (result.success.titleDetailView != null) {
val mangaPlusTitle = result.success.titleDetailView.title.let {
val correctLanguage = titlesToFix[it.titleId]
if (correctLanguage != null) it.copy(language = correctLanguage) else it
}
if (mangaPlusTitle.language == langCode) {
val manga = SManga.create().apply {
title = mangaPlusTitle.name
thumbnail_url = mangaPlusTitle.portraitImageUrl
url = "#/titles/${mangaPlusTitle.titleId}"
}
return MangasPage(listOf(manga), hasNextPage = false)
}
return MangasPage(emptyList(), hasNextPage = false)
}
titleList = result.success.allTitlesView!!.titles
.fixWrongLanguages()
.filter { it.language == langCode }
@ -201,11 +231,11 @@ abstract class MangaPlus(
}
}
return MangasPage(mangas, false)
return MangasPage(mangas, hasNextPage = false)
}
private fun titleDetailsRequest(manga: SManga): Request {
val titleId = manga.url.substringAfterLast("/")
private fun titleDetailsRequest(mangaUrl: String): Request {
val titleId = mangaUrl.substringAfterLast("/")
val newHeaders = headersBuilder()
.set("Referer", "$baseUrl/titles/$titleId")
@ -216,7 +246,7 @@ abstract class MangaPlus(
// Workaround to allow "Open in browser" use the real URL.
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(titleDetailsRequest(manga))
return client.newCall(titleDetailsRequest(manga.url))
.asObservableSuccess()
.map { response ->
mangaDetailsParse(response).apply { initialized = true }
@ -247,7 +277,7 @@ abstract class MangaPlus(
}
}
override fun chapterListRequest(manga: SManga): Request = titleDetailsRequest(manga)
override fun chapterListRequest(manga: SManga): Request = titleDetailsRequest(manga.url)
override fun chapterListParse(response: Response): List<SChapter> {
val result = response.asProto()
@ -474,5 +504,8 @@ abstract class MangaPlus(
private val COMPLETE_REGEX = "completado|complete".toRegex()
private const val TITLE_THUMBNAIL_PATH = "title_thumbnail_portrait_list"
const val PREFIX_ID_SEARCH = "id:"
private val ID_SEARCH_PATTERN = "^id:(\\d+)$".toRegex()
}
}

View File

@ -0,0 +1,37 @@
package eu.kanade.tachiyomi.extension.all.mangaplus
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 MangaPlusUrlActivity : 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", MangaPlus.PREFIX_ID_SEARCH + titleId)
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e("MangaPlusUrlActivity", e.toString())
}
} else {
Log.e("MangaPlusUrlActivity", "Could not parse URI from intent $intent")
}
finish()
exitProcess(0)
}
}

View File

@ -1,2 +1,27 @@
<?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=".pt.tsukimangas.TsukiMangasUrlActivity"
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="tsukimangas.com"
android:pathPattern="/obra/..*/..*"
android:scheme="https" />
<data
android:host="www.tsukimangas.com"
android:pathPattern="/obra/..*/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -6,7 +6,7 @@ ext {
extName = 'Tsuki Mangás'
pkgNameSuffix = 'pt.tsukimangas'
extClass = '.TsukiMangas'
extVersionCode = 18
extVersionCode = 19
libVersion = '1.2'
containsNsfw = true
}

View File

@ -98,6 +98,10 @@ class TsukiMangas : HttpSource() {
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.startsWith(PREFIX_ID_SEARCH) && query.matches(ID_SEARCH_PATTERN)) {
return mangaDetailsApiRequest(query.removePrefix(PREFIX_ID_SEARCH))
}
val newHeaders = headersBuilder()
.set("Referer", "$baseUrl/lista-completa")
.build()
@ -156,6 +160,12 @@ class TsukiMangas : HttpSource() {
}
override fun searchMangaParse(response: Response): MangasPage {
if (response.request.url.toString().contains("/mangas/")) {
val manga = mangaDetailsParse(response)
return MangasPage(listOf(manga), hasNextPage = false)
}
val result = json.decodeFromString<TsukiPaginatedDto>(response.body!!.string())
val searchResults = result.data.map(::searchMangaItemParse)
@ -175,21 +185,17 @@ class TsukiMangas : HttpSource() {
// Workaround to allow "Open in browser" use the real URL.
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsApiRequest(manga))
return client.newCall(mangaDetailsApiRequest(manga.url))
.asObservableSuccess()
.map { response ->
mangaDetailsParse(response).apply { initialized = true }
}
}
private fun mangaDetailsApiRequest(manga: SManga): Request {
val newHeaders = headersBuilder()
.set("Referer", baseUrl + manga.url)
.build()
private fun mangaDetailsApiRequest(mangaUrl: String): Request {
val mangaId = mangaUrl.substringAfter("obra/").substringBefore("/")
val mangaId = manga.url.substringAfter("obra/").substringBefore("/")
return GET("$baseUrl/api/v2/mangas/$mangaId", newHeaders)
return GET("$baseUrl/api/v2/mangas/$mangaId", headers)
}
override fun mangaDetailsRequest(manga: SManga): Request {
@ -211,6 +217,7 @@ class TsukiMangas : HttpSource() {
author = mangaDto.author.orEmpty()
artist = mangaDto.artist.orEmpty()
genre = mangaDto.genres.joinToString { it.genre }
url = "/obra/${mangaDto.id}/${mangaDto.url}"
}
override fun chapterListRequest(manga: SManga): Request {
@ -460,5 +467,8 @@ class TsukiMangas : HttpSource() {
private const val EMPTY_COVER = "/ext/errorcapa.jpg"
private val DATE_FORMATTER by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) }
const val PREFIX_ID_SEARCH = "id:"
private val ID_SEARCH_PATTERN = "^id:(\\d+)$".toRegex()
}
}

View File

@ -0,0 +1,37 @@
package eu.kanade.tachiyomi.extension.pt.tsukimangas
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 TsukiMangasUrlActivity : 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", TsukiMangas.PREFIX_ID_SEARCH + titleId)
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e("TsukiMangasUrlActivity", e.toString())
}
} else {
Log.e("TsukiMangasUrlActivity", "Could not parse URI from intent $intent")
}
finish()
exitProcess(0)
}
}