From 8773ee434146292cee6d6d9a94814346588910c6 Mon Sep 17 00:00:00 2001 From: Sorrow446 <47045733+Sorrow446@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:02:06 +0000 Subject: [PATCH] https://github.com/Sorrow446/go-mp4tag/issues/11 - Issue #11 implemented. - Fixed assignment to nil map error for the custom field. - UpperCustom func, pass false to disable uppercasing custom tags' names for read & write. --- mp4tag.go | 11 +++++++- objects.go | 2 ++ read.go | 59 ++++++++++++++++++++++++++++++++++------- write.go | 78 +++++++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 134 insertions(+), 16 deletions(-) diff --git a/mp4tag.go b/mp4tag.go index 95571d9..738675d 100644 --- a/mp4tag.go +++ b/mp4tag.go @@ -7,6 +7,10 @@ import ( "os" ) +func (mp4 *MP4) UpperCustom(b bool) { + mp4.upperCustom = b +} + func (mp4 *MP4) Close() error { return mp4.f.Close() } @@ -59,7 +63,12 @@ func Open(trackPath string) (*MP4, error) { return nil, err } - mp4 := &MP4{f: f, size : stat.Size(), path: trackPath} + mp4 := &MP4{ + f: f, + size : stat.Size(), + path: trackPath, + upperCustom: true, + } err = mp4.checkHeader() if err != nil { f.Close() diff --git a/objects.go b/objects.go index 9152e18..f116e0c 100644 --- a/objects.go +++ b/objects.go @@ -62,6 +62,7 @@ type MP4 struct { f *os.File path string size int64 + upperCustom bool } type MP4Box struct { @@ -297,6 +298,7 @@ type MP4Tags struct { ItunesArtistID int32 Lyrics string Narrator string + OtherCustom map[string][]string Pictures []*MP4Picture Publisher string Title string diff --git a/read.go b/read.go index 44700a8..38ec509 100644 --- a/read.go +++ b/read.go @@ -229,7 +229,18 @@ func (mp4 MP4) readTrknDisk(boxes MP4Boxes, boxName string) (int16, int16, error return num, total, nil } -func (mp4 MP4) readCustom(boxes MP4Boxes) (map[string]string, error) { +func addToOthers(others map[string][]string, key, val string) map[string][]string { + existingOthers, ok := others[key] + if ok { + existingOthers = append(existingOthers, val) + others[key] = existingOthers + } else { + others[key] = []string{val} + } + return others +} + +func (mp4 MP4) readCustom(boxes MP4Boxes) (map[string]string, map[string][]string, error) { var ( names []string values []string @@ -237,38 +248,67 @@ func (mp4 MP4) readCustom(boxes MP4Boxes) (map[string]string, error) { path := "moov.udta.meta.ilst.----" nameBoxes := boxes.getBoxesByPath(path+".name") if nameBoxes == nil { - return nil, nil + return nil, nil, nil } for _, box := range nameBoxes { _, err := mp4.f.Seek(box.StartOffset+12, io.SeekStart) if err != nil { - return nil, err + return nil, nil, err } name, err := mp4.readString(box.BoxSize-12) if err != nil { - return nil, err + return nil, nil, err + } + if mp4.upperCustom { + name = strings.ToUpper(name) } names = append(names, name) } + others := map[string][]string{} + dataBoxes := boxes.getBoxesByPath(path+".data") + + var ( + prev int64 + idx int + ) + for _, box := range dataBoxes { _, err := mp4.f.Seek(box.StartOffset+16, io.SeekStart) if err != nil { - return nil, err + return nil, nil, err } value, err := mp4.readString(box.BoxSize-16) if err != nil { - return nil, err + return nil, nil, err + } + if box.StartOffset == prev { + others = addToOthers(others, names[idx-1], value) + prev = box.EndOffset + continue } values = append(values, value) + prev = box.EndOffset + idx++ } custom := map[string]string{} for idx, name := range names { - custom[name] = values[idx] + _, ok := custom[name] + if ok { + existingOthers, ok := others[name] + if ok { + existingOthers = append(existingOthers, values[idx]) + others[name] = existingOthers + } else { + others[name] = []string{values[idx]} + } + } else { + custom[name] = values[idx] + } } - return custom, nil + return custom, others, nil } func (mp4 MP4) readITAlbumID(boxes MP4Boxes) (int32, error) { @@ -373,7 +413,7 @@ func (mp4 MP4) readTags(boxes MP4Boxes) (*MP4Tags, error) { if err != nil { return nil, err } - custom, err := mp4.readCustom(boxes) + custom, otherCustom, err := mp4.readCustom(boxes) if err != nil { return nil, err } @@ -455,6 +495,7 @@ func (mp4 MP4) readTags(boxes MP4Boxes) (*MP4Tags, error) { ItunesArtistID: artistID, Lyrics: lyrics, Narrator: narrator, + OtherCustom: otherCustom, Pictures: pics, Publisher: publisher, Title: title, diff --git a/write.go b/write.go index c05b3f7..c9cfba2 100644 --- a/write.go +++ b/write.go @@ -20,6 +20,16 @@ func overwriteTags(mergedTags, tags *MP4Tags, delStrings []string) *MP4Tags{ } else if containsStr(delStrings, "allcustom") { mergedTags.Custom = map[string]string{} } + if containsStr(delStrings, "allothercustom") { + mergedTags.OtherCustom = map[string][]string{} + } + + if mergedTags.Custom == nil { + mergedTags.Custom = map[string]string{} + } + if mergedTags.OtherCustom == nil { + mergedTags.OtherCustom = map[string][]string{} + } if containsStr(delStrings, "album") { mergedTags.Album = "" @@ -265,11 +275,27 @@ func overwriteTags(mergedTags, tags *MP4Tags, delStrings []string) *MP4Tags{ } for k, v := range tags.Custom { + if containsStr(delStrings, "custom:"+k) { + continue + } if v != "" { mergedTags.Custom[k] = v } } + for k, v := range tags.OtherCustom { + if containsStr(delStrings, "custom:"+k) || len(v) < 1 { + continue + } + _, ok := mergedTags.OtherCustom[k] + if ok { + mergedTags.OtherCustom[k] = append(mergedTags.OtherCustom[k], v...) + } else { + mergedTags.OtherCustom[k] = v + } + + } + var filteredPics []*MP4Picture for idx, p := range mergedTags.Pictures { @@ -595,13 +621,25 @@ func writeItunesArtistID(f *os.File, artistID int32) error { return err } -func writeCustom(f *os.File, name, value string) error { - nameUpperBytes := []byte(strings.ToUpper(name)) +func writeCustom(f *os.File, name, value string, upper bool, others map[string][]string) error { valueBytes := []byte(value) - nameSize := len(nameUpperBytes) valueSize := len(valueBytes) - sizeBytes := putI32BE(int32(nameSize+valueSize)+64) + if upper { + name = strings.ToUpper(name) + } + nameUpperBytes := []byte(name) + nameSize := len(nameUpperBytes) + + totalSize := nameSize+valueSize+64 + otherCust, _ := others[name] + + for _, other := range otherCust { + totalSize += len([]byte(other)) + 16 + } + + // 48 + sizeBytes := putI32BE(int32(totalSize)) _, err := f.Write(sizeBytes) if err != nil { return err @@ -658,7 +696,35 @@ func writeCustom(f *os.File, name, value string) error { return err } _, err = f.Write(valueBytes) - return err + if err != nil { + return err + } + + + for _, v := range otherCust { + valueBytes = []byte(v) + valueSize = len(valueBytes) + sizeBytes = putI32BE(int32(valueSize)+16) + _, err = f.Write(sizeBytes) + if err != nil { + return err + } + _, err = f.WriteString("data") + if err != nil { + return err + } + _, err = f.Write( + []byte{0x0, 0x0, 0x0, 0x01, 0x0, 0x0, 0x0, 0x0}) + if err != nil { + return err + } + _, err = f.Write(valueBytes) + if err != nil { + return err + } + } + + return nil } func getPicFormat(imageType ImageType, magic []byte) uint8 { @@ -993,7 +1059,7 @@ func (mp4 MP4) writeTags(boxes MP4Boxes, tags *MP4Tags, tempPath string) error { } for k, v := range tags.Custom { - err = writeCustom(f, k, v) + err = writeCustom(f, k, v, mp4.upperCustom, tags.OtherCustom) if err != nil { return err }