fix(server): optimize playlist import

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2025-02-24 21:14:34 -05:00
parent 0954928b14
commit 19cbd93698
5 changed files with 26 additions and 20 deletions

View File

@ -9,6 +9,7 @@ import (
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"time" "time"
@ -228,31 +229,39 @@ func (s *playlists) normalizePaths(ctx context.Context, pls *model.Playlist, fol
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Normalize paths to be relative to the library root. Search for roots in any library
var res []string // Create regex patterns for each library path
patterns := make([]string, len(libs))
for i, lib := range libs {
cleanPath := filepath.Clean(lib.Path)
escapedPath := regexp.QuoteMeta(cleanPath)
patterns[i] = fmt.Sprintf("^%s(?:/|$)", escapedPath)
}
// Combine all patterns into a single regex
combinedPattern := strings.Join(patterns, "|")
libRegex := regexp.MustCompile(combinedPattern)
res := make([]string, 0, len(lines))
for idx, line := range lines { for idx, line := range lines {
var libPath string var libPath string
var filePath string var filePath string
if folder != nil && !filepath.IsAbs(line) { if folder != nil && !filepath.IsAbs(line) {
libPath = folder.LibraryPath libPath = folder.LibraryPath
filePath = filepath.Join(folder.AbsolutePath(), line) filePath = filepath.Join(folder.AbsolutePath(), line)
} else { } else {
for _, lib := range libs { cleanLine := filepath.Clean(line)
if strings.HasPrefix(line, lib.Path) { if libPath = libRegex.FindString(cleanLine); libPath != "" {
libPath = lib.Path filePath = cleanLine
filePath = line
break
}
} }
} }
if libPath != "" { if libPath != "" {
var err error if rel, err := filepath.Rel(libPath, filePath); err == nil {
filePath, err = filepath.Rel(libPath, filePath) res = append(res, rel)
if err != nil { } else {
log.Trace(ctx, "Error getting relative path", "playlist", pls.Name, "path", line, "folder", folder, err) log.Trace(ctx, "Error getting relative path", "playlist", pls.Name, "path", line, "folder", folder, err)
continue
} }
res = append(res, filePath)
} else { } else {
log.Warn(ctx, "Path in playlist not found in any library", "path", line, "line", idx) log.Warn(ctx, "Path in playlist not found in any library", "path", line, "line", idx)
} }

View File

@ -56,10 +56,9 @@ var _ = Describe("Playlists", func() {
pls, err := ps.ImportFile(ctx, folder, "pls1.m3u") pls, err := ps.ImportFile(ctx, folder, "pls1.m3u")
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(pls.OwnerID).To(Equal("123")) Expect(pls.OwnerID).To(Equal("123"))
Expect(pls.Tracks).To(HaveLen(3)) Expect(pls.Tracks).To(HaveLen(2))
Expect(pls.Tracks[0].Path).To(Equal("tests/fixtures/playlists/test.mp3")) Expect(pls.Tracks[0].Path).To(Equal("tests/fixtures/playlists/test.mp3"))
Expect(pls.Tracks[1].Path).To(Equal("tests/fixtures/playlists/test.ogg")) Expect(pls.Tracks[1].Path).To(Equal("tests/fixtures/playlists/test.ogg"))
Expect(pls.Tracks[2].Path).To(Equal("tests/fixtures/01 Invisible (RED) Edit Version.mp3"))
Expect(mockPlsRepo.last).To(Equal(pls)) Expect(mockPlsRepo.last).To(Equal(pls))
}) })

View File

@ -1,3 +1,2 @@
test.mp3 test.mp3
test.ogg test.ogg
file:///tests/fixtures/01%20Invisible%20(RED)%20Edit%20Version.mp3

View File

@ -2,7 +2,6 @@ package tests
import ( import (
"context" "context"
"io/fs"
"os" "os"
"path/filepath" "path/filepath"
@ -18,7 +17,7 @@ func TempFileName(t testingT, prefix, suffix string) string {
return filepath.Join(t.TempDir(), prefix+id.NewRandom()+suffix) return filepath.Join(t.TempDir(), prefix+id.NewRandom()+suffix)
} }
func TempFile(t testingT, prefix, suffix string) (fs.File, string, error) { func TempFile(t testingT, prefix, suffix string) (*os.File, string, error) {
name := TempFileName(t, prefix, suffix) name := TempFileName(t, prefix, suffix)
f, err := os.Create(name) f, err := os.Create(name)
return f, name, err return f, name, err

View File

@ -140,7 +140,7 @@ var _ = Describe("Slice Utils", func() {
Expect(count).To(Equal(expected)) Expect(count).To(Equal(expected))
}, },
Entry("returns empty slice for an empty input", "tests/fixtures/empty.txt", 0), Entry("returns empty slice for an empty input", "tests/fixtures/empty.txt", 0),
Entry("returns the lines of a file", "tests/fixtures/playlists/pls1.m3u", 3), Entry("returns the lines of a file", "tests/fixtures/playlists/pls1.m3u", 2),
Entry("returns empty if file does not exist", "tests/fixtures/NON-EXISTENT", 0), Entry("returns empty if file does not exist", "tests/fixtures/NON-EXISTENT", 0),
) )