taglib

package module
v0.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 8, 2026 License: LGPL-2.1 Imports: 12 Imported by: 11

README

go-taglib

This project is a Go library for reading and writing audio metadata tags. By packaging an embedded Wasm binary, the library needs no external dependencies or CGo. Meaning easy static builds and cross compilation.

Current bundled TagLib version is v2.1.1.

To reproduce or verify the bundled binary, see the attestations.

godoc

Features

  • Read and write metadata tags for audio files, including support for multi-valued tags.
  • Read and write embedded images (album artwork) from audio files.
  • Retrieve audio properties such as length, bitrate, sample rate, and channels.
  • Supports multiple audio formats including MP3, FLAC, M4A, WAV, OGG, WMA, and more.
  • Safe for concurrent use
  • Reasonably fast

Usage

Add the library to your project with go get go.senan.xyz/taglib@latest

Reading metadata
func main() {
    tags, err := taglib.ReadTags("path/to/audiofile.mp3")
    // check(err)

    fmt.Printf("tags: %v\n", tags) // map[string][]string

    fmt.Printf("AlbumArtist: %q\n", tags[taglib.AlbumArtist])
    fmt.Printf("Album: %q\n", tags[taglib.Album])
    fmt.Printf("TrackNumber: %q\n", tags[taglib.TrackNumber])
}
Writing metadata
func main() {
    err := taglib.WriteTags("path/to/audiofile.mp3", map[string][]string{
        // Multi-valued tags allowed
        taglib.AlbumArtist:   {"David Byrne", "Brian Eno"},
        taglib.Album:         {"My Life in the Bush of Ghosts"},
        taglib.TrackNumber:   {"1"},

        // Non-standard allowed too
        "ALBUMARTIST_CREDIT": {"Brian Eno & David Byrne"},
    }, 0)
    // check(err)
}
Options for writing

The behaviour of writing can be configured with some bitset flags

The options are

  • Clear which indicates that all existing tags not present in the new map should be removed

The options can be combined the with the bitwise OR operator (|)

    taglib.WriteTags(path, tags, taglib.Clear)
    taglib.WriteTags(path, tags, 0)
Reading properties
func main() {
    properties, err := taglib.ReadProperties("path/to/audiofile.mp3")
    // check(err)

    fmt.Printf("Length: %v\n", properties.Length)
    fmt.Printf("Bitrate: %d\n", properties.Bitrate)
    fmt.Printf("SampleRate: %d\n", properties.SampleRate)
    fmt.Printf("Channels: %d\n", properties.Channels)

    // Image metadata (without reading actual image data)
    for i, img := range properties.Images {
        fmt.Printf("Image %d - Type: %s, Description: %s, MIME type: %s\n",
            i, img.Type, img.Description, img.MIMEType)
    }
}
Reading embedded images
import (
    "bytes"
    "image"

    _ "image/gif"
    _ "image/jpeg"
    _ "image/png"

    // ...
)

func main() {
    // Read first image (index 0)
    imageBytes, err := taglib.ReadImage("path/to/audiofile.mp3")
    // check(err)

    if imageBytes == nil {
        fmt.Printf("File contains no image")
        return
    }

    img, format, err := image.Decode(bytes.NewReader(imageBytes))
    // check(err)

    fmt.Printf("format: %q\n", format)

    bounds := img.Bounds()
    fmt.Printf("width: %d\n", bounds.Dx())
    fmt.Printf("height: %d\n", bounds.Dy())

    // Read a specific image by index
    backCover, err := taglib.ReadImageOptions("path/to/audiofile.mp3", 1)
    // check(err)
}
Writing embedded images
func main() {
    var imageBytes []byte // Some image data, from somewhere

    // Write as front cover with auto-detected MIME type
    err = taglib.WriteImage("path/to/audiofile.mp3", imageBytes)
    // check(err)

    // Write with custom options
    err = taglib.WriteImageOptions(
        "path/to/audiofile.mp3",
        imageBytes,
        0,                  // replaces image at index; use higher index to append
        "Back Cover",       // picture type
        "Back artwork",     // description
        "image/jpeg",       // MIME type
    )
    // check(err)
}

Manually Building and Using the Wasm Binary

The binary is already included in the package. However if you want to manually build and override it, you can with WASI SDK and Go build flags

  1. Clone this repository and Git submodules

    $ git clone "https://github.com/sentriz/go-taglib.git" --recursive
    $ cd go-taglib
    

[!NOTE] Make sure to use the --recursive flag, without it there will be no TagLib submodule to build with

  1. Generate the Wasm binary:

    $ ./build/build-docker.sh
    $ # taglib.wasm created
    

    Or, to build without Docker install WASI SDK and Binaryen, then

    $ ./build/build.sh /path/to/wasi-sdk # eg /opt/wasi-sdk
    $ # taglib.wasm created
    
  2. Use the new binary in your project

    $ CGO_ENABLED=0 go build -ldflags="-X 'go.senan.xyz/taglib.binaryPath=/path/to/taglib.wasm'" ./your/project/...
    
Performance

In this example, tracks are read on average in 0.3 ms, and written in 1.85 ms

goos: linux
goarch: amd64
pkg: go.senan.xyz/taglib
cpu: AMD Ryzen 7 7840U w/ Radeon  780M Graphics
BenchmarkWrite-16         608   1847873 ns/op
BenchmarkRead-16         3802    299247 ns/op

License

This project is licensed under the GNU Lesser General Public License v2.1. See the LICENSE file for details.

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

Acknowledgments

  • TagLib for the audio metadata library.
  • Wazero for the WebAssembly runtime in Go.

Documentation

Index

Constants

View Source
const (
	AcoustIDFingerprint       = "ACOUSTID_FINGERPRINT"
	AcoustIDID                = "ACOUSTID_ID"
	Album                     = "ALBUM"
	AlbumArtist               = "ALBUMARTIST"
	AlbumArtistSort           = "ALBUMARTISTSORT"
	AlbumSort                 = "ALBUMSORT"
	Arranger                  = "ARRANGER"
	Artist                    = "ARTIST"
	Artists                   = "ARTISTS"
	ArtistSort                = "ARTISTSORT"
	ArtistWebpage             = "ARTISTWEBPAGE"
	ASIN                      = "ASIN"
	AudioSourceWebpage        = "AUDIOSOURCEWEBPAGE"
	Barcode                   = "BARCODE"
	BPM                       = "BPM"
	CatalogNumber             = "CATALOGNUMBER"
	Comment                   = "COMMENT"
	Compilation               = "COMPILATION"
	Composer                  = "COMPOSER"
	ComposerSort              = "COMPOSERSORT"
	Conductor                 = "CONDUCTOR"
	Copyright                 = "COPYRIGHT"
	CopyrightURL              = "COPYRIGHTURL"
	Date                      = "DATE"
	DiscNumber                = "DISCNUMBER"
	DiscSubtitle              = "DISCSUBTITLE"
	DJMixer                   = "DJMIXER"
	EncodedBy                 = "ENCODEDBY"
	Encoding                  = "ENCODING"
	EncodingTime              = "ENCODINGTIME"
	Engineer                  = "ENGINEER"
	FileType                  = "FILETYPE"
	FileWebpage               = "FILEWEBPAGE"
	GaplessPlayback           = "GAPLESSPLAYBACK"
	Genre                     = "GENRE"
	Grouping                  = "GROUPING"
	InitialKey                = "INITIALKEY"
	InvolvedPeople            = "INVOLVEDPEOPLE"
	ISRC                      = "ISRC"
	Label                     = "LABEL"
	Language                  = "LANGUAGE"
	Length                    = "LENGTH"
	License                   = "LICENSE"
	Lyricist                  = "LYRICIST"
	Lyrics                    = "LYRICS"
	Media                     = "MEDIA"
	Mixer                     = "MIXER"
	Mood                      = "MOOD"
	MovementCount             = "MOVEMENTCOUNT"
	MovementName              = "MOVEMENTNAME"
	MovementNumber            = "MOVEMENTNUMBER"
	MusicBrainzAlbumID        = "MUSICBRAINZ_ALBUMID"
	MusicBrainzAlbumArtistID  = "MUSICBRAINZ_ALBUMARTISTID"
	MusicBrainzArtistID       = "MUSICBRAINZ_ARTISTID"
	MusicBrainzReleaseGroupID = "MUSICBRAINZ_RELEASEGROUPID"
	MusicBrainzReleaseTrackID = "MUSICBRAINZ_RELEASETRACKID"
	MusicBrainzTrackID        = "MUSICBRAINZ_TRACKID"
	MusicBrainzWorkID         = "MUSICBRAINZ_WORKID"
	MusicianCredits           = "MUSICIANCREDITS"
	MusicIPPUID               = "MUSICIP_PUID"
	OriginalAlbum             = "ORIGINALALBUM"
	OriginalArtist            = "ORIGINALARTIST"
	OriginalDate              = "ORIGINALDATE"
	OriginalFilename          = "ORIGINALFILENAME"
	OriginalLyricist          = "ORIGINALLYRICIST"
	Owner                     = "OWNER"
	PaymentWebpage            = "PAYMENTWEBPAGE"
	Performer                 = "PERFORMER"
	PlaylistDelay             = "PLAYLISTDELAY"
	Podcast                   = "PODCAST"
	PodcastCategory           = "PODCASTCATEGORY"
	PodcastDesc               = "PODCASTDESC"
	PodcastID                 = "PODCASTID"
	PodcastURL                = "PODCASTURL"
	ProducedNotice            = "PRODUCEDNOTICE"
	Producer                  = "PRODUCER"
	PublisherWebpage          = "PUBLISHERWEBPAGE"
	RadioStation              = "RADIOSTATION"
	RadioStationOwner         = "RADIOSTATIONOWNER"
	RadioStationWebpage       = "RADIOSTATIONWEBPAGE"
	ReleaseCountry            = "RELEASECOUNTRY"
	ReleaseDate               = "RELEASEDATE"
	ReleaseStatus             = "RELEASESTATUS"
	ReleaseType               = "RELEASETYPE"
	Remixer                   = "REMIXER"
	Script                    = "SCRIPT"
	ShowSort                  = "SHOWSORT"
	ShowWorkMovement          = "SHOWWORKMOVEMENT"
	Subtitle                  = "SUBTITLE"
	TaggingDate               = "TAGGINGDATE"
	Title                     = "TITLE"
	TitleSort                 = "TITLESORT"
	TrackNumber               = "TRACKNUMBER"
	TVEpisode                 = "TVEPISODE"
	TVEpisodeID               = "TVEPISODEID"
	TVNetwork                 = "TVNETWORK"
	TVSeason                  = "TVSEASON"
	TVShow                    = "TVSHOW"
	URL                       = "URL"
	Work                      = "WORK"
)

