diff --git a/cmus2obs b/cmus2obs index cf0a55a..4e69bac 100755 Binary files a/cmus2obs and b/cmus2obs differ diff --git a/main.go b/main.go index ad9e074..d74e82c 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "image" "log" "os" + "path/filepath" "strings" "time" @@ -34,12 +35,12 @@ func main() { o, _ := c.Output() remoteResp := strings.Split(string(o), "\n") - filepath, err := getAttribute(remoteResp, "file ") + path, err := getAttribute(remoteResp, "file ") if err != nil { - log.Fatal(err.Error()) + log.Fatalln(err.Error()) } - if filepath != prevFilepath { + if path != prevFilepath { //Album album, err := getAttribute(remoteResp, "tag album ") if err != nil { @@ -58,31 +59,50 @@ func main() { // Image img := make([]byte, 0) - if strings.HasSuffix(filepath, ".flac") { - img, err = getFlacArt(filepath) + + switch { + case hasFlacTrackArt(path): + img, err = getFlacArt(path) if err != nil { - img = defaultArt() + img, err = getCoverJpg(path) + if err != nil { + log.Println(err.Error()) + img = getDefaultArt() + } } - } else if strings.HasSuffix(filepath, ".mp3") { - img, err = getMP3Art(filepath) + case hasMp3TrackArt(path): + img, err = getMP3Art(path) if err != nil { - img = defaultArt() + img, err = getCoverJpg(path) + if err != nil { + log.Println(err.Error()) + img = getDefaultArt() + } } - } else { - img = defaultArt() + case hasCoverJpg(path): + img, err = getCoverJpg(path) + if err != nil { + log.Println(err.Error()) + img = getDefaultArt() + } + default: + img = getDefaultArt() } imgBuff := bytes.NewBuffer(img) imgOrig, _, err := image.Decode(imgBuff) if err != nil { - log.Fatal(err.Error()) + log.Panic(err.Error()) } imgOut := image.NewRGBA(image.Rect(0, 0, IMAGE_SIZE, IMAGE_SIZE)) draw.BiLinear.Scale(imgOut, imgOut.Rect, imgOrig, imgOrig.Bounds(), draw.Over, nil) - jpeg.Encode(imgBuff, imgOut, nil) + err = jpeg.Encode(imgBuff, imgOut, nil) + if err != nil { + log.Panic(err.Error()) + } writeTxt("SongAlbum", album) writeTxt("SongArtist", artist) @@ -90,11 +110,87 @@ func main() { writeJpg("AlbumArt", img) } - prevFilepath = filepath + prevFilepath = path time.Sleep(TIMER * time.Second) } } +// has + +func hasFlacTrackArt(s string) bool { + // dumb check + if !strings.HasSuffix(s, ".flac") { + return false + } + + // is parsable + f, err := flac.ParseFile(s) + if err != nil { + return false + } + + // has any frames + if len(f.Meta) == 0 { + return false + } + + // has any pictures + for _, metadata := range f.Meta { + if metadata.Type == flac.Picture { + return true + } + } + + // no pictures + return false +} + +func hasMp3TrackArt(s string) bool { + // dumb check + if !strings.HasSuffix(s, ".mp3") { + return false + } + + // is parsable + m, err := id3v2.Open(s, id3v2.Options{Parse: true}) + if err != nil { + return false + } + defer m.Close() + + // has a picture + pic := m.GetFrames(m.CommonID("Attached picture")) + if pic != nil { + return true + } + + // no pictures + return false +} + +func hasCoverJpg(s string) bool { + exts := []string{".jpg", ".jpeg", ".png", ".gif"} // 'hasCoverJpg' is a misnomer, check all sane image types + dir := filepath.Dir(s) + file, err := os.Open(dir) + if err != nil { + log.Printf("can't open %v; %v\n", dir, err.Error()) + return false + } + defer file.Close() + indexes, _ := file.ReadDir(0) + for i := range indexes { + for _, key := range exts { + if "cover"+key == indexes[i].Name() { + return true + } + } + } + + return false +} + +// get + func getFlacArt(s string) ([]byte, error) { f, err := flac.ParseFile(s) if err != nil { @@ -102,6 +198,7 @@ func getFlacArt(s string) ([]byte, error) { } for _, metadata := range f.Meta { if metadata.Type == flac.Picture { + // do not care if it has multiple pictures, pick the first one. pic, err := flacpicture.ParseFromMetaDataBlock(*metadata) return pic.ImageData, err } @@ -117,12 +214,44 @@ func getMP3Art(s string) ([]byte, error) { defer tags.Close() pic := tags.GetFrames(tags.CommonID("Attached picture")) if pic != nil { - return pic[0].(id3v2.PictureFrame).Picture, nil //i literally do not care if it has multiple pictures, pick the first one. + // do not care if it has multiple pictures, pick the first one. + return pic[0].(id3v2.PictureFrame).Picture, nil } return nil, errors.New("no image found") } -func defaultArt() []byte { +func getCoverJpg(s string) ([]byte, error) { + exts := []string{".jpg", ".jpeg", ".png", ".gif"} + dir := filepath.Dir(s) + file, err := os.Open(dir) + if err != nil { + return nil, fmt.Errorf("can't open %v; %v", dir, err.Error()) + } + defer file.Close() + indexes, _ := file.ReadDir(0) + for i := range indexes { + for _, key := range exts { + if "cover"+key == indexes[i].Name() { + cover, err := os.Open(dir + "/" + indexes[i].Name()) + if err != nil { + return nil, fmt.Errorf("can't open %v", indexes[i].Name()) + } + defer cover.Close() + info, err := indexes[i].Info() + if err != nil { + return nil, fmt.Errorf("cant stat %v; %v", indexes[i].Name(), err.Error()) + } + art := make([]byte, info.Size()) + + cover.Read(art) + return art, nil + } + } + } + return nil, errors.New("cover not found") +} + +func getDefaultArt() []byte { file, err := os.Open("default.jpg") if err != nil { log.Fatal(err.Error()) @@ -138,6 +267,23 @@ func defaultArt() []byte { return art } +func getAttribute(input []string, prefix string) (string, error) { + var attr string + for i := range input { + has := strings.HasPrefix(input[i], prefix) + if has { + attr = input[i] + } + } + attr, b := strings.CutPrefix(attr, prefix) + if !b { + return "", fmt.Errorf("did not find prefix \"%v\"", prefix) + } + return attr, nil +} + +// write + func writeJpg(filename string, input []byte) { os.Mkdir("output", 0755) f, err := os.Create("./output/" + filename + ".jpg") @@ -159,18 +305,3 @@ func writeTxt(filename, input string) { f.Write([]byte(input)) } - -func getAttribute(input []string, prefix string) (string, error) { - var attr string - for i := range input { - has := strings.HasPrefix(input[i], prefix) - if has { - attr = input[i] - } - } - attr, b := strings.CutPrefix(attr, prefix) - if !b { - return "", fmt.Errorf("did not find prefix \"%v\"", prefix) - } - return attr, nil -}