tools: move to cmd subdir
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
// Copyright 2015, David Howden
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
The check tool performs tag lookups on full music collections (iTunes or directory tree of files).
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/dhowden/itl"
|
||||
"github.com/dhowden/tag"
|
||||
)
|
||||
|
||||
var itlXML, path string
|
||||
var sum bool
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&itlXML, "itlXML", "", "iTunes Library Path")
|
||||
flag.StringVar(&path, "path", "", "path to directory containing audio files")
|
||||
flag.BoolVar(&sum, "sum", false, "compute the checksum of the audio file (doesn't work for .flac or .ogg yet)")
|
||||
}
|
||||
|
||||
func decodeLocation(l string) (string, error) {
|
||||
u, err := url.ParseRequestURI(l)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Annoyingly this doesn't replace & (&)
|
||||
path := strings.Replace(u.Path, "&", "&", -1)
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if itlXML == "" && path == "" || itlXML != "" && path != "" {
|
||||
fmt.Println("you must specify one of -itlXML or -path")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var paths <-chan string
|
||||
if itlXML != "" {
|
||||
var err error
|
||||
paths, err = walkLibrary(itlXML)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if path != "" {
|
||||
paths = walkPath(path)
|
||||
}
|
||||
|
||||
p := &processor{
|
||||
decodingErrors: make(map[string]int),
|
||||
hashErrors: make(map[string]int),
|
||||
hashes: make(map[string]int),
|
||||
}
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
p.do(paths)
|
||||
fmt.Println(p)
|
||||
close(done)
|
||||
}()
|
||||
<-done
|
||||
}
|
||||
|
||||
func walkPath(root string) <-chan string {
|
||||
ch := make(chan string)
|
||||
fn := func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
ch <- path
|
||||
return nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := filepath.Walk(root, fn)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func walkLibrary(path string) (<-chan string, error) {
|
||||
f, err := os.Open(itlXML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
l, err := itl.ReadFromXML(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paths := make(chan string)
|
||||
go func() {
|
||||
for _, t := range l.Tracks {
|
||||
loc, err := decodeLocation(t.Location)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
paths <- loc
|
||||
}
|
||||
close(paths)
|
||||
}()
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
type processor struct {
|
||||
decodingErrors map[string]int
|
||||
hashErrors map[string]int
|
||||
hashes map[string]int
|
||||
}
|
||||
|
||||
func (p *processor) String() string {
|
||||
result := ""
|
||||
for k, v := range p.decodingErrors {
|
||||
result += fmt.Sprintf("%v : %v\n", k, v)
|
||||
}
|
||||
|
||||
for k, v := range p.hashErrors {
|
||||
result += fmt.Sprintf("%v : %v\n", k, v)
|
||||
}
|
||||
|
||||
for k, v := range p.hashErrors {
|
||||
if v > 1 {
|
||||
result += fmt.Sprintf("%v : %v\n", k, v)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *processor) do(ch <-chan string) {
|
||||
for path := range ch {
|
||||
func() {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
fmt.Printf("Panicing at: %v", path)
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
tf, err := os.Open(path)
|
||||
if err != nil {
|
||||
p.decodingErrors["error opening file"]++
|
||||
return
|
||||
}
|
||||
defer tf.Close()
|
||||
|
||||
_, _, err = tag.Identify(tf)
|
||||
if err != nil {
|
||||
fmt.Println("IDENTIFY:", path, err.Error())
|
||||
}
|
||||
|
||||
_, err = tag.ReadFrom(tf)
|
||||
if err != nil {
|
||||
fmt.Println("READFROM:", path, err.Error())
|
||||
p.decodingErrors[err.Error()]++
|
||||
}
|
||||
|
||||
if sum {
|
||||
_, err = tf.Seek(0, os.SEEK_SET)
|
||||
if err != nil {
|
||||
fmt.Println("DIED:", path, "error seeking back to 0:", err)
|
||||
return
|
||||
}
|
||||
|
||||
h, err := tag.Sum(tf)
|
||||
if err != nil {
|
||||
fmt.Println("SUM:", path, err.Error())
|
||||
p.hashErrors[err.Error()]++
|
||||
}
|
||||
p.hashes[h]++
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2015, David Howden
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
The sum tool constructs a checksum of a media file exluding any metadata
|
||||
(as recognised by the tag library).
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/dhowden/tag"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Printf("usage: %v filename\n", os.Args[0])
|
||||
return
|
||||
}
|
||||
|
||||
f, err := os.Open(os.Args[1])
|
||||
if err != nil {
|
||||
fmt.Printf("error loading file: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h, err := tag.Sum(f)
|
||||
if err != nil {
|
||||
fmt.Printf("error constructing checksum: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(h)
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
// Copyright 2015, David Howden
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
The tag tool reads metadata from media files (as supported by the tag library).
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/dhowden/tag"
|
||||
"github.com/dhowden/tag/mbz"
|
||||
)
|
||||
|
||||
var raw bool
|
||||
var extractMBZ bool
|
||||
|
||||
var usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s [optional flags] filename\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&raw, "raw", false, "show raw tag data")
|
||||
flag.BoolVar(&extractMBZ, "mbz", false, "extract MusicBrainz tag data (if available)")
|
||||
|
||||
flag.Usage = usage
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
|
||||
f, err := os.Open(flag.Arg(0))
|
||||
if err != nil {
|
||||
fmt.Printf("error loading file: %v", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
m, err := tag.ReadFrom(f)
|
||||
if err != nil {
|
||||
fmt.Printf("error reading file: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
printMetadata(m)
|
||||
|
||||
if raw {
|
||||
fmt.Println()
|
||||
fmt.Println()
|
||||
|
||||
tags := m.Raw()
|
||||
for k, v := range tags {
|
||||
if _, ok := v.(*tag.Picture); ok {
|
||||
fmt.Printf("%#v: %v\n", k, v)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%#v: %#v\n", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
if extractMBZ {
|
||||
b, err := json.MarshalIndent(mbz.Extract(m), "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("error marshalling MusicBrainz info: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\nMusicBrainz Info:\n%v\n", string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func printMetadata(m tag.Metadata) {
|
||||
fmt.Printf("Metadata Format: %v\n", m.Format())
|
||||
fmt.Printf("File Type: %v\n", m.FileType())
|
||||
|
||||
fmt.Printf(" Title: %v\n", m.Title())
|
||||
fmt.Printf(" Album: %v\n", m.Album())
|
||||
fmt.Printf(" Artist: %v\n", m.Artist())
|
||||
fmt.Printf(" Composer: %v\n", m.Composer())
|
||||
fmt.Printf(" Genre: %v\n", m.Genre())
|
||||
fmt.Printf(" Year: %v\n", m.Year())
|
||||
|
||||
track, trackCount := m.Track()
|
||||
fmt.Printf(" Track: %v of %v\n", track, trackCount)
|
||||
|
||||
disc, discCount := m.Disc()
|
||||
fmt.Printf(" Disc: %v of %v\n", disc, discCount)
|
||||
|
||||
fmt.Printf(" Picture: %v\n", m.Picture())
|
||||
fmt.Printf(" Lyrics: %v\n", m.Lyrics())
|
||||
}
|
||||
Reference in New Issue
Block a user