diff --git a/go.mod b/go.mod index 4a401c2..5f7bb4b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module butterfliu go 1.23.5 require ( - github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 github.com/go-chi/chi/v5 v5.2.3 github.com/mattn/go-sqlite3 v1.14.32 ) diff --git a/go.sum b/go.sum index 3d7fb2d..dbec922 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 h1:OtSeLS5y0Uy01jaKK4mA/WVIYtpzVm63vLVAPzJXigg= -github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8/go.mod h1:apkPC/CR3s48O2D7Y++n1XWEpgPNNCjXYga3PPbJe2E= github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= diff --git a/internal/scanner/scanner.go b/internal/scanner/scanner.go index 3da7a83..95ec942 100644 --- a/internal/scanner/scanner.go +++ b/internal/scanner/scanner.go @@ -1,12 +1,14 @@ package scanner import ( + "encoding/json" "fmt" "os" + "os/exec" "path/filepath" + "slices" + "strconv" "strings" - - "github.com/dhowden/tag" ) type ScannedSong struct { @@ -17,13 +19,28 @@ type ScannedSong struct { Path string } +// ffprobeFormat holds the JSON output from ffprobe format section +type ffprobeFormat struct { + Tags map[string]string `json:"tags"` + Duration string `json:"duration"` +} + +// ffprobeOutput holds the full ffprobe JSON output +type ffprobeOutput struct { + Format ffprobeFormat `json:"format"` +} + func ScanDirectory(dirPath string) ([]ScannedSong, error) { songs := []ScannedSong{} - if _, err := os.Stat(dirPath); os.IsNotExist(err) { - return songs, err + if _, err := exec.LookPath("ffprobe"); err != nil { + return songs, fmt.Errorf("ffprobe not found in PATH: %v", err) } - filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { + + err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } if info.IsDir() { return nil @@ -34,48 +51,61 @@ func ScanDirectory(dirPath string) ([]ScannedSong, error) { } if song, err := processAudioFile(path); err != nil { - return err + // Log error but continue scanning other files + return nil } else { songs = append(songs, song) } return nil }) - return songs, nil + return songs, err } func processAudioFile(filePath string) (ScannedSong, error) { - file, err := os.Open(filePath) + cmd := exec.Command("ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", filePath) + output, err := cmd.Output() if err != nil { - return ScannedSong{}, fmt.Errorf("failed to open file: %v", err) - } - defer file.Close() - - metadata, err := tag.ReadFrom(file) - if err != nil { - return ScannedSong{}, fmt.Errorf("failed to read metadata: %v", err) + return ScannedSong{}, fmt.Errorf("ffprobe failed for %s: %v", filePath, err) } - title := metadata.Title() + var probeOutput ffprobeOutput + if err := json.Unmarshal(output, &probeOutput); err != nil { + return ScannedSong{}, fmt.Errorf("failed to parse ffprobe output for %s: %v", filePath, err) + } + + tags := probeOutput.Format.Tags + if tags == nil { + tags = make(map[string]string) + } + + title := tags["title"] if title == "" { title = filepath.Base(filePath) } - artist := metadata.Artist() + artist := tags["artist"] if artist == "" { artist = "Unknown Artist" } - album := metadata.Album() + album := tags["album"] if album == "" { album = "Unknown Album" } + duration := 0 + if probeOutput.Format.Duration != "" { + if d, err := strconv.ParseFloat(probeOutput.Format.Duration, 64); err == nil { + duration = int(d) + } + } + return ScannedSong{ Title: title, Artist: artist, Album: album, - Duration: 100, + Duration: duration, Path: filePath, }, nil } @@ -83,11 +113,5 @@ func processAudioFile(filePath string) (ScannedSong, error) { func isAudioFile(path string) bool { ext := strings.ToLower(filepath.Ext(path)) audioExtensions := []string{".mp3", ".flac", ".m4a", ".wav", ".ogg"} - - for _, audioExt := range audioExtensions { - if ext == audioExt { - return true - } - } - return false + return slices.Contains(audioExtensions, ext) } diff --git a/web/src/views/AboutView.vue b/web/src/views/AboutView.vue index 7da2d5f..9819ed5 100644 --- a/web/src/views/AboutView.vue +++ b/web/src/views/AboutView.vue @@ -64,7 +64,7 @@

后端

-

Go 1.23 · chi 路由 · SQLite · dhowden/tag

+

Go 1.23 · chi 路由 · SQLite · ffprobe

前端