package process import ( "fmt" "os" "strings" "github.com/akamensky/argparse" "github.com/rs/zerolog/log" "gitlab.com/ChaotiCryptidz/musicutil/types" "gitlab.com/ChaotiCryptidz/musicutil/utils" "gitlab.com/ChaotiCryptidz/musicutil/utils/ascii_reduce" "gitlab.com/ChaotiCryptidz/musicutil/utils/tag_cache" ) type ProcessCommandArgs struct { Source *string DryRun *bool } func RegisterProcessCommand(parser *argparse.Parser) (*argparse.Command, *ProcessCommandArgs) { cmd := parser.NewCommand("process", "") arguments := &ProcessCommandArgs{} arguments.Source = cmd.String("s", "src", &argparse.Options{ Required: true, Validate: func(args []string) error { fileInfo, err := os.Stat(args[0]) if err != nil { return fmt.Errorf("Source does not exist") } if !fileInfo.IsDir() { return fmt.Errorf("Source is not a directory") } return nil }, }) arguments.DryRun = cmd.Flag("", "dry-run", &argparse.Options{}) return cmd, arguments } type ProcessCommand struct { Args *ProcessCommandArgs Files []*types.File TagCache *tag_cache.TagCache } func NewProcessCommand(args *ProcessCommandArgs) *ProcessCommand { return &ProcessCommand{ Args: args, Files: make([]*types.File, 0), } } func (c *ProcessCommand) Run() { log.Info().Str("dir", *c.Args.Source).Msg("Processing") c.TagCache = tag_cache.NewTagCache(*c.Args.Source) defer c.TagCache.Close() c.scan_for_music() c.load_tag_information() c.rename_files() } func (c *ProcessCommand) scan_for_music() { log.Info().Msg("Scanning For Music") files, err := utils.ScanForMusic(*c.Args.Source) if err != nil { panic(err) } c.Files = files } func (c *ProcessCommand) load_tag_information() { log.Info().Msg("Load Tag Information") for _, file := range c.Files { if cached_tags, ok := c.TagCache.IsCached(file); !ok { log.Info().Str("file", file.JoinFilename()).Msg("Loading Tags") tags, err := utils.ExtractTags(file) if err != nil { panic(err) } file.Tags = tags c.TagCache.AddToCache(file, tags) } else { log.Info().Str("file", file.JoinFilename()).Msg("Loading Tags From Cache") file.Tags = cached_tags } } } // returns the new file so if renamed, // any step after it is ran can operate on file // if dry run then returns orig file without changed filename func (c *ProcessCommand) rename_file(file *types.File) *types.File { title := file.Tags.Title artist := file.Tags.Artist replace_char := "_" // Step 1: Remove Newlines title = strings.ReplaceAll(title, "\n", "") artist = strings.ReplaceAll(artist, "\n", "") // Step 2: Strip ASCII title = ascii_reduce.Reduce(title, replace_char) artist = ascii_reduce.Reduce(artist, replace_char) // Step 3: Remove File Seperators title = strings.ReplaceAll(title, "\\", replace_char) title = strings.ReplaceAll(title, "/", replace_char) artist = strings.ReplaceAll(artist, "\\", replace_char) artist = strings.ReplaceAll(artist, "/", replace_char) // Step 4: Join Filename filename := fmt.Sprintf("%s - %s", artist, title) if filename == file.Filename { return file } // Step 5: Make new File with filename set new_file := file.DeepCopy() if !*c.Args.DryRun { new_file.Filename = filename } // Step 6: Rename File log.Info(). Str("old", file.Filename). Str("new", filename). Msg("Renaming File") if !*c.Args.DryRun { if _, err := os.Stat(new_file.JoinPathTo()); err == nil { log.Fatal(). Str("old", file.JoinFilename()). Str("new", new_file.JoinFilename()). Msg("Refusing to rename files, please retag to be distinct") } err := os.Rename(file.JoinPathTo(), new_file.JoinPathTo()) if err != nil { log.Fatal().Err(err).Msg("Failed to rename file") } } return new_file } func (c *ProcessCommand) rename_files() { log.Info().Msg("Renaming Files") for _, file := range c.Files { c.rename_file(file) } }