Auto sync read chapters from Trackers & mark read to update (#28)

* Mark chapter(s) as read will update Tracking

* Fix: sync tracking 2 ways for all trackers

* automatically sync read chapters from Trackers
This commit is contained in:
Cuong M. Tran 2024-05-22 17:54:22 +07:00
parent 81376ae818
commit ce7177efc6
7 changed files with 98 additions and 12 deletions

View file

@ -1,7 +1,6 @@
package eu.kanade.domain.track.interactor package eu.kanade.domain.track.interactor
import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.Tracker
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
@ -22,26 +21,36 @@ class SyncChapterProgressWithTrack(
remoteTrack: Track, remoteTrack: Track,
tracker: Tracker, tracker: Tracker,
) { ) {
if (tracker !is EnhancedTracker) { // KKM -->
return // if (tracker !is EnhancedTracker) {
} // return
// }
// <-- KKM
// Current chapters in database
val sortedChapters = getChaptersByMangaId.await(mangaId) val sortedChapters = getChaptersByMangaId.await(mangaId)
.sortedBy { it.chapterNumber } .sortedBy { it.chapterNumber }
.filter { it.isRecognizedNumber } .filter { it.isRecognizedNumber }
// Chapters to update to follow tracker
val chapterUpdates = sortedChapters val chapterUpdates = sortedChapters
.filter { chapter -> chapter.chapterNumber <= remoteTrack.lastChapterRead && !chapter.read } .filter { chapter -> chapter.chapterNumber <= remoteTrack.lastChapterRead && !chapter.read }
.map { it.copy(read = true).toChapterUpdate() } .map { it.copy(read = true).toChapterUpdate() }
// only take into account continuous reading // only take into account continuous reading
val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapterNumber ?: 0F val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapterNumber ?: 0F
// Tracker will update to latest read chapter
val updatedTrack = remoteTrack.copy(lastChapterRead = localLastRead.toDouble()) val updatedTrack = remoteTrack.copy(lastChapterRead = localLastRead.toDouble())
try { try {
tracker.update(updatedTrack.toDbTrack()) // Update Tracker to localLastRead if needed
if (updatedTrack.lastChapterRead > remoteTrack.lastChapterRead) {
tracker.update(updatedTrack.toDbTrack())
// update Track in database
insertTrack.await(updatedTrack)
}
// Update local chapters following Tracker
updateChapter.awaitAll(chapterUpdates) updateChapter.awaitAll(chapterUpdates)
insertTrack.await(updatedTrack)
} catch (e: Throwable) { } catch (e: Throwable) {
logcat(LogPriority.WARN, e) logcat(LogPriority.WARN, e)
} }

View file

@ -35,4 +35,6 @@ class TrackPreferences(
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10) fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true) fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
fun updateTrackMarkedRead() = preferenceStore.getBoolean("pref_update_tracking_marked_read_key", false)
fun autoSyncReadChapters() = preferenceStore.getBoolean("pref_auto_sync_read_chapters_key", false)
} }

View file

