mikkyang / id3-go Goto Github PK
View Code? Open in Web Editor NEWID3 library for Go
License: MIT License
ID3 library for Go
License: MIT License
Currently, opening a read-only file fails. This is because id3.Open
opens the files in read write mode (os.OpenFile(name, os.O_RDWR, 0666)
).
One thought I had would be to fallback on read-only mode if readwrite mode fails. Then we could just throw an error if someone tries to write to the object. (is it still considered a breaking change if it changes when an error is given?)
Probably the least offensive change would be adding another function, maybe called Read
, which would be just like Load, except with os.O_RDONLY
. Would that be more confusing?
Hello there,
I used to do active development on a go 1.5.X setup on my macbook. I recently upgraded to go 1.7.1 for darwin and I am seeing runtime errors when trying to run some code that uses id3-go
. The following function is fairly simple, it takes in a file extension as a string, and a path as a string, and recursively walks through the directory tree starting at the path, and returns a slice of SongFile{} representing an MP3 file.
type SongFile struct {
RawArtist string `json:"raw_artist"`
RawAlbum string `json:"raw_album"`
RawTitle string `json:"raw_title"`
Path string `json:"path"`
FileName string `json:"filename"`
AlbumArt string `json"album_art"`
}
func checkExt(ext string, path string) []SongFile {
var files []SongFile
filepath.Walk(path, func(p string, f os.FileInfo, _ error) error {
if !f.IsDir() {
r, err := regexp.MatchString(ext, f.Name())
if err == nil && r {
mp3File, err := id3.Open(p)
defer mp3File.Close()
if err == nil {
// Sometimes the strings come out bad with null bytes, so I do a string replace
artist := strings.Replace(mp3File.Artist(), "\u0000", "", -1)
album := strings.Replace(mp3File.Album(), "\u0000", "", -1)
title := strings.Replace(mp3File.Title(), "\u0000", "", -1)
if artist == "" {
artist = "Unknown Artist"
}
if album == "" {
album = "Unknown Album"
}
if title == "" {
title = "Unknown Title"
}
sf := SongFile{Path: p, FileName: f.Name(), RawArtist: artist, RawAlbum: album, RawTitle: title}
files = append(files, sf)
} else {
fmt.Println(err.Error())
fmt.Println("! " + p + " !")
}
}
}
return nil
})
return files
}
The exact stack trace I am seeing when I call this function is as follows
panic: runtime error: cgo argument has Go pointer to Go pointer
goroutine 1 [running]:
panic(0x428d620, 0xc42027e5f0)
/usr/local/go/src/runtime/panic.go:500 +0x1a1
github.com/djimenez/iconv-go.(*Converter).Convert(0xc420019360, 0xc42027e5a8, 0x5, 0x8, 0xc42027e5b0, 0xa, 0xa, 0x40415bb, 0x10, 0x4277ca0, ...)
/Users/david.shure/go/src/github.com/djimenez/iconv-go/converter.go:83 +0x55c
github.com/djimenez/iconv-go.(*Converter).ConvertString(0xc420019360, 0xc4204b34b8, 0x5, 0x5, 0xc4204b34b8, 0x5, 0x4013418)
/Users/david.shure/go/src/github.com/djimenez/iconv-go/converter.go:123 +0x184
github.com/mikkyang/id3-go/encodedbytes.(*Reader).ReadRestString(0xc4204b3528, 0xc42000cf00, 0x6, 0x0, 0x0, 0x6)
/Users/david.shure/go/src/github.com/mikkyang/id3-go/encodedbytes/reader.go:69 +0xaa
github.com/mikkyang/id3-go/v2.ParseTextFrame(0x42daac4, 0x4, 0x42e4613, 0x22, 0x42ff9b0, 0x600000000, 0x0, 0xc42027e59a, 0x6, 0x6, ...)
/Users/david.shure/go/src/github.com/mikkyang/id3-go/v2/frame.go:233 +0x152
github.com/mikkyang/id3-go/v2.ParseV23Frame(0x4401540, 0xc420472118, 0xc420472118, 0x4401540)
/Users/david.shure/go/src/github.com/mikkyang/id3-go/v2/id3v23.go:150 +0x389
github.com/mikkyang/id3-go/v2.ParseTag(0x4404040, 0xc420472118, 0x2)
/Users/david.shure/go/src/github.com/mikkyang/id3-go/v2/id3v2.go:74 +0x11b
github.com/mikkyang/id3-go.Open(0xc42011ebd0, 0x2f, 0xc42011ebf1, 0xe, 0x476e701)
/Users/david.shure/go/src/github.com/mikkyang/id3-go/id3.go:58 +0xd0
main.checkExt.func1(0xc42011ebd0, 0x2f, 0x4406680, 0xc42025edd0, 0x0, 0x0, 0x0, 0x0)
/Users/david.shure/stash/griffon/server/griffon.go:94 +0x152
path/filepath.walk(0xc42011ebd0, 0x2f, 0x4406680, 0xc42025edd0, 0xc4200cc8e0, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:351 +0x81
path/filepath.walk(0xc42030e8a0, 0x20, 0x4406680, 0xc42025ed00, 0xc4200cc8e0, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:376 +0x344
path/filepath.walk(0xc42030e760, 0x1e, 0x4406680, 0xc42025eb60, 0xc4200cc8e0, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:376 +0x344
path/filepath.walk(0xc4202a3600, 0x1c, 0x4406680, 0xc420076820, 0xc4200cc8e0, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:376 +0x344
path/filepath.walk(0x7fff5fbff58c, 0x13, 0x4406680, 0xc420077a00, 0xc4200cc8e0, 0x0, 0x1)
/usr/local/go/src/path/filepath/path.go:376 +0x344
path/filepath.Walk(0x7fff5fbff58c, 0x13, 0xc4200cc8e0, 0x1c, 0x42ff9b0)
/usr/local/go/src/path/filepath/path.go:398 +0xd5
main.checkExt(0x42dad2d, 0x5, 0x7fff5fbff58c, 0x13, 0x8, 0x42ff9b8, 0x42da786)
/Users/david.shure/stash/griffon/server/griffon.go:121 +0xce
main.main()
/Users/david.shure/stash/griffon/server/griffon.go:139 +0x153
After I first encountered this issue, I deleted the source code of id3-go from my $GOPATH, and re-ran go get github.com/mikkyang/id3-go
but this hasn't fixed the issue. Please let me know if you need any additional information from my environment. Any information on this would be greatly appreciated. Thanks!
Add support for text based (T000-TZZZ) frames in version 2.4
Support non-extended v1 tags
I have played around with the library and since there's no default way (at least I didn't see one) of setting a cover-image for an MP3 file, I put this into the frame.go
-file (since it uses internal variables of ImageFrame
):
func NewImageFrame(ft FrameType, mime_type string, image_data []byte) *ImageFrame{
data_frame := NewDataFrame(ft, image_data)
data_frame.size += 1 // Add space for the pictureType (do we need this??)
image_frame := &ImageFrame{
DataFrame: *data_frame, // DataFrame header
pictureType: byte(0x03), // Image Type, in this case Front Cover (http://id3.org/id3v2.3.0#Attached_picture)
}
image_frame.SetEncoding("UTF-8")
image_frame.SetMIMEType(mime_type)
return image_frame
}
However, this does not work. Am I missing something or is there an easier way?
Also, when I use this image_frame := mp3.Tagger.Frame("APIC")
to see if the file already has a cover-art (which it does), it returns nil
.
Comment frames don't seem to be parsed back the same way they were written:
package main
import (
"fmt"
id3 "github.com/mikkyang/id3-go"
"github.com/mikkyang/id3-go/v2"
"io/ioutil"
"os"
)
func main() {
var (
tempfile *os.File
err error
f *id3.File
tagger *v2.Tag
ft v2.FrameType
utextFrame *v2.UnsynchTextFrame
parsedFrame v2.Framer
)
tempfile, err = ioutil.TempFile("", "id3v2")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
tagger = v2.NewTag(3)
ft = v2.V23FrameTypeMap["COMM"]
utextFrame = v2.NewUnsynchTextFrame(ft, "Comment", "Foo")
tagger.AddFrames(utextFrame)
_, err = tempfile.Write(tagger.Bytes())
tempfile.Close()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
f, err = id3.Open(tempfile.Name())
if err != nil {
fmt.Println(err)
os.Exit(1)
}
parsedFrame = f.Frame("COMM")
fmt.Printf("=== Expected:\n%v\n=== Got:\n%v\n", utextFrame, parsedFrame)
}
Outputs:
=== Expected:
eng Comment:
Foo
=== Got:
eng :
CommentFoo
Hi,
Please advise how to pull bitrate value?
Also, is "id3-go" choosing ID3 version automatically if I import just "github.com/mikkyang/id3-go"?
Thank you!
I noticed this functionality doesn't exist. Is this something you'd be interested in?
Thanks.
When i try to do a go get github.com/mikkyang/id3-go
on windows it fails. I just install gcc with MinGW and it is a fresh install so i doubt it come from this part.
go get -x github.com/mikkyang/id3-go
WORK=C:\Users\admin\AppData\Local\Temp\go-build230481106
mkdir -p $WORK\github.com\djimenez\iconv-go\_obj\
mkdir -p $WORK\github.com\djimenez\
cd D:\Pro\go\src\github.com\djimenez\iconv-go
CGO_LDFLAGS="-g" "-O2" "-liconv" "C:\\Go\\pkg\\tool\\windows_amd64\\cgo.exe" -objdir "C:\\Users\\admin\\AppData
\\Local\\Temp\\go-build230481106\\github.com\\djimenez\\iconv-go\\_obj\\" -importpath github.com/djimenez/iconv
-go -- -I "C:\\Users\\admin\\AppData\\Local\\Temp\\go-build230481106\\github.com\\djimenez\\iconv-go\\_obj\\" c
onverter.go
go build github.com/djimenez/iconv-go: C:\Go\pkg\tool\windows_amd64\cgo.exe: exit status 2
Do not rewrite the entire file if there is sufficient padding available
Sometimes id3-go tells me it can't open an mp3 file. From studying the source I believe this must be because it can't find a idv1 nor idv2 tag header (or footer). Is it possible to add id3v2 headers where none existed before?
I think this library must also support it, because it's necessary for some projects (for example, for mine)
Hopefully I will try to make it and contribute on this weekend
This component somehow alters mp3 file duration in a really strange format. On my phone, the result is songs lasting 40 minutes, on VLC (on desktop) they have reasonable duration, but lasting time decrease three times faster.
I use your code this way:
track_mp3, err := id3.Open((*arg_folder) + "/" + track.FilenameTemp + track.FilenameExt)
if err != nil {
logger.Fatal("Something bad happened while opening " + track.Filename + ": " + err.Error() + ".")
} else {
track_mp3.SetTitle(track.Title)
track_mp3.SetArtist(track.Artist)
track_mp3.SetAlbum(track.Album)
track_mp3.Close()
}
func tagIt(filename string, title string, album string, artist string, genre string) {
mp3File, err := id3.Open(filename)
if err != nil {
panic(err)
}
defer mp3File.Close()
mp3File.SetArtist(artist + "x")
mp3File.SetAlbum("ๅๅ")
mp3File.SetTitle(title + "x")
mp3File.SetGenre(genre + "x")
}
pure English works, but Chinese not.
Should I convert the Chinese string to some other encoding first?
First of: thanks for an excellently designed library, the code looks very clean.
Now for the bug report: on some files, trying to Open
/Close
a file multiple times (without even changing the tags) causes a panic after a few tries (3 in this case, I believe).
One can reproduce like this:
go test -v
in the main folderWhat it does? Downloads 2 mp3 files (if they don't exist yet) and attempts to tag them with the correct artist and title (yet it doesn't tag if those tags already exist). For the first mp3 file (https://soundcloud.com/purple_oslo/sleepers) this if fine, no matter how many times I tag. The test succeeds. However for the second file (https://soundcloud.com/justinfaustmusic/justin-faust-la-fete-free) this is not the case.
You can probably also reproduce this by downloading the files yourself and using id3.Open()/id3.Close() in succession (that's basically what my test does).
Oh, and another small question: sometimes id3-go tells me it can't open the file. I think this is because the mp3 file doesn't contain any tags at all. How do I add id3v2 headers where none existed before?
EDIT: it looks like the panic is related to issue #10. However, this probably shouldn't be papered over, but fixed at the root.
I'm writing a program that indexes a music library. I'm experimenting with making some operations concurrent to improve performance for very large libraries.
id3-go
is panicking when I have several goroutines calling id3.Open()
at the same time [1].
I'm happy to put in a pull request to fix this, but first I'd like to get your input on which approach to take.
I think the issue is coming from reader.go#L69, in ReadRestString()
:
return Decoders[encoding].ConvertString(string(b))
Decoders
are defined at the package level as vars (see here. I guess they have some kind of internal state, so they're not happy when multiple goroutines call ConvertString()
.
I can think of two ways to fix this:
Decoders
and Encoders
I think the second one is cleaner and simpler, and involves less contention - but I don't have any context on the cost of creating them.
I've made the second change locally and id3-go is still blazing fast.
Let me know. Keen to put in a PR if I get your feedback.
[1]
panic: runtime error: slice bounds out of range
goroutine 19 [running]:
github.com/djimenez/iconv-go.(*Converter).ConvertString(0xc4200705d0, 0xc4200369b8, 0x10, 0x10, 0xc4200369b8, 0x10, 0x0)
/home/cera/src/vir/backend/vendor/src/github.com/djimenez/iconv-go/converter.go:155 +0x513
github.com/mikkyang/id3-go/encodedbytes.(*Reader).ReadRestString(0xc420036a90, 0x0, 0xc42000c980, 0x11, 0x11, 0x11)
/home/cera/src/vir/backend/vendor/src/github.com/mikkyang/id3-go/encodedbytes/reader.go:69 +0x14f
github.com/mikkyang/id3-go/v2.ParseTextFrame(0x5d9829, 0x4, 0x5de24a, 0x1c, 0x5e2100, 0x1100000000, 0x0, 0xc42000c980, 0x11, 0x11, ...)
/home/cera/src/vir/backend/vendor/src/github.com/mikkyang/id3-go/v2/frame.go:233 +0x266
github.com/mikkyang/id3-go/v2.ParseV23Frame(0x88ff00, 0xc42000e028, 0xc42000e028, 0x88ff00)
/home/cera/src/vir/backend/vendor/src/github.com/mikkyang/id3-go/v2/id3v23.go:150 +0x43b
github.com/mikkyang/id3-go/v2.ParseTag(0x890880, 0xc42000e028, 0x2)
/home/cera/src/vir/backend/vendor/src/github.com/mikkyang/id3-go/v2/id3v2.go:74 +0x19e
github.com/mikkyang/id3-go.Open(0xc4200b4700, 0x3f, 0x123dea0, 0x0, 0x0)
/home/cera/src/vir/backend/vendor/src/github.com/mikkyang/id3-go/id3.go:58 +0x140
vir.loadTrackMetadataFromFile(0xc4200b4700, 0x3f, 0x0, 0x0, 0x0)
/home/cera/src/vir/backend/src/vir/metadata.go:22 +0x82
vir.loadMetadataConcurrent.func1(0xc420070770, 0xc42006e120, 0xc42006e180)
/home/cera/src/vir/backend/src/vir/index.go:192 +0x14e
created by vir.loadMetadataConcurrent
/home/cera/src/vir/backend/src/vir/index.go:210 +0xfb
I'm attempting to add a SetComment
method so I can write comment fields on v2 files. This is what I have so far and it almost works:
id3v2.go
func (t *Tag) SetComment(text string) {
t.setTextFrameText(t.commonMap["Comments"], text)
}
then in testing:
file, err := id3.Open(filePath, false)
if err != nil {
panic(err)
}
file.SetComment("this is my test comment with absolutely no special characters woohoo")
err = file.Close()
if err != nil {
panic(err)
}
The ID3 spec defines the Comments frame as such:
<Header for 'Comment', ID: "COMM">
Text encoding $xx
Language $xx xx xx
Short content descrip. <text string according to encoding> $00 (00)
The actual text <full text string according to encoding>
It seems like the comment text is actually ending up in the "Short content description" portion instead of "the actual text".
I'd love any tips and I'll be sure to create a PR and share back my improvements when I get this working
Hey,
I really like your library -- it seems quite complete and well thought out, and in pure Go. I'm working on a little pet project to download and format soundcloud files for personal use -- and one thing that'd be cool is to be able to embed album art directly into the files. This is more valuable than normal, since most music on soundcloud is pretty indie, or remixes, so standard album artwork lookup doesn't tend to work.
From what I can tell, you already support ImageFrames to some degree (at least for basic parsing). Assuming I find the motivation, would you be willing to accept a patch to basically just add a NewImageFrame function that takes a path to an image and jams it in?
(I'm not really an id3 expert at this point, but it doesn't look too crazy to implement.)
panic: runtime error: cgo argument has Go pointer to Go pointer
goroutine 1 [running]:
panic(0x797660, 0xc8203e2070)
/opt/go/src/runtime/panic.go:464 +0x3e6
github.com/djimenez/iconv-go.(*Converter).Convert(0xc820070f50, 0xc8203e2020, 0x4, 0x8, 0xc8203e2028, 0x8, 0x8, 0x0, 0x0, 0x0, ...)
fakePath/dependencies/src/github.com/djimenez/iconv-go/converter.go:83 +0x2a4
github.com/djimenez/iconv-go.(*Converter).ConvertString(0xc820070f50, 0xc8203d6408, 0x4, 0x0, 0x0, 0x0, 0x0)
fakePath/dependencies/src/github.com/djimenez/iconv-go/converter.go:123 +0x1f2
github.com/mikkyang/id3-go/encodedbytes.EncodedDiff(0x80d403, 0xc8203d6408, 0x4, 0x450400, 0xc8203d6408, 0x4, 0xc820000180, 0x0, 0x0)
fakePath/dependencies/src/github.com/mikkyang/id3-go/encodedbytes/util.go:141 +0x69
github.com/mikkyang/id3-go/v2.(*TextFrame).SetEncoding(0xc820378000, 0x80d478, 0x5, 0x0, 0x0)
fakePath/dependencies/src/github.com/mikkyang/id3-go/v2/frame.go:250 +0x170
github.com/mikkyang/id3-go/v2.(*Tag).setTextFrameText(0xc820288280, 0x80d220, 0x4, 0x85cb20, 0x1c, 0x8b5cf0, 0xc8203d6408, 0x4)
fakePath/dependencies/src/github.com/mikkyang/id3-go/v2/id3v2.go:286 +0x510
github.com/mikkyang/id3-go/v2.(*Tag).SetArtist(0xc820288280, 0xc8203d6408, 0x4)
fakePath/dependencies/src/github.com/mikkyang/id3-go/v2/id3v2.go:247 +0xd5
modules/downloader.DownloadAlbum(0x7fffeb3c45f3, 0x19, 0xc82006c380, 0x0, 0x0, 0x0, 0x0)
fakePath/src/modules/someMyCode.go:68 +0x114c
main.main()
I would like to read the Popularimeter frame from a file with id3-go.
This is how the frame looks like when printing with mutagen-inspect
:
$ mutagen-inspect samples/with_popm.mp3 | grep POPM
[email protected]=0 255/255
I would like to read the value (255/255) from the file. As I could not find any documentation, my naive approach is:
popFrame := mp3File.Frame("POPM")
log.Println(popFrame.String())
But when I run this (on a file with and also without a popularimeter tag), I get segmentation faults:
$ ./popm-read samples/with_popm.mp3
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x4ac302]
goroutine 1 [running]:
main.main()
/home/ifischer/src/rivamp/rivamp-dist/id3-go-popm-example/main.go:21 +0xd2
$ ./popm-read samples/without_popm.mp3
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x4ac302]
goroutine 1 [running]:
main.main()
/home/ifischer/src/rivamp/rivamp-dist/id3-go-popm-example/main.go:21 +0xd2
I setup a sample repository containing two sample files (one with, one without popularimeter frame) here: https://github.com/ifischer/id3-go-popm-example
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.