mirror of
https://github.com/navidrome/navidrome.git
synced 2026-05-03 06:51:16 +00:00
Merge branch 'master' into fix/UppercaseRepoName
This commit is contained in:
commit
5c1a21da24
4
Makefile
4
Makefile
@ -115,7 +115,7 @@ docker-platforms: ##@Cross_Compilation List supported platforms
|
|||||||
.PHONY: docker-platforms
|
.PHONY: docker-platforms
|
||||||
|
|
||||||
docker-build: ##@Cross_Compilation Cross-compile for any supported platform (check `make docker-platforms`)
|
docker-build: ##@Cross_Compilation Cross-compile for any supported platform (check `make docker-platforms`)
|
||||||
docker build \
|
docker buildx build \
|
||||||
--platform $(PLATFORMS) \
|
--platform $(PLATFORMS) \
|
||||||
--build-arg GIT_TAG=${GIT_TAG} \
|
--build-arg GIT_TAG=${GIT_TAG} \
|
||||||
--build-arg GIT_SHA=${GIT_SHA} \
|
--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 "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 "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
|
@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) \
|
--platform $(IMAGE_PLATFORMS) \
|
||||||
--build-arg GIT_TAG=${GIT_TAG} \
|
--build-arg GIT_TAG=${GIT_TAG} \
|
||||||
--build-arg GIT_SHA=${GIT_SHA} \
|
--build-arg GIT_SHA=${GIT_SHA} \
|
||||||
|
|||||||
@ -18,8 +18,6 @@ import (
|
|||||||
type FFmpeg interface {
|
type FFmpeg interface {
|
||||||
Transcode(ctx context.Context, command, path string, maxBitRate, offset int) (io.ReadCloser, error)
|
Transcode(ctx context.Context, command, path string, maxBitRate, offset int) (io.ReadCloser, error)
|
||||||
ExtractImage(ctx context.Context, path string) (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)
|
Probe(ctx context.Context, files []string) (string, error)
|
||||||
CmdPath() (string, error)
|
CmdPath() (string, error)
|
||||||
IsAvailable() bool
|
IsAvailable() bool
|
||||||
@ -33,8 +31,6 @@ func New() FFmpeg {
|
|||||||
const (
|
const (
|
||||||
extractImageCmd = "ffmpeg -i %s -an -vcodec copy -f image2pipe -"
|
extractImageCmd = "ffmpeg -i %s -an -vcodec copy -f image2pipe -"
|
||||||
probeCmd = "ffmpeg %s -f ffmetadata"
|
probeCmd = "ffmpeg %s -f ffmetadata"
|
||||||
createWavCmd = "ffmpeg -i %s -c:a pcm_s16le -f wav -"
|
|
||||||
createFLACCmd = "ffmpeg -i %s -f flac -"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ffmpeg struct{}
|
type ffmpeg struct{}
|
||||||
@ -55,16 +51,6 @@ func (e *ffmpeg) ExtractImage(ctx context.Context, path string) (io.ReadCloser,
|
|||||||
return e.start(ctx, args)
|
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) {
|
func (e *ffmpeg) Probe(ctx context.Context, files []string) (string, error) {
|
||||||
if _, err := ffmpegCmd(); err != nil {
|
if _, err := ffmpegCmd(); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -153,31 +139,26 @@ func (j *ffCmd) wait() {
|
|||||||
|
|
||||||
// Path will always be an absolute path
|
// Path will always be an absolute path
|
||||||
func createFFmpegCommand(cmd, path string, maxBitRate, offset int) []string {
|
func createFFmpegCommand(cmd, path string, maxBitRate, offset int) []string {
|
||||||
split := strings.Split(fixCmd(cmd), " ")
|
var args []string
|
||||||
var parts []string
|
for _, s := range fixCmd(cmd) {
|
||||||
|
|
||||||
for _, s := range split {
|
|
||||||
if strings.Contains(s, "%s") {
|
if strings.Contains(s, "%s") {
|
||||||
s = strings.ReplaceAll(s, "%s", path)
|
s = strings.ReplaceAll(s, "%s", path)
|
||||||
parts = append(parts, s)
|
args = append(args, s)
|
||||||
if offset > 0 && !strings.Contains(cmd, "%t") {
|
if offset > 0 && !strings.Contains(cmd, "%t") {
|
||||||
parts = append(parts, "-ss", strconv.Itoa(offset))
|
args = append(args, "-ss", strconv.Itoa(offset))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s = strings.ReplaceAll(s, "%t", strconv.Itoa(offset))
|
s = strings.ReplaceAll(s, "%t", strconv.Itoa(offset))
|
||||||
s = strings.ReplaceAll(s, "%b", strconv.Itoa(maxBitRate))
|
s = strings.ReplaceAll(s, "%b", strconv.Itoa(maxBitRate))
|
||||||
parts = append(parts, s)
|
args = append(args, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return args
|
||||||
return parts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createProbeCommand(cmd string, inputs []string) []string {
|
func createProbeCommand(cmd string, inputs []string) []string {
|
||||||
split := strings.Split(fixCmd(cmd), " ")
|
|
||||||
var args []string
|
var args []string
|
||||||
|
for _, s := range fixCmd(cmd) {
|
||||||
for _, s := range split {
|
|
||||||
if s == "%s" {
|
if s == "%s" {
|
||||||
for _, inp := range inputs {
|
for _, inp := range inputs {
|
||||||
args = append(args, "-i", inp)
|
args = append(args, "-i", inp)
|
||||||
@ -189,18 +170,15 @@ func createProbeCommand(cmd string, inputs []string) []string {
|
|||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixCmd(cmd string) string {
|
func fixCmd(cmd string) []string {
|
||||||
split := strings.Split(cmd, " ")
|
split := strings.Fields(cmd)
|
||||||
var result []string
|
|
||||||
cmdPath, _ := ffmpegCmd()
|
cmdPath, _ := ffmpegCmd()
|
||||||
for _, s := range split {
|
for i, s := range split {
|
||||||
if s == "ffmpeg" || s == "ffmpeg.exe" {
|
if s == "ffmpeg" || s == "ffmpeg.exe" {
|
||||||
result = append(result, cmdPath)
|
split[i] = cmdPath
|
||||||
} else {
|
|
||||||
result = append(result, s)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return strings.Join(result, " ")
|
return split
|
||||||
}
|
}
|
||||||
|
|
||||||
func ffmpegCmd() (string, error) {
|
func ffmpegCmd() (string, error) {
|
||||||
@ -223,6 +201,7 @@ func ffmpegCmd() (string, error) {
|
|||||||
return ffmpegPath, ffmpegErr
|
return ffmpegPath, ffmpegErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These variables are accessible here for tests. Do not use them directly in production code. Use ffmpegCmd() instead.
|
||||||
var (
|
var (
|
||||||
ffOnce sync.Once
|
ffOnce sync.Once
|
||||||
ffmpegPath string
|
ffmpegPath string
|
||||||
|
|||||||
@ -27,6 +27,10 @@ var _ = Describe("ffmpeg", func() {
|
|||||||
args := createFFmpegCommand("ffmpeg -i %s -b:a %bk mp3 -", "/music library/file.mp3", 123, 0)
|
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", "-"}))
|
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() {
|
Context("when command has time offset param", func() {
|
||||||
It("creates a valid command line with offset", 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)
|
args := createFFmpegCommand("ffmpeg -i %s -b:a %bk -ss %t mp3 -", "/music library/file.mp3", 123, 456)
|
||||||
@ -48,4 +52,17 @@ var _ = Describe("ffmpeg", func() {
|
|||||||
Expect(args).To(Equal([]string{"ffmpeg", "-i", "/music library/one.mp3", "-i", "/music library/two.mp3", "-f", "ffmetadata"}))
|
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"}))
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -162,10 +162,10 @@ func (s *playlists) parseM3U(ctx context.Context, pls *model.Playlist, baseDir s
|
|||||||
}
|
}
|
||||||
existing := make(map[string]int, len(found))
|
existing := make(map[string]int, len(found))
|
||||||
for idx := range found {
|
for idx := range found {
|
||||||
existing[found[idx].Path] = idx
|
existing[strings.ToLower(found[idx].Path)] = idx
|
||||||
}
|
}
|
||||||
for _, path := range filteredLines {
|
for _, path := range filteredLines {
|
||||||
idx, ok := existing[path]
|
idx, ok := existing[strings.ToLower(path)]
|
||||||
if ok {
|
if ok {
|
||||||
mfs = append(mfs, found[idx])
|
mfs = append(mfs, found[idx])
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -142,6 +142,20 @@ var _ = Describe("Playlists", func() {
|
|||||||
Expect(pls.Tracks[1].Path).To(Equal("test1.mp3"))
|
Expect(pls.Tracks[1].Path).To(Equal("test1.mp3"))
|
||||||
Expect(pls.Tracks[2].Path).To(Equal("test2.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"))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
9
db/migrations/20241020003138_add_sort_tags_index.sql
Normal file
9
db/migrations/20241020003138_add_sort_tags_index.sql
Normal file
@ -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;
|
||||||
@ -37,20 +37,6 @@ func (ff *MockFFmpeg) ExtractImage(context.Context, string) (io.ReadCloser, erro
|
|||||||
return ff, nil
|
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) {
|
func (ff *MockFFmpeg) Probe(context.Context, []string) (string, error) {
|
||||||
if ff.Error != nil {
|
if ff.Error != nil {
|
||||||
return "", ff.Error
|
return "", ff.Error
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user