ID3v2.4 tag for .mp3 files: Go parsing library

File extension

mp3

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of ID3v2.4 tag for .mp3 files using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Go source code to parse ID3v2.4 tag for .mp3 files

id3v2_4.go

// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.

import (
	"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
	"bytes"
)


/**
 * @see <a href="http://id3.org/id3v2.4.0-structure">Source</a>
 * @see <a href="http://id3.org/id3v2.4.0-frames">Source</a>
 */
type Id3v24 struct {
	Tag *Id3v24_Tag
	_io *kaitai.Stream
	_root *Id3v24
	_parent kaitai.Struct
}
func NewId3v24() *Id3v24 {
	return &Id3v24{
	}
}

func (this Id3v24) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24) Read(io *kaitai.Stream, parent kaitai.Struct, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp1 := NewId3v24_Tag()
	err = tmp1.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Tag = tmp1
	return err
}
type Id3v24_Footer struct {
	Magic []byte
	VersionMajor uint8
	VersionRevision uint8
	Flags *Id3v24_Footer_Flags
	Size *Id3v24_U4beSynchsafe
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_Tag
}
func NewId3v24_Footer() *Id3v24_Footer {
	return &Id3v24_Footer{
	}
}

func (this Id3v24_Footer) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_Footer) Read(io *kaitai.Stream, parent *Id3v24_Tag, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp2, err := this._io.ReadBytes(int(3))
	if err != nil {
		return err
	}
	tmp2 = tmp2
	this.Magic = tmp2
	if !(bytes.Equal(this.Magic, []uint8{51, 68, 73})) {
		return kaitai.NewValidationNotEqualError([]uint8{51, 68, 73}, this.Magic, this._io, "/types/footer/seq/0")
	}
	tmp3, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.VersionMajor = tmp3
	tmp4, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.VersionRevision = tmp4
	tmp5 := NewId3v24_Footer_Flags()
	err = tmp5.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Flags = tmp5
	tmp6 := NewId3v24_U4beSynchsafe()
	err = tmp6.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Size = tmp6
	return err
}
type Id3v24_Footer_Flags struct {
	FlagUnsynchronization bool
	FlagHeaderex bool
	FlagExperimental bool
	FlagFooter bool
	Reserved uint64
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_Footer
}
func NewId3v24_Footer_Flags() *Id3v24_Footer_Flags {
	return &Id3v24_Footer_Flags{
	}
}

func (this Id3v24_Footer_Flags) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_Footer_Flags) Read(io *kaitai.Stream, parent *Id3v24_Footer, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp7, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagUnsynchronization = tmp7 != 0
	tmp8, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagHeaderex = tmp8 != 0
	tmp9, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagExperimental = tmp9 != 0
	tmp10, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagFooter = tmp10 != 0
	tmp11, err := this._io.ReadBitsIntBe(4)
	if err != nil {
		return err
	}
	this.Reserved = tmp11
	return err
}
type Id3v24_Frame struct {
	Id string
	Size *Id3v24_U4beSynchsafe
	FlagsStatus *Id3v24_Frame_FlagsStatus
	FlagsFormat *Id3v24_Frame_FlagsFormat
	Data []byte
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_Tag
	_f_isInvalid bool
	isInvalid bool
}
func NewId3v24_Frame() *Id3v24_Frame {
	return &Id3v24_Frame{
	}
}

