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 ( import (
"fmt" "fmt"
"io" "io"
"strings"
"github.com/Unbewohnte/id3ed/util" "github.com/Unbewohnte/id3ed/util"
) )
@ -24,26 +25,37 @@ type Frame struct {
Contents []byte Contents []byte
} }
// Reads ID3v2.3.0 or ID3v2.4.0 frame var ErrGotPadding error = fmt.Errorf("got padding")
func ReadFrame(rs io.Reader) (*Frame, error) {
// 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 var frame Frame
// ID // ID
identifier, err := util.ReadToString(rs, 4) identifier, err := util.ReadToString(rs, 4)
if err != nil { 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 frame.ID = identifier
// Size // Size
framesizeBytes, err := util.Read(rs, 4) framesizeBytes, err := util.Read(rs, 4)
if err != nil { if err != nil {
return nil, err return Frame{}, err
} }
framesize, err := util.BytesToIntIgnoreFirstBit(framesizeBytes) framesize, err := util.BytesToIntIgnoreFirstBit(framesizeBytes)
if err != nil { if err != nil {
return nil, err return Frame{}, err
} }
frame.Size = framesize frame.Size = framesize
@ -52,12 +64,12 @@ func ReadFrame(rs io.Reader) (*Frame, error) {
frameFlagsByte1, err := util.Read(rs, 1) frameFlagsByte1, err := util.Read(rs, 1)
if err != nil { if err != nil {
return nil, err return Frame{}, err
} }
frameFlagsByte2, err := util.Read(rs, 1) frameFlagsByte2, err := util.Read(rs, 1)
if err != nil { if err != nil {
return nil, err return Frame{}, err
} }
// I don`t have enough knowledge to handle this more elegantly // 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 { if flags.InGroup {
groupByte, err := util.Read(rs, 1) groupByte, err := util.Read(rs, 1)
if err != nil { if err != nil {
return nil, err return Frame{}, err
} }
frame.GroupByte = groupByte[0] frame.GroupByte = groupByte[0]
} }
@ -111,48 +123,44 @@ func ReadFrame(rs io.Reader) (*Frame, error) {
// Body // Body
frameContents, err := util.Read(rs, uint64(framesize)) frameContents, err := util.Read(rs, uint64(framesize))
if err != nil { if err != nil {
return nil, err return Frame{}, err
} }
frame.Contents = frameContents frame.Contents = frameContents
return &frame, nil return frame, nil
} }
// Reads ID3v2 frames from rs. NOT TESTED !!!! // Reads all ID3v2 frames from rs.
func GetFrames(rs io.ReadSeeker) ([]*Frame, error) { // Returns a nil as []Frame if encountered an error
header, err := GetHeader(rs) func GetFrames(rs io.ReadSeeker) ([]Frame, error) {
// skip header
_, err := rs.Seek(10, io.SeekStart)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("could not skip header: %s", err)
} }
tagsize := header.Size var frames []Frame
fmt.Println("NEED TO READ ", tagsize)
var frames []*Frame
var read uint64 = 0
for { for {
if read == uint64(tagsize) { frame, err := ReadFrame(rs)
break if err == ErrGotPadding {
return frames, nil
} }
frame, err := ReadFrame(rs)
if err != nil { 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 frames = append(frames, frame)
read += 10 // frame header
if frame.Flags.InGroup {
// header has 1 additional byte
read += 1
} }
read += uint64(frame.Size) // and the contents itself }
fmt.Println("Read: ", read, " ", frame.ID) // 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 frames, nil 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) { if util.ToString(frame.Contents) != "title" {
// f, err := os.Open(filepath.Join(TESTDATAPATH, "testreadv2.mp3")) t.Errorf("GetFrame failed: expected contents to be %s; got %s", "title", util.ToString(frame.Contents))
// if err != nil { }
// t.Errorf("%s", err) }
// }
// _, err = GetFrames(f)
// if err != nil {
// t.Errorf("GetFrames failed: %s", err)
// }
// }

25
v2/header.go

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

4
v2/v2tags.go

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

Loading…
Cancel
Save