musicutil/commands/process/process.go

166 lines
3.8 KiB
Go
Raw Normal View History

2022-02-22 14:02:58 +00:00
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)
}
}