Skip to content

Commit d19a2f0

Browse files
committed
feat: add support for synchronized lyrics
1 parent 84acf24 commit d19a2f0

File tree

23 files changed

+231
-46
lines changed

23 files changed

+231
-46
lines changed

cmd/attach.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"path/filepath"
55
"strconv"
66

7-
"github.com/bogem/id3v2/v2"
87
"github.com/spf13/cobra"
8+
"github.com/streambinder/id3v2-sylt"
99
"github.com/streambinder/spotitube/downloader"
1010
"github.com/streambinder/spotitube/entity/id3"
1111
"github.com/streambinder/spotitube/lyrics"

cmd/attach_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"testing"
66

77
"github.com/agiledragon/gomonkey/v2"
8-
"github.com/bogem/id3v2/v2"
8+
"github.com/streambinder/id3v2-sylt"
99
"github.com/streambinder/spotitube/downloader"
1010
"github.com/streambinder/spotitube/entity"
1111
"github.com/streambinder/spotitube/lyrics"

cmd/show.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import (
55
"os"
66
"text/tabwriter"
77

8-
"github.com/bogem/id3v2/v2"
98
"github.com/fatih/color"
109
"github.com/spf13/cobra"
10+
"github.com/streambinder/id3v2-sylt"
1111
"github.com/streambinder/spotitube/entity/id3"
1212
"github.com/streambinder/spotitube/util"
1313
)
@@ -45,7 +45,7 @@ func cmdShow() *cobra.Command {
4545
fmt.Fprintln(table, "Artwork URL\t", util.Fallback(tag.ArtworkURL(), fallback))
4646
fmt.Fprintln(table, "Duration\t", util.Fallback(fmt.Sprintf("%ss", tag.Duration()), fallback))
4747
fmt.Fprintln(table, "Upstream URL\t", util.Fallback(tag.UpstreamURL(), fallback))
48-
fmt.Fprintln(table, "Lyrics\t", util.Fallback(util.Excerpt(tag.UnsynchronizedLyrics(), 64), fallback))
48+
fmt.Fprintln(table, "Lyrics\t", util.Fallback(util.Excerpt(util.FirstLine(tag.UnsynchronizedLyrics()), 64)+"...", fallback))
4949
fmt.Fprintln(table, "Artwork\t", func(mimeType string, data []byte) string {
5050
if len(data) > 0 {
5151
return fmt.Sprintf("%s (%s)", mimeType, util.HumanizeBytes(len(data)))

cmd/sync.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import (
1010

1111
"github.com/adrg/xdg"
1212
"github.com/arunsworld/nursery"
13-
"github.com/bogem/id3v2/v2"
1413
"github.com/spf13/cobra"
1514
"github.com/spf13/pflag"
15+
"github.com/streambinder/id3v2-sylt"
1616
"github.com/streambinder/spotitube/downloader"
1717
"github.com/streambinder/spotitube/entity"
1818
"github.com/streambinder/spotitube/entity/id3"
@@ -394,7 +394,7 @@ func routineCollectLyrics(track *entity.Track) func(context.Context, chan error)
394394
}
395395
tui.Lot("compose").Wipe()
396396
track.Lyrics = lyrics
397-
tui.Printf("lyrics for %s by %s: %s", track.Title, track.Artists[0], util.Excerpt(lyrics))
397+
tui.Printf("lyrics for %s by %s: %s...", track.Title, track.Artists[0], util.Excerpt(util.FirstLine(lyrics), 64))
398398
}
399399
}
400400

cmd/sync_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"time"
88

99
"github.com/agiledragon/gomonkey/v2"
10-
"github.com/bogem/id3v2/v2"
10+
"github.com/streambinder/id3v2-sylt"
1111
"github.com/streambinder/spotitube/downloader"
1212
"github.com/streambinder/spotitube/entity"
1313
"github.com/streambinder/spotitube/entity/id3"

entity/id3/id3.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package id3
22

33
import (
4+
"fmt"
45
"strings"
56

6-
"github.com/bogem/id3v2/v2"
7+
"github.com/streambinder/id3v2-sylt"
78
"github.com/streambinder/spotitube/lyrics"
9+
"github.com/streambinder/spotitube/util"
810
)
911

1012
const (
1113
frameAttachedPicture = "Attached picture"
1214
frameTrackNumber = "Track number/Position in set"
1315
frameUnsynchronizedLyrics = "Unsynchronised lyrics/text transcription"
16+
frameSynchronizedLyrics = "Synchronised lyrics/text"
1417
frameSpotifyID = "Spotify ID"
1518
frameArtworkURL = "Artwork URL"
1619
frameDuration = "Duration"
@@ -126,20 +129,47 @@ func (tag *Tag) SetLyrics(title, lyrics string) {
126129
tag.setUnsynchronizedLyrics(title, lyrics)
127130
}
128131

129-
func (tag *Tag) setSynchronizedLyrics(_, data string) {
132+
func (tag *Tag) setSynchronizedLyrics(title, data string) {
130133
if !lyrics.IsSynced(data) {
131134
return
132135
}
133136

134-
// implement me
137+
var syncedText []id3v2.SyncedText
138+
for _, line := range lyrics.GetSync(data) {
139+
syncedText = append(syncedText, id3v2.SyncedText{
140+
Timestamp: line.Time,
141+
Text: line.Text,
142+
})
143+
}
144+
145+
tag.AddSynchronisedLyricsFrame(id3v2.SynchronisedLyricsFrame{
146+
Encoding: tag.DefaultEncoding(),
147+
Language: "eng",
148+
TimestampFormat: id3v2.SYLTAbsoluteMillisecondsTimestampFormat,
149+
ContentType: id3v2.SYLTLyricsContentType,
150+
ContentDescriptor: title,
151+
SynchronizedTexts: syncedText,
152+
})
153+
}
154+
155+
func (tag *Tag) SynchronizedLyrics() string {
156+
frame, ok := tag.GetLastFrame(tag.CommonID(frameSynchronizedLyrics)).(id3v2.SynchronisedLyricsFrame)
157+
if ok {
158+
var lyrics []string
159+
for _, line := range frame.SynchronizedTexts {
160+
lyrics = append(lyrics, fmt.Sprintf("[%s]%s", util.MillisToColonMinutes(line.Timestamp), line.Text))
161+
}
162+
return strings.Join(lyrics, "\n")
163+
}
164+
return ""
135165
}
136166

137-
func (tag *Tag) setUnsynchronizedLyrics(title, lyrics string) {
167+
func (tag *Tag) setUnsynchronizedLyrics(title, data string) {
138168
tag.AddUnsynchronisedLyricsFrame(id3v2.UnsynchronisedLyricsFrame{
139169
Encoding: tag.DefaultEncoding(),
140170
Language: "eng",
141171
ContentDescriptor: title,
142-
Lyrics: lyrics,
172+
Lyrics: lyrics.GetPlain(data),
143173
})
144174
}
145175

entity/id3/id3_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"testing"
66

77
"github.com/agiledragon/gomonkey/v2"
8-
"github.com/bogem/id3v2/v2"
8+
"github.com/streambinder/id3v2-sylt"
99
"github.com/streambinder/spotitube/util"
1010
"github.com/stretchr/testify/assert"
1111
)
@@ -31,9 +31,11 @@ func TestOpen(t *testing.T) {
3131
assert.Empty(t, mimeType)
3232
assert.Empty(t, image)
3333
assert.Empty(t, tag.UnsynchronizedLyrics())
34+
assert.Empty(t, tag.SynchronizedLyrics())
3435

3536
tag.SetAttachedPicture([]byte("picture"))
3637
tag.SetLyrics("title", "lyrics")
38+
tag.SetLyrics("title", "[01:01.15]lyrics")
3739
tag.SetTrackNumber("1")
3840
tag.SetSpotifyID("Spotify ID")
3941
tag.SetArtworkURL("Artwork URL")
@@ -43,7 +45,8 @@ func TestOpen(t *testing.T) {
4345
mimeType, image = tag.AttachedPicture()
4446
assert.Equal(t, "image/jpeg", mimeType)
4547
assert.Equal(t, []byte("picture"), image)
46-
assert.Equal(t, "lyrics", tag.UnsynchronizedLyrics())
48+
assert.Equal(t, "[01:01.15]lyrics", tag.UnsynchronizedLyrics())
49+
assert.Equal(t, "[01:01.15]lyrics", tag.SynchronizedLyrics())
4750
assert.Equal(t, "1", tag.TrackNumber())
4851
assert.Equal(t, "Spotify ID", tag.SpotifyID())
4952
assert.Equal(t, "Artwork URL", tag.ArtworkURL())

entity/index/index.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import (
66
"strings"
77
"sync"
88

9-
"github.com/bogem/id3v2/v2"
109
"github.com/gosimple/slug"
10+
"github.com/streambinder/id3v2-sylt"
1111
"github.com/streambinder/spotitube/entity"
1212
"github.com/streambinder/spotitube/entity/id3"
1313
)

entity/index/index_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"testing"
99

1010
"github.com/agiledragon/gomonkey/v2"
11-
"github.com/bogem/id3v2/v2"
11+
"github.com/streambinder/id3v2-sylt"
1212
"github.com/streambinder/spotitube/entity"
1313
"github.com/streambinder/spotitube/entity/id3"
1414
"github.com/streambinder/spotitube/util"

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ require (
3434
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
3535
github.com/modern-go/reflect2 v1.0.2 // indirect
3636
github.com/pmezard/go-difflib v1.0.0 // indirect
37+
github.com/streambinder/id3v2-sylt v0.0.0-20250518085742-fbdd8d5d8b8b // indirect
3738
golang.org/x/net v0.38.0 // indirect
3839
golang.org/x/sys v0.31.0 // indirect
39-
golang.org/x/text v0.23.0 // indirect
40+
golang.org/x/text v0.25.0 // indirect
4041
gopkg.in/yaml.v3 v3.0.1 // indirect
4142
)

0 commit comments

Comments
 (0)