Option to clear manga database

This commit is contained in:
Koitharu
2024-12-20 12:30:48 +02:00
parent 22e2411c77
commit a54744abc6
8 changed files with 61 additions and 7 deletions

View File

@@ -58,6 +58,19 @@ abstract class MangaDao {
@Delete @Delete
abstract suspend fun delete(subjects: Collection<MangaEntity>) abstract suspend fun delete(subjects: Collection<MangaEntity>)
@Query(
"""
DELETE FROM manga WHERE NOT EXISTS(SELECT * FROM history WHERE history.manga_id == manga.manga_id)
AND NOT EXISTS(SELECT * FROM favourites WHERE favourites.manga_id == manga.manga_id)
AND NOT EXISTS(SELECT * FROM bookmarks WHERE bookmarks.manga_id == manga.manga_id)
AND NOT EXISTS(SELECT * FROM suggestions WHERE suggestions.manga_id == manga.manga_id)
AND NOT EXISTS(SELECT * FROM scrobblings WHERE scrobblings.manga_id == manga.manga_id)
AND NOT EXISTS(SELECT * FROM local_index WHERE local_index.manga_id == manga.manga_id)
AND manga.manga_id NOT IN (:idsToKeep)
""",
)
abstract suspend fun cleanup(idsToKeep: Set<Long>)
@Transaction @Transaction
open suspend fun upsert(manga: MangaEntity, tags: Iterable<TagEntity>? = null) { open suspend fun upsert(manga: MangaEntity, tags: Iterable<TagEntity>? = null) {
upsert(manga) upsert(manga)

View File

@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.core.db.entity.toMangaTags
import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.model.LocalMangaSource
import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.model.isLocal
import org.koitharu.kotatsu.core.nav.MangaIntent import org.koitharu.kotatsu.core.nav.MangaIntent
import org.koitharu.kotatsu.core.os.AppShortcutManager
import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.prefs.ReaderMode
import org.koitharu.kotatsu.core.util.ext.toFileOrNull import org.koitharu.kotatsu.core.util.ext.toFileOrNull
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
@@ -28,6 +29,7 @@ import javax.inject.Provider
class MangaDataRepository @Inject constructor( class MangaDataRepository @Inject constructor(
private val db: MangaDatabase, private val db: MangaDatabase,
private val resolverProvider: Provider<MangaLinkResolver>, private val resolverProvider: Provider<MangaLinkResolver>,
private val appShortcutManagerProvider: Provider<AppShortcutManager>,
) { ) {
suspend fun saveReaderMode(manga: Manga, mode: ReaderMode) { suspend fun saveReaderMode(manga: Manga, mode: ReaderMode) {
@@ -119,7 +121,7 @@ class MangaDataRepository @Inject constructor(
} }
} }
suspend fun gcChapters() { suspend fun gcChaptersCache() {
db.getChaptersDao().gc() db.getChaptersDao().gc()
} }
@@ -136,6 +138,14 @@ class MangaDataRepository @Inject constructor(
} }
} }
suspend fun cleanupDatabase() {
db.withTransaction {
gcChaptersCache()
val idsFromShortcuts = appShortcutManagerProvider.get().getMangaShortcuts()
db.getMangaDao().cleanup(idsFromShortcuts)
}
}
private fun MangaPrefsEntity.getColorFilterOrNull(): ReaderColorFilter? { private fun MangaPrefsEntity.getColorFilterOrNull(): ReaderColorFilter? {
return if (cfBrightness != 0f || cfContrast != 0f || cfInvert || cfGrayscale) { return if (cfBrightness != 0f || cfContrast != 0f || cfInvert || cfGrayscale) {
ReaderColorFilter(cfBrightness, cfContrast, cfInvert, cfGrayscale) ReaderColorFilter(cfBrightness, cfContrast, cfInvert, cfGrayscale)

View File

@@ -740,6 +740,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_HANDLE_LINKS = "handle_links" const val KEY_HANDLE_LINKS = "handle_links"
const val KEY_BACKUP_TG_OPEN = "backup_periodic_tg_open" const val KEY_BACKUP_TG_OPEN = "backup_periodic_tg_open"
const val KEY_BACKUP_TG_TEST = "backup_periodic_tg_test" const val KEY_BACKUP_TG_TEST = "backup_periodic_tg_test"
const val KEY_CLEAR_MANGA_DATA = "manga_data_clear"
// old keys are for migration only // old keys are for migration only
private const val KEY_IMAGES_PROXY_OLD = "images_proxy" private const val KEY_IMAGES_PROXY_OLD = "images_proxy"

View File

@@ -158,17 +158,17 @@ class HistoryRepository @Inject constructor(
suspend fun delete(manga: Manga) = db.withTransaction { suspend fun delete(manga: Manga) = db.withTransaction {
db.getHistoryDao().delete(manga.id) db.getHistoryDao().delete(manga.id)
mangaRepository.gcChapters() mangaRepository.gcChaptersCache()
} }
suspend fun deleteAfter(minDate: Long) = db.withTransaction { suspend fun deleteAfter(minDate: Long) = db.withTransaction {
db.getHistoryDao().deleteAfter(minDate) db.getHistoryDao().deleteAfter(minDate)
mangaRepository.gcChapters() mangaRepository.gcChaptersCache()
} }
suspend fun deleteNotFavorite() = db.withTransaction { suspend fun deleteNotFavorite() = db.withTransaction {
db.getHistoryDao().deleteNotFavorite() db.getHistoryDao().deleteNotFavorite()
mangaRepository.gcChapters() mangaRepository.gcChaptersCache()
} }
suspend fun delete(ids: Collection<Long>): ReversibleHandle { suspend fun delete(ids: Collection<Long>): ReversibleHandle {
@@ -176,7 +176,7 @@ class HistoryRepository @Inject constructor(
for (id in ids) { for (id in ids) {
db.getHistoryDao().delete(id) db.getHistoryDao().delete(id)
} }
mangaRepository.gcChapters() mangaRepository.gcChaptersCache()
} }
return ReversibleHandle { return ReversibleHandle {
recover(ids) recover(ids)

View File

@@ -107,7 +107,7 @@ class UserDataSettingsFragment : BasePreferenceFragment(R.string.data_and_privac
viewModel.loadingKeys.observe(viewLifecycleOwner) { keys -> viewModel.loadingKeys.observe(viewLifecycleOwner) { keys ->
loadingPrefs.addAll(keys) loadingPrefs.addAll(keys)
loadingPrefs.forEach { prefKey -> loadingPrefs.forEach { prefKey ->
findPreference<Preference>(prefKey)!!.isEnabled = prefKey !in keys findPreference<Preference>(prefKey)?.isEnabled = prefKey !in keys
} }
} }
viewModel.onError.observeEvent(viewLifecycleOwner, SnackbarErrorObserver(listView, this)) viewModel.onError.observeEvent(viewLifecycleOwner, SnackbarErrorObserver(listView, this))
@@ -153,6 +153,11 @@ class UserDataSettingsFragment : BasePreferenceFragment(R.string.data_and_privac
true true
} }
AppSettings.KEY_CLEAR_MANGA_DATA -> {
viewModel.clearMangaData()
true
}
AppSettings.KEY_UPDATES_FEED_CLEAR -> { AppSettings.KEY_UPDATES_FEED_CLEAR -> {
viewModel.clearUpdatesFeed() viewModel.clearUpdatesFeed()
true true

View File

@@ -4,7 +4,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@@ -13,6 +12,7 @@ import kotlinx.coroutines.runInterruptible
import okhttp3.Cache import okhttp3.Cache
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
import org.koitharu.kotatsu.core.parser.MangaDataRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.core.prefs.observeAsFlow
import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.ui.BaseViewModel
@@ -27,6 +27,7 @@ import org.koitharu.kotatsu.search.domain.MangaSearchRepository
import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import java.util.EnumMap import java.util.EnumMap
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider
@HiltViewModel @HiltViewModel
class UserDataSettingsViewModel @Inject constructor( class UserDataSettingsViewModel @Inject constructor(
@@ -37,6 +38,7 @@ class UserDataSettingsViewModel @Inject constructor(
private val cookieJar: MutableCookieJar, private val cookieJar: MutableCookieJar,
private val settings: AppSettings, private val settings: AppSettings,
private val deleteReadChaptersUseCase: DeleteReadChaptersUseCase, private val deleteReadChaptersUseCase: DeleteReadChaptersUseCase,
private val mangaDataRepositoryProvider: Provider<MangaDataRepository>,
) : BaseViewModel() { ) : BaseViewModel() {
val onActionDone = MutableEventFlow<ReversibleAction>() val onActionDone = MutableEventFlow<ReversibleAction>()
@@ -139,6 +141,21 @@ class UserDataSettingsViewModel @Inject constructor(
} }
} }
fun clearMangaData() {
launchJob(Dispatchers.Default) {
try {
loadingKeys.update { it + AppSettings.KEY_CLEAR_MANGA_DATA }
trackingRepository.gc()
val repository = mangaDataRepositoryProvider.get()
repository.cleanupLocalManga()
repository.cleanupDatabase()
onActionDone.call(ReversibleAction(R.string.updates_feed_cleared, null))
} finally {
loadingKeys.update { it - AppSettings.KEY_CLEAR_MANGA_DATA }
}
}
}
fun cleanupChapters() { fun cleanupChapters() {
launchJob(Dispatchers.Default) { launchJob(Dispatchers.Default) {
try { try {

View File

@@ -787,4 +787,6 @@
<string name="test_connection">Test connection</string> <string name="test_connection">Test connection</string>
<string name="telegram_chat_id_summary">Enter the chat ID where backups should be sent</string> <string name="telegram_chat_id_summary">Enter the chat ID where backups should be sent</string>
<string name="open_telegram_bot_summary">Press to open chat with Kotatsu Backup Bot</string> <string name="open_telegram_bot_summary">Press to open chat with Kotatsu Backup Bot</string>
<string name="clear_database">Clear database</string>
<string name="clear_database_summary">Delete information about manga that is not used</string>
</resources> </resources>

View File

@@ -89,6 +89,12 @@
android:summary="@string/loading_" android:summary="@string/loading_"
android:title="@string/clear_network_cache" /> android:title="@string/clear_network_cache" />
<Preference
android:key="manga_data_clear"
android:persistent="false"
android:summary="@string/clear_database_summary"
android:title="@string/clear_database" />
<Preference <Preference
android:key="cookies_clear" android:key="cookies_clear"
android:persistent="false" android:persistent="false"