mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
Tracking from everywhere (#1173)
* Add tracking everywhere * Add update chapters progress from tracker * Change tracking everywhere to track when marked as read * Mutualize updateTrack method * Mutualize updateTrack method with reader and notification * Revert "Add paused tracking" This reverts commit 9eb37533 * Revert change for library tracking * Revert "Add update chapters progress from tracker" This reverts commit 193a4a8a * rework of updateTrackChapterMarkedAsRead * mutualization after rework * remove oldChapter * reorder updateTrackChapterMarkedAsRead parameters
This commit is contained in:
parent
c1e4effeb5
commit
0d693a910c
13 changed files with 132 additions and 55 deletions
|
@ -10,13 +10,15 @@ import eu.kanade.tachiyomi.data.track.TrackService
|
|||
|
||||
interface TrackQueries : DbProvider {
|
||||
|
||||
fun getTracks(manga: Manga) = db.get()
|
||||
fun getTracks(manga: Manga) = getTracks(manga.id)
|
||||
|
||||
fun getTracks(mangaId: Long?) = db.get()
|
||||
.listOfObjects(Track::class.java)
|
||||
.withQuery(
|
||||
Query.builder()
|
||||
.table(TrackTable.TABLE)
|
||||
.where("${TrackTable.COL_MANGA_ID} = ?")
|
||||
.whereArgs(manga.id)
|
||||
.whereArgs(mangaId)
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
|
|
|
@ -23,6 +23,7 @@ import eu.kanade.tachiyomi.ui.main.MainActivity
|
|||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.setting.AboutController
|
||||
import eu.kanade.tachiyomi.util.chapter.updateTrackChapterMarkedAsRead
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||
|
@ -192,18 +193,21 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||
*/
|
||||
private fun markAsRead(chapterUrls: Array<String>, mangaId: Long) {
|
||||
val db: DatabaseHelper = Injekt.get()
|
||||
chapterUrls.forEach {
|
||||
val preferences: PreferencesHelper = Injekt.get()
|
||||
val chapters = chapterUrls.map {
|
||||
val chapter = db.getChapter(it, mangaId).executeAsBlocking() ?: return
|
||||
chapter.read = true
|
||||
db.updateChapterProgress(chapter).executeAsBlocking()
|
||||
val preferences: PreferencesHelper = Injekt.get()
|
||||
if (preferences.removeAfterMarkedAsRead()) {
|
||||
val manga = db.getManga(mangaId).executeAsBlocking() ?: return
|
||||
val sourceManager: SourceManager = Injekt.get()
|
||||
val source = sourceManager.get(manga.source) ?: return
|
||||
downloadManager.deleteChapters(listOf(chapter), manga, source)
|
||||
}
|
||||
return@map chapter
|
||||
}
|
||||
val newLastChapter = chapters.maxByOrNull { it.chapter_number.toInt() }
|
||||
updateTrackChapterMarkedAsRead(db, preferences, newLastChapter, mangaId, 0)
|
||||
}
|
||||
|
||||
/** Method called when user wants to stop a restore
|
||||
|
|
|
@ -95,6 +95,8 @@ object PreferenceKeys {
|
|||
|
||||
const val autoUpdateTrack = "pref_auto_update_manga_sync_key"
|
||||
|
||||
const val trackMarkedAsRead = "track_marked_as_read"
|
||||
|
||||
const val trackingsToAddOnline = "pref_tracking_for_online"
|
||||
|
||||
const val lastUsedCatalogueSource = "last_catalogue_source"
|
||||
|
|
|
@ -194,6 +194,8 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun autoUpdateTrack() = prefs.getBoolean(Keys.autoUpdateTrack, true)
|
||||
|
||||
fun trackMarkedAsRead() = prefs.getBoolean(Keys.trackMarkedAsRead, false)
|
||||
|
||||
fun trackingsToAddOnline() = flowPrefs.getStringSet(Keys.trackingsToAddOnline, emptySet())
|
||||
|
||||
fun lastUsedCatalogueSource() = flowPrefs.getLong(Keys.lastUsedCatalogueSource, -1)
|
||||
|
|
|
@ -44,9 +44,7 @@ import kotlinx.coroutines.runBlocking
|
|||
import kotlinx.coroutines.withContext
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.ArrayList
|
||||
import java.util.Calendar
|
||||
import java.util.Comparator
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
|
|
@ -80,6 +80,7 @@ import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
|||
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.addOrRemoveToFavorites
|
||||
import eu.kanade.tachiyomi.util.chapter.updateTrackChapterMarkedAsRead
|
||||
import eu.kanade.tachiyomi.util.isLocal
|
||||
import eu.kanade.tachiyomi.util.moveCategories
|
||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
|
@ -864,6 +865,8 @@ class MangaDetailsController :
|
|||
}
|
||||
|
||||
fun toggleReadChapter(position: Int) {
|
||||
val preferences = presenter.preferences
|
||||
val db = presenter.db
|
||||
val item = adapter?.getItem(position) as? ChapterItem ?: return
|
||||
val chapter = item.chapter
|
||||
val lastRead = chapter.last_page_read
|
||||
|
@ -885,8 +888,13 @@ class MangaDetailsController :
|
|||
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||
super.onDismissed(transientBottomBar, event)
|
||||
if (!undoing && !read && presenter.preferences.removeAfterMarkedAsRead()) {
|
||||
presenter.deleteChapters(listOf(item))
|
||||
if (!undoing && !read) {
|
||||
if (preferences.removeAfterMarkedAsRead()) {
|
||||
presenter.deleteChapters(listOf(item))
|
||||
}
|
||||
updateTrackChapterMarkedAsRead(db, preferences, chapter, manga?.id) {
|
||||
presenter.fetchTracks()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ import eu.kanade.tachiyomi.util.chapter.ChapterSort
|
|||
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithTrackServiceTwoWay
|
||||
import eu.kanade.tachiyomi.util.chapter.updateTrackChapterMarkedAsRead
|
||||
import eu.kanade.tachiyomi.util.lang.trimOrNull
|
||||
import eu.kanade.tachiyomi.util.manga.MangaShortcutManager
|
||||
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
||||
|
@ -488,6 +489,12 @@ class MangaDetailsPresenter(
|
|||
}
|
||||
getChapters()
|
||||
withContext(Dispatchers.Main) { controller?.updateChapters(chapters) }
|
||||
if (read && deleteNow) {
|
||||
val latestReadChapter = selectedChapters.maxByOrNull { it.chapter_number.toInt() }?.chapter
|
||||
updateTrackChapterMarkedAsRead(db, preferences, latestReadChapter, manga.id) {
|
||||
fetchTracks()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -858,7 +865,7 @@ class MangaDetailsPresenter(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchTracks() {
|
||||
suspend fun fetchTracks() {
|
||||
tracks = withContext(Dispatchers.IO) { db.getTracks(manga).executeAsBlocking() }
|
||||
trackList = loggedServices.map { service ->
|
||||
TrackItem(tracks.find { it.sync_id == service.id }, service)
|
||||
|
|
|
@ -17,8 +17,6 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
|
|||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.DelayedTrackingUpdateJob
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
|
@ -35,16 +33,16 @@ import eu.kanade.tachiyomi.ui.reader.settings.ReadingModeType
|
|||
import eu.kanade.tachiyomi.util.chapter.ChapterFilter
|
||||
import eu.kanade.tachiyomi.util.chapter.ChapterSort
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||
import eu.kanade.tachiyomi.util.chapter.updateTrackChapterRead
|
||||
import eu.kanade.tachiyomi.util.lang.getUrlWithoutDomain
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||
import eu.kanade.tachiyomi.util.system.isOnline
|
||||
import eu.kanade.tachiyomi.util.system.launchIO
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import eu.kanade.tachiyomi.util.system.withUIContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -491,7 +489,7 @@ class ReaderPresenter(
|
|||
)
|
||||
) {
|
||||
selectedChapter.chapter.read = true
|
||||
updateTrackChapterRead(selectedChapter)
|
||||
updateTrackChapterAfterReading(selectedChapter)
|
||||
deleteChapterIfNeeded(selectedChapter)
|
||||
}
|
||||
|
||||
|
@ -877,41 +875,12 @@ class ReaderPresenter(
|
|||
* Starts the service that updates the last chapter read in sync services. This operation
|
||||
* will run in a background thread and errors are ignored.
|
||||
*/
|
||||
private fun updateTrackChapterRead(readerChapter: ReaderChapter) {
|
||||
private fun updateTrackChapterAfterReading(readerChapter: ReaderChapter) {
|
||||
if (!preferences.autoUpdateTrack()) return
|
||||
val manga = manga ?: return
|
||||
|
||||
val chapterRead = readerChapter.chapter.chapter_number.toInt()
|
||||
|
||||
val trackManager = Injekt.get<TrackManager>()
|
||||
|
||||
// We want these to execute even if the presenter is destroyed so launch on GlobalScope
|
||||
GlobalScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val trackList = db.getTracks(manga).executeAsBlocking()
|
||||
trackList.map { track ->
|
||||
val service = trackManager.getService(track.sync_id)
|
||||
if (service != null && service.isLogged && chapterRead > track.last_chapter_read) {
|
||||
if (!preferences.context.isOnline()) {
|
||||
val mangaId = manga.id ?: return@map
|
||||
val trackings = preferences.trackingsToAddOnline().get().toMutableSet()
|
||||
val currentTracking = trackings.find { it.startsWith("$mangaId:${track.sync_id}:") }
|
||||
trackings.remove(currentTracking)
|
||||
trackings.add("$mangaId:${track.sync_id}:$chapterRead")
|
||||
preferences.trackingsToAddOnline().set(trackings)
|
||||
DelayedTrackingUpdateJob.setupTask(preferences.context)
|
||||
} else {
|
||||
try {
|
||||
track.last_chapter_read = chapterRead
|
||||
service.update(track, true)
|
||||
db.insertTrack(track).executeAsBlocking()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
launchIO {
|
||||
val newChapterRead = readerChapter.chapter.chapter_number.toInt()
|
||||
updateTrackChapterRead(db, preferences, manga?.id, newChapterRead, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
|||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.recents.options.TabbedRecentsOptionsSheet
|
||||
import eu.kanade.tachiyomi.ui.source.browse.ProgressItem
|
||||
import eu.kanade.tachiyomi.util.chapter.updateTrackChapterMarkedAsRead
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.getBottomGestureInsets
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
|
@ -668,6 +669,8 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
}
|
||||
|
||||
override fun markAsRead(position: Int) {
|
||||
val preferences = presenter.preferences
|
||||
val db = presenter.db
|
||||
val item = adapter.getItem(position) as? RecentMangaItem ?: return
|
||||
val chapter = item.chapter
|
||||
val manga = item.mch.manga
|
||||
|
@ -691,11 +694,14 @@ class RecentsController(bundle: Bundle? = null) :
|
|||
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||
super.onDismissed(transientBottomBar, event)
|
||||
if (!undoing && presenter.preferences.removeAfterMarkedAsRead() &&
|
||||
!wasRead
|
||||
) {
|
||||
lastChapterId = chapter.id
|
||||
presenter.deleteChapter(chapter, manga)
|
||||
if (!undoing && !wasRead) {
|
||||
if (preferences.removeAfterMarkedAsRead()) {
|
||||
lastChapterId = chapter.id
|
||||
presenter.deleteChapter(chapter, manga)
|
||||
}
|
||||
updateTrackChapterMarkedAsRead(db, preferences, chapter, manga.id) {
|
||||
(router.backstack.lastOrNull()?.controller as? MangaDetailsController)?.presenter?.fetchTracks()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ import kotlin.math.abs
|
|||
class RecentsPresenter(
|
||||
val preferences: PreferencesHelper = Injekt.get(),
|
||||
val downloadManager: DownloadManager = Injekt.get(),
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
val db: DatabaseHelper = Injekt.get(),
|
||||
private val chapterFilter: ChapterFilter = Injekt.get()
|
||||
) : BaseCoroutinePresenter<RecentsController>(), DownloadQueue.DownloadListener, LibraryServiceListener, DownloadServiceListener {
|
||||
|
||||
|
|
|
@ -33,9 +33,14 @@ class SettingsTrackingController :
|
|||
|
||||
switchPreference {
|
||||
key = Keys.autoUpdateTrack
|
||||
titleRes = R.string.sync_chapters_after_reading
|
||||
titleRes = R.string.update_tracking_after_reading
|
||||
defaultValue = true
|
||||
}
|
||||
switchPreference {
|
||||
key = Keys.trackMarkedAsRead
|
||||
titleRes = R.string.update_tracking_marked_read
|
||||
defaultValue = false
|
||||
}
|
||||
preferenceCategory {
|
||||
titleRes = R.string.services
|
||||
|
||||
|
|
|
@ -3,9 +3,17 @@ package eu.kanade.tachiyomi.util.chapter
|
|||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.DelayedTrackingUpdateJob
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.util.system.isOnline
|
||||
import eu.kanade.tachiyomi.util.system.launchIO
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
/**
|
||||
* Helper method for syncing a remote track with the local chapters, and back
|
||||
|
@ -40,3 +48,68 @@ fun syncChaptersWithTrackServiceTwoWay(db: DatabaseHelper, chapters: List<Chapte
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var trackingJobs = HashMap<Long, Pair<Job?, Int?>>()
|
||||
|
||||
/**
|
||||
* Starts the service that updates the last chapter read in sync services. This operation
|
||||
* will run in a background thread and errors are ignored.
|
||||
*/
|
||||
fun updateTrackChapterMarkedAsRead(
|
||||
db: DatabaseHelper,
|
||||
preferences: PreferencesHelper,
|
||||
newLastChapter: Chapter?,
|
||||
mangaId: Long?,
|
||||
delay: Long = 3000,
|
||||
fetchTracks: (suspend () -> Unit)? = null
|
||||
) {
|
||||
if (!preferences.trackMarkedAsRead()) return
|
||||
mangaId ?: return
|
||||
|
||||
val newChapterRead = newLastChapter?.chapter_number?.toInt() ?: 0
|
||||
|
||||
// To avoid unnecessary calls if multiple marked as read for same manga
|
||||
if (trackingJobs[mangaId]?.second ?: 0 < newChapterRead) {
|
||||
trackingJobs[mangaId]?.first?.cancel()
|
||||
|
||||
// We want these to execute even if the presenter is destroyed
|
||||
trackingJobs[mangaId] = launchIO {
|
||||
delay(delay)
|
||||
updateTrackChapterRead(db, preferences, mangaId, newChapterRead)
|
||||
fetchTracks?.invoke()
|
||||
trackingJobs.remove(mangaId)
|
||||
} to newChapterRead
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateTrackChapterRead(
|
||||
db: DatabaseHelper,
|
||||
preferences: PreferencesHelper,
|
||||
mangaId: Long?,
|
||||
newChapterRead: Int,
|
||||
retryWhenOnline: Boolean = false
|
||||
) {
|
||||
val trackManager = Injekt.get<TrackManager>()
|
||||
val trackList = db.getTracks(mangaId).executeAsBlocking()
|
||||
trackList.map { track ->
|
||||
val service = trackManager.getService(track.sync_id)
|
||||
if (service != null && service.isLogged && newChapterRead > track.last_chapter_read) {
|
||||
if (retryWhenOnline && !preferences.context.isOnline()) {
|
||||
val trackings = preferences.trackingsToAddOnline().get().toMutableSet()
|
||||
val currentTracking = trackings.find { it.startsWith("$mangaId:${track.sync_id}:") }
|
||||
trackings.remove(currentTracking)
|
||||
trackings.add("$mangaId:${track.sync_id}:$newChapterRead")
|
||||
preferences.trackingsToAddOnline().set(trackings)
|
||||
DelayedTrackingUpdateJob.setupTask(preferences.context)
|
||||
} else if (preferences.context.isOnline()) {
|
||||
try {
|
||||
track.last_chapter_read = newChapterRead
|
||||
service.update(track, true)
|
||||
db.insertTrack(track).executeAsBlocking()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -562,7 +562,8 @@
|
|||
<string name="tracking_info">One-way sync to update the chapter progress in tracking services. Set up tracking for individual manga entries from their tracking button.</string>
|
||||
<string name="enhanced_services">Enhanced services</string>
|
||||
<string name="enhanced_tracking_info">Services that provide enhanced features for specific sources. Manga are automatically tracked when added to your library.</string>
|
||||
<string name="sync_chapters_after_reading">Sync chapters after reading</string>
|
||||
<string name="update_tracking_after_reading">Update tracking after reading</string>
|
||||
<string name="update_tracking_marked_read">Update tracking when marked as read</string>
|
||||
<string name="track_when_adding_to_library">Track when adding to library</string>
|
||||
<string name="only_applies_silent_trackers">Only applies to silent trackers, such as Komga</string>
|
||||
<string name="reading">Reading</string>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue