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{} }