Last commit merged: 772db51593
This commit is contained in:
LuftVerbot 2023-11-19 14:39:59 +01:00
parent 2c4230376c
commit f1cd8f69d3
67 changed files with 355 additions and 414 deletions

View file

@ -1,5 +1,4 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jmailen.gradle.kotlinter.tasks.LintTask
import java.io.FileInputStream import java.io.FileInputStream
import java.util.Properties import java.util.Properties
@ -293,10 +292,6 @@ androidComponents {
} }
tasks { tasks {
withType<LintTask>().configureEach {
exclude { it.file.path.contains("generated[\\\\/]".toRegex()) }
}
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers) // See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
withType<KotlinCompile> { withType<KotlinCompile> {
kotlinOptions.freeCompilerArgs += listOf( kotlinOptions.freeCompilerArgs += listOf(

View file

@ -13,11 +13,11 @@ import eu.kanade.domain.extension.manga.interactor.GetExtensionSources
import eu.kanade.domain.extension.manga.interactor.GetMangaExtensionLanguages import eu.kanade.domain.extension.manga.interactor.GetMangaExtensionLanguages
import eu.kanade.domain.extension.manga.interactor.GetMangaExtensionsByType import eu.kanade.domain.extension.manga.interactor.GetMangaExtensionsByType
import eu.kanade.domain.items.chapter.interactor.SetReadStatus import eu.kanade.domain.items.chapter.interactor.SetReadStatus
import eu.kanade.domain.items.chapter.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
import eu.kanade.domain.items.episode.interactor.SetSeenStatus import eu.kanade.domain.items.episode.interactor.SetSeenStatus
import eu.kanade.domain.items.episode.interactor.SyncEpisodeProgressWithTrack
import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithSource import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithSource
import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithTrackServiceTwoWay
import eu.kanade.domain.source.anime.interactor.GetAnimeSourcesWithFavoriteCount import eu.kanade.domain.source.anime.interactor.GetAnimeSourcesWithFavoriteCount
import eu.kanade.domain.source.anime.interactor.GetEnabledAnimeSources import eu.kanade.domain.source.anime.interactor.GetEnabledAnimeSources
import eu.kanade.domain.source.anime.interactor.GetLanguagesWithAnimeSources import eu.kanade.domain.source.anime.interactor.GetLanguagesWithAnimeSources
@ -235,7 +235,7 @@ class DomainModule : InjektModule {
addFactory { SetSeenStatus(get(), get(), get(), get()) } addFactory { SetSeenStatus(get(), get(), get(), get()) }
addFactory { ShouldUpdateDbEpisode() } addFactory { ShouldUpdateDbEpisode() }
addFactory { SyncEpisodesWithSource(get(), get(), get(), get(), get(), get(), get()) } addFactory { SyncEpisodesWithSource(get(), get(), get(), get(), get(), get(), get()) }
addFactory { SyncEpisodesWithTrackServiceTwoWay(get(), get()) } addFactory { SyncEpisodeProgressWithTrack(get(), get(), get()) }
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) } addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
addFactory { GetChapter(get()) } addFactory { GetChapter(get()) }
@ -244,7 +244,7 @@ class DomainModule : InjektModule {
addFactory { SetReadStatus(get(), get(), get(), get()) } addFactory { SetReadStatus(get(), get(), get(), get()) }
addFactory { ShouldUpdateDbChapter() } addFactory { ShouldUpdateDbChapter() }
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get()) } addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get()) }
addFactory { SyncChaptersWithTrackServiceTwoWay(get(), get()) } addFactory { SyncChapterProgressWithTrack(get(), get(), get()) }
addSingletonFactory<AnimeHistoryRepository> { AnimeHistoryRepositoryImpl(get()) } addSingletonFactory<AnimeHistoryRepository> { AnimeHistoryRepositoryImpl(get()) }
addFactory { GetAnimeHistory(get()) } addFactory { GetAnimeHistory(get()) }

View file

@ -1,27 +1,35 @@
package eu.kanade.domain.items.chapter.interactor package eu.kanade.domain.items.chapter.interactor
import eu.kanade.domain.track.manga.model.toDbTrack import eu.kanade.domain.track.manga.model.toDbTrack
import eu.kanade.tachiyomi.data.track.EnhancedMangaTrackService
import eu.kanade.tachiyomi.data.track.MangaTrackService import eu.kanade.tachiyomi.data.track.MangaTrackService
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.items.chapter.interactor.GetChapterByMangaId
import tachiyomi.domain.items.chapter.interactor.UpdateChapter import tachiyomi.domain.items.chapter.interactor.UpdateChapter
import tachiyomi.domain.items.chapter.model.Chapter
import tachiyomi.domain.items.chapter.model.toChapterUpdate import tachiyomi.domain.items.chapter.model.toChapterUpdate
import tachiyomi.domain.track.manga.interactor.InsertMangaTrack import tachiyomi.domain.track.manga.interactor.InsertMangaTrack
import tachiyomi.domain.track.manga.model.MangaTrack import tachiyomi.domain.track.manga.model.MangaTrack
import uy.kohesive.injekt.api.get
class SyncChaptersWithTrackServiceTwoWay( class SyncChapterProgressWithTrack(
private val updateChapter: UpdateChapter, private val updateChapter: UpdateChapter,
private val insertTrack: InsertMangaTrack, private val insertTrack: InsertMangaTrack,
private val getChapterByMangaId: GetChapterByMangaId,
) { ) {
suspend fun await( suspend fun await(
chapters: List<Chapter>, mangaId: Long,
remoteTrack: MangaTrack, remoteTrack: MangaTrack,
service: MangaTrackService, service: MangaTrackService,
) { ) {
val sortedChapters = chapters.sortedBy { it.chapterNumber } if (service !is EnhancedMangaTrackService) {
return
}
val sortedChapters = getChapterByMangaId.await(mangaId)
.sortedBy { it.chapterNumber }
.filter { it.isRecognizedNumber }
val chapterUpdates = sortedChapters val chapterUpdates = sortedChapters
.filter { chapter -> chapter.chapterNumber <= remoteTrack.lastChapterRead && !chapter.read } .filter { chapter -> chapter.chapterNumber <= remoteTrack.lastChapterRead && !chapter.read }
.map { it.copy(read = true).toChapterUpdate() } .map { it.copy(read = true).toChapterUpdate() }

View file

@ -2,26 +2,35 @@ package eu.kanade.domain.items.episode.interactor
import eu.kanade.domain.track.anime.model.toDbTrack import eu.kanade.domain.track.anime.model.toDbTrack
import eu.kanade.tachiyomi.data.track.AnimeTrackService import eu.kanade.tachiyomi.data.track.AnimeTrackService
import eu.kanade.tachiyomi.data.track.EnhancedAnimeTrackService
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.items.episode.interactor.GetEpisodeByAnimeId
import tachiyomi.domain.items.episode.interactor.UpdateEpisode import tachiyomi.domain.items.episode.interactor.UpdateEpisode
import tachiyomi.domain.items.episode.model.Episode
import tachiyomi.domain.items.episode.model.toEpisodeUpdate import tachiyomi.domain.items.episode.model.toEpisodeUpdate
import tachiyomi.domain.track.anime.interactor.InsertAnimeTrack import tachiyomi.domain.track.anime.interactor.InsertAnimeTrack
import tachiyomi.domain.track.anime.model.AnimeTrack import tachiyomi.domain.track.anime.model.AnimeTrack
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class SyncEpisodesWithTrackServiceTwoWay( class SyncEpisodeProgressWithTrack(
private val updateEpisode: UpdateEpisode, private val updateEpisode: UpdateEpisode,
private val insertTrack: InsertAnimeTrack, private val insertTrack: InsertAnimeTrack,
private val getEpisodeByAnimeId: GetEpisodeByAnimeId,
) { ) {
suspend fun await( suspend fun await(
episodes: List<Episode>, animeId: Long,
remoteTrack: AnimeTrack, remoteTrack: AnimeTrack,
service: AnimeTrackService, service: AnimeTrackService,
) { ) {
val sortedEpisodes = episodes.sortedBy { it.episodeNumber } if (service !is EnhancedAnimeTrackService) {
return
}
val sortedEpisodes = getEpisodeByAnimeId.await(animeId)
.sortedBy { it.episodeNumber }
.filter { it.isRecognizedNumber }
val episodeUpdates = sortedEpisodes val episodeUpdates = sortedEpisodes
.filter { episode -> episode.episodeNumber <= remoteTrack.lastEpisodeSeen && !episode.seen } .filter { episode -> episode.episodeNumber <= remoteTrack.lastEpisodeSeen && !episode.seen }
.map { it.copy(seen = true).toEpisodeUpdate() } .map { it.copy(seen = true).toEpisodeUpdate() }

View file

@ -0,0 +1,48 @@
package eu.kanade.domain.track.anime.interactor
import eu.kanade.domain.items.episode.interactor.SyncEpisodeProgressWithTrack
import eu.kanade.domain.track.anime.model.toDbTrack
import eu.kanade.domain.track.anime.model.toDomainTrack
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.supervisorScope
import tachiyomi.domain.track.anime.interactor.GetAnimeTracks
import tachiyomi.domain.track.anime.interactor.InsertAnimeTrack
class RefreshAnimeTracks(
private val getTracks: GetAnimeTracks,
private val trackManager: TrackManager,
private val insertTrack: InsertAnimeTrack,
private val syncEpisodeProgressWithTrack: SyncEpisodeProgressWithTrack,
) {
/**
* Fetches updated tracking data from all logged in trackers.
*
* @return Failed updates.
*/
suspend fun await(animeId: Long): List<Pair<TrackService?, Throwable>> {
return supervisorScope {
return@supervisorScope getTracks.await(animeId)
.map { track ->
async {
val service = trackManager.getService(track.syncId)
return@async try {
if (service?.isLoggedIn == true) {
val updatedTrack = service.animeService.refresh(track.toDbTrack())
insertTrack.await(updatedTrack.toDomainTrack()!!)
syncEpisodeProgressWithTrack.await(animeId, track, service.animeService)
}
null
} catch (e: Throwable) {
service to e
}
}
}
.awaitAll()
.filterNotNull()
}
}
}

View file

@ -48,7 +48,7 @@ class DelayedAnimeTrackingUpdateJob(context: Context, workerParams: WorkerParame
.forEach { animeTrack -> .forEach { animeTrack ->
try { try {
val service = trackManager.getService(animeTrack.syncId) val service = trackManager.getService(animeTrack.syncId)
if (service != null && service.isLogged) { if (service != null && service.isLoggedIn) {
logcat(LogPriority.DEBUG) { "Updating delayed track item: ${animeTrack.id}, last episode seen: ${animeTrack.lastEpisodeSeen}" } logcat(LogPriority.DEBUG) { "Updating delayed track item: ${animeTrack.id}, last episode seen: ${animeTrack.lastEpisodeSeen}" }
service.animeService.update(animeTrack.toDbTrack(), true) service.animeService.update(animeTrack.toDbTrack(), true)
insertTrack.await(animeTrack) insertTrack.await(animeTrack)

View file

@ -0,0 +1,52 @@
package eu.kanade.domain.track.manga.interactor
import eu.kanade.domain.items.chapter.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.track.manga.model.toDbTrack
import eu.kanade.domain.track.manga.model.toDomainTrack
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.supervisorScope
import tachiyomi.domain.track.manga.interactor.GetMangaTracks
import tachiyomi.domain.track.manga.interactor.InsertMangaTrack
class RefreshMangaTracks(
private val getTracks: GetMangaTracks,
private val trackManager: TrackManager,
private val insertTrack: InsertMangaTrack,
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
) {
/**
* Fetches updated tracking data from all logged in trackers.
*
* @return Failed updates.
*/
suspend fun await(mangaId: Long): List<Pair<TrackService?, Throwable>> {
return supervisorScope {
return@supervisorScope getTracks.await(mangaId)
.map { track ->
async {
val service = trackManager.getService(track.syncId)
return@async try {
if (service?.isLoggedIn == true) {
val updatedTrack = service.mangaService.refresh(track.toDbTrack())
insertTrack.await(updatedTrack.toDomainTrack()!!)
syncChapterProgressWithTrack.await(
mangaId,
track,
service.mangaService,
)
}
null
} catch (e: Throwable) {
service to e
}
}
}
.awaitAll()
.filterNotNull()
}
}
}

View file

@ -48,7 +48,7 @@ class DelayedMangaTrackingUpdateJob(context: Context, workerParams: WorkerParame
.forEach { track -> .forEach { track ->
try { try {
val service = trackManager.getService(track.syncId) val service = trackManager.getService(track.syncId)
if (service != null && service.isLogged) { if (service != null && service.isLoggedIn) {
logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.id}, last chapter read: ${track.lastChapterRead}" } logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.id}, last chapter read: ${track.lastChapterRead}" }
service.mangaService.update(track.toDbTrack(), true) service.mangaService.update(track.toDbTrack(), true)
insertTrack.await(track) insertTrack.await(track)

View file

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

View file

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

View file

@ -179,7 +179,7 @@ internal fun PreferenceItem(
TrackingPreferenceWidget( TrackingPreferenceWidget(
service = this, service = this,
checked = uName.isNotEmpty(), checked = uName.isNotEmpty(),
onClick = { if (isLogged) item.logout() else item.login() }, onClick = { if (isLoggedIn) item.logout() else item.login() },
) )
} }
} }

View file

@ -115,9 +115,7 @@ object SettingsTrackingScreen : SearchableSettings {
if (enhancedMangaTrackers.second.isNotEmpty()) { if (enhancedMangaTrackers.second.isNotEmpty()) {
val missingMangaSourcesInfo = stringResource( val missingMangaSourcesInfo = stringResource(
R.string.enhanced_services_not_installed, R.string.enhanced_services_not_installed,
enhancedMangaTrackers.second enhancedMangaTrackers.second.joinToString { it.name },
.map { stringResource(it.nameRes()) }
.joinToString(),
) )
enhancedMangaTrackerInfo += "\n\n$missingMangaSourcesInfo" enhancedMangaTrackerInfo += "\n\n$missingMangaSourcesInfo"
} }
@ -139,43 +137,43 @@ object SettingsTrackingScreen : SearchableSettings {
title = stringResource(R.string.services), title = stringResource(R.string.services),
preferenceItems = listOf( preferenceItems = listOf(
Preference.PreferenceItem.TrackingPreference( Preference.PreferenceItem.TrackingPreference(
title = stringResource(trackManager.myAnimeList.nameRes()), title = trackManager.myAnimeList.name,
service = trackManager.myAnimeList, service = trackManager.myAnimeList,
login = { context.openInBrowser(MyAnimeListApi.authUrl(), forceDefaultBrowser = true) }, login = { context.openInBrowser(MyAnimeListApi.authUrl(), forceDefaultBrowser = true) },
logout = { dialog = LogoutDialog(trackManager.myAnimeList) }, logout = { dialog = LogoutDialog(trackManager.myAnimeList) },
), ),
Preference.PreferenceItem.TrackingPreference( Preference.PreferenceItem.TrackingPreference(
title = stringResource(trackManager.aniList.nameRes()), title = trackManager.aniList.name,
service = trackManager.aniList, service = trackManager.aniList,
login = { context.openInBrowser(AnilistApi.authUrl(), forceDefaultBrowser = true) }, login = { context.openInBrowser(AnilistApi.authUrl(), forceDefaultBrowser = true) },
logout = { dialog = LogoutDialog(trackManager.aniList) }, logout = { dialog = LogoutDialog(trackManager.aniList) },
), ),
Preference.PreferenceItem.TrackingPreference( Preference.PreferenceItem.TrackingPreference(
title = stringResource(trackManager.kitsu.nameRes()), title = trackManager.kitsu.name,
service = trackManager.kitsu, service = trackManager.kitsu,
login = { dialog = LoginDialog(trackManager.kitsu, R.string.email) }, login = { dialog = LoginDialog(trackManager.kitsu, R.string.email) },
logout = { dialog = LogoutDialog(trackManager.kitsu) }, logout = { dialog = LogoutDialog(trackManager.kitsu) },
), ),
Preference.PreferenceItem.TrackingPreference( Preference.PreferenceItem.TrackingPreference(
title = stringResource(trackManager.mangaUpdates.nameRes()), title = trackManager.mangaUpdates.name,
service = trackManager.mangaUpdates, service = trackManager.mangaUpdates,
login = { dialog = LoginDialog(trackManager.mangaUpdates, R.string.username) }, login = { dialog = LoginDialog(trackManager.mangaUpdates, R.string.username) },
logout = { dialog = LogoutDialog(trackManager.mangaUpdates) }, logout = { dialog = LogoutDialog(trackManager.mangaUpdates) },
), ),
Preference.PreferenceItem.TrackingPreference( Preference.PreferenceItem.TrackingPreference(
title = stringResource(trackManager.shikimori.nameRes()), title = trackManager.shikimori.name,
service = trackManager.shikimori, service = trackManager.shikimori,
login = { context.openInBrowser(ShikimoriApi.authUrl(), forceDefaultBrowser = true) }, login = { context.openInBrowser(ShikimoriApi.authUrl(), forceDefaultBrowser = true) },
logout = { dialog = LogoutDialog(trackManager.shikimori) }, logout = { dialog = LogoutDialog(trackManager.shikimori) },
), ),
Preference.PreferenceItem.TrackingPreference( Preference.PreferenceItem.TrackingPreference(
title = stringResource(trackManager.simkl.nameRes()), title = trackManager.simkl.name,
service = trackManager.simkl, service = trackManager.simkl,
login = { context.openInBrowser(SimklApi.authUrl(), forceDefaultBrowser = true) }, login = { context.openInBrowser(SimklApi.authUrl(), forceDefaultBrowser = true) },
logout = { dialog = LogoutDialog(trackManager.simkl) }, logout = { dialog = LogoutDialog(trackManager.simkl) },
), ),
Preference.PreferenceItem.TrackingPreference( Preference.PreferenceItem.TrackingPreference(
title = stringResource(trackManager.bangumi.nameRes()), title = trackManager.bangumi.name,
service = trackManager.bangumi, service = trackManager.bangumi,
login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) }, login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) },
logout = { dialog = LogoutDialog(trackManager.bangumi) }, logout = { dialog = LogoutDialog(trackManager.bangumi) },
@ -188,7 +186,7 @@ object SettingsTrackingScreen : SearchableSettings {
preferenceItems = enhancedMangaTrackers.first preferenceItems = enhancedMangaTrackers.first
.map { service -> .map { service ->
Preference.PreferenceItem.TrackingPreference( Preference.PreferenceItem.TrackingPreference(
title = stringResource(service.nameRes()), title = service.name,
service = service, service = service,
login = { (service as EnhancedMangaTrackService).loginNoop() }, login = { (service as EnhancedMangaTrackService).loginNoop() },
logout = service::logout, logout = service::logout,
@ -218,7 +216,7 @@ object SettingsTrackingScreen : SearchableSettings {
title = { title = {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Text( Text(
text = stringResource(R.string.login_title, stringResource(service.nameRes())), text = stringResource(R.string.login_title, service.name),
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
) )
IconButton(onClick = onDismissRequest) { IconButton(onClick = onDismissRequest) {
@ -326,7 +324,7 @@ object SettingsTrackingScreen : SearchableSettings {
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
title = { title = {
Text( Text(
text = stringResource(R.string.logout_title, stringResource(service.nameRes())), text = stringResource(R.string.logout_title, service.name),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) )

View file

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

View file

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

View file

@ -149,7 +149,7 @@ object Migrations {
// Force MAL log out due to login flow change // Force MAL log out due to login flow change
// v52: switched from scraping to WebView // v52: switched from scraping to WebView
// v53: switched from WebView to OAuth // v53: switched from WebView to OAuth
if (trackManager.myAnimeList.isLogged) { if (trackManager.myAnimeList.isLoggedIn) {
trackManager.myAnimeList.logout() trackManager.myAnimeList.logout()
context.toast(R.string.myanimelist_relogin) context.toast(R.string.myanimelist_relogin)
} }

View file

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

View file

@ -18,18 +18,13 @@ import eu.kanade.domain.entries.anime.interactor.UpdateAnime
import eu.kanade.domain.entries.anime.model.copyFrom import eu.kanade.domain.entries.anime.model.copyFrom
import eu.kanade.domain.entries.anime.model.toSAnime import eu.kanade.domain.entries.anime.model.toSAnime
import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithSource import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithSource
import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithTrackServiceTwoWay import eu.kanade.domain.track.anime.interactor.RefreshAnimeTracks
import eu.kanade.domain.track.anime.model.toDbTrack
import eu.kanade.domain.track.anime.model.toDomainTrack
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.animesource.UnmeteredSource import eu.kanade.tachiyomi.animesource.UnmeteredSource
import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
import eu.kanade.tachiyomi.data.download.anime.AnimeDownloadManager import eu.kanade.tachiyomi.data.download.anime.AnimeDownloadManager
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.track.EnhancedAnimeTrackService
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.model.UpdateStrategy import eu.kanade.tachiyomi.model.UpdateStrategy
import eu.kanade.tachiyomi.util.prepUpdateCover import eu.kanade.tachiyomi.util.prepUpdateCover
import eu.kanade.tachiyomi.util.shouldDownloadNewEpisodes import eu.kanade.tachiyomi.util.shouldDownloadNewEpisodes
@ -44,7 +39,6 @@ import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.sync.withPermit
import logcat.LogPriority import logcat.LogPriority
@ -59,7 +53,6 @@ import tachiyomi.domain.entries.anime.interactor.GetLibraryAnime
import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.anime.model.toAnimeUpdate import tachiyomi.domain.entries.anime.model.toAnimeUpdate
import tachiyomi.domain.items.episode.interactor.GetEpisodeByAnimeId
import tachiyomi.domain.items.episode.model.Episode import tachiyomi.domain.items.episode.model.Episode
import tachiyomi.domain.items.episode.model.NoEpisodesException import tachiyomi.domain.items.episode.model.NoEpisodesException
import tachiyomi.domain.library.anime.LibraryAnime import tachiyomi.domain.library.anime.LibraryAnime
@ -73,8 +66,6 @@ import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_NON_V
import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_OUTSIDE_RELEASE_PERIOD import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_OUTSIDE_RELEASE_PERIOD
import tachiyomi.domain.source.anime.model.AnimeSourceNotInstalledException import tachiyomi.domain.source.anime.model.AnimeSourceNotInstalledException
import tachiyomi.domain.source.anime.service.AnimeSourceManager import tachiyomi.domain.source.anime.service.AnimeSourceManager
import tachiyomi.domain.track.anime.interactor.GetAnimeTracks
import tachiyomi.domain.track.anime.interactor.InsertAnimeTrack
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
@ -92,17 +83,13 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private val downloadPreferences: DownloadPreferences = Injekt.get() private val downloadPreferences: DownloadPreferences = Injekt.get()
private val libraryPreferences: LibraryPreferences = Injekt.get() private val libraryPreferences: LibraryPreferences = Injekt.get()
private val downloadManager: AnimeDownloadManager = Injekt.get() private val downloadManager: AnimeDownloadManager = Injekt.get()
private val trackManager: TrackManager = Injekt.get()
private val coverCache: AnimeCoverCache = Injekt.get() private val coverCache: AnimeCoverCache = Injekt.get()
private val getLibraryAnime: GetLibraryAnime = Injekt.get() private val getLibraryAnime: GetLibraryAnime = Injekt.get()
private val getAnime: GetAnime = Injekt.get() private val getAnime: GetAnime = Injekt.get()
private val updateAnime: UpdateAnime = Injekt.get() private val updateAnime: UpdateAnime = Injekt.get()
private val getEpisodeByAnimeId: GetEpisodeByAnimeId = Injekt.get()
private val getCategories: GetAnimeCategories = Injekt.get() private val getCategories: GetAnimeCategories = Injekt.get()
private val syncEpisodesWithSource: SyncEpisodesWithSource = Injekt.get() private val syncEpisodesWithSource: SyncEpisodesWithSource = Injekt.get()
private val getTracks: GetAnimeTracks = Injekt.get() private val refreshAnimeTracks: RefreshAnimeTracks = Injekt.get()
private val insertTrack: InsertAnimeTrack = Injekt.get()
private val syncEpisodesWithTrackServiceTwoWay: SyncEpisodesWithTrackServiceTwoWay = Injekt.get()
private val setAnimeFetchInterval: SetAnimeFetchInterval = Injekt.get() private val setAnimeFetchInterval: SetAnimeFetchInterval = Injekt.get()
private val notifier = AnimeLibraryUpdateNotifier(context) private val notifier = AnimeLibraryUpdateNotifier(context)
@ -296,8 +283,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
} }
if (libraryPreferences.autoUpdateTrackers().get()) { if (libraryPreferences.autoUpdateTrackers().get()) {
val loggedServices = trackManager.services.filter { it.isLogged } refreshAnimeTracks(anime.id)
updateTrackings(anime, loggedServices)
} }
} }
} }
@ -417,48 +403,25 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private suspend fun updateTrackings() { private suspend fun updateTrackings() {
coroutineScope { coroutineScope {
var progressCount = 0 var progressCount = 0
val loggedServices = trackManager.services.filter { it.isLogged }
animeToUpdate.forEach { libraryAnime -> animeToUpdate.forEach { libraryAnime ->
val anime = libraryAnime.anime
ensureActive() ensureActive()
val anime = libraryAnime.anime
notifier.showProgressNotification(listOf(anime), progressCount++, animeToUpdate.size) notifier.showProgressNotification(listOf(anime), progressCount++, animeToUpdate.size)
refreshAnimeTracks(anime.id)
// Update the tracking details.
updateTrackings(anime, loggedServices)
} }
notifier.cancelProgressNotification() notifier.cancelProgressNotification()
} }
} }
private suspend fun updateTrackings(anime: Anime, loggedServices: List<TrackService>) { private suspend fun refreshAnimeTracks(animeId: Long) {
getTracks.await(anime.id) refreshAnimeTracks.await(animeId).forEach { (_, e) ->
.map { track ->
supervisorScope {
async {
val service = trackManager.getService(track.syncId)
if (service != null && service in loggedServices) {
try {
val updatedTrack = service.animeService.refresh(track.toDbTrack())
insertTrack.await(updatedTrack.toDomainTrack()!!)
if (service is EnhancedAnimeTrackService) {
val episodes = getEpisodeByAnimeId.await(anime.id)
syncEpisodesWithTrackServiceTwoWay.await(episodes, track, service.animeService)
}
} catch (e: Throwable) {
// Ignore errors and continue // Ignore errors and continue
logcat(LogPriority.ERROR, e) logcat(LogPriority.ERROR, e)
} }
} }
}
}
}
.awaitAll()
}
private suspend fun withUpdateNotification( private suspend fun withUpdateNotification(
updatingAnime: CopyOnWriteArrayList<Anime>, updatingAnime: CopyOnWriteArrayList<Anime>,

View file

@ -18,16 +18,11 @@ import eu.kanade.domain.entries.manga.interactor.UpdateManga
import eu.kanade.domain.entries.manga.model.copyFrom import eu.kanade.domain.entries.manga.model.copyFrom
import eu.kanade.domain.entries.manga.model.toSManga import eu.kanade.domain.entries.manga.model.toSManga
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithTrackServiceTwoWay import eu.kanade.domain.track.manga.interactor.RefreshMangaTracks
import eu.kanade.domain.track.manga.model.toDbTrack
import eu.kanade.domain.track.manga.model.toDomainTrack
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.MangaCoverCache import eu.kanade.tachiyomi.data.cache.MangaCoverCache
import eu.kanade.tachiyomi.data.download.manga.MangaDownloadManager import eu.kanade.tachiyomi.data.download.manga.MangaDownloadManager
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.track.EnhancedMangaTrackService
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.model.UpdateStrategy import eu.kanade.tachiyomi.model.UpdateStrategy
import eu.kanade.tachiyomi.source.UnmeteredSource import eu.kanade.tachiyomi.source.UnmeteredSource
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
@ -44,7 +39,6 @@ import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.sync.withPermit
import logcat.LogPriority import logcat.LogPriority
@ -59,7 +53,6 @@ import tachiyomi.domain.entries.manga.interactor.GetManga
import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.entries.manga.model.toMangaUpdate import tachiyomi.domain.entries.manga.model.toMangaUpdate
import tachiyomi.domain.items.chapter.interactor.GetChapterByMangaId
import tachiyomi.domain.items.chapter.model.Chapter import tachiyomi.domain.items.chapter.model.Chapter
import tachiyomi.domain.items.chapter.model.NoChaptersException import tachiyomi.domain.items.chapter.model.NoChaptersException
import tachiyomi.domain.library.manga.LibraryManga import tachiyomi.domain.library.manga.LibraryManga
@ -73,8 +66,6 @@ import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_NON_V
import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_OUTSIDE_RELEASE_PERIOD import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_OUTSIDE_RELEASE_PERIOD
import tachiyomi.domain.source.manga.model.SourceNotInstalledException import tachiyomi.domain.source.manga.model.SourceNotInstalledException
import tachiyomi.domain.source.manga.service.MangaSourceManager import tachiyomi.domain.source.manga.service.MangaSourceManager
import tachiyomi.domain.track.manga.interactor.GetMangaTracks
import tachiyomi.domain.track.manga.interactor.InsertMangaTrack
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
@ -92,17 +83,13 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private val downloadPreferences: DownloadPreferences = Injekt.get() private val downloadPreferences: DownloadPreferences = Injekt.get()
private val libraryPreferences: LibraryPreferences = Injekt.get() private val libraryPreferences: LibraryPreferences = Injekt.get()
private val downloadManager: MangaDownloadManager = Injekt.get() private val downloadManager: MangaDownloadManager = Injekt.get()
private val trackManager: TrackManager = Injekt.get()
private val coverCache: MangaCoverCache = Injekt.get() private val coverCache: MangaCoverCache = Injekt.get()
private val getLibraryManga: GetLibraryManga = Injekt.get() private val getLibraryManga: GetLibraryManga = Injekt.get()
private val getManga: GetManga = Injekt.get() private val getManga: GetManga = Injekt.get()
private val updateManga: UpdateManga = Injekt.get() private val updateManga: UpdateManga = Injekt.get()
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get()
private val getCategories: GetMangaCategories = Injekt.get() private val getCategories: GetMangaCategories = Injekt.get()
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get() private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get()
private val getTracks: GetMangaTracks = Injekt.get() private val refreshMangaTracks: RefreshMangaTracks = Injekt.get()
private val insertTrack: InsertMangaTrack = Injekt.get()
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get()
private val setMangaFetchInterval: SetMangaFetchInterval = Injekt.get() private val setMangaFetchInterval: SetMangaFetchInterval = Injekt.get()
private val notifier = MangaLibraryUpdateNotifier(context) private val notifier = MangaLibraryUpdateNotifier(context)
@ -296,8 +283,7 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
} }
if (libraryPreferences.autoUpdateTrackers().get()) { if (libraryPreferences.autoUpdateTrackers().get()) {
val loggedServices = trackManager.services.filter { it.isLogged } refreshMangaTracks(manga.id)
updateTrackings(manga, loggedServices)
} }
} }
} }
@ -417,48 +403,25 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private suspend fun updateTrackings() { private suspend fun updateTrackings() {
coroutineScope { coroutineScope {
var progressCount = 0 var progressCount = 0
val loggedServices = trackManager.services.filter { it.isLogged }
mangaToUpdate.forEach { libraryManga -> mangaToUpdate.forEach { libraryManga ->
val manga = libraryManga.manga
ensureActive() ensureActive()
val manga = libraryManga.manga
notifier.showProgressNotification(listOf(manga), progressCount++, mangaToUpdate.size) notifier.showProgressNotification(listOf(manga), progressCount++, mangaToUpdate.size)
refreshMangaTracks(manga.id)
// Update the tracking details.
updateTrackings(manga, loggedServices)
} }
notifier.cancelProgressNotification() notifier.cancelProgressNotification()
} }
} }
private suspend fun updateTrackings(manga: Manga, loggedServices: List<TrackService>) { private suspend fun refreshMangaTracks(mangaId: Long) {
getTracks.await(manga.id) refreshMangaTracks.await(mangaId).forEach { (_, e) ->
.map { track ->
supervisorScope {
async {
val service = trackManager.getService(track.syncId)
if (service != null && service in loggedServices) {
try {
val updatedTrack = service.mangaService.refresh(track.toDbTrack())
insertTrack.await(updatedTrack.toDomainTrack()!!)
if (service is EnhancedMangaTrackService) {
val chapters = getChapterByMangaId.await(manga.id)
syncChaptersWithTrackServiceTwoWay.await(chapters, track, service.mangaService)
}
} catch (e: Throwable) {
// Ignore errors and continue // Ignore errors and continue
logcat(LogPriority.ERROR, e) logcat(LogPriority.ERROR, e)
} }
} }
}
}
}
.awaitAll()
}
private suspend fun withUpdateNotification( private suspend fun withUpdateNotification(
updatingManga: CopyOnWriteArrayList<Manga>, updatingManga: CopyOnWriteArrayList<Manga>,

View file

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.data.track package eu.kanade.tachiyomi.data.track
import android.app.Application import android.app.Application
import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithTrackServiceTwoWay import eu.kanade.domain.items.episode.interactor.SyncEpisodeProgressWithTrack
import eu.kanade.domain.track.anime.model.toDbTrack import eu.kanade.domain.track.anime.model.toDbTrack
import eu.kanade.domain.track.anime.model.toDomainTrack import eu.kanade.domain.track.anime.model.toDomainTrack
import eu.kanade.tachiyomi.data.database.models.anime.AnimeTrack import eu.kanade.tachiyomi.data.database.models.anime.AnimeTrack
@ -22,6 +22,7 @@ import java.time.ZoneOffset
import tachiyomi.domain.track.anime.model.AnimeTrack as DomainAnimeTrack import tachiyomi.domain.track.anime.model.AnimeTrack as DomainAnimeTrack
private val insertTrack: InsertAnimeTrack by injectLazy() private val insertTrack: InsertAnimeTrack by injectLazy()
private val syncEpisodeProgressWithTrack: SyncEpisodeProgressWithTrack by injectLazy()
interface AnimeTrackService { interface AnimeTrackService {
@ -56,7 +57,8 @@ interface AnimeTrackService {
suspend fun refresh(track: AnimeTrack): AnimeTrack suspend fun refresh(track: AnimeTrack): AnimeTrack
suspend fun registerTracking(item: AnimeTrack, animeId: Long) { // TODO: move this to an interactor, and update all trackers based on common data
suspend fun register(item: AnimeTrack, animeId: Long) {
item.anime_id = animeId item.anime_id = animeId
try { try {
withIOContext { withIOContext {
@ -68,6 +70,7 @@ interface AnimeTrackService {
insertTrack.await(track) insertTrack.await(track)
// TODO: merge into SyncChaptersWithTrackServiceTwoWay?
// Update episode progress if newer episodes marked seen locally // Update episode progress if newer episodes marked seen locally
if (hasSeenEpisodes) { if (hasSeenEpisodes) {
val latestLocalSeenEpisodeNumber = allEpisodes val latestLocalSeenEpisodeNumber = allEpisodes
@ -102,9 +105,7 @@ interface AnimeTrackService {
} }
} }
if (this is EnhancedAnimeTrackService) { syncEpisodeProgressWithTrack.await(animeId, track, this@AnimeTrackService)
Injekt.get<SyncEpisodesWithTrackServiceTwoWay>().await(allEpisodes, track, this@AnimeTrackService)
}
} }
} catch (e: Throwable) { } catch (e: Throwable) {
withUIContext { Injekt.get<Application>().toast(e.message) } withUIContext { Injekt.get<Application>().toast(e.message) }

View file

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.data.track package eu.kanade.tachiyomi.data.track
import android.app.Application import android.app.Application
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithTrackServiceTwoWay import eu.kanade.domain.items.chapter.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.track.manga.model.toDbTrack import eu.kanade.domain.track.manga.model.toDbTrack
import eu.kanade.domain.track.manga.model.toDomainTrack import eu.kanade.domain.track.manga.model.toDomainTrack
import eu.kanade.tachiyomi.data.database.models.manga.MangaTrack import eu.kanade.tachiyomi.data.database.models.manga.MangaTrack
@ -22,6 +22,7 @@ import java.time.ZoneOffset
import tachiyomi.domain.track.manga.model.MangaTrack as DomainTrack import tachiyomi.domain.track.manga.model.MangaTrack as DomainTrack
private val insertTrack: InsertMangaTrack by injectLazy() private val insertTrack: InsertMangaTrack by injectLazy()
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack by injectLazy()
interface MangaTrackService { interface MangaTrackService {
@ -56,7 +57,8 @@ interface MangaTrackService {
suspend fun refresh(track: MangaTrack): MangaTrack suspend fun refresh(track: MangaTrack): MangaTrack
suspend fun registerTracking(item: MangaTrack, mangaId: Long) { // TODO: move this to an interactor, and update all trackers based on common data
suspend fun register(item: MangaTrack, mangaId: Long) {
item.manga_id = mangaId item.manga_id = mangaId
try { try {
withIOContext { withIOContext {
@ -68,6 +70,7 @@ interface MangaTrackService {
insertTrack.await(track) insertTrack.await(track)
// TODO: merge into SyncChaptersWithTrackServiceTwoWay?
// Update chapter progress if newer chapters marked read locally // Update chapter progress if newer chapters marked read locally
if (hasReadChapters) { if (hasReadChapters) {
val latestLocalReadChapterNumber = allChapters val latestLocalReadChapterNumber = allChapters
@ -102,9 +105,7 @@ interface MangaTrackService {
} }
} }
if (this is EnhancedMangaTrackService) { syncChapterProgressWithTrack.await(mangaId, track, this@MangaTrackService)
Injekt.get<SyncChaptersWithTrackServiceTwoWay>().await(allChapters, track, this@MangaTrackService)
}
} }
} catch (e: Throwable) { } catch (e: Throwable) {
withUIContext { Injekt.get<Application>().toast(e.message) } withUIContext { Injekt.get<Application>().toast(e.message) }

View file

@ -32,7 +32,7 @@ class TrackManager(context: Context) {
val kitsu = Kitsu(KITSU) val kitsu = Kitsu(KITSU)
val shikimori = Shikimori(SHIKIMORI) val shikimori = Shikimori(SHIKIMORI)
val bangumi = Bangumi(BANGUMI) val bangumi = Bangumi(BANGUMI)
val komga = Komga(context, KOMGA) val komga = Komga(KOMGA)
val mangaUpdates = MangaUpdates(MANGA_UPDATES) val mangaUpdates = MangaUpdates(MANGA_UPDATES)
val kavita = Kavita(context, KAVITA) val kavita = Kavita(context, KAVITA)
val suwayomi = Suwayomi(SUWAYOMI) val suwayomi = Suwayomi(SUWAYOMI)
@ -42,5 +42,5 @@ class TrackManager(context: Context) {
fun getService(id: Long) = services.find { it.id == id } fun getService(id: Long) = services.find { it.id == id }
fun hasLoggedServices() = services.any { it.isLogged } fun hasLoggedServices() = services.any { it.isLoggedIn }
} }

View file

@ -9,7 +9,7 @@ import eu.kanade.tachiyomi.network.NetworkHelper
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
abstract class TrackService(val id: Long) { abstract class TrackService(val id: Long, val name: String) {
val trackPreferences: TrackPreferences by injectLazy() val trackPreferences: TrackPreferences by injectLazy()
val networkService: NetworkHelper by injectLazy() val networkService: NetworkHelper by injectLazy()
@ -17,10 +17,6 @@ abstract class TrackService(val id: Long) {
open val client: OkHttpClient open val client: OkHttpClient
get() = networkService.client get() = networkService.client
// Name of the manga sync service to display
@StringRes
abstract fun nameRes(): Int
// Application and remote support for reading dates // Application and remote support for reading dates
open val supportsReadingDates: Boolean = false open val supportsReadingDates: Boolean = false
@ -40,7 +36,7 @@ abstract class TrackService(val id: Long) {
trackPreferences.setTrackCredentials(this, "", "") trackPreferences.setTrackCredentials(this, "", "")
} }
open val isLogged: Boolean open val isLoggedIn: Boolean
get() = getUsername().isNotEmpty() && get() = getUsername().isNotEmpty() &&
getPassword().isNotEmpty() getPassword().isNotEmpty()

View file

@ -19,7 +19,7 @@ import uy.kohesive.injekt.injectLazy
import tachiyomi.domain.track.anime.model.AnimeTrack as DomainAnimeTrack import tachiyomi.domain.track.anime.model.AnimeTrack as DomainAnimeTrack
import tachiyomi.domain.track.manga.model.MangaTrack as DomainTrack import tachiyomi.domain.track.manga.model.MangaTrack as DomainTrack
class Anilist(id: Long) : TrackService(id), MangaTrackService, AnimeTrackService, DeletableMangaTrackService, DeletableAnimeTrackService { class Anilist(id: Long) : TrackService(id, "AniList"), MangaTrackService, AnimeTrackService, DeletableMangaTrackService, DeletableAnimeTrackService {
companion object { companion object {
const val READING = 1 const val READING = 1
@ -59,9 +59,6 @@ class Anilist(id: Long) : TrackService(id), MangaTrackService, AnimeTrackService
} }
} }
@StringRes
override fun nameRes() = R.string.tracker_anilist
override fun getLogo() = R.drawable.ic_tracker_anilist override fun getLogo() = R.drawable.ic_tracker_anilist
override fun getLogoColor() = Color.rgb(18, 25, 35) override fun getLogoColor() = Color.rgb(18, 25, 35)

View file

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

View file

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

View file

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

View file

@ -18,7 +18,7 @@ import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.DecimalFormat import java.text.DecimalFormat
class Kitsu(id: Long) : TrackService(id), AnimeTrackService, MangaTrackService, DeletableMangaTrackService, DeletableAnimeTrackService { class Kitsu(id: Long) : TrackService(id, "Kitsu"), AnimeTrackService, MangaTrackService, DeletableMangaTrackService, DeletableAnimeTrackService {
companion object { companion object {
const val READING = 1 const val READING = 1
@ -30,9 +30,6 @@ class Kitsu(id: Long) : TrackService(id), AnimeTrackService, MangaTrackService,
const val PLAN_TO_WATCH = 15 const val PLAN_TO_WATCH = 15
} }
@StringRes
override fun nameRes() = R.string.tracker_kitsu
override val supportsReadingDates: Boolean = true override val supportsReadingDates: Boolean = true
private val json: Json by injectLazy() private val json: Json by injectLazy()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -17,7 +17,7 @@ import eu.kanade.core.preference.asState
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.entries.anime.interactor.UpdateAnime import eu.kanade.domain.entries.anime.interactor.UpdateAnime
import eu.kanade.domain.entries.anime.model.toDomainAnime import eu.kanade.domain.entries.anime.model.toDomainAnime
import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithTrackServiceTwoWay import eu.kanade.domain.items.episode.interactor.SyncEpisodeProgressWithTrack
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.anime.model.toDomainTrack import eu.kanade.domain.track.anime.model.toDomainTrack
import eu.kanade.presentation.util.ioCoroutineScope import eu.kanade.presentation.util.ioCoroutineScope
@ -52,7 +52,6 @@ import tachiyomi.domain.entries.anime.interactor.GetDuplicateLibraryAnime
import tachiyomi.domain.entries.anime.interactor.NetworkToLocalAnime import tachiyomi.domain.entries.anime.interactor.NetworkToLocalAnime
import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.anime.model.toAnimeUpdate import tachiyomi.domain.entries.anime.model.toAnimeUpdate
import tachiyomi.domain.items.episode.interactor.GetEpisodeByAnimeId
import tachiyomi.domain.items.episode.interactor.SetAnimeDefaultEpisodeFlags import tachiyomi.domain.items.episode.interactor.SetAnimeDefaultEpisodeFlags
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.source.anime.interactor.GetRemoteAnime import tachiyomi.domain.source.anime.interactor.GetRemoteAnime
@ -74,17 +73,16 @@ class BrowseAnimeSourceScreenModel(
private val getRemoteAnime: GetRemoteAnime = Injekt.get(), private val getRemoteAnime: GetRemoteAnime = Injekt.get(),
private val getDuplicateAnimelibAnime: GetDuplicateLibraryAnime = Injekt.get(), private val getDuplicateAnimelibAnime: GetDuplicateLibraryAnime = Injekt.get(),
private val getCategories: GetAnimeCategories = Injekt.get(), private val getCategories: GetAnimeCategories = Injekt.get(),
private val getEpisodeByAnimeId: GetEpisodeByAnimeId = Injekt.get(),
private val setAnimeCategories: SetAnimeCategories = Injekt.get(), private val setAnimeCategories: SetAnimeCategories = Injekt.get(),
private val setAnimeDefaultEpisodeFlags: SetAnimeDefaultEpisodeFlags = Injekt.get(), private val setAnimeDefaultEpisodeFlags: SetAnimeDefaultEpisodeFlags = Injekt.get(),
private val getAnime: GetAnime = Injekt.get(), private val getAnime: GetAnime = Injekt.get(),
private val networkToLocalAnime: NetworkToLocalAnime = Injekt.get(), private val networkToLocalAnime: NetworkToLocalAnime = Injekt.get(),
private val updateAnime: UpdateAnime = Injekt.get(), private val updateAnime: UpdateAnime = Injekt.get(),
private val insertTrack: InsertAnimeTrack = Injekt.get(), private val insertTrack: InsertAnimeTrack = Injekt.get(),
private val syncEpisodesWithTrackServiceTwoWay: SyncEpisodesWithTrackServiceTwoWay = Injekt.get(), private val syncEpisodeProgressWithTrack: SyncEpisodeProgressWithTrack = Injekt.get(),
) : StateScreenModel<BrowseAnimeSourceScreenModel.State>(State(Listing.valueOf(listingQuery))) { ) : StateScreenModel<BrowseAnimeSourceScreenModel.State>(State(Listing.valueOf(listingQuery))) {
private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLogged } } private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLoggedIn } }
var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope) var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope)
@ -304,8 +302,7 @@ class BrowseAnimeSourceScreenModel(
(service as TrackService).animeService.bind(track) (service as TrackService).animeService.bind(track)
insertTrack.await(track.toDomainTrack()!!) insertTrack.await(track.toDomainTrack()!!)
val chapters = getEpisodeByAnimeId.await(anime.id) syncEpisodeProgressWithTrack.await(anime.id, track.toDomainTrack()!!, service.animeService)
syncEpisodesWithTrackServiceTwoWay.await(chapters, track.toDomainTrack()!!, service.animeService)
} }
} catch (e: Exception) { } catch (e: Exception) {
logcat( logcat(

View file

@ -9,7 +9,6 @@ import androidx.compose.ui.unit.dp
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.cachedIn import androidx.paging.cachedIn
import androidx.paging.filter
import androidx.paging.map import androidx.paging.map
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
@ -17,7 +16,7 @@ import eu.kanade.core.preference.asState
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.entries.manga.interactor.UpdateManga import eu.kanade.domain.entries.manga.interactor.UpdateManga
import eu.kanade.domain.entries.manga.model.toDomainManga import eu.kanade.domain.entries.manga.model.toDomainManga
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithTrackServiceTwoWay import eu.kanade.domain.items.chapter.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.manga.model.toDomainTrack import eu.kanade.domain.track.manga.model.toDomainTrack
import eu.kanade.presentation.util.ioCoroutineScope import eu.kanade.presentation.util.ioCoroutineScope
@ -34,7 +33,6 @@ import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
@ -52,7 +50,6 @@ import tachiyomi.domain.entries.manga.interactor.GetManga
import tachiyomi.domain.entries.manga.interactor.NetworkToLocalManga import tachiyomi.domain.entries.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.entries.manga.model.toMangaUpdate import tachiyomi.domain.entries.manga.model.toMangaUpdate
import tachiyomi.domain.items.chapter.interactor.GetChapterByMangaId
import tachiyomi.domain.items.chapter.interactor.SetMangaDefaultChapterFlags import tachiyomi.domain.items.chapter.interactor.SetMangaDefaultChapterFlags
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.source.manga.interactor.GetRemoteManga import tachiyomi.domain.source.manga.interactor.GetRemoteManga
@ -74,17 +71,16 @@ class BrowseMangaSourceScreenModel(
private val getRemoteManga: GetRemoteManga = Injekt.get(), private val getRemoteManga: GetRemoteManga = Injekt.get(),
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(), private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
private val getCategories: GetMangaCategories = Injekt.get(), private val getCategories: GetMangaCategories = Injekt.get(),
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
private val setMangaCategories: SetMangaCategories = Injekt.get(), private val setMangaCategories: SetMangaCategories = Injekt.get(),
private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(), private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(),
private val getManga: GetManga = Injekt.get(), private val getManga: GetManga = Injekt.get(),
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(),
private val insertTrack: InsertMangaTrack = Injekt.get(), private val insertTrack: InsertMangaTrack = Injekt.get(),
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(), private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack = Injekt.get(),
) : StateScreenModel<BrowseMangaSourceScreenModel.State>(State(Listing.valueOf(listingQuery))) { ) : StateScreenModel<BrowseMangaSourceScreenModel.State>(State(Listing.valueOf(listingQuery))) {
private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLogged } } private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLoggedIn } }
var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope) var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope)
@ -301,8 +297,7 @@ class BrowseMangaSourceScreenModel(
(service as TrackService).mangaService.bind(track) (service as TrackService).mangaService.bind(track)
insertTrack.await(track.toDomainTrack()!!) insertTrack.await(track.toDomainTrack()!!)
val chapters = getChapterByMangaId.await(manga.id) syncChapterProgressWithTrack.await(manga.id, track.toDomainTrack()!!, service.mangaService)
syncChaptersWithTrackServiceTwoWay.await(chapters, track.toDomainTrack()!!, service.mangaService)
} }
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.WARN, e) { "Could not match manga: ${manga.title} with service $service" } logcat(LogPriority.WARN, e) { "Could not match manga: ${manga.title} with service $service" }

View file

@ -111,7 +111,7 @@ class AnimeScreenModel(
private val successState: State.Success? private val successState: State.Success?
get() = state.value as? State.Success get() = state.value as? State.Success
private val loggedServices by lazy { trackManager.services.filter { it.isLogged && it is AnimeTrackService } } private val loggedServices by lazy { trackManager.services.filter { it.isLoggedIn && it is AnimeTrackService } }
val anime: Anime? val anime: Anime?
get() = successState?.anime get() = successState?.anime
@ -336,7 +336,7 @@ class AnimeScreenModel(
launchIO { launchIO {
try { try {
service.match(anime)?.let { track -> service.match(anime)?.let { track ->
(service as AnimeTrackService).registerTracking(track, animeId) (service as AnimeTrackService).register(track, animeId)
} }
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.WARN, e) { logcat(LogPriority.WARN, e) {

View file

@ -39,9 +39,8 @@ import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithTrackServiceTwoWay import eu.kanade.domain.track.anime.interactor.RefreshAnimeTracks
import eu.kanade.domain.track.anime.model.toDbTrack import eu.kanade.domain.track.anime.model.toDbTrack
import eu.kanade.domain.track.anime.model.toDomainTrack
import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.track.TrackDateSelector import eu.kanade.presentation.track.TrackDateSelector
import eu.kanade.presentation.track.TrackItemSelector import eu.kanade.presentation.track.TrackItemSelector
@ -72,11 +71,9 @@ import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.entries.anime.interactor.GetAnime import tachiyomi.domain.entries.anime.interactor.GetAnime
import tachiyomi.domain.entries.anime.interactor.GetAnimeWithEpisodes
import tachiyomi.domain.source.anime.service.AnimeSourceManager import tachiyomi.domain.source.anime.service.AnimeSourceManager
import tachiyomi.domain.track.anime.interactor.DeleteAnimeTrack import tachiyomi.domain.track.anime.interactor.DeleteAnimeTrack
import tachiyomi.domain.track.anime.interactor.GetAnimeTracks import tachiyomi.domain.track.anime.interactor.GetAnimeTracks
import tachiyomi.domain.track.anime.interactor.InsertAnimeTrack
import tachiyomi.domain.track.anime.model.AnimeTrack import tachiyomi.domain.track.anime.model.AnimeTrack
import tachiyomi.presentation.core.components.material.AlertDialogContent import tachiyomi.presentation.core.components.material.AlertDialogContent
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
@ -210,7 +207,7 @@ data class AnimeTrackInfoDialogHomeScreen(
val anime = Injekt.get<GetAnime>().await(animeId) ?: return@launchNonCancellable val anime = Injekt.get<GetAnime>().await(animeId) ?: return@launchNonCancellable
try { try {
val matchResult = item.service.match(anime) ?: throw Exception() val matchResult = item.service.match(anime) ?: throw Exception()
item.service.animeService.registerTracking(matchResult, animeId) item.service.animeService.register(matchResult, animeId)
} catch (e: Exception) { } catch (e: Exception) {
withUIContext { Injekt.get<Application>().toast(R.string.error_no_match) } withUIContext { Injekt.get<Application>().toast(R.string.error_no_match) }
} }
@ -218,49 +215,30 @@ data class AnimeTrackInfoDialogHomeScreen(
} }
private suspend fun refreshTrackers() { private suspend fun refreshTrackers() {
val insertAnimeTrack = Injekt.get<InsertAnimeTrack>() val refreshTracks = Injekt.get<RefreshAnimeTracks>()
val getAnimeWithEpisodes = Injekt.get<GetAnimeWithEpisodes>()
val syncTwoWayService = Injekt.get<SyncEpisodesWithTrackServiceTwoWay>()
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
try { refreshTracks.await(animeId)
val trackItems = getTracks.await(animeId).mapToTrackItem() .filter { it.first != null }
for (trackItem in trackItems) { .forEach { (track, e) ->
try { logcat(LogPriority.ERROR, e) {
val track = trackItem.track ?: continue "Failed to refresh track data mangaId=$animeId for service ${track!!.id}"
val domainAnimeTrack = trackItem.service.animeService.refresh(track.toDbTrack()).toDomainTrack() ?: continue
insertAnimeTrack.await(domainAnimeTrack)
if (trackItem.service is EnhancedAnimeTrackService) {
val allEpisodes = getAnimeWithEpisodes.awaitEpisodes(animeId)
syncTwoWayService.await(allEpisodes, domainAnimeTrack, trackItem.service.animeService)
} }
} catch (e: Exception) {
logcat(
LogPriority.ERROR,
e,
) { "Failed to refresh track data mangaId=$animeId for service ${trackItem.service.id}" }
withUIContext { withUIContext {
context.toast( context.toast(
context.getString( context.getString(
R.string.track_error, R.string.track_error,
context.getString(trackItem.service.nameRes()), track!!.name,
e.message, e.message,
), ),
) )
} }
} }
} }
} catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to refresh track data animeId=$animeId" }
withUIContext { context.toast(e.message) }
}
}
private fun List<AnimeTrack>.mapToTrackItem(): List<AnimeTrackItem> { private fun List<AnimeTrack>.mapToTrackItem(): List<AnimeTrackItem> {
val dbTracks = map { it.toDbTrack() }
val loggedServices = Injekt.get<TrackManager>().services.filter { val loggedServices = Injekt.get<TrackManager>().services.filter {
it.isLogged && it is AnimeTrackService it.isLoggedIn && it is AnimeTrackService
} }
val source = Injekt.get<AnimeSourceManager>().getOrStub(sourceId) val source = Injekt.get<AnimeSourceManager>().getOrStub(sourceId)
return loggedServices return loggedServices
@ -594,7 +572,7 @@ private data class TrackDateRemoverScreen(
) )
}, },
text = { text = {
val serviceName = stringResource(sm.getServiceNameRes()) val serviceName = sm.getServiceName()
Text( Text(
text = if (start) { text = if (start) {
stringResource(R.string.track_remove_start_date_conf_text, serviceName) stringResource(R.string.track_remove_start_date_conf_text, serviceName)
@ -631,7 +609,7 @@ private data class TrackDateRemoverScreen(
private val start: Boolean, private val start: Boolean,
) : ScreenModel { ) : ScreenModel {
fun getServiceNameRes() = service.nameRes() fun getServiceName() = service.name
fun removeDate() { fun removeDate() {
coroutineScope.launchNonCancellable { coroutineScope.launchNonCancellable {
@ -716,7 +694,7 @@ data class TrackServiceSearchScreen(
} }
fun registerTracking(item: AnimeTrackSearch) { fun registerTracking(item: AnimeTrackSearch) {
coroutineScope.launchNonCancellable { service.animeService.registerTracking(item, animeId) } coroutineScope.launchNonCancellable { service.animeService.register(item, animeId) }
} }
fun updateSelection(selected: AnimeTrackSearch) { fun updateSelection(selected: AnimeTrackSearch) {
@ -747,7 +725,7 @@ private data class TrackAnimeServiceRemoveScreen(
service = Injekt.get<TrackManager>().getService(serviceId)!!, service = Injekt.get<TrackManager>().getService(serviceId)!!,
) )
} }
val serviceName = stringResource(sm.getServiceNameRes()) val serviceName = sm.getServiceName()
var removeRemoteTrack by remember { mutableStateOf(false) } var removeRemoteTrack by remember { mutableStateOf(false) }
AlertDialogContent( AlertDialogContent(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars), modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars),
@ -812,7 +790,7 @@ private data class TrackAnimeServiceRemoveScreen(
private val deleteTrack: DeleteAnimeTrack = Injekt.get(), private val deleteTrack: DeleteAnimeTrack = Injekt.get(),
) : ScreenModel { ) : ScreenModel {
fun getServiceNameRes() = service.nameRes() fun getServiceName() = service.name
fun isServiceDeletable() = service is DeletableAnimeTrackService fun isServiceDeletable() = service is DeletableAnimeTrackService

View file

@ -107,7 +107,7 @@ class MangaScreenModel(
private val successState: State.Success? private val successState: State.Success?
get() = state.value as? State.Success get() = state.value as? State.Success
private val loggedServices by lazy { trackManager.services.filter { it.isLogged && it is MangaTrackService } } private val loggedServices by lazy { trackManager.services.filter { it.isLoggedIn && it is MangaTrackService } }
val manga: Manga? val manga: Manga?
get() = successState?.manga get() = successState?.manga
@ -333,7 +333,7 @@ class MangaScreenModel(
launchIO { launchIO {
try { try {
service.match(manga)?.let { track -> service.match(manga)?.let { track ->
(service as MangaTrackService).registerTracking(track, mangaId) (service as MangaTrackService).register(track, mangaId)
} }
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.WARN, e) { logcat(LogPriority.WARN, e) {

View file

@ -39,9 +39,8 @@ import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithTrackServiceTwoWay import eu.kanade.domain.track.manga.interactor.RefreshMangaTracks
import eu.kanade.domain.track.manga.model.toDbTrack import eu.kanade.domain.track.manga.model.toDbTrack
import eu.kanade.domain.track.manga.model.toDomainTrack
import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.track.TrackDateSelector import eu.kanade.presentation.track.TrackDateSelector
import eu.kanade.presentation.track.TrackItemSelector import eu.kanade.presentation.track.TrackItemSelector
@ -72,11 +71,9 @@ import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.entries.manga.interactor.GetManga import tachiyomi.domain.entries.manga.interactor.GetManga
import tachiyomi.domain.entries.manga.interactor.GetMangaWithChapters
import tachiyomi.domain.source.manga.service.MangaSourceManager import tachiyomi.domain.source.manga.service.MangaSourceManager
import tachiyomi.domain.track.manga.interactor.DeleteMangaTrack import tachiyomi.domain.track.manga.interactor.DeleteMangaTrack
import tachiyomi.domain.track.manga.interactor.GetMangaTracks import tachiyomi.domain.track.manga.interactor.GetMangaTracks
import tachiyomi.domain.track.manga.interactor.InsertMangaTrack
import tachiyomi.domain.track.manga.model.MangaTrack import tachiyomi.domain.track.manga.model.MangaTrack
import tachiyomi.presentation.core.components.material.AlertDialogContent import tachiyomi.presentation.core.components.material.AlertDialogContent
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
@ -210,7 +207,7 @@ data class MangaTrackInfoDialogHomeScreen(
val manga = Injekt.get<GetManga>().await(mangaId) ?: return@launchNonCancellable val manga = Injekt.get<GetManga>().await(mangaId) ?: return@launchNonCancellable
try { try {
val matchResult = item.service.match(manga) ?: throw Exception() val matchResult = item.service.match(manga) ?: throw Exception()
item.service.mangaService.registerTracking(matchResult, mangaId) item.service.mangaService.register(matchResult, mangaId)
} catch (e: Exception) { } catch (e: Exception) {
withUIContext { Injekt.get<Application>().toast(R.string.error_no_match) } withUIContext { Injekt.get<Application>().toast(R.string.error_no_match) }
} }
@ -218,48 +215,30 @@ data class MangaTrackInfoDialogHomeScreen(
} }
private suspend fun refreshTrackers() { private suspend fun refreshTrackers() {
val insertTrack = Injekt.get<InsertMangaTrack>() val refreshTracks = Injekt.get<RefreshMangaTracks>()
val getMangaWithChapters = Injekt.get<GetMangaWithChapters>()
val syncTwoWayService = Injekt.get<SyncChaptersWithTrackServiceTwoWay>()
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
try { refreshTracks.await(mangaId)
val trackItems = getTracks.await(mangaId).mapToTrackItem() .filter { it.first != null }
for (trackItem in trackItems) { .forEach { (track, e) ->
try { logcat(LogPriority.ERROR, e) {
val track = trackItem.track ?: continue "Failed to refresh track data mangaId=$mangaId for service ${track!!.name}"
val domainMangaTrack = trackItem.service.mangaService.refresh(track.toDbTrack()).toDomainTrack() ?: continue
insertTrack.await(domainMangaTrack)
if (trackItem.service is EnhancedMangaTrackService) {
val allChapters = getMangaWithChapters.awaitChapters(mangaId)
syncTwoWayService.await(allChapters, domainMangaTrack, trackItem.service.mangaService)
} }
} catch (e: Exception) {
logcat(
LogPriority.ERROR,
e,
) { "Failed to refresh track data mangaId=$mangaId for service ${trackItem.service.id}" }
withUIContext { withUIContext {
context.toast( context.toast(
context.getString( context.getString(
R.string.track_error, R.string.track_error,
context.getString(trackItem.service.nameRes()), track!!.name,
e.message, e.message,
), ),
) )
} }
} }
} }
} catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to refresh track data mangaId=$mangaId" }
withUIContext { context.toast(e.message) }
}
}
private fun List<MangaTrack>.mapToTrackItem(): List<MangaTrackItem> { private fun List<MangaTrack>.mapToTrackItem(): List<MangaTrackItem> {
val loggedServices = Injekt.get<TrackManager>().services.filter { val loggedServices = Injekt.get<TrackManager>().services.filter {
it.isLogged && it is MangaTrackService it.isLoggedIn && it is MangaTrackService
} }
val source = Injekt.get<MangaSourceManager>().getOrStub(sourceId) val source = Injekt.get<MangaSourceManager>().getOrStub(sourceId)
return loggedServices return loggedServices
@ -593,7 +572,7 @@ private data class TrackDateRemoverScreen(
) )
}, },
text = { text = {
val serviceName = stringResource(sm.getServiceNameRes()) val serviceName = sm.getServiceName()
Text( Text(
text = if (start) { text = if (start) {
stringResource(R.string.track_remove_start_date_conf_text, serviceName) stringResource(R.string.track_remove_start_date_conf_text, serviceName)
@ -630,7 +609,7 @@ private data class TrackDateRemoverScreen(
private val start: Boolean, private val start: Boolean,
) : ScreenModel { ) : ScreenModel {
fun getServiceNameRes() = service.nameRes() fun getServiceName() = service.name
fun removeDate() { fun removeDate() {
coroutineScope.launchNonCancellable { coroutineScope.launchNonCancellable {
@ -715,7 +694,7 @@ data class TrackServiceSearchScreen(
} }
fun registerTracking(item: MangaTrackSearch) { fun registerTracking(item: MangaTrackSearch) {
coroutineScope.launchNonCancellable { service.mangaService.registerTracking(item, mangaId) } coroutineScope.launchNonCancellable { service.mangaService.register(item, mangaId) }
} }
fun updateSelection(selected: MangaTrackSearch) { fun updateSelection(selected: MangaTrackSearch) {
@ -746,7 +725,7 @@ private data class TrackMangaServiceRemoveScreen(
service = Injekt.get<TrackManager>().getService(serviceId)!!, service = Injekt.get<TrackManager>().getService(serviceId)!!,
) )
} }
val serviceName = stringResource(sm.getServiceNameRes()) val serviceName = sm.getServiceName()
var removeRemoteTrack by remember { mutableStateOf(false) } var removeRemoteTrack by remember { mutableStateOf(false) }
AlertDialogContent( AlertDialogContent(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars), modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars),
@ -811,7 +790,7 @@ private data class TrackMangaServiceRemoveScreen(
private val deleteTrack: DeleteMangaTrack = Injekt.get(), private val deleteTrack: DeleteMangaTrack = Injekt.get(),
) : ScreenModel { ) : ScreenModel {
fun getServiceNameRes() = service.nameRes() fun getServiceName() = service.name
fun isServiceDeletable() = service is DeletableMangaTrackService fun isServiceDeletable() = service is DeletableMangaTrackService

View file

@ -373,7 +373,7 @@ class AnimeLibraryScreenModel(
* @return map of track id with the filter value * @return map of track id with the filter value
*/ */
private fun getTrackingFilterFlow(): Flow<Map<Long, TriState>> { private fun getTrackingFilterFlow(): Flow<Map<Long, TriState>> {
val loggedServices = trackManager.services.filter { it.isLogged && it is AnimeTrackService } val loggedServices = trackManager.services.filter { it.isLoggedIn && it is AnimeTrackService }
return if (loggedServices.isNotEmpty()) { return if (loggedServices.isNotEmpty()) {
val prefFlows = loggedServices val prefFlows = loggedServices
.map { libraryPreferences.filterTrackedAnime(it.id.toInt()).changes() } .map { libraryPreferences.filterTrackedAnime(it.id.toInt()).changes() }

View file

@ -26,7 +26,7 @@ class AnimeLibrarySettingsScreenModel(
) : ScreenModel { ) : ScreenModel {
val trackServices val trackServices
get() = trackManager.services.filter { it.isLogged } get() = trackManager.services.filter { it.isLoggedIn }
fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriState>) { fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriState>) {
preference(libraryPreferences).getAndSet { preference(libraryPreferences).getAndSet {

View file

@ -367,7 +367,7 @@ class MangaLibraryScreenModel(
* @return map of track id with the filter value * @return map of track id with the filter value
*/ */
private fun getTrackingFilterFlow(): Flow<Map<Long, TriState>> { private fun getTrackingFilterFlow(): Flow<Map<Long, TriState>> {
val loggedServices = trackManager.services.filter { it.isLogged && it is MangaTrackService } val loggedServices = trackManager.services.filter { it.isLoggedIn && it is MangaTrackService }
return if (loggedServices.isNotEmpty()) { return if (loggedServices.isNotEmpty()) {
val prefFlows = loggedServices val prefFlows = loggedServices
.map { libraryPreferences.filterTrackedManga(it.id.toInt()).changes() } .map { libraryPreferences.filterTrackedManga(it.id.toInt()).changes() }

View file

@ -26,7 +26,7 @@ class MangaLibrarySettingsScreenModel(
) : ScreenModel { ) : ScreenModel {
val trackServices val trackServices
get() = trackManager.services.filter { it.isLogged } get() = trackManager.services.filter { it.isLoggedIn }
fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriState>) { fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriState>) {
preference(libraryPreferences).getAndSet { preference(libraryPreferences).getAndSet {

View file

@ -481,7 +481,7 @@ class ExternalIntents {
getTracks.await(anime.id) getTracks.await(anime.id)
.mapNotNull { track -> .mapNotNull { track ->
val service = trackManager.getService(track.syncId) val service = trackManager.getService(track.syncId)
if (service != null && service.isLogged && if (service != null && service.isLoggedIn &&
service is AnimeTrackService && episodeNumber > track.lastEpisodeSeen service is AnimeTrackService && episodeNumber > track.lastEpisodeSeen
) { ) {
val updatedTrack = track.copy(lastEpisodeSeen = episodeNumber) val updatedTrack = track.copy(lastEpisodeSeen = episodeNumber)

View file

@ -575,7 +575,7 @@ class PlayerViewModel @JvmOverloads constructor(
getTracks.await(anime.id) getTracks.await(anime.id)
.mapNotNull { track -> .mapNotNull { track ->
val service = trackManager.getService(track.syncId) val service = trackManager.getService(track.syncId)
if (service != null && service.isLogged && episodeSeen > track.lastEpisodeSeen) { if (service != null && service.isLoggedIn && episodeSeen > track.lastEpisodeSeen) {
val updatedTrack = track.copy(lastEpisodeSeen = episodeSeen) val updatedTrack = track.copy(lastEpisodeSeen = episodeSeen)
// We want these to execute even if the presenter is destroyed and leaks // We want these to execute even if the presenter is destroyed and leaks

View file

@ -869,7 +869,7 @@ class ReaderViewModel @JvmOverloads constructor(
getTracks.await(manga.id) getTracks.await(manga.id)
.mapNotNull { track -> .mapNotNull { track ->
val service = trackManager.getService(track.syncId) val service = trackManager.getService(track.syncId)
if (service != null && service.isLogged && chapterRead > track.lastChapterRead) { if (service != null && service.isLoggedIn && chapterRead > track.lastChapterRead) {
val updatedTrack = track.copy(lastChapterRead = chapterRead) val updatedTrack = track.copy(lastChapterRead = chapterRead)
// We want these to execute even if the presenter is destroyed and leaks // We want these to execute even if the presenter is destroyed and leaks

View file

@ -37,7 +37,7 @@ class AnimeStatsScreenModel(
private val trackManager: TrackManager = Injekt.get(), private val trackManager: TrackManager = Injekt.get(),
) : StateScreenModel<StatsScreenState>(StatsScreenState.Loading) { ) : StateScreenModel<StatsScreenState>(StatsScreenState.Loading) {
private val loggedServices by lazy { trackManager.services.fastFilter { it.isLogged && it is AnimeTrackService } } private val loggedServices by lazy { trackManager.services.fastFilter { it.isLoggedIn && it is AnimeTrackService } }
init { init {
coroutineScope.launchIO { coroutineScope.launchIO {

View file

@ -37,7 +37,7 @@ class MangaStatsScreenModel(
private val trackManager: TrackManager = Injekt.get(), private val trackManager: TrackManager = Injekt.get(),
) : StateScreenModel<StatsScreenState>(StatsScreenState.Loading) { ) : StateScreenModel<StatsScreenState>(StatsScreenState.Loading) {
private val loggedServices by lazy { trackManager.services.fastFilter { it.isLogged && it is MangaTrackService } } private val loggedServices by lazy { trackManager.services.fastFilter { it.isLoggedIn && it is MangaTrackService } }
init { init {
coroutineScope.launchIO { coroutineScope.launchIO {

View file

@ -6,7 +6,7 @@ dependencies {
implementation(androidxLibs.gradle) implementation(androidxLibs.gradle)
implementation(kotlinLibs.gradle) implementation(kotlinLibs.gradle)
implementation(libs.kotlinter) implementation(libs.ktlint)
implementation(gradleApi()) implementation(gradleApi())
} }

View file

@ -1,21 +1,15 @@
import org.jmailen.gradle.kotlinter.KotlinterExtension import org.jlleitschuh.gradle.ktlint.KtlintExtension
import org.jmailen.gradle.kotlinter.KotlinterPlugin import org.jlleitschuh.gradle.ktlint.KtlintPlugin
apply<KotlinterPlugin>() apply<KtlintPlugin>()
extensions.configure<KotlinterExtension>("kotlinter") { extensions.configure<KtlintExtension>("ktlint") {
experimentalRules = true version.set("0.50.0")
android.set(true)
enableExperimentalRules.set(true)
disabledRules = arrayOf( filter {
"experimental:argument-list-wrapping", // Doesn't play well with Android Studio exclude("**/generated/**")
"filename", // Often broken to give a more general name
)
}
tasks {
named<DefaultTask>("preBuild").configure {
if (!System.getenv("CI").toBoolean())
dependsOn("formatKotlin")
} }
} }

View file

@ -9,11 +9,12 @@ class AnimeUpdatesRepositoryImpl(
private val databaseHandler: AnimeDatabaseHandler, private val databaseHandler: AnimeDatabaseHandler,
) : AnimeUpdatesRepository { ) : AnimeUpdatesRepository {
override suspend fun awaitWithSeen(seen: Boolean, after: Long): List<AnimeUpdatesWithRelations> { override suspend fun awaitWithSeen(seen: Boolean, after: Long, limit: Long): List<AnimeUpdatesWithRelations> {
return databaseHandler.awaitList { return databaseHandler.awaitList {
animeupdatesViewQueries.getUpdatesBySeenStatus( animeupdatesViewQueries.getUpdatesBySeenStatus(
seen = seen, seen = seen,
after = after, after = after,
limit = limit,
mapper = animeUpdateWithRelationMapper, mapper = animeUpdateWithRelationMapper,
) )
} }
@ -25,11 +26,12 @@ class AnimeUpdatesRepositoryImpl(
} }
} }
override fun subscribeWithSeen(seen: Boolean, after: Long): Flow<List<AnimeUpdatesWithRelations>> { override fun subscribeWithSeen(seen: Boolean, after: Long, limit: Long): Flow<List<AnimeUpdatesWithRelations>> {
return databaseHandler.subscribeToList { return databaseHandler.subscribeToList {
animeupdatesViewQueries.getUpdatesBySeenStatus( animeupdatesViewQueries.getUpdatesBySeenStatus(
seen = seen, seen = seen,
after = after, after = after,
limit = limit,
mapper = animeUpdateWithRelationMapper, mapper = animeUpdateWithRelationMapper,
) )
} }

View file

@ -9,11 +9,12 @@ class MangaUpdatesRepositoryImpl(
private val databaseHandler: MangaDatabaseHandler, private val databaseHandler: MangaDatabaseHandler,
) : MangaUpdatesRepository { ) : MangaUpdatesRepository {
override suspend fun awaitWithRead(read: Boolean, after: Long): List<MangaUpdatesWithRelations> { override suspend fun awaitWithRead(read: Boolean, after: Long, limit: Long): List<MangaUpdatesWithRelations> {
return databaseHandler.awaitList { return databaseHandler.awaitList {
updatesViewQueries.getUpdatesByReadStatus( updatesViewQueries.getUpdatesByReadStatus(
read = read, read = read,
after = after, after = after,
limit = limit,
mapper = mangaUpdateWithRelationMapper, mapper = mangaUpdateWithRelationMapper,
) )
} }
@ -25,11 +26,12 @@ class MangaUpdatesRepositoryImpl(
} }
} }
override fun subscribeWithRead(read: Boolean, after: Long): Flow<List<MangaUpdatesWithRelations>> { override fun subscribeWithRead(read: Boolean, after: Long, limit: Long): Flow<List<MangaUpdatesWithRelations>> {
return databaseHandler.subscribeToList { return databaseHandler.subscribeToList {
updatesViewQueries.getUpdatesByReadStatus( updatesViewQueries.getUpdatesByReadStatus(
read = read, read = read,
after = after, after = after,
limit = limit,
mapper = mangaUpdateWithRelationMapper, mapper = mangaUpdateWithRelationMapper,
) )
} }

View file

@ -30,4 +30,6 @@ getUpdatesByReadStatus:
SELECT * SELECT *
FROM updatesView FROM updatesView
WHERE read = :read WHERE read = :read
AND dateUpload > :after; AND dateUpload > :after
LIMIT :limit;

View file

@ -31,4 +31,6 @@ getUpdatesBySeenStatus:
SELECT * SELECT *
FROM animeupdatesView FROM animeupdatesView
WHERE seen = :seen WHERE seen = :seen
AND dateUpload > :after; AND dateUpload > :after
LIMIT :limit;

View file

@ -10,7 +10,7 @@ class GetAnimeUpdates(
) { ) {
suspend fun await(seen: Boolean, after: Long): List<AnimeUpdatesWithRelations> { suspend fun await(seen: Boolean, after: Long): List<AnimeUpdatesWithRelations> {
return repository.awaitWithSeen(seen, after) return repository.awaitWithSeen(seen, after, limit = 500)
} }
fun subscribe(calendar: Calendar): Flow<List<AnimeUpdatesWithRelations>> { fun subscribe(calendar: Calendar): Flow<List<AnimeUpdatesWithRelations>> {
@ -18,6 +18,6 @@ class GetAnimeUpdates(
} }
fun subscribe(seen: Boolean, after: Long): Flow<List<AnimeUpdatesWithRelations>> { fun subscribe(seen: Boolean, after: Long): Flow<List<AnimeUpdatesWithRelations>> {
return repository.subscribeWithSeen(seen, after) return repository.subscribeWithSeen(seen, after, limit = 500)
} }
} }

View file

@ -5,9 +5,9 @@ import tachiyomi.domain.updates.anime.model.AnimeUpdatesWithRelations
interface AnimeUpdatesRepository { interface AnimeUpdatesRepository {
suspend fun awaitWithSeen(seen: Boolean, after: Long): List<AnimeUpdatesWithRelations> suspend fun awaitWithSeen(seen: Boolean, after: Long, limit: Long): List<AnimeUpdatesWithRelations>
fun subscribeAllAnimeUpdates(after: Long, limit: Long): Flow<List<AnimeUpdatesWithRelations>> fun subscribeAllAnimeUpdates(after: Long, limit: Long): Flow<List<AnimeUpdatesWithRelations>>
fun subscribeWithSeen(seen: Boolean, after: Long): Flow<List<AnimeUpdatesWithRelations>> fun subscribeWithSeen(seen: Boolean, after: Long, limit: Long): Flow<List<AnimeUpdatesWithRelations>>
} }

View file

@ -10,7 +10,7 @@ class GetMangaUpdates(
) { ) {
suspend fun await(read: Boolean, after: Long): List<MangaUpdatesWithRelations> { suspend fun await(read: Boolean, after: Long): List<MangaUpdatesWithRelations> {
return repository.awaitWithRead(read, after) return repository.awaitWithRead(read, after, limit = 500)
} }
fun subscribe(calendar: Calendar): Flow<List<MangaUpdatesWithRelations>> { fun subscribe(calendar: Calendar): Flow<List<MangaUpdatesWithRelations>> {
@ -18,6 +18,6 @@ class GetMangaUpdates(
} }
fun subscribe(read: Boolean, after: Long): Flow<List<MangaUpdatesWithRelations>> { fun subscribe(read: Boolean, after: Long): Flow<List<MangaUpdatesWithRelations>> {
return repository.subscribeWithRead(read, after) return repository.subscribeWithRead(read, after, limit = 500)
} }
} }

View file

@ -5,9 +5,9 @@ import tachiyomi.domain.updates.manga.model.MangaUpdatesWithRelations
interface MangaUpdatesRepository { interface MangaUpdatesRepository {
suspend fun awaitWithRead(read: Boolean, after: Long): List<MangaUpdatesWithRelations> suspend fun awaitWithRead(read: Boolean, after: Long, limit: Long): List<MangaUpdatesWithRelations>
fun subscribeAllMangaUpdates(after: Long, limit: Long): Flow<List<MangaUpdatesWithRelations>> fun subscribeAllMangaUpdates(after: Long, limit: Long): Flow<List<MangaUpdatesWithRelations>>
fun subscribeWithRead(read: Boolean, after: Long): Flow<List<MangaUpdatesWithRelations>> fun subscribeWithRead(read: Boolean, after: Long, limit: Long): Flow<List<MangaUpdatesWithRelations>>
} }

View file

@ -28,7 +28,7 @@ guava = "com.google.guava:guava:32.1.2-android"
paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" } paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" }
paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" }
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.0-beta04" benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.0-beta05"
test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha01" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha01"
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha01" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha01"
test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-alpha04" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-alpha04"

View file

@ -1,6 +1,6 @@
[versions] [versions]
kotlin_version = "1.9.0" kotlin_version = "1.9.0"
serialization_version = "1.5.1" serialization_version = "1.6.0"
xml_serialization_version = "0.86.1" xml_serialization_version = "0.86.1"
[libraries] [libraries]

View file

@ -32,7 +32,7 @@ junrar = "com.github.junrar:junrar:7.5.5"
sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" } sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" }
sqlite-ktx = { module = "androidx.sqlite:sqlite-ktx", version.ref = "sqlite" } sqlite-ktx = { module = "androidx.sqlite:sqlite-ktx", version.ref = "sqlite" }
sqlite-android = "com.github.requery:sqlite-android:3.42.0" sqlite-android = "com.github.requery:sqlite-android:3.43.0"
preferencektx = "androidx.preference:preference-ktx:1.2.1" preferencektx = "androidx.preference:preference-ktx:1.2.1"
@ -89,7 +89,7 @@ voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.
voyager-tab-navigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" } voyager-tab-navigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" }
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" } voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
kotlinter = "org.jmailen.gradle:kotlinter-gradle:3.13.0" ktlint = "org.jlleitschuh.gradle:ktlint-gradle:11.5.1"
aniyomi-mpv = "com.github.aniyomiorg:aniyomi-mpv-lib:1.10.n" aniyomi-mpv = "com.github.aniyomiorg:aniyomi-mpv-lib:1.10.n"
ffmpeg-kit = "com.github.jmir1:ffmpeg-kit:1.10" ffmpeg-kit = "com.github.jmir1:ffmpeg-kit:1.10"

View file

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

View file

@ -37,14 +37,14 @@ import tachiyomi.presentation.widget.util.appWidgetBackgroundRadius
import tachiyomi.presentation.widget.util.calculateRowAndColumnCount import tachiyomi.presentation.widget.util.calculateRowAndColumnCount
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.util.Calendar import java.util.Calendar
import java.util.Date import java.util.Date
class AnimeUpdatesGridGlanceWidget : GlanceAppWidget() { class AnimeUpdatesGridGlanceWidget(
private val context: Context = Injekt.get<Application>(),
private val app: Application by injectLazy() private val getUpdates: GetAnimeUpdates = Injekt.get(),
private val preferences: SecurityPreferences by injectLazy() private val preferences: SecurityPreferences = Injekt.get(),
) : GlanceAppWidget() {
private var data: List<Pair<Long, Bitmap?>>? = null private var data: List<Pair<Long, Bitmap?>>? = null
@ -64,14 +64,13 @@ class AnimeUpdatesGridGlanceWidget : GlanceAppWidget() {
} }
} }
private suspend fun loadData(list: List<AnimeUpdatesWithRelations>? = null) { private suspend fun loadData() {
withIOContext { val manager = GlanceAppWidgetManager(context)
val manager = GlanceAppWidgetManager(app)
val ids = manager.getGlanceIds(this@AnimeUpdatesGridGlanceWidget::class.java) val ids = manager.getGlanceIds(this@AnimeUpdatesGridGlanceWidget::class.java)
if (ids.isEmpty()) return@withIOContext if (ids.isEmpty()) return
val processList = list withIOContext {
?: Injekt.get<GetAnimeUpdates>().await( val updates = getUpdates.await(
seen = false, seen = false,
after = DateLimit.timeInMillis, after = DateLimit.timeInMillis,
) )
@ -80,7 +79,7 @@ class AnimeUpdatesGridGlanceWidget : GlanceAppWidget() {
.maxBy { it.height.value * it.width.value } .maxBy { it.height.value * it.width.value }
.calculateRowAndColumnCount() .calculateRowAndColumnCount()
data = prepareList(processList, rowCount * columnCount) data = prepareList(updates, rowCount * columnCount)
} }
} }
@ -88,12 +87,12 @@ class AnimeUpdatesGridGlanceWidget : GlanceAppWidget() {
// Resize to cover size // Resize to cover size
val widthPx = CoverWidth.value.toInt().dpToPx val widthPx = CoverWidth.value.toInt().dpToPx
val heightPx = CoverHeight.value.toInt().dpToPx val heightPx = CoverHeight.value.toInt().dpToPx
val roundPx = app.resources.getDimension(R.dimen.appwidget_inner_radius) val roundPx = context.resources.getDimension(R.dimen.appwidget_inner_radius)
return processList return processList
.distinctBy { it.animeId } .distinctBy { it.animeId }
.take(take) .take(take)
.map { animeupdatesView -> .map { animeupdatesView ->
val request = ImageRequest.Builder(app) val request = ImageRequest.Builder(context)
.data( .data(
AnimeCover( AnimeCover(
animeId = animeupdatesView.animeId, animeId = animeupdatesView.animeId,
@ -115,7 +114,7 @@ class AnimeUpdatesGridGlanceWidget : GlanceAppWidget() {
} }
} }
.build() .build()
Pair(animeupdatesView.animeId, app.imageLoader.executeBlocking(request).drawable?.toBitmap()) Pair(animeupdatesView.animeId, context.imageLoader.executeBlocking(request).drawable?.toBitmap())
} }
} }

View file

@ -37,14 +37,14 @@ import tachiyomi.presentation.widget.util.appWidgetBackgroundRadius
import tachiyomi.presentation.widget.util.calculateRowAndColumnCount import tachiyomi.presentation.widget.util.calculateRowAndColumnCount
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.util.Calendar import java.util.Calendar
import java.util.Date import java.util.Date
class MangaUpdatesGridGlanceWidget : GlanceAppWidget() { class MangaUpdatesGridGlanceWidget(
private val context: Context = Injekt.get<Application>(),
private val app: Application by injectLazy() private val getUpdates: GetMangaUpdates = Injekt.get(),
private val preferences: SecurityPreferences by injectLazy() private val preferences: SecurityPreferences = Injekt.get(),
) : GlanceAppWidget() {
private var data: List<Pair<Long, Bitmap?>>? = null private var data: List<Pair<Long, Bitmap?>>? = null
@ -64,14 +64,13 @@ class MangaUpdatesGridGlanceWidget : GlanceAppWidget() {
} }
} }
private suspend fun loadData(list: List<MangaUpdatesWithRelations>? = null) { private suspend fun loadData() {
withIOContext { val manager = GlanceAppWidgetManager(context)
val manager = GlanceAppWidgetManager(app)
val ids = manager.getGlanceIds(this@MangaUpdatesGridGlanceWidget::class.java) val ids = manager.getGlanceIds(this@MangaUpdatesGridGlanceWidget::class.java)
if (ids.isEmpty()) return@withIOContext if (ids.isEmpty()) return
val processList = list withIOContext {
?: Injekt.get<GetMangaUpdates>().await( val updates = getUpdates.await(
read = false, read = false,
after = DateLimit.timeInMillis, after = DateLimit.timeInMillis,
) )
@ -80,7 +79,7 @@ class MangaUpdatesGridGlanceWidget : GlanceAppWidget() {
.maxBy { it.height.value * it.width.value } .maxBy { it.height.value * it.width.value }
.calculateRowAndColumnCount() .calculateRowAndColumnCount()
data = prepareList(processList, rowCount * columnCount) data = prepareList(updates, rowCount * columnCount)
} }
} }
@ -88,12 +87,12 @@ class MangaUpdatesGridGlanceWidget : GlanceAppWidget() {
// Resize to cover size // Resize to cover size
val widthPx = CoverWidth.value.toInt().dpToPx val widthPx = CoverWidth.value.toInt().dpToPx
val heightPx = CoverHeight.value.toInt().dpToPx val heightPx = CoverHeight.value.toInt().dpToPx
val roundPx = app.resources.getDimension(R.dimen.appwidget_inner_radius) val roundPx = context.resources.getDimension(R.dimen.appwidget_inner_radius)
return processList return processList
.distinctBy { it.mangaId } .distinctBy { it.mangaId }
.take(take) .take(take)
.map { updatesView -> .map { updatesView ->
val request = ImageRequest.Builder(app) val request = ImageRequest.Builder(context)
.data( .data(
MangaCover( MangaCover(
mangaId = updatesView.mangaId, mangaId = updatesView.mangaId,
@ -115,7 +114,7 @@ class MangaUpdatesGridGlanceWidget : GlanceAppWidget() {
} }
} }
.build() .build()
Pair(updatesView.mangaId, app.imageLoader.executeBlocking(request).drawable?.toBitmap()) Pair(updatesView.mangaId, context.imageLoader.executeBlocking(request).drawable?.toBitmap())
} }
} }