diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt
index 05e43a26d..cd526090a 100644
--- a/app/src/main/java/eu/kanade/domain/DomainModule.kt
+++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt
@@ -1,7 +1,6 @@
 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.download.interactor.DeleteDownload
 import eu.kanade.domain.extension.interactor.GetExtensionLanguages
@@ -16,7 +15,9 @@ import eu.kanade.domain.source.interactor.SetMigrateSorting
 import eu.kanade.domain.source.interactor.ToggleLanguage
 import eu.kanade.domain.source.interactor.ToggleSource
 import eu.kanade.domain.source.interactor.ToggleSourcePin
+import eu.kanade.domain.track.interactor.AddTracks
 import eu.kanade.domain.track.interactor.RefreshTracks
+import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
 import eu.kanade.domain.track.interactor.TrackChapter
 import tachiyomi.data.category.CategoryRepositoryImpl
 import tachiyomi.data.chapter.ChapterRepositoryImpl
@@ -114,11 +115,13 @@ class DomainModule : InjektModule {
 
         addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
         addFactory { TrackChapter(get(), get(), get(), get()) }
+        addFactory { AddTracks(get(), get(), get()) }
         addFactory { RefreshTracks(get(), get(), get(), get()) }
         addFactory { DeleteTrack(get()) }
         addFactory { GetTracksPerManga(get(), get()) }
         addFactory { GetTracks(get()) }
         addFactory { InsertTrack(get()) }
+        addFactory { SyncChapterProgressWithTrack(get(), get(), get()) }
 
         addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
         addFactory { GetChapter(get()) }
@@ -127,7 +130,6 @@ class DomainModule : InjektModule {
         addFactory { SetReadStatus(get(), get(), get(), get(), get()) }
         addFactory { ShouldUpdateDbChapter() }
         addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get()) }
-        addFactory { SyncChapterProgressWithTrack(get(), get(), get()) }
 
         addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
         addFactory { GetHistory(get()) }
diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt b/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt
new file mode 100644
index 000000000..5a56889fb
--- /dev/null
+++ b/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt
@@ -0,0 +1,47 @@
+package eu.kanade.domain.track.interactor
+
+import eu.kanade.domain.track.model.toDomainTrack
+import eu.kanade.tachiyomi.data.track.EnhancedTracker
+import eu.kanade.tachiyomi.data.track.Tracker
+import eu.kanade.tachiyomi.source.Source
+import logcat.LogPriority
+import tachiyomi.core.util.lang.withNonCancellableContext
+import tachiyomi.core.util.system.logcat
+import tachiyomi.domain.manga.model.Manga
+import tachiyomi.domain.track.interactor.GetTracks
+import tachiyomi.domain.track.interactor.InsertTrack
+
+class AddTracks(
+    private val getTracks: GetTracks,
+    private val insertTrack: InsertTrack,
+    private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
+) {
+
+    suspend fun bindEnhancedTracks(manga: Manga, source: Source) {
+        withNonCancellableContext {
+            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" }
+                    }
+                }
+        }
+    }
+}
diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt b/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt
index 87b7c8d99..8c8952304 100644
--- a/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt
+++ b/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt
@@ -1,10 +1,9 @@
 package eu.kanade.domain.track.interactor
 
