Skip to content

Commit eaf7769

Browse files
authored
Merge pull request #111 from nouun/feature/synced-lyrics
feat(lyrics): add ability to fetch synced lyrics
2 parents 49ff4d8 + e4e0b07 commit eaf7769

File tree

7 files changed

+61
-14
lines changed

7 files changed

+61
-14
lines changed

config.example.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ lyrics:
5555
providers:
5656
lrclib:
5757
enabled: true
58+
prefer_synced: false # Some programs like navidrome require synced lyrics
5859
sync:
5960
enabled: true
6061
devices:

docs/jobs.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ jobs:
88
log_path: ./logs/jobs
99
webhooks:
1010
enabled: true
11-
job_types:
12-
- directory_import
11+
job_types:
12+
- directory_import
1313
- download_album
1414
- dap_sync
1515
command: | # ntfy

docs/lyrics.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Lyrics
2+
3+
Soulsolid supports automatic lyric retrieval and embedding via lyric providers
4+
5+
Lyric handling is separate from downloaders, allowing new providers to be added without modifying downloader plugins.
6+
7+
## Lyric Providers
8+
9+
Lyric providers are responsible for searching, retrieving, and returning lyrics for tracks based on available metadata (e.g. title, artist, album, ISRC, duration).
10+
11+
Providers may supply:
12+
• Unsynced lyrics — plain text lyrics
13+
• Synced lyrics — time-aligned lyrics (e.g. LRC-style timestamps)
14+
15+
## Configuration
16+
17+
Lyric providers are configured in the main config.yaml file:
18+
19+
```yml
20+
lyrics:
21+
providers:
22+
lrclib:
23+
enabled: true
24+
prefer_synced: false
25+
```
26+
27+
## Provider Options
28+
- **enabled**: Enables or disables the provider entirely.
29+
- **prefer_synced**: When set to true, synced lyrics will be preferred over unsynced lyrics if both are available.

src/features/config/config.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,23 @@ type Metadata struct {
8181
Providers map[string]Provider `yaml:"providers"`
8282
}
8383

84-
// Lyrics holds the configuration for lyrics providers
85-
type Lyrics struct {
86-
Providers map[string]Provider `yaml:"providers"`
87-
}
88-
8984
// Provider holds configuration for individual tagging providers
9085
type Provider struct {
9186
Enabled bool `yaml:"enabled"`
9287
Secret *string `yaml:"secret,omitempty"`
9388
}
9489

90+
// Lyrics holds the configuration for lyrics providers
91+
type Lyrics struct {
92+
Providers map[string]LyricsProvider `yaml:"providers"`
93+
}
94+
95+
// Provider holds configuration for individual lyric providers
96+
type LyricsProvider struct {
97+
Enabled bool `yaml:"enabled"`
98+
PreferSynced bool `yaml:"prefer_synced,omitempty"`
99+
}
100+
95101
// Sync holds configuration for device synchronization
96102
type Sync struct {
97103
Enabled bool `yaml:"enabled"`

src/features/config/loader.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,11 @@ func createDefaultConfig() *Config {
145145
},
146146
},
147147
},
148-
Lyrics: Lyrics{ // NOTE: Add this to the documentation
149-
Providers: map[string]Provider{
148+
Lyrics: Lyrics{
149+
Providers: map[string]LyricsProvider{
150150
"lrclib": {
151-
Enabled: true,
151+
Enabled: true,
152+
PreferSynced: false,
152153
},
153154
},
154155
},

src/infra/providers/lrclib.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ type lrclibSong struct {
2626

2727
// LRCLibProvider implements LyricsProvider for LRCLib
2828
type LRCLibProvider struct {
29-
enabled bool
29+
enabled bool
30+
preferSynced bool
3031
}
3132

3233
// NewLRCLibProvider creates a new LRCLib provider
33-
func NewLRCLibProvider(enabled bool) *LRCLibProvider {
34-
return &LRCLibProvider{enabled: enabled}
34+
func NewLRCLibProvider(enabled bool, preferSynced bool) *LRCLibProvider {
35+
return &LRCLibProvider{enabled: enabled, preferSynced: preferSynced}
3536
}
3637

3738
func (p *LRCLibProvider) SearchLyrics(ctx context.Context, params music.LyricsSearchParams) (string, error) {
@@ -84,6 +85,15 @@ func (p *LRCLibProvider) SearchLyrics(ctx context.Context, params music.LyricsSe
8485
return "", fmt.Errorf("no lyrics found")
8586
}
8687

88+
// Iterate over all songs to find synced lyrics
89+
if p.preferSynced {
90+
for _, song := range searchResp {
91+
if song.SyncedLyrics != "" {
92+
return song.SyncedLyrics, nil
93+
}
94+
}
95+
}
96+
8797
// Return the first result's plain lyrics
8898
song := searchResp[0]
8999
if song.PlainLyrics != "" {

src/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func main() {
9090
discogsProvider := providers.NewDiscogsProvider(cfgManager.Get().Metadata.Providers["discogs"].Enabled, discogsSecret)
9191
deezerProvider := providers.NewDeezerProvider(cfgManager.Get().Metadata.Providers["deezer"].Enabled)
9292

93-
lrclibProvider := providers.NewLRCLibProvider(cfgManager.Get().Lyrics.Providers["lrclib"].Enabled)
93+
lrclibProvider := providers.NewLRCLibProvider(cfgManager.Get().Lyrics.Providers["lrclib"].Enabled, cfgManager.Get().Lyrics.Providers["lrclib"].PreferSynced)
9494

9595
acoustIDService := providers.NewAcoustIDService(cfgManager)
9696
lyricsService := lyrics.NewService(tagWriter, tagReader, db, map[string]lyrics.LyricsProvider{

0 commit comments

Comments
 (0)