add settings for skipping one-star or tracks with keyword (#724)

This commit is contained in:
Drew Weymouth
2025-09-24 18:59:55 -07:00
committed by GitHub
parent d5c47b65fa
commit 4c1a95e851
6 changed files with 51 additions and 22 deletions
+5 -3
View File
@@ -112,9 +112,11 @@ type NowPlayingPageConfig struct {
}
type PlaybackConfig struct {
Autoplay bool
RepeatMode string
UseWaveformSeekbar bool
Autoplay bool
RepeatMode string
SkipOneStarWhenShuffling bool
SkipKeywordWhenShuffling string
UseWaveformSeekbar bool
}
type LocalPlaybackConfig struct {
+32 -18
View File
@@ -11,6 +11,7 @@ import (
"sync"
"time"
"github.com/charlievieth/strcase"
"github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/backend/player"
"github.com/dweymouth/supersonic/backend/player/dlna"
@@ -27,7 +28,8 @@ type PlaybackManager struct {
wfmGen *WaveformImageGenerator
cache *AudioCache
cmdQueue *playbackCommandQueue
cfg *AppConfig
appCfg *AppConfig
cfg *PlaybackConfig
localPlayer player.BasePlayer
remotePlayersLock sync.Mutex
@@ -36,8 +38,6 @@ type PlaybackManager struct {
onWaveformImgUpdate []func(*WaveformImage)
autoplay bool
lastPlayTime float64
lastPlayingID string
wfmUpdateImageCancel context.CancelFunc
@@ -66,8 +66,8 @@ func NewPlaybackManager(
pm := &PlaybackManager{
engine: e,
cmdQueue: q,
cfg: appCfg,
autoplay: playbackCfg.Autoplay,
appCfg: appCfg,
cfg: playbackCfg,
localPlayer: p,
cache: c,
}
@@ -117,7 +117,7 @@ func (p *PlaybackManager) addOnTrackChangeHook() {
p.OnSongChange(func(item mediaprovider.MediaItem, _ *mediaprovider.Track) {
// Autoplay if enabled and we are on the last track
if p.autoplay && p.NowPlayingIndex() == len(p.engine.playQueue)-1 {
if p.cfg.Autoplay && p.NowPlayingIndex() == len(p.engine.playQueue)-1 {
p.enqueueAutoplayTracks()
}
p.handleWaveformImageSongChange(item)
@@ -470,13 +470,23 @@ func (p *PlaybackManager) PlayTrackAt(idx int) {
func (p *PlaybackManager) PlayRandomSongs(genreName string) error {
return p.fetchAndPlayTracks(func() ([]*mediaprovider.Track, error) {
return p.engine.sm.Server.GetRandomTracks(genreName, p.cfg.EnqueueBatchSize)
tr, err := p.engine.sm.Server.GetRandomTracks(genreName, p.appCfg.EnqueueBatchSize)
if err != nil {
return nil, err
}
return sharedutil.FilterSlice(tr, func(t *mediaprovider.Track) bool {
skipKwd := p.cfg.SkipKeywordWhenShuffling
include :=
(skipKwd == "" || !strcase.Contains(t.Title, skipKwd)) &&
(!p.cfg.SkipOneStarWhenShuffling || t.Rating != 1)
return include
}), nil
})
}
func (p *PlaybackManager) PlaySimilarSongs(id string) error {
return p.fetchAndPlayTracks(func() ([]*mediaprovider.Track, error) {
return p.engine.sm.Server.GetSimilarTracks(id, p.cfg.EnqueueBatchSize)
return p.engine.sm.Server.GetSimilarTracks(id, p.appCfg.EnqueueBatchSize)
})
}
@@ -592,7 +602,7 @@ func (p *PlaybackManager) GetLoopMode() LoopMode {
}
func (p *PlaybackManager) IsAutoplay() bool {
return p.autoplay
return p.cfg.Autoplay
}
func (p *PlaybackManager) PlaybackStatus() PlaybackStatus {
@@ -604,7 +614,7 @@ func (p *PlaybackManager) SetVolume(vol int) {
}
func (p *PlaybackManager) SetAutoplay(autoplay bool) {
p.autoplay = autoplay
p.cfg.Autoplay = autoplay
if autoplay && p.NowPlayingIndex() == len(p.engine.playQueue)-1 {
p.enqueueAutoplayTracks()
}
@@ -705,11 +715,15 @@ func (p *PlaybackManager) enqueueAutoplayTracks() {
// tracks we will enqueue
var tracks []*mediaprovider.Track
filterRecentlyPlayed := func(tracks []*mediaprovider.Track) []*mediaprovider.Track {
filterAutoplayTracks := func(tracks []*mediaprovider.Track) []*mediaprovider.Track {
return sharedutil.FilterSlice(tracks, func(t *mediaprovider.Track) bool {
return !slices.ContainsFunc(queue, func(i mediaprovider.MediaItem) bool {
shouldSkip :=
(p.cfg.SkipOneStarWhenShuffling && t.Rating == 1) ||
(p.cfg.SkipKeywordWhenShuffling != "" && strcase.Contains(t.Title, p.cfg.SkipKeywordWhenShuffling))
recentlyPlayed := slices.ContainsFunc(queue, func(i mediaprovider.MediaItem) bool {
return i.Metadata().Type == mediaprovider.MediaItemTypeTrack && i.Metadata().ID == t.ID
})
return !shouldSkip && !recentlyPlayed
})
}
@@ -722,11 +736,11 @@ func (p *PlaybackManager) enqueueAutoplayTracks() {
// similar tracks by artist
if len(tr.ArtistIDs) > 0 {
similar, err := s.GetSimilarTracks(tr.ArtistIDs[0], p.cfg.EnqueueBatchSize)
similar, err := s.GetSimilarTracks(tr.ArtistIDs[0], p.appCfg.EnqueueBatchSize)
if err != nil {
log.Printf("autoplay error: failed to get similar tracks: %v", err)
}
tracks = filterRecentlyPlayed(similar)
tracks = filterAutoplayTracks(similar)
}
// fallback to random tracks from genre
@@ -735,11 +749,11 @@ func (p *PlaybackManager) enqueueAutoplayTracks() {
if g == "" {
continue
}
byGenre, err := s.GetRandomTracks(g, p.cfg.EnqueueBatchSize)
byGenre, err := s.GetRandomTracks(g, p.appCfg.EnqueueBatchSize)
if err != nil {
log.Printf("autoplay error: failed to get tracks by genre: %v", err)
}
tracks = filterRecentlyPlayed(byGenre)
tracks = filterAutoplayTracks(byGenre)
if len(tracks) > 0 {
break
}
@@ -750,11 +764,11 @@ func (p *PlaybackManager) enqueueAutoplayTracks() {
// random tracks works regardless of the type of the last playing media
if len(tracks) == 0 {
// fallback to random tracks
random, err := s.GetRandomTracks("", p.cfg.EnqueueBatchSize)
random, err := s.GetRandomTracks("", p.appCfg.EnqueueBatchSize)
if err != nil {
log.Printf("autoplay error: failed to get random tracks: %v", err)
}
tracks = filterRecentlyPlayed(random)
tracks = filterAutoplayTracks(random)
}
if len(tracks) > 0 {
+1
View File
@@ -33,6 +33,7 @@ require (
al.essio.dev/pkg/shellescape v1.5.1 // indirect
fyne.io/systray v1.11.0 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/charlievieth/strcase v0.0.5 // indirect
github.com/danieljoos/wincred v1.2.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fogleman/gg v1.3.0 // indirect
+2
View File
@@ -10,6 +10,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/cenkalti/dominantcolor v1.0.3 h1:Pt0vfRZ8enkZh1n22RvoboA53SMM/v2aEwNQTZKSqww=
github.com/cenkalti/dominantcolor v1.0.3/go.mod h1:mGpFMbWUnyXaGN48Zbf9bU9HJP1eCCD7dnsscb4lyR4=
github.com/charlievieth/strcase v0.0.5 h1:gV4iXVyD6eI5KdfOV+/vIVCKXZwtCWOmDMcu7Uy00Rs=
github.com/charlievieth/strcase v0.0.5/go.mod h1:FIOYY1aDBMSIOFqmVomHBpoK+bteGlESRsgsdWjrhx8=
github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+3
View File
@@ -211,8 +211,10 @@
"Singles": "Singles",
"Size": "Size",
"Skip duplicate tracks": "Skip duplicate tracks",
"Skip one-star tracks": "Skip one-star tracks",
"Skip SSL certificate verification": "Skip SSL certificate verification",
"Skip this version": "Skip this version",
"Skip tracks with keyword": "Skip tracks with keyword",
"Smaller": "Smaller",
"Sort": "Sort",
"Soundtrack": "Soundtrack",
@@ -251,6 +253,7 @@
"version": "version",
"Visualizations": "Visualizations",
"Volume": "Volume",
"When enqueuing random": "When enqueuing random",
"wrong URL": "wrong URL",
"wrong username/password": "wrong username/password",
"Year": "Year",
+8 -1
View File
@@ -427,12 +427,19 @@ func (s *SettingsDialog) createPlaybackTab(isLocalPlayer, isReplayGainPlayer boo
disableTranscode,
container.NewHBox(transcode, transcodeCodec, transcodeBitRate),
s.newSectionSeparator(),
widget.NewRichText(&widget.TextSegment{Text: "ReplayGain", Style: util.BoldRichTextStyle}),
widget.NewLabelWithStyle("ReplayGain", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
container.New(layout.NewFormLayout(),
widget.NewLabel(lang.L("ReplayGain mode")), container.NewGridWithColumns(2, replayGainSelect),
widget.NewLabel(lang.L("ReplayGain preamp")), container.NewHBox(preampGain, widget.NewLabel("dB")),
widget.NewLabel(lang.L("Prevent clipping")), preventClipping,
),
s.newSectionSeparator(),
widget.NewLabelWithStyle(lang.L("When enqueuing random"), fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
widget.NewCheckWithData(lang.L("Skip one-star tracks"), binding.BindBool(&s.config.Playback.SkipOneStarWhenShuffling)),
container.NewBorder(nil, nil,
widget.NewLabel(lang.L("Skip tracks with keyword")), nil,
widget.NewEntryWithData(binding.BindString(&s.config.Playback.SkipKeywordWhenShuffling)),
),
))
}