mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-20 09:39:18 +01:00
Misc cleanup
- Rebased against master - Removed unnecessary tabs for now
This commit is contained in:
parent
255ed50685
commit
14eb114ee8
@ -22,7 +22,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "eu.kanade.tachiyomi"
|
applicationId = "eu.kanade.tachiyomi"
|
||||||
|
|
||||||
versionCode = 109
|
versionCode = 110
|
||||||
versionName = "0.14.7"
|
versionName = "0.14.7"
|
||||||
|
|
||||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
|
@ -29,6 +29,7 @@ import tachiyomi.data.manga.MangaRepositoryImpl
|
|||||||
import tachiyomi.data.release.ReleaseServiceImpl
|
import tachiyomi.data.release.ReleaseServiceImpl
|
||||||
import tachiyomi.data.source.SourceRepositoryImpl
|
import tachiyomi.data.source.SourceRepositoryImpl
|
||||||
import tachiyomi.data.source.StubSourceRepositoryImpl
|
import tachiyomi.data.source.StubSourceRepositoryImpl
|
||||||
|
import tachiyomi.data.stat.DownloadStatRepositoryImpl
|
||||||
import tachiyomi.data.track.TrackRepositoryImpl
|
import tachiyomi.data.track.TrackRepositoryImpl
|
||||||
import tachiyomi.data.updates.UpdatesRepositoryImpl
|
import tachiyomi.data.updates.UpdatesRepositoryImpl
|
||||||
import tachiyomi.domain.category.interactor.CreateCategoryWithName
|
import tachiyomi.domain.category.interactor.CreateCategoryWithName
|
||||||
@ -72,6 +73,9 @@ import tachiyomi.domain.source.interactor.GetRemoteManga
|
|||||||
import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryManga
|
import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryManga
|
||||||
import tachiyomi.domain.source.repository.SourceRepository
|
import tachiyomi.domain.source.repository.SourceRepository
|
||||||
import tachiyomi.domain.source.repository.StubSourceRepository
|
import tachiyomi.domain.source.repository.StubSourceRepository
|
||||||
|
import tachiyomi.domain.stat.interactor.AddDownloadStatOperation
|
||||||
|
import tachiyomi.domain.stat.interactor.GetDownloadStatOperations
|
||||||
|
import tachiyomi.domain.stat.repository.DownloadStatRepository
|
||||||
import tachiyomi.domain.track.interactor.DeleteTrack
|
import tachiyomi.domain.track.interactor.DeleteTrack
|
||||||
import tachiyomi.domain.track.interactor.GetTracks
|
import tachiyomi.domain.track.interactor.GetTracks
|
||||||
import tachiyomi.domain.track.interactor.GetTracksPerManga
|
import tachiyomi.domain.track.interactor.GetTracksPerManga
|
||||||
@ -167,5 +171,9 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { ToggleLanguage(get()) }
|
addFactory { ToggleLanguage(get()) }
|
||||||
addFactory { ToggleSource(get()) }
|
addFactory { ToggleSource(get()) }
|
||||||
addFactory { ToggleSourcePin(get()) }
|
addFactory { ToggleSourcePin(get()) }
|
||||||
|
|
||||||
|
addSingletonFactory<DownloadStatRepository> { DownloadStatRepositoryImpl(get()) }
|
||||||
|
addFactory { GetDownloadStatOperations(get()) }
|
||||||
|
addFactory { AddDownloadStatOperation(get()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
package eu.kanade.presentation.more.download
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
import eu.kanade.presentation.more.download.components.DeletedStatsRow
|
||||||
|
import eu.kanade.presentation.more.download.components.DownloadStatOverviewSection
|
||||||
|
import eu.kanade.presentation.more.download.components.DownloadStatsRow
|
||||||
|
import eu.kanade.presentation.util.Screen
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||||
|
|
||||||
|
class DownloadStatsScreen : Screen() {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val screenModel = rememberScreenModel { DownloadStatsScreenModel() }
|
||||||
|
val state by screenModel.state.collectAsState()
|
||||||
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = { scrollBehavior ->
|
||||||
|
AppBar(
|
||||||
|
title = stringResource(MR.strings.label_download_stats),
|
||||||
|
navigateUp = navigator::pop,
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { contentPadding ->
|
||||||
|
when {
|
||||||
|
state.isLoading -> LoadingScreen(Modifier.padding(contentPadding))
|
||||||
|
else -> {
|
||||||
|
OverallStats(
|
||||||
|
state = state,
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun OverallStats(
|
||||||
|
state: DownloadStatsScreenModel.State,
|
||||||
|
contentPadding: PaddingValues,
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
DownloadStatOverviewSection(state.uniqueItems)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
DownloadStatsRow(state.downloadStatOperations.filter { it.size > 0 })
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
DeletedStatsRow(state.downloadStatOperations.filter { it.size < 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package eu.kanade.presentation.more.download
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
|
import eu.kanade.presentation.more.download.data.DownloadStatManga
|
||||||
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
|
import eu.kanade.tachiyomi.data.download.DownloadProvider
|
||||||
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import tachiyomi.core.util.lang.launchIO
|
||||||
|
import tachiyomi.domain.manga.interactor.GetLibraryManga
|
||||||
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
|
import tachiyomi.domain.stat.interactor.GetDownloadStatOperations
|
||||||
|
import tachiyomi.domain.stat.model.DownloadStatOperation
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class DownloadStatsScreenModel(
|
||||||
|
private val getLibraryManga: GetLibraryManga = Injekt.get(),
|
||||||
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
|
private val downloadManager: DownloadManager = Injekt.get(),
|
||||||
|
private val downloadProvider: DownloadProvider = Injekt.get(),
|
||||||
|
private val getDownloadStatOperations: GetDownloadStatOperations = Injekt.get(),
|
||||||
|
) : StateScreenModel<DownloadStatsScreenModel.State>(State()) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
screenModelScope.launchIO {
|
||||||
|
mutableState.update { state ->
|
||||||
|
state.copy(
|
||||||
|
items = getLibraryManga.await().map { libraryManga ->
|
||||||
|
val source = sourceManager.getOrStub(libraryManga.manga.source)
|
||||||
|
val path = downloadProvider.findMangaDir(
|
||||||
|
libraryManga.manga.title,
|
||||||
|
source,
|
||||||
|
)?.filePath
|
||||||
|
DownloadStatManga(
|
||||||
|
libraryManga = libraryManga,
|
||||||
|
folderSize = if (path != null) DiskUtil.getDirectorySize(File(path)) else 0,
|
||||||
|
downloadChaptersCount = downloadManager.getDownloadCount(libraryManga.manga),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
downloadStatOperations = getDownloadStatOperations.await(),
|
||||||
|
isLoading = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
screenModelScope.launchIO {
|
||||||
|
getDownloadStatOperations.subscribe()
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.collectLatest { operations ->
|
||||||
|
mutableState.update { state ->
|
||||||
|
val oldOperationsId = state.downloadStatOperations.map { it.id }.toHashSet()
|
||||||
|
val newOperations = operations
|
||||||
|
.mapNotNull { if (!oldOperationsId.contains(it.id)) it else null }
|
||||||
|
.groupBy { it.mangaId }
|
||||||
|
val newItems = state.items.map { item ->
|
||||||
|
if (newOperations.containsKey(item.libraryManga.id)) {
|
||||||
|
item.copy(
|
||||||
|
folderSize = item.folderSize +
|
||||||
|
newOperations[item.libraryManga.id]!!
|
||||||
|
.sumOf { it.size },
|
||||||
|
downloadChaptersCount = item.downloadChaptersCount +
|
||||||
|
newOperations[item.libraryManga.id]!!.sumOf { it.units }.toInt(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.copy(
|
||||||
|
items = newItems,
|
||||||
|
downloadStatOperations = operations,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class State(
|
||||||
|
val isLoading: Boolean = true,
|
||||||
|
val items: List<DownloadStatManga> = emptyList(),
|
||||||
|
val downloadStatOperations: List<DownloadStatOperation> = emptyList(),
|
||||||
|
) {
|
||||||
|
|
||||||
|
val uniqueItems: List<DownloadStatManga> by lazy {
|
||||||
|
val uniqueIds = HashSet<Long>()
|
||||||
|
val uniqueMangas = mutableListOf<DownloadStatManga>()
|
||||||
|
for (manga in items) {
|
||||||
|
if (uniqueIds.add(manga.libraryManga.manga.id)) {
|
||||||
|
uniqueMangas.add(manga)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uniqueMangas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package eu.kanade.presentation.more.download.components
|
||||||
|
|
||||||
|
import android.text.format.Formatter
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Book
|
||||||
|
import androidx.compose.material.icons.outlined.CollectionsBookmark
|
||||||
|
import androidx.compose.material.icons.outlined.Storage
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import eu.kanade.presentation.more.download.data.DownloadStatManga
|
||||||
|
import eu.kanade.presentation.more.stats.components.StatsItem
|
||||||
|
import eu.kanade.presentation.more.stats.components.StatsOverviewItem
|
||||||
|
import eu.kanade.presentation.more.stats.components.StatsSection
|
||||||
|
import tachiyomi.domain.stat.model.DownloadStatOperation
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DownloadStatOverviewSection(
|
||||||
|
items: List<DownloadStatManga>,
|
||||||
|
) {
|
||||||
|
StatsSection(MR.strings.label_overview_section) {
|
||||||
|
Row {
|
||||||
|
StatsOverviewItem(
|
||||||
|
title = folderSizeText(items.sumOf { it.folderSize }),
|
||||||
|
subtitle = stringResource(MR.strings.label_size),
|
||||||
|
icon = Icons.Outlined.Storage,
|
||||||
|
)
|
||||||
|
StatsOverviewItem(
|
||||||
|
title = items.sumOf { it.downloadChaptersCount }.toString(),
|
||||||
|
subtitle = stringResource(MR.strings.chapters),
|
||||||
|
icon = Icons.Outlined.Book,
|
||||||
|
)
|
||||||
|
StatsOverviewItem(
|
||||||
|
title = items.filter { it.downloadChaptersCount > 0 }.size.toString(),
|
||||||
|
subtitle = stringResource(MR.strings.manga),
|
||||||
|
icon = Icons.Outlined.CollectionsBookmark,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DownloadStatsRow(
|
||||||
|
data: List<DownloadStatOperation>,
|
||||||
|
) {
|
||||||
|
StatsSection(MR.strings.downloaded_chapters) {
|
||||||
|
Row {
|
||||||
|
StatsItem(
|
||||||
|
data.size.toString(),
|
||||||
|
stringResource(MR.strings.label_total_chapters),
|
||||||
|
)
|
||||||
|
StatsItem(
|
||||||
|
folderSizeText(data.sumOf { abs(it.size) }),
|
||||||
|
stringResource(MR.strings.label_size),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DeletedStatsRow(
|
||||||
|
data: List<DownloadStatOperation>,
|
||||||
|
) {
|
||||||
|
StatsSection(MR.strings.deleted_chapters) {
|
||||||
|
Row {
|
||||||
|
StatsItem(
|
||||||
|
abs(data.sumOf { it.units }).toString(),
|
||||||
|
stringResource(MR.strings.label_total_chapters),
|
||||||
|
)
|
||||||
|
StatsItem(
|
||||||
|
folderSizeText(abs(data.sumOf { it.size })),
|
||||||
|
stringResource(MR.strings.label_size),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun folderSizeText(folderSize: Long): String {
|
||||||
|
val context = LocalContext.current
|
||||||
|
return Formatter.formatFileSize(context, folderSize)
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package eu.kanade.presentation.more.download.data
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class DownloadStatManga(
|
||||||
|
val libraryManga: LibraryManga,
|
||||||
|
val folderSize: Long = 0,
|
||||||
|
val downloadChaptersCount: Int = 0,
|
||||||
|
)
|
@ -28,6 +28,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
|
import eu.kanade.presentation.more.download.DownloadStatsScreen
|
||||||
import eu.kanade.presentation.more.settings.Preference
|
import eu.kanade.presentation.more.settings.Preference
|
||||||
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
|
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
|
||||||
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
||||||
@ -255,6 +256,7 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() }
|
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() }
|
||||||
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
|
||||||
val chapterCache = remember { Injekt.get<ChapterCache>() }
|
val chapterCache = remember { Injekt.get<ChapterCache>() }
|
||||||
var cacheReadableSizeSema by remember { mutableIntStateOf(0) }
|
var cacheReadableSizeSema by remember { mutableIntStateOf(0) }
|
||||||
@ -287,6 +289,10 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
pref = libraryPreferences.autoClearChapterCache(),
|
pref = libraryPreferences.autoClearChapterCache(),
|
||||||
title = stringResource(MR.strings.pref_auto_clear_chapter_cache),
|
title = stringResource(MR.strings.pref_auto_clear_chapter_cache),
|
||||||
),
|
),
|
||||||
|
Preference.PreferenceItem.TextPreference(
|
||||||
|
title = stringResource(MR.strings.label_download_stats),
|
||||||
|
onClick = { navigator.push(DownloadStatsScreen()) },
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
@ -22,9 +23,12 @@ import tachiyomi.domain.chapter.model.Chapter
|
|||||||
import tachiyomi.domain.download.service.DownloadPreferences
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
|
import tachiyomi.domain.stat.interactor.AddDownloadStatOperation
|
||||||
|
import tachiyomi.domain.stat.model.DownloadStatOperation
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used to manage chapter downloads in the application. It must be instantiated once
|
* This class is used to manage chapter downloads in the application. It must be instantiated once
|
||||||
@ -38,6 +42,7 @@ class DownloadManager(
|
|||||||
private val getCategories: GetCategories = Injekt.get(),
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
||||||
|
private val addDownloadStatOperation: AddDownloadStatOperation = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -224,6 +229,13 @@ class DownloadManager(
|
|||||||
removeFromDownloadQueue(filteredChapters)
|
removeFromDownloadQueue(filteredChapters)
|
||||||
|
|
||||||
val (mangaDir, chapterDirs) = provider.findChapterDirs(filteredChapters, manga, source)
|
val (mangaDir, chapterDirs) = provider.findChapterDirs(filteredChapters, manga, source)
|
||||||
|
addDownloadStatOperation.await(
|
||||||
|
DownloadStatOperation.create().copy(
|
||||||
|
mangaId = manga.id,
|
||||||
|
size = chapterDirs.sumOf { DiskUtil.getDirectorySize(File(it.filePath!!)) } * -1,
|
||||||
|
units = filteredChapters.size.toLong() * -1,
|
||||||
|
),
|
||||||
|
)
|
||||||
chapterDirs.forEach { it.delete() }
|
chapterDirs.forEach { it.delete() }
|
||||||
cache.removeChapters(filteredChapters, manga)
|
cache.removeChapters(filteredChapters, manga)
|
||||||
|
|
||||||
@ -246,7 +258,18 @@ class DownloadManager(
|
|||||||
if (removeQueued) {
|
if (removeQueued) {
|
||||||
downloader.removeFromQueue(manga)
|
downloader.removeFromQueue(manga)
|
||||||
}
|
}
|
||||||
provider.findMangaDir(manga.title, source)?.delete()
|
val mangaDir = provider.findMangaDir(manga.title, source)
|
||||||
|
val dirSize = DiskUtil.getDirectorySize(File(mangaDir?.filePath!!))
|
||||||
|
if (dirSize > 0) {
|
||||||
|
addDownloadStatOperation.await(
|
||||||
|
DownloadStatOperation.create().copy(
|
||||||
|
mangaId = manga.id,
|
||||||
|
size = dirSize * -1,
|
||||||
|
units = cache.getDownloadCount(manga).toLong() * -1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mangaDir.delete()
|
||||||
cache.removeManga(manga)
|
cache.removeManga(manga)
|
||||||
|
|
||||||
// Delete source directory if empty
|
// Delete source directory if empty
|
||||||
|
@ -54,6 +54,8 @@ import tachiyomi.domain.chapter.model.Chapter
|
|||||||
import tachiyomi.domain.download.service.DownloadPreferences
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
|
import tachiyomi.domain.stat.interactor.AddDownloadStatOperation
|
||||||
|
import tachiyomi.domain.stat.model.DownloadStatOperation
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
@ -75,6 +77,7 @@ class Downloader(
|
|||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val chapterCache: ChapterCache = Injekt.get(),
|
private val chapterCache: ChapterCache = Injekt.get(),
|
||||||
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
||||||
|
private val addDownloadStatOperation: AddDownloadStatOperation = Injekt.get(),
|
||||||
private val xml: XML = Injekt.get(),
|
private val xml: XML = Injekt.get(),
|
||||||
private val getCategories: GetCategories = Injekt.get(),
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
@ -406,6 +409,22 @@ class Downloader(
|
|||||||
|
|
||||||
DiskUtil.createNoMediaFile(tmpDir, context)
|
DiskUtil.createNoMediaFile(tmpDir, context)
|
||||||
|
|
||||||
|
addDownloadStatOperation.await(
|
||||||
|
DownloadStatOperation.create().copy(
|
||||||
|
mangaId = download.manga.id,
|
||||||
|
size = DiskUtil.getDirectorySize(
|
||||||
|
File(
|
||||||
|
provider.findChapterDir(
|
||||||
|
download.chapter.name,
|
||||||
|
download.chapter.scanlator,
|
||||||
|
download.manga.title,
|
||||||
|
download.source,
|
||||||
|
)?.filePath!!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
download.status = Download.State.DOWNLOADED
|
download.status = Download.State.DOWNLOADED
|
||||||
} catch (error: Throwable) {
|
} catch (error: Throwable) {
|
||||||
if (error is CancellationException) throw error
|
if (error is CancellationException) throw error
|
||||||
|
19
data/src/main/java/tachiyomi/data/stat/DownloadStatMapper.kt
Normal file
19
data/src/main/java/tachiyomi/data/stat/DownloadStatMapper.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package tachiyomi.data.stat
|
||||||
|
|
||||||
|
import tachiyomi.domain.stat.model.DownloadStatOperation
|
||||||
|
|
||||||
|
object DownloadStatMapper {
|
||||||
|
fun map(
|
||||||
|
id: Long,
|
||||||
|
mangaId: Long?,
|
||||||
|
date: Long,
|
||||||
|
size: Long,
|
||||||
|
units: Long,
|
||||||
|
): DownloadStatOperation = DownloadStatOperation(
|
||||||
|
id = id,
|
||||||
|
mangaId = mangaId,
|
||||||
|
date = date * 1000,
|
||||||
|
size = size,
|
||||||
|
units = units,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package tachiyomi.data.stat
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import tachiyomi.data.DatabaseHandler
|
||||||
|
import tachiyomi.domain.stat.model.DownloadStatOperation
|
||||||
|
import tachiyomi.domain.stat.repository.DownloadStatRepository
|
||||||
|
|
||||||
|
class DownloadStatRepositoryImpl(
|
||||||
|
private val handler: DatabaseHandler,
|
||||||
|
) : DownloadStatRepository {
|
||||||
|
|
||||||
|
override suspend fun getStatOperations(): List<DownloadStatOperation> {
|
||||||
|
return handler.awaitList { download_statQueries.getStatOperations(DownloadStatMapper::map) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getStatOperationsAsFlow(): Flow<List<DownloadStatOperation>> {
|
||||||
|
return handler.subscribeToList { download_statQueries.getStatOperations(DownloadStatMapper::map) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun insert(operation: DownloadStatOperation) {
|
||||||
|
handler.await {
|
||||||
|
download_statQueries.insert(
|
||||||
|
mangaId = operation.mangaId,
|
||||||
|
date = operation.date / 1000,
|
||||||
|
size = operation.size,
|
||||||
|
units = operation.units,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
data/src/main/sqldelight/tachiyomi/data/download_stat.sq
Normal file
18
data/src/main/sqldelight/tachiyomi/data/download_stat.sq
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
CREATE TABLE download_stat(
|
||||||
|
_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
manga_id INTEGER,
|
||||||
|
date INTEGER NOT NULL DEFAULT 0,
|
||||||
|
size INTEGER NOT NULL,
|
||||||
|
units INTEGER NOT NULL,
|
||||||
|
|
||||||
|
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
||||||
|
ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Methods
|
||||||
|
getStatOperations:
|
||||||
|
SELECT * FROM download_stat;
|
||||||
|
|
||||||
|
insert:
|
||||||
|
INSERT INTO download_stat(manga_id, date, size, units)
|
||||||
|
VALUES (:mangaId, :date, :size, :units);
|
10
data/src/main/sqldelight/tachiyomi/migrations/28.sqm
Normal file
10
data/src/main/sqldelight/tachiyomi/migrations/28.sqm
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
CREATE TABLE download_stat(
|
||||||
|
_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
manga_id INTEGER,
|
||||||
|
date INTEGER NOT NULL DEFAULT 0,
|
||||||
|
size INTEGER NOT NULL,
|
||||||
|
units INTEGER NOT NULL,
|
||||||
|
|
||||||
|
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
||||||
|
ON DELETE SET NULL
|
||||||
|
);
|
@ -0,0 +1,13 @@
|
|||||||
|
package tachiyomi.domain.stat.interactor
|
||||||
|
|
||||||
|
import tachiyomi.domain.stat.model.DownloadStatOperation
|
||||||
|
import tachiyomi.domain.stat.repository.DownloadStatRepository
|
||||||
|
|
||||||
|
class AddDownloadStatOperation(
|
||||||
|
private val repository: DownloadStatRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(actions: DownloadStatOperation) {
|
||||||
|
repository.insert(actions)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package tachiyomi.domain.stat.interactor
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import tachiyomi.domain.stat.model.DownloadStatOperation
|
||||||
|
import tachiyomi.domain.stat.repository.DownloadStatRepository
|
||||||
|
|
||||||
|
class GetDownloadStatOperations(
|
||||||
|
private val repository: DownloadStatRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(): List<DownloadStatOperation> {
|
||||||
|
return repository.getStatOperations()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun subscribe(): Flow<List<DownloadStatOperation>> {
|
||||||
|
return repository.getStatOperationsAsFlow()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package tachiyomi.domain.stat.model
|
||||||
|
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
data class DownloadStatOperation(
|
||||||
|
val id: Long,
|
||||||
|
val mangaId: Long?,
|
||||||
|
val date: Long,
|
||||||
|
val size: Long,
|
||||||
|
val units: Long,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun create() = DownloadStatOperation(
|
||||||
|
id = -1,
|
||||||
|
mangaId = -1,
|
||||||
|
date = Date().time,
|
||||||
|
size = -1,
|
||||||
|
units = 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package tachiyomi.domain.stat.repository
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import tachiyomi.domain.stat.model.DownloadStatOperation
|
||||||
|
|
||||||
|
interface DownloadStatRepository {
|
||||||
|
|
||||||
|
suspend fun getStatOperations(): List<DownloadStatOperation>
|
||||||
|
|
||||||
|
suspend fun getStatOperationsAsFlow(): Flow<List<DownloadStatOperation>>
|
||||||
|
|
||||||
|
suspend fun insert(operation: DownloadStatOperation)
|
||||||
|
}
|
@ -31,6 +31,7 @@
|
|||||||
<string name="label_backup">Backup and restore</string>
|
<string name="label_backup">Backup and restore</string>
|
||||||
<string name="label_data_storage">Data and storage</string>
|
<string name="label_data_storage">Data and storage</string>
|
||||||
<string name="label_stats">Statistics</string>
|
<string name="label_stats">Statistics</string>
|
||||||
|
<string name="label_download_stats">Download statistics</string>
|
||||||
<string name="label_migration">Migrate</string>
|
<string name="label_migration">Migrate</string>
|
||||||
<string name="label_extensions">Extensions</string>
|
<string name="label_extensions">Extensions</string>
|
||||||
<string name="label_extension_info">Extension info</string>
|
<string name="label_extension_info">Extension info</string>
|
||||||
@ -166,6 +167,11 @@
|
|||||||
<string name="action_faq_and_guides">FAQ and Guides</string>
|
<string name="action_faq_and_guides">FAQ and Guides</string>
|
||||||
<string name="action_not_now">Not now</string>
|
<string name="action_not_now">Not now</string>
|
||||||
|
|
||||||
|
<!-- downloads stats screen -->
|
||||||
|
<string name="label_download_stats_overall_tab">Overall</string>
|
||||||
|
<string name="label_size">Size</string>
|
||||||
|
<string name="deleted_chapters">Deleted chapters</string>
|
||||||
|
|
||||||
<!-- Operations -->
|
<!-- Operations -->
|
||||||
<string name="loading">Loading…</string>
|
<string name="loading">Loading…</string>
|
||||||
<string name="internal_error">InternalError: Check crash logs for further information</string>
|
<string name="internal_error">InternalError: Check crash logs for further information</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user