package ripsort import ( "fmt" "io" "os" "path/filepath" "regexp" "strings" ) func sortSong(src, dst string) error { m, err := ReadAudioTags(src) if err != nil { return err } dstPath := pathForFile(src, *m) finalPath := filepath.Join(dst, dstPath) err = copyFile(src, finalPath) if err != nil { return err } return nil } func sanitizeName(name string) *string { if name == "" { return nil } re := regexp.MustCompile(`[<>:"/\\|?*\x00-\x1F]`) name = re.ReplaceAllString(name, "_") name = strings.Trim(name, " .") if name == "" { return nil } return &name } func getArtistName(m metadata) string { var artist *string if len(m.Artist) > 0 { artist = sanitizeName(m.Artist[0]) } if artist == nil && len(m.AlbumArtist) > 0 { if aa := m.AlbumArtist[0]; aa != "" { artist = sanitizeName(aa) } } if artist == nil { return "Unknown Artist" } return *artist } func getAlbumName(m metadata) string { album := sanitizeName(m.Album) if album == nil { return "Unknown Album" } return *album } func getTitle(src string, m metadata) string { title := sanitizeName(m.Title) if title == nil { return *sanitizeName(strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))) } return *title } func pathForFile(src string, m metadata) string { ext := strings.ToLower(filepath.Ext(src)) artist := getArtistName(m) album := getAlbumName(m) title := getTitle(src, m) filename := title + ext return filepath.Join(artist, album, filename) } func copyFile(src, dst string) error { in, err := os.Open(src) if err != nil { return fmt.Errorf("open source file: %w", err) } defer in.Close() dir := filepath.Dir(dst) if err := os.MkdirAll(dir, 0o755); err != nil { return fmt.Errorf("create destination directory: %w", err) } out, err := os.Create(dst) if err != nil { return fmt.Errorf("create destination file: %w", err) } defer func() { if cerr := out.Close(); err == nil && cerr != nil { err = cerr } }() if _, err = io.Copy(out, in); err != nil { return fmt.Errorf("copy file contents: %w", err) } if err = out.Sync(); err != nil { return fmt.Errorf("sync destination file: %w", err) } return nil }