Tracker-related cleanup

(cherry picked from commit c8e226acb2ef460f78a298ef44412bb5b2915daf)

# Conflicts:
#	app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt
This commit is contained in:
arkon 2023-09-25 23:22:16 -04:00 committed by Jobobby04
parent e2348638f0
commit c24cdb598e
64 changed files with 470 additions and 438 deletions

View File

@ -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()) }

View File

@ -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" }
}
}
}
}
}

View File

@ -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())
val updatedTrack = service!!.refresh(track.toDbTrack())
insertTrack.await(updatedTrack.toDomainTrack()!!)
syncChapterProgressWithTrack.await(mangaId, track, service)
}
null
} catch (e: Throwable) {
service to e

View File

@ -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) {

View File

@ -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
}

View File

@ -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)

View File

@ -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)

View File

@ -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) {

View File

@ -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,

View File

@ -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() },
)

View File

@ -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(

View File

@ -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),
),

View File

@ -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,
)

View File

@ -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),

View File

@ -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 <--
},

View File

@ -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

View File

@ -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,

View File

@ -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,
)
}
}

View File

@ -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) }

View File

@ -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)

View File

@ -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()

View File

@ -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()) {

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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) }

View File

@ -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 }
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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()
}

View File

@ -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 = ""
}

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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
}
}

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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 ?: ""

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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 = ""

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -172,7 +172,7 @@ class MainActivity : BaseActivity() {
libraryPreferences = libraryPreferences,
readerPreferences = Injekt.get(),
backupPreferences = Injekt.get(),
trackManager = Injekt.get(),
trackerManager = Injekt.get(),
)
} else {
false

View File

@ -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.

View File

@ -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())
}
}

View File

@ -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)

View File

@ -577,6 +577,7 @@ class ReaderActivity : BaseActivity() {
}
}
},
state.dateRelativeTime
)
}
null -> {}

View File

@ -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

View File

@ -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?)

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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() {

View File

@ -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

View File

@ -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) {

View File

@ -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>