-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 eu.kanade.tachiyomi.data.track.Tracker
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.supervisorScope
@@ -13,7 +12,7 @@ import tachiyomi.domain.track.interactor.InsertTrack
 
 class RefreshTracks(
     private val getTracks: GetTracks,
-    private val trackManager: TrackManager,
+    private val trackerManager: TrackerManager,
     private val insertTrack: InsertTrack,
     private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
 ) {
@@ -23,18 +22,17 @@ class RefreshTracks(
      *
      * @return Failed updates.
      */
-    suspend fun await(mangaId: Long): List<Pair<TrackService?, Throwable>> {
+    suspend fun await(mangaId: Long): List<Pair<Tracker?, Throwable>> {
         return supervisorScope {
             return@supervisorScope getTracks.await(mangaId)
-                .map { track ->
+                .map { it to trackerManager.get(it.syncId) }
+                .filter { (_, service) -> service?.isLoggedIn == true }
+                .map { (track, service) ->
                     async {
-                        val service = trackManager.getService(track.syncId)
                         return@async try {
-                            if (service?.isLoggedIn == true) {
-                                val updatedTrack = service.refresh(track.toDbTrack())
-                                insertTrack.await(updatedTrack.toDomainTrack()!!)
-                                syncChapterProgressWithTrack.await(mangaId, track, service)
-                            }
+                            val updatedTrack = service!!.refresh(track.toDbTrack())
+                            insertTrack.await(updatedTrack.toDomainTrack()!!)
+                            syncChapterProgressWithTrack.await(mangaId, track, service)
                             null
                         } catch (e: Throwable) {
                             service to e
diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChapterProgressWithTrack.kt b/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt
similarity index 83%
rename from app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChapterProgressWithTrack.kt
rename to app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt
index 86862504c..dcb95ff26 100644
--- a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChapterProgressWithTrack.kt
+++ b/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt
@@ -1,8 +1,8 @@
-package eu.kanade.domain.chapter.interactor
+package eu.kanade.domain.track.interactor
 
 import eu.kanade.domain.track.model.toDbTrack
-import eu.kanade.tachiyomi.data.track.EnhancedTrackService
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.EnhancedTracker
+import eu.kanade.tachiyomi.data.track.Tracker
 import logcat.LogPriority
 import tachiyomi.core.util.system.logcat
 import tachiyomi.domain.chapter.interactor.GetChapterByMangaId
@@ -20,9 +20,9 @@ class SyncChapterProgressWithTrack(
     suspend fun await(
         mangaId: Long,
         remoteTrack: Track,
-        service: TrackService,
+        tracker: Tracker,
     ) {
-        if (service !is EnhancedTrackService) {
+        if (tracker !is EnhancedTracker) {
             return
         }
 
@@ -39,7 +39,7 @@ class SyncChapterProgressWithTrack(
         val updatedTrack = remoteTrack.copy(lastChapterRead = localLastRead.toDouble())
 
         try {
-            service.update(updatedTrack.toDbTrack())
+            tracker.update(updatedTrack.toDbTrack())
             updateChapter.awaitAll(chapterUpdates)
             insertTrack.await(updatedTrack)
         } catch (e: Throwable) {
diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt
index beb55b8ca..9046244f3 100644
--- a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt
+++ b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt
@@ -4,32 +4,32 @@ import android.content.Context
 import eu.kanade.domain.track.model.toDbTrack
 import eu.kanade.domain.track.service.DelayedTrackingUpdateJob
 import eu.kanade.domain.track.store.DelayedTrackingStore
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
+import eu.kanade.tachiyomi.data.track.mdlist.MdList
 import exh.md.utils.FollowStatus
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.coroutineScope
 import logcat.LogPriority
-import tachiyomi.core.util.lang.launchNonCancellable
+import tachiyomi.core.util.lang.withNonCancellableContext
 import tachiyomi.core.util.system.logcat
 import tachiyomi.domain.track.interactor.GetTracks
 import tachiyomi.domain.track.interactor.InsertTrack
 
 class TrackChapter(
     private val getTracks: GetTracks,
-    private val trackManager: TrackManager,
+    private val trackerManager: TrackerManager,
     private val insertTrack: InsertTrack,
     private val delayedTrackingStore: DelayedTrackingStore,
 ) {
 
-    suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) = coroutineScope {
-        launchNonCancellable {
+    suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) {
+        withNonCancellableContext {
             val tracks = getTracks.await(mangaId)
-            if (tracks.isEmpty()) return@launchNonCancellable
+            if (tracks.isEmpty()) return@withNonCancellableContext
 
             tracks.mapNotNull { track ->
-                val service = trackManager.getService(track.syncId)
-                if (service == null || !service.isLoggedIn || chapterNumber <= track.lastChapterRead /* SY --> */ || (service.id == TrackManager.MDLIST && track.status == FollowStatus.UNFOLLOWED.int.toLong())/* SY <-- */) {
+                val service = trackerManager.get(track.syncId)
+                if (service == null || !service.isLoggedIn || chapterNumber <= track.lastChapterRead /* SY --> */ || (service is MdList && track.status == FollowStatus.UNFOLLOWED.int.toLong())/* SY <-- */) {
                     return@mapNotNull null
                 }
 
diff --git a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt
index 0273e0fdc..f578bd600 100644
--- a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt
+++ b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt
@@ -10,7 +10,7 @@ import androidx.work.OneTimeWorkRequestBuilder
 import androidx.work.WorkerParameters
 import eu.kanade.domain.track.model.toDbTrack
 import eu.kanade.domain.track.store.DelayedTrackingStore
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.util.system.workManager
 import logcat.LogPriority
 import tachiyomi.core.util.lang.withIOContext
@@ -33,7 +33,7 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
         val getTracks = Injekt.get<GetTracks>()
         val insertTrack = Injekt.get<InsertTrack>()
 
-        val trackManager = Injekt.get<TrackManager>()
+        val trackerManager = Injekt.get<TrackerManager>()
         val delayedTrackingStore = Injekt.get<DelayedTrackingStore>()
 
         withIOContext {
@@ -47,7 +47,7 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
                 }
                 .forEach { track ->
                     try {
-                        val service = trackManager.getService(track.syncId)
+                        val service = trackerManager.get(track.syncId)
                         if (service != null && service.isLoggedIn) {
                             logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.id}, last chapter read: ${track.lastChapterRead}" }
                             service.update(track.toDbTrack(), true)
diff --git a/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt b/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt
index 2ddba51e0..418d794b9 100644
--- a/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt
+++ b/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt
@@ -1,6 +1,6 @@
 package eu.kanade.domain.track.service
 
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.data.track.anilist.Anilist
 import tachiyomi.core.preference.PreferenceStore
 
@@ -8,16 +8,16 @@ class TrackPreferences(
     private val preferenceStore: PreferenceStore,
 ) {
 
-    fun trackUsername(sync: TrackService) = preferenceStore.getString(trackUsername(sync.id), "")
+    fun trackUsername(sync: Tracker) = preferenceStore.getString(trackUsername(sync.id), "")
 
-    fun trackPassword(sync: TrackService) = preferenceStore.getString(trackPassword(sync.id), "")
+    fun trackPassword(sync: Tracker) = preferenceStore.getString(trackPassword(sync.id), "")
 
-    fun setTrackCredentials(sync: TrackService, username: String, password: String) {
+    fun setCredentials(sync: Tracker, username: String, password: String) {
         trackUsername(sync).set(username)
         trackPassword(sync).set(password)
     }
 
-    fun trackToken(sync: TrackService) = preferenceStore.getString(trackToken(sync.id), "")
+    fun trackToken(sync: Tracker) = preferenceStore.getString(trackToken(sync.id), "")
 
     fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
 
diff --git a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt
index 1d34a17c9..a8daf3011 100644
--- a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt
@@ -134,13 +134,13 @@ private fun ColumnScope.FilterPage(
     )
     // SY <--
 
-    val trackServices = remember { screenModel.trackServices }
-    when (trackServices.size) {
+    val trackers = remember { screenModel.trackers }
+    when (trackers.size) {
         0 -> {
             // No trackers
         }
         1 -> {
-            val service = trackServices[0]
+            val service = trackers[0]
             val filterTracker by screenModel.libraryPreferences.filterTracking(service.id.toInt()).collectAsState()
             TriStateItem(
                 label = stringResource(R.string.action_filter_tracked),
@@ -150,7 +150,7 @@ private fun ColumnScope.FilterPage(
         }
         else -> {
             HeadingItem(R.string.action_filter_tracked)
-            trackServices.map { service ->
+            trackers.map { service ->
                 val filterTracker by screenModel.libraryPreferences.filterTracking(service.id.toInt()).collectAsState()
                 TriStateItem(
                     label = service.name,
@@ -311,12 +311,12 @@ private fun ColumnScope.GroupPage(
     screenModel: LibrarySettingsScreenModel,
     hasCategories: Boolean,
 ) {
-    val groups = remember(hasCategories, screenModel.trackServices) {
+    val groups = remember(hasCategories, screenModel.trackers) {
         buildList {
             add(LibraryGroup.BY_DEFAULT)
             add(LibraryGroup.BY_SOURCE)
             add(LibraryGroup.BY_STATUS)
-            if (screenModel.trackServices.isNotEmpty()) {
+            if (screenModel.trackers.isNotEmpty()) {
                 add(LibraryGroup.BY_TRACK_STATUS)
             }
             if (hasCategories) {
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt b/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt
index 1ceb73c44..760745cd6 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt
@@ -5,7 +5,7 @@ import androidx.compose.runtime.remember
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.res.stringResource
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.Tracker
 import tachiyomi.core.preference.Preference as PreferenceData
 
 sealed class Preference {
@@ -135,10 +135,10 @@ sealed class Preference {
         ) : PreferenceItem<String>()
 
         /**
-         * A [PreferenceItem] for individual tracking service.
+         * A [PreferenceItem] for individual tracker.
          */
-        data class TrackingPreference(
-            val service: TrackService,
+        data class TrackerPreference(
+            val tracker: Tracker,
             override val title: String,
             val login: () -> Unit,
             val logout: () -> Unit,
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt
index 940a48225..b68f17fcd 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt
@@ -156,13 +156,13 @@ internal fun PreferenceItem(
                     },
                 )
             }
-            is Preference.PreferenceItem.TrackingPreference -> {
+            is Preference.PreferenceItem.TrackerPreference -> {
                 val uName by Injekt.get<PreferenceStore>()
-                    .getString(TrackPreferences.trackUsername(item.service.id))
+                    .getString(TrackPreferences.trackUsername(item.tracker.id))
                     .collectAsState()
-                item.service.run {
+                item.tracker.run {
                     TrackingPreferenceWidget(
-                        service = this,
+                        tracker = this,
                         checked = uName.isNotEmpty(),
                         onClick = { if (isLoggedIn) item.logout() else item.login() },
                     )
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
index c7898edd7..a82428b41 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
@@ -53,7 +53,7 @@ import eu.kanade.tachiyomi.data.cache.PagePreviewCache
 import eu.kanade.tachiyomi.data.download.DownloadCache
 import eu.kanade.tachiyomi.data.download.DownloadManager
 import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.network.NetworkHelper
 import eu.kanade.tachiyomi.network.NetworkPreferences
 import eu.kanade.tachiyomi.network.PREF_DOH_360
@@ -390,7 +390,7 @@ object SettingsAdvancedScreen : SearchableSettings {
     private fun getLibraryGroup(): Preference.PreferenceGroup {
         val scope = rememberCoroutineScope()
         val context = LocalContext.current
-        val trackManager = remember { Injekt.get<TrackManager>() }
+        val trackerManager = remember { Injekt.get<TrackerManager>() }
 
         return Preference.PreferenceGroup(
             title = stringResource(R.string.label_library),
@@ -402,7 +402,7 @@ object SettingsAdvancedScreen : SearchableSettings {
                 Preference.PreferenceItem.TextPreference(
                     title = stringResource(R.string.pref_refresh_library_tracking),
                     subtitle = stringResource(R.string.pref_refresh_library_tracking_summary),
-                    enabled = trackManager.hasLoggedServices(),
+                    enabled = trackerManager.hasLoggedIn(),
                     onClick = { LibraryUpdateJob.startNow(context, target = LibraryUpdateJob.Target.TRACKING) },
                 ),
                 Preference.PreferenceItem.TextPreference(
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
index 9ca06d621..262a6c5fe 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
@@ -23,7 +23,7 @@ import eu.kanade.presentation.more.settings.Preference
 import eu.kanade.presentation.more.settings.widget.TriStateListDialog
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.ui.category.CategoryScreen
 import eu.kanade.tachiyomi.ui.category.genre.SortTagScreen
 import kotlinx.coroutines.launch
@@ -220,7 +220,7 @@ object SettingsLibraryScreen : SearchableSettings {
                 ),
                 Preference.PreferenceItem.SwitchPreference(
                     pref = libraryPreferences.autoUpdateTrackers(),
-                    enabled = Injekt.get<TrackManager>().hasLoggedServices(),
+                    enabled = Injekt.get<TrackerManager>().hasLoggedIn(),
                     title = stringResource(R.string.pref_library_update_refresh_trackers),
                     subtitle = stringResource(R.string.pref_library_update_refresh_trackers_summary),
                 ),
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt
index d57034536..655d79cc2 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt
@@ -44,9 +44,9 @@ import androidx.compose.ui.unit.dp
 import eu.kanade.domain.track.service.TrackPreferences
 import eu.kanade.presentation.more.settings.Preference
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.track.EnhancedTrackService
-import eu.kanade.tachiyomi.data.track.TrackManager
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.EnhancedTracker
+import eu.kanade.tachiyomi.data.track.Tracker
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.data.track.anilist.AnilistApi
 import eu.kanade.tachiyomi.data.track.bangumi.BangumiApi
 import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeListApi
@@ -82,7 +82,7 @@ object SettingsTrackingScreen : SearchableSettings {
     override fun getPreferences(): List<Preference> {
         val context = LocalContext.current
         val trackPreferences = remember { Injekt.get<TrackPreferences>() }
-        val trackManager = remember { Injekt.get<TrackManager>() }
+        val trackerManager = remember { Injekt.get<TrackerManager>() }
         val sourceManager = remember { Injekt.get<SourceManager>() }
 
         var dialog by remember { mutableStateOf<Any?>(null) }
@@ -90,24 +90,24 @@ object SettingsTrackingScreen : SearchableSettings {
             when (this) {
                 is LoginDialog -> {
                     TrackingLoginDialog(
-                        service = service,
+                        tracker = tracker,
                         uNameStringRes = uNameStringRes,
                         onDismissRequest = { dialog = null },
                     )
                 }
                 is LogoutDialog -> {
                     TrackingLogoutDialog(
-                        service = service,
+                        tracker = tracker,
                         onDismissRequest = { dialog = null },
                     )
                 }
             }
         }
 
-        val enhancedTrackers = trackManager.services
-            .filter { it is EnhancedTrackService }
+        val enhancedTrackers = trackerManager.trackers
+            .filter { it is EnhancedTracker }
             .partition { service ->
-                val acceptedSources = (service as EnhancedTrackService).getAcceptedSources()
+                val acceptedSources = (service as EnhancedTracker).getAcceptedSources()
                 sourceManager.getCatalogueSources().any { it::class.qualifiedName in acceptedSources }
             }
         var enhancedTrackerInfo = stringResource(R.string.enhanced_tracking_info)
@@ -127,41 +127,41 @@ object SettingsTrackingScreen : SearchableSettings {
             Preference.PreferenceGroup(
                 title = stringResource(R.string.services),
                 preferenceItems = listOf(
-                    Preference.PreferenceItem.TrackingPreference(
-                        title = trackManager.myAnimeList.name,
-                        service = trackManager.myAnimeList,
+                    Preference.PreferenceItem.TrackerPreference(
+                        title = trackerManager.myAnimeList.name,
+                        tracker = trackerManager.myAnimeList,
                         login = { context.openInBrowser(MyAnimeListApi.authUrl(), forceDefaultBrowser = true) },
-                        logout = { dialog = LogoutDialog(trackManager.myAnimeList) },
+                        logout = { dialog = LogoutDialog(trackerManager.myAnimeList) },
                     ),
-                    Preference.PreferenceItem.TrackingPreference(
-                        title = trackManager.aniList.name,
-                        service = trackManager.aniList,
+                    Preference.PreferenceItem.TrackerPreference(
+                        title = trackerManager.aniList.name,
+                        tracker = trackerManager.aniList,
                         login = { context.openInBrowser(AnilistApi.authUrl(), forceDefaultBrowser = true) },
-                        logout = { dialog = LogoutDialog(trackManager.aniList) },
+                        logout = { dialog = LogoutDialog(trackerManager.aniList) },
                     ),
-                    Preference.PreferenceItem.TrackingPreference(
-                        title = trackManager.kitsu.name,
-                        service = trackManager.kitsu,
-                        login = { dialog = LoginDialog(trackManager.kitsu, R.string.email) },
-                        logout = { dialog = LogoutDialog(trackManager.kitsu) },
+                    Preference.PreferenceItem.TrackerPreference(
+                        title = trackerManager.kitsu.name,
+                        tracker = trackerManager.kitsu,
+                        login = { dialog = LoginDialog(trackerManager.kitsu, R.string.email) },
+                        logout = { dialog = LogoutDialog(trackerManager.kitsu) },
                     ),
-                    Preference.PreferenceItem.TrackingPreference(
-                        title = trackManager.mangaUpdates.name,
-                        service = trackManager.mangaUpdates,
-                        login = { dialog = LoginDialog(trackManager.mangaUpdates, R.string.username) },
-                        logout = { dialog = LogoutDialog(trackManager.mangaUpdates) },
+                    Preference.PreferenceItem.TrackerPreference(
+                        title = trackerManager.mangaUpdates.name,
+                        tracker = trackerManager.mangaUpdates,
+                        login = { dialog = LoginDialog(trackerManager.mangaUpdates, R.string.username) },
+                        logout = { dialog = LogoutDialog(trackerManager.mangaUpdates) },
                     ),
-                    Preference.PreferenceItem.TrackingPreference(
-                        title = trackManager.shikimori.name,
-                        service = trackManager.shikimori,
+                    Preference.PreferenceItem.TrackerPreference(
+                        title = trackerManager.shikimori.name,
+                        tracker = trackerManager.shikimori,
                         login = { context.openInBrowser(ShikimoriApi.authUrl(), forceDefaultBrowser = true) },
-                        logout = { dialog = LogoutDialog(trackManager.shikimori) },
+                        logout = { dialog = LogoutDialog(trackerManager.shikimori) },
                     ),
-                    Preference.PreferenceItem.TrackingPreference(
-                        title = trackManager.bangumi.name,
-                        service = trackManager.bangumi,
+                    Preference.PreferenceItem.TrackerPreference(
+                        title = trackerManager.bangumi.name,
+                        tracker = trackerManager.bangumi,
                         login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) },
-                        logout = { dialog = LogoutDialog(trackManager.bangumi) },
+                        logout = { dialog = LogoutDialog(trackerManager.bangumi) },
                     ),
                     Preference.PreferenceItem.InfoPreference(stringResource(R.string.tracking_info)),
                 ),
@@ -170,10 +170,10 @@ object SettingsTrackingScreen : SearchableSettings {
                 title = stringResource(R.string.enhanced_services),
                 preferenceItems = enhancedTrackers.first
                     .map { service ->
-                        Preference.PreferenceItem.TrackingPreference(
+                        Preference.PreferenceItem.TrackerPreference(
                             title = service.name,
-                            service = service,
-                            login = { (service as EnhancedTrackService).loginNoop() },
+                            tracker = service,
+                            login = { (service as EnhancedTracker).loginNoop() },
                             logout = service::logout,
                         )
                     } + listOf(Preference.PreferenceItem.InfoPreference(enhancedTrackerInfo)),
@@ -183,15 +183,15 @@ object SettingsTrackingScreen : SearchableSettings {
 
     @Composable
     private fun TrackingLoginDialog(
-        service: TrackService,
+        tracker: Tracker,
         @StringRes uNameStringRes: Int,
         onDismissRequest: () -> Unit,
     ) {
         val context = LocalContext.current
         val scope = rememberCoroutineScope()
 
-        var username by remember { mutableStateOf(TextFieldValue(service.getUsername())) }
-        var password by remember { mutableStateOf(TextFieldValue(service.getPassword())) }
+        var username by remember { mutableStateOf(TextFieldValue(tracker.getUsername())) }
+        var password by remember { mutableStateOf(TextFieldValue(tracker.getPassword())) }
         var processing by remember { mutableStateOf(false) }
         var inputError by remember { mutableStateOf(false) }
 
@@ -200,7 +200,7 @@ object SettingsTrackingScreen : SearchableSettings {
             title = {
                 Row(verticalAlignment = Alignment.CenterVertically) {
                     Text(
-                        text = stringResource(R.string.login_title, service.name),
+                        text = stringResource(R.string.login_title, tracker.name),
                         modifier = Modifier.weight(1f),
                     )
                     IconButton(onClick = onDismissRequest) {
@@ -264,7 +264,7 @@ object SettingsTrackingScreen : SearchableSettings {
                             processing = true
                             val result = checkLogin(
                                 context = context,
-                                service = service,
+                                tracker = tracker,
                                 username = username.text,
                                 password = password.text,
                             )
@@ -283,16 +283,16 @@ object SettingsTrackingScreen : SearchableSettings {
 
     private suspend fun checkLogin(
         context: Context,
-        service: TrackService,
+        tracker: Tracker,
         username: String,
         password: String,
     ): Boolean {
         return try {
-            service.login(username, password)
+            tracker.login(username, password)
             withUIContext { context.toast(R.string.login_success) }
             true
         } catch (e: Throwable) {
-            service.logout()
+            tracker.logout()
             withUIContext { context.toast(e.message.toString()) }
             false
         }
@@ -300,7 +300,7 @@ object SettingsTrackingScreen : SearchableSettings {
 
     @Composable
     private fun TrackingLogoutDialog(
-        service: TrackService,
+        tracker: Tracker,
         onDismissRequest: () -> Unit,
     ) {
         val context = LocalContext.current
@@ -308,7 +308,7 @@ object SettingsTrackingScreen : SearchableSettings {
             onDismissRequest = onDismissRequest,
             title = {
                 Text(
-                    text = stringResource(R.string.logout_title, service.name),
+                    text = stringResource(R.string.logout_title, tracker.name),
                     textAlign = TextAlign.Center,
                     modifier = Modifier.fillMaxWidth(),
                 )
@@ -324,7 +324,7 @@ object SettingsTrackingScreen : SearchableSettings {
                     Button(
                         modifier = Modifier.weight(1f),
                         onClick = {
-                            service.logout()
+                            tracker.logout()
                             onDismissRequest()
                             context.toast(R.string.logout_success)
                         },
@@ -342,10 +342,10 @@ object SettingsTrackingScreen : SearchableSettings {
 }
 
 private data class LoginDialog(
-    val service: TrackService,
+    val tracker: Tracker,
     @StringRes val uNameStringRes: Int,
 )
 
 private data class LogoutDialog(
-    val service: TrackService,
+    val tracker: Tracker,
 )
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TrackingPreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TrackingPreferenceWidget.kt
index e0c34fbc4..d8544d156 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TrackingPreferenceWidget.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TrackingPreferenceWidget.kt
@@ -20,12 +20,12 @@ import androidx.compose.ui.unit.dp
 import eu.kanade.presentation.more.settings.LocalPreferenceHighlighted
 import eu.kanade.presentation.track.components.TrackLogoIcon
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.Tracker
 
 @Composable
 fun TrackingPreferenceWidget(
     modifier: Modifier = Modifier,
-    service: TrackService,
+    tracker: Tracker,
     checked: Boolean,
     onClick: (() -> Unit)? = null,
 ) {
@@ -38,9 +38,9 @@ fun TrackingPreferenceWidget(
                 .padding(horizontal = PrefsHorizontalPadding, vertical = 8.dp),
             verticalAlignment = Alignment.CenterVertically,
         ) {
-            TrackLogoIcon(service)
+            TrackLogoIcon(tracker)
             Text(
-                text = service.name,
+                text = tracker.name,
                 modifier = Modifier
                     .weight(1f)
                     .padding(horizontal = 16.dp),
diff --git a/app/src/main/java/eu/kanade/presentation/reader/ChapterListDialog.kt b/app/src/main/java/eu/kanade/presentation/reader/ChapterListDialog.kt
index 541a4451a..6582838fb 100644
--- a/app/src/main/java/eu/kanade/presentation/reader/ChapterListDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/reader/ChapterListDialog.kt
@@ -30,6 +30,7 @@ fun ChapterListDialog(
     chapters: List<ReaderChapterItem>,
     onClickChapter: (Chapter) -> Unit,
     onBookmark: (Chapter) -> Unit,
+    dateRelativeTime: Boolean,
 ) {
     val manga by screenModel.mangaFlow.collectAsState()
     val context = LocalContext.current
@@ -56,7 +57,7 @@ fun ChapterListDialog(
                             if (manga?.isEhBasedManga() == true) {
                                 MetadataUtil.EX_DATE_FORMAT.format(Date(it))
                             } else {
-                                Date(it).toRelativeString(context, chapterItem.dateFormat)
+                                Date(it).toRelativeString(context, dateRelativeTime, chapterItem.dateFormat)
                             }
                             // SY <--
                         },
diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt
index d9a899d5d..bf7860147 100644
--- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt
+++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt
@@ -49,7 +49,7 @@ import eu.kanade.domain.track.model.toDbTrack
 import eu.kanade.presentation.components.DropdownMenu
 import eu.kanade.presentation.track.components.TrackLogoIcon
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.ui.manga.track.TrackItem
 import eu.kanade.tachiyomi.util.system.copyToClipboard
 import java.text.DateFormat
@@ -80,12 +80,12 @@ fun TrackInfoDialogHome(
     ) {
         trackItems.forEach { item ->
             if (item.track != null) {
-                val supportsScoring = item.service.getScoreList().isNotEmpty()
-                val supportsReadingDates = item.service.supportsReadingDates
+                val supportsScoring = item.tracker.getScoreList().isNotEmpty()
+                val supportsReadingDates = item.tracker.supportsReadingDates
                 TrackInfoItem(
                     title = item.track.title,
-                    service = item.service,
-                    status = item.service.getStatus(item.track.status.toInt()),
+                    tracker = item.tracker,
+                    status = item.tracker.getStatus(item.track.status.toInt()),
                     onStatusClick = { onStatusClick(item) },
                     chapters = "${item.track.lastChapterRead.toInt()}".let {
                         val totalChapters = item.track.totalChapters
@@ -97,7 +97,7 @@ fun TrackInfoDialogHome(
                         }
                     },
                     onChaptersClick = { onChapterClick(item) },
-                    score = item.service.displayScore(item.track.toDbTrack())
+                    score = item.tracker.displayScore(item.track.toDbTrack())
                         .takeIf { supportsScoring && item.track.score != 0.0 },
                     onScoreClick = { onScoreClick(item) }
                         .takeIf { supportsScoring },
@@ -115,7 +115,7 @@ fun TrackInfoDialogHome(
                 )
             } else {
                 TrackInfoItemEmpty(
-                    service = item.service,
+                    tracker = item.tracker,
                     onNewSearch = { onNewSearch(item) },
                 )
             }
@@ -126,7 +126,7 @@ fun TrackInfoDialogHome(
 @Composable
 private fun TrackInfoItem(
     title: String,
-    service: TrackService,
+    tracker: Tracker,
     @StringRes status: Int?,
     onStatusClick: () -> Unit,
     chapters: String,
@@ -147,7 +147,7 @@ private fun TrackInfoItem(
             verticalAlignment = Alignment.CenterVertically,
         ) {
             TrackLogoIcon(
-                service = service,
+                tracker = tracker,
                 onClick = onOpenInBrowser,
             )
             Box(
@@ -260,13 +260,13 @@ private fun TrackDetailsItem(
 
 @Composable
 private fun TrackInfoItemEmpty(
-    service: TrackService,
+    tracker: Tracker,
     onNewSearch: () -> Unit,
 ) {
     Row(
         verticalAlignment = Alignment.CenterVertically,
     ) {
-        TrackLogoIcon(service)
+        TrackLogoIcon(tracker)
         TextButton(
             onClick = onNewSearch,
             modifier = Modifier
diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackServiceSearch.kt b/app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt
similarity index 99%
rename from app/src/main/java/eu/kanade/presentation/track/TrackServiceSearch.kt
rename to app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt
index 1ac38ecf0..c2b951453 100644
--- a/app/src/main/java/eu/kanade/presentation/track/TrackServiceSearch.kt
+++ b/app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt
@@ -70,7 +70,7 @@ import tachiyomi.presentation.core.util.runOnEnterKeyPressed
 import tachiyomi.presentation.core.util.secondaryItemAlpha
 
 @Composable
-fun TrackServiceSearch(
+fun TrackerSearch(
     query: TextFieldValue,
     onQueryChange: (TextFieldValue) -> Unit,
     onDispatchQuery: () -> Unit,
diff --git a/app/src/main/java/eu/kanade/presentation/track/components/TrackLogoIcon.kt b/app/src/main/java/eu/kanade/presentation/track/components/TrackLogoIcon.kt
index 44d98cbd2..52bf66575 100644
--- a/app/src/main/java/eu/kanade/presentation/track/components/TrackLogoIcon.kt
+++ b/app/src/main/java/eu/kanade/presentation/track/components/TrackLogoIcon.kt
@@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.unit.dp
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.Tracker
 import tachiyomi.presentation.core.util.clickableNoIndication
 
 @Composable
 fun TrackLogoIcon(
-    service: TrackService,
+    tracker: Tracker,
     onClick: (() -> Unit)? = null,
 ) {
     val modifier = if (onClick != null) {
@@ -29,13 +29,13 @@ fun TrackLogoIcon(
     Box(
         modifier = modifier
             .size(48.dp)
-            .background(color = Color(service.getLogoColor()), shape = MaterialTheme.shapes.medium)
+            .background(color = Color(tracker.getLogoColor()), shape = MaterialTheme.shapes.medium)
             .padding(4.dp),
         contentAlignment = Alignment.Center,
     ) {
         Image(
-            painter = painterResource(service.getLogo()),
-            contentDescription = service.name,
+            painter = painterResource(tracker.getLogo()),
+            contentDescription = tracker.name,
         )
     }
 }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt
index 52937d28a..249c95019 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt
@@ -20,7 +20,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache
 import eu.kanade.tachiyomi.data.download.DownloadManager
 import eu.kanade.tachiyomi.data.download.DownloadProvider
 import eu.kanade.tachiyomi.data.saver.ImageSaver
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.extension.ExtensionManager
 import eu.kanade.tachiyomi.network.JavaScriptEngine
 import eu.kanade.tachiyomi.network.NetworkHelper
@@ -165,7 +165,7 @@ class AppModule(val app: Application) : InjektModule {
         addSingletonFactory { DownloadManager(app) }
         addSingletonFactory { DownloadCache(app) }
 
-        addSingletonFactory { TrackManager(app) }
+        addSingletonFactory { TrackerManager() }
         addSingletonFactory { DelayedTrackingStore(app) }
 
         addSingletonFactory { ImageSaver(app) }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
index 48905438c..afc41dbcb 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
@@ -9,7 +9,7 @@ import eu.kanade.domain.ui.UiPreferences
 import eu.kanade.tachiyomi.core.security.SecurityPreferences
 import eu.kanade.tachiyomi.data.backup.BackupCreateJob
 import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.network.NetworkPreferences
 import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
 import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
@@ -49,7 +49,7 @@ object Migrations {
         libraryPreferences: LibraryPreferences,
         readerPreferences: ReaderPreferences,
         backupPreferences: BackupPreferences,
-        trackManager: TrackManager,
+        trackerManager: TrackerManager,
     ): Boolean {
         val lastVersionCode = preferenceStore.getInt("last_version_code", 0)
         val oldVersion = lastVersionCode.get()
@@ -137,8 +137,8 @@ object Migrations {
                 // Force MAL log out due to login flow change
                 // v52: switched from scraping to WebView
                 // v53: switched from WebView to OAuth
-                if (trackManager.myAnimeList.isLoggedIn) {
-                    trackManager.myAnimeList.logout()
+                if (trackerManager.myAnimeList.isLoggedIn) {
+                    trackerManager.myAnimeList.logout()
                     context.toast(R.string.myanimelist_relogin)
                 }
             }
@@ -345,7 +345,7 @@ object Migrations {
                     "pref_filter_library_started",
                     "pref_filter_library_bookmarked",
                     "pref_filter_library_completed",
-                ) + trackManager.services.map { "pref_filter_library_tracked_${it.id}" }
+                ) + trackerManager.trackers.map { "pref_filter_library_tracked_${it.id}" }
 
                 prefKeys.forEach { key ->
                     val pref = preferenceStore.getInt(key, 0)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupFileValidator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupFileValidator.kt
index 0fe9ddff9..ea8f3c9ea 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupFileValidator.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupFileValidator.kt
@@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.data.backup
 import android.content.Context
 import android.net.Uri
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.util.BackupUtil
 import tachiyomi.domain.source.service.SourceManager
 import uy.kohesive.injekt.Injekt
@@ -11,7 +11,7 @@ import uy.kohesive.injekt.api.get
 
 class BackupFileValidator(
     private val sourceManager: SourceManager = Injekt.get(),
-    private val trackManager: TrackManager = Injekt.get(),
+    private val trackerManager: TrackerManager = Injekt.get(),
 ) {
 
     /**
@@ -50,7 +50,7 @@ class BackupFileValidator(
             .map { it.syncId }
             .distinct()
         val missingTrackers = trackers
-            .mapNotNull { trackManager.getService(it.toLong()) }
+            .mapNotNull { trackerManager.get(it.toLong()) }
             .filter { !it.isLoggedIn }
             .map { it.name }
             .sorted()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt
index a91d0d09e..75b66259b 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt
@@ -25,8 +25,8 @@ import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.cache.CoverCache
 import eu.kanade.tachiyomi.data.download.DownloadManager
 import eu.kanade.tachiyomi.data.notification.Notifications
-import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.data.track.TrackStatus
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.source.UnmeteredSource
 import eu.kanade.tachiyomi.source.model.SManga
 import eu.kanade.tachiyomi.source.model.UpdateStrategy
@@ -121,7 +121,8 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
     private val getMergedMangaForDownloading: GetMergedMangaForDownloading = Injekt.get()
     private val getTracks: GetTracks = Injekt.get()
     private val insertTrack: InsertTrack = Injekt.get()
-    private val mdList = Injekt.get<TrackManager>().mdList
+    private val trackerManager: TrackerManager = Injekt.get()
+    private val mdList = trackerManager.mdList
     // SY <--
 
     private val notifier = LibraryUpdateNotifier(context)
@@ -238,7 +239,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
 
                     libraryManga.filter { (manga) ->
                         val status = tracks[manga.id]?.firstNotNullOfOrNull { track ->
-                            TrackStatus.parseTrackerStatus(track.syncId, track.status)
+                            TrackStatus.parseTrackerStatus(trackerManager, track.syncId, track.status)
                         } ?: TrackStatus.OTHER
                         status.int == trackingExtra
                     }
@@ -317,7 +318,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
                                     mangaInSource.forEach { (manga) ->
                                         try {
                                             val tracks = getTracks.await(manga.id)
-                                            if (tracks.isEmpty() || tracks.none { it.syncId == TrackManager.MDLIST }) {
+                                            if (tracks.isEmpty() || tracks.none { it.syncId == TrackerManager.MDLIST }) {
                                                 val track = mdList.createInitialTracker(manga)
                                                 insertTrack.await(mdList.refresh(track).toDomainTrack(false)!!)
                                             }
@@ -613,7 +614,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
                 val dbTracks = getTracks.await(manga.id)
 
                 // find the mdlist entry if its unfollowed the follow it
-                var tracker = dbTracks.firstOrNull { it.syncId == TrackManager.MDLIST }
+                var tracker = dbTracks.firstOrNull { it.syncId == TrackerManager.MDLIST }
                     ?: mdList.createInitialTracker(manga).toDomainTrack(idRequired = false)
 
                 if (tracker?.status == FollowStatus.UNFOLLOWED.int.toLong()) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/DeletableTrackService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/DeletableTracker.kt
similarity index 56%
rename from app/src/main/java/eu/kanade/tachiyomi/data/track/DeletableTrackService.kt
rename to app/src/main/java/eu/kanade/tachiyomi/data/track/DeletableTracker.kt
index 7f1494707..c61c55e78 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/DeletableTrackService.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/DeletableTracker.kt
@@ -3,9 +3,9 @@ package eu.kanade.tachiyomi.data.track
 import eu.kanade.tachiyomi.data.database.models.Track
 
 /**
- * For track services api that support deleting a manga entry for a user's list
+ * Tracker that support deleting am entry from a user's list.
  */
-interface DeletableTrackService {
+interface DeletableTracker {
 
     suspend fun delete(track: Track): Track
 }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/EnhancedTrackService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/EnhancedTracker.kt
similarity index 62%
rename from app/src/main/java/eu/kanade/tachiyomi/data/track/EnhancedTrackService.kt
rename to app/src/main/java/eu/kanade/tachiyomi/data/track/EnhancedTracker.kt
index 75245cf80..a501cded8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/EnhancedTrackService.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/EnhancedTracker.kt
@@ -6,31 +6,32 @@ import tachiyomi.domain.manga.model.Manga
 import tachiyomi.domain.track.model.Track
 
 /**
- * An Enhanced Track Service will never prompt the user to match a manga with the remote.
- * It is expected that such Track Service can only work with specific sources and unique IDs.
+ * A tracker that will never prompt the user to manually bind an entry.
+ * It is expected that such tracker can only work with specific sources and unique IDs.
  */
-interface EnhancedTrackService {
+interface EnhancedTracker {
+
     /**
-     * This TrackService will only work with the sources that are accepted by this filter function.
+     * This tracker will only work with the sources that are accepted by this filter function.
      */
     fun accept(source: Source): Boolean {
         return source::class.qualifiedName in getAcceptedSources()
     }
 
     /**
-     * Fully qualified source classes that this track service is compatible with.
+     * Fully qualified source classes that this tracker is compatible with.
      */
     fun getAcceptedSources(): List<String>
 
     fun loginNoop()
 
     /**
-     * match is similar to TrackService.search, but only return zero or one match.
+     * Similar to [Tracker].search, but only returns zero or one match.
      */
     suspend fun match(manga: Manga): TrackSearch?
 
     /**
-     * Checks whether the provided source/track/manga triplet is from this TrackService
+     * Checks whether the provided source/track/manga triplet is from this [Tracker]
      */
     fun isTrackFrom(track: Track, manga: Manga, source: Source?): Boolean
 
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackStatus.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackStatus.kt
index 588f4279f..77473cbe1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackStatus.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackStatus.kt
@@ -22,10 +22,10 @@ enum class TrackStatus(val int: Int, @StringRes val res: Int) {
     ;
 
     companion object {
-        fun parseTrackerStatus(tracker: Long, statusLong: Long): TrackStatus? {
+        fun parseTrackerStatus(trackerManager: TrackerManager, tracker: Long, statusLong: Long): TrackStatus? {
             val status = statusLong.toInt()
             return when (tracker) {
-                TrackManager.MDLIST -> {
+                trackerManager.mdList.id -> {
                     when (FollowStatus.fromInt(status)) {
                         FollowStatus.UNFOLLOWED -> null
                         FollowStatus.READING -> READING
@@ -36,7 +36,7 @@ enum class TrackStatus(val int: Int, @StringRes val res: Int) {
                         FollowStatus.RE_READING -> REPEATING
                     }
                 }
-                TrackManager.MYANIMELIST -> {
+                trackerManager.myAnimeList.id -> {
                     when (status) {
                         MyAnimeList.READING -> READING
                         MyAnimeList.COMPLETED -> COMPLETED
@@ -47,7 +47,7 @@ enum class TrackStatus(val int: Int, @StringRes val res: Int) {
                         else -> null
                     }
                 }
-                TrackManager.ANILIST -> {
+                trackerManager.aniList.id -> {
                     when (status) {
                         Anilist.READING -> READING
                         Anilist.COMPLETED -> COMPLETED
@@ -58,7 +58,7 @@ enum class TrackStatus(val int: Int, @StringRes val res: Int) {
                         else -> null
                     }
                 }
-                TrackManager.KITSU -> {
+                trackerManager.kitsu.id -> {
                     when (status) {
                         Kitsu.READING -> READING
                         Kitsu.COMPLETED -> COMPLETED
@@ -68,7 +68,7 @@ enum class TrackStatus(val int: Int, @StringRes val res: Int) {
                         else -> null
                     }
                 }
-                TrackManager.SHIKIMORI -> {
+                trackerManager.shikimori.id -> {
                     when (status) {
                         Shikimori.READING -> READING
                         Shikimori.COMPLETED -> COMPLETED
@@ -79,7 +79,7 @@ enum class TrackStatus(val int: Int, @StringRes val res: Int) {
                         else -> null
                     }
                 }
-                TrackManager.BANGUMI -> {
+                trackerManager.bangumi.id -> {
                     when (status) {
                         Bangumi.READING -> READING
                         Bangumi.COMPLETED -> COMPLETED
@@ -89,7 +89,7 @@ enum class TrackStatus(val int: Int, @StringRes val res: Int) {
                         else -> null
                     }
                 }
-                TrackManager.KOMGA -> {
+                trackerManager.komga.id -> {
                     when (status) {
                         Komga.READING -> READING
                         Komga.COMPLETED -> COMPLETED
@@ -97,7 +97,7 @@ enum class TrackStatus(val int: Int, @StringRes val res: Int) {
                         else -> null
                     }
                 }
-                TrackManager.MANGA_UPDATES -> {
+                trackerManager.mangaUpdates.id -> {
                     when (status) {
                         MangaUpdates.READING_LIST -> READING
                         MangaUpdates.COMPLETE_LIST -> COMPLETED
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt
similarity index 95%
rename from app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt
rename to app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt
index e30100e29..490ea0e96 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.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.SyncChapterProgressWithTrack
+import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
 import eu.kanade.domain.track.model.toDbTrack
 import eu.kanade.domain.track.model.toDomainTrack
 import eu.kanade.domain.track.service.TrackPreferences
@@ -28,7 +28,7 @@ import uy.kohesive.injekt.injectLazy
 import java.time.ZoneOffset
 import tachiyomi.domain.track.model.Track as DomainTrack
 
-abstract class TrackService(val id: Long, val name: String) {
+abstract class Tracker(val id: Long, val name: String) {
 
     val trackPreferences: TrackPreferences by injectLazy()
     val networkService: NetworkHelper by injectLazy()
@@ -83,7 +83,7 @@ abstract class TrackService(val id: Long, val name: String) {
 
     @CallSuper
     open fun logout() {
-        trackPreferences.setTrackCredentials(this, "", "")
+        trackPreferences.setCredentials(this, "", "")
     }
 
     open val isLoggedIn: Boolean
@@ -95,7 +95,7 @@ abstract class TrackService(val id: Long, val name: String) {
     fun getPassword() = trackPreferences.trackPassword(this).get()
 
     fun saveCredentials(username: String, password: String) {
-        trackPreferences.setTrackCredentials(this, username, password)
+        trackPreferences.setCredentials(this, username, password)
     }
 
     // TODO: move this to an interactor, and update all trackers based on common data
@@ -111,7 +111,7 @@ abstract class TrackService(val id: Long, val name: String) {
 
                 insertTrack.await(track)
 
-                // TODO: merge into SyncChaptersWithTrackServiceTwoWay?
+                // TODO: merge into [SyncChapterProgressWithTrack]?
                 // Update chapter progress if newer chapters marked read locally
                 if (hasReadChapters) {
                     val latestLocalReadChapterNumber = allChapters
@@ -143,7 +143,7 @@ abstract class TrackService(val id: Long, val name: String) {
                     }
                 }
 
-                syncChapterProgressWithTrack.await(mangaId, track, this@TrackService)
+                syncChapterProgressWithTrack.await(mangaId, track, this@Tracker)
             }
         } catch (e: Throwable) {
             withUIContext { Injekt.get<Application>().toast(e.message) }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt
similarity index 56%
rename from app/src/main/java/eu/kanade/tachiyomi/data/track/TrackManager.kt
rename to app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt
index 94e3ae719..ff9a1b8f1 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackManager.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt
@@ -1,6 +1,5 @@
 package eu.kanade.tachiyomi.data.track
 
-import android.content.Context
 import eu.kanade.tachiyomi.data.track.anilist.Anilist
 import eu.kanade.tachiyomi.data.track.bangumi.Bangumi
 import eu.kanade.tachiyomi.data.track.kavita.Kavita
@@ -12,18 +11,12 @@ import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeList
 import eu.kanade.tachiyomi.data.track.shikimori.Shikimori
 import eu.kanade.tachiyomi.data.track.suwayomi.Suwayomi
 
-class TrackManager(context: Context) {
+class TrackerManager {
 
     companion object {
-        const val MYANIMELIST = 1L
         const val ANILIST = 2L
         const val KITSU = 3L
-        const val SHIKIMORI = 4L
-        const val BANGUMI = 5L
-        const val KOMGA = 6L
-        const val MANGA_UPDATES = 7L
         const val KAVITA = 8L
-        const val SUWAYOMI = 9L
 
         // SY --> Mangadex from Neko
         const val MDLIST = 60L
@@ -32,19 +25,19 @@ class TrackManager(context: Context) {
 
     val mdList = MdList(MDLIST)
 
-    val myAnimeList = MyAnimeList(MYANIMELIST)
+    val myAnimeList = MyAnimeList(1L)
     val aniList = Anilist(ANILIST)
     val kitsu = Kitsu(KITSU)
-    val shikimori = Shikimori(SHIKIMORI)
-    val bangumi = Bangumi(BANGUMI)
-    val komga = Komga(KOMGA)
-    val mangaUpdates = MangaUpdates(MANGA_UPDATES)
-    val kavita = Kavita(context, KAVITA)
-    val suwayomi = Suwayomi(SUWAYOMI)
+    val shikimori = Shikimori(4L)
+    val bangumi = Bangumi(5L)
+    val komga = Komga(6L)
+    val mangaUpdates = MangaUpdates(7L)
+    val kavita = Kavita(KAVITA)
+    val suwayomi = Suwayomi(9L)
 
-    val services = listOf(mdList, myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi)
+    val trackers = listOf(mdList, myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi)
 
-    fun getService(id: Long) = services.find { it.id == id }
+    fun get(id: Long) = trackers.find { it.id == id }
 
-    fun hasLoggedServices() = services.any { it.isLoggedIn }
+    fun hasLoggedIn() = trackers.any { it.isLoggedIn }
 }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt
index c6365106e..3ecf9119d 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt
@@ -4,15 +4,15 @@ import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.DeletableTrackService
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.DeletableTracker
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
 import uy.kohesive.injekt.injectLazy
 import tachiyomi.domain.track.model.Track as DomainTrack
 
-class Anilist(id: Long) : TrackService(id, "AniList"), DeletableTrackService {
+class Anilist(id: Long) : Tracker(id, "AniList"), DeletableTracker {
 
     companion object {
         const val READING = 1
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt
index 0e39d680f..46de5e735 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt
@@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.track.anilist
 
 import eu.kanade.domain.track.service.TrackPreferences
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import kotlinx.serialization.Serializable
 import uy.kohesive.injekt.injectLazy
@@ -20,7 +20,7 @@ data class ALManga(
     val total_chapters: Int,
 ) {
 
-    fun toTrack() = TrackSearch.create(TrackManager.ANILIST).apply {
+    fun toTrack() = TrackSearch.create(TrackerManager.ANILIST).apply {
         media_id = this@ALManga.media_id
         title = title_user_pref
         total_chapters = this@ALManga.total_chapters
@@ -50,7 +50,7 @@ data class ALUserManga(
     val manga: ALManga,
 ) {
 
-    fun toTrack() = Track.create(TrackManager.ANILIST).apply {
+    fun toTrack() = Track.create(TrackerManager.ANILIST).apply {
         media_id = manga.media_id
         title = manga.title_user_pref
         status = toTrackStatus()
@@ -62,7 +62,7 @@ data class ALUserManga(
         total_chapters = manga.total_chapters
     }
 
-    fun toTrackStatus() = when (list_status) {
+    private fun toTrackStatus() = when (list_status) {
         "CURRENT" -> Anilist.READING
         "COMPLETED" -> Anilist.COMPLETED
         "PAUSED" -> Anilist.ON_HOLD
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt
index f5d8ecc0d..71aff2f2a 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt
@@ -4,19 +4,19 @@ import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
 import uy.kohesive.injekt.injectLazy
 
-class Bangumi(id: Long) : TrackService(id, "Bangumi") {
+class Bangumi(id: Long) : Tracker(id, "Bangumi") {
 
     private val json: Json by injectLazy()
 
     private val interceptor by lazy { BangumiInterceptor(this) }
 
-    private val api by lazy { BangumiApi(client, interceptor) }
+    private val api by lazy { BangumiApi(id, client, interceptor) }
 
     override fun getScoreList(): List<String> {
         return IntRange(0, 10).map(Int::toString)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt
index 444487df1..6762fdcaf 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt
@@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.track.bangumi
 import android.net.Uri
 import androidx.core.net.toUri
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.POST
@@ -26,7 +25,11 @@ import uy.kohesive.injekt.injectLazy
 import java.net.URLEncoder
 import java.nio.charset.StandardCharsets
 
-class BangumiApi(private val client: OkHttpClient, interceptor: BangumiInterceptor) {
+class BangumiApi(
+    private val trackId: Long,
+    private val client: OkHttpClient,
+    interceptor: BangumiInterceptor,
+) {
 
     private val json: Json by injectLazy()
 
@@ -105,7 +108,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
         } else {
             0
         }
-        return TrackSearch.create(TrackManager.BANGUMI).apply {
+        return TrackSearch.create(trackId).apply {
             media_id = obj["id"]!!.jsonPrimitive.long
             title = obj["name_cn"]!!.jsonPrimitive.content
             cover_url = coverUrl
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt
index c11730754..f90038161 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt
@@ -1,20 +1,21 @@
 package eu.kanade.tachiyomi.data.track.kavita
 
-import android.content.Context
-import android.content.SharedPreferences
 import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.EnhancedTrackService
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.EnhancedTracker
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
+import eu.kanade.tachiyomi.source.ConfigurableSource
 import eu.kanade.tachiyomi.source.Source
 import tachiyomi.domain.manga.model.Manga
+import tachiyomi.domain.source.service.SourceManager
+import uy.kohesive.injekt.injectLazy
 import java.security.MessageDigest
 import tachiyomi.domain.track.model.Track as DomainTrack
 
-class Kavita(private val context: Context, id: Long) : TrackService(id, "Kavita"), EnhancedTrackService {
+class Kavita(id: Long) : Tracker(id, "Kavita"), EnhancedTracker {
 
     companion object {
         const val UNREAD = 1
@@ -27,6 +28,8 @@ class Kavita(private val context: Context, id: Long) : TrackService(id, "Kavita"
     private val interceptor by lazy { KavitaInterceptor(this) }
     val api by lazy { KavitaApi(client, interceptor) }
 
+    private val sourceManager: SourceManager by injectLazy()
+
     override fun getLogo(): Int = R.drawable.ic_tracker_kavita
 
     override fun getLogoColor() = Color.rgb(74, 198, 148)
@@ -83,7 +86,7 @@ class Kavita(private val context: Context, id: Long) : TrackService(id, "Kavita"
         saveCredentials("user", "pass")
     }
 
-    // TrackService.isLogged works by checking that credentials are saved.
+    // [Tracker].isLogged works by checking that credentials are saved.
     // By saving dummy, unused credentials, we can activate the tracker simply by login/logout
     override fun loginNoop() {
         saveCredentials("user", "pass")
@@ -110,28 +113,29 @@ class Kavita(private val context: Context, id: Long) : TrackService(id, "Kavita"
 
     fun loadOAuth() {
         val oauth = OAuth()
-        for (sourceId in 1..3) {
-            val authentication = oauth.authentications[sourceId - 1]
-            val sourceSuffixID by lazy {
-                val key = "kavita_$sourceId/all/1" // Hardcoded versionID to 1
+        for (id in 1..3) {
+            val authentication = oauth.authentications[id - 1]
+            val sourceId by lazy {
+                val key = "kavita_$id/all/1" // Hardcoded versionID to 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
             }
-            val preferences: SharedPreferences by lazy {
-                context.getSharedPreferences("source_$sourceSuffixID", Context.MODE_PRIVATE)
-            }
-            val prefApiUrl = preferences.getString("APIURL", "")!!
-            if (prefApiUrl.isEmpty()) {
+            val preferences = (sourceManager.get(sourceId) as ConfigurableSource).getPreferences()
+
+            val prefApiUrl = preferences.getString("APIURL", "")
+            val prefApiKey = preferences.getString("APIKEY", "")
+            if (prefApiUrl.isNullOrEmpty() || prefApiKey.isNullOrEmpty()) {
                 // Source not configured. Skip
                 continue
             }
-            val prefApiKey = preferences.getString("APIKEY", "")!!
+
             val token = api.getNewToken(apiUrl = prefApiUrl, apiKey = prefApiKey)
             if (token.isNullOrEmpty()) {
                 // Source is not accessible. Skip
                 continue
             }
+
             authentication.apiUrl = prefApiUrl
             authentication.jwtToken = token.toString()
         }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaModels.kt
index 3900c3f39..6f42f6836 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaModels.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaModels.kt
@@ -1,6 +1,6 @@
 package eu.kanade.tachiyomi.data.track.kavita
 
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import kotlinx.serialization.Serializable
 
@@ -22,7 +22,7 @@ data class SeriesDto(
     val libraryId: Int,
     val libraryName: String? = "",
 ) {
-    fun toTrack(): TrackSearch = TrackSearch.create(TrackManager.KAVITA).also {
+    fun toTrack(): TrackSearch = TrackSearch.create(TrackerManager.KAVITA).also {
         it.title = name
         it.summary = ""
     }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt
index ade2456bb..a2764c685 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt
@@ -4,15 +4,15 @@ import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.DeletableTrackService
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.DeletableTracker
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
 import uy.kohesive.injekt.injectLazy
 import java.text.DecimalFormat
 
-class Kitsu(id: Long) : TrackService(id, "Kitsu"), DeletableTrackService {
+class Kitsu(id: Long) : Tracker(id, "Kitsu"), DeletableTracker {
 
     companion object {
         const val READING = 1
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt
index 915c7ae8b..1cbf16c71 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt
@@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.track.kitsu
 
 import androidx.annotation.CallSuper
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.json.JsonObject
@@ -35,7 +35,7 @@ class KitsuSearchManga(obj: JsonObject) {
     private val endDate = obj["endDate"]?.jsonPrimitive?.contentOrNull
 
     @CallSuper
-    fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
+    fun toTrack() = TrackSearch.create(TrackerManager.KITSU).apply {
         media_id = this@KitsuSearchManga.id
         title = canonicalTitle
         total_chapters = chapterCount ?: 0
@@ -67,7 +67,7 @@ class KitsuLibManga(obj: JsonObject, manga: JsonObject) {
     private val ratingTwenty = obj["attributes"]!!.jsonObject["ratingTwenty"]?.jsonPrimitive?.contentOrNull
     val progress = obj["attributes"]!!.jsonObject["progress"]!!.jsonPrimitive.int
 
-    fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
+    fun toTrack() = TrackSearch.create(TrackerManager.KITSU).apply {
         media_id = libraryId
         title = canonicalTitle
         total_chapters = chapterCount ?: 0
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt
index 8937bd21a..450e502be 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt
@@ -4,8 +4,8 @@ import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.EnhancedTrackService
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.EnhancedTracker
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.source.Source
 import okhttp3.Dns
@@ -13,7 +13,7 @@ import okhttp3.OkHttpClient
 import tachiyomi.domain.manga.model.Manga
 import tachiyomi.domain.track.model.Track as DomainTrack
 
-class Komga(id: Long) : TrackService(id, "Komga"), EnhancedTrackService {
+class Komga(id: Long) : Tracker(id, "Komga"), EnhancedTracker {
 
     companion object {
         const val UNREAD = 1
@@ -26,7 +26,7 @@ class Komga(id: Long) : TrackService(id, "Komga"), EnhancedTrackService {
             .dns(Dns.SYSTEM) // don't use DNS over HTTPS as it breaks IP addressing
             .build()
 
-    val api by lazy { KomgaApi(client) }
+    val api by lazy { KomgaApi(id, client) }
 
     override fun getLogo() = R.drawable.ic_tracker_komga
 
@@ -85,7 +85,7 @@ class Komga(id: Long) : TrackService(id, "Komga"), EnhancedTrackService {
         saveCredentials("user", "pass")
     }
 
-    // TrackService.isLogged works by checking that credentials are saved.
+    // [Tracker].isLogged works by checking that credentials are saved.
     // By saving dummy, unused credentials, we can activate the tracker simply by login/logout
     override fun loginNoop() {
         saveCredentials("user", "pass")
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt
index 89e7fafbd..5992727ae 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt
@@ -1,7 +1,6 @@
 package eu.kanade.tachiyomi.data.track.komga
 
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.awaitSuccess
@@ -19,7 +18,10 @@ import uy.kohesive.injekt.injectLazy
 
 private const val READLIST_API = "/api/v1/readlists"
 
-class KomgaApi(private val client: OkHttpClient) {
+class KomgaApi(
+    private val trackId: Long,
+    private val client: OkHttpClient,
+) {
 
     private val json: Json by injectLazy()
 
@@ -85,13 +87,13 @@ class KomgaApi(private val client: OkHttpClient) {
         return getTrackSearch(track.tracking_url)
     }
 
-    private fun SeriesDto.toTrack(): TrackSearch = TrackSearch.create(TrackManager.KOMGA).also {
+    private fun SeriesDto.toTrack(): TrackSearch = TrackSearch.create(trackId).also {
         it.title = metadata.title
         it.summary = metadata.summary
         it.publishing_status = metadata.status
     }
 
-    private fun ReadListDto.toTrack(): TrackSearch = TrackSearch.create(TrackManager.KOMGA).also {
+    private fun ReadListDto.toTrack(): TrackSearch = TrackSearch.create(trackId).also {
         it.title = name
     }
 }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt
index 33df93222..417f6acb8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt
@@ -4,13 +4,13 @@ import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.DeletableTrackService
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.DeletableTracker
+import eu.kanade.tachiyomi.data.track.Tracker
 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, "MangaUpdates"), DeletableTrackService {
+class MangaUpdates(id: Long) : Tracker(id, "MangaUpdates"), DeletableTracker {
 
     companion object {
         const val READING_LIST = 0
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/mdlist/MdList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/mdlist/MdList.kt
index 64dff283d..1c373216c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/mdlist/MdList.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/mdlist/MdList.kt
@@ -4,8 +4,7 @@ import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackManager
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.source.model.FilterList
 import eu.kanade.tachiyomi.source.model.SManga
@@ -19,7 +18,7 @@ import tachiyomi.domain.manga.model.Manga
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 
-class MdList(id: Long) : TrackService(id, "MDList") {
+class MdList(id: Long) : Tracker(id, "MDList") {
 
     private val mdex by lazy { MdUtil.getEnabledMangaDex(Injekt.get()) }
 
@@ -128,7 +127,7 @@ class MdList(id: Long) : TrackService(id, "MDList") {
     }
 
     fun createInitialTracker(dbManga: Manga, mdManga: Manga = dbManga): Track {
-        return Track.create(TrackManager.MDLIST).apply {
+        return Track.create(id).apply {
             manga_id = dbManga.id
             status = FollowStatus.UNFOLLOWED.int
             tracking_url = MdUtil.baseUrl + mdManga.url
@@ -151,7 +150,7 @@ class MdList(id: Long) : TrackService(id, "MDList") {
         }
     }
 
-    private fun toTrackSearch(mangaInfo: SManga): TrackSearch = TrackSearch.create(TrackManager.MDLIST).apply {
+    private fun toTrackSearch(mangaInfo: SManga): TrackSearch = TrackSearch.create(id).apply {
         tracking_url = MdUtil.baseUrl + mangaInfo.url
         title = mangaInfo.title
         cover_url = mangaInfo.thumbnail_url.orEmpty()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt
index 3acbb035d..cd0d1eb1b 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt
@@ -4,14 +4,14 @@ import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.DeletableTrackService
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.DeletableTracker
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
 import uy.kohesive.injekt.injectLazy
 
-class MyAnimeList(id: Long) : TrackService(id, "MyAnimeList"), DeletableTrackService {
+class MyAnimeList(id: Long) : Tracker(id, "MyAnimeList"), DeletableTracker {
 
     companion object {
         const val READING = 1
@@ -28,7 +28,7 @@ class MyAnimeList(id: Long) : TrackService(id, "MyAnimeList"), DeletableTrackSer
     private val json: Json by injectLazy()
 
     private val interceptor by lazy { MyAnimeListInterceptor(this, getPassword()) }
-    private val api by lazy { MyAnimeListApi(client, interceptor) }
+    private val api by lazy { MyAnimeListApi(id, client, interceptor) }
 
     override val supportsReadingDates: Boolean = true
 
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt
index 1bfb4e581..d66a4c561 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt
@@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.track.myanimelist
 import android.net.Uri
 import androidx.core.net.toUri
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.POST
@@ -32,7 +31,11 @@ import uy.kohesive.injekt.injectLazy
 import java.text.SimpleDateFormat
 import java.util.Locale
 
-class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListInterceptor) {
+class MyAnimeListApi(
+    private val trackId: Long,
+    private val client: OkHttpClient,
+    interceptor: MyAnimeListInterceptor,
+) {
 
     private val json: Json by injectLazy()
 
@@ -106,7 +109,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
                     .parseAs<JsonObject>()
                     .let {
                         val obj = it.jsonObject
-                        TrackSearch.create(TrackManager.MYANIMELIST).apply {
+                        TrackSearch.create(trackId).apply {
                             media_id = obj["id"]!!.jsonPrimitive.long
                             title = obj["title"]!!.jsonPrimitive.content
                             summary = obj["synopsis"]?.jsonPrimitive?.content ?: ""
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt
index 463059055..312c9acbd 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt
@@ -4,14 +4,14 @@ import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.DeletableTrackService
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.DeletableTracker
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
 import uy.kohesive.injekt.injectLazy
 
-class Shikimori(id: Long) : TrackService(id, "Shikimori"), DeletableTrackService {
+class Shikimori(id: Long) : Tracker(id, "Shikimori"), DeletableTracker {
 
     companion object {
         const val READING = 1
@@ -26,7 +26,7 @@ class Shikimori(id: Long) : TrackService(id, "Shikimori"), DeletableTrackService
 
     private val interceptor by lazy { ShikimoriInterceptor(this) }
 
-    private val api by lazy { ShikimoriApi(client, interceptor) }
+    private val api by lazy { ShikimoriApi(id, client, interceptor) }
 
     override fun getScoreList(): List<String> {
         return IntRange(0, 10).map(Int::toString)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt
index 0137ed6df..fd62f18fc 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt
@@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.data.track.shikimori
 
 import androidx.core.net.toUri
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.network.DELETE
 import eu.kanade.tachiyomi.network.GET
@@ -28,7 +27,11 @@ import okhttp3.RequestBody.Companion.toRequestBody
 import tachiyomi.core.util.lang.withIOContext
 import uy.kohesive.injekt.injectLazy
 
-class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInterceptor) {
+class ShikimoriApi(
+    private val trackId: Long,
+    private val client: OkHttpClient,
+    interceptor: ShikimoriInterceptor,
+) {
 
     private val json: Json by injectLazy()
 
@@ -96,7 +99,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
     }
 
     private fun jsonToSearch(obj: JsonObject): TrackSearch {
-        return TrackSearch.create(TrackManager.SHIKIMORI).apply {
+        return TrackSearch.create(trackId).apply {
             media_id = obj["id"]!!.jsonPrimitive.long
             title = obj["name"]!!.jsonPrimitive.content
             total_chapters = obj["chapters"]!!.jsonPrimitive.int
@@ -110,7 +113,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
     }
 
     private fun jsonToTrack(obj: JsonObject, mangas: JsonObject): Track {
-        return Track.create(TrackManager.SHIKIMORI).apply {
+        return Track.create(trackId).apply {
             title = mangas["name"]!!.jsonPrimitive.content
             media_id = obj["id"]!!.jsonPrimitive.long
             total_chapters = mangas["chapters"]!!.jsonPrimitive.int
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt
index 3958bfa2e..2c7fd67c4 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt
@@ -4,16 +4,16 @@ import android.graphics.Color
 import androidx.annotation.StringRes
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.EnhancedTrackService
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.EnhancedTracker
+import eu.kanade.tachiyomi.data.track.Tracker
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 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, "Suwayomi"), EnhancedTrackService {
+class Suwayomi(id: Long) : Tracker(id, "Suwayomi"), EnhancedTracker {
 
-    val api by lazy { TachideskApi() }
+    val api by lazy { SuwayomiApi(id) }
 
     override fun getLogo() = R.drawable.ic_tracker_suwayomi
 
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/TachideskApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt
similarity index 87%
rename from app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/TachideskApi.kt
rename to app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt
index 86227095d..5c5367bec 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/TachideskApi.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt
@@ -4,7 +4,6 @@ import android.app.Application
 import android.content.Context
 import android.content.SharedPreferences
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.NetworkHelper
@@ -24,7 +23,7 @@ import uy.kohesive.injekt.injectLazy
 import java.nio.charset.Charset
 import java.security.MessageDigest
 
-class TachideskApi {
+class SuwayomiApi(private val trackId: Long) {
 
     private val network: NetworkHelper by injectLazy()
     private val json: Json by injectLazy()
@@ -62,7 +61,7 @@ class TachideskApi {
                 .parseAs<MangaDataClass>()
         }
 
-        TrackSearch.create(TrackManager.SUWAYOMI).apply {
+        TrackSearch.create(trackId).apply {
             title = manga.title
             cover_url = "$url/thumbnail"
             summary = manga.description.orEmpty()
@@ -101,26 +100,24 @@ class TachideskApi {
         return getTrackSearch(track.tracking_url)
     }
 
-    private val tachideskExtensionId by lazy {
+    private val sourceId 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
     }
 
     private val preferences: SharedPreferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$tachideskExtensionId", Context.MODE_PRIVATE)
+        Injekt.get<Application>().getSharedPreferences("source_$sourceId", Context.MODE_PRIVATE)
     }
 
     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 = ""
-        private const val LOGIN_TITLE = "Login (Basic Auth)"
-        private const val LOGIN_DEFAULT = ""
-        private const val PASSWORD_TITLE = "Password (Basic Auth)"
-        private const val PASSWORD_DEFAULT = ""
-    }
 }
+
+private const val ADDRESS_TITLE = "Server URL Address"
+private const val ADDRESS_DEFAULT = ""
+private const val LOGIN_TITLE = "Login (Basic Auth)"
+private const val LOGIN_DEFAULT = ""
+private const val PASSWORD_TITLE = "Password (Basic Auth)"
+private const val PASSWORD_DEFAULT = ""
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/TachideskDto.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiModels.kt
similarity index 100%
rename from app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/TachideskDto.kt
rename to app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiModels.kt
diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt
index 92a3d1ca3..41ee259bb 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt
@@ -5,7 +5,7 @@ import android.content.SharedPreferences
 import android.net.Uri
 import eu.kanade.domain.track.service.TrackPreferences
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.data.track.mdlist.MdList
 import eu.kanade.tachiyomi.network.asObservableSuccess
 import eu.kanade.tachiyomi.network.awaitSuccess
@@ -70,7 +70,7 @@ class MangaDex(delegate: HttpSource, val context: Context) :
     override val matchingHosts: List<String> = listOf("mangadex.org", "www.mangadex.org")
 
     val trackPreferences: TrackPreferences by injectLazy()
-    val mdList: MdList by lazy { Injekt.get<TrackManager>().mdList }
+    val mdList: MdList by lazy { Injekt.get<TrackerManager>().mdList }
 
     private val sourcePreferences: SharedPreferences by lazy {
         context.getSharedPreferences("source_$id", 0x0000)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
index 0e9684614..6ea7aebb0 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
@@ -15,19 +15,16 @@ 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.SyncChapterProgressWithTrack
 import eu.kanade.domain.manga.interactor.UpdateManga
 import eu.kanade.domain.manga.model.toDomainManga
 import eu.kanade.domain.source.interactor.GetExhSavedSearch
 import eu.kanade.domain.source.service.SourcePreferences
 import eu.kanade.domain.track.model.toDomainTrack
+import eu.kanade.domain.track.interactor.AddTracks
 import eu.kanade.domain.ui.UiPreferences
 import eu.kanade.presentation.util.ioCoroutineScope
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.cache.CoverCache
-import eu.kanade.tachiyomi.data.track.EnhancedTrackService
-import eu.kanade.tachiyomi.data.track.TrackManager
-import eu.kanade.tachiyomi.data.track.TrackService
 import eu.kanade.tachiyomi.source.CatalogueSource
 import eu.kanade.tachiyomi.source.model.FilterList
 import eu.kanade.tachiyomi.source.online.MetadataSource
@@ -81,7 +78,6 @@ import tachiyomi.domain.source.model.EXHSavedSearch
 import tachiyomi.domain.source.model.SavedSearch
 import tachiyomi.domain.source.repository.SourcePagingSourceType
 import tachiyomi.domain.source.service.SourceManager
-import tachiyomi.domain.track.interactor.InsertTrack
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 import xyz.nulldev.ts.api.http.serializer.FilterSerializer
@@ -108,8 +104,7 @@ open class BrowseSourceScreenModel(
     private val getManga: GetManga = Injekt.get(),
     private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
     private val updateManga: UpdateManga = Injekt.get(),
-    private val insertTrack: InsertTrack = Injekt.get(),
-    private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack = Injekt.get(),
+    private val addTracks: AddTracks = Injekt.get(),
 
     // SY -->
     unsortedPreferences: UnsortedPreferences = Injekt.get(),
@@ -121,8 +116,6 @@ open class BrowseSourceScreenModel(
     // SY <--
 ) : StateScreenModel<BrowseSourceScreenModel.State>(State(Listing.valueOf(listingQuery))) {
 
-    private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLoggedIn } }
-
     var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope)
 
     val source = sourceManager.getOrStub(sourceId)
@@ -209,7 +202,7 @@ open class BrowseSourceScreenModel(
                         // SY <--
                         .stateIn(ioCoroutineScope)
                 }
-                    .filter { !hideInLibraryItems || !it.value.favorite }
+                    .filter { !hideInLibraryItems || !it.value.first.favorite }
             }
                 .cachedIn(ioCoroutineScope)
         }
@@ -346,8 +339,7 @@ open class BrowseSourceScreenModel(
                 new = new.removeCovers(coverCache)
             } else {
                 setMangaDefaultChapterFlags.await(manga)
-
-                autoAddTrack(manga)
+                addTracks.bindEnhancedTracks(manga, source)
             }
 
             updateManga.await(new.toMangaUpdate())
@@ -384,25 +376,6 @@ open class BrowseSourceScreenModel(
         }
     }
 
-    private suspend fun autoAddTrack(manga: Manga) {
-        loggedServices
-            .filterIsInstance<EnhancedTrackService>()
-            .filter { it.accept(source) }
-            .forEach { service ->
-                try {
-                    service.match(manga)?.let { track ->
-                        track.manga_id = manga.id
-                        (service as TrackService).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" }
-                }
-            }
-    }
-
     // SY -->
     open fun createSourcePagingSource(query: String, filters: FilterList): SourcePagingSourceType {
         return getRemoteManga.subscribe(sourceId, query, filters)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
index f386f17fa..947e32766 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
@@ -29,8 +29,8 @@ import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.cache.CoverCache
 import eu.kanade.tachiyomi.data.download.DownloadCache
 import eu.kanade.tachiyomi.data.download.DownloadManager
-import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.data.track.TrackStatus
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.source.Source
 import eu.kanade.tachiyomi.source.model.SManga
 import eu.kanade.tachiyomi.source.online.HttpSource
@@ -131,7 +131,7 @@ class LibraryScreenModel(
     private val sourceManager: SourceManager = Injekt.get(),
     private val downloadManager: DownloadManager = Injekt.get(),
     private val downloadCache: DownloadCache = Injekt.get(),
-    private val trackManager: TrackManager = Injekt.get(),
+    private val trackerManager: TrackerManager = Injekt.get(),
     // SY -->
     private val unsortedPreferences: UnsortedPreferences = Injekt.get(),
     private val sourcePreferences: SourcePreferences = Injekt.get(),
@@ -170,18 +170,18 @@ class LibraryScreenModel(
                     ::Pair,
                 ),
                 // SY <--
-            ) { searchQuery, library, tracks, (loggedInTrackServices, _), (groupType, sort) ->
+            ) { searchQuery, library, tracks, (loggedInTrackers, _), (groupType, sort) ->
                 library
                     // SY -->
                     .applyGrouping(groupType)
                     // SY <--
-                    .applyFilters(tracks, loggedInTrackServices)
+                    .applyFilters(tracks, loggedInTrackers)
                     .applySort(/* SY --> */sort.takeIf { groupType != LibraryGroup.BY_DEFAULT } /* SY <-- */)
                     .mapValues { (_, value) ->
                         if (searchQuery != null) {
                             // Filter query
                             // SY -->
-                            filterLibrary(value, searchQuery, loggedInTrackServices)
+                            filterLibrary(value, searchQuery, loggedInTrackers)
                             // SY <--
                         } else {
                             // Don't do anything
@@ -268,7 +268,7 @@ class LibraryScreenModel(
      */
     private suspend fun LibraryMap.applyFilters(
         trackMap: Map<Long, List<Long>>,
-        loggedInTrackServices: Map<Long, TriState>,
+        loggedInTrackers: Map<Long, TriState>,
     ): LibraryMap {
         val prefs = getLibraryItemPreferencesFlow().first()
         val downloadedOnly = prefs.globalFilterDownloaded
@@ -279,10 +279,10 @@ class LibraryScreenModel(
         val filterBookmarked = prefs.filterBookmarked
         val filterCompleted = prefs.filterCompleted
 
-        val isNotLoggedInAnyTrack = loggedInTrackServices.isEmpty()
+        val isNotLoggedInAnyTrack = loggedInTrackers.isEmpty()
 
-        val excludedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null }
-        val includedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null }
+        val excludedTracks = loggedInTrackers.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null }
+        val includedTracks = loggedInTrackers.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null }
         val trackFiltersIsIgnored = includedTracks.isEmpty() && excludedTracks.isEmpty()
 
         // SY -->
@@ -550,14 +550,14 @@ class LibraryScreenModel(
      * @return map of track id with the filter value
      */
     private fun getTrackingFilterFlow(): Flow<Map<Long, TriState>> {
-        val loggedServices = trackManager.services.filter { it.isLoggedIn }
-        return if (loggedServices.isNotEmpty()) {
-            val prefFlows = loggedServices
+        val loggedInTrackers = trackerManager.trackers.filter { it.isLoggedIn }
+        return if (loggedInTrackers.isNotEmpty()) {
+            val prefFlows = loggedInTrackers
                 .map { libraryPreferences.filterTracking(it.id.toInt()).changes() }
                 .toTypedArray()
             combine(*prefFlows) {
-                loggedServices
-                    .mapIndexed { index, trackService -> trackService.id to it[index] }
+                loggedInTrackers
+                    .mapIndexed { index, tracker -> tracker.id to it[index] }
                     .toMap()
             }
         } else {
@@ -975,12 +975,12 @@ class LibraryScreenModel(
 
     private fun filterTracks(constraint: String, tracks: List<Track>, context: Context): Boolean {
         return tracks.fastAny { track ->
-            val trackService = trackManager.getService(track.syncId)
+            val trackService = trackerManager.get(track.syncId)
             if (trackService != null) {
                 val status = trackService.getStatus(track.status.toInt())?.let {
                     context.getString(it)
                 }
-                val name = trackManager.getService(track.syncId)?.name
+                val name = trackerManager.get(track.syncId)?.name
                 status?.contains(constraint, true) == true || name?.contains(constraint, true) == true
             } else {
                 false
@@ -1132,7 +1132,7 @@ class LibraryScreenModel(
                 val tracks = runBlocking { getTracks.await() }.groupBy { it.mangaId }
                 libraryManga.groupBy { item ->
                     val status = tracks[item.libraryManga.manga.id]?.firstNotNullOfOrNull { track ->
-                        TrackStatus.parseTrackerStatus(track.syncId, track.status)
+                        TrackStatus.parseTrackerStatus(trackerManager, track.syncId, track.status)
                     } ?: TrackStatus.OTHER
 
                     status.int
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt
index eb76c18a7..7f7cdf082 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt
@@ -5,7 +5,7 @@ import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.core.model.coroutineScope
 import eu.kanade.core.preference.asState
 import eu.kanade.domain.base.BasePreferences
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import tachiyomi.core.preference.Preference
 import tachiyomi.core.preference.TriState
 import tachiyomi.core.preference.getAndSet
@@ -24,11 +24,11 @@ class LibrarySettingsScreenModel(
     val libraryPreferences: LibraryPreferences = Injekt.get(),
     private val setDisplayMode: SetDisplayMode = Injekt.get(),
     private val setSortModeForCategory: SetSortModeForCategory = Injekt.get(),
-    private val trackManager: TrackManager = Injekt.get(),
+    private val trackerManager: TrackerManager = Injekt.get(),
 ) : ScreenModel {
 
-    val trackServices
-        get() = trackManager.services.filter { it.isLoggedIn }
+    val trackers
+        get() = trackerManager.trackers.filter { it.isLoggedIn }
 
     // SY -->
     val grouping by libraryPreferences.groupLibraryBy().asState(coroutineScope)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
index bd648e537..e2e848e17 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
@@ -172,7 +172,7 @@ class MainActivity : BaseActivity() {
                 libraryPreferences = libraryPreferences,
                 readerPreferences = Injekt.get(),
                 backupPreferences = Injekt.get(),
-                trackManager = Injekt.get(),
+                trackerManager = Injekt.get(),
             )
         } else {
             false
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
index 4e8a0f243..7c51bd3c2 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
@@ -28,9 +28,10 @@ import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.download.DownloadCache
 import eu.kanade.tachiyomi.data.download.DownloadManager
 import eu.kanade.tachiyomi.data.download.model.Download
-import eu.kanade.tachiyomi.data.track.EnhancedTrackService
-import eu.kanade.tachiyomi.data.track.TrackManager
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.EnhancedTracker
+import eu.kanade.tachiyomi.data.track.Tracker
+import eu.kanade.tachiyomi.data.track.TrackerManager
+import eu.kanade.tachiyomi.data.track.mdlist.MdList
 import eu.kanade.tachiyomi.network.HttpException
 import eu.kanade.tachiyomi.source.PagePreviewSource
 import eu.kanade.tachiyomi.source.Source
@@ -130,7 +131,7 @@ class MangaScreenModel(
     private val libraryPreferences: LibraryPreferences = Injekt.get(),
     readerPreferences: ReaderPreferences = Injekt.get(),
     uiPreferences: UiPreferences = Injekt.get(),
-    private val trackManager: TrackManager = Injekt.get(),
+    private val trackerManager: TrackerManager = Injekt.get(),
     private val downloadManager: DownloadManager = Injekt.get(),
     private val downloadCache: DownloadCache = Injekt.get(),
     private val getMangaAndChapters: GetMangaWithChapters = Injekt.get(),
@@ -169,7 +170,7 @@ class MangaScreenModel(
     private val successState: State.Success?
         get() = state.value as? State.Success
 
-    private val loggedServices by lazy { trackManager.services.filter { it.isLoggedIn } }
+    private val loggedInTrackers by lazy { trackerManager.trackers.filter { it.isLoggedIn } }
 
     val manga: Manga?
         get() = successState?.manga
@@ -738,14 +739,14 @@ class MangaScreenModel(
                 // Finally match with enhanced tracking when available
                 val source = state.source
                 state.trackItems
-                    .map { it.service }
-                    .filterIsInstance<EnhancedTrackService>()
+                    .map { it.tracker }
+                    .filterIsInstance<EnhancedTracker>()
                     .filter { it.accept(source) }
                     .forEach { service ->
                         launchIO {
                             try {
                                 service.match(manga)?.let { track ->
-                                    (service as TrackService).register(track, mangaId)
+                                    (service as Tracker).register(track, mangaId)
                                 }
                             } catch (e: Exception) {
                                 logcat(LogPriority.WARN, e) {
@@ -1451,16 +1452,16 @@ class MangaScreenModel(
             getTracks.subscribe(manga.id)
                 .catch { logcat(LogPriority.ERROR, it) }
                 .map { tracks ->
-                    loggedServices
+                    loggedInTrackers
                         // Map to TrackItem
                         .map { service -> TrackItem(tracks.find { it.syncId == service.id }, service) }
                         // Show only if the service supports this manga's source
-                        .filter { (it.service as? EnhancedTrackService)?.accept(source!!) ?: true }
+                        .filter { (it.tracker as? EnhancedTracker)?.accept(source!!) ?: true }
                 }
                 // SY -->
                 .map { trackItems ->
                     if (manga.source in mangaDexSourceIds || state.mergedData?.manga?.values.orEmpty().any { it.source in mangaDexSourceIds }) {
-                        val mdTrack = trackItems.firstOrNull { it.service.id == TrackManager.MDLIST }
+                        val mdTrack = trackItems.firstOrNull { it.tracker is MdList }
                         when {
                             mdTrack == null -> {
                                 trackItems
@@ -1488,10 +1489,10 @@ class MangaScreenModel(
         val mdManga = state.manga.takeIf { it.source in mangaDexSourceIds }
             ?: state.mergedData?.manga?.values?.find { it.source in mangaDexSourceIds }
             ?: throw IllegalArgumentException("Could not create initial track")
-        val track = trackManager.mdList.createInitialTracker(state.manga, mdManga)
+        val track = trackerManager.mdList.createInitialTracker(state.manga, mdManga)
             .toDomainTrack(false)!!
         insertTrack.await(track)
-        return TrackItem(getTracks.await(mangaId).first { it.syncId == TrackManager.MDLIST }, trackManager.mdList)
+        return TrackItem(getTracks.await(mangaId).first { it.syncId == trackerManager.mdList.id }, trackerManager.mdList)
     }
     // SY <--
 
@@ -1592,7 +1593,7 @@ class MangaScreenModel(
                 get() = trackItems.isNotEmpty()
 
             val trackingCount: Int
-                get() = trackItems.count { it.track != null && ((it.service.id == TrackManager.MDLIST && it.track.status != FollowStatus.UNFOLLOWED.int.toLong()) || it.service.id != TrackManager.MDLIST) }
+                get() = trackItems.count { it.track != null && ((it.tracker is MdList && it.track.status != FollowStatus.UNFOLLOWED.int.toLong()) || it.tracker !is MdList ) }
 
             /**
              * Applies the view filters to the list of chapters obtained from the database.
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt
index f92374b2a..f25dab5c8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt
@@ -46,14 +46,14 @@ import eu.kanade.presentation.track.TrackChapterSelector
 import eu.kanade.presentation.track.TrackDateSelector
 import eu.kanade.presentation.track.TrackInfoDialogHome
 import eu.kanade.presentation.track.TrackScoreSelector
-import eu.kanade.presentation.track.TrackServiceSearch
 import eu.kanade.presentation.track.TrackStatusSelector
+import eu.kanade.presentation.track.TrackerSearch
 import eu.kanade.presentation.util.Screen
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.track.DeletableTrackService
-import eu.kanade.tachiyomi.data.track.EnhancedTrackService
-import eu.kanade.tachiyomi.data.track.TrackManager
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.DeletableTracker
+import eu.kanade.tachiyomi.data.track.EnhancedTracker
+import eu.kanade.tachiyomi.data.track.Tracker
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
 import eu.kanade.tachiyomi.util.system.openInBrowser
@@ -105,7 +105,7 @@ data class TrackInfoDialogHomeScreen(
                 navigator.push(
                     TrackStatusSelectorScreen(
                         track = it.track!!,
-                        serviceId = it.service.id,
+                        serviceId = it.tracker.id,
                     ),
                 )
             },
@@ -113,7 +113,7 @@ data class TrackInfoDialogHomeScreen(
                 navigator.push(
                     TrackChapterSelectorScreen(
                         track = it.track!!,
-                        serviceId = it.service.id,
+                        serviceId = it.tracker.id,
                     ),
                 )
             },
@@ -121,7 +121,7 @@ data class TrackInfoDialogHomeScreen(
                 navigator.push(
                     TrackScoreSelectorScreen(
                         track = it.track!!,
-                        serviceId = it.service.id,
+                        serviceId = it.tracker.id,
                     ),
                 )
             },
@@ -129,7 +129,7 @@ data class TrackInfoDialogHomeScreen(
                 navigator.push(
                     TrackDateSelectorScreen(
                         track = it.track!!,
-                        serviceId = it.service.id,
+                        serviceId = it.tracker.id,
                         start = true,
                     ),
                 )
@@ -138,21 +138,21 @@ data class TrackInfoDialogHomeScreen(
                 navigator.push(
                     TrackDateSelectorScreen(
                         track = it.track!!,
-                        serviceId = it.service.id,
+                        serviceId = it.tracker.id,
                         start = false,
                     ),
                 )
             },
             onNewSearch = {
-                if (it.service is EnhancedTrackService) {
+                if (it.tracker is EnhancedTracker) {
                     sm.registerEnhancedTracking(it)
                 } else {
                     navigator.push(
-                        TrackServiceSearchScreen(
+                        TrackerSearchScreen(
                             mangaId = mangaId,
                             initialQuery = it.track?.title ?: mangaTitle,
                             currentUrl = it.track?.remoteUrl,
-                            serviceId = it.service.id,
+                            serviceId = it.tracker.id,
                         ),
                     )
                 }
@@ -160,10 +160,10 @@ data class TrackInfoDialogHomeScreen(
             onOpenInBrowser = { openTrackerInBrowser(context, it) },
             onRemoved = {
                 navigator.push(
-                    TrackServiceRemoveScreen(
+                    TrackerRemoveScreen(
                         mangaId = mangaId,
                         track = it.track!!,
-                        serviceId = it.service.id,
+                        serviceId = it.tracker.id,
                     ),
                 )
             },
@@ -201,12 +201,12 @@ data class TrackInfoDialogHomeScreen(
         }
 
         fun registerEnhancedTracking(item: TrackItem) {
-            item.service as EnhancedTrackService
+            item.tracker as EnhancedTracker
             coroutineScope.launchNonCancellable {
                 val manga = Injekt.get<GetManga>().await(mangaId) ?: return@launchNonCancellable
                 try {
-                    val matchResult = item.service.match(manga) ?: throw Exception()
-                    item.service.register(matchResult, mangaId)
+                    val matchResult = item.tracker.match(manga) ?: throw Exception()
+                    item.tracker.register(matchResult, mangaId)
                 } catch (e: Exception) {
                     withUIContext { Injekt.get<Application>().toast(R.string.error_no_match) }
                 }
@@ -236,13 +236,13 @@ data class TrackInfoDialogHomeScreen(
         }
 
         private fun List<Track>.mapToTrackItem(): List<TrackItem> {
-            val loggedServices = Injekt.get<TrackManager>().services.filter { it.isLoggedIn }
+            val loggedInTrackers = Injekt.get<TrackerManager>().trackers.filter { it.isLoggedIn }
             val source = Injekt.get<SourceManager>().getOrStub(sourceId)
-            return loggedServices
+            return loggedInTrackers
                 // Map to TrackItem
                 .map { service -> TrackItem(find { it.syncId == service.id }, service) }
                 // Show only if the service supports this manga's source
-                .filter { (it.service as? EnhancedTrackService)?.accept(source) ?: true }
+                .filter { (it.tracker as? EnhancedTracker)?.accept(source) ?: true }
         }
 
         @Immutable
@@ -263,7 +263,7 @@ private data class TrackStatusSelectorScreen(
         val sm = rememberScreenModel {
             Model(
                 track = track,
-                service = Injekt.get<TrackManager>().getService(serviceId)!!,
+                tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
             )
         }
         val state by sm.state.collectAsState()
@@ -281,11 +281,11 @@ private data class TrackStatusSelectorScreen(
 
     private class Model(
         private val track: Track,
-        private val service: TrackService,
+        private val tracker: Tracker,
     ) : StateScreenModel<Model.State>(State(track.status.toInt())) {
 
         fun getSelections(): Map<Int, Int?> {
-            return service.getStatusList().associateWith { service.getStatus(it) }
+            return tracker.getStatusList().associateWith { tracker.getStatus(it) }
         }
 
         fun setSelection(selection: Int) {
@@ -294,7 +294,7 @@ private data class TrackStatusSelectorScreen(
 
         fun setStatus() {
             coroutineScope.launchNonCancellable {
-                service.setRemoteStatus(track.toDbTrack(), state.value.selection)
+                tracker.setRemoteStatus(track.toDbTrack(), state.value.selection)
             }
         }
 
@@ -316,7 +316,7 @@ private data class TrackChapterSelectorScreen(
         val sm = rememberScreenModel {
             Model(
                 track = track,
-                service = Injekt.get<TrackManager>().getService(serviceId)!!,
+                tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
             )
         }
         val state by sm.state.collectAsState()
@@ -335,7 +335,7 @@ private data class TrackChapterSelectorScreen(
 
     private class Model(
         private val track: Track,
-        private val service: TrackService,
+        private val tracker: Tracker,
     ) : StateScreenModel<Model.State>(State(track.lastChapterRead.toInt())) {
 
         fun getRange(): Iterable<Int> {
@@ -353,7 +353,7 @@ private data class TrackChapterSelectorScreen(
 
         fun setChapter() {
             coroutineScope.launchNonCancellable {
-                service.setRemoteLastChapterRead(track.toDbTrack(), state.value.selection)
+                tracker.setRemoteLastChapterRead(track.toDbTrack(), state.value.selection)
             }
         }
 
@@ -375,7 +375,7 @@ private data class TrackScoreSelectorScreen(
         val sm = rememberScreenModel {
             Model(
                 track = track,
-                service = Injekt.get<TrackManager>().getService(serviceId)!!,
+                tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
             )
         }
         val state by sm.state.collectAsState()
@@ -394,11 +394,11 @@ private data class TrackScoreSelectorScreen(
 
     private class Model(
         private val track: Track,
-        private val service: TrackService,
-    ) : StateScreenModel<Model.State>(State(service.displayScore(track.toDbTrack()))) {
+        private val tracker: Tracker,
+    ) : StateScreenModel<Model.State>(State(tracker.displayScore(track.toDbTrack()))) {
 
         fun getSelections(): List<String> {
-            return service.getScoreList()
+            return tracker.getScoreList()
         }
 
         fun setSelection(selection: String) {
@@ -407,7 +407,7 @@ private data class TrackScoreSelectorScreen(
 
         fun setScore() {
             coroutineScope.launchNonCancellable {
-                service.setRemoteScore(track.toDbTrack(), state.value.selection)
+                tracker.setRemoteScore(track.toDbTrack(), state.value.selection)
             }
         }
 
@@ -486,7 +486,7 @@ private data class TrackDateSelectorScreen(
         val sm = rememberScreenModel {
             Model(
                 track = track,
-                service = Injekt.get<TrackManager>().getService(serviceId)!!,
+                tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
                 start = start,
             )
         }
@@ -515,7 +515,7 @@ private data class TrackDateSelectorScreen(
 
     private class Model(
         private val track: Track,
-        private val service: TrackService,
+        private val tracker: Tracker,
         private val start: Boolean,
     ) : ScreenModel {
 
@@ -534,15 +534,15 @@ private data class TrackDateSelectorScreen(
             val localMillis = millis.convertEpochMillisZone(ZoneOffset.UTC, ZoneOffset.systemDefault())
             coroutineScope.launchNonCancellable {
                 if (start) {
-                    service.setRemoteStartDate(track.toDbTrack(), localMillis)
+                    tracker.setRemoteStartDate(track.toDbTrack(), localMillis)
                 } else {
-                    service.setRemoteFinishDate(track.toDbTrack(), localMillis)
+                    tracker.setRemoteFinishDate(track.toDbTrack(), localMillis)
                 }
             }
         }
 
         fun confirmRemoveDate(navigator: Navigator) {
-            navigator.push(TrackDateRemoverScreen(track, service.id, start))
+            navigator.push(TrackDateRemoverScreen(track, tracker.id, start))
         }
     }
 }
@@ -559,7 +559,7 @@ private data class TrackDateRemoverScreen(
         val sm = rememberScreenModel {
             Model(
                 track = track,
-                service = Injekt.get<TrackManager>().getService(serviceId)!!,
+                tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
                 start = start,
             )
         }
@@ -614,25 +614,25 @@ private data class TrackDateRemoverScreen(
 
     private class Model(
         private val track: Track,
-        private val service: TrackService,
+        private val tracker: Tracker,
         private val start: Boolean,
     ) : ScreenModel {
 
-        fun getServiceName() = service.name
+        fun getServiceName() = tracker.name
 
         fun removeDate() {
             coroutineScope.launchNonCancellable {
                 if (start) {
-                    service.setRemoteStartDate(track.toDbTrack(), 0)
+                    tracker.setRemoteStartDate(track.toDbTrack(), 0)
                 } else {
-                    service.setRemoteFinishDate(track.toDbTrack(), 0)
+                    tracker.setRemoteFinishDate(track.toDbTrack(), 0)
                 }
             }
         }
     }
 }
 
-data class TrackServiceSearchScreen(
+data class TrackerSearchScreen(
     private val mangaId: Long,
     private val initialQuery: String,
     private val currentUrl: String?,
@@ -647,14 +647,14 @@ data class TrackServiceSearchScreen(
                 mangaId = mangaId,
                 currentUrl = currentUrl,
                 initialQuery = initialQuery,
-                service = Injekt.get<TrackManager>().getService(serviceId)!!,
+                tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
             )
         }
 
         val state by sm.state.collectAsState()
 
         var textFieldValue by remember { mutableStateOf(TextFieldValue(initialQuery)) }
-        TrackServiceSearch(
+        TrackerSearch(
             query = textFieldValue,
             onQueryChange = { textFieldValue = it },
             onDispatchQuery = { sm.trackingSearch(textFieldValue.text) },
@@ -673,7 +673,7 @@ data class TrackServiceSearchScreen(
         private val mangaId: Long,
         private val currentUrl: String? = null,
         initialQuery: String,
-        private val service: TrackService,
+        private val tracker: Tracker,
     ) : StateScreenModel<Model.State>(State()) {
 
         init {
@@ -690,7 +690,7 @@ data class TrackServiceSearchScreen(
 
                 val result = withIOContext {
                     try {
-                        val results = service.search(query)
+                        val results = tracker.search(query)
                         Result.success(results)
                     } catch (e: Throwable) {
                         Result.failure(e)
@@ -706,7 +706,7 @@ data class TrackServiceSearchScreen(
         }
 
         fun registerTracking(item: TrackSearch) {
-            coroutineScope.launchNonCancellable { service.register(item, mangaId) }
+            coroutineScope.launchNonCancellable { tracker.register(item, mangaId) }
         }
 
         fun updateSelection(selected: TrackSearch) {
@@ -721,7 +721,7 @@ data class TrackServiceSearchScreen(
     }
 }
 
-private data class TrackServiceRemoveScreen(
+private data class TrackerRemoveScreen(
     private val mangaId: Long,
     private val track: Track,
     private val serviceId: Long,
@@ -734,10 +734,10 @@ private data class TrackServiceRemoveScreen(
             Model(
                 mangaId = mangaId,
                 track = track,
-                service = Injekt.get<TrackManager>().getService(serviceId)!!,
+                tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
             )
         }
-        val serviceName = sm.getServiceName()
+        val serviceName = sm.getName()
         var removeRemoteTrack by remember { mutableStateOf(false) }
         AlertDialogContent(
             modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars),
@@ -758,7 +758,7 @@ private data class TrackServiceRemoveScreen(
                     Text(
                         text = stringResource(R.string.track_delete_text, serviceName),
                     )
-                    if (sm.isServiceDeletable()) {
+                    if (sm.isDeletable()) {
                         Row(verticalAlignment = Alignment.CenterVertically) {
                             Checkbox(checked = removeRemoteTrack, onCheckedChange = { removeRemoteTrack = it })
                             Text(text = stringResource(R.string.track_delete_remote_text, serviceName))
@@ -798,17 +798,17 @@ private data class TrackServiceRemoveScreen(
     private class Model(
         private val mangaId: Long,
         private val track: Track,
-        private val service: TrackService,
+        private val tracker: Tracker,
         private val deleteTrack: DeleteTrack = Injekt.get(),
     ) : ScreenModel {
 
-        fun getServiceName() = service.name
+        fun getName() = tracker.name
 
-        fun isServiceDeletable() = service is DeletableTrackService
+        fun isDeletable() = tracker is DeletableTracker
 
         fun deleteMangaFromService() {
             coroutineScope.launchNonCancellable {
-                (service as DeletableTrackService).delete(track.toDbTrack())
+                (tracker as DeletableTracker).delete(track.toDbTrack())
             }
         }
 
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackItem.kt
index d0883e593..fd46e0eec 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackItem.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackItem.kt
@@ -1,6 +1,6 @@
 package eu.kanade.tachiyomi.ui.manga.track
 
-import eu.kanade.tachiyomi.data.track.TrackService
+import eu.kanade.tachiyomi.data.track.Tracker
 import tachiyomi.domain.track.model.Track
 
-data class TrackItem(val track: Track?, val service: TrackService)
+data class TrackItem(val track: Track?, val tracker: Tracker)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
index 56f4ace95..f440340e2 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
@@ -577,6 +577,7 @@ class ReaderActivity : BaseActivity() {
                                 }
                             }
                         },
+                        state.dateRelativeTime
                     )
                 }
                 null -> {}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
index 724c46083..cd93f612c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
@@ -319,8 +319,9 @@ class ReaderViewModel @JvmOverloads constructor(
                     }
                     val mergedReferences = if (source is MergedSource) runBlocking { getMergedReferencesById.await(manga.id) } else emptyList()
                     val mergedManga = if (source is MergedSource) runBlocking { getMergedMangaById.await(manga.id) }.associateBy { it.id } else emptyMap()
+                    val relativeTime = uiPreferences.relativeTime().get()
                     // SY <--
-                    mutableState.update { it.copy(manga = manga /* SY --> */, meta = metadata, mergedManga = mergedManga/* SY <-- */) }
+                    mutableState.update { it.copy(manga = manga /* SY --> */, meta = metadata, mergedManga = mergedManga, dateRelativeTime = relativeTime/* SY <-- */) }
                     if (chapterId == -1L) chapterId = initialChapterId
 
                     val context = Injekt.get<Application>()
@@ -1193,6 +1194,7 @@ class ReaderViewModel @JvmOverloads constructor(
         val indexPageToShift: Int? = null,
         val indexChapterToShift: Long? = null,
         val doublePages: Boolean = false,
+        val dateRelativeTime: Boolean = true,
         // SY <--
     ) {
         val totalPages: Int
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt
index a1a91a003..92302d718 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt
@@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.ui.setting.track
 import android.content.Intent
 import android.net.Uri
 import android.os.Bundle
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
 import eu.kanade.tachiyomi.ui.main.MainActivity
 import eu.kanade.tachiyomi.util.view.setComposeContent
@@ -12,7 +12,7 @@ import uy.kohesive.injekt.injectLazy
 
 abstract class BaseOAuthLoginActivity : BaseActivity() {
 
-    internal val trackManager: TrackManager by injectLazy()
+    internal val trackerManager: TrackerManager by injectLazy()
 
     abstract fun handleResult(data: Uri?)
 
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/TrackLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/TrackLoginActivity.kt
index af13de51b..47cd88416 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/TrackLoginActivity.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/TrackLoginActivity.kt
@@ -20,11 +20,11 @@ class TrackLoginActivity : BaseOAuthLoginActivity() {
         val matchResult = regex.find(data.fragment.toString())
         if (matchResult?.groups?.get(1) != null) {
             lifecycleScope.launchIO {
-                trackManager.aniList.login(matchResult.groups[1]!!.value)
+                trackerManager.aniList.login(matchResult.groups[1]!!.value)
                 returnToSettings()
             }
         } else {
-            trackManager.aniList.logout()
+            trackerManager.aniList.logout()
             returnToSettings()
         }
     }
@@ -33,11 +33,11 @@ class TrackLoginActivity : BaseOAuthLoginActivity() {
         val code = data.getQueryParameter("code")
         if (code != null) {
             lifecycleScope.launchIO {
-                trackManager.bangumi.login(code)
+                trackerManager.bangumi.login(code)
                 returnToSettings()
             }
         } else {
-            trackManager.bangumi.logout()
+            trackerManager.bangumi.logout()
             returnToSettings()
         }
     }
@@ -46,11 +46,11 @@ class TrackLoginActivity : BaseOAuthLoginActivity() {
         val code = data.getQueryParameter("code")
         if (code != null) {
             lifecycleScope.launchIO {
-                trackManager.myAnimeList.login(code)
+                trackerManager.myAnimeList.login(code)
                 returnToSettings()
             }
         } else {
-            trackManager.myAnimeList.logout()
+            trackerManager.myAnimeList.logout()
             returnToSettings()
         }
     }
@@ -59,11 +59,11 @@ class TrackLoginActivity : BaseOAuthLoginActivity() {
         val code = data.getQueryParameter("code")
         if (code != null) {
             lifecycleScope.launchIO {
-                trackManager.shikimori.login(code)
+                trackerManager.shikimori.login(code)
                 returnToSettings()
             }
         } else {
-            trackManager.shikimori.logout()
+            trackerManager.shikimori.logout()
             returnToSettings()
         }
     }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt
index e6d7117f4..11e0ffaf0 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt
@@ -10,7 +10,7 @@ import eu.kanade.core.util.fastMapNotNull
 import eu.kanade.presentation.more.stats.StatsScreenState
 import eu.kanade.presentation.more.stats.data.StatsData
 import eu.kanade.tachiyomi.data.download.DownloadManager
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.source.model.SManga
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -37,13 +37,13 @@ class StatsScreenModel(
     private val getTotalReadDuration: GetTotalReadDuration = Injekt.get(),
     private val getTracks: GetTracks = Injekt.get(),
     private val preferences: LibraryPreferences = Injekt.get(),
-    private val trackManager: TrackManager = Injekt.get(),
+    private val trackerManager: TrackerManager = Injekt.get(),
     // SY -->
     private val getReadMangaNotInLibrary: GetReadMangaNotInLibrary = Injekt.get(),
     // SY <--
 ) : StateScreenModel<StatsScreenState>(StatsScreenState.Loading) {
 
-    private val loggedServices by lazy { trackManager.services.fastFilter { it.isLoggedIn } }
+    private val loggedInTrackers by lazy { trackerManager.trackers.fastFilter { it.isLoggedIn } }
 
     // SY -->
     private val _allRead = MutableStateFlow(false)
@@ -91,7 +91,7 @@ class StatsScreenModel(
             val trackersStatData = StatsData.Trackers(
                 trackedTitleCount = mangaTrackMap.count { it.value.isNotEmpty() },
                 meanScore = meanScore,
-                trackerCount = loggedServices.size,
+                trackerCount = loggedInTrackers.size,
             )
 
             mutableState.update {
@@ -136,10 +136,10 @@ class StatsScreenModel(
     }
 
     private suspend fun getMangaTrackMap(libraryManga: List<LibraryManga>): Map<Long, List<Track>> {
-        val loggedServicesIds = loggedServices.map { it.id }.toHashSet()
+        val loggedInTrackerIds = loggedInTrackers.map { it.id }.toHashSet()
         return libraryManga.associate { manga ->
             val tracks = getTracks.await(manga.id)
-                .fastFilter { it.syncId in loggedServicesIds }
+                .fastFilter { it.syncId in loggedInTrackerIds }
 
             manga.id to tracks
         }
@@ -165,7 +165,7 @@ class StatsScreenModel(
     }
 
     private fun get10PointScore(track: Track): Double {
-        val service = trackManager.getService(track.syncId)!!
+        val service = trackerManager.get(track.syncId)!!
         return service.get10PointScore(track)
     }
 
diff --git a/app/src/main/java/exh/EXHMigrations.kt b/app/src/main/java/exh/EXHMigrations.kt
index ea940b333..aeb66f468 100644
--- a/app/src/main/java/exh/EXHMigrations.kt
+++ b/app/src/main/java/exh/EXHMigrations.kt
@@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.BuildConfig
 import eu.kanade.tachiyomi.core.security.SecurityPreferences
 import eu.kanade.tachiyomi.data.backup.BackupCreateJob
 import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.network.NetworkPreferences
 import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
 import eu.kanade.tachiyomi.source.Source
@@ -98,7 +98,7 @@ object EXHMigrations {
         libraryPreferences: LibraryPreferences,
         readerPreferences: ReaderPreferences,
         backupPreferences: BackupPreferences,
-        trackManager: TrackManager,
+        trackerManager: TrackerManager,
     ): Boolean {
         val lastVersionCode = preferenceStore.getInt("eh_last_version_code", 0)
         val oldVersion = lastVersionCode.get()
@@ -205,7 +205,7 @@ object EXHMigrations {
                 }
                 if (oldVersion under 12) {
                     // Force MAL log out due to login flow change
-                    trackManager.myAnimeList.logout()
+                    trackerManager.myAnimeList.logout()
                 }
                 if (oldVersion under 14) {
                     // Migrate DNS over HTTPS setting
@@ -502,7 +502,7 @@ object EXHMigrations {
                 }
                 if (oldVersion under 45) {
                     // Force MangaDex log out due to login flow change
-                    trackManager.mdList.logout()
+                    trackerManager.mdList.logout()
                 }
                 if (oldVersion under 51) {
                     LibraryUpdateJob.cancelAllWorks(context)
@@ -520,7 +520,7 @@ object EXHMigrations {
                         "pref_filter_library_bookmarked",
                         "pref_filter_library_completed",
                         "pref_filter_library_lewd",
-                    ) + trackManager.services.map { "pref_filter_library_tracked_${it.id}" }
+                    ) + trackerManager.trackers.map { "pref_filter_library_tracked_${it.id}" }
 
                     prefKeys.forEach { key ->
                         val pref = preferenceStore.getInt(key, 0)
diff --git a/app/src/main/java/exh/debug/DebugFunctions.kt b/app/src/main/java/exh/debug/DebugFunctions.kt
index b7e63c5c5..343179cd3 100644
--- a/app/src/main/java/exh/debug/DebugFunctions.kt
+++ b/app/src/main/java/exh/debug/DebugFunctions.kt
@@ -8,7 +8,7 @@ import eu.kanade.domain.source.service.SourcePreferences
 import eu.kanade.domain.ui.UiPreferences
 import eu.kanade.tachiyomi.core.security.SecurityPreferences
 import eu.kanade.tachiyomi.data.backup.models.Backup
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.network.NetworkPreferences
 import eu.kanade.tachiyomi.source.AndroidSourceManager
 import eu.kanade.tachiyomi.source.online.all.NHentai
@@ -51,7 +51,7 @@ object DebugFunctions {
     val libraryPrefs: LibraryPreferences by injectLazy()
     val readerPrefs: ReaderPreferences by injectLazy()
     val backupPrefs: BackupPreferences by injectLazy()
-    val trackManager: TrackManager by injectLazy()
+    val trackerManager: TrackerManager by injectLazy()
     val sourceManager: SourceManager by injectLazy()
     val updateManga: UpdateManga by injectLazy()
     val getFavorites: GetFavorites by injectLazy()
@@ -64,13 +64,13 @@ object DebugFunctions {
     fun forceUpgradeMigration() {
         val lastVersionCode = prefsStore.getInt("eh_last_version_code", 0)
         lastVersionCode.set(1)
-        EXHMigrations.upgrade(app, prefsStore, basePrefs, uiPrefs, networkPrefs, sourcePrefs, securityPrefs, libraryPrefs, readerPrefs, backupPrefs, trackManager)
+        EXHMigrations.upgrade(app, prefsStore, basePrefs, uiPrefs, networkPrefs, sourcePrefs, securityPrefs, libraryPrefs, readerPrefs, backupPrefs, trackerManager)
     }
 
     fun forceSetupJobs() {
         val lastVersionCode = prefsStore.getInt("eh_last_version_code", 0)
         lastVersionCode.set(0)
-        EXHMigrations.upgrade(app, prefsStore, basePrefs, uiPrefs, networkPrefs, sourcePrefs, securityPrefs, libraryPrefs, readerPrefs, backupPrefs, trackManager)
+        EXHMigrations.upgrade(app, prefsStore, basePrefs, uiPrefs, networkPrefs, sourcePrefs, securityPrefs, libraryPrefs, readerPrefs, backupPrefs, trackerManager)
     }
 
     fun resetAgedFlagInEXHManga() {
diff --git a/app/src/main/java/exh/md/handlers/FollowsHandler.kt b/app/src/main/java/exh/md/handlers/FollowsHandler.kt
index 2cf06f1d5..3e2cdbc43 100644
--- a/app/src/main/java/exh/md/handlers/FollowsHandler.kt
+++ b/app/src/main/java/exh/md/handlers/FollowsHandler.kt
@@ -1,7 +1,7 @@
 package exh.md.handlers
 
 import eu.kanade.tachiyomi.data.database.models.Track
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.source.model.MetadataMangasPage
 import eu.kanade.tachiyomi.source.model.SManga
 import exh.md.dto.MangaDataDto
@@ -153,7 +153,7 @@ class FollowsHandler(
                 service.mangasRating(mangaId).ratings.asMdMap<PersonalRatingDto>()[mangaId]
             }
             val (followStatus, rating) = followStatusDef.await() to ratingDef.await()
-            Track.create(TrackManager.MDLIST).apply {
+            Track.create(TrackerManager.MDLIST).apply {
                 title = ""
                 status = followStatus.int
                 tracking_url = url
diff --git a/app/src/main/java/exh/recs/RecommendsPagingSource.kt b/app/src/main/java/exh/recs/RecommendsPagingSource.kt
index de4072c14..b46cfabc8 100644
--- a/app/src/main/java/exh/recs/RecommendsPagingSource.kt
+++ b/app/src/main/java/exh/recs/RecommendsPagingSource.kt
@@ -1,6 +1,6 @@
 package exh.recs
 
-import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.data.track.TrackerManager
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.NetworkHelper
 import eu.kanade.tachiyomi.network.POST
@@ -268,6 +268,7 @@ open class RecommendsPagingSource(
     private val smart: Boolean = true,
     private var preferredApi: API = API.MYANIMELIST,
 ) : SourcePagingSource(source) {
+    val trackerManager: TrackerManager by injectLazy()
     val getTracks: GetTracks by injectLazy()
 
     override suspend fun requestNextPage(currentPage: Int): MangasPage {
@@ -280,8 +281,8 @@ open class RecommendsPagingSource(
         val recs = apiList.firstNotNullOfOrNull { (key, api) ->
             try {
                 val id = when (key) {
-                    API.MYANIMELIST -> tracks.find { it.syncId == TrackManager.MYANIMELIST }?.remoteId
-                    API.ANILIST -> tracks.find { it.syncId == TrackManager.ANILIST }?.remoteId
+                    API.MYANIMELIST -> tracks.find { it.syncId == trackerManager.myAnimeList.id }?.remoteId
+                    API.ANILIST -> tracks.find { it.syncId == trackerManager.aniList.id }?.remoteId
                 }
 
                 val recs = if (id != null) {
diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml
index c8d6858cf..92e05c245 100755
--- a/i18n/src/main/res/values/strings.xml
+++ b/i18n/src/main/res/values/strings.xml
@@ -459,13 +459,13 @@
     <!-- Tracking section -->
     <string name="tracking_guide">Tracking guide</string>
     <string name="pref_auto_update_manga_sync">Update progress after reading</string>
-    <string name="services">Services</string>
-    <string name="tracking_info">One-way sync to update the chapter progress in tracking services. Set up tracking for individual entries from their tracking button.</string>
-    <string name="enhanced_services">Enhanced services</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="enhanced_services">Enhanced trackers</string>
     <string name="enhanced_services_not_installed">Available but source not installed: %s</string>
-    <string name="enhanced_tracking_info">Services that provide enhanced features for specific sources. Entries are automatically tracked when added to your library.</string>
+    <string name="enhanced_tracking_info">Provides enhanced features for specific sources. Entries are automatically tracked when added to your library.</string>
     <string name="action_track">Track</string>
-    <string name="track_activity_name">Tracking login</string>
+    <string name="track_activity_name">Tracker login</string>
 
       <!-- Browse section -->
     <string name="pref_hide_in_library_items">Hide entries already in library</string>