⬥ ID3 encoding/decoding library in Go ⬥
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
3.2 KiB

package v2
import (
"fmt"
"io"
"strings"
"github.com/Unbewohnte/id3ed/util"
)
type FrameFlags struct {
TagAlterPreservation bool
FileAlterPreservation bool
ReadOnly bool
Compressed bool
Encrypted bool
InGroup bool
}
type Frame struct {
ID string
Size int64
Flags FrameFlags
GroupByte byte
Contents []byte
}
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 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 Frame{}, err
}
framesize, err := util.BytesToIntIgnoreFirstBit(framesizeBytes)
if err != nil {
return Frame{}, err
}
frame.Size = framesize
// Flags
frameFlagsByte1, err := util.Read(rs, 1)
if err != nil {
return Frame{}, err
}
frameFlagsByte2, err := util.Read(rs, 1)
if err != nil {
return Frame{}, err
}
// I don`t have enough knowledge to handle this more elegantly
// Any pointers ?
flagsByte1Bits := fmt.Sprintf("%08b", frameFlagsByte1)
flagsByte2Bits := fmt.Sprintf("%08b", frameFlagsByte2)
var flags FrameFlags
if flagsByte1Bits[0] == 1 {
flags.TagAlterPreservation = true
} else {
flags.TagAlterPreservation = false
}
if flagsByte1Bits[1] == 1 {
flags.FileAlterPreservation = true
} else {
flags.FileAlterPreservation = false
}
if flagsByte1Bits[2] == 1 {
flags.ReadOnly = true
} else {
flags.ReadOnly = false
}
if flagsByte2Bits[0] == 1 {
flags.Compressed = true
} else {
flags.Compressed = false
}
if flagsByte2Bits[1] == 1 {
flags.Encrypted = true
} else {
flags.Encrypted = false
}
if flagsByte2Bits[2] == 1 {
flags.InGroup = true
} else {
flags.InGroup = false
}
frame.Flags = flags
if flags.InGroup {
groupByte, err := util.Read(rs, 1)
if err != nil {
return Frame{}, err
}
frame.GroupByte = groupByte[0]
}
// Body
frameContents, err := util.Read(rs, uint64(framesize))
if err != nil {
return Frame{}, err
}
frame.Contents = frameContents
return frame, nil
}
// 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, fmt.Errorf("could not skip header: %s", err)
}
var frames []Frame
for {
frame, err := ReadFrame(rs)
if err == ErrGotPadding {
return frames, nil
}
if err != nil {
return nil, fmt.Errorf("could not read frame: %s", err)
}
frames = append(frames, frame)
}
}
// 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{}
}