Browse Source

▣ Now can read all ID3v2 frames ! ▣

main
Unbewohnte 3 years ago
parent
commit
625f33f9ea
  1. 76
      v2/frame.go
  2. 42
      v2/frame_test.go
  3. 25
      v2/header.go
  4. 4
      v2/v2tags.go

76
v2/frame.go

@ -3,6 +3,7 @@ package v2
import (
"fmt"
"io"
"strings"
"github.com/Unbewohnte/id3ed/util"
)
@ -24,26 +25,37 @@ type Frame struct {
Contents []byte
}
// Reads ID3v2.3.0 or ID3v2.4.0 frame
func ReadFrame(rs io.Reader) (*Frame, error) {
var ErrGotPadding error = fmt.Errorf("got padding")
// Reads next ID3v2.3.0 or ID3v2.4.0 frame.
// Returns a blank Frame struct if encountered an error
func ReadFrame(rs io.Reader) (Frame, error) {
var frame Frame
// ID
identifier, err := util.ReadToString(rs, 4)
if err != nil {
return nil, err
return Frame{}, err
}
if len(identifier) < 1 {
// probably read all frames and got padding as identifier
// I know that it`s a terrible desicion, but with my current
// implementation it`s the only way I can see that will somewhat work
return Frame{}, ErrGotPadding
}
frame.ID = identifier
// Size
framesizeBytes, err := util.Read(rs, 4)
if err != nil {
return nil, err
return Frame{}, err
}
framesize, err := util.BytesToIntIgnoreFirstBit(framesizeBytes)
if err != nil {
return nil, err
return Frame{}, err
}
frame.Size = framesize
@ -52,12 +64,12 @@ func ReadFrame(rs io.Reader) (*Frame, error) {
frameFlagsByte1, err := util.Read(rs, 1)
if err != nil {
return nil, err
return Frame{}, err
}
frameFlagsByte2, err := util.Read(rs, 1)
if err != nil {
return nil, err
return Frame{}, err
}
// I don`t have enough knowledge to handle this more elegantly
@ -103,7 +115,7 @@ func ReadFrame(rs io.Reader) (*Frame, error) {
if flags.InGroup {
groupByte, err := util.Read(rs, 1)
if err != nil {
return nil, err
return Frame{}, err
}
frame.GroupByte = groupByte[0]
}
@ -111,48 +123,44 @@ func ReadFrame(rs io.Reader) (*Frame, error) {
// Body
frameContents, err := util.Read(rs, uint64(framesize))
if err != nil {
return nil, err
return Frame{}, err
}
frame.Contents = frameContents
return &frame, nil
return frame, nil
}
// Reads ID3v2 frames from rs. NOT TESTED !!!!
func GetFrames(rs io.ReadSeeker) ([]*Frame, error) {
header, err := GetHeader(rs)
// Reads all ID3v2 frames from rs.
// Returns a nil as []Frame if encountered an error
func GetFrames(rs io.ReadSeeker) ([]Frame, error) {
// skip header
_, err := rs.Seek(10, io.SeekStart)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not skip header: %s", err)
}
tagsize := header.Size
fmt.Println("NEED TO READ ", tagsize)
var frames []*Frame
var read uint64 = 0
var frames []Frame
for {
if read == uint64(tagsize) {
break
frame, err := ReadFrame(rs)
if err == ErrGotPadding {
return frames, nil
}
frame, err := ReadFrame(rs)
if err != nil {
return frames, fmt.Errorf("could not read frame: %s", err)
return nil, fmt.Errorf("could not read frame: %s", err)
}
frames = append(frames, frame)
// counting how many bytes has been read
read += 10 // frame header
if frame.Flags.InGroup {
// header has 1 additional byte
read += 1
frames = append(frames, frame)
}
read += uint64(frame.Size) // and the contents itself
fmt.Println("Read: ", read, " ", frame.ID)
}
return frames, nil
// Looks for a certain identificator in given frames and returns frame if found
func GetFrame(id string, frames []Frame) Frame {
for _, frame := range frames {
if strings.Contains(frame.ID, id) {
return frame
}
}
return Frame{}
}

42
v2/frame_test.go

@ -45,15 +45,35 @@ func TestReadFrame(t *testing.T) {
}
}
func TestGetFrames(t *testing.T) {
f, err := os.Open(filepath.Join(TESTDATAPATH, "testreadv2.mp3"))
if err != nil {
t.Errorf("%s", err)
}
_, err = GetFrames(f)
if err != nil {
t.Errorf("GetFrames failed: %s", err)
}
}
func TestGetFrame(t *testing.T) {
f, err := os.Open(filepath.Join(TESTDATAPATH, "testreadv2.mp3"))
if err != nil {
t.Errorf("%s", err)
}
frames, err := GetFrames(f)
if err != nil {
t.Errorf("GetFrames failed: %s", err)
}
frame := GetFrame("TIT2", frames)
if frame.ID == "" {
t.Errorf("GetFrame failed: expected to find %s; got nothing", "TIT1")
}
// func TestGetFrames(t *testing.T) {
// f, err := os.Open(filepath.Join(TESTDATAPATH, "testreadv2.mp3"))
// if err != nil {
// t.Errorf("%s", err)
// }
// _, err = GetFrames(f)
// if err != nil {
// t.Errorf("GetFrames failed: %s", err)
// }
// }
if util.ToString(frame.Contents) != "title" {
t.Errorf("GetFrame failed: expected contents to be %s; got %s", "title", util.ToString(frame.Contents))
}
}

25
v2/header.go

@ -23,35 +23,36 @@ type Header struct {
Size int64 // size of the whole tag - 10 header bytes
}
// Reads and structuralises ID3v2.3.0 or ID3v2.4.0 header
func GetHeader(rs io.ReadSeeker) (*Header, error) {
// Reads and structuralises ID3v2.3.0 or ID3v2.4.0 header.
// Returns a blank header struct if encountered an error
func GetHeader(rs io.ReadSeeker) (Header, error) {
var header Header
rs.Seek(0, io.SeekStart)
identifier, err := util.Read(rs, 3)
if err != nil {
return nil, err
return Header{}, err
}
// check if ID3v2 is used
if !bytes.Equal([]byte(HEADERIDENTIFIER), identifier) {
return nil, fmt.Errorf("no ID3v2 identifier found")
return Header{}, fmt.Errorf("no ID3v2 identifier found")
}
header.Identifier = string(identifier)
// version
VersionBytes, err := util.Read(rs, 2)
if err != nil {
return nil, err
return Header{}, err
}
majorVersion, err := util.ByteToInt(VersionBytes[0])
if err != nil {
return nil, err
return Header{}, err
}
revisionNumber, err := util.ByteToInt(VersionBytes[1])
if err != nil {
return nil, err
return Header{}, err
}
var version string
@ -63,7 +64,7 @@ func GetHeader(rs io.ReadSeeker) (*Header, error) {
case 4:
version = V2_4
default:
return nil, fmt.Errorf("ID3v2.%d.%d is not supported", majorVersion, revisionNumber)
return Header{}, fmt.Errorf("ID3v2.%d.%d is not supported", majorVersion, revisionNumber)
}
header.Version = version
@ -71,7 +72,7 @@ func GetHeader(rs io.ReadSeeker) (*Header, error) {
// flags
flags, err := util.Read(rs, 1)
if err != nil {
return nil, err
return Header{}, err
}
flagBits := fmt.Sprintf("%08b", flags) // 1 byte is 8 bits
@ -123,15 +124,15 @@ func GetHeader(rs io.ReadSeeker) (*Header, error) {
// size
sizeBytes, err := util.Read(rs, 4)
if err != nil {
return nil, err
return Header{}, err
}
size, err := util.BytesToIntIgnoreFirstBit(sizeBytes)
if err != nil {
return nil, err
return Header{}, err
}
header.Size = size
return &header, nil
return header, nil
}

4
v2/v2tags.go

@ -1,8 +1,8 @@
package v2
// type ID3v2Tag struct {
// Header *Header
// Frames []*Frame
// Header Header
// Frames []Frame
// }
// type V2TagReader interface {

Loading…
Cancel
Save