func getUnsupportedVorbisTags(data []byte) ([]byte, int) { result := make([]byte, len(data)) fields := 0 size := 0 numberOfFields := utils.ReadInt32Le(data[0:4]) data = data[4:] for i := 0; i < numberOfFields; i++ { fieldSize := utils.ReadInt32Le(data[0:4]) if len(data) < fieldSize+4 { break } pos := bytes.IndexByte(data[4:4+fieldSize], 0x3d) if pos == -1 { break } fieldName := strings.ToUpper(string(data[4 : 4+pos])) switch fieldName { case "TITLE", "ARTIST", "ALBUM", "TRACKNUMBER", "DATE", "GENRE", "METADATA_BLOCK_PICTURE": break default: copy(result[size:size+4+fieldSize], data[:4+fieldSize]) fields++ size += 4 + fieldSize } data = data[4+fieldSize:] } return result[:size], fields }
func (editor *OggTagEditor) makeNewPages(existingCommentPages []byte, tag Tag) ([]byte, []byte, int) { // bitstream number var bitstream int = 31013 if len(existingCommentPages) > oggPageHeaderSize { bitstream = utils.ReadInt32Le(existingCommentPages[14:18]) } commentHeader, existingTagData, setupHeader := editor.splitCommentPages(existingCommentPages) if len(commentHeader) == 0 { commentHeader = make([]byte, 1+len(oggHeaderMagic)+4) commentHeader[0] = 3 copy(commentHeader[1:1+len(oggHeaderMagic)], oggHeaderMagic) utils.WriteInt32Le(0, commentHeader[len(oggHeaderMagic):len(oggHeaderMagic)+4]) } unsupportedTagData, unsupportedFields := getUnsupportedVorbisTags(existingTagData) newTagData, totalFields := serializeVorbisTag(tag, unsupportedFields) tagData := make([]byte, len(commentHeader)+4+len(newTagData)+len(unsupportedTagData)+1) copy(tagData, commentHeader) utils.WriteInt32Le(totalFields, tagData[len(commentHeader):len(commentHeader)+4]) copy(tagData[len(commentHeader)+4:], newTagData) copy(tagData[len(commentHeader)+4+len(newTagData):], unsupportedTagData) tagData[len(tagData)-1] = 1 newCommentHeader, commentPages := editor.packTagDataIntoFrames(bitstream, 1, tagData) newSetupHeader, setupPages := editor.packTagDataIntoFrames(bitstream, commentPages+1, setupHeader) return newCommentHeader, newSetupHeader, commentPages + setupPages }
func (editor *OggTagEditor) splitCommentPages(pages []byte) ([]byte, []byte, []byte) { var commentHeader []byte = nil var tagData []byte = nil var setupHeader []byte = nil for len(pages) > oggPageHeaderSize { pageHeaderSize := oggPageHeaderSize + int(pages[oggPageHeaderSize-1]) // it's the 1st page, the one with comment header if pages[5]&headerTypeContinue == 0 { if len(pages) < pageHeaderSize+5+len(oggHeaderMagic) { return nil, nil, nil } commentHeaderSize := utils.ReadInt32Le(pages[pageHeaderSize+1+len(oggHeaderMagic) : pageHeaderSize+5+len(oggHeaderMagic)]) if len(pages) < pageHeaderSize+5+len(oggHeaderMagic)+commentHeaderSize { return nil, nil, nil } commentHeader = pages[pageHeaderSize : pageHeaderSize+5+len(oggHeaderMagic)+commentHeaderSize] pageHeaderSize += 5 + len(oggHeaderMagic) + commentHeaderSize } pageSize := editor.getPageSize(pages) tagData = append(tagData, pages[pageHeaderSize:pageSize]...) pages = pages[pageSize:] } pos := bytes.Index(tagData, []byte(oggHeaderMagic)) if pos != -1 { setupHeader = tagData[pos-1:] tagData = tagData[:pos-1] } return commentHeader, tagData, setupHeader }
func (editor *FlacTagEditor) parseCommentBlock(data []byte, tag *Tag) error { if len(data) < 8 { return errors.New("no flac comment block to parse") } vendorSize := utils.ReadInt32Le(data[4:8]) return parseVorbisTags(data[8+vendorSize:], tag) }
func parseVorbisTags(data []byte, tag *Tag) error { if len(data) < 4 { return errors.New("vorbis data is too short to contain a tag") } numberOfFields := utils.ReadInt32Le(data[0:4]) data = data[4:] for i := 0; i < numberOfFields; i++ { fieldSize := utils.ReadInt32Le(data[0:4]) if fieldSize+4 <= len(data) { parseVorbisTagField(data[4:4+fieldSize], tag) data = data[4+fieldSize:] } } return nil }
func (editor *FlacTagEditor) makeNewCommentBlock(tag Tag, existingCommentBlock []byte) []byte { var vendorData []byte = nil var unsupportedTagData []byte = nil var unsupportedFields int = 0 if len(existingCommentBlock) >= 8 { vendorSize := utils.ReadInt32Le(existingCommentBlock[4:8]) vendorData = existingCommentBlock[4 : 8+vendorSize] unsupportedTagData, unsupportedFields = getUnsupportedVorbisTags(existingCommentBlock[8+vendorSize:]) } newCommentData, totalFields := serializeVorbisTag(tag, unsupportedFields) newCommentBlock := make([]byte, 4+len(vendorData)+4+len(newCommentData)+len(unsupportedTagData)) newCommentBlock[0] = commentBlockType utils.WriteInt24Be(len(newCommentBlock)-4, newCommentBlock[1:4]) copy(newCommentBlock[4:], vendorData) utils.WriteInt32Le(totalFields, newCommentBlock[4+len(vendorData):8+len(vendorData)]) copy(newCommentBlock[8+len(vendorData):], newCommentData) copy(newCommentBlock[8+len(vendorData)+len(newCommentData):], unsupportedTagData) return newCommentBlock }