瀏覽代碼

Refactor some tracking-related logic

arkon 1 年之前
父節點
當前提交
98d6ce2eaf
共有 28 個文件被更改,包括 104 次插入151 次删除
  1. 2 2
      app/src/main/java/eu/kanade/domain/DomainModule.kt
  2. 1 1
      app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChapterProgressWithTrack.kt
  3. 18 13
      app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt
  4. 1 1
      app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt
  5. 10 12
      app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt
  6. 1 1
      app/src/main/java/eu/kanade/presentation/more/settings/widget/TrackingPreferenceWidget.kt
  7. 1 2
      app/src/main/java/eu/kanade/presentation/track/components/TrackLogoIcon.kt
  8. 1 1
      app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupFileValidator.kt
  9. 9 2
      app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt
  10. 1 1
      app/src/main/java/eu/kanade/tachiyomi/data/track/TrackManager.kt
  11. 5 9
      app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt
  12. 1 4
      app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt
  13. 1 4
      app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt
  14. 1 4
      app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt
  15. 8 8
      app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt
  16. 1 4
      app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt
  17. 1 5
      app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt
  18. 1 1
      app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt
  19. 1 4
      app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt
  20. 1 4
      app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt
  21. 1 4
      app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt
  22. 2 2
      app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt
  23. 1 4
      app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt
  24. 8 8
      app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/TachideskApi.kt
  25. 3 3
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
  26. 1 1
      app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
  27. 22 37
      app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt
  28. 0 9
      i18n/src/main/res/values/strings.xml

+ 2 - 2
app/src/main/java/eu/kanade/domain/DomainModule.kt

@@ -1,8 +1,8 @@
 package eu.kanade.domain
 
 import eu.kanade.domain.chapter.interactor.SetReadStatus
+import eu.kanade.domain.chapter.interactor.SyncChapterProgressWithTrack
 import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
-import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
 import eu.kanade.domain.download.interactor.DeleteDownload
 import eu.kanade.domain.extension.interactor.GetExtensionLanguages
 import eu.kanade.domain.extension.interactor.GetExtensionSources
@@ -127,7 +127,7 @@ class DomainModule : InjektModule {
         addFactory { SetReadStatus(get(), get(), get(), get()) }
         addFactory { ShouldUpdateDbChapter() }
         addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get()) }
-        addFactory { SyncChaptersWithTrackServiceTwoWay(get(), get(), get()) }
+        addFactory { SyncChapterProgressWithTrack(get(), get(), get()) }
 
         addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
         addFactory { GetHistory(get()) }

+ 1 - 1
app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithTrackServiceTwoWay.kt → app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChapterProgressWithTrack.kt

@@ -11,7 +11,7 @@ import tachiyomi.domain.chapter.model.toChapterUpdate
 import tachiyomi.domain.track.interactor.InsertTrack
 import tachiyomi.domain.track.model.Track
 
