Move tracker binding logic to interactor

This commit is contained in:
arkon 2023-11-04 17:05:38 -04:00
parent 4b225a4ff1
commit 69223df27c
5 changed files with 97 additions and 88 deletions

View file

@ -118,7 +118,7 @@ class DomainModule : InjektModule {
addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) } addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
addFactory { TrackChapter(get(), get(), get(), get()) } addFactory { TrackChapter(get(), get(), get(), get()) }
addFactory { AddTracks(get(), get(), get()) } addFactory { AddTracks(get(), get(), get(), get()) }
addFactory { RefreshTracks(get(), get(), get(), get()) } addFactory { RefreshTracks(get(), get(), get(), get()) }
addFactory { DeleteTrack(get()) } addFactory { DeleteTrack(get()) }
addFactory { GetTracksPerManga(get()) } addFactory { GetTracksPerManga(get()) }

View file

@ -1,45 +1,104 @@
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.toDomainTrack import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.EnhancedTracker import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.lang.withNonCancellableContext import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack import tachiyomi.domain.track.interactor.InsertTrack
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.time.ZoneOffset
class AddTracks( class AddTracks(
private val getTracks: GetTracks, private val getTracks: GetTracks,
private val insertTrack: InsertTrack, private val insertTrack: InsertTrack,
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack, private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
private val getChaptersByMangaId: GetChaptersByMangaId,
) { ) {
suspend fun bindEnhancedTracks(manga: Manga, source: Source) = withNonCancellableContext { // TODO: update all trackers based on common data
getTracks.await(manga.id) suspend fun bind(tracker: Tracker, item: Track, mangaId: Long) = withNonCancellableContext {
.filterIsInstance<EnhancedTracker>() withIOContext {
.filter { it.accept(source) } val allChapters = getChaptersByMangaId.await(mangaId)
.forEach { service -> val hasReadChapters = allChapters.any { it.read }
try { tracker.bind(item, hasReadChapters)
service.match(manga)?.let { track ->
track.manga_id = manga.id
(service as Tracker).bind(track)
insertTrack.await(track.toDomainTrack()!!)
syncChapterProgressWithTrack.await( var track = item.toDomainTrack(idRequired = false) ?: return@withIOContext
manga.id,
track.toDomainTrack()!!, insertTrack.await(track)
service,
// TODO: merge into [SyncChapterProgressWithTrack]?
// Update chapter progress if newer chapters marked read locally
if (hasReadChapters) {
val latestLocalReadChapterNumber = allChapters
.sortedBy { it.chapterNumber }
.takeWhile { it.read }
.lastOrNull()
?.chapterNumber ?: -1.0
if (latestLocalReadChapterNumber > track.lastChapterRead) {
track = track.copy(
lastChapterRead = latestLocalReadChapterNumber,
)
tracker.setRemoteLastChapterRead(track.toDbTrack(), latestLocalReadChapterNumber.toInt())
}
if (track.startDate <= 0) {
val firstReadChapterDate = Injekt.get<GetHistory>().await(mangaId)
.sortedBy { it.readAt }
.firstOrNull()
?.readAt
firstReadChapterDate?.let {
val startDate = firstReadChapterDate.time.convertEpochMillisZone(ZoneOffset.systemDefault(), ZoneOffset.UTC)
track = track.copy(
startDate = startDate,
) )
tracker.setRemoteStartDate(track.toDbTrack(), startDate)
} }
} catch (e: Exception) {
logcat(
LogPriority.WARN,
e,
) { "Could not match manga: ${manga.title} with service $service" }
} }
} }
syncChapterProgressWithTrack.await(mangaId, track, tracker)
}
}
suspend fun bindEnhancedTrackers(manga: Manga, source: Source) = withNonCancellableContext {
withIOContext {
getTracks.await(manga.id)
.filterIsInstance<EnhancedTracker>()
.filter { it.accept(source) }
.forEach { service ->
try {
service.match(manga)?.let { track ->
track.manga_id = manga.id
(service as Tracker).bind(track)
insertTrack.await(track.toDomainTrack()!!)
syncChapterProgressWithTrack.await(
manga.id,
track.toDomainTrack()!!,
service,
)
}
} catch (e: Exception) {
logcat(
LogPriority.WARN,
e,
) { "Could not match manga: ${manga.title} with service $service" }
}
}
}
} }
} }

View file

