Filter scanlators on delegated manga

This commit is contained in:
Jobobby04 2020-10-25 20:24:30 -04:00
parent 32232c80aa
commit e9cef78d19
7 changed files with 128 additions and 10 deletions

View File

@ -547,6 +547,7 @@ class MangaController :
if (mainSource is MetadataSource<*, *>) { if (mainSource is MetadataSource<*, *>) {
presenter.meta = flatMetadata.raise(mainSource.metaClass) presenter.meta = flatMetadata.raise(mainSource.metaClass)
mangaMetaInfoAdapter?.notifyDataSetChanged() mangaMetaInfoAdapter?.notifyDataSetChanged()
updateFilterIconState()
} }
} }
// SY <-- // SY <--

View File

@ -4,7 +4,6 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
import com.elvishew.xlog.XLog
import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -44,10 +43,13 @@ import exh.debug.DebugToggles
import exh.eh.EHentaiUpdateHelper import exh.eh.EHentaiUpdateHelper
import exh.isEhBasedSource import exh.isEhBasedSource
import exh.md.utils.FollowStatus import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil
import exh.md.utils.scanlatorList
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import exh.metadata.metadata.base.FlatMetadata import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.util.asObservable import exh.util.asObservable
import exh.util.await import exh.util.await
@ -133,6 +135,8 @@ class MangaPresenter(
private var mergedManga = emptyList<Manga>() private var mergedManga = emptyList<Manga>()
var dedupe: Boolean = true var dedupe: Boolean = true
var allChapterScanlators: Set<String> = emptySet()
// EXH <-- // EXH <--
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
@ -163,7 +167,14 @@ class MangaPresenter(
} }
} }
.subscribeLatestCache({ view, (manga, flatMetadata) -> .subscribeLatestCache({ view, (manga, flatMetadata) ->
if (flatMetadata != null) view.onNextMetaInfo(flatMetadata) else XLog.d("Invalid metadata") flatMetadata?.let { metadata ->
view.onNextMetaInfo(metadata)
meta?.let {
it.filteredScanlators?.let {
if (chapters.isNotEmpty()) chaptersRelay.call(chapters)
}
}
}
// SY <-- // SY <--
view.onNextMangaInfo(manga, source) view.onNextMangaInfo(manga, source)
}) })
@ -193,6 +204,8 @@ class MangaPresenter(
// Find downloaded chapters // Find downloaded chapters
setDownloadedChapters(chapters) setDownloadedChapters(chapters)
allChapterScanlators = chapters.flatMap { it.chapter.scanlatorList() }.toSet()
// Store the last emission // Store the last emission
this.chapters = chapters this.chapters = chapters
@ -803,6 +816,15 @@ class MangaPresenter(
observable = observable.filter { !it.bookmark } observable = observable.filter { !it.bookmark }
} }
// SY -->
meta?.let { metadata ->
metadata.filteredScanlators?.let { filteredScanlatorString ->
val filteredScanlators = MdUtil.getScanlators(filteredScanlatorString)
observable = observable.filter { it.scanlatorList().any { group -> filteredScanlators.contains(group) } }
}
}
// SY <--
val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) { val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
Manga.SORTING_SOURCE -> when (sortDescending()) { Manga.SORTING_SOURCE -> when (sortDescending()) {
true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) } true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
@ -994,6 +1016,17 @@ class MangaPresenter(
refreshChapters() refreshChapters()
} }
// SY -->
fun setScanlatorFilter(filteredScanlators: Set<String>) {
val meta = meta ?: return
meta.filteredScanlators = if (filteredScanlators.size == allChapterScanlators.size) null else MdUtil.getScanlatorString(filteredScanlators)
meta.flatten().let {
db.insertFlatMetadata(it).await()
}
refreshChapters()
}
// SY <--
/** /**
* Sets the active display mode. * Sets the active display mode.
* @param mode the mode to set. * @param mode the mode to set.

View File

@ -4,14 +4,21 @@ import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.bluelinelabs.conductor.Router import com.bluelinelabs.conductor.Router
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.ui.manga.MangaPresenter import eu.kanade.tachiyomi.ui.manga.MangaPresenter
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.popupMenu import eu.kanade.tachiyomi.util.view.popupMenu
import eu.kanade.tachiyomi.widget.ExtendedNavigationView import eu.kanade.tachiyomi.widget.ExtendedNavigationView
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.State import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.State
import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog
import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata
import exh.source.EnhancedHttpSource.Companion.getMainSource
class ChaptersSettingsSheet( class ChaptersSettingsSheet(
private val router: Router, private val router: Router,
@ -82,7 +89,7 @@ class ChaptersSettingsSheet(
* Returns true if there's at least one filter from [FilterGroup] active. * Returns true if there's at least one filter from [FilterGroup] active.
*/ */
fun hasActiveFilters(): Boolean { fun hasActiveFilters(): Boolean {
return filterGroup.items.any { it.state != State.IGNORE.value } return filterGroup.items.any { it.state != State.IGNORE.value } || (presenter.meta?.let { it is MangaDexSearchMetadata && it.filteredScanlators != null } ?: false)
} }
inner class FilterGroup : Group { inner class FilterGroup : Group {
@ -91,8 +98,10 @@ class ChaptersSettingsSheet(
private val unread = Item.TriStateGroup(R.string.action_filter_unread, this) private val unread = Item.TriStateGroup(R.string.action_filter_unread, this)
private val bookmarked = Item.TriStateGroup(R.string.action_filter_bookmarked, this) private val bookmarked = Item.TriStateGroup(R.string.action_filter_bookmarked, this)
private val scanlatorFilters = Item.DrawableSelection(0, this, R.string.scanlator, R.drawable.ic_outline_people_alt_24dp)
override val header = null override val header = null
override val items = listOf(downloaded, unread, bookmarked) override val items = listOf(downloaded, unread, bookmarked) + if (presenter.source.getMainSource() is MetadataSource<*, *>) listOf(scanlatorFilters) else emptyList()
override val footer = null override val footer = null
override fun initModels() { override fun initModels() {
@ -107,6 +116,34 @@ class ChaptersSettingsSheet(
} }
override fun onItemClicked(item: Item) { override fun onItemClicked(item: Item) {
if (item is Item.DrawableSelection) {
val meta = presenter.meta
if (meta == null) {
context.toast(R.string.metadata_corrupted)
return
} else if (presenter.allChapterScanlators.isEmpty()) {
context.toast(R.string.no_scanlators)
return
}
val scanlators = presenter.allChapterScanlators.toList()
val filteredScanlators = meta.filteredScanlators?.let { MdUtil.getScanlators(it) }
val preselected = if (filteredScanlators.isNullOrEmpty()) scanlators.mapIndexed { index, _ -> index }.toIntArray() else filteredScanlators.map { scanlators.indexOf(it) }.toIntArray()
MaterialDialog(context)
.title(R.string.select_scanlators)
.listItemsMultiChoice(items = presenter.allChapterScanlators.toList(), initialSelection = preselected) { _, selections, _ ->
val selected = selections.map { scanlators[it] }.toSet()
presenter.setScanlatorFilter(selected)
onGroupClicked(this)
}
.negativeButton(R.string.action_reset) {
presenter.setScanlatorFilter(presenter.allChapterScanlators)
onGroupClicked(this)
}
.positiveButton(android.R.string.ok)
.show()
return
}
item as Item.TriStateGroup item as Item.TriStateGroup
val newState = when (item.state) { val newState = when (item.state) {
State.IGNORE.value -> State.INCLUDE State.IGNORE.value -> State.INCLUDE

View File

@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.MetadataSource
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.reader.chapter.ReaderChapterItem import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterItem
@ -35,6 +36,11 @@ import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.md.utils.FollowStatus import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil
import exh.md.utils.scanlatorList
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.util.awaitSingleOrNull import exh.util.awaitSingleOrNull
import exh.util.defaultReaderType import exh.util.defaultReaderType
import exh.util.shouldDeleteChapters import exh.util.shouldDeleteChapters
@ -72,6 +78,11 @@ class ReaderPresenter(
var manga: Manga? = null var manga: Manga? = null
private set private set
// SY -->
var meta: RaisedSearchMetadata? = null
private set
// SY <--
/** /**
* The chapter id of the currently loaded chapter. Used to restore from process kill. * The chapter id of the currently loaded chapter. Used to restore from process kill.
*/ */
@ -103,7 +114,11 @@ class ReaderPresenter(
*/ */
private val chapterList by lazy { private val chapterList by lazy {
val manga = manga!! val manga = manga!!
val dbChapters = if (manga.source == MERGED_SOURCE_ID) runBlocking { (sourceManager.get(MERGED_SOURCE_ID) as? MergedSource)?.getChaptersFromDB(manga)?.awaitSingleOrNull() ?: emptyList() } else db.getChapters(manga).executeAsBlocking() // SY -->
val meta = meta
val filteredScanlators = MdUtil.getScanlators(meta?.filteredScanlators.orEmpty())
// SY <--
val dbChapters = /* SY --> */if (manga.source == MERGED_SOURCE_ID) runBlocking { (sourceManager.get(MERGED_SOURCE_ID) as? MergedSource)?.getChaptersFromDB(manga)?.awaitSingleOrNull() ?: emptyList() } else /* SY <-- */ db.getChapters(manga).executeAsBlocking()
val selectedChapter = dbChapters.find { it.id == chapterId } val selectedChapter = dbChapters.find { it.id == chapterId }
?: error("Requested chapter of id $chapterId not found in chapter list") ?: error("Requested chapter of id $chapterId not found in chapter list")
@ -122,7 +137,10 @@ class ReaderPresenter(
manga.downloadedFilter == Manga.SHOW_DOWNLOADED && manga.downloadedFilter == Manga.SHOW_DOWNLOADED &&
!downloadManager.isChapterDownloaded(it, manga) !downloadManager.isChapterDownloaded(it, manga)
) || ) ||
(manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED && !it.bookmark) (manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED && !it.bookmark) ||
// SY -->
(meta != null && it.scanlatorList().none { group -> filteredScanlators.contains(group) })
// SY <--
) { ) {
return@filter false return@filter false
} }
@ -221,7 +239,19 @@ class ReaderPresenter(
db.getManga(mangaId).asRxObservable() db.getManga(mangaId).asRxObservable()
.first() .first()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnNext { init(it, initialChapterId) } // SY -->
.flatMap { manga ->
val source = sourceManager.get(manga.source)?.getMainSource()
if (manga.initialized && source is MetadataSource<*, *>) {
db.getFlatMetadataForManga(mangaId).asRxSingle().map {
manga to it?.raise(source.metaClass)
}.toObservable()
} else {
Observable.just(manga to null)
}
}
.doOnNext { init(it.first, initialChapterId, it.second) }
// SY <--
.subscribeFirst( .subscribeFirst(
{ _, _ -> { _, _ ->
// Ignore onNext event // Ignore onNext event
@ -234,10 +264,13 @@ class ReaderPresenter(
* Initializes this presenter with the given [manga] and [initialChapterId]. This method will * Initializes this presenter with the given [manga] and [initialChapterId]. This method will
* set the chapter loader, view subscriptions and trigger an initial load. * set the chapter loader, view subscriptions and trigger an initial load.
*/ */
private fun init(manga: Manga, initialChapterId: Long) { private fun init(manga: Manga, initialChapterId: Long /* SY --> */, metadata: RaisedSearchMetadata?/* SY <-- */) {
if (!needsInit()) return if (!needsInit()) return
this.manga = manga this.manga = manga
// SY -->
this.meta = metadata
// SY <--
if (chapterId == -1L) chapterId = initialChapterId if (chapterId == -1L) chapterId = initialChapterId
val context = Injekt.get<Application>() val context = Injekt.get<Application>()

View File

@ -1,5 +1,6 @@
package exh.md.utils package exh.md.utils
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
@ -37,8 +38,7 @@ class MdUtil {
prettyPrint = true prettyPrint = true
} }
private const private const val scanlatorSeparator = " & "
val scanlatorSeparator = " & "
val validOneShotFinalChapters = listOf("0", "1") val validOneShotFinalChapters = listOf("0", "1")
@ -276,3 +276,9 @@ private fun getMDUrlWithoutDomain(orig: String): String {
orig orig
} }
} }
fun Chapter.scanlatorList(): List<String> {
return this.scanlator?.let {
MdUtil.getScanlators(it)
} ?: listOf("No scanlator")
}

View File

@ -46,6 +46,8 @@ abstract class RaisedSearchMetadata {
@Transient @Transient
val titles = mutableListOf<RaisedTitle>() val titles = mutableListOf<RaisedTitle>()
var filteredScanlators: String? = null
fun getTitleOfType(type: Int): String? = titles.find { it.type == type }?.title fun getTitleOfType(type: Int): String? = titles.find { it.type == type }?.title
fun replaceTitleOfType(type: Int, newTitle: String?) { fun replaceTitleOfType(type: Int, newTitle: String?) {

View File

@ -567,4 +567,10 @@
<string name="mangadex_push_favorites_to_mangadex_summary">Syncs any non MdList tracked manga to MangaDex as reading.</string> <string name="mangadex_push_favorites_to_mangadex_summary">Syncs any non MdList tracked manga to MangaDex as reading.</string>
<string name="mangadex_push_favorites_to_mangadex_toast">Pushed %d manga to MangaDex</string> <string name="mangadex_push_favorites_to_mangadex_toast">Pushed %d manga to MangaDex</string>
<!-- Scanlator filters -->
<string name="select_scanlators">Scanlator groups to show</string>
<string name="metadata_corrupted">Metadata corrupted, please refresh the manga</string>
<string name="no_scanlators">No scanlators available</string>
</resources> </resources>