@ -125,6 +125,14 @@ object SettingsTrackingScreen : SearchableSettings {
pref = trackPreferences.autoUpdateTrack(), pref = trackPreferences.autoUpdateTrack(),
title = stringResource(MR.strings.pref_auto_update_manga_sync), title = stringResource(MR.strings.pref_auto_update_manga_sync),
), ),
Preference.PreferenceItem.SwitchPreference(
pref = trackPreferences.updateTrackMarkedRead(),
title = stringResource(MR.strings.pref_update_tracking_marked_read),
),
Preference.PreferenceItem.SwitchPreference(
pref = trackPreferences.autoSyncReadChapters(),
title = stringResource(MR.strings.pref_auto_sync_read_chapters),
),
Preference.PreferenceGroup( Preference.PreferenceGroup(
title = stringResource(MR.strings.services), title = stringResource(MR.strings.services),
preferenceItems = persistentListOf( preferenceItems = persistentListOf(

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.manga package eu.kanade.tachiyomi.ui.manga
import android.app.Application
import android.content.Context import android.content.Context
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarResult
@ -29,7 +30,10 @@ import eu.kanade.domain.manga.model.toDomainManga
import eu.kanade.domain.manga.model.toSManga import eu.kanade.domain.manga.model.toSManga
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.interactor.AddTracks import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.domain.track.interactor.RefreshTracks
import eu.kanade.domain.track.interactor.TrackChapter
import eu.kanade.domain.track.model.toDomainTrack import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.manga.DownloadAction import eu.kanade.presentation.manga.DownloadAction
import eu.kanade.presentation.manga.components.ChapterDownloadAction import eu.kanade.presentation.manga.components.ChapterDownloadAction
@ -55,6 +59,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.chapter.getNextUnread import eu.kanade.tachiyomi.util.chapter.getNextUnread
import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.removeCovers
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import eu.kanade.tachiyomi.util.system.toast
import exh.debug.DebugToggles import exh.debug.DebugToggles
import exh.eh.EHentaiUpdateHelper import exh.eh.EHentaiUpdateHelper
import exh.log.xLogD import exh.log.xLogD
@ -156,6 +161,10 @@ class MangaScreenModel(
private val libraryPreferences: LibraryPreferences = Injekt.get(), private val libraryPreferences: LibraryPreferences = Injekt.get(),
readerPreferences: ReaderPreferences = Injekt.get(), readerPreferences: ReaderPreferences = Injekt.get(),
uiPreferences: UiPreferences = Injekt.get(), uiPreferences: UiPreferences = Injekt.get(),
// KMK -->
private val trackPreferences: TrackPreferences = Injekt.get(),
private val trackChapter: TrackChapter = Injekt.get(),
// KMK <--
private val trackerManager: TrackerManager = Injekt.get(), private val trackerManager: TrackerManager = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(),
private val downloadCache: DownloadCache = Injekt.get(), private val downloadCache: DownloadCache = Injekt.get(),
@ -450,6 +459,7 @@ class MangaScreenModel(
} }
// KMK <-- // KMK <--
val fetchFromSourceTasks = listOf( val fetchFromSourceTasks = listOf(
async { syncTrackers() },
async { if (needRefreshInfo) fetchMangaFromSource() }, async { if (needRefreshInfo) fetchMangaFromSource() },
async { if (needRefreshChapter) fetchChaptersFromSource() }, async { if (needRefreshChapter) fetchChaptersFromSource() },
) )
@ -461,10 +471,35 @@ class MangaScreenModel(
} }
} }
// KMK -->
private suspend fun syncTrackers() {
if (!trackPreferences.updateTrackMarkedRead().get()) return
val refreshTracks = Injekt.get<RefreshTracks>()
refreshTracks.await(mangaId)
.filter { it.first != null }
.forEach { (track, e) ->
logcat(LogPriority.ERROR, e) {
"Failed to refresh track data mangaId=$mangaId for service ${track!!.id}"
}
withUIContext {
context.toast(
context.stringResource(
MR.strings.track_error,
track!!.name,
e.message ?: "",
),
)
}
}
}
// KMK <--
fun fetchAllFromSource(manualFetch: Boolean = true) { fun fetchAllFromSource(manualFetch: Boolean = true) {
screenModelScope.launch { screenModelScope.launch {
updateSuccessState { it.copy(isRefreshingData = true) } updateSuccessState { it.copy(isRefreshingData = true) }
val fetchFromSourceTasks = listOf( val fetchFromSourceTasks = listOf(
async { syncTrackers() },
async { fetchMangaFromSource(manualFetch) }, async { fetchMangaFromSource(manualFetch) },
async { fetchChaptersFromSource(manualFetch) }, async { fetchChaptersFromSource(manualFetch) },
) )
@ -669,7 +704,7 @@ class MangaScreenModel(
mergedManga = networkToLocalManga.await(mergedManga) mergedManga = networkToLocalManga.await(mergedManga)
getCategories.await(originalMangaId) getCategories.await(originalMangaId)
.let { .let { it ->
setMangaCategories.await(mergedManga.id, it.map { it.id }) setMangaCategories.await(mergedManga.id, it.map { it.id })
} }
@ -1163,6 +1198,11 @@ class MangaScreenModel(
when (swipeAction) { when (swipeAction) {
LibraryPreferences.ChapterSwipeAction.ToggleRead -> { LibraryPreferences.ChapterSwipeAction.ToggleRead -> {
markChaptersRead(listOf(chapter), !chapter.read) markChaptersRead(listOf(chapter), !chapter.read)
// KMK -->
if (!chapter.read) {
updateTrackChapterMarkedAsRead(chapter)
}
// KMK <--
} }
LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> { LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> {
bookmarkChapters(listOf(chapter), !chapter.bookmark) bookmarkChapters(listOf(chapter), !chapter.bookmark)
@ -1306,9 +1346,32 @@ class MangaScreenModel(
chapters = chapters.toTypedArray(), chapters = chapters.toTypedArray(),
) )
} }
// KMK -->
if (read) {
chapters.maxByOrNull { it.chapterNumber }
?.also { updateTrackChapterMarkedAsRead(it) }
}
// KMK <--
toggleAllSelection(false) toggleAllSelection(false)
} }
// KMK -->
/**
* Starts the service that updates the chapter marked as read in sync services. This operation
* will run in a background thread and errors are ignored.
*/
private fun updateTrackChapterMarkedAsRead(chapter: Chapter) {
if (!trackPreferences.updateTrackMarkedRead().get()) return
val manga = manga ?: return
val context = Injekt.get<Application>()
screenModelScope.launchNonCancellable {
trackChapter.await(context, manga.id, chapter.chapterNumber)
}
}
// KMK <--
/** /**
* Downloads the given list of chapters with the manager. * Downloads the given list of chapters with the manager.
* @param chapters the list of chapters to download. * @param chapters the list of chapters to download.

View file

@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<changelog bulletedList="true"> <changelog bulletedList="true">
<changelogversion versionName="Latest (r8894)" changeDate="May 17, 2024"> <changelogversion versionName="Latest (r8894)" changeDate="May 17, 2024">
<changelogtext>A dedicated screen for related entries, fetching directly from sources</changelogtext> <changelogtext>[b]New[/b] A dedicated screen for related entries, fetching directly from sources</changelogtext>
<changelogtext>Faster loading of related entries</changelogtext> <changelogtext>[b]Improved[/b] Faster loading of related entries</changelogtext>
<changelogtext>Enable fetching related entries from site's entry page (things like "More like this", "Recommendations" etc.)</changelogtext> <changelogtext>[b]Improved[/b] Enable fetching related entries from site's entry page (things like "More like this", "Recommendations" etc.)</changelogtext>
<changelogtext>Improve &amp; fix bugs of algorithm looking for related entries</changelogtext> <changelogtext>Improve &amp; fix bugs of algorithm looking for related entries</changelogtext>
<changelogtext>A peek into Preview-build's 'What's New' in About screen</changelogtext> <changelogtext>[b]Improved[/b] Auto sync read chapters from Trackers &amp; mark read to update</changelogtext>
<changelogtext>Update to TachiyomiSY preview-142</changelogtext> <changelogtext>[b]New[/b] A peek into Preview-build's 'What's New' in About screen</changelogtext>
<changelogtext>[b]Update to TachiyomiSY preview-142[/b]</changelogtext>
</changelogversion> </changelogversion>
<changelogversion versionName="r8852" changeDate="May 9,2024"> <changelogversion versionName="r8852" changeDate="May 9,2024">
<changelogtext>Beta build switch to 'preview' repo</changelogtext> <changelogtext>Beta build switch to 'preview' repo</changelogtext>

View file

@ -5,6 +5,7 @@
<changelogtext>[b]Improved[/b] Faster loading of related entries</changelogtext> <changelogtext>[b]Improved[/b] Faster loading of related entries</changelogtext>
<changelogtext>[b]Improved[/b] Enable fetching related entries from site's entry page (things like "More like this", "Recommendations" etc.)</changelogtext> <changelogtext>[b]Improved[/b] Enable fetching related entries from site's entry page (things like "More like this", "Recommendations" etc.)</changelogtext>
<changelogtext>Improve &amp; fix bugs of algorithm looking for related entries</changelogtext> <changelogtext>Improve &amp; fix bugs of algorithm looking for related entries</changelogtext>
<changelogtext>[b]Improved[/b] Auto sync read chapters from Trackers &amp; mark read to update</changelogtext>
<changelogtext>[b]New[/b] A peek into Preview-build's 'What's New' in About screen</changelogtext> <changelogtext>[b]New[/b] A peek into Preview-build's 'What's New' in About screen</changelogtext>
<changelogtext>[b]Update to TachiyomiSY 1.10.6[/b]</changelogtext> <changelogtext>[b]Update to TachiyomiSY 1.10.6[/b]</changelogtext>
<changelogtext>[b]Update to Mihon 0.16.6[/b]</changelogtext> <changelogtext>[b]Update to Mihon 0.16.6[/b]</changelogtext>

View file

@ -501,6 +501,8 @@
<!-- Tracking section --> <!-- Tracking section -->
<string name="tracking_guide">Tracking guide</string> <string name="tracking_guide">Tracking guide</string>
<string name="pref_auto_update_manga_sync">Update progress after reading</string> <string name="pref_auto_update_manga_sync">Update progress after reading</string>
<string name="pref_update_tracking_marked_read">Update progress when marked as read</string>
<string name="pref_auto_sync_read_chapters">Auto sync read chapters from Trackers</string>
<string name="services">Trackers</string> <string name="services">Trackers</string>
<string name="tracking_info">One-way sync to update the chapter progress in external tracker services. Set up tracking for individual entries from their tracking button.</string> <string name="tracking_info">One-way sync to update the chapter progress in external tracker services. Set up tracking for individual entries from their tracking button.</string>
<string name="enhanced_services">Enhanced trackers</string> <string name="enhanced_services">Enhanced trackers</string>