From c7e0585c6f579e04e4c2867f9a50e79b0dbdfb8b Mon Sep 17 00:00:00 2001 From: David Brochero <65723952+D-Brox@users.noreply.github.com> Date: Mon, 2 Feb 2026 02:17:23 -0300 Subject: [PATCH] feat(tracker): add support for `EnhancedTracker` to merged sources (#932) * fix: add support for `EnhancedTracker` to merged sources * ref: readability * fix: use only sources specific to the merged title * fix: don't match using name * formatting * don't block, suspend * Move merged source out of EnhancedTracker * Extract Injekt * Return first accepted merged manga * Move getMergedSources to SourceManager and update usages accordingly * simplify code --------- Co-authored-by: Cuong-Tran Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com> --- .../tachiyomi/data/track/EnhancedTracker.kt | 9 ++++ .../tachiyomi/source/AndroidSourceManager.kt | 12 +++++ .../tachiyomi/ui/manga/MangaScreenModel.kt | 11 ++++- .../ui/manga/track/TrackInfoDialog.kt | 47 ++++++++++++++++--- .../list/MigrationListScreenModel.kt | 7 +-- .../domain/source/service/SourceManager.kt | 4 ++ 6 files changed, 76 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/EnhancedTracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/EnhancedTracker.kt index a501cded8..8fb578ba2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/EnhancedTracker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/EnhancedTracker.kt @@ -18,6 +18,15 @@ interface EnhancedTracker { return source::class.qualifiedName in getAcceptedSources() } + // KMK --> + /** + * This tracker will only work with the sources that are accepted by this filter function. + */ + fun accept(sources: List): Boolean { + return sources.any { accept(it) } + } + // KMK <-- + /** * Fully qualified source classes that this tracker is compatible with. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt index 87c3a0d1b..e5280bc02 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt @@ -41,6 +41,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import tachiyomi.domain.manga.interactor.GetMergedReferencesById import tachiyomi.domain.source.model.StubSource import tachiyomi.domain.source.repository.StubSourceRepository import tachiyomi.domain.source.service.SourceManager @@ -76,6 +77,9 @@ class AndroidSourceManager( private val exhPreferences: ExhPreferences by injectLazy() private val sourcePreferences: SourcePreferences by injectLazy() // SY <-- + // KMK --> + private val getMergedReferencesById: GetMergedReferencesById by injectLazy() + // KMK <-- init { scope.launch { @@ -238,6 +242,14 @@ class AndroidSourceManager( } // SY <-- + // KMK --> + override suspend fun getMergedSources(mangaId: Long): List { + val sources = getMergedReferencesById.await(mangaId) + return sources.distinctBy { it.mangaSourceId } + .map { getOrStub(it.mangaSourceId) } + } + // KMK <-- + private fun registerStubSource(source: StubSource) { scope.launch { val dbSource = sourceRepository.getStubSource(source.id) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index f2d6f4934..216f00e46 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -1823,7 +1823,16 @@ class MangaScreenModel( trackerManager.loggedInTrackersFlow(), ) { mangaTracks, loggedInTrackers -> // Show only if the service supports this manga's source - val supportedTrackers = loggedInTrackers.filter { (it as? EnhancedTracker)?.accept(source!!) ?: true } + // KMK --> + val supportedTrackers = source?.let { source -> + val sources = if (source is MergedSource) { + state.mergedData?.sources ?: emptyList() + } else { + listOf(source) + } + loggedInTrackers.filter { (it as? EnhancedTracker)?.accept(sources) ?: true } + } ?: loggedInTrackers.filterNot { it is EnhancedTracker } + // KMK <-- val supportedTrackerIds = supportedTrackers.map { it.id }.toHashSet() val supportedTrackerTracks = mangaTracks.filter { it.trackerId in supportedTrackerIds } supportedTrackerTracks to supportedTrackers diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt index 0e346ce19..dc5760eb4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt @@ -60,12 +60,14 @@ import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.TrackerManager import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.source.online.MetadataSource +import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone import eu.kanade.tachiyomi.util.lang.toLocalDate import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.toast import exh.metadata.metadata.base.TrackerIdMetadata +import exh.source.MERGED_SOURCE_ID import exh.source.getMainSource import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.flow.catch @@ -83,6 +85,8 @@ import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.interactor.GetFlatMetadataById import tachiyomi.domain.manga.interactor.GetManga +import tachiyomi.domain.manga.interactor.GetMergedReferencesById +import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.track.interactor.DeleteTrack import tachiyomi.domain.track.interactor.GetTracks @@ -94,6 +98,7 @@ import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy import java.time.Instant import java.time.LocalDate import java.time.ZoneOffset @@ -230,10 +235,14 @@ data class TrackInfoDialogHomeScreen( private val trackPreferences: TrackPreferences = Injekt.get(), // SY <-- // KMK --> - val sourceManager: SourceManager = Injekt.get(), - val getFlatMetadataById: GetFlatMetadataById = Injekt.get(), + private val sourceManager: SourceManager = Injekt.get(), // KMK <-- ) : StateScreenModel(State()) { + // KMK --> + private val getFlatMetadataById: GetFlatMetadataById by injectLazy() + private val getMangaById: GetManga by injectLazy() + private val getMergedReferencesById: GetMergedReferencesById by injectLazy() + // KMK <-- init { screenModelScope.launch { @@ -249,10 +258,25 @@ data class TrackInfoDialogHomeScreen( } } + // KMK --> + private suspend fun getMangaForTracking(item: TrackItem): Manga? { + if (sourceId != MERGED_SOURCE_ID) { + return getMangaById.await(mangaId) + } + item.tracker as EnhancedTracker + val references = getMergedReferencesById.await(mangaId) + return references.distinctBy { it.mangaSourceId }.firstNotNullOfOrNull { ref -> + sourceManager.get(ref.mangaSourceId) + ?.takeIf(item.tracker::accept) + ?.let { ref.mangaId?.let { mangaId -> getMangaById.await(mangaId) } } + } + } + // KMK <-- + fun registerEnhancedTracking(item: TrackItem) { item.tracker as EnhancedTracker screenModelScope.launchNonCancellable { - val manga = Injekt.get().await(mangaId) ?: return@launchNonCancellable + val manga = getMangaForTracking(item) ?: return@launchNonCancellable try { val matchResult = item.tracker.match(manga) ?: throw Exception() item.tracker.register(matchResult, mangaId) @@ -361,14 +385,23 @@ data class TrackInfoDialogHomeScreen( } } - private fun List.mapToTrackItem(): List { - val loggedInTrackers = Injekt.get().loggedInTrackers() - val source = Injekt.get().getOrStub(sourceId) + private suspend fun List.mapToTrackItem(): List { + val loggedInTrackers = trackerManager.loggedInTrackers() + val source = sourceManager.getOrStub(sourceId) return loggedInTrackers // Map to TrackItem .map { service -> TrackItem(find { it.trackerId == service.id }, service) } // Show only if the service supports this manga's source - .filter { (it.tracker as? EnhancedTracker)?.accept(source) ?: true } + // KMK --> + .let { trackers -> + val sources = if (source is MergedSource) { + sourceManager.getMergedSources(mangaId) + } else { + listOf(source) + } + trackers.filter { (it.tracker as? EnhancedTracker)?.accept(sources) ?: true } + } + // KMK <-- } @Immutable diff --git a/app/src/main/java/mihon/feature/migration/list/MigrationListScreenModel.kt b/app/src/main/java/mihon/feature/migration/list/MigrationListScreenModel.kt index d10128e9f..c6ea53f5e 100644 --- a/app/src/main/java/mihon/feature/migration/list/MigrationListScreenModel.kt +++ b/app/src/main/java/mihon/feature/migration/list/MigrationListScreenModel.kt @@ -39,7 +39,6 @@ import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId import tachiyomi.domain.manga.interactor.GetManga -import tachiyomi.domain.manga.interactor.GetMergedReferencesById import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager @@ -57,9 +56,6 @@ class MigrationListScreenModel( private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(), private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get(), private val migrateManga: MigrateMangaUseCase = Injekt.get(), - // SY --> - private val getMergedReferencesById: GetMergedReferencesById = Injekt.get(), - // SY <-- ) : StateScreenModel(State()) { private val smartSearchEngine = SmartSourceSearchEngine(extraSearchQuery) @@ -97,8 +93,7 @@ class MigrationListScreenModel( source = sourceManager.getOrStub(manga.source).getNameForMangaInfo( // KMK --> if (manga.source == MERGED_SOURCE_ID) { - getMergedReferencesById.await(manga.id) - .map { sourceManager.getOrStub(it.mangaSourceId) } + sourceManager.getMergedSources(manga.id) } else { null }, diff --git a/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt b/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt index 349506ee7..e6302a37f 100644 --- a/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt +++ b/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt @@ -27,5 +27,9 @@ interface SourceManager { fun getVisibleCatalogueSources(): List // SY <-- + // KMK --> + suspend fun getMergedSources(mangaId: Long): List + // KMK <-- + fun getStubSources(): List }