mirror of
https://github.com/Luzifer/id3patch.git
synced 2024-11-09 16:00:11 +00:00
142 lines
3.4 KiB
Go
142 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
|
|
"github.com/Luzifer/rconfig/v2"
|
|
"github.com/bogem/id3v2"
|
|
"github.com/pkg/errors"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var (
|
|
cfg = struct {
|
|
Album string `flag:"album" default:"" description:"Set album tag"`
|
|
Artist string `flag:"artist" default:"" description:"Set artist tag"`
|
|
File string `flag:"file,f" default:"" description:"File to read / write" validate:"nonzero"`
|
|
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
|
|
Title string `flag:"title" default:"" description:"Set title tag"`
|
|
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
|
|
Year string `flag:"year" default:"" description:"Set year tag"`
|
|
}{}
|
|
|
|
version = "dev"
|
|
)
|
|
|
|
func init() {
|
|
rconfig.AutoEnv(true)
|
|
if err := rconfig.ParseAndValidate(&cfg); err != nil {
|
|
log.Fatalf("Unable to parse commandline options: %s", err)
|
|
}
|
|
|
|
if cfg.VersionAndExit {
|
|
fmt.Printf("id3patch %s\n", version)
|
|
os.Exit(0)
|
|
}
|
|
|
|
if l, err := log.ParseLevel(cfg.LogLevel); err != nil {
|
|
log.WithError(err).Fatal("Unable to parse log level")
|
|
} else {
|
|
log.SetLevel(l)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
var (
|
|
logger = log.WithField("file", cfg.File)
|
|
needsWrite bool
|
|
)
|
|
|
|
tag, err := id3v2.Open(cfg.File, id3v2.Options{Parse: true})
|
|
switch err {
|
|
|
|
case nil:
|
|
// Everything fine
|
|
|
|
case id3v2.ErrUnsupportedVersion:
|
|
logger.Warn("No supported ID3v2 tags found")
|
|
|
|
default:
|
|
logger.WithError(err).Fatal("Unable to open file")
|
|
|
|
}
|
|
defer tag.Close()
|
|
|
|
logger.WithFields(log.Fields{
|
|
"album": tag.Album(),
|
|
"artist": tag.Artist(),
|
|
"title": tag.Title(),
|
|
"tag_version": tag.Version(),
|
|
"year": tag.Year(),
|
|
}).Info("File opened successfully")
|
|
|
|
needsWrite = modTag(cfg.Album, tag.Album, tag.SetAlbum, needsWrite)
|
|
needsWrite = modTag(cfg.Artist, tag.Artist, tag.SetArtist, needsWrite)
|
|
needsWrite = modTag(cfg.Title, tag.Title, tag.SetTitle, needsWrite)
|
|
needsWrite = modTag(cfg.Year, tag.Year, tag.SetYear, needsWrite)
|
|
|
|
if !needsWrite {
|
|
logger.Info("No tags changed, no write needed")
|
|
return
|
|
}
|
|
|
|
if err := save(tag); err != nil {
|
|
logger.WithError(err).Fatal("Unable to save tags")
|
|
}
|
|
|
|
logger.Info("Tags written successfully")
|
|
}
|
|
|
|
func modTag(content string, contentFunc func() string, setFunc func(string), needsWrite bool) bool {
|
|
if content == "" || content == contentFunc() {
|
|
return needsWrite
|
|
}
|
|
|
|
setFunc(content)
|
|
return true
|
|
}
|
|
|
|
func save(tag *id3v2.Tag) error {
|
|
var err = tag.Save()
|
|
switch err {
|
|
|
|
case id3v2.ErrNoFile:
|
|
// We need to do the save ourselves
|
|
|
|
default:
|
|
return err
|
|
|
|
}
|
|
|
|
// Library does not supporting initial tag addition, assemble the
|
|
// newly added tag ourselves...
|
|
var buf = new(bytes.Buffer)
|
|
|
|
oStat, err := os.Stat(cfg.File)
|
|
if err != nil {
|
|
return errors.Wrap(err, "Unable to determine original file stats")
|
|
}
|
|
|
|
oFile, err := os.Open(cfg.File)
|
|
if err != nil {
|
|
return errors.Wrap(err, "Unable to open original file")
|
|
}
|
|
|
|
if _, err = tag.WriteTo(buf); err != nil {
|
|
return errors.Wrap(err, "Unable to write tag header to buffer")
|
|
}
|
|
|
|
if _, err = io.Copy(buf, oFile); err != nil {
|
|
return errors.Wrap(err, "Unable to copy original file contents to buffer")
|
|
}
|
|
|
|
if err = oFile.Close(); err != nil {
|
|
return errors.Wrap(err, "Unable to close original file")
|
|
}
|
|
|
|
return errors.Wrap(ioutil.WriteFile(cfg.File, buf.Bytes(), oStat.Mode()), "Unable to write file contents")
|
|
}
|