From 9e89bab2fb5eeea69646f0fdb9770855ba120c39 Mon Sep 17 00:00:00 2001 From: sevonj <100710152+sevonj@users.noreply.github.com> Date: Thu, 5 Dec 2024 21:40:19 +0200 Subject: [PATCH] Feat: Song duration filter --- .phrasey/schema.toml | 3 ++ .../zyrouge/symphony/services/Settings.kt | 1 + .../symphony/services/groove/MediaExposer.kt | 5 ++++ .../ui/view/settings/GrooveSettingsView.kt | 30 +++++++++++++++++++ i18n/en.toml | 1 + 5 files changed, 40 insertions(+) diff --git a/.phrasey/schema.toml b/.phrasey/schema.toml index 55e218a9..48cb980c 100644 --- a/.phrasey/schema.toml +++ b/.phrasey/schema.toml @@ -756,3 +756,6 @@ name = "CaseSensitiveSorting" [[keys]] name = "KeepScreenAwakeOnLyrics" + +[[keys]] +name = "MinSongDurationFilter" diff --git a/app/src/main/java/io/github/zyrouge/symphony/services/Settings.kt b/app/src/main/java/io/github/zyrouge/symphony/services/Settings.kt index 67d39b54..e3cdac3b 100644 --- a/app/src/main/java/io/github/zyrouge/symphony/services/Settings.kt +++ b/app/src/main/java/io/github/zyrouge/symphony/services/Settings.kt @@ -253,6 +253,7 @@ class Settings(private val symphony: Symphony) { } val lastHomeTab = EnumEntry("home_last_page", enumEntries(), HomePage.Songs) val songsFilterPattern = NullableStringEntry("songs_filter_pattern") + val minSongDuration = IntEntry("min_song_duration", 0) val checkForUpdates = BooleanEntry("check_for_updates", false) val fadePlayback = BooleanEntry("fade_playback", false) val requireAudioFocus = BooleanEntry("require_audio_focus", true) diff --git a/app/src/main/java/io/github/zyrouge/symphony/services/groove/MediaExposer.kt b/app/src/main/java/io/github/zyrouge/symphony/services/groove/MediaExposer.kt index 50eff091..a527ab4a 100644 --- a/app/src/main/java/io/github/zyrouge/symphony/services/groove/MediaExposer.kt +++ b/app/src/main/java/io/github/zyrouge/symphony/services/groove/MediaExposer.kt @@ -19,6 +19,8 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.withContext import java.util.concurrent.ConcurrentHashMap +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds class MediaExposer(private val symphony: Symphony) { internal val uris = ConcurrentHashMap() @@ -128,6 +130,9 @@ class MediaExposer(private val symphony: Symphony) { cacheHit -> cached else -> Song.parse(symphony, path, file) } + if (song.duration.milliseconds < symphony.settings.minSongDuration.value.seconds) { + return + } if (!cacheHit) { symphony.database.songCache.insert(song) cached?.coverFile?.let { diff --git a/app/src/main/java/io/github/zyrouge/symphony/ui/view/settings/GrooveSettingsView.kt b/app/src/main/java/io/github/zyrouge/symphony/ui/view/settings/GrooveSettingsView.kt index dea489d7..30fefb41 100644 --- a/app/src/main/java/io/github/zyrouge/symphony/ui/view/settings/GrooveSettingsView.kt +++ b/app/src/main/java/io/github/zyrouge/symphony/ui/view/settings/GrooveSettingsView.kt @@ -53,6 +53,7 @@ import io.github.zyrouge.symphony.ui.components.settings.SettingsMultiTextOption import io.github.zyrouge.symphony.ui.components.settings.SettingsOptionTile import io.github.zyrouge.symphony.ui.components.settings.SettingsSideHeading import io.github.zyrouge.symphony.ui.components.settings.SettingsSimpleTile +import io.github.zyrouge.symphony.ui.components.settings.SettingsSliderTile import io.github.zyrouge.symphony.ui.components.settings.SettingsSwitchTile import io.github.zyrouge.symphony.ui.components.settings.SettingsTextInputTile import io.github.zyrouge.symphony.ui.helpers.TransitionDurations @@ -63,6 +64,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.serialization.Serializable +import kotlin.math.roundToInt @Serializable data class GrooveSettingsViewRoute(val initialElement: String? = null) @@ -74,6 +76,7 @@ fun GrooveSettingsView(context: ViewContext, route: GrooveSettingsViewRoute) { val snackbarHostState = remember { SnackbarHostState() } val scrollState = rememberScrollState() val songsFilterPattern by context.symphony.settings.songsFilterPattern.flow.collectAsState() + val minSongDuration by context.symphony.settings.minSongDuration.flow.collectAsState() val blacklistFolders by context.symphony.settings.blacklistFolders.flow.collectAsState() val whitelistFolders by context.symphony.settings.whitelistFolders.flow.collectAsState() val artistTagSeparators by context.symphony.settings.artistTagSeparators.flow.collectAsState() @@ -122,6 +125,7 @@ fun GrooveSettingsView(context: ViewContext, route: GrooveSettingsViewRoute) { ) { Column(modifier = Modifier.verticalScroll(scrollState)) { val defaultSongsFilterPattern = ".*" + val minSongDurationRange = 0f..60f ConsiderContributingTile(context) SettingsSideHeading(context.symphony.t.Groove) @@ -165,6 +169,32 @@ fun GrooveSettingsView(context: ViewContext, route: GrooveSettingsViewRoute) { } ) HorizontalDivider() + SettingsSliderTile( + context, + icon = { + Icon(Icons.Filled.FilterAlt, null) + }, + title = { + Text(context.symphony.t.MinSongDurationFilter) + }, + label = { value -> + Text(context.symphony.t.XSecs(value.toString())) + }, + range = minSongDurationRange, + initialValue = minSongDuration.toFloat(), + onValue = { value -> + value.roundToInt().toFloat() + }, + onChange = { value -> + context.symphony.settings.minSongDuration.setValue(value.toInt()) + }, + onReset = { + context.symphony.settings.minSongDuration.setValue( + context.symphony.settings.minSongDuration.defaultValue, + ) + }, + ) + HorizontalDivider() SettingsMultiGrooveFolderTile( context, icon = { diff --git a/i18n/en.toml b/i18n/en.toml index 2fe9e4c4..a5500fa6 100644 --- a/i18n/en.toml +++ b/i18n/en.toml @@ -253,3 +253,4 @@ GaplessPlayback = "Gapless playback" GridColumns = "Grid columns" CaseSensitiveSorting = "Case sensitive sorting" KeepScreenAwakeOnLyrics = "Keep screen awake on lyrics content" +MinSongDurationFilter = "Minimum song duration filter" \ No newline at end of file