Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data saver #1081

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions app/src/main/java/eu/kanade/domain/SYDomainModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package eu.kanade.domain

import eu.kanade.domain.source.manga.interactor.ToggleExcludeFromMangaDataSaver
import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addFactory
import uy.kohesive.injekt.api.get

class SYDomainModule : InjektModule {

override fun InjektRegistrar.registerInjectables() {
addFactory { ToggleExcludeFromMangaDataSaver(get()) }
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package eu.kanade.domain.source.manga.interactor

import eu.kanade.domain.source.service.SourcePreferences
import exh.source.BlacklistedSources
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
Expand All @@ -19,17 +20,27 @@ class GetEnabledMangaSources(
return combine(
preferences.pinnedMangaSources().changes(),
preferences.enabledLanguages().changes(),
preferences.disabledMangaSources().changes(),
preferences.lastUsedMangaSource().changes(),
combine(
preferences.disabledMangaSources().changes(),
preferences.lastUsedMangaSource().changes(),
// SY -->
preferences.dataSaverExcludedSources().changes(),
// SY <--
) { a, b, c -> Triple(a, b, c) },
repository.getMangaSources(),
) { pinnedSourceIds, enabledLanguages, disabledSources, lastUsedSource, sources ->
) { pinnedMangaSourceIds, enabledLanguages, (disabledSources, lastUsedSource, excludedFromDataSaver), sources ->
sources
.filter { it.lang in enabledLanguages || it.id == LocalMangaSource.ID }
.filterNot { it.id.toString() in disabledSources }
.filterNot { it.id.toString() in disabledSources || it.id in BlacklistedSources.HIDDEN_SOURCES }
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name })
.flatMap {
val flag = if ("${it.id}" in pinnedSourceIds) Pins.pinned else Pins.unpinned
val source = it.copy(pin = flag)
val flag = if ("${it.id}" in pinnedMangaSourceIds) Pins.pinned else Pins.unpinned
val source = it.copy(
pin = flag,
// SY -->
isExcludedFromDataSaver = it.id.toString() in excludedFromDataSaver,
// SY <--
)
val toFlatten = mutableListOf(source)
if (source.id == lastUsedSource) {
toFlatten.add(source.copy(isUsedLast = true, pin = source.pin - Pin.Actual))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package eu.kanade.domain.source.manga.interactor

import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.preference.getAndSet
import tachiyomi.domain.source.manga.model.Source

class ToggleExcludeFromMangaDataSaver(
private val preferences: SourcePreferences,
) {

fun await(source: Source) {
preferences.dataSaverExcludedSources().getAndSet {
if (source.id.toString() in it) {
it - source.id.toString()
} else {
it + source.id.toString()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,39 @@ class SourcePreferences(
fun hideInAnimeLibraryItems() = preferenceStore.getBoolean("browse_hide_in_anime_library_items", false)

fun hideInMangaLibraryItems() = preferenceStore.getBoolean("browse_hide_in_library_items", false)

// SY -->

// fun enableSourceBlacklist() = preferenceStore.getBoolean("eh_enable_source_blacklist", true)

// fun sourcesTabCategories() = preferenceStore.getStringSet("sources_tab_categories", mutableSetOf())

// fun sourcesTabCategoriesFilter() = preferenceStore.getBoolean("sources_tab_categories_filter", false)

// fun sourcesTabSourcesInCategories() = preferenceStore.getStringSet("sources_tab_source_categories", mutableSetOf())

fun dataSaver() = preferenceStore.getEnum("data_saver", DataSaver.NONE)

fun dataSaverIgnoreJpeg() = preferenceStore.getBoolean("ignore_jpeg", false)

fun dataSaverIgnoreGif() = preferenceStore.getBoolean("ignore_gif", true)

fun dataSaverImageQuality() = preferenceStore.getInt("data_saver_image_quality", 80)

fun dataSaverImageFormatJpeg() = preferenceStore.getBoolean("data_saver_image_format_jpeg", false)

fun dataSaverServer() = preferenceStore.getString("data_saver_server", "")

fun dataSaverColorBW() = preferenceStore.getBoolean("data_saver_color_bw", false)

fun dataSaverExcludedSources() = preferenceStore.getStringSet("data_saver_excluded", emptySet())

fun dataSaverDownloader() = preferenceStore.getBoolean("data_saver_downloader", true)

enum class DataSaver {
NONE,
BANDWIDTH_HERO,
WSRV_NL,
}
// SY <--
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ fun MangaSourceOptionsDialog(
source: Source,
onClickPin: () -> Unit,
onClickDisable: () -> Unit,
// SY -->
onClickToggleDataSaver: (() -> Unit)?,
// SY <--
onDismiss: () -> Unit,
) {
AlertDialog(
Expand All @@ -185,6 +188,21 @@ fun MangaSourceOptionsDialog(
.padding(vertical = 16.dp),
)
}
// SY -->
if (onClickToggleDataSaver != null) {
Text(
text = if (source.isExcludedFromDataSaver) {
stringResource(id = R.string.data_saver_stop_exclude)
} else {
stringResource(id = R.string.data_saver_exclude)
},
modifier = Modifier
.clickable(onClick = onClickToggleDataSaver)
.fillMaxWidth()
.padding(vertical = 16.dp),
)
}
// SY <--
}
},
onDismissRequest = onDismiss,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import androidx.core.net.toUri
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.source.service.SourcePreferences.DataSaver
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.screen.debug.DebugInfoScreen
import eu.kanade.presentation.util.collectAsState
Expand Down Expand Up @@ -115,6 +117,9 @@ object SettingsAdvancedScreen : SearchableSettings {
getNetworkGroup(networkPreferences = networkPreferences),
getLibraryGroup(),
getExtensionsGroup(basePreferences = basePreferences),
// SY -->
getDataSaverGroup(),
// SY <--
)
}

Expand Down Expand Up @@ -400,4 +405,82 @@ object SettingsAdvancedScreen : SearchableSettings {
),
)
}

// SY -->
@Composable
private fun getDataSaverGroup(): Preference.PreferenceGroup {
val sourcePreferences = remember { Injekt.get<SourcePreferences>() }
val dataSaver by sourcePreferences.dataSaver().collectAsState()
return Preference.PreferenceGroup(
title = stringResource(R.string.data_saver),
preferenceItems = listOf(
Preference.PreferenceItem.ListPreference(
pref = sourcePreferences.dataSaver(),
title = stringResource(R.string.data_saver),
subtitle = stringResource(R.string.data_saver_summary),
entries = mapOf(
DataSaver.NONE to stringResource(R.string.disabled),
DataSaver.BANDWIDTH_HERO to stringResource(R.string.bandwidth_hero),
DataSaver.WSRV_NL to stringResource(R.string.wsrv),
),
),
Preference.PreferenceItem.EditTextPreference(
pref = sourcePreferences.dataSaverServer(),
title = stringResource(R.string.bandwidth_data_saver_server),
subtitle = stringResource(R.string.data_saver_server_summary),
enabled = dataSaver == DataSaver.BANDWIDTH_HERO,
),
Preference.PreferenceItem.SwitchPreference(
pref = sourcePreferences.dataSaverDownloader(),
title = stringResource(R.string.data_saver_downloader),
enabled = dataSaver != DataSaver.NONE,
),
Preference.PreferenceItem.SwitchPreference(
pref = sourcePreferences.dataSaverIgnoreJpeg(),
title = stringResource(R.string.data_saver_ignore_jpeg),
enabled = dataSaver != DataSaver.NONE,
),
Preference.PreferenceItem.SwitchPreference(
pref = sourcePreferences.dataSaverIgnoreGif(),
title = stringResource(R.string.data_saver_ignore_gif),
enabled = dataSaver != DataSaver.NONE,
),
Preference.PreferenceItem.ListPreference(
pref = sourcePreferences.dataSaverImageQuality(),
title = stringResource(R.string.data_saver_image_quality),
subtitle = stringResource(R.string.data_saver_image_quality_summary),
entries = listOf(
"10%",
"20%",
"40%",
"50%",
"70%",
"80%",
"90%",
"95%",
).associateBy { it.trimEnd('%').toInt() },
enabled = dataSaver != DataSaver.NONE,
),
kotlin.run {
val dataSaverImageFormatJpeg by sourcePreferences.dataSaverImageFormatJpeg().collectAsState()
Preference.PreferenceItem.SwitchPreference(
pref = sourcePreferences.dataSaverImageFormatJpeg(),
title = stringResource(R.string.data_saver_image_format),
subtitle = if (dataSaverImageFormatJpeg) {
stringResource(R.string.data_saver_image_format_summary_on)
} else {
stringResource(R.string.data_saver_image_format_summary_off)
},
enabled = dataSaver != DataSaver.NONE,
)
},
Preference.PreferenceItem.SwitchPreference(
pref = sourcePreferences.dataSaverColorBW(),
title = stringResource(R.string.data_saver_color_bw),
enabled = dataSaver == DataSaver.BANDWIDTH_HERO,
),
),
)
}
// SY <--
}
4 changes: 4 additions & 0 deletions app/src/main/java/eu/kanade/tachiyomi/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import coil.decode.ImageDecoderDecoder
import coil.disk.DiskCache
import coil.util.DebugLogger
import eu.kanade.domain.DomainModule
import eu.kanade.domain.SYDomainModule
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
Expand Down Expand Up @@ -85,6 +86,9 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
Injekt.importModule(AppModule(this))
Injekt.importModule(PreferenceModule(this))
Injekt.importModule(DomainModule())
// SY -->
Injekt.importModule(SYDomainModule())
// SY <--

setupAcra()
setupNotificationChannels()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.hippo.unifile.UniFile
import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.domain.entries.manga.model.getComicInfo
import eu.kanade.domain.items.chapter.model.toSChapter
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.download.manga.model.MangaDownload
Expand All @@ -16,6 +17,8 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.DiskUtil.NOMEDIA_FILE
import eu.kanade.tachiyomi.util.storage.saveTo
import exh.util.DataSaver
import exh.util.DataSaver.Companion.getImage
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
Expand Down Expand Up @@ -75,6 +78,9 @@ class MangaDownloader(
private val chapterCache: ChapterCache = Injekt.get(),
private val downloadPreferences: DownloadPreferences = Injekt.get(),
private val xml: XML = Injekt.get(),
// SY -->
private val sourcePreferences: SourcePreferences = Injekt.get(),
// SY <--
) {

/**
Expand Down Expand Up @@ -331,6 +337,12 @@ class MangaDownloader(
reIndexedPages
}

val dataSaver = if (sourcePreferences.dataSaverDownloader().get()) {
DataSaver(download.source, sourcePreferences)
} else {
DataSaver.NoOp
}

// Delete all temporary (unfinished) files
tmpDir.listFiles()
?.filter { it.name!!.endsWith(".tmp") }
Expand All @@ -353,7 +365,7 @@ class MangaDownloader(
pageList.asFlow()
.flatMapMerge(concurrency = 2) { page ->
flow {
withIOContext { getOrDownloadImage(page, download, tmpDir) }
withIOContext { getOrDownloadImage(page, download, tmpDir, dataSaver) }
emit(page)
}.flowOn(Dispatchers.IO)
}
Expand All @@ -380,7 +392,7 @@ class MangaDownloader(
* @param download the download of the page.
* @param tmpDir the temporary directory of the download.
*/
private suspend fun getOrDownloadImage(page: Page, download: MangaDownload, tmpDir: UniFile) {
private suspend fun getOrDownloadImage(page: Page, download: MangaDownload, tmpDir: UniFile, dataSaver: DataSaver) {
// If the image URL is empty, do nothing
if (page.imageUrl == null) {
return
Expand All @@ -401,7 +413,7 @@ class MangaDownloader(
val file = when {
imageFile != null -> imageFile
chapterCache.isImageInCache(page.imageUrl!!) -> copyImageFromCache(chapterCache.getImageFile(page.imageUrl!!), tmpDir, filename)
else -> downloadImage(page, download.source, tmpDir, filename)
else -> downloadImage(page, download.source, tmpDir, filename, dataSaver)
}

// When the page is ready, set page path, progress (just in case) and status
Expand All @@ -426,11 +438,11 @@ class MangaDownloader(
* @param tmpDir the temporary directory of the download.
* @param filename the filename of the image.
*/
private suspend fun downloadImage(page: Page, source: HttpSource, tmpDir: UniFile, filename: String): UniFile {
private suspend fun downloadImage(page: Page, source: HttpSource, tmpDir: UniFile, filename: String, dataSaver: DataSaver): UniFile {
page.status = Page.State.DOWNLOAD_IMAGE
page.progress = 0
return flow {
val response = source.getImage(page)
val response = source.getImage(page, dataSaver)
val file = tmpDir.createFile("$filename.tmp")
try {
response.body.source().saveTo(file.openOutputStream())
Expand Down
Loading