@ -2,26 +2,21 @@ package eu.kanade.tachiyomi.data.track
import android.app.Application import android.app.Application
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.domain.track.model.toDbTrack
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.track.service.TrackPreferences
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import logcat.LogPriority import logcat.LogPriority
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.track.interactor.InsertTrack import tachiyomi.domain.track.interactor.InsertTrack
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.time.ZoneOffset
import tachiyomi.domain.track.model.Track as DomainTrack import tachiyomi.domain.track.model.Track as DomainTrack
abstract class BaseTracker( abstract class BaseTracker(
@ -31,8 +26,8 @@ abstract class BaseTracker(
val trackPreferences: TrackPreferences by injectLazy() val trackPreferences: TrackPreferences by injectLazy()
val networkService: NetworkHelper by injectLazy() val networkService: NetworkHelper by injectLazy()
private val addTracks: AddTracks by injectLazy()
private val insertTrack: InsertTrack by injectLazy() private val insertTrack: InsertTrack by injectLazy()
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack by injectLazy()
override val client: OkHttpClient override val client: OkHttpClient
get() = networkService.client get() = networkService.client
@ -66,53 +61,10 @@ abstract class BaseTracker(
trackPreferences.setCredentials(this, username, password) trackPreferences.setCredentials(this, username, password)
} }
// TODO: move this to an interactor, and update all trackers based on common data
override suspend fun register(item: Track, mangaId: Long) { override suspend fun register(item: Track, mangaId: Long) {
item.manga_id = mangaId item.manga_id = mangaId
try { try {
withIOContext { addTracks.bind(this, item, mangaId)
val allChapters = Injekt.get<GetChaptersByMangaId>().await(mangaId)
val hasReadChapters = allChapters.any { it.read }
bind(item, hasReadChapters)
var track = item.toDomainTrack(idRequired = false) ?: return@withIOContext
insertTrack.await(track)
// TODO: merge into [SyncChapterProgressWithTrack]?
// Update chapter progress if newer chapters marked read locally
if (hasReadChapters) {
val latestLocalReadChapterNumber = allChapters
.sortedBy { it.chapterNumber }
.takeWhile { it.read }
.lastOrNull()
?.chapterNumber ?: -1.0
if (latestLocalReadChapterNumber > track.lastChapterRead) {
track = track.copy(
lastChapterRead = latestLocalReadChapterNumber,
)
setRemoteLastChapterRead(track.toDbTrack(), latestLocalReadChapterNumber.toInt())
}
if (track.startDate <= 0) {
val firstReadChapterDate = Injekt.get<GetHistory>().await(mangaId)
.sortedBy { it.readAt }
.firstOrNull()
?.readAt
firstReadChapterDate?.let {
val startDate = firstReadChapterDate.time.convertEpochMillisZone(ZoneOffset.systemDefault(), ZoneOffset.UTC)
track = track.copy(
startDate = startDate,
)
setRemoteStartDate(track.toDbTrack(), startDate)
}
}
}
syncChapterProgressWithTrack.await(mangaId, track, this@BaseTracker)
}
} catch (e: Throwable) { } catch (e: Throwable) {
withUIContext { Injekt.get<Application>().toast(e.message) } withUIContext { Injekt.get<Application>().toast(e.message) }
} }
@ -123,7 +75,7 @@ abstract class BaseTracker(
if (track.status == getCompletionStatus() && track.total_chapters != 0) { if (track.status == getCompletionStatus() && track.total_chapters != 0) {
track.last_chapter_read = track.total_chapters.toFloat() track.last_chapter_read = track.total_chapters.toFloat()
} }
withIOContext { updateRemote(track) } updateRemote(track)
} }
override suspend fun setRemoteLastChapterRead(track: Track, chapterNumber: Int) { override suspend fun setRemoteLastChapterRead(track: Track, chapterNumber: Int) {
@ -135,35 +87,33 @@ abstract class BaseTracker(
track.status = getCompletionStatus() track.status = getCompletionStatus()
track.finished_reading_date = System.currentTimeMillis() track.finished_reading_date = System.currentTimeMillis()
} }
withIOContext { updateRemote(track) } updateRemote(track)
} }
override suspend fun setRemoteScore(track: Track, scoreString: String) { override suspend fun setRemoteScore(track: Track, scoreString: String) {
track.score = indexToScore(getScoreList().indexOf(scoreString)) track.score = indexToScore(getScoreList().indexOf(scoreString))
withIOContext { updateRemote(track) } updateRemote(track)
} }
override suspend fun setRemoteStartDate(track: Track, epochMillis: Long) { override suspend fun setRemoteStartDate(track: Track, epochMillis: Long) {
track.started_reading_date = epochMillis track.started_reading_date = epochMillis
withIOContext { updateRemote(track) } updateRemote(track)
} }
override suspend fun setRemoteFinishDate(track: Track, epochMillis: Long) { override suspend fun setRemoteFinishDate(track: Track, epochMillis: Long) {
track.finished_reading_date = epochMillis track.finished_reading_date = epochMillis
withIOContext { updateRemote(track) } updateRemote(track)
} }
private suspend fun updateRemote(track: Track) { private suspend fun updateRemote(track: Track): Unit = withIOContext {
withIOContext { try {
try { update(track)
update(track) track.toDomainTrack(idRequired = false)?.let {
track.toDomainTrack(idRequired = false)?.let { insertTrack.await(it)
insertTrack.await(it)
}
} catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to update remote track data id=$id" }
withUIContext { Injekt.get<Application>().toast(e.message) }
} }
} catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to update remote track data id=$id" }
withUIContext { Injekt.get<Application>().toast(e.message) }
} }
} }
} }

View file

@ -233,7 +233,7 @@ class BrowseSourceScreenModel(
new = new.removeCovers(coverCache) new = new.removeCovers(coverCache)
} else { } else {
setMangaDefaultChapterFlags.await(manga) setMangaDefaultChapterFlags.await(manga)
addTracks.bindEnhancedTracks(manga, source) addTracks.bindEnhancedTrackers(manga, source)
} }
updateManga.await(new.toMangaUpdate()) updateManga.await(new.toMangaUpdate())

View file

@ -319,7 +319,7 @@ class MangaScreenModel(
} }
// Finally match with enhanced tracking when available // Finally match with enhanced tracking when available
addTracks.bindEnhancedTracks(manga, state.source) addTracks.bindEnhancedTrackers(manga, state.source)
} }
} }
} }