These constants define normalized tag keys used by TagLib's property mapping. When using ReadTags, the library will map format-specific metadata to these standardized keys. Similarly, WriteTags will map these keys back to the appropriate format-specific fields.

While these constants provide a consistent interface across different audio formats, you can also use custom tag keys if the underlying format supports arbitrary tags.

Variables

View Source
var ErrInvalidFile = fmt.Errorf("invalid file")
View Source
var ErrSavingFile = fmt.Errorf("can't save file")

Functions

func ReadImage added in v0.8.0

func ReadImage(path string) ([]byte, error)

ReadImage reads the first embedded image from path. Returns empty byte slice if no images exist.

func ReadImageOptions added in v0.10.0

func ReadImageOptions(path string, index int) ([]byte, error)

ReadImageOptions reads the embedded image at the specified index from path. Index 0 is the first image. Returns empty byte slice if index is out of range.

func ReadTags

func ReadTags(path string) (map[string][]string, error)

ReadTags reads all metadata tags from an audio file at the given path.

func WriteImage added in v0.8.0

func WriteImage(path string, image []byte) error

WriteImage writes image as an embedded "Front Cover" at index 0 with auto-detected MIME type. Set image to nil to clear the image at that index.

func WriteImageOptions added in v0.10.0

func WriteImageOptions(path string, image []byte, index int, imageType, description, mimeType string) error

WriteImageOptions writes an image with custom metadata. Index specifies which image slot to write to (0 = first image). Set image to nil to clear the image at that index.

func WriteTags

func WriteTags(path string, tags map[string][]string, opts WriteOption) error

WriteTags writes the metadata key-values pairs to path. The behavior can be controlled with WriteOption.

Types

type ImageDesc added in v0.10.0

type ImageDesc struct {
	// Type is the picture type (e.g., "Front Cover", "Back Cover")
	Type string
	// Description is a textual description of the image
	Description string
	// MIMEType is the MIME type of the image (e.g., "image/jpeg")
	MIMEType string
}

ImageDesc contains metadata about an embedded image without the actual image data.

type Properties

type Properties struct {
	// Length is the duration of the audio
	Length time.Duration
	// Channels is the number of audio channels
	Channels uint
	// SampleRate in Hz
	SampleRate uint
	// Bitrate in kbit/s
	Bitrate uint
	// Images contains metadata about all embedded images
	Images []ImageDesc
}

Properties contains the audio properties of a media file.

func ReadProperties

func ReadProperties(path string) (Properties, error)

ReadProperties reads the audio properties from a file at the given path.

type WriteOption added in v0.6.0

type WriteOption uint8

WriteOption configures the behavior of write operations. The can be passed to WriteTags and combined with the bitwise OR operator.

const (
	// Clear indicates that all existing tags not present in the new map should be removed.
	Clear WriteOption = 1 << iota
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL