Giter Site home page Giter Site logo

id3v2's Issues

There is a bug in handling duplicates in your package.

As I was writing a test, I noticed that in most cases you keep the first instance of a tag. All the others will be ignored.

I did not test all the tags or look at your code, but some tags can and are even expected to be duplicated.

Specifically, the TXXX tag has to be distinguished from its label and not the tag itself.

You add tag function works:

id3Tag.AddUserDefinedTextFrame(someFrame)

since it adds a new tag. However, it works the other way around: it accepts new entries which are potentially duplicates.

So... if you find two entries that looks like:

TXXX <size> <flags> 3 <name> \0 <data>

and the <name> are different, keep both in the order they were added. If the user calls AddUserDefinedText(). The reading of the tags should probably make use of the same add functions so that way you do not have to repeat that logic everywhere.

Documentation: http://id3.org/id3v2.3.0#User_defined_text_information_frame

AddAttachedPicture not working as expected

I've been working on a command line utility to replace a bunch of Ruby scripts i've had to modify mp3 tags/artwork/etc and your library has made this a breeze - thanks!

All has been working great but I've been having some difficulty setting the artwork on a file. Basically, the problem i'm seeing is that there appears to be a discrepancy between bogem/id3v2 and what taglib is showing. Here's what i'm observing:

I have an image which I've added to my mp3 using the following code:

const coverFilename string = "cover.jpg"

func addPicture() {
	filename := "Blure - Branches.mp3"

	frontCover, err := os.Open(coverFilename)
	if err != nil {
		log.Fatal("error opening cover file", err)
	}
	defer frontCover.Close()

	frontCoverBytes, err := ioutil.ReadAll(frontCover)
	if err != nil {
		log.Fatal("error reading cover file", err)
	}

	pic := id3v2.PictureFrame{
		Encoding:    id3v2.ENUTF8,
		MimeType:    "image/jpeg",
		Picture:     frontCoverBytes,
		PictureType: id3v2.PTFrontCover,
		Description: "Cover",
	}

	// Open file and find tag in it
	tag, err := id3v2.Open(filename)
	if err != nil {
		log.Fatal("error opening mp3 file", err)
	}
	defer tag.Close()

	tag.AddAttachedPicture(pic)
	if err = tag.Save(); err != nil {
		log.Fatal("Error while saving a tag:", err)
	}
}