func (this Id3v24_Frame) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_Frame) Read(io *kaitai.Stream, parent *Id3v24_Tag, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp12, err := this._io.ReadBytes(int(4))
	if err != nil {
		return err
	}
	tmp12 = tmp12
	this.Id = string(tmp12)
	tmp13 := NewId3v24_U4beSynchsafe()
	err = tmp13.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Size = tmp13
	tmp14 := NewId3v24_Frame_FlagsStatus()
	err = tmp14.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.FlagsStatus = tmp14
	tmp15 := NewId3v24_Frame_FlagsFormat()
	err = tmp15.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.FlagsFormat = tmp15
	tmp16, err := this.Size.Value()
	if err != nil {
		return err
	}
	tmp17, err := this._io.ReadBytes(int(tmp16))
	if err != nil {
		return err
	}
	tmp17 = tmp17
	this.Data = tmp17
	return err
}
func (this *Id3v24_Frame) IsInvalid() (v bool, err error) {
	if (this._f_isInvalid) {
		return this.isInvalid, nil
	}
	this._f_isInvalid = true
	this.isInvalid = bool(this.Id == "\000\000\000\000")
	return this.isInvalid, nil
}
type Id3v24_Frame_FlagsFormat struct {
	Reserved1 bool
	FlagGrouping bool
	Reserved2 uint64
	FlagCompressed bool
	FlagEncrypted bool
	FlagUnsynchronisated bool
	FlagIndicator bool
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_Frame
}
func NewId3v24_Frame_FlagsFormat() *Id3v24_Frame_FlagsFormat {
	return &Id3v24_Frame_FlagsFormat{
	}
}

func (this Id3v24_Frame_FlagsFormat) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_Frame_FlagsFormat) Read(io *kaitai.Stream, parent *Id3v24_Frame, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp18, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.Reserved1 = tmp18 != 0
	tmp19, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagGrouping = tmp19 != 0
	tmp20, err := this._io.ReadBitsIntBe(2)
	if err != nil {
		return err
	}
	this.Reserved2 = tmp20
	tmp21, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagCompressed = tmp21 != 0
	tmp22, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagEncrypted = tmp22 != 0
	tmp23, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagUnsynchronisated = tmp23 != 0
	tmp24, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagIndicator = tmp24 != 0
	return err
}
type Id3v24_Frame_FlagsStatus struct {
	Reserved1 bool
	FlagDiscardAlterTag bool
	FlagDiscardAlterFile bool
	FlagReadOnly bool
	Reserved2 uint64
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_Frame
}
func NewId3v24_Frame_FlagsStatus() *Id3v24_Frame_FlagsStatus {
	return &Id3v24_Frame_FlagsStatus{
	}
}

func (this Id3v24_Frame_FlagsStatus) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_Frame_FlagsStatus) Read(io *kaitai.Stream, parent *Id3v24_Frame, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp25, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.Reserved1 = tmp25 != 0
	tmp26, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagDiscardAlterTag = tmp26 != 0
	tmp27, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagDiscardAlterFile = tmp27 != 0
	tmp28, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagReadOnly = tmp28 != 0
	tmp29, err := this._io.ReadBitsIntBe(4)
	if err != nil {
		return err
	}
	this.Reserved2 = tmp29
	return err
}
type Id3v24_Header struct {
	Magic []byte
	VersionMajor uint8
	VersionRevision uint8
	Flags *Id3v24_Header_Flags
	Size *Id3v24_U4beSynchsafe
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_Tag
}
func NewId3v24_Header() *Id3v24_Header {
	return &Id3v24_Header{
	}
}

func (this Id3v24_Header) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_Header) Read(io *kaitai.Stream, parent *Id3v24_Tag, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp30, err := this._io.ReadBytes(int(3))
	if err != nil {
		return err
	}
	tmp30 = tmp30
	this.Magic = tmp30
	if !(bytes.Equal(this.Magic, []uint8{73, 68, 51})) {
		return kaitai.NewValidationNotEqualError([]uint8{73, 68, 51}, this.Magic, this._io, "/types/header/seq/0")
	}
	tmp31, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.VersionMajor = tmp31
	tmp32, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.VersionRevision = tmp32
	tmp33 := NewId3v24_Header_Flags()
	err = tmp33.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Flags = tmp33
	tmp34 := NewId3v24_U4beSynchsafe()
	err = tmp34.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Size = tmp34
	return err
}
type Id3v24_Header_Flags struct {
	FlagUnsynchronization bool
	FlagHeaderex bool
	FlagExperimental bool
	FlagFooter bool
	Reserved uint64
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_Header
}
func NewId3v24_Header_Flags() *Id3v24_Header_Flags {
	return &Id3v24_Header_Flags{
	}
}

func (this Id3v24_Header_Flags) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_Header_Flags) Read(io *kaitai.Stream, parent *Id3v24_Header, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp35, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagUnsynchronization = tmp35 != 0
	tmp36, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagHeaderex = tmp36 != 0
	tmp37, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagExperimental = tmp37 != 0
	tmp38, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagFooter = tmp38 != 0
	tmp39, err := this._io.ReadBitsIntBe(4)
	if err != nil {
		return err
	}
	this.Reserved = tmp39
	return err
}
type Id3v24_HeaderEx struct {
	Size *Id3v24_U4beSynchsafe
	FlagsEx *Id3v24_HeaderEx_FlagsEx
	Data []byte
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_Tag
}
func NewId3v24_HeaderEx() *Id3v24_HeaderEx {
	return &Id3v24_HeaderEx{
	}
}

func (this Id3v24_HeaderEx) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_HeaderEx) Read(io *kaitai.Stream, parent *Id3v24_Tag, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp40 := NewId3v24_U4beSynchsafe()
	err = tmp40.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Size = tmp40
	tmp41 := NewId3v24_HeaderEx_FlagsEx()
	err = tmp41.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.FlagsEx = tmp41
	tmp42, err := this.Size.Value()
	if err != nil {
		return err
	}
	tmp43, err := this._io.ReadBytes(int(tmp42 - 5))
	if err != nil {
		return err
	}
	tmp43 = tmp43
	this.Data = tmp43
	return err
}
type Id3v24_HeaderEx_FlagsEx struct {
	Reserved1 bool
	FlagUpdate bool
	FlagCrc bool
	FlagRestrictions bool
	Reserved2 uint64
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_HeaderEx
}
func NewId3v24_HeaderEx_FlagsEx() *Id3v24_HeaderEx_FlagsEx {
	return &Id3v24_HeaderEx_FlagsEx{
	}
}

func (this Id3v24_HeaderEx_FlagsEx) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_HeaderEx_FlagsEx) Read(io *kaitai.Stream, parent *Id3v24_HeaderEx, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp44, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.Reserved1 = tmp44 != 0
	tmp45, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagUpdate = tmp45 != 0
	tmp46, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagCrc = tmp46 != 0
	tmp47, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.FlagRestrictions = tmp47 != 0
	tmp48, err := this._io.ReadBitsIntBe(4)
	if err != nil {
		return err
	}
	this.Reserved2 = tmp48
	return err
}
type Id3v24_Padding struct {
	Padding []byte
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_Tag
}
func NewId3v24_Padding() *Id3v24_Padding {
	return &Id3v24_Padding{
	}
}

func (this Id3v24_Padding) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_Padding) Read(io *kaitai.Stream, parent *Id3v24_Tag, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp49, err := this._root.Tag.Header.Size.Value()
	if err != nil {
		return err
	}
	tmp50, err := this._io.Pos()
	if err != nil {
		return err
	}
	tmp51, err := this._io.ReadBytes(int(tmp49 - tmp50))
	if err != nil {
		return err
	}
	tmp51 = tmp51
	this.Padding = tmp51
	return err
}
type Id3v24_Tag struct {
	Header *Id3v24_Header
	HeaderEx *Id3v24_HeaderEx
	Frames []*Id3v24_Frame
	Padding *Id3v24_Padding
	Footer *Id3v24_Footer
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24
}
func NewId3v24_Tag() *Id3v24_Tag {
	return &Id3v24_Tag{
	}
}

