Skip to content

Commit

Permalink
Migrate to go-flac v2 (go-flac/go-flac#5)
Browse files Browse the repository at this point in the history
Signed-off-by: eternal-flame-AD <[email protected]>
  • Loading branch information
eternal-flame-AD committed Aug 2, 2024
1 parent a593e72 commit e2254dd
Show file tree
Hide file tree
Showing 7 changed files with 316 additions and 0 deletions.
38 changes: 38 additions & 0 deletions v2/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package flacvorbis

const (
APP_VERSION = "0.1.0"
)

const (
// FIELD_TITLE Track/Work name
FIELD_TITLE = "TITLE"
// FIELD_VERSION The version field may be used to differentiate multiple versions of the same track title in a single collection. (e.g. remix info)
FIELD_VERSION = "VERSION"
// FIELD_ALBUM The collection name to which this track belongs
FIELD_ALBUM = "ALBUM"
// FIELD_TRACKNUMBER The track number of this piece if part of a specific larger collection or album
FIELD_TRACKNUMBER = "TRACKNUMBER"
// FIELD_ARTIST The artist generally considered responsible for the work. In popular music this is usually the performing band or singer. For classical music it would be the composer. For an audio book it would be the author of the original text.
FIELD_ARTIST = "ARTIST"
// FIELD_PERFORMER The artist(s) who performed the work. In classical music this would be the conductor, orchestra, soloists. In an audio book it would be the actor who did the reading. In popular music this is typically the same as the ARTIST and is omitted.
FIELD_PERFORMER = "PERFORMER"
// FIELD_COPYRIGHT Copyright attribution, e.g., '2001 Nobody's Band' or '1999 Jack Moffitt'
FIELD_COPYRIGHT = "COPYRIGHT"
// FIELD_LICENSE License information, eg, 'All Rights Reserved', 'Any Use Permitted', a URL to a license such as a Creative Commons license ("www.creativecommons.org/blahblah/license.html") or the EFF Open Audio License ('distributed under the terms of the Open Audio License. see http://www.eff.org/IP/Open_licenses/eff_oal.html for details'), etc.
FIELD_LICENSE = "LICENSE"
// FIELD_ORGANIZATION Name of the organization producing the track (i.e. the 'record label')
FIELD_ORGANIZATION = "ORGANIZATION"
// FIELD_DESCRIPTION A short text description of the contents
FIELD_DESCRIPTION = "DESCRIPTION"
// FIELD_GENRE A short text indication of music genre
FIELD_GENRE = "GENRE"
// FIELD_DATE Date the track was recorded
FIELD_DATE = "DATE"
// FIELD_LOCATION Location where track was recorded
FIELD_LOCATION = "LOCATION"
// FIELD_CONTACT Contact information for the creators or distributors of the track. This could be a URL, an email address, the physical address of the producing label.
FIELD_CONTACT = "CONTACT"
// FIELD_ISRC ISRC number for the track; see the ISRC intro page for more information on ISRC numbers.
FIELD_ISRC = "ISRC"
)
10 changes: 10 additions & 0 deletions v2/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package flacvorbis

import "errors"

var (
ErrorNotVorbisComment = errors.New("Not a vorbis comment metadata block")
ErrorUnexpEof = errors.New("Unexpected end of stream")
ErrorMalformedComment = errors.New("Malformed comment")
ErrorInvalidFieldName = errors.New("Malformed Field Name")
)
5 changes: 5 additions & 0 deletions v2/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/go-flac/flacvorbis

go 1.20

require github.com/go-flac/go-flac/v2 v2.0.1
2 changes: 2 additions & 0 deletions v2/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/go-flac/go-flac/v2 v2.0.1 h1:1zilNkbmmpK9DLsz2NbjLHG8avOmthYqUfVc9YKB/Ps=
github.com/go-flac/go-flac/v2 v2.0.1/go.mod h1:hvgeR2hElLbwk0Q1/vMazIDmIc2LAFSd9Bx/Fk6ViKo=
26 changes: 26 additions & 0 deletions v2/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package flacvorbis

import (
"bytes"
"encoding/binary"
"io"
)

func encodeUint32(n uint32) []byte {
buf := bytes.NewBuffer([]byte{})
if err := binary.Write(buf, binary.LittleEndian, n); err != nil {
panic(err)
}
return buf.Bytes()
}

func readUint32(r io.Reader) (res uint32, err error) {
err = binary.Read(r, binary.LittleEndian, &res)
return
}

func packStr(w io.Writer, s string) {
data := []byte(s)
w.Write(encodeUint32(uint32(len(data))))
w.Write(data)
}
109 changes: 109 additions & 0 deletions v2/vorbis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package flacvorbis

import (
"bytes"
"strings"

flac "github.com/go-flac/go-flac/v2"
)

type MetaDataBlockVorbisComment struct {
Vendor string
Comments []string
}

// New creates a new MetaDataBlockVorbisComment
// vendor is set to flacvorbis <version> by default
func New() *MetaDataBlockVorbisComment {
return &MetaDataBlockVorbisComment{
"flacvorbis " + APP_VERSION,
[]string{},
}
}

// Get get all comments with field name specified by the key parameter
// If there is no match, error would still be nil
func (c *MetaDataBlockVorbisComment) Get(key string) ([]string, error) {
res := make([]string, 0)
for _, cmt := range c.Comments {
p := strings.SplitN(cmt, "=", 2)
if len(p) != 2 {
return nil, ErrorMalformedComment
}
if strings.EqualFold(p[0], key) {
res = append(res, p[1])
}
}
return res, nil
}

// Add adds a key-val pair to the comments
func (c *MetaDataBlockVorbisComment) Add(key string, val string) error {
for _, char := range key {
if char < 0x20 || char > 0x7d || char == '=' {
return ErrorInvalidFieldName
}
}
c.Comments = append(c.Comments, key+"="+val)
return nil
}

// Marshal marshals this block back into a flac.MetaDataBlock
func (c MetaDataBlockVorbisComment) Marshal() flac.MetaDataBlock {
data := bytes.NewBuffer([]byte{})
packStr(data, c.Vendor)
data.Write(encodeUint32(uint32(len(c.Comments))))
for _, cmt := range c.Comments {
packStr(data, cmt)
}
return flac.MetaDataBlock{
Type: flac.VorbisComment,
Data: data.Bytes(),
}
}

// ParseFromMetaDataBlock parses an existing picture MetaDataBlock
func ParseFromMetaDataBlock(meta flac.MetaDataBlock) (*MetaDataBlockVorbisComment, error) {
if meta.Type != flac.VorbisComment {
return nil, ErrorNotVorbisComment
}

reader := bytes.NewReader(meta.Data)
res := new(MetaDataBlockVorbisComment)

vendorlen, err := readUint32(reader)
if err != nil {
return nil, err
}
vendorbytes := make([]byte, vendorlen)
nn, err := reader.Read(vendorbytes)
if err != nil {
return nil, err
}
if nn != int(vendorlen) {
return nil, ErrorUnexpEof
}
res.Vendor = string(vendorbytes)

cmtcount, err := readUint32(reader)
if err != nil {
return nil, err
}
res.Comments = make([]string, cmtcount)
for i := range res.Comments {
cmtlen, err := readUint32(reader)
if err != nil {
return nil, err
}
cmtbytes := make([]byte, cmtlen)
nn, err := reader.Read(cmtbytes)
if err != nil {
return nil, err
}
if nn != int(cmtlen) {
return nil, ErrorUnexpEof
}
res.Comments[i] = string(cmtbytes)
}
return res, nil
}
126 changes: 126 additions & 0 deletions v2/vorbis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package flacvorbis

import (
"archive/zip"
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"

flac "github.com/go-flac/go-flac/v2"
)

func httpGetBytes(url string) ([]byte, error) {
res, err := http.Get(url)
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("HTTP status %d", res.StatusCode)
}
return ioutil.ReadAll(res.Body)
}

func TestNewVorbisComment(t *testing.T) {
cmt := New()
if !strings.HasPrefix(cmt.Vendor, "flacvorbis") {
t.Errorf("Unexpected vendor: %s\n", cmt.Vendor)
t.Fail()
}
if len(cmt.Comments) != 0 {
t.Error("Unexpected comments: ", cmt.Comments)
t.Fail()
}
}
func TestVorbisFromExistingFlac(t *testing.T) {
zipdata, err := httpGetBytes("http://helpguide.sony.net/high-res/sample1/v1/data/Sample_BeeMoved_96kHz24bit.flac.zip")
if err != nil {
t.Errorf("Error while downloading test file: %s", err.Error())
t.FailNow()
}
zipfile, err := zip.NewReader(bytes.NewReader(zipdata), int64(len(zipdata)))
if err != nil {
t.Errorf("Error while decompressing test file: %s", err.Error())
t.FailNow()
}
if zipfile.File[0].Name != "Sample_BeeMoved_96kHz24bit.flac" {
t.Errorf("Unexpected test file content: %s", zipfile.File[0].Name)
t.FailNow()
}

flachandle, err := zipfile.File[0].Open()
if err != nil {
t.Errorf("Failed to decompress test file: %s", err)
t.FailNow()
}

f, err := flac.ParseBytes(flachandle)
if err != nil {
t.Errorf("Failed to parse flac file: %s", err)
t.FailNow()
}

var cmt *MetaDataBlockVorbisComment
for _, meta := range f.Meta {
if meta.Type == flac.VorbisComment {
cmt, err = ParseFromMetaDataBlock(*meta)
if err != nil {
t.Errorf("Error while parsing metadata image: %s\n", err.Error())
t.Fail()
}
}
}

if err := cmt.Add(FIELD_GENRE, "Bee Pop"); err != nil {
t.Error(err)
t.Fail()
}

check := func(cmt *MetaDataBlockVorbisComment) {
if cmt.Vendor != "reference libFLAC 1.2.1 win64 20080709" {
t.Errorf("Unexpected vendor string: %s\n", cmt.Vendor)
t.Fail()
}
if res, err := cmt.Get(FIELD_ALBUM); err != nil {
t.Error(err)
t.Fail()
} else if len(res) != 1 || res[0] != "Bee Moved" {
t.Error("Unexpected album name: ", res)
t.Fail()
}

if res, err := cmt.Get(FIELD_ARTIST); err != nil {
t.Error(err)
t.Fail()
} else if len(res) != 1 || res[0] != "Blue Monday FM" {
t.Error("Unexpected artist name: ", res)
t.Fail()
}

if res, err := cmt.Get(FIELD_TITLE); err != nil {
t.Error(err)
t.Fail()
} else if len(res) != 1 || res[0] != "Bee Moved" {
t.Error("Unexpected title name: ", res)
t.Fail()
}

if res, err := cmt.Get(FIELD_GENRE); err != nil {
t.Error(err)
t.Fail()
} else if len(res) != 1 || res[0] != "Bee Pop" {
t.Error("Unexpected title name: ", res)
t.Fail()
}
}
check(cmt)
new, err := ParseFromMetaDataBlock(cmt.Marshal())
if err != nil {
t.Error(err)
t.Fail()
}
check(new)

}

0 comments on commit e2254dd

Please sign in to comment.