mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-10 11:27:11 +02:00
add migration, more tests
This commit is contained in:
parent
6e6f7304ed
commit
40d29f2cb1
8 changed files with 261 additions and 16 deletions
77
db/migration/20231209211223_alter_lyric_column.go
Normal file
77
db/migration/20231209211223_alter_lyric_column.go
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/pressly/goose/v3"
|
||||
)
|
||||
|
||||
func init() {
|
||||
goose.AddMigrationContext(upAlterLyricColumn, downAlterLyricColumn)
|
||||
}
|
||||
|
||||
func upAlterLyricColumn(ctx context.Context, tx *sql.Tx) error {
|
||||
_, err := tx.ExecContext(ctx, `alter table media_file rename COLUMN lyrics TO lyrics_old`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, `alter table media_file add lyrics JSONB default '[]';`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`update media_file SET lyrics = ? where id = ?`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := tx.Query(`select id, lyrics_old FROM media_file WHERE lyrics_old <> '';`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var id, lyrics string
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&id, &lyrics)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lyrics, err := model.ToLyrics("xxx", lyrics)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
text, err := json.Marshal(model.Lyrics{*lyrics})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = stmt.Exec(string(text[:]), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, `ALTER TABLE media_file DROP COLUMN lyrics_old;`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notice(tx, "A full rescan will be performed to pick up additional lyrics (existing lyrics have been preserved)")
|
||||
return forceFullRescan(tx)
|
||||
}
|
||||
|
||||
func downAlterLyricColumn(ctx context.Context, tx *sql.Tx) error {
|
||||
// This code is executed when the migration is rolled back.
|
||||
return nil
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/utils"
|
||||
)
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ type Lyric struct {
|
|||
const timeRegexString = `(\[(([0-9]{1,2}):)?([0-9]{1,2}):([0-9]{1,2})(\.([0-9]{1,3}))?\])`
|
||||
|
||||
var (
|
||||
lineRegex = regexp.MustCompile(timeRegexString + "([^\n]+)")
|
||||
lineRegex = regexp.MustCompile(timeRegexString + "([^\n]+)?")
|
||||
lrcIdRegex = regexp.MustCompile(`\[(ar|ti|offset):([^\]]+)\]`)
|
||||
)
|
||||
|
||||
|
|
@ -54,18 +55,18 @@ func ToLyrics(language, text string) (*Lyric, error) {
|
|||
if idTag != nil {
|
||||
switch idTag[1] {
|
||||
case "ar":
|
||||
artist = idTag[2]
|
||||
artist = utils.SanitizeText(strings.TrimSpace(idTag[2]))
|
||||
case "offset":
|
||||
{
|
||||
off, err := strconv.ParseInt(idTag[2], 10, 64)
|
||||
off, err := strconv.ParseInt(strings.TrimSpace(idTag[2]), 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Warn("Error parsing offset", "offset", idTag[2], "error", err)
|
||||
} else {
|
||||
offset = &off
|
||||
}
|
||||
|
||||
offset = &off
|
||||
}
|
||||
case "ti":
|
||||
title = idTag[2]
|
||||
title = utils.SanitizeText(strings.TrimSpace(idTag[2]))
|
||||
}
|
||||
|
||||
continue
|
||||
|
|
@ -74,9 +75,9 @@ func ToLyrics(language, text string) (*Lyric, error) {
|
|||
syncedMatch := lineRegex.FindStringSubmatch(line)
|
||||
if syncedMatch == nil {
|
||||
synced = false
|
||||
text = line
|
||||
text = utils.SanitizeText(line)
|
||||
} else {
|
||||
var hours int64
|
||||
var hours, millis int64
|
||||
var err error
|
||||
|
||||
if syncedMatch[3] != "" {
|
||||
|
|
@ -96,18 +97,20 @@ func ToLyrics(language, text string) (*Lyric, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
millis, err := strconv.ParseInt(syncedMatch[7], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if syncedMatch[7] != "" {
|
||||
millis, err = strconv.ParseInt(syncedMatch[7], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(syncedMatch[7]) == 2 {
|
||||
millis *= 10
|
||||
if len(syncedMatch[7]) == 2 {
|
||||
millis *= 10
|
||||
}
|
||||
}
|
||||
|
||||
timeInMillis := (((((hours * 60) + min) * 60) + sec) * 1000) + millis
|
||||
time = &timeInMillis
|
||||
text = syncedMatch[8]
|
||||
text = utils.SanitizeText(syncedMatch[8])
|
||||
}
|
||||
} else {
|
||||
text = line
|
||||
|
|
|
|||
38
model/lyrics_test.go
Normal file
38
model/lyrics_test.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package model_test
|
||||
|
||||
import (
|
||||
. "github.com/navidrome/navidrome/model"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("ToLyrics", func() {
|
||||
num := int64(1551)
|
||||
|
||||
It("should parse tags with spaces", func() {
|
||||
lyrics, err := ToLyrics("xxx", "[offset: 1551 ]\n[ti: A title ]\n[ar: An artist ]\n[00:00.00]Hi there")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(lyrics.DisplayArtist).To(Equal("An artist"))
|
||||
Expect(lyrics.DisplayTitle).To(Equal("A title"))
|
||||
Expect(lyrics.Offset).To(Equal(&num))
|
||||
})
|
||||
|
||||
It("Should ignore bad offset", func() {
|
||||
lyrics, err := ToLyrics("xxx", "[offset: NotANumber ]\n[00:00.00]Hi there")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(lyrics.Offset).To(BeNil())
|
||||
})
|
||||
|
||||
It("should accept lines with no text and weird times", func() {
|
||||
var a, b, c, d = int64(0), int64(10040), int64(40000), int64(1000 * 60 * 60)
|
||||
lyrics, err := ToLyrics("xxx", "[00:00.00]Hi there\n\n\n[00:10.040] \n[00:40]Test\n[01:00:00]late")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(lyrics.Synced).To(BeTrue())
|
||||
Expect(lyrics.Line).To(Equal([]Line{
|
||||
{Start: &a, Value: "Hi there"},
|
||||
{Start: &b, Value: ""},
|
||||
{Start: &c, Value: "Test"},
|
||||
{Start: &d, Value: "late"},
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"status": "ok",
|
||||
"version": "1.8.0",
|
||||
"type": "navidrome",
|
||||
"serverVersion": "v0.0.0",
|
||||
"openSubsonic": true,
|
||||
"lyricsList": {
|
||||
"structuredLyrics": [
|
||||
{
|
||||
"lang": "eng",
|
||||
"line": [
|
||||
{
|
||||
"start": 18800,
|
||||
"value": "We're no strangers to love"
|
||||
},
|
||||
{
|
||||
"start": 22801,
|
||||
"value": "You know the rules and so do I"
|
||||
}
|
||||
],
|
||||
"synced": true
|
||||
},
|
||||
{
|
||||
"lang": "xxx",
|
||||
"line": [
|
||||
{
|
||||
"value": "We're no strangers to love"
|
||||
},
|
||||
{
|
||||
"value": "You know the rules and so do I"
|
||||
}
|
||||
],
|
||||
"synced": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0" openSubsonic="true">
|
||||
<lyricsList>
|
||||
<structuredLyrics lang="eng" synced="true">
|
||||
<line start="18800">
|
||||
<value>We're no strangers to love</value>
|
||||
</line>
|
||||
<line start="22801">
|
||||
<value>You know the rules and so do I</value>
|
||||
</line>
|
||||
</structuredLyrics>
|
||||
<structuredLyrics lang="xxx" synced="false">
|
||||
<line>
|
||||
<value>We're no strangers to love</value>
|
||||
</line>
|
||||
<line>
|
||||
<value>You know the rules and so do I</value>
|
||||
</line>
|
||||
</structuredLyrics>
|
||||
</lyricsList>
|
||||
</subsonic-response>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"status": "ok",
|
||||
"version": "1.8.0",
|
||||
"type": "navidrome",
|
||||
"serverVersion": "v0.0.0",
|
||||
"openSubsonic": true,
|
||||
"lyricsList": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0" openSubsonic="true">
|
||||
<lyricsList></lyricsList>
|
||||
</subsonic-response>
|
||||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/navidrome/navidrome/consts"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
. "github.com/navidrome/navidrome/server/subsonic/responses"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
|
@ -795,4 +796,62 @@ var _ = Describe("Responses", func() {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("getLyricsBySongId", func() {
|
||||
BeforeEach(func() {
|
||||
response.LyricsList = &LyricsList{}
|
||||
})
|
||||
|
||||
Describe("without data", func() {
|
||||
It("should match .XML", func() {
|
||||
Expect(xml.MarshalIndent(response, "", " ")).To(MatchSnapshot())
|
||||
})
|
||||
It("should match .JSON", func() {
|
||||
Expect(json.MarshalIndent(response, "", " ")).To(MatchSnapshot())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("with data", func() {
|
||||
BeforeEach(func() {
|
||||
times := []int64{int64(18800), int64(22801)}
|
||||
|
||||
response.LyricsList.StructuredLyrics = model.Lyrics{
|
||||
{
|
||||
Lang: "eng",
|
||||
Synced: true,
|
||||
Line: []model.Line{
|
||||
{
|
||||
Start: ×[0],
|
||||
Value: "We're no strangers to love",
|
||||
},
|
||||
{
|
||||
Start: ×[1],
|
||||
Value: "You know the rules and so do I",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Lang: "xxx",
|
||||
Synced: false,
|
||||
Line: []model.Line{
|
||||
{
|
||||
Value: "We're no strangers to love",
|
||||
},
|
||||
{
|
||||
Value: "You know the rules and so do I",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
It("should match .XML", func() {
|
||||
Expect(xml.MarshalIndent(response, "", " ")).To(MatchSnapshot())
|
||||
})
|
||||
It("should match .JSON", func() {
|
||||
Expect(json.MarshalIndent(response, "", " ")).To(MatchSnapshot())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue