Tsumino is now supported by the new tag display, you may have to refresh your Tsumino manga for them to work properly. Also Set the groundwork for more special features

This commit is contained in:
Jobobby04 2020-07-16 22:34:50 -04:00
parent 9e986bbeb6
commit a8f2f03562
6 changed files with 77 additions and 24 deletions

View File

@ -4,6 +4,7 @@ import android.content.Context
import android.net.Uri
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.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource
@ -14,6 +15,8 @@ import exh.metadata.metadata.TsuminoSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource
import exh.util.dropBlank
import exh.util.trimAll
import exh.util.urlImportFetchSearchManga
import java.text.SimpleDateFormat
import java.util.Locale
@ -28,13 +31,13 @@ class Tsumino(delegate: HttpSource, val context: Context) :
override val lang = "en"
// Support direct URL importing
override fun fetchSearchManga(page: Int, query: String, filters: FilterList) =
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
urlImportFetchSearchManga(context, query) {
super.fetchSearchManga(page, query, filters)
}
override fun mapUrlToMangaUrl(uri: Uri): String? {
val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase() ?: return null
val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase(Locale.ROOT) ?: return null
if (lcFirstPathSegment != "read" && lcFirstPathSegment != "book" && lcFirstPathSegment != "entry") {
return null
}
@ -58,9 +61,12 @@ class Tsumino(delegate: HttpSource, val context: Context) :
title = it.trim()
}
input.getElementById("Artist")?.children()?.first()?.text()?.trim()?.let {
tags.add(RaisedTag("artist", it, TAG_TYPE_VIRTUAL))
artist = it
input.getElementById("Artist")?.children()?.first()?.text()?.trim()?.let { artistString ->
artistString.split("|").trimAll().dropBlank().forEach {
tags.add(RaisedTag("artist", it, TAG_TYPE_DEFAULT))
}
tags.add(RaisedTag("artist", artistString, TAG_TYPE_VIRTUAL))
artist = artistString
}
input.getElementById("Uploader")?.children()?.first()?.text()?.trim()?.let {
@ -86,18 +92,19 @@ class Tsumino(delegate: HttpSource, val context: Context) :
input.getElementById("Collection")?.children()?.first()?.text()?.let {
collection = it.trim()
tags.add(RaisedTag("collection", it, TAG_TYPE_DEFAULT))
}
input.getElementById("Group")?.children()?.first()?.text()?.let {
group = it.trim()
tags.add(RaisedTag("group", it, TAG_TYPE_VIRTUAL))
tags.add(RaisedTag("group", it, TAG_TYPE_DEFAULT))
}
val newParody = mutableListOf<String>()
input.getElementById("Parody")?.children()?.forEach {
val entry = it.text().trim()
newParody.add(entry)
tags.add(RaisedTag("parody", entry, TAG_TYPE_VIRTUAL))
tags.add(RaisedTag("parody", entry, TAG_TYPE_DEFAULT))
}
parody = newParody
@ -105,14 +112,14 @@ class Tsumino(delegate: HttpSource, val context: Context) :
input.getElementById("Character")?.children()?.forEach {
val entry = it.text().trim()
newCharacter.add(entry)
tags.add(RaisedTag("character", entry, TAG_TYPE_VIRTUAL))
tags.add(RaisedTag("character", entry, TAG_TYPE_DEFAULT))
}
character = newCharacter
input.getElementById("Tag")?.children()?.let {
input.getElementById("Tag")?.children()?.let { tagElements ->
tags.addAll(
it.map {
RaisedTag(null, it.text().trim(), TAG_TYPE_DEFAULT)
tagElements.map {
RaisedTag("tags", it.text().trim(), TAG_TYPE_DEFAULT)
}
)
}
@ -126,6 +133,5 @@ class Tsumino(delegate: HttpSource, val context: Context) :
companion object {
val TM_DATE_FORMAT = SimpleDateFormat("yyyy MMM dd", Locale.US)
private val ASP_NET_COOKIE_NAME = "ASP.NET_SessionId"
}
}

View File

@ -37,6 +37,7 @@ import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.ui.base.controller.FabController
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
@ -69,6 +70,9 @@ import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.shrinkOnScroll
import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.util.view.visible
import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.source.EnhancedHttpSource
import java.io.IOException
import kotlinx.android.synthetic.main.main_activity.root_coordinator
import kotlinx.coroutines.CancellationException
@ -419,6 +423,16 @@ class MangaController :
// Manga info - start
// SY -->
fun onNextMetaInfo(flatMetadata: FlatMetadata) {
presenter.meta = if (presenter.source is LewdSource<*, *>) {
flatMetadata.raise((presenter.source as LewdSource<*, *>).metaClass)
} else if (presenter.source is EnhancedHttpSource && (presenter.source as EnhancedHttpSource).enhancedSource is LewdSource<*, *>) {
flatMetadata.raise(((presenter.source as EnhancedHttpSource).enhancedSource as LewdSource<*, *>).metaClass)
} else null
}
// SY <--
/**
* Check if manga is initialized.
* If true update header with manga information,
@ -427,10 +441,10 @@ class MangaController :
* @param manga manga object containing information about manga.
* @param source the source of the manga.
*/
fun onNextMangaInfo(manga: Manga, source: Source) {
fun onNextMangaInfo(manga: Manga, source: Source /* SY --> */, meta: RaisedSearchMetadata? /* SY <-- */) {
if (manga.initialized) {
// Update view.
mangaInfoAdapter?.update(manga, source)
mangaInfoAdapter?.update(manga, source /* SY --> */, meta /* SY <-- */)
} else {
// Initialize manga.
fetchMangaInfoFromSource()

View File

@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
@ -33,6 +34,10 @@ import exh.MERGED_SOURCE_ID
import exh.debug.DebugToggles
import exh.eh.EHentaiUpdateHelper
import exh.isEhBasedSource
import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.source.EnhancedHttpSource
import exh.util.await
import exh.util.trimOrNull
import java.util.Date
@ -101,15 +106,23 @@ class MangaPresenter(
private val redirectUserRelay = BehaviorRelay.create<EXHRedirect>()
data class EXHRedirect(val manga: Manga, val update: Boolean)
var meta: RaisedSearchMetadata? = null
// EXH <--
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
// SY -->
if (manga.initialized && (source is LewdSource<*, *> || (source is EnhancedHttpSource && source.enhancedSource is LewdSource<*, *>))) {
getMangaMetaObservable().subscribeLatestCache({ view, flatMetadata -> if (flatMetadata != null) view.onNextMetaInfo(flatMetadata) else Timber.d("Invalid metadata") })
}
// SY <--
// Manga info - start
getMangaObservable()
.subscribeLatestCache({ view, manga -> view.onNextMangaInfo(manga, source) })
.subscribeLatestCache({ view, manga -> view.onNextMangaInfo(manga, source /* SY --> */, meta/* SY <-- */) })
// Prepare the relay.
chaptersRelay.flatMap { applyChapterFilters(it) }
@ -177,6 +190,16 @@ class MangaPresenter(
.observeOn(AndroidSchedulers.mainThread())
}
// SY -->
private fun getMangaMetaObservable(): Observable<FlatMetadata?> {
val mangaId = manga.id
return if (mangaId != null) {
db.getFlatMetadataForManga(mangaId).asRxObservable()
.observeOn(AndroidSchedulers.mainThread())
} else Observable.just(null)
}
// SY <--
/**
* Fetch manga information from source.
*/
@ -190,6 +213,13 @@ class MangaPresenter(
db.insertManga(manga).executeAsBlocking()
manga
}
// SY -->
.doOnNext {
if (source is LewdSource<*, *> || (source is EnhancedHttpSource && source.enhancedSource is LewdSource<*, *>)) {
getMangaMetaObservable().subscribeLatestCache({ view, flatMetadata -> if (flatMetadata != null) view.onNextMetaInfo(flatMetadata) else Timber.d("Invalid metadata") })
}
}
// SY <--
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
@ -259,7 +289,7 @@ class MangaPresenter(
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(
{ view, _ ->
view.onNextMangaInfo(manga, source)
view.onNextMangaInfo(manga, source /* SY --> */, meta/* SY <-- */)
}
)
}

View File

@ -30,12 +30,9 @@ import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.setTooltip
import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.util.view.visibleIf
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.MERGED_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PURURIN_SOURCE_ID
import exh.isNamespaceSource
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.util.SourceTagsUtil
import exh.util.makeSearchChip
import exh.util.setChipsExtended
@ -57,6 +54,7 @@ class MangaInfoHeaderAdapter(
private var manga: Manga = controller.presenter.manga
private var source: Source = controller.presenter.source
private var meta: RaisedSearchMetadata? = controller.presenter.meta
private val scope = CoroutineScope(Job() + Dispatchers.Main)
private lateinit var binding: MangaInfoHeaderBinding
@ -83,9 +81,10 @@ class MangaInfoHeaderAdapter(
* @param manga manga object containing information about manga.
* @param source the source of the manga.
*/
fun update(manga: Manga, source: Source) {
fun update(manga: Manga, source: Source, meta: RaisedSearchMetadata?) {
this.manga = manga
this.source = source
this.meta = meta
notifyDataSetChanged()
}
@ -336,7 +335,7 @@ class MangaInfoHeaderAdapter(
// Update genres list
if (!manga.genre.isNullOrBlank()) {
// SY -->
if (source != null && (source.id == EH_SOURCE_ID || source.id == EXH_SOURCE_ID || source.id == NHENTAI_SOURCE_ID || source.id == HITOMI_SOURCE_ID || source.id == PURURIN_SOURCE_ID)) {
if (source != null && source.isNamespaceSource()) {
val genre = manga.getGenres()
if (!genre.isNullOrEmpty()) {
val namespaceTags = genre.map { SourceTagsUtil().parseTag(it) }
@ -414,7 +413,7 @@ class MangaInfoHeaderAdapter(
binding.mangaGenresTagsCompact.visibleIf { isExpanded }
// SY -->
if (source.id == EH_SOURCE_ID || source.id == EXH_SOURCE_ID || source.id == NHENTAI_SOURCE_ID || source.id == HITOMI_SOURCE_ID || source.id == PURURIN_SOURCE_ID) {
if (source.isNamespaceSource()) {
binding.mangaNamespaceTagsRecycler.visibleIf { !isExpanded }
} else {
binding.mangaGenresTagsFullChips.visibleIf { !isExpanded }

View File

@ -57,3 +57,5 @@ fun isLewdSource(source: Long) = source in 6900..6999 ||
lewdDelegatedSourceIds.binarySearch(source) >= 0
fun Source.isEhBasedSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID
fun Source.isNamespaceSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID || id == NHENTAI_SOURCE_ID || id == HITOMI_SOURCE_ID || id == PURURIN_SOURCE_ID || id == TSUMINO_SOURCE_ID

View File

@ -5,6 +5,7 @@ import exh.EXH_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PURURIN_SOURCE_ID
import exh.TSUMINO_SOURCE_ID
class SourceTagsUtil {
fun getWrappedTag(sourceId: Long, namespace: String? = null, tag: String? = null, fullTag: String? = null): String? {
@ -15,6 +16,7 @@ class SourceTagsUtil {
HITOMI_SOURCE_ID -> wrapTagHitomi(parsed.first, parsed.second.substringBefore('|').trim())
NHENTAI_SOURCE_ID -> wrapTagNHentai(parsed.first, parsed.second.substringBefore('|').trim())
PURURIN_SOURCE_ID -> parsed.second.substringBefore('|').trim()
TSUMINO_SOURCE_ID -> parsed.second.substringBefore('|').trim()
else -> wrapTag(parsed.first, parsed.second.substringBefore('|').trim())
}
} else null