From 9c46e2b2622e7ed4fb3a068f92e98efe928fe0dd Mon Sep 17 00:00:00 2001 From: Deluan Date: Sat, 19 Oct 2024 11:55:03 -0400 Subject: [PATCH 1/6] fix: use docker buildx, as required by Linux --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 47e9d9376..46af0edb1 100644 --- a/Makefile +++ b/Makefile @@ -115,7 +115,7 @@ docker-platforms: ##@Cross_Compilation List supported platforms .PHONY: docker-platforms docker-build: ##@Cross_Compilation Cross-compile for any supported platform (check `make docker-platforms`) - docker build \ + docker buildx build \ --platform $(PLATFORMS) \ --build-arg GIT_TAG=${GIT_TAG} \ --build-arg GIT_SHA=${GIT_SHA} \ @@ -127,7 +127,7 @@ docker-image: ##@Cross_Compilation Build Docker image, tagged as `deluan/navidro @echo $(IMAGE_PLATFORMS) | grep -q "windows" && echo "ERROR: Windows is not supported for Docker builds" && exit 1 || true @echo $(IMAGE_PLATFORMS) | grep -q "darwin" && echo "ERROR: macOS is not supported for Docker builds" && exit 1 || true @echo $(IMAGE_PLATFORMS) | grep -q "arm/v5" && echo "ERROR: Linux ARMv5 is not supported for Docker builds" && exit 1 || true - docker build \ + docker buildx build \ --platform $(IMAGE_PLATFORMS) \ --build-arg GIT_TAG=${GIT_TAG} \ --build-arg GIT_SHA=${GIT_SHA} \ From 97c06aba1a49cb51836aa73227c175deafada405 Mon Sep 17 00:00:00 2001 From: Deluan Date: Sat, 19 Oct 2024 20:46:54 -0400 Subject: [PATCH 2/6] perf(server): add index for sort tags. Improves search performance when searching with PreferSortTags=true --- db/migrations/20241020003138_add_sort_tags_index.sql | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 db/migrations/20241020003138_add_sort_tags_index.sql diff --git a/db/migrations/20241020003138_add_sort_tags_index.sql b/db/migrations/20241020003138_add_sort_tags_index.sql new file mode 100644 index 000000000..6d86971e7 --- /dev/null +++ b/db/migrations/20241020003138_add_sort_tags_index.sql @@ -0,0 +1,9 @@ +-- +goose Up +create index if not exists media_file_sort_title on media_file(coalesce(nullif(sort_title,''),order_title)); +create index if not exists album_sort_name on album(coalesce(nullif(sort_album_name,''),order_album_name)); +create index if not exists artist_sort_name on artist(coalesce(nullif(sort_artist_name,''),order_artist_name)); + +-- +goose Down +drop index if exists media_file_sort_title; +drop index if exists album_sort_name; +drop index if exists artist_sort_name; \ No newline at end of file From 28668782c61b85f6f6f08ff7b57f7a8e2c8c08d0 Mon Sep 17 00:00:00 2001 From: Deluan Date: Sun, 20 Oct 2024 13:58:39 -0400 Subject: [PATCH 3/6] fix(server): FFmpegPath can contain spaces --- core/ffmpeg/ffmpeg.go | 23 ++++++++++------------- core/ffmpeg/ffmpeg_test.go | 13 +++++++++++++ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/core/ffmpeg/ffmpeg.go b/core/ffmpeg/ffmpeg.go index 0b8a927a0..712e220af 100644 --- a/core/ffmpeg/ffmpeg.go +++ b/core/ffmpeg/ffmpeg.go @@ -153,31 +153,27 @@ func (j *ffCmd) wait() { // Path will always be an absolute path func createFFmpegCommand(cmd, path string, maxBitRate, offset int) []string { - split := strings.Split(fixCmd(cmd), " ") - var parts []string - - for _, s := range split { + var args []string + for _, s := range fixCmd(cmd) { if strings.Contains(s, "%s") { s = strings.ReplaceAll(s, "%s", path) - parts = append(parts, s) + args = append(args, s) if offset > 0 && !strings.Contains(cmd, "%t") { - parts = append(parts, "-ss", strconv.Itoa(offset)) + args = append(args, "-ss", strconv.Itoa(offset)) } } else { s = strings.ReplaceAll(s, "%t", strconv.Itoa(offset)) s = strings.ReplaceAll(s, "%b", strconv.Itoa(maxBitRate)) - parts = append(parts, s) + args = append(args, s) } } - return parts + return args } func createProbeCommand(cmd string, inputs []string) []string { - split := strings.Split(fixCmd(cmd), " ") var args []string - - for _, s := range split { + for _, s := range fixCmd(cmd) { if s == "%s" { for _, inp := range inputs { args = append(args, "-i", inp) @@ -189,7 +185,7 @@ func createProbeCommand(cmd string, inputs []string) []string { return args } -func fixCmd(cmd string) string { +func fixCmd(cmd string) []string { split := strings.Split(cmd, " ") var result []string cmdPath, _ := ffmpegCmd() @@ -200,7 +196,7 @@ func fixCmd(cmd string) string { result = append(result, s) } } - return strings.Join(result, " ") + return result } func ffmpegCmd() (string, error) { @@ -223,6 +219,7 @@ func ffmpegCmd() (string, error) { return ffmpegPath, ffmpegErr } +// These variables are accessible here for tests. Do not use them directly in production code. Use ffmpegCmd() instead. var ( ffOnce sync.Once ffmpegPath string diff --git a/core/ffmpeg/ffmpeg_test.go b/core/ffmpeg/ffmpeg_test.go index 71d874083..4b8437e2c 100644 --- a/core/ffmpeg/ffmpeg_test.go +++ b/core/ffmpeg/ffmpeg_test.go @@ -48,4 +48,17 @@ var _ = Describe("ffmpeg", func() { Expect(args).To(Equal([]string{"ffmpeg", "-i", "/music library/one.mp3", "-i", "/music library/two.mp3", "-f", "ffmetadata"})) }) }) + + When("ffmpegPath is set", func() { + It("returns the correct ffmpeg path", func() { + ffmpegPath = "/usr/bin/ffmpeg" + args := createProbeCommand(probeCmd, []string{"one.mp3"}) + Expect(args).To(Equal([]string{"/usr/bin/ffmpeg", "-i", "one.mp3", "-f", "ffmetadata"})) + }) + It("returns the correct ffmpeg path with spaces", func() { + ffmpegPath = "/usr/bin/with spaces/ffmpeg.exe" + args := createProbeCommand(probeCmd, []string{"one.mp3"}) + Expect(args).To(Equal([]string{"/usr/bin/with spaces/ffmpeg.exe", "-i", "one.mp3", "-f", "ffmetadata"})) + }) + }) }) From 82633d7490df13b825a01164afe1be4f7d2d0a66 Mon Sep 17 00:00:00 2001 From: Caio Cotts Date: Sun, 20 Oct 2024 14:21:39 -0400 Subject: [PATCH 4/6] fix(playlists): make the m3u parser case-insensitive again #3410 --- core/playlists.go | 4 ++-- core/playlists_test.go | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/core/playlists.go b/core/playlists.go index bc870bd24..5bb3f57af 100644 --- a/core/playlists.go +++ b/core/playlists.go @@ -162,10 +162,10 @@ func (s *playlists) parseM3U(ctx context.Context, pls *model.Playlist, baseDir s } existing := make(map[string]int, len(found)) for idx := range found { - existing[found[idx].Path] = idx + existing[strings.ToLower(found[idx].Path)] = idx } for _, path := range filteredLines { - idx, ok := existing[path] + idx, ok := existing[strings.ToLower(path)] if ok { mfs = append(mfs, found[idx]) } else { diff --git a/core/playlists_test.go b/core/playlists_test.go index ca1ddbfe6..e31dc4610 100644 --- a/core/playlists_test.go +++ b/core/playlists_test.go @@ -142,6 +142,20 @@ var _ = Describe("Playlists", func() { Expect(pls.Tracks[1].Path).To(Equal("test1.mp3")) Expect(pls.Tracks[2].Path).To(Equal("test2.mp3")) }) + + It("is case-insensitive when comparing paths", func() { + repo.data = []string{ + "tEsT1.Mp3", + } + m3u := strings.Join([]string{ + "TeSt1.mP3", + }, "\n") + f := strings.NewReader(m3u) + pls, err := ps.ImportM3U(ctx, f) + Expect(err).ToNot(HaveOccurred()) + Expect(pls.Tracks).To(HaveLen(1)) + Expect(pls.Tracks[0].Path).To(Equal("tEsT1.Mp3")) + }) }) }) From bbb3182bc97605020af5b4a76efc58a3cf2a7b88 Mon Sep 17 00:00:00 2001 From: Deluan Date: Sun, 20 Oct 2024 18:59:47 -0400 Subject: [PATCH 5/6] refactor(server): remove ffmpeg unused code --- core/ffmpeg/ffmpeg.go | 24 +++--------------------- tests/mock_ffmpeg.go | 14 -------------- 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/core/ffmpeg/ffmpeg.go b/core/ffmpeg/ffmpeg.go index 712e220af..4b782f01c 100644 --- a/core/ffmpeg/ffmpeg.go +++ b/core/ffmpeg/ffmpeg.go @@ -18,8 +18,6 @@ import ( type FFmpeg interface { Transcode(ctx context.Context, command, path string, maxBitRate, offset int) (io.ReadCloser, error) ExtractImage(ctx context.Context, path string) (io.ReadCloser, error) - ConvertToWAV(ctx context.Context, path string) (io.ReadCloser, error) - ConvertToFLAC(ctx context.Context, path string) (io.ReadCloser, error) Probe(ctx context.Context, files []string) (string, error) CmdPath() (string, error) IsAvailable() bool @@ -33,8 +31,6 @@ func New() FFmpeg { const ( extractImageCmd = "ffmpeg -i %s -an -vcodec copy -f image2pipe -" probeCmd = "ffmpeg %s -f ffmetadata" - createWavCmd = "ffmpeg -i %s -c:a pcm_s16le -f wav -" - createFLACCmd = "ffmpeg -i %s -f flac -" ) type ffmpeg struct{} @@ -55,16 +51,6 @@ func (e *ffmpeg) ExtractImage(ctx context.Context, path string) (io.ReadCloser, return e.start(ctx, args) } -func (e *ffmpeg) ConvertToWAV(ctx context.Context, path string) (io.ReadCloser, error) { - args := createFFmpegCommand(createWavCmd, path, 0, 0) - return e.start(ctx, args) -} - -func (e *ffmpeg) ConvertToFLAC(ctx context.Context, path string) (io.ReadCloser, error) { - args := createFFmpegCommand(createFLACCmd, path, 0, 0) - return e.start(ctx, args) -} - func (e *ffmpeg) Probe(ctx context.Context, files []string) (string, error) { if _, err := ffmpegCmd(); err != nil { return "", err @@ -167,7 +153,6 @@ func createFFmpegCommand(cmd, path string, maxBitRate, offset int) []string { args = append(args, s) } } - return args } @@ -187,16 +172,13 @@ func createProbeCommand(cmd string, inputs []string) []string { func fixCmd(cmd string) []string { split := strings.Split(cmd, " ") - var result []string cmdPath, _ := ffmpegCmd() - for _, s := range split { + for i, s := range split { if s == "ffmpeg" || s == "ffmpeg.exe" { - result = append(result, cmdPath) - } else { - result = append(result, s) + split[i] = cmdPath } } - return result + return split } func ffmpegCmd() (string, error) { diff --git a/tests/mock_ffmpeg.go b/tests/mock_ffmpeg.go index 2d5ef8ac5..a792ae9d3 100644 --- a/tests/mock_ffmpeg.go +++ b/tests/mock_ffmpeg.go @@ -37,20 +37,6 @@ func (ff *MockFFmpeg) ExtractImage(context.Context, string) (io.ReadCloser, erro return ff, nil } -func (ff *MockFFmpeg) ConvertToFLAC(context.Context, string) (io.ReadCloser, error) { - if ff.Error != nil { - return nil, ff.Error - } - return ff, nil -} - -func (ff *MockFFmpeg) ConvertToWAV(context.Context, string) (io.ReadCloser, error) { - if ff.Error != nil { - return nil, ff.Error - } - return ff, nil -} - func (ff *MockFFmpeg) Probe(context.Context, []string) (string, error) { if ff.Error != nil { return "", ff.Error From 8808eadddaa517ab58ce33a70d08f37b0ffdef0e Mon Sep 17 00:00:00 2001 From: Deluan Date: Sun, 20 Oct 2024 19:09:09 -0400 Subject: [PATCH 6/6] fix(server): allow extra spaces in transcoding commands --- core/ffmpeg/ffmpeg.go | 2 +- core/ffmpeg/ffmpeg_test.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/ffmpeg/ffmpeg.go b/core/ffmpeg/ffmpeg.go index 4b782f01c..62a8e13d5 100644 --- a/core/ffmpeg/ffmpeg.go +++ b/core/ffmpeg/ffmpeg.go @@ -171,7 +171,7 @@ func createProbeCommand(cmd string, inputs []string) []string { } func fixCmd(cmd string) []string { - split := strings.Split(cmd, " ") + split := strings.Fields(cmd) cmdPath, _ := ffmpegCmd() for i, s := range split { if s == "ffmpeg" || s == "ffmpeg.exe" { diff --git a/core/ffmpeg/ffmpeg_test.go b/core/ffmpeg/ffmpeg_test.go index 4b8437e2c..7e67a2a6a 100644 --- a/core/ffmpeg/ffmpeg_test.go +++ b/core/ffmpeg/ffmpeg_test.go @@ -27,6 +27,10 @@ var _ = Describe("ffmpeg", func() { args := createFFmpegCommand("ffmpeg -i %s -b:a %bk mp3 -", "/music library/file.mp3", 123, 0) Expect(args).To(Equal([]string{"ffmpeg", "-i", "/music library/file.mp3", "-b:a", "123k", "mp3", "-"})) }) + It("handles extra spaces in the command string", func() { + args := createFFmpegCommand("ffmpeg -i %s -b:a %bk mp3 -", "/music library/file.mp3", 123, 0) + Expect(args).To(Equal([]string{"ffmpeg", "-i", "/music library/file.mp3", "-b:a", "123k", "mp3", "-"})) + }) Context("when command has time offset param", func() { It("creates a valid command line with offset", func() { args := createFFmpegCommand("ffmpeg -i %s -b:a %bk -ss %t mp3 -", "/music library/file.mp3", 123, 456)