-class SyncChaptersWithTrackServiceTwoWay(
+class SyncChapterProgressWithTrack(
     private val updateChapter: UpdateChapter,
     private val insertTrack: InsertTrack,
     private val getChapterByMangaId: GetChapterByMangaId,

+ 18 - 13
app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt

@@ -1,14 +1,13 @@
 package eu.kanade.domain.track.interactor
 
-import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
+import eu.kanade.domain.chapter.interactor.SyncChapterProgressWithTrack
 import eu.kanade.domain.track.model.toDbTrack
 import eu.kanade.domain.track.model.toDomainTrack
 import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackService
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.supervisorScope
-import logcat.LogPriority
-import tachiyomi.core.util.system.logcat
 import tachiyomi.domain.track.interactor.GetTracks
 import tachiyomi.domain.track.interactor.InsertTrack
 
@@ -16,28 +15,34 @@ class RefreshTracks(
     private val getTracks: GetTracks,
     private val trackManager: TrackManager,
     private val insertTrack: InsertTrack,
-    private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay,
+    private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
 ) {
 
-    suspend fun await(mangaId: Long) {
-        supervisorScope {
-            getTracks.await(mangaId)
+    /**
+     * Fetches updated tracking data from all logged in trackers.
+     *
+     * @return Failed updates.
+     */
+    suspend fun await(mangaId: Long): List<Pair<TrackService?, Throwable>> {
+        return supervisorScope {
+            return@supervisorScope getTracks.await(mangaId)
                 .map { track ->
                     async {
                         val service = trackManager.getService(track.syncId)
-                        if (service != null && service.isLoggedIn) {
-                            try {
+                        return@async try {
+                            if (service?.isLoggedIn == true) {
                                 val updatedTrack = service.refresh(track.toDbTrack())
                                 insertTrack.await(updatedTrack.toDomainTrack()!!)
-                                syncChaptersWithTrackServiceTwoWay.await(mangaId, track, service)
-                            } catch (e: Throwable) {
-                                // Ignore errors and continue
-                                logcat(LogPriority.ERROR, e)
+                                syncChapterProgressWithTrack.await(mangaId, track, service)
                             }
+                            null
+                        } catch (e: Throwable) {
+                            service to e
                         }
                     }
                 }
                 .awaitAll()
+                .filterNotNull()
         }
     }
 }

+ 1 - 1
app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt

@@ -127,7 +127,7 @@ private fun ColumnScope.FilterPage(
             trackServices.map { service ->
                 val filterTracker by screenModel.libraryPreferences.filterTracking(service.id.toInt()).collectAsState()
                 TriStateItem(
-                    label = stringResource(service.nameRes()),
+                    label = service.name,
                     state = filterTracker,
                     onClick = { screenModel.toggleTracker(service.id.toInt()) },
                 )

+ 10 - 12
app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt

@@ -114,9 +114,7 @@ object SettingsTrackingScreen : SearchableSettings {
         if (enhancedTrackers.second.isNotEmpty()) {
             val missingSourcesInfo = stringResource(
                 R.string.enhanced_services_not_installed,
-                enhancedTrackers.second
-                    .map { stringResource(it.nameRes()) }
-                    .joinToString(),
+                enhancedTrackers.second.joinToString { it.name },
             )
             enhancedTrackerInfo += "\n\n$missingSourcesInfo"
         }
@@ -130,37 +128,37 @@ object SettingsTrackingScreen : SearchableSettings {
                 title = stringResource(R.string.services),
                 preferenceItems = listOf(
                     Preference.PreferenceItem.TrackingPreference(
-                        title = stringResource(trackManager.myAnimeList.nameRes()),
+                        title = trackManager.myAnimeList.name,
                         service = trackManager.myAnimeList,
                         login = { context.openInBrowser(MyAnimeListApi.authUrl(), forceDefaultBrowser = true) },
                         logout = { dialog = LogoutDialog(trackManager.myAnimeList) },
                     ),
                     Preference.PreferenceItem.TrackingPreference(
-                        title = stringResource(trackManager.aniList.nameRes()),
+                        title = trackManager.aniList.name,
                         service = trackManager.aniList,
                         login = { context.openInBrowser(AnilistApi.authUrl(), forceDefaultBrowser = true) },
                         logout = { dialog = LogoutDialog(trackManager.aniList) },
                     ),
                     Preference.PreferenceItem.TrackingPreference(
-                        title = stringResource(trackManager.kitsu.nameRes()),
+                        title = trackManager.kitsu.name,
                         service = trackManager.kitsu,
                         login = { dialog = LoginDialog(trackManager.kitsu, R.string.email) },
                         logout = { dialog = LogoutDialog(trackManager.kitsu) },
                     ),
                     Preference.PreferenceItem.TrackingPreference(
-                        title = stringResource(trackManager.mangaUpdates.nameRes()),
+                        title = trackManager.mangaUpdates.name,
                         service = trackManager.mangaUpdates,
                         login = { dialog = LoginDialog(trackManager.mangaUpdates, R.string.username) },
                         logout = { dialog = LogoutDialog(trackManager.mangaUpdates) },
                     ),
                     Preference.PreferenceItem.TrackingPreference(
-                        title = stringResource(trackManager.shikimori.nameRes()),
+                        title = trackManager.shikimori.name,
                         service = trackManager.shikimori,
                         login = { context.openInBrowser(ShikimoriApi.authUrl(), forceDefaultBrowser = true) },
                         logout = { dialog = LogoutDialog(trackManager.shikimori) },
                     ),
                     Preference.PreferenceItem.TrackingPreference(
-                        title = stringResource(trackManager.bangumi.nameRes()),
+                        title = trackManager.bangumi.name,
                         service = trackManager.bangumi,
                         login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) },
                         logout = { dialog = LogoutDialog(trackManager.bangumi) },
@@ -173,7 +171,7 @@ object SettingsTrackingScreen : SearchableSettings {
                 preferenceItems = enhancedTrackers.first
                     .map { service ->
                         Preference.PreferenceItem.TrackingPreference(
-                            title = stringResource(service.nameRes()),
+                            title = service.name,
                             service = service,
                             login = { (service as EnhancedTrackService).loginNoop() },
                             logout = service::logout,
@@ -202,7 +200,7 @@ object SettingsTrackingScreen : SearchableSettings {
             title = {
                 Row(verticalAlignment = Alignment.CenterVertically) {
                     Text(
-                        text = stringResource(R.string.login_title, stringResource(service.nameRes())),
+                        text = stringResource(R.string.login_title, service.name),
                         modifier = Modifier.weight(1f),
                     )
                     IconButton(onClick = onDismissRequest) {
@@ -310,7 +308,7 @@ object SettingsTrackingScreen : SearchableSettings {
             onDismissRequest = onDismissRequest,
             title = {
                 Text(
-                    text = stringResource(R.string.logout_title, stringResource(service.nameRes())),
+                    text = stringResource(R.string.logout_title, service.name),
                     textAlign = TextAlign.Center,
                     modifier = Modifier.fillMaxWidth(),
                 )

+ 1 - 1
app/src/main/java/eu/kanade/presentation/more/settings/widget/TrackingPreferenceWidget.kt

@@ -40,7 +40,7 @@ fun TrackingPreferenceWidget(
         ) {
             TrackLogoIcon(service)
             Text(
-                text = stringResource(service.nameRes()),
+                text = service.name,
                 modifier = Modifier
                     .weight(1f)
                     .padding(horizontal = 16.dp),

+ 1 - 2
app/src/main/java/eu/kanade/presentation/track/components/TrackLogoIcon.kt

@@ -11,7 +11,6 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import eu.kanade.tachiyomi.data.track.TrackService
 import tachiyomi.presentation.core.util.clickableNoIndication
@@ -36,7 +35,7 @@ fun TrackLogoIcon(
     ) {
         Image(
             painter = painterResource(service.getLogo()),
-            contentDescription = stringResource(service.nameRes()),
+            contentDescription = service.name,
         )
     }
 }

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupFileValidator.kt

@@ -52,7 +52,7 @@ class BackupFileValidator(
         val missingTrackers = trackers
             .mapNotNull { trackManager.getService(it.toLong()) }
             .filter { !it.isLoggedIn }
-            .map { context.getString(it.nameRes()) }
+            .map { it.name }
             .sorted()
 
         return Results(missingSources, missingTrackers)

+ 9 - 2
app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt

@@ -283,7 +283,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
                                     }
 
                                     if (libraryPreferences.autoUpdateTrackers().get()) {
-                                        refreshTracks.await(manga.id)
+                                        refreshTracks(manga.id)
                                     }
                                 }
                             }
@@ -409,13 +409,20 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
 
                 val manga = libraryManga.manga
                 notifier.showProgressNotification(listOf(manga), progressCount++, mangaToUpdate.size)
-                refreshTracks.await(manga.id)
+                refreshTracks(manga.id)
             }
 
             notifier.cancelProgressNotification()
         }
     }
 
+    private suspend fun refreshTracks(mangaId: Long) {
+        refreshTracks.await(mangaId).forEach { (_, e) ->
+            // Ignore errors and continue
+            logcat(LogPriority.ERROR, e)
+        }
+    }
+
     private suspend fun withUpdateNotification(
         updatingManga: CopyOnWriteArrayList<Manga>,
         completed: AtomicInteger,

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/track/TrackManager.kt

@@ -30,7 +30,7 @@ class TrackManager(context: Context) {
     val kitsu = Kitsu(KITSU)
     val shikimori = Shikimori(SHIKIMORI)
     val bangumi = Bangumi(BANGUMI)
-    val komga = Komga(context, KOMGA)
+    val komga = Komga(KOMGA)
     val mangaUpdates = MangaUpdates(MANGA_UPDATES)
     val kavita = Kavita(context, KAVITA)
     val suwayomi = Suwayomi(SUWAYOMI)

+ 5 - 9
app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt

@@ -5,7 +5,7 @@ import androidx.annotation.CallSuper
 import androidx.annotation.ColorInt
 import androidx.annotation.DrawableRes
 import androidx.annotation.StringRes
-import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
+import eu.kanade.domain.chapter.interactor.SyncChapterProgressWithTrack
 import eu.kanade.domain.track.model.toDbTrack
 import eu.kanade.domain.track.model.toDomainTrack
 import eu.kanade.domain.track.service.TrackPreferences
@@ -28,20 +28,16 @@ import uy.kohesive.injekt.injectLazy
 import java.time.ZoneOffset
 import tachiyomi.domain.track.model.Track as DomainTrack
 
-abstract class TrackService(val id: Long) {
+abstract class TrackService(val id: Long, val name: String) {
 
     val trackPreferences: TrackPreferences by injectLazy()
     val networkService: NetworkHelper by injectLazy()
     private val insertTrack: InsertTrack by injectLazy()
-    private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay by injectLazy()
+    private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack by injectLazy()
 
     open val client: OkHttpClient
         get() = networkService.client
 
-    // Name of the manga sync service to display
-    @StringRes
-    abstract fun nameRes(): Int
-
     // Application and remote support for reading dates
     open val supportsReadingDates: Boolean = false
 
@@ -103,7 +99,7 @@ abstract class TrackService(val id: Long) {
     }
 
     // TODO: move this to an interactor, and update all trackers based on common data
-    suspend fun registerTracking(item: Track, mangaId: Long) {
+    suspend fun register(item: Track, mangaId: Long) {
         item.manga_id = mangaId
         try {
             withIOContext {
@@ -147,7 +143,7 @@ abstract class TrackService(val id: Long) {
                     }
                 }
 
-                syncChaptersWithTrackServiceTwoWay.await(mangaId, track, this@TrackService)
+                syncChapterProgressWithTrack.await(mangaId, track, this@TrackService)
             }
         } catch (e: Throwable) {
             withUIContext { Injekt.get<Application>().toast(e.message) }

+ 1 - 4
app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt

@@ -12,7 +12,7 @@ import kotlinx.serialization.json.Json
 import uy.kohesive.injekt.injectLazy
 import tachiyomi.domain.track.model.Track as DomainTrack
 
-class Anilist(id: Long) : TrackService(id), DeletableTrackService {
+class Anilist(id: Long) : TrackService(id, "AniList"), DeletableTrackService {
 
     companion object {
         const val READING = 1
@@ -49,9 +49,6 @@ class Anilist(id: Long) : TrackService(id), DeletableTrackService {
         }
     }
 
-    @StringRes
-    override fun nameRes() = R.string.tracker_anilist
-
     override fun getLogo() = R.drawable.ic_tracker_anilist
 
     override fun getLogoColor() = Color.rgb(18, 25, 35)

+ 1 - 4
app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt

@@ -10,7 +10,7 @@ import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
 import uy.kohesive.injekt.injectLazy
 
-class Bangumi(id: Long) : TrackService(id) {
+class Bangumi(id: Long) : TrackService(id, "Bangumi") {
 
     private val json: Json by injectLazy()
 
@@ -18,9 +18,6 @@ class Bangumi(id: Long) : TrackService(id) {
 
     private val api by lazy { BangumiApi(client, interceptor) }
 
-    @StringRes
-    override fun nameRes() = R.string.tracker_bangumi
-
     override fun getScoreList(): List<String> {
         return IntRange(0, 10).map(Int::toString)
     }

+ 1 - 4
app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt

@@ -14,7 +14,7 @@ import tachiyomi.domain.manga.model.Manga
 import java.security.MessageDigest
 import tachiyomi.domain.track.model.Track as DomainTrack
 
-class Kavita(private val context: Context, id: Long) : TrackService(id), EnhancedTrackService {
+class Kavita(private val context: Context, id: Long) : TrackService(id, "Kavita"), EnhancedTrackService {
 
     companion object {
         const val UNREAD = 1
@@ -27,9 +27,6 @@ class Kavita(private val context: Context, id: Long) : TrackService(id), Enhance
     private val interceptor by lazy { KavitaInterceptor(this) }
     val api by lazy { KavitaApi(client, interceptor) }
 
-    @StringRes
-    override fun nameRes() = R.string.tracker_kavita
-
     override fun getLogo(): Int = R.drawable.ic_tracker_kavita
 
     override fun getLogoColor() = Color.rgb(74, 198, 148)

+ 8 - 8
app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt

@@ -115,8 +115,8 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
     }
 
     private fun getLatestChapterRead(url: String): Float {
-        val serieId = getIdFromUrl(url)
-        val requestUrl = "${getApiFromUrl(url)}/Tachiyomi/latest-chapter?seriesId=$serieId"
+        val seriesId = getIdFromUrl(url)
+        val requestUrl = "${getApiFromUrl(url)}/Tachiyomi/latest-chapter?seriesId=$seriesId"
         try {
             with(json) {
                 authClient.newCall(GET(requestUrl)).execute().use {
@@ -137,21 +137,21 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
 
     suspend fun getTrackSearch(url: String): TrackSearch = withIOContext {
         try {
-            val serieDto: SeriesDto = with(json) {
+            val seriesDto: SeriesDto = with(json) {
                 authClient.newCall(GET(url))
                     .awaitSuccess()
                     .parseAs()
             }
 
-            val track = serieDto.toTrack()
+            val track = seriesDto.toTrack()
             track.apply {
-                cover_url = serieDto.thumbnail_url.toString()
+                cover_url = seriesDto.thumbnail_url.toString()
                 tracking_url = url
                 total_chapters = getTotalChapters(url)
 
-                title = serieDto.name
-                status = when (serieDto.pagesRead) {
-                    serieDto.pages -> Kavita.COMPLETED
+                title = seriesDto.name
+                status = when (seriesDto.pagesRead) {
+                    seriesDto.pages -> Kavita.COMPLETED
                     0 -> Kavita.UNREAD
                     else -> Kavita.READING
                 }

+ 1 - 4
app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt

@@ -12,7 +12,7 @@ import kotlinx.serialization.json.Json
 import uy.kohesive.injekt.injectLazy
 import java.text.DecimalFormat
 
-class Kitsu(id: Long) : TrackService(id), DeletableTrackService {
+class Kitsu(id: Long) : TrackService(id, "Kitsu"), DeletableTrackService {
 
     companion object {
         const val READING = 1
@@ -22,9 +22,6 @@ class Kitsu(id: Long) : TrackService(id), DeletableTrackService {
         const val PLAN_TO_READ = 5
     }
 
-    @StringRes
-    override fun nameRes() = R.string.tracker_kitsu
-
     override val supportsReadingDates: Boolean = true
 
     private val json: Json by injectLazy()

+ 1 - 5
app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt

@@ -1,6 +1,5 @@
 package eu.kanade.tachiyomi.data.track.komga
 
-import android.content.Context
 import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
@@ -14,7 +13,7 @@ import okhttp3.OkHttpClient
 import tachiyomi.domain.manga.model.Manga
 import tachiyomi.domain.track.model.Track as DomainTrack
 
-class Komga(private val context: Context, id: Long) : TrackService(id), EnhancedTrackService {
+class Komga(id: Long) : TrackService(id, "Komga"), EnhancedTrackService {
 
     companion object {
         const val UNREAD = 1
@@ -29,9 +28,6 @@ class Komga(private val context: Context, id: Long) : TrackService(id), Enhanced
 
     val api by lazy { KomgaApi(client) }
 
-    @StringRes
-    override fun nameRes() = R.string.tracker_komga
-
     override fun getLogo() = R.drawable.ic_tracker_komga
 
     override fun getLogoColor() = Color.rgb(51, 37, 50)

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt

@@ -17,7 +17,7 @@ import tachiyomi.core.util.lang.withIOContext
 import tachiyomi.core.util.system.logcat
 import uy.kohesive.injekt.injectLazy
 
-const val READLIST_API = "/api/v1/readlists"
+private const val READLIST_API = "/api/v1/readlists"
 
 class KomgaApi(private val client: OkHttpClient) {
 

+ 1 - 4
app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt

@@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.data.track.mangaupdates.dto.copyTo
 import eu.kanade.tachiyomi.data.track.mangaupdates.dto.toTrackSearch
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 
-class MangaUpdates(id: Long) : TrackService(id), DeletableTrackService {
+class MangaUpdates(id: Long) : TrackService(id, "MangaUpdates"), DeletableTrackService {
 
     companion object {
         const val READING_LIST = 0
@@ -24,9 +24,6 @@ class MangaUpdates(id: Long) : TrackService(id), DeletableTrackService {
 
     private val api by lazy { MangaUpdatesApi(interceptor, client) }
 
-    @StringRes
-    override fun nameRes(): Int = R.string.tracker_manga_updates
-
     override fun getLogo(): Int = R.drawable.ic_manga_updates
 
     override fun getLogoColor(): Int = Color.rgb(146, 160, 173)

+ 1 - 4
app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt

@@ -11,7 +11,7 @@ import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
 import uy.kohesive.injekt.injectLazy
 
-class MyAnimeList(id: Long) : TrackService(id), DeletableTrackService {
+class MyAnimeList(id: Long) : TrackService(id, "MyAnimeList"), DeletableTrackService {
 
     companion object {
         const val READING = 1
@@ -30,9 +30,6 @@ class MyAnimeList(id: Long) : TrackService(id), DeletableTrackService {
     private val interceptor by lazy { MyAnimeListInterceptor(this, getPassword()) }
     private val api by lazy { MyAnimeListApi(client, interceptor) }
 
-    @StringRes
-    override fun nameRes() = R.string.tracker_myanimelist
-
     override val supportsReadingDates: Boolean = true
 
     override fun getLogo() = R.drawable.ic_tracker_mal

+ 1 - 4
app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt

@@ -11,7 +11,7 @@ import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
 import uy.kohesive.injekt.injectLazy
 
-class Shikimori(id: Long) : TrackService(id), DeletableTrackService {
+class Shikimori(id: Long) : TrackService(id, "Shikimori"), DeletableTrackService {
 
     companion object {
         const val READING = 1
@@ -28,9 +28,6 @@ class Shikimori(id: Long) : TrackService(id), DeletableTrackService {
 
     private val api by lazy { ShikimoriApi(client, interceptor) }
 
-    @StringRes
-    override fun nameRes() = R.string.tracker_shikimori
-
     override fun getScoreList(): List<String> {
         return IntRange(0, 10).map(Int::toString)
     }

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt

@@ -122,7 +122,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
         }
     }
 
-    suspend fun findLibManga(track: Track, user_id: String): Track? {
+    suspend fun findLibManga(track: Track, userId: String): Track? {
         return withIOContext {
             val urlMangas = "$apiUrl/mangas".toUri().buildUpon()
                 .appendPath(track.media_id.toString())
@@ -134,7 +134,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
             }
 
             val url = "$apiUrl/v2/user_rates".toUri().buildUpon()
-                .appendQueryParameter("user_id", user_id)
+                .appendQueryParameter("user_id", userId)
                 .appendQueryParameter("target_id", track.media_id.toString())
                 .appendQueryParameter("target_type", "Manga")
                 .build()

+ 1 - 4
app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt

@@ -11,13 +11,10 @@ import eu.kanade.tachiyomi.source.Source
 import tachiyomi.domain.manga.model.Manga as DomainManga
 import tachiyomi.domain.track.model.Track as DomainTrack
 
-class Suwayomi(id: Long) : TrackService(id), EnhancedTrackService {
+class Suwayomi(id: Long) : TrackService(id, "Suwayomi"), EnhancedTrackService {
 
     val api by lazy { TachideskApi() }
 
-    @StringRes
-    override fun nameRes() = R.string.tracker_suwayomi
-
     override fun getLogo() = R.drawable.ic_tracker_suwayomi
 
     override fun getLogoColor() = Color.rgb(255, 35, 35) // TODO

+ 8 - 8
app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/TachideskApi.kt

@@ -28,19 +28,19 @@ class TachideskApi {
     private val network: NetworkHelper by injectLazy()
     private val json: Json by injectLazy()
 
-    val client: OkHttpClient =
+    private val client: OkHttpClient =
         network.client.newBuilder()
             .dns(Dns.SYSTEM) // don't use DNS over HTTPS as it breaks IP addressing
             .build()
 
-    fun headersBuilder(): Headers.Builder = Headers.Builder().apply {
+    private fun headersBuilder(): Headers.Builder = Headers.Builder().apply {
         if (basePassword.isNotEmpty() && baseLogin.isNotEmpty()) {
             val credentials = Credentials.basic(baseLogin, basePassword)
             add("Authorization", credentials)
         }
     }
 
-    val headers: Headers by lazy { headersBuilder().build() }
+    private val headers: Headers by lazy { headersBuilder().build() }
 
     private val baseUrl by lazy { getPrefBaseUrl() }
     private val baseLogin by lazy { getPrefBaseLogin() }
@@ -100,7 +100,7 @@ class TachideskApi {
         return getTrackSearch(track.tracking_url)
     }
 
-    val tachideskExtensionId by lazy {
+    private val tachideskExtensionId by lazy {
         val key = "tachidesk/en/1"
         val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray())
         (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE
@@ -110,6 +110,10 @@ class TachideskApi {
         Injekt.get<Application>().getSharedPreferences("source_$tachideskExtensionId", 0x0000)
     }
 
+    private fun getPrefBaseUrl(): String = preferences.getString(ADDRESS_TITLE, ADDRESS_DEFAULT)!!
+    private fun getPrefBaseLogin(): String = preferences.getString(LOGIN_TITLE, LOGIN_DEFAULT)!!
+    private fun getPrefBasePassword(): String = preferences.getString(PASSWORD_TITLE, PASSWORD_DEFAULT)!!
+
     companion object {
         private const val ADDRESS_TITLE = "Server URL Address"
         private const val ADDRESS_DEFAULT = ""
@@ -118,8 +122,4 @@ class TachideskApi {
         private const val PASSWORD_TITLE = "Password (Basic Auth)"
         private const val PASSWORD_DEFAULT = ""
     }
-
-    private fun getPrefBaseUrl(): String = preferences.getString(ADDRESS_TITLE, ADDRESS_DEFAULT)!!
-    private fun getPrefBaseLogin(): String = preferences.getString(LOGIN_TITLE, LOGIN_DEFAULT)!!
-    private fun getPrefBasePassword(): String = preferences.getString(PASSWORD_TITLE, PASSWORD_DEFAULT)!!
 }

+ 3 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt

@@ -14,7 +14,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel
 import cafe.adriel.voyager.core.model.coroutineScope
 import eu.kanade.core.preference.asState
 import eu.kanade.domain.base.BasePreferences
-import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
+import eu.kanade.domain.chapter.interactor.SyncChapterProgressWithTrack
 import eu.kanade.domain.manga.interactor.UpdateManga
 import eu.kanade.domain.manga.model.toDomainManga
 import eu.kanade.domain.source.service.SourcePreferences
@@ -77,7 +77,7 @@ class BrowseSourceScreenModel(
     private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
     private val updateManga: UpdateManga = Injekt.get(),
     private val insertTrack: InsertTrack = Injekt.get(),
-    private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(),
+    private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack = Injekt.get(),
 ) : StateScreenModel<BrowseSourceScreenModel.State>(State(Listing.valueOf(listingQuery))) {
 
     private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLoggedIn } }
@@ -297,7 +297,7 @@ class BrowseSourceScreenModel(
                         (service as TrackService).bind(track)
                         insertTrack.await(track.toDomainTrack()!!)
 
-                        syncChaptersWithTrackServiceTwoWay.await(manga.id, track.toDomainTrack()!!, service)
+                        syncChapterProgressWithTrack.await(manga.id, track.toDomainTrack()!!, service)
                     }
                 } catch (e: Exception) {
                     logcat(LogPriority.WARN, e) { "Could not match manga: ${manga.title} with service $service" }

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt

@@ -323,7 +323,7 @@ class MangaScreenModel(
                         launchIO {
                             try {
                                 service.match(manga)?.let { track ->
-                                    (service as TrackService).registerTracking(track, mangaId)
+                                    (service as TrackService).register(track, mangaId)
                                 }
                             } catch (e: Exception) {
                                 logcat(LogPriority.WARN, e) {

+ 22 - 37
app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt

@@ -39,9 +39,8 @@ import cafe.adriel.voyager.core.model.rememberScreenModel
 import cafe.adriel.voyager.navigator.LocalNavigator
 import cafe.adriel.voyager.navigator.Navigator
 import cafe.adriel.voyager.navigator.currentOrThrow
-import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
+import eu.kanade.domain.track.interactor.RefreshTracks
 import eu.kanade.domain.track.model.toDbTrack
-import eu.kanade.domain.track.model.toDomainTrack
 import eu.kanade.domain.ui.UiPreferences
 import eu.kanade.presentation.track.TrackChapterSelector
 import eu.kanade.presentation.track.TrackDateSelector
@@ -74,7 +73,6 @@ import tachiyomi.domain.manga.interactor.GetManga
 import tachiyomi.domain.source.service.SourceManager
 import tachiyomi.domain.track.interactor.DeleteTrack
 import tachiyomi.domain.track.interactor.GetTracks
-import tachiyomi.domain.track.interactor.InsertTrack
 import tachiyomi.domain.track.model.Track
 import tachiyomi.presentation.core.components.material.AlertDialogContent
 import tachiyomi.presentation.core.components.material.padding
@@ -208,7 +206,7 @@ data class TrackInfoDialogHomeScreen(
                 val manga = Injekt.get<GetManga>().await(mangaId) ?: return@launchNonCancellable
                 try {
                     val matchResult = item.service.match(manga) ?: throw Exception()
-                    item.service.registerTracking(matchResult, mangaId)
+                    item.service.register(matchResult, mangaId)
                 } catch (e: Exception) {
                     withUIContext { Injekt.get<Application>().toast(R.string.error_no_match) }
                 }
@@ -216,38 +214,25 @@ data class TrackInfoDialogHomeScreen(
         }
 
         private suspend fun refreshTrackers() {
-            val insertTrack = Injekt.get<InsertTrack>()
-            val syncChaptersWithTrackServiceTwoWay = Injekt.get<SyncChaptersWithTrackServiceTwoWay>()
+            val refreshTracks = Injekt.get<RefreshTracks>()
             val context = Injekt.get<Application>()
 
-            try {
-                val trackItems = getTracks.await(mangaId).mapToTrackItem()
-                for (trackItem in trackItems) {
-                    try {
-                        val track = trackItem.track ?: continue
-                        val domainTrack = trackItem.service.refresh(track.toDbTrack()).toDomainTrack() ?: continue
-                        insertTrack.await(domainTrack)
-                        syncChaptersWithTrackServiceTwoWay.await(mangaId, domainTrack, trackItem.service)
-                    } catch (e: Exception) {
-                        logcat(
-                            LogPriority.ERROR,
-                            e,
-                        ) { "Failed to refresh track data mangaId=$mangaId for service ${trackItem.service.id}" }
-                        withUIContext {
-                            context.toast(
-                                context.getString(
-                                    R.string.track_error,
-                                    context.getString(trackItem.service.nameRes()),
-                                    e.message,
-                                ),
-                            )
-                        }
+            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.getString(
+                                R.string.track_error,
+                                track!!.name,
+                                e.message,
+                            ),
+                        )
                     }
                 }
-            } catch (e: Exception) {
-                logcat(LogPriority.ERROR, e) { "Failed to refresh track data mangaId=$mangaId" }
-                withUIContext { context.toast(e.message) }
-            }
         }
 
         private fun List<Track>.mapToTrackItem(): List<TrackItem> {
@@ -581,7 +566,7 @@ private data class TrackDateRemoverScreen(
                 )
             },
             text = {
-                val serviceName = stringResource(sm.getServiceNameRes())
+                val serviceName = sm.getServiceName()
                 Text(
                     text = if (start) {
                         stringResource(R.string.track_remove_start_date_conf_text, serviceName)
@@ -618,7 +603,7 @@ private data class TrackDateRemoverScreen(
         private val start: Boolean,
     ) : ScreenModel {
 
-        fun getServiceNameRes() = service.nameRes()
+        fun getServiceName() = service.name
 
         fun removeDate() {
             coroutineScope.launchNonCancellable {
@@ -703,7 +688,7 @@ data class TrackServiceSearchScreen(
         }
 
         fun registerTracking(item: TrackSearch) {
-            coroutineScope.launchNonCancellable { service.registerTracking(item, mangaId) }
+            coroutineScope.launchNonCancellable { service.register(item, mangaId) }
         }
 
         fun updateSelection(selected: TrackSearch) {
@@ -734,7 +719,7 @@ private data class TrackServiceRemoveScreen(
                 service = Injekt.get<TrackManager>().getService(serviceId)!!,
             )
         }
-        val serviceName = stringResource(sm.getServiceNameRes())
+        val serviceName = sm.getServiceName()
         var removeRemoteTrack by remember { mutableStateOf(false) }
         AlertDialogContent(
             modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars),
@@ -799,7 +784,7 @@ private data class TrackServiceRemoveScreen(
         private val deleteTrack: DeleteTrack = Injekt.get(),
     ) : ScreenModel {
 
-        fun getServiceNameRes() = service.nameRes()
+        fun getServiceName() = service.name
 
         fun isServiceDeletable() = service is DeletableTrackService
 

+ 0 - 9
i18n/src/main/res/values/strings.xml

@@ -697,15 +697,6 @@
     <string name="are_you_sure">Are you sure?</string>
 
     <!-- Tracking Screen -->
-    <string name="tracker_anilist" translatable="false">AniList</string>
-    <string name="tracker_myanimelist" translatable="false">MyAnimeList</string>
-    <string name="tracker_kitsu" translatable="false">Kitsu</string>
-    <string name="tracker_komga" translatable="false">Komga</string>
-    <string name="tracker_bangumi" translatable="false">Bangumi</string>
-    <string name="tracker_shikimori" translatable="false">Shikimori</string>
-    <string name="tracker_manga_updates" translatable="false">MangaUpdates</string>
-    <string name="tracker_kavita" translatable="false">Kavita</string>
-    <string name="tracker_suwayomi" translatable="false">Suwayomi</string>
     <string name="manga_tracking_tab">Tracking</string>
     <plurals name="num_trackers">
         <item quantity="one">%d tracker</item>