tunetag

package module
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: May 17, 2026 License: MIT Imports: 14 Imported by: 0

README

tunetag

test Go Reference

Pure Go audio metadata library. Reads and writes tags for MP3, FLAC, MP4 / M4A, WAV, AIFF / AIFC, Ogg Vorbis / Opus, APEv2 (Monkey's Audio / WavPack), raw AAC, and ASF / WMA. All using only the Go standard library — no cgo, no bundled WASM, no external taglib.

Status

Approaching feature-complete for v1. Read and write paths are solid for every supported container:

  • MP3: ID3v1, ID3v2.2, ID3v2.3, ID3v2.4. Writes use in-place overwrites when the new tag fits in the existing slot and atomic temp-file rewrites otherwise. The v2.4 footer flag is honoured (mutually-exclusive with padding per spec).
  • FLAC: VORBIS_COMMENT and PICTURE round-trip with PADDING-block absorption, atomic rewrite fallback, and non-metadata blocks preserved byte-for-byte.
  • MP4 / M4A: in-place via sibling free absorption (Tier 1) and full atomic rewrite with stco / co64 patching (Tier 2/3). When patching would push a 32-bit chunk offset past 2^32-1, every stco in the file is auto-promoted to co64. Fragmented MP4 (mvex / moof) is detected and rejected.
  • WAV (RIFF/WAVE): LIST/INFO entries and embedded id3 chunks (ID3v2 inside WAV) both round-trip. Non-metadata chunks (fmt , data, fact, JUNK, …) are preserved byte-for-byte. RF64 / BW64 (64-bit RIFF) is detected and rejected.
  • AIFF / AIFC: NAME / AUTH / "(c) " / ANNO text chunks (with multi-instance ANNO) and embedded ID3 chunks round-trip; non-metadata chunks (COMM / SSND / FVER / MARK / …) pass through verbatim. Big-endian sizes; both AIFF and AIFC form types are recognised.
  • Ogg Vorbis / Opus: comment packets round-trip with full re-paging — the writer encodes a fresh comment packet, splits it across as many pages as needed (with continuation flags and per-page CRC-32), and rewrites subsequent pages of the same logical bitstream with shifted sequence numbers. Cover art via Xiph's METADATA_BLOCK_PICTURE (base64 of a FLAC PICTURE block) is supported.
  • APEv2 (Monkey's Audio .ape, WavPack .wv, or any file with an APEv2 trailer): text items, binary items, and cover art ("Cover Art (Front)" / "(Back)" / "(Other)") round-trip. ID3v1 trailers after the APEv2 tag are preserved.
  • Raw AAC (ADTS): optional leading ID3v2 + trailing ID3v1 round-trip; bare ADTS files (no tags) are recognised so tunetag.Open returns an empty tag rather than ErrUnknownFormat.
  • ASF / WMA: Content Description Object (Title / Author / Copyright / Description / Rating) and Extended Content Description Object (any WM/* descriptor, with typed Bool / Word / DWord / QWord / String / Binary values). WM/Picture cover art is supported. Non-metadata Header child objects (File Properties, Stream Properties, Header Extension, Codec List, …) and the Data + Index objects round-trip byte-for-byte.

Install

go get github.com/cabbagekobe/tunetag

Requires Go 1.23 or later.

Format support matrix

Container Read Write Notes
ID3v1 / v1.1 trailer in / out, Winamp genres
ID3v2.2 4-char canonical IDs in memory; PIC body translated
ID3v2.3 UTF-16 default to preserve CJK
ID3v2.4 UTF-8 default
ID3v2 unsynchronisation decoded on read; never re-emitted
ID3v2 extended header read-skipped; not preserved
ID3v2 footer (v2.4) excludes padding when emitted
FLAC VORBIS_COMMENT UTF-8, case-insensitive lookup
FLAC PICTURE 21 ID3-compatible picture types
FLAC unknown blocks preserved verbatim
MP4 ilst (©nam, ©ART, …) Tier 1 in-place + Tier 2/3 rewrite
MP4 freeform ---- (read) mean / name / data preserved
MP4 covr (JPEG / PNG) as above
MP4 stco / co64 patch shifted by moov delta on rewrite
stco → co64 auto promotion triggered when entries overflow
Fragmented MP4 (mvex/moof) rejected on write
WAV LIST/INFO INAM / IART / IPRD / ICRD / IGNR / ICMT / ITRK
WAV embedded id3 chunk full ID3v2 tag round-trip (incl. APIC)
WAV non-metadata chunks fmt , data, fact, JUNK, … preserved verbatim
RF64 / BW64 (64-bit RIFF) detected and rejected
AIFF / AIFC text chunks NAME / AUTH / "(c) " / ANNO (multi-instance)
AIFF embedded ID3 chunk full ID3v2 round-trip; preferred over text chunks
AIFF non-metadata chunks COMM / SSND / FVER / MARK / … preserved verbatim
Ogg Vorbis comment header re-pages comment packet + renumbers / re-CRCs subsequent pages
Ogg Opus comment header as above; supports OpusHead / OpusTags variant
Ogg METADATA_BLOCK_PICTURE base64-wrapped FLAC PICTURE block; shared format helpers
APEv2 (.ape / .wv / any) text + binary items, with/without header, ID3v1-coexistence
APEv2 Cover Art "Cover Art (Front)" / "(Back)" / "(Other)" binary items
APEv1 refused with ErrUnsupportedVersion
Raw AAC (ADTS) leading ID3v2 prefix + trailing ID3v1; bare ADTS recognised
ASF / WMA CDO Title / Author / Copyright / Description / Rating
ASF / WMA ECDO WM/AlbumTitle / WM/AlbumArtist / WM/Year / WM/Genre / WM/TrackNumber / WM/Composer / WM/PartOfSet / …
ASF WM/Picture full cover-art round-trip
ASF non-metadata objects File Properties / Stream Properties / Header Extension / Data / Index preserved verbatim

Usage

Reading any container
tag, err := tunetag.Open("song.mp3")
if err != nil { log.Fatal(err) }
fmt.Println(tag.Title(), tag.Artist(), tag.Year(), tag.Format())

tunetag.Tag is a read-only common interface (Title, Artist, Album, Year, TrackNumber, DiscNumber, Genre, Composer, Comment, Pictures, Format). For writes, use the format- specific subpackages.

MP3 (ID3v2)
t, err := id3v2.ReadFile("song.mp3")
if err != nil { log.Fatal(err) }
t.SetTitle("New Title")
t.SetArtist("New Artist")
if err := t.WriteFile("song.mp3"); err != nil { log.Fatal(err) }

The first edit usually rewrites the file (because the source has no padding); subsequent edits fit in the 1 KiB padding tunetag adds by default and stay in place.

FLAC
f, err := flac.ReadFile("song.flac")
if err != nil { log.Fatal(err) }
vc := f.VorbisComment() // creates one if absent
vc.Set("TITLE", "New Title")
vc.Set("DATE", "2026")
if err := f.WriteFile("song.flac"); err != nil { log.Fatal(err) }

PADDING blocks are absorbed / created automatically so the audio offset stays stable when room exists. Otherwise the file is rewritten via a temp file and atomic rename.

MP4 / M4A
m, err := mp4.Read("song.m4a")
if err != nil { log.Fatal(err) }
m.Tag.SetTitle("New Title")
m.Tag.SetArtist("New Artist")
m.Tag.SetTrack(3, 12)
if err := m.WriteFile("song.m4a"); err != nil {
    // Fragmented MP4 returns mp4.ErrFragmentedUnsupport. Any other
    // failure is an I/O or container error.
    log.Fatal(err)
}
WAV
w, err := wav.ReadFile("song.wav")
if err != nil { log.Fatal(err) }
// LIST/INFO entries:
w.SetInfo(wav.InfoTitle,  "New Title")
w.SetInfo(wav.InfoArtist, "New Artist")
w.SetInfo(wav.InfoDate,   "2026")
// Or use the embedded id3 chunk for richer fields (APIC, etc.):
if w.ID3 == nil {
    w.ID3 = &id3v2.Tag{Version: id3v2.V24, Padding: 0}
}
w.ID3.SetTitle("New Title")
if err := w.WriteFile("song.wav"); err != nil { log.Fatal(err) }

When both a LIST/INFO chunk and an id3 chunk are present, the common tunetag.Tag interface prefers the id3 chunk's values. RF64 / BW64 (64-bit RIFF) files are rejected with wav.ErrRF64Unsupported rather than silently mis-parsed.

AIFF / AIFC
a, err := aiff.ReadFile("song.aif")
if err != nil { log.Fatal(err) }
a.SetTitle("New Title")     // NAME chunk
a.SetAuthor("New Artist")   // AUTH chunk
// Or use the embedded ID3 tag for richer fields:
if a.ID3 == nil { a.ID3 = &id3v2.Tag{Version: id3v2.V24} }
a.ID3.SetAlbum("Album")
a.ID3.SetText("TDRC", "2026")
if err := a.WriteFile("song.aif"); err != nil { log.Fatal(err) }
Ogg Vorbis / Opus
o, err := ogg.ReadFile("song.ogg")
if err != nil { log.Fatal(err) }
o.Comments.Set("TITLE",  "New Title")
o.Comments.Set("ARTIST", "New Artist")
if err := o.WriteFile("song.ogg"); err != nil { log.Fatal(err) }

The writer encodes the new comment packet, re-pages it (a single page when the bytes fit; multiple pages with continuation flags when they don't), and rewrites the per-page sequence numbers + CRC-32 for every subsequent page of the same logical bitstream. Concurrently-multiplexed streams pass through unchanged.

Cover art uses the Xiph METADATA_BLOCK_PICTURE convention (the value is base64 of a FLAC PICTURE block):

o.AddPicture(&flac.Picture{
    PictureType: 3, // CoverFront
    MIME:        "image/jpeg",
    Data:        jpegBytes,
})
APEv2 (Monkey's Audio / WavPack)
t, err := ape.ReadFile("song.wv")
if err != nil { log.Fatal(err) }
t.Set(ape.KeyTitle,  "New Title")
t.Set(ape.KeyArtist, "New Artist")
t.Set(ape.KeyYear,   "2026")
if err := t.WriteFile("song.wv"); err != nil { log.Fatal(err) }

The same package works on any file with an APEv2 trailer (MPC, MP3-with-APE, etc.). An ID3v1 trailer following the APEv2 tag is preserved across writes.

Cover art is stored as a binary item under "Cover Art (Front)" (or "(Back)" / "(Other)"). The on-disk value is <filename>\x00<image bytes>:

t.AddPicture(&ape.Picture{Filename: "cover.jpg", Data: jpegBytes})
Raw AAC (ADTS)
a, err := aac.ReadFile("song.aac")
if err != nil { log.Fatal(err) }
if a.V2 == nil { a.V2 = &id3v2.Tag{Version: id3v2.V24} }
a.V2.SetTitle("New Title")
if err := a.WriteFile("song.aac"); err != nil { log.Fatal(err) }

Bare ADTS files (no tags at all) are recognised as FormatAAC so tunetag.Open succeeds and returns an empty tag rather than ErrUnknownFormat.

ASF / WMA
w, err := asf.ReadFile("song.wma")
if err != nil { log.Fatal(err) }
w.Title = "New Title"
w.SetArtist("New Artist")  // CDO Author
w.SetAlbum("Best of 2026") // WM/AlbumTitle in the ECDO
w.SetYear(2026)
w.SetTrackNumber(3, 12)
if err := w.WriteFile("song.wma"); err != nil { log.Fatal(err) }

The package handles only ASF metadata (Header Object → Content Description + Extended Content Description). File Properties, Stream Properties, Header Extension, Codec List, Data Object, and Index Object(s) round-trip byte-for-byte. RF64-style 64-bit sizes inside Header Extension objects are preserved verbatim (they live inside opaque child object bodies).

WM/Picture cover art is accessible via File.Pictures() / AddPicture / RemovePictures for full read-write round-trip.

CLI

A thin command-line driver lives in cmd/tunetag.

tunetag print  song.mp3
tunetag dump   song.mp3
tunetag set    song.mp3 --title="Hello" --artist="Alice" --year=2026 --track=3/12
tunetag strip  song.mp3
tunetag cover  song.mp3 --extract /tmp/cover.jpg
tunetag cover  song.mp3 --set    /tmp/cover.jpg

print shows the common metadata fields; dump lists every parsed frame / ilst item / FLAC block including unknown or non-standard ones (useful for inspecting iTunes private data, Traktor PRIV payloads, etc.).

Build with go install github.com/cabbagekobe/tunetag/cmd/tunetag@latest.

Practical patterns

  • Bulk library scan: tunetag.Open(path) is ~50 µs per file (cycling through real-world MP3 / M4A fixtures on Apple M4 Pro). A 100 k-track library scans in roughly five seconds.
  • In-place re-tag without growing the file: ID3v2 writes default to 1 KiB of padding, and FLAC writes absorb diffs into an existing PADDING block when possible. Mutating Title / Artist on an already-tagged file usually does not touch any audio bytes.
  • Preserving unknown metadata: tunetag never silently drops data. Unknown ID3v2 frames are kept as GenericFrame; unknown FLAC blocks ride through as RawBlock; iTunes purchase info and freeform ---- atoms in MP4 are preserved across writes unless Strip is called.

Concurrency

A *Tag, *flac.File, or *mp4.File value is not safe for concurrent use. Holding format-specific values across goroutines requires external synchronisation. The pure parsing functions (id3v2.Read, flac.Read, mp4.Read, tunetag.Detect) are re-entrant.

Comparison

  • dhowden/tag — pure Go but read-only.
  • bogem/id3v2 — pure Go and read+write, but ID3 only.
  • go-taglib/go-taglib — wide format coverage but ships an embedded WASM build of taglib; not strictly pure Go.

tunetag aims to fill the gap: multi-format, read+write, true pure Go.

License

MIT — see LICENSE.

Documentation

Overview

Package tunetag is a pure Go audio metadata library supporting MP3 (ID3v1, ID3v2.2/2.3/2.4), FLAC (Vorbis Comment + Picture), MP4/M4A (iTunes-style ilst), WAV (RIFF LIST/INFO and embedded id3 chunks), AIFF / AIFC (text chunks + embedded ID3), Ogg Vorbis / Ogg Opus (Vorbis Comment, read+write with full re-paging), APEv2 (Monkey's Audio .ape, WavPack .wv, or any file with an APEv2 trailer), raw ADTS AAC (.aac, with optional ID3v2 prefix and ID3v1 trailer), and ASF / WMA (Content Description + Extended Content Description). It reads and writes tags using only the Go standard library — no cgo and no bundled native binaries.

Decision tree

Pick the entry point based on whether you need read or write access:

  • For read-only access where the file's container does not matter, use Detect (to identify the format) and Open (which returns a Tag interface exposing the common fields).
  • For format-specific reads and writes, use the typed openers OpenMP3, OpenFLAC, OpenMP4, OpenWAV, OpenAIFF, OpenOgg, OpenAPE, OpenAAC, or OpenASF. Each returns a value from the respective subpackage that supports the full set of mutations and a WriteFile method.
  • To erase every metadata block from a file while preserving the audio body, call Strip.

Read-only Tag interface

The Tag interface returned by Open exposes only fields that are universally meaningful across formats (Title, Artist, Album, Year, Genre, Track, Disc, Composer, Comment, AlbumArtist, Pictures). Writes deliberately are not on this interface because each format's setter semantics differ in ways that a unified API would obscure (notably the multiple genre representations on MP4 and the Vorbis case-sensitivity rules on FLAC).

Format-specific subpackages

Concurrency

None of the per-format value types (Tag, *id3v2.Tag, *flac.File, *mp4.File, *wav.File, *aiff.File, *ogg.File, *ape.Tag, *aac.File, *asf.File) are safe for concurrent use. The pure parsing entry points (Detect, id3v2.Read, flac.Read, mp4.Read, wav.Read, aiff.Read, ogg.Read, ape.Read, aac.Read, asf.Read) are re-entrant as long as each call uses its own reader.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrUnknownFormat is returned by Detect and Open when the input
	// does not match any supported container.
	ErrUnknownFormat = errors.New("tunetag: unknown format")

	// ErrEmptyFile is returned by Detect and Open when the input has
	// zero bytes. errors.Is(err, ErrUnknownFormat) reports true so
	// existing callers that only branch on ErrUnknownFormat keep
	// working.
	ErrEmptyFile = &detectError{msg: "tunetag: empty file"}

	// ErrFileTooSmall is returned by Detect and Open when the input
	// is shorter than any supported tag header can be. As with
	// ErrEmptyFile, errors.Is(err, ErrUnknownFormat) reports true.
	ErrFileTooSmall = &detectError{msg: "tunetag: file too small to contain any tag"}
)

Functions

func OpenAAC added in v0.1.3

func OpenAAC(path string) (*aac.File, error)

OpenAAC opens a raw ADTS AAC file. Both a leading ID3v2 tag and a trailing ID3v1 tag are recognised; an untagged file is represented by a *aac.File with V2 == V1 == nil.

func OpenAIFF added in v0.1.3

func OpenAIFF(path string) (*aiff.File, error)

OpenAIFF opens an AIFF / AIFC file for read-write metadata access. The returned *aiff.File exposes the NAME / AUTH / "(c) " / ANNO text chunks via Text + Annotations and any embedded "ID3 " chunk via ID3.

func OpenAPE added in v0.1.3

func OpenAPE(path string) (*ape.Tag, error)

OpenAPE locates and parses an APEv2 tag at the end of path. The audio container itself can be anything: APEv2 is the canonical tag format for Monkey's Audio (.ape) and WavPack (.wv) but is also valid on MP3, MPC, OFR, and others.

func OpenASF added in v0.1.3

func OpenASF(path string) (*asf.File, error)

OpenASF opens an ASF / WMA file. The returned *asf.File exposes the Content Description Object fields (Title / Author / Copyright / Description / Rating) plus the Extended Content Description Object descriptors (Extended) and any embedded WM/Picture entries.

func OpenFLAC

func OpenFLAC(path string) (*flac.File, error)

OpenFLAC opens a FLAC file for read-write metadata access.

Example

ExampleOpenFLAC shows the Vorbis Comment workflow: get the (creating-if-absent) VorbisComment block, mutate it, and call WriteFile to persist.

package main

import (
	"log"

	"github.com/cabbagekobe/tunetag"
)

func main() {
	f, err := tunetag.OpenFLAC("/path/to/song.flac")
	if err != nil {
		log.Fatal(err)
	}
	vc := f.VorbisComment()
	vc.Set("TITLE", "New Title")
	vc.Set("DATE", "2026")
	if err := f.WriteFile("/path/to/song.flac"); err != nil {
		log.Fatal(err)
	}
}

func OpenMP4

func OpenMP4(path string) (*mp4.File, error)

OpenMP4 opens an MP4 / M4A file for read-write metadata access.

Example

ExampleOpenMP4 demonstrates editing an MP4 / M4A. The library auto-promotes 32-bit stco chunk offsets to 64-bit co64 when a rewrite would otherwise overflow; freeform "----" items are preserved.

package main

import (
	"log"

	"github.com/cabbagekobe/tunetag"
)

func main() {
	m, err := tunetag.OpenMP4("/path/to/song.m4a")
	if err != nil {
		log.Fatal(err)
	}
	m.Tag.SetTitle("New Title")
	m.Tag.SetArtist("New Artist")
	m.Tag.SetTrack(3, 12)
	if err := m.WriteFile("/path/to/song.m4a"); err != nil {
		log.Fatal(err)
	}
}

func OpenOgg added in v0.1.3

func OpenOgg(path string) (*ogg.File, error)

OpenOgg opens an Ogg (Vorbis or Opus) file. The returned *ogg.File is read-only; writing Ogg comments is not yet implemented.

func OpenWAV added in v0.1.3

func OpenWAV(path string) (*wav.File, error)

OpenWAV opens a WAV (RIFF/WAVE) file for read-write metadata access. The returned *wav.File exposes the LIST/INFO entries and any embedded id3 chunk via its Info field and ID3 field.

func SniffImageMIME added in v0.1.3

func SniffImageMIME(b []byte) string

SniffImageMIME returns a best-effort image MIME type sniffed from the leading bytes of b. Recognised: JPEG, PNG, GIF, BMP. Returns "" when no signature matches.

This is intended for callers building Picture-like values for containers that do not carry MIME alongside the image data (notably APEv2 and Vorbis Comment's METADATA_BLOCK_PICTURE before the FLAC PICTURE block fields are filled in). It is deliberately narrower than net/http.DetectContentType — only image formats are tested, so non-image input always returns "" rather than a spurious match.

func Strip

func Strip(path string) error

Strip removes every metadata block at path, leaving the audio body intact. The format is auto-detected; ID3v2 is removed by rewriting the file without the leading tag, ID3v1 by truncating the trailer, FLAC by replacing all metadata blocks with a single empty STREAMINFO + minimal padding, and MP4 by emptying ilst.

Example

ExampleStrip removes every metadata block from a file, leaving the audio body untouched.

package main

import (
	"log"

	"github.com/cabbagekobe/tunetag"
)

func main() {
	if err := tunetag.Strip("/path/to/song.mp3"); err != nil {
		log.Fatal(err)
	}
}

Types

type Format

type Format int

Format identifies the on-disk container of an audio file.

const (
	FormatUnknown Format = iota
	FormatID3v1
	FormatID3v2
	FormatFLAC
	FormatMP4
	FormatWAV
	FormatAIFF
	FormatOgg
	FormatAPE
	FormatAAC
	FormatASF
)

func Detect

func Detect(rs io.ReadSeeker) (Format, error)

Detect inspects the start (and, when needed, the end) of rs to identify the container format. The seek position of rs is restored before returning when possible.

On failure the returned error is one of:

  • ErrEmptyFile when rs is zero bytes long,
  • ErrFileTooSmall when rs is shorter than any supported tag header can be,
  • ErrUnknownFormat otherwise.

ErrEmptyFile and ErrFileTooSmall are refinements of ErrUnknownFormat — errors.Is reports true for both, so callers that only branch on ErrUnknownFormat keep working unchanged.

func (Format) String

func (f Format) String() string

type MP3

type MP3 struct {
	V2 *id3v2.Tag // may be nil if the file had only an ID3v1 trailer
	V1 *id3v1.Tag // may be nil
}

MP3 carries the parsed ID3v2 and/or ID3v1 tags from an MP3 file. V2 is preferred; V1 is exposed for inspection.

func OpenMP3

func OpenMP3(path string) (*MP3, error)

OpenMP3 returns the parsed ID3v2 (preferred) and/or ID3v1 tag found in path.

Example

ExampleOpenMP3 demonstrates editing an MP3's ID3v2 tag in place and writing it back to disk. tunetag.OpenMP3 returns both the v2 and v1 parses; writes go through the v2 representation.

package main

import (
	"log"

	"github.com/cabbagekobe/tunetag"
	"github.com/cabbagekobe/tunetag/id3v2"
)

func main() {
	mp3, err := tunetag.OpenMP3("/path/to/song.mp3")
	if err != nil {
		log.Fatal(err)
	}
	if mp3.V2 == nil {
		// Build a fresh ID3v2.4 tag.
		mp3.V2 = &id3v2.Tag{Version: id3v2.V24, Padding: id3v2.DefaultPadding}
	}
	mp3.V2.SetTitle("New Title")
	mp3.V2.SetArtist("New Artist")
	if err := mp3.V2.WriteFile("/path/to/song.mp3"); err != nil {
		log.Fatal(err)
	}
}

type Picture

type Picture struct {
	MIME        string
	Type        PictureType
	Description string
	Data        []byte
}

Picture is an embedded image attached to an audio file.

type PictureType

type PictureType uint8

PictureType is the role of an embedded picture. The 21 values are shared between ID3v2 APIC frames and FLAC METADATA_BLOCK_PICTURE.

const (
	PictureOther              PictureType = 0
	PictureFileIcon32         PictureType = 1
	PictureFileIcon           PictureType = 2
	PictureCoverFront         PictureType = 3
	PictureCoverBack          PictureType = 4
	PictureLeafletPage        PictureType = 5
	PictureMedia              PictureType = 6
	PictureLeadArtist         PictureType = 7
	PictureArtist             PictureType = 8
	PictureConductor          PictureType = 9
	PictureBand               PictureType = 10
	PictureComposer           PictureType = 11
	PictureLyricist           PictureType = 12
	PictureRecordingLocation  PictureType = 13
	PictureDuringRecording    PictureType = 14
	PictureDuringPerformance  PictureType = 15
	PictureMovieScreenshot    PictureType = 16
	PictureBrightColouredFish PictureType = 17
	PictureIllustration       PictureType = 18
)

type Tag

type Tag interface {
	Title() string
	Artist() string
	AlbumArtist() string
	Album() string
	Year() int
	TrackNumber() (n, total int)
	DiscNumber() (n, total int)
	Genre() string
	Composer() string
	Comment() string
	Pictures() []Picture
	Format() Format
}

Tag is the read-only common interface implemented by every format-specific tag type. Setters are intentionally absent — write semantics differ enough between formats that a unified setter API would be misleading. Use the format subpackages for writes.

func Open

func Open(path string) (Tag, error)

Open auto-detects the container at path and returns a read-only Tag for the most informative metadata block in the file.

  • MP3: ID3v2 if present, otherwise ID3v1.
  • FLAC / MP4: the corresponding format-specific reader.
  • WAV: embedded "id3 " chunk if present, else LIST/INFO.
  • AIFF / AIFC: embedded "ID3 " chunk if present, else NAME / AUTH / "(c) " / ANNO text chunks.
  • Ogg (Vorbis / Opus): the codec's comment header.
  • APEv2 (.ape, .wv, or any file with a trailing APEv2 tag).
  • AAC: leading ID3v2 if present, else trailing ID3v1, else an empty tag (so untagged .aac files still resolve).
  • ASF / WMA: the Content Description Object fields, with WM/AlbumArtist (in the Extended Content Description Object) preferred over the CDO's Author field when both exist.

For format-specific writes, use OpenMP3, OpenFLAC, OpenMP4, OpenWAV, OpenAIFF, OpenOgg, OpenAPE, OpenAAC, or OpenASF directly.

Example

ExampleOpen shows the simplest read path: auto-detect the container and pull out a handful of common fields via the read-only Tag interface.

package main

import (
	"fmt"
	"log"

	"github.com/cabbagekobe/tunetag"
)

func main() {
	tag, err := tunetag.Open("/path/to/song.mp3")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(tag.Format(), tag.Title(), "/", tag.Artist())
}

Directories

Path Synopsis
Package aac handles raw ADTS-framed AAC files.
Package aac handles raw ADTS-framed AAC files.
Package aiff reads and writes AIFF (FORM/AIFF) and AIFC (FORM/AIFC) file metadata.
Package aiff reads and writes AIFF (FORM/AIFF) and AIFC (FORM/AIFC) file metadata.
Package ape reads and writes APEv2 tags.
Package ape reads and writes APEv2 tags.
Package asf reads and writes ASF / WMA file metadata.
Package asf reads and writes ASF / WMA file metadata.
cmd
tunetag command
tunetag is a command-line driver for the tunetag library.
tunetag is a command-line driver for the tunetag library.
Package flac reads and writes FLAC metadata blocks.
Package flac reads and writes FLAC metadata blocks.
Package id3v1 reads and writes ID3v1 / ID3v1.1 trailer tags (the fixed 128-byte block at the end of an MP3 file).
Package id3v1 reads and writes ID3v1 / ID3v1.1 trailer tags (the fixed 128-byte block at the end of an MP3 file).
Package id3v2 reads and writes ID3v2.2, v2.3, and v2.4 tags (the variable-length block at the start of an MP3 file marked with "ID3").
Package id3v2 reads and writes ID3v2.2, v2.3, and v2.4 tags (the variable-length block at the start of an MP3 file marked with "ID3").
internal
mp4test
Package mp4test provides minimal MP4 byte-blob fixtures for the mp4 package tests.
Package mp4test provides minimal MP4 byte-blob fixtures for the mp4 package tests.
Package mp4 reads and writes iTunes-style metadata from MP4 / M4A containers.
Package mp4 reads and writes iTunes-style metadata from MP4 / M4A containers.
Package ogg reads Vorbis Comment metadata from Ogg Vorbis and Ogg Opus streams.
Package ogg reads Vorbis Comment metadata from Ogg Vorbis and Ogg Opus streams.
Package wav reads and writes WAV (RIFF/WAVE) file metadata.
Package wav reads and writes WAV (RIFF/WAVE) file metadata.

Jump to

Keyboard shortcuts

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