diff --git a/cmdHandlers.go b/cmdHandlers.go index c43f638..a8c9173 100644 --- a/cmdHandlers.go +++ b/cmdHandlers.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "log/slog" + "math" "net/http" "os" "path/filepath" @@ -13,6 +14,7 @@ import ( "song-recognition/spotify" "song-recognition/utils" "song-recognition/wav" + "strconv" "strings" "github.com/fatih/color" @@ -235,3 +237,77 @@ func erase(songsDir string) { fmt.Println("Erase complete") } + +// index processes the path, whether it's a directory or a single file. +func save(path string, force bool) { + fileInfo, err := os.Stat(path) + if err != nil { + fmt.Printf("Error stating path %v: %v\n", path, err) + return + } + + if fileInfo.IsDir() { + err := filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error { + if err != nil { + fmt.Printf("Error walking the path %v: %v\n", filePath, err) + return err + } + // Process only files, skip directories + if !info.IsDir() { + err := saveSong(filePath, force) + if err != nil { + fmt.Printf("Error indexing song (%v): %v\n", filePath, err) + } + } + return nil + }) + if err != nil { + fmt.Printf("Error walking the directory %v: %v\n", path, err) + } + } else { + // If it's a file, process it directly + err := saveSong(path, force) + if err != nil { + fmt.Printf("Error indexing song (%v): %v\n", path, err) + } + } +} + +func saveSong(filePath string, force bool) error { + metadata, err := wav.GetMetadata(filePath) + if err != nil { + return err + } + + durationFloat, err := strconv.ParseFloat(metadata.Format.Duration, 64) + if err != nil { + return fmt.Errorf("failed to parse duration to float: %v", err) + } + + tags := metadata.Format.Tags + track := &spotify.Track{ + Album: tags["album"], + Artist: tags["artist"], + Title: tags["title"], + Duration: int(math.Round(durationFloat)), + } + + ytID, err := spotify.GetYoutubeId(*track) + if err != nil && !force { + return fmt.Errorf("failed to get YouTube ID for song: %v", err) + } + + if track.Title == "" { + return fmt.Errorf("no title found in metadata") + } + if track.Artist == "" { + return fmt.Errorf("no artist found in metadata") + } + + err = spotify.ProcessAndSaveSong(filePath, track.Title, track.Artist, ytID) + if err != nil { + return fmt.Errorf("failed to process or save song: %v", err) + } + + return nil +} diff --git a/main.go b/main.go index e415643..7836995 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,7 @@ func main() { } if len(os.Args) < 2 { - fmt.Println("Expected 'find', 'download', 'erase', or 'serve' subcommands") + fmt.Println("Expected 'find', 'download', 'erase', 'save', or 'serve' subcommands") os.Exit(1) } @@ -57,8 +57,19 @@ func main() { serve(*protocol, *port) case "erase": erase(SONGS_DIR) + case "save": + indexCmd := flag.NewFlagSet("save", flag.ExitOnError) + force := indexCmd.Bool("force", false, "save song with or without YouTube ID") + indexCmd.BoolVar(force, "f", false, "save song with or without YouTube ID (shorthand)") + indexCmd.Parse(os.Args[2:]) + if indexCmd.NArg() < 1 { + fmt.Println("Usage: main.go save [-f|--force] ") + os.Exit(1) + } + filePath := indexCmd.Arg(0) + save(filePath, *force) default: - fmt.Println("Expected 'find', 'download', 'erase', or 'serve' subcommands") + fmt.Println("Expected 'find', 'download', 'erase', 'save', or 'serve' subcommands") os.Exit(1) } }