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 android.net.Uri
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.FilterList 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.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource 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.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.util.dropBlank
import exh.util.trimAll
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -28,13 +31,13 @@ class Tsumino(delegate: HttpSource, val context: Context) :
override val lang = "en" override val lang = "en"
// Support direct URL importing // 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) { urlImportFetchSearchManga(context, query) {
super.fetchSearchManga(page, query, filters) super.fetchSearchManga(page, query, filters)
} }
override fun mapUrlToMangaUrl(uri: Uri): String? { 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") { if (lcFirstPathSegment != "read" && lcFirstPathSegment != "book" && lcFirstPathSegment != "entry") {
return null return null
} }
@ -58,9 +61,12 @@ class Tsumino(delegate: HttpSource, val context: Context) :
title = it.trim() title = it.trim()
} }
input.getElementById("Artist")?.children()?.first()?.text()?.trim()?.let { input.getElementById("Artist")?.children()?.first()?.text()?.trim()?.let { artistString ->
tags.add(RaisedTag("artist", it, TAG_TYPE_VIRTUAL)) artistString.split("|").trimAll().dropBlank().forEach {
artist = it 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 { 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 { input.getElementById("Collection")?.children()?.first()?.text()?.let {
collection = it.trim() collection = it.trim()
tags.add(RaisedTag("collection", it, TAG_TYPE_DEFAULT))
} }
input.getElementById("Group")?.children()?.first()?.text()?.let { input.getElementById("Group")?.children()?.first()?.text()?.let {
group = it.trim() group = it.trim()
tags.add(RaisedTag("group", it, TAG_TYPE_VIRTUAL)) tags.add(RaisedTag("group", it, TAG_TYPE_DEFAULT))
} }
val newParody = mutableListOf<String>() val newParody = mutableListOf<String>()
input.getElementById("Parody")?.children()?.forEach { input.getElementById("Parody")?.children()?.forEach {
val entry = it.text().trim() val entry = it.text().trim()
newParody.add(entry) newParody.add(entry)
tags.add(RaisedTag("parody", entry, TAG_TYPE_VIRTUAL)) tags.add(RaisedTag("parody", entry, TAG_TYPE_DEFAULT))
} }
parody = newParody parody = newParody
@ -105,14 +112,14 @@ class Tsumino(delegate: HttpSource, val context: Context) :
input.getElementById("Character")?.children()?.forEach { input.getElementById("Character")?.children()?.forEach {
val entry = it.text().trim() val entry = it.text().trim()
newCharacter.add(entry) newCharacter.add(entry)
tags.add(RaisedTag("character", entry, TAG_TYPE_VIRTUAL)) tags.add(RaisedTag("character", entry, TAG_TYPE_DEFAULT))
} }
character = newCharacter character = newCharacter
input.getElementById("Tag")?.children()?.let { input.getElementById("Tag")?.children()?.let { tagElements ->
tags.addAll( tags.addAll(
it.map { tagElements.map {
RaisedTag(null, it.text().trim(), TAG_TYPE_DEFAULT) RaisedTag("tags", it.text().trim(), TAG_TYPE_DEFAULT)
} }
) )
} }
@ -126,6 +133,5 @@ class Tsumino(delegate: HttpSource, val context: Context) :
companion object { companion object {
val TM_DATE_FORMAT = SimpleDateFormat("yyyy MMM dd", Locale.US) 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.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource 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.FabController
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction 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.shrinkOnScroll
import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.util.view.visible 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 java.io.IOException
import kotlinx.android.synthetic.main.main_activity.root_coordinator import kotlinx.android.synthetic.main.main_activity.root_coordinator
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@ -419,6 +423,16 @@ class MangaController :
// Manga info - start // 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. * Check if manga is initialized.
* If true update header with manga information, * If true update header with manga information,
@ -427,10 +441,10 @@ class MangaController :
* @param manga manga object containing information about manga. * @param manga manga object containing information about manga.
* @param source the source of the 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) { if (manga.initialized) {
// Update view. // Update view.
mangaInfoAdapter?.update(manga, source) mangaInfoAdapter?.update(manga, source /* SY --> */, meta /* SY <-- */)
} else { } else {
// Initialize manga. // Initialize manga.
fetchMangaInfoFromSource() 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.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source 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.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
@ -33,6 +34,10 @@ import exh.MERGED_SOURCE_ID
import exh.debug.DebugToggles import exh.debug.DebugToggles
import exh.eh.EHentaiUpdateHelper import exh.eh.EHentaiUpdateHelper
import exh.isEhBasedSource 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.await
import exh.util.trimOrNull import exh.util.trimOrNull
import java.util.Date import java.util.Date
@ -101,15 +106,23 @@ class MangaPresenter(
private val redirectUserRelay = BehaviorRelay.create<EXHRedirect>() private val redirectUserRelay = BehaviorRelay.create<EXHRedirect>()
data class EXHRedirect(val manga: Manga, val update: Boolean) data class EXHRedirect(val manga: Manga, val update: Boolean)
var meta: RaisedSearchMetadata? = null
// EXH <-- // EXH <--
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) 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 // Manga info - start
getMangaObservable() getMangaObservable()
.subscribeLatestCache({ view, manga -> view.onNextMangaInfo(manga, source) }) .subscribeLatestCache({ view, manga -> view.onNextMangaInfo(manga, source /* SY --> */, meta/* SY <-- */) })
// Prepare the relay. // Prepare the relay.
chaptersRelay.flatMap { applyChapterFilters(it) } chaptersRelay.flatMap { applyChapterFilters(it) }
@ -177,6 +190,16 @@ class MangaPresenter(
.observeOn(AndroidSchedulers.mainThread()) .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. * Fetch manga information from source.
*/ */
@ -190,6 +213,13 @@ class MangaPresenter(
db.insertManga(manga).executeAsBlocking() db.insertManga(manga).executeAsBlocking()
manga 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()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribeFirst( .subscribeFirst(
@ -259,7 +289,7 @@ class MangaPresenter(
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache( .subscribeLatestCache(
{ view, _ -> { 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.setTooltip
import eu.kanade.tachiyomi.util.view.visible import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.util.view.visibleIf 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.MERGED_SOURCE_ID
import exh.NHENTAI_SOURCE_ID import exh.isNamespaceSource
import exh.PURURIN_SOURCE_ID import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.util.SourceTagsUtil import exh.util.SourceTagsUtil
import exh.util.makeSearchChip import exh.util.makeSearchChip
import exh.util.setChipsExtended import exh.util.setChipsExtended
@ -57,6 +54,7 @@ class MangaInfoHeaderAdapter(
private var manga: Manga = controller.presenter.manga private var manga: Manga = controller.presenter.manga
private var source: Source = controller.presenter.source private var source: Source = controller.presenter.source
private var meta: RaisedSearchMetadata? = controller.presenter.meta
private val scope = CoroutineScope(Job() + Dispatchers.Main) private val scope = CoroutineScope(Job() + Dispatchers.Main)
private lateinit var binding: MangaInfoHeaderBinding private lateinit var binding: MangaInfoHeaderBinding
@ -83,9 +81,10 @@ class MangaInfoHeaderAdapter(
* @param manga manga object containing information about manga. * @param manga manga object containing information about manga.
* @param source the source of the 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.manga = manga
this.source = source this.source = source
this.meta = meta
notifyDataSetChanged() notifyDataSetChanged()
} }
@ -336,7 +335,7 @@ class MangaInfoHeaderAdapter(
// Update genres list // Update genres list
if (!manga.genre.isNullOrBlank()) { if (!manga.genre.isNullOrBlank()) {
// SY --> // 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() val genre = manga.getGenres()
if (!genre.isNullOrEmpty()) { if (!genre.isNullOrEmpty()) {
val namespaceTags = genre.map { SourceTagsUtil().parseTag(it) } val namespaceTags = genre.map { SourceTagsUtil().parseTag(it) }
@ -414,7 +413,7 @@ class MangaInfoHeaderAdapter(
binding.mangaGenresTagsCompact.visibleIf { isExpanded } binding.mangaGenresTagsCompact.visibleIf { isExpanded }
// SY --> // 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 } binding.mangaNamespaceTagsRecycler.visibleIf { !isExpanded }
} else { } else {
binding.mangaGenresTagsFullChips.visibleIf { !isExpanded } binding.mangaGenresTagsFullChips.visibleIf { !isExpanded }

View File

@ -57,3 +57,5 @@ fun isLewdSource(source: Long) = source in 6900..6999 ||
lewdDelegatedSourceIds.binarySearch(source) >= 0 lewdDelegatedSourceIds.binarySearch(source) >= 0
fun Source.isEhBasedSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID 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.HITOMI_SOURCE_ID
import exh.NHENTAI_SOURCE_ID import exh.NHENTAI_SOURCE_ID
import exh.PURURIN_SOURCE_ID import exh.PURURIN_SOURCE_ID
import exh.TSUMINO_SOURCE_ID
class SourceTagsUtil { class SourceTagsUtil {
fun getWrappedTag(sourceId: Long, namespace: String? = null, tag: String? = null, fullTag: String? = null): String? { 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()) HITOMI_SOURCE_ID -> wrapTagHitomi(parsed.first, parsed.second.substringBefore('|').trim())
NHENTAI_SOURCE_ID -> wrapTagNHentai(parsed.first, parsed.second.substringBefore('|').trim()) NHENTAI_SOURCE_ID -> wrapTagNHentai(parsed.first, parsed.second.substringBefore('|').trim())
PURURIN_SOURCE_ID -> 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 -> wrapTag(parsed.first, parsed.second.substringBefore('|').trim())
} }
} else null } else null