func (this Id3v24_Tag) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_Tag) Read(io *kaitai.Stream, parent *Id3v24, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp52 := NewId3v24_Header()
	err = tmp52.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Header = tmp52
	if (this.Header.Flags.FlagHeaderex) {
		tmp53 := NewId3v24_HeaderEx()
		err = tmp53.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.HeaderEx = tmp53
	}
	for i := 1;; i++ {
		tmp54 := NewId3v24_Frame()
		err = tmp54.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		_it := tmp54
		this.Frames = append(this.Frames, _it)
		tmp55, err := this._io.Pos()
		if err != nil {
			return err
		}
		tmp56, err := _it.Size.Value()
		if err != nil {
			return err
		}
		tmp57, err := this.Header.Size.Value()
		if err != nil {
			return err
		}
		tmp58, err := _it.IsInvalid()
		if err != nil {
			return err
		}
		if  ((tmp55 + tmp56 > tmp57) || (tmp58))  {
			break
		}
	}
	if (!(this.Header.Flags.FlagFooter)) {
		tmp59 := NewId3v24_Padding()
		err = tmp59.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Padding = tmp59
	}
	if (this.Header.Flags.FlagFooter) {
		tmp60 := NewId3v24_Footer()
		err = tmp60.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Footer = tmp60
	}
	return err
}
type Id3v24_U1beSynchsafe struct {
	Padding bool
	Value uint64
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_U2beSynchsafe
}
func NewId3v24_U1beSynchsafe() *Id3v24_U1beSynchsafe {
	return &Id3v24_U1beSynchsafe{
	}
}

func (this Id3v24_U1beSynchsafe) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_U1beSynchsafe) Read(io *kaitai.Stream, parent *Id3v24_U2beSynchsafe, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp61, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.Padding = tmp61 != 0
	tmp62, err := this._io.ReadBitsIntBe(7)
	if err != nil {
		return err
	}
	this.Value = tmp62
	return err
}
type Id3v24_U2beSynchsafe struct {
	Byte0 *Id3v24_U1beSynchsafe
	Byte1 *Id3v24_U1beSynchsafe
	_io *kaitai.Stream
	_root *Id3v24
	_parent *Id3v24_U4beSynchsafe
	_f_value bool
	value int
}
func NewId3v24_U2beSynchsafe() *Id3v24_U2beSynchsafe {
	return &Id3v24_U2beSynchsafe{
	}
}

func (this Id3v24_U2beSynchsafe) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_U2beSynchsafe) Read(io *kaitai.Stream, parent *Id3v24_U4beSynchsafe, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp63 := NewId3v24_U1beSynchsafe()
	err = tmp63.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Byte0 = tmp63
	tmp64 := NewId3v24_U1beSynchsafe()
	err = tmp64.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Byte1 = tmp64
	return err
}
func (this *Id3v24_U2beSynchsafe) Value() (v int, err error) {
	if (this._f_value) {
		return this.value, nil
	}
	this._f_value = true
	this.value = int(this.Byte0.Value << 7 | this.Byte1.Value)
	return this.value, nil
}
type Id3v24_U4beSynchsafe struct {
	Short0 *Id3v24_U2beSynchsafe
	Short1 *Id3v24_U2beSynchsafe
	_io *kaitai.Stream
	_root *Id3v24
	_parent kaitai.Struct
	_f_value bool
	value int
}
func NewId3v24_U4beSynchsafe() *Id3v24_U4beSynchsafe {
	return &Id3v24_U4beSynchsafe{
	}
}

func (this Id3v24_U4beSynchsafe) IO_() *kaitai.Stream {
	return this._io
}

func (this *Id3v24_U4beSynchsafe) Read(io *kaitai.Stream, parent kaitai.Struct, root *Id3v24) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp65 := NewId3v24_U2beSynchsafe()
	err = tmp65.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Short0 = tmp65
	tmp66 := NewId3v24_U2beSynchsafe()
	err = tmp66.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Short1 = tmp66
	return err
}
func (this *Id3v24_U4beSynchsafe) Value() (v int, err error) {
	if (this._f_value) {
		return this.value, nil
	}
	this._f_value = true
	tmp67, err := this.Short0.Value()
	if err != nil {
		return 0, err
	}
	tmp68, err := this.Short1.Value()
	if err != nil {
		return 0, err
	}
	this.value = int(tmp67 << 14 | tmp68)
	return this.value, nil
}