As best I can tell, the attachment has been added. However, Finder (i'm on a mac) wasn't showing the cover on the file in Preview. That was my first indication that something wasn't right so I started to dig a little deeper. I made a few scripts in Ruby and Go to see what frames are included and this is where I'm seeing a difference.

Using Go:

Detecting MP3 files...
********************
File: Blure - Branches.mp3:
TCON
APIC

Using Ruby (taglib):

Detecting MP3 files...
********************
File: Blure - Branches.mp3
TCON

Using exiftool:

ExifTool Version Number         : 10.36
File Name                       : Blure - Branches.mp3
Directory                       : .
File Size                       : 7.1 MB
File Modification Date/Time     : 2017:01:06 11:08:58-05:00
File Access Date/Time           : 2017:01:06 11:09:12-05:00
File Inode Change Date/Time     : 2017:01:06 11:08:59-05:00
File Permissions                : rw-r--r--
File Type                       : MP3
File Type Extension             : mp3
MIME Type                       : audio/mpeg
MPEG Audio Version              : 1
Audio Layer                     : 3
Audio Bitrate                   : 320 kbps
Sample Rate                     : 44100
Channel Mode                    : Stereo
MS Stereo                       : Off
Intensity Stereo                : Off
Copyright Flag                  : False
Original Media                  : False
Emphasis                        : None
ID3 Size                        : 51131
Genre                           : Blues
Title                           :
Artist                          :
Album                           :
Year                            :
Comment                         :
Duration                        : 0:03:05 (approx)

If the file were to include an image, we'd see something like the following:

Picture MIME Type               : image/jpeg
Picture Type                    : Front Cover
Picture Description             : Cover
Picture                         : (Binary data 50939 bytes, use -b option to extract)

As you can see, the only way that I'm able to see evidence of an APIC frame is with my Go code. Am I doing something incorrectly when adding the picture frame? I'm fairly new to Go so it's entirely possible that this is something i'm doing incorrectly.

I created an example repository here with pared down versions of the scripts i've been using and the files from the above examples. Thanks!

Expose Tag's default encoding

Perhaps I've missed something here. Adding my own TextFrames to Tags seems to be more complicated than it need be, as Tag doesn't export its default encoding.

The Tag.Set* convenience methods use TextFrame{Encoding: tag.defaultEncoding ...}.

Because Tag.defaultEncoding isn't exported, you need to re-implement Tag.setDefaultEncoding() to add your own TextFrame.

(I'm assuming I shouldn't just smush UTF-8 TextFrames into a Tag whose version is < 4.)

Feature request: Add support for .m4a files (MPEG-4 container format)

Hi there,

After several hours of scratching my head about why none of the tags I was writing to a file were being saved, I finally managed to understand what was wrong: I was trying to add ID3v2 tags to a MPEG-4 container, not a MP3!

Reading your code, I believe you might consider slightly changing the code of parse(), which currently will return an empty tag (i.e. set to nil) if the file does not contain the magic word ID3 at the beginning. Your parseHeader() correctly returns a errNoTag to parse(), but that error is silently discarded when passed upstream. It might make some sense in some scenarios, but, in my case, I would rather prefer to get a ErrNoTag (i.e. a global error that can be checked by the code calling your library) instead of an all-ok, here's-your-empty-Tag-to-play-with approach. Notably, at this moment, I cannot use your library to figure out if it did open the file I told it to open, and that this file remains open for writing at a later stage.

This means that there is no clue about attempting to read or write the 'wrong' container format.

Anyway, it would be nice if you could add support for writing tags to .m4a files as well. This, of course, requires a lot of work, but... you might really not need to do much, except for including the go-mp4tag library. This will essentially write out iTunes-compatible tags to a MPEG-4 container (but there is flexibility to add your own), as well as the album cover in a 'proper' format. While this particular library is essentially write-only, @Sorrow446 is currently experimenting with a read & write version. Both versions, of course, rely heavily on the popular abema/go-mp4 library — which is terribly complex but does everything.

It would be nice to have a combination of both in a single library 😸 — which would make it work just like things like ffmpeg, which doesn't really care about what the destination format is: it will just use the appropriate tagging system, transparently to the user.

Thanks! :)

Doesn't add PictureFrame to ogg formats!

Thanks for the great package.
It just works for all mp3 files, but when it comes to different formats, like ogg it adds everything except the PictureFrame, which is kinda weird.

...
	if imgBytes != nil {
		tag.AddAttachedPicture(
			id3v2.PictureFrame{
				Encoding:    id3v2.EncodingUTF8,
				MimeType:    "image/jpeg",
				Picture:     imgBytes,
				Description: track.SoundData.Description, // well, coz why not :D
			},
		)
	}
...

Improving approach for APIC frame conflict solving.

A file containing multiple APIC frames will lose all but the last one when parsing.

iTunes appears to be perfectly capable of parsing files with multiple pieces of artwork (APIC) attached. When parsing such file with id3v2, we see only a single APIC coming out of the "AllFrames()" factory. When then saving we did effectively delete all those additional APIC frames.

The AddFrame implementation of sequence is rather confusing to me. It tries to find out if a key already exists within the sequence and if so, does not add the frame but replaces the existing frame in the sequence. What is the point of a sequence if we only ever add a single element?

Anyway, by patching AddFrame as follows, we get proper amounts of pictures out of the parser;

func (s *sequence) AddFrame(f Framer) {
	s.frames = append(s.frames, f)
}

Not sure what I am breaking here though....

Temp file gets created, and if issue occurs, never gets cleaned up

I just noticed a bunch of random files in /tmp. They had randomly generated names so I couldn't tell what they were from. Digging deeper I found they were audio files, but I didn't know how they got there.

Looking through the different possibilities I came upon https://github.com/bogem/id3v2/blob/master/tag.go#L225. It seems to make sense. I've seen some tagging errors happen over time, and my guess is each time I have an error tagging with id3v2 it leaves that temp file.

Would this be the case?

Add support of padding

It is OPTIONAL to include padding after the final frame (at the end
of the ID3 tag), making the size of all the frames together smaller
than the size given in the tag header. A possible purpose of this
padding is to allow for adding a few additional frames or enlarge
existing frames within the tag without having to rewrite the entire
file. The value of the padding bytes must be $00. A tag MUST NOT have
any padding between the frames or between the tag header and the
frames. Furthermore it MUST NOT have any padding when a tag footer is
added to the tag.

http://id3.org/id3v2.4.0-structure

Convert UTF-16 from e.g. comments into UTF-8?

Hi,

I'd like to print the comment field to the console (under Windows in this case). This doesn't really work well^^ if that field was stored with UTF-16 BOM encoding before, e.g. by MP3Tag.
Is there any way to tell the library that I get a (converted) UTF-8 string instead?
The console supports UTF-8 and with the right font this would result in some readable text :)

                comments := tag.GetFrames(tag.CommonID("Comments"))
                for _, v := range comments {
                    comment, ok := v.(id3v2.CommentFrame)
                    if !ok {
                        log.Fatal("Couldn't assert comment frame")
                    }
                    fmt.Printf("\nComment: %s", comment.Text)
                    fmt.Printf("\nComment: %s", comment.Encoding)
                }

ATM it looks like this:

Comment: �C o m m e n t
Comment: UTF-16 encoded Unicode with BOM

The original comment is really "Comment" for testing purposes...

32bit support

building on 32bit systems (respberry pi) is currently not possible.

GOARCH=arm go build
# github.com/bogem/id3v2
../../bogem/id3v2/util.go:43:10: constant 4261412864 overflows int

pre-conditions for mp3?

the code from the Readme:

package main

import (
  "github.com/bogem/id3v2"
  "log"
  "os"
)

func main() {
  tag, err := id3v2.Open("file.mp3")
  if err != nil {
    log.Fatal("Error while opening mp3 file: ", err)
  }

  pic := id3v2.NewAttachedPicture()
  pic.SetMimeType("image/jpeg")
  pic.SetDescription("Cover")
  pic.SetPictureType("Cover (front)")

  artwork, err := os.Open("artwork.jpg")
  if err != nil {
    log.Fatal("Error while opening an artwork file: ", err)
  }
  defer artwork.Close()

  if err = pic.SetPictureFromFile(artwork); err != nil {
    log.Fatal("Error while setting a picture from file: ", err)
  }
  if err = tag.SetAttachedPicture(pic); err != nil {
    log.Fatal("Error while setting a picture frame to tag: ", err)
  }

  if err = tag.Flush(); err != nil {
    log.Fatal("Error while closing a tag: ", err)
  }
}

doesn't set the image for e.g. https://raw.githubusercontent.com/mikkyang/id3-go/master/test.mp3 as file.mp3.

Add support of unsynchronization

The only purpose of unsynchronisation is to make the ID3v2 tag as
compatible as possible with existing software and hardware. There is
no use in 'unsynchronising' tags if the file is only to be processed
only by ID3v2 aware software and hardware. Unsynchronisation is only
useful with tags in MPEG 1/2 layer I, II and III, MPEG 2.5 and AAC
files.

http://id3.org/id3v2.4.0-structure

Can't parse v2.3 sizes

I'm just noticed there are a lot of tracks that have non-standard size of frames. For example, some audios have e.g. following size of APIC frame: 00000000 00000100 01001101 10101001. Last byte begins with 1, but in specification it says that frame size should always starts with 0.
id3v2 doesn't read frames which have an invalid size format. But iTunes does it correctly, so there is some way how these frames should be parsed.
I think, it should be a nice feature to read and process non-standard size formats. I tried to do it, but there were always some significant problems so I have no idea how to implement it correctly.

add COMM and USLT

you mentioned to add frames on request. For my internet recorder, I add the radio broadcast abstract both into COMM and USLT. In order to reduce the technology mix a bit and get rid of ruby it would be really neat to have a Go option.

Would you mind enabling them?

Race condition in UnknownFrame

UnknownFrame generates unique frame identifier using Rand.Int() method on shared instance of Rand in a variable uidGenerator. But this method is not thread safe.

Go race detector finds this when trying to concurrently create ID3 tags with such frames.

Clearing TCON

Hi, thank you for this nice library!

I have problems clearing (not setting) the TCON (= genre) Frame. For every written MP3 VLC as well as exiftool show "Blues". According to https://de.wikipedia.org/wiki/Liste_der_ID3v1-Genres Blues has the identifier 0 which probably is the default value when writing. I would like to set this identifier to 255 as mentioned here: https://en.wikipedia.org/wiki/ID3#Layout

I was able to set the genre to Classic Rock (id: 1) doing

tag.AddFrame("TCON", id3v2.TextFrame{
	Encoding: id3v2.ENUTF8,
	Text: "1"
})

However, setting to 255 does not work.
So far I tried:

tag.DeleteFrames("TCON")
tag.AddFrame("TCON", id3v2.TextFrame{
	Encoding: id3v2.ENUTF8,
	Text: "255"
})
tag.AddFrame("TCON", id3v2.TextFrame{
	Encoding: id3v2.ENUTF8,
	Text: "FF"
})
tag.AddFrame("TCON", id3v2.TextFrame{
	Encoding: id3v2.ENUTF8,
	Text: string(255)
})
tag.AddFrame("TCON", id3v2.PictureFrame{
	Picture: []byte{ 255 },
})

I do not know much about how id3 works so some tries might be pointless.
Do you know, how to unset the genre?

Thank you in advance!

Edit:
I wrote this to list all frames:

tag, err := id3v2.Open("test.mp3", id3v2.Options{ Parse: true })
if err != nil {
	panic(err)
}
defer tag.Close()

for k, v := range tag.AllFrames() {
	fmt.Println(k)
	for _, f := range v {
		fmt.Printf("    %T\n", f)
	}
}

I tested it with two MP3s: one for which exiftool (and VLC) shows 'Blues' as Genre and one for which none of the tools show a Genre.
For both it does not list TCON as frame. Here is the output:

The one from id3v2 where it says 'Blues':

TRCK
    id3v2.TextFrame
TPE1
    id3v2.TextFrame
TALB
    id3v2.TextFrame
TIT2
    id3v2.TextFrame
TYER
    id3v2.TextFrame
APIC
    id3v2.PictureFrame

Another one where the tools do not show a genre:

TIT2
    id3v2.TextFrame
TPE1
    id3v2.TextFrame
TPE2
    id3v2.TextFrame
TALB
    id3v2.TextFrame
TYER
    id3v2.TextFrame
TRCK
    id3v2.TextFrame

(BTW: why is there no APIC frame, if the player shows a picture?)

Default encoding issues

Let's say that I do this:

	tags.SetDefaultEncoding(id3v2.EncodingUTF16)
	tags.SetVersion(3)                           // id3v2.3.0 right?

I would expect that I'm using ID3v2.3.0, and that my default encoding is UTF-16 (with BOM). But it looks like tags.SetVersion(3) will overwrite my default choice:

id3v2/v2/tag.go

Lines 305 to 315 in e76f9ab

// SetVersion sets given ID3v2 version to tag.
// If version is less than 3 or greater than 4, then this method will do nothing.
// If tag has some frames, which are deprecated or changed in given version,
// then to your notice you can delete, change or just stay them.
func (tag *Tag) SetVersion(version byte) {
if version < 3 || version > 4 {
return
}
tag.version = version
tag.setDefaultEncodingBasedOnVersion(version)
}

id3v2/v2/tag.go

Lines 202 to 208 in e76f9ab

func (tag *Tag) setDefaultEncodingBasedOnVersion(version byte) {
if version == 4 {
tag.SetDefaultEncoding(EncodingUTF8)
} else {
tag.SetDefaultEncoding(EncodingISO)
}
}

So for now I'm just changing the order around. But I have another thought:

Why is the default for version 3 ISO-8859-1? It seems to me like it should be UTF16.

	if version == 4 {
		tag.SetDefaultEncoding(EncodingUTF8)
	} else {
		tag.SetDefaultEncoding(EncodingISO)
	}

I would like to advocate for replacing that choice with UTF-16 with a BOM. From the spec:

All Unicode strings use 16-bit unicode 2.0 (ISO/IEC 10646-1:1993, UCS-2). Unicode strings must begin with the Unicode BOM ($FF FE or $FE FF) to identify the byte order.

So it seems like both specs can handle that, and it can handle (almost) all characters. Using your library, my Garmin GPS displays mojibake because of this default when listening to Лигалайз or other Russian rappers.

Nitpick: UCS-2 is not UTF-16, but it should be close enough that nobody notices (maybe actually encode strings as UCS-2, or at least check that the length is exactly 2 bytes per character). Here's a really fun read on the subject: https://unascribed.com/b/2019-08-02-the-tragedy-of-ucs2.html

Move from Travis CI to another CI

Travis CI stopped to work on this repo and I didn't manage to migrate Travis to Github Apps. UX of Travis sucks. I just don't see id3v2 in list of repos 🤷‍♂️

I am looking into CircleCI

Add support for writing v2 tags on files with v1 tags

I've got a number of files that already have 1-2 ID3v1 tags written to them. When I try to open these files for writing with this library, I get an Unsupported Version error. It would be really nice to be able to write v2 tags to files that already have v1 tags.

How to read a tag from a .mp3 file like "Track", "Composer" or "Comment"?

I'm slowly getting into using your library (sorry, absolute golang newbie...).

What I currently don't see:
How do I read specific tags (apart from the ones that are provided by existing methods like .Artist(), .Title(), etc.)?

E.g.:
Track (TRCK)
Comments (COMM)
or
Composer (TCOM)?
and eventually:
DISCNUMBER?

Regards,
Highend

panic: encoding: rune not supported by encoding.

These three method not supporting rune, in other words, it only support ASCII characters

tag.SetArtist()
tag.SetTitle()
tag.SetAlbum()

I tried Chinese, Japanse, Korean, all panic: encoding: rune not supported by encoding.

tag.SetTitle("勇气")
tag.SetTitle("モトカレ")
tag.SetTitle("우유쏭")

Problem solved, just set the default encoding before setting the title, artist and album.

tag.SetDefaultEncoding(id3v2.EncodingUTF8)
tag.SetTitle("勇气")
tag.SetTitle("モトカレ")
tag.SetTitle("우유쏭")

"The system cannot move the file to a different disk drive" message

Hi,

after compiling your example script (I get a compile error without commenting out the "comment := id3v2.CommentFrame{...}" part but I'll eventually post another issue about that...) and placing a file.mp3
in the same directory as the final .exe file I'll get this when I execute it:

id3tagger.exe
Lena
Stardust
2017/01/16 01:05:32 Error while saving a tag: rename C:\Users\Highend\AppData\Local\Temp\150698803 file.mp3: The system cannot move the file to a different disk drive.

This happens under Windows Server 2016 and the folder for id3tagger.exe and file.mp3 is
"D:\Users\Highend\Dokumente\Go\id3tagger". The "file.mp3" doesn't get modified.

If I copy the id3tagger folder to the C:\ drive (e.g. "C:\Temp\id3tagger", I get no error message when I execute it and it modifies the .mp3 file correctly

But ofc it's necessary to be able to process files that are not on the same drive where the %TEMP% folder resides :)

So, is there a way to fix this issue?

Regards,
Highend

UTF16 encoded text data might get damaged when parsing / decoding.

It appears text data, when UTF16 LE encoded, won't get properly parsed for some frame types.

Consider the following user defined text frame:

00000000: 54 58 58 58 00 00 00 25 00 00 01 ff fe 44 00 49 TXXX...%.....D.I
00000010: 00 53 00 43 00 49 00 44 00 00 00 ff fe 36 00 33 .S.C.I.D.....6.3
00000020: 00 30 00 41 00 43 00 30 00 30 00 38 00 00 00 .0.A.C.0.0.8...

Encoding key is 0x01 which is UTF16 with BOM. The description of that TXXX is "DISCID" and its trailing 'D' is encoded by [0x44, 0x00].

The problem I noticed with UTF16 LE encoded, user defined text frame contents that for the description, the trailing character may get garbled, the following value then starts at the wrong offset, adding garbage at the beginning.

readTillDelims will seek through that frame's byte stream and will try to detect the expected two trailing zero bytes without adding them to the respective frame-data - in this case the description.
The intend is to stop at the delimiter [0x00, 0x00], we do however incorrectly consider that last byte of 'D' rune data which happens to be 0x00 as being the first byte of the delimiter. We end up with truncated UTF16 data.

The user defined text frame parser will then try to read the value starting one byte too early, adding a prefixing 0x00.

The following output is what I get based on id3v2's functionality;

TXXX: DISCI�: ��630AC008 (encoding: 1)

This nasty workaround -- not a fix!

diff --git a/user_defined_text_frame.go b/user_defined_text_frame.go
index f6cd917..ed722ba 100644
--- a/user_defined_text_frame.go
+++ b/user_defined_text_frame.go
@@ -30,6 +30,7 @@ func (udtf UserDefinedTextFrame) WriteTo(w io.Writer) (n int64, err error) {
 func parseUserDefinedTextFrame(br *bufReader) (Framer, error) {
        encoding := getEncoding(br.ReadByte())
        description := br.ReadText(encoding)
+       description = append(description, br.ReadByte())

        if br.Err() != nil {
                return nil, br.Err()

Then produces the correct output:

TXXX: DISCID: 630AC008 (encoding: 1)

A proper fix needs to be located in readTillDelims, I guess.

Missing /v2 folder

When trying to bump to v2.0.0, we see the following

github.com/bogem/id3v2: github.com/bogem/id3v2/[email protected]: go.mod has non-.../v2 module path "github.com/bogem/id3v2" (and .../v2/go.mod does not exist) at revision v2.0.0

Broken id3v2.3 tag after resaving

Package version: github.com/bogem/id3v2/v2 v2.1.4

Problem:

ID3v2.3 tags are somewhat broken after resaving them without any modifications. In kid3 tags are readable, but album art doesn't render. Completely unreadable on my Walkman NW-A55 (see screenshots).

walkman:

kid3:

Truncated in.mp3, github doesn't allow mp3 (and archived mp3) attachments: https://files.catbox.moe/dh1w34.mp3.

Run https://play.golang.com/p/aONev45lE-K to create out.mp3

$ id3v2 -l in.mp3 
id3v2 tag info for in.mp3:
TIT2 (Title/songname/content description): Thunderstruck
TPE1 (Lead performer(s)/Soloist(s)): AC/DC
TALB (Album/Movie/Show title): The Razors Edge
TRCK (Track number/Position in set): 1
TCON (Content type): Hard Rock (79)
APIC (Attached picture): ()[, 3]: image/jpg, 8890 bytes
TYER (Year): 1990
in.mp3: No ID3v1 tag

$ id3v2 -l out.mp3 
id3v2 tag info for out.mp3:
TALB (Album/Movie/Show title): The Razors Edge
TRCK (Track number/Position in set): 1
TCON (Content type): Hard Rock (79)
TYER (Year): 1990
TIT2 (Title/songname/content description): Thunderstruck
TPE1 (Lead performer(s)/Soloist(s)): AC/DC
APIC (Attached picture): ()[, 3]: image/jpg, 8891 bytes
out.mp3: No ID3v1 tag

$ id3v2 --version
id3v2 0.1.12
Uses id3lib-3.8.3

change album image

hello
thanks for create this lib

how i can change mp3 file album art picture?
please help me
:)

this code not work

tag, err := id3v2.Open("public/music/Siavash.mp3", id3v2.Options{Parse: true})
	if tag == nil || err != nil {
		Core.Error400(c, "Error while opening mp3 file: ","")
	}
	defer tag.Close()


	artwork, err := ioutil.ReadFile("public/test.jpg")
	if err != nil {
		Core.Error400(c, "Error while opening mp3 file: ","")
	}

	pic := id3v2.PictureFrame{
		Encoding:    id3v2.EncodingUTF8,
		MimeType:    "image/jpeg",
		PictureType: id3v2.PTBackCover,
		Description: "Front cover",
		Picture:     artwork,
	}

	tag.AddAttachedPicture(pic)

UTF-16 Encoding Issue

Thanks for a great library! I appreciate your hard work on it.

Like in my last issue (which is a distinct encoding issue), I'm running into more trouble with encoding.

Note that I'm calling SetVersion prior to SetDefaultEncoding because of #85

tags.SetVersion(3)                           // id3v2.3.0
tags.SetDefaultEncoding(id3v2.EncodingUTF16) // UTf-8 would be cool, but 2.3 doesn't support it
tags.SetTitle("Мир! Вашему! Дому! (K-DEF Remix)")

When I run exiftool -v3 -l myfile.mp3, it indicates this for TIT2

  |     009c: 01 fe ff 04 1c 04 38 04 40 00 21 00 20 04 12 04 [......8.@.!. ...]
  |     00ac: 30 04 48 04 35 04 3c 04 43 00 21 00 20 04 14 04 [0.H.5.<.C.!. ...]
  |     00bc: 3e 04 3c 04 43 00 21 00 20 00 28 00 4b 00 2d 00 [>.<.C.!. .(.K.-.]
  |     00cc: 44 00 45 00 46 00 20 00 52 00 65 00 6d 00 69 00 [D.E.F. .R.e.m.i.]
  |     00dc: 78 00 29 00 00 00                               [x.)...]

I see that the first 0x01 indicates that we're dealing with UCS-2 with a BOM. Let's disregard that. Then the BOM says it's big endian (fine). I edited out the 0x01 tag in a text editor then pasted it into an interactive python session:

>>> hexstr="""
... fe ff 04 1c 04 38 04 40 00 21 00 20 04 12 04
... 30 04 48 04 35 04 3c 04 43 00 21 00 20 04 14 04
... 3e 04 3c 04 43 00 21 00 20 00 28 00 4b 00 2d 00
... 44 00 45 00 46 00 20 00 52 00 65 00 6d 00 69 00
... 78 00 29 00 00 00
... """
>>> raw = bytes([int(x, 16) for x in hexstr.replace("\n"," ").split(" ") if x != ""])
>>> raw.decode("utf16")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-16-be' codec can't decode byte 0x00 in position 68: truncated data
>>> len(raw)
69

Kinda hard to get to the standard at the moment (it's been like this for a while):

image

Here's a copy: https://web.archive.org/web/20190207033339/https://id3.org/id3v2.3.0#ID3v2_frame_overview

At least in 2.3.0, it does say that you're supposed to have a null terminator (so, 00 00 for UTF-16/UCS-2). I don't think that you can have a UCS-2 string with an odd number of characters. So it seems like you're appending an additional null character, and the length becomes not a multiple of 2, which makes it not legit UCS-2 or UTF-16.

I'm pasting a screenshot rather than copying the text of the output of id3edit because I think the colors are cool:

image

So that thinks it's bad too. If I just remove the one extra null byte, python seems happy with it (and other tools):

>>> hexstr = """
... fe ff 04 1c 04 38 04 40 00 21 00 20 04 12 04
... 30 04 48 04 35 04 3c 04 43 00 21 00 20 04 14 04
... 3e 04 3c 04 43 00 21 00 20 00 28 00 4b 00 2d 00
... 44 00 45 00 46 00 20 00 52 00 65 00 6d 00 69 00
... 78 00 29 00 00
... """
>>> raw = bytes([int(x, 16) for x in hexstr.replace("\n"," ").split(" ") if x != ""])
>>> raw.decode("utf16")
'Мир! Вашему! Дому! (K-DEF Remix)\x00'

I think this has something to do with other PRs that were meant to fix things for other encodings maybe in 2.4...

Add support of footer

To speed up the process of locating an ID3v2 tag when searching from
the end of a file, a footer can be added to the tag. It is REQUIRED
to add a footer to an appended tag, i.e. a tag located after all
audio data. The footer is a copy of the header, but with a different
identifier.

http://id3.org/id3v2.4.0-structure

Need examples of setting attached pictures of a mp3 file

I use the following code, no error but no effect too (go version go1.21.0 darwin/amd64).

package main

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/bogem/id3v2"
)

func main() {
	curDir := GetCurDir()
	mp3File := filepath.Join(curDir, "test.mp3")
	coverFile := filepath.Join(curDir, "cover.jpg")
	SetMp3Cover(mp3File, coverFile)
}

// SetMp3Cover Sets the attached picture of a mp3 file
func SetMp3Cover(mp3File string, coverFile string) {
	tag, err := id3v2.Open(mp3File, id3v2.Options{Parse: true})
	if err != nil {
		panic(err)
	}
	defer tag.Close()

	imageData, err := os.ReadFile(coverFile)
	if err != nil {
		panic(err)
	}

	tag.AddAttachedPicture(id3v2.PictureFrame{
		PictureType: id3v2.PTFrontCover,
		MimeType:    "image/jpeg",
		Description: "Front Cover",
		Picture:     imageData,
	})

	if err := tag.Save(); err != nil {
		panic(err)
	}
	fmt.Println("Set mp3 Cover successfully.")
}

// getSourceCodeDir Get the dir where the .go source code file located
func getSourceCodeDir() string {
	var abPath string
	_, filename, _, ok := runtime.Caller(0)
	if ok {
		abPath = path.Dir(filename)
	}
	return abPath
}

// GetCurDir Get the current dir
func GetCurDir() string {
	exePath, err := os.Executable()
	if err != nil {
		panic(err)
	}
	curDir := filepath.Dir(exePath)

	// if "go run .", the exePath will be as bellow
	// exePath: /var/folders/b8/m8lcy7wx235d02cwqsjphzjm0000gn/T/go-build1874405170/b001/exe/ncm2mp3

	// if "go run .", use the dir when .go source code file located
	// instead of the dir where the executable file located
	if strings.Contains(curDir, "go-build") {
		curDir = getSourceCodeDir()
	}
	return curDir
}

BTW, it seems I can set many Pictures? not only one?

null byte in tags

When using id3v2.ParseReader(reader, id3v2.Options{Parse: true}) on (all?) mp3 downloaded from https://www.jamendo.com, extracted artist, album, title, ... contains a null byte at the end of the string.

I'm using strings.TrimRight(s,"\x00") as a workaround but I think that should be done inside id3v2.decodeText

tag.AddAttachedPicture() not working

The album art doesn't seem to get added in the music file. Here path is a valid mp3 file and metadata.Image is a valid jpeg image byte array.

	tag, err := id3v2.Open(path, id3v2.Options{Parse: true})
	checkErr(err)
	defer tag.Close()

	pic := id3v2.PictureFrame{
		Encoding:    id3v2.EncodingUTF8,
		MimeType:    "image/jpeg",
		PictureType: id3v2.PTFrontCover,
		Description: "Front cover",
		Picture:     metadata.Image,
	}
	tag.AddAttachedPicture(pic)
	if err = tag.Save(); err != nil {
		log.Fatal("Error : ", err)
	}

Add support of flags

The version is followed by the ID3v2 flags field, of which currently
four flags are used.

a - Unsynchronisation

 Bit 7 in the 'ID3v2 flags' indicates whether or not
 unsynchronisation is applied on all frames (see section 6.1 for
 details); a set bit indicates usage.

b - Extended header

 The second bit (bit 6) indicates whether or not the header is
 followed by an extended header. The extended header is described in
 section 3.2. A set bit indicates the presence of an extended
 header.

c - Experimental indicator

 The third bit (bit 5) is used as an 'experimental indicator'. This
 flag SHALL always be set when the tag is in an experimental stage.

d - Footer present

 Bit 4 indicates that a footer (section 3.4) is present at the very
 end of the tag. A set bit indicates the presence of a footer.

http://id3.org/id3v2.4.0-structure

Are chapters supported?

I'm currently working with some podcasts and I'm specifically trying to pull out their chapters. From what I can tell a CHAP (chapter) frame should be returned in an AllFrames() call, but only a TIT2 frame is being returned. Here's my code:

func main() {
	mp3, err := id3v2.Open(os.Args[1], id3v2.Options{Parse: true})
	if err != nil {
		panic(err)
	}
	defer mp3.Close()

	for key, frame := range mp3.AllFrames() {
		fmt.Println(key)
		fmt.Println(frame)
	}
}

Is this the correct method or does the library not support chapters? Also, here's the specific file I'm working with:

https://audio.fireside.fm/podcasts/audio/b/b44de5fa-47c1-4e94-bf9e-c72f8d1c8f5d/episodes/2/2bedf85e-f897-41e5-b498-243b1dea12b4/2bedf85e-f897-41e5-b498-243b1dea12b4.mp3

Edit: I think I should be looking for CHAP frames and not CTOC frames.

Version() always returns 4?

Forget about it, Version() works fine and reports 3 / 4 depending on which idv3 tag is present. Everythings fine but I can't delete the issue... sigh

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.