Move tracker binding logic to interactor
This commit is contained in:
parent
4b225a4ff1
commit
69223df27c
5 changed files with 97 additions and 88 deletions
|
@ -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()) }
|
||||||
|
|
|
@ -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" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue