id3

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package id3 implements reading and writing ID3v2 tags (v2.2, v2.3, v2.4) and reading ID3v1. It is shared by the internal codecs that embed ID3 (MP3, raw AAC, WAV, and AIFF), so the frame model, text encodings, unsynchronisation, the numeric-genre table, and the canonical projection live here once. It is reimplemented from the ID3v2.2/2.3/2.4 specifications; reference implementations were consulted for design only.

The native model is preservation-first: every frame is kept (decoded ones in a clean, re-renderable form; compressed/encrypted/unknown ones verbatim) in original order, so a tag edit rewrites only the affected frames. v2.2 is read in full but normalised to v2.3 frame identifiers internally and written back as v2.3 (v2.2 is obsolete); v2.3 and v2.4 round-trip at their own version.

Index

Constants

View Source
const MaxChapters = 255

MaxChapters is the most chapters an ID3 tag can carry because the CTOC entry count is a single byte. The editor enforces the same value through Capabilities.Chapters.MaxItems.

Variables

This section is empty.

Functions

func AppendRebuildWarnings

func AppendRebuildWarnings(ws []core.Warning, info RebuildInfo, retained tag.TagSet) []core.Warning

AppendRebuildWarnings appends warnings for losses found while rebuilding ID3 frames: dates that render no v2.3 frame at all, dates whose v2.3 TDAT/TIME rendering drops precision, and malformed pictures dropped during a picture edit. Date warnings are suppressed when the re-projected output still retains the attempted value in another container, such as WAV's LIST/INFO ICRD. The keyed warnings let --strict name the field and keep the four ID3-backed codecs' wording and suppression rules aligned.

func CarryProjectionWarnings

func CarryProjectionWarnings(sourceWarnings, newTagWarnings []core.Warning) []core.Warning

CarryProjectionWarnings returns warnings for a post-write MP3/AAC document. Front-tag codecs carry source parse warnings forward because buildResult cannot recompute the audio and container warnings (trailing-id3v1, legacy-ape, ...). But an edit can resolve a warning the ID3 projection produced, so any such warning the rewritten tag no longer projects must be dropped, or the returned document would disagree with a fresh parse of the written bytes. newTagWarnings is Project(newTag).Warnings; each reconciled code is stripped from the carried set only when the new projection lacks it, preserving the remaining warning order:

  • chapters-flattened: a nested CTOC rewritten as a flat list no longer flattens;
  • invalid-picture: a picture edit drops a malformed APIC (HasDroppedMalformedPicture), so the malformed cover the source-parse warning described is gone from the output.

func CheckChapterCount

func CheckChapterCount(chapters []core.Chapter) error

CheckChapterCount rejects a chapter list past the CTOC's single-byte count, so a write never wraps the count field in encodeCTOC.

func CheckSize

func CheckSize(writeVersion byte, frames []Frame, maxElements int) error

CheckSize rejects a frame list that cannot be encoded: more than maxElements frames, or a frame region overflowing the sync-safe size fields. The element cap is checked first so a write can never mint a tag the read path would then refuse to re-parse - an edit that nets a frame onto a tag already at the cap. The realistic size trigger is an over-large embedded picture, reported as ErrPictureTooLarge; anything else is ErrSizeTooLarge.

The cap test is a strict len(frames) > maxElements, NOT bits.CheckElementCap: the reader calls CheckElementCap with the pre-append count, so it errors at >= max and thus accepts exactly maxElements frames. A final-count CheckElementCap (which trips at >= max) would reject a legitimate maxElements-frame tag the reader reads back fine; the strict > test matches the reader's boundary. Pass bits.DefaultLimits.MaxElements, the read-path default.

func ContainerOps

func ContainerOps(picturesChanged bool, pictureCount int, chaptersChanged bool, chapterCount int, syncedLyricsChanged bool, syncedLyricsCount int) []string

ContainerOps returns the write-report operation lines for the embedded ID3 containers - pictures, chapters, and synced lyrics - each gated on its own change flag and canonical model count. A cleared container (count 0) emits no line: "pictures: 0" reads oddly and the removal is already captured by the tag rewrite/removal op. It is the single gate shared by RenderFrontTag (MP3/AAC) and the WAV/AIFF planChunks callers, so the four ID3-backed codecs cannot drift on it.

func DecodeText

func DecodeText(f Frame) []string

DecodeText decodes a plain text frame's value(s), for callers outside the package that need a frame's textual content (e.g. encoder-noise detection). It returns nil for non-text, user-defined (TXXX), or opaque frames, and for the involved-people frames TIPL and TMCL, whose bodies are NUL-separated function/name pairs rather than plain text. (IPLS, their v2.3 counterpart, begins with 'I', so the leading-'T' check already excludes it without an explicit case.)

func EncoderNoise

func EncoderNoise(t *Tag) []core.Warning

EncoderNoise reports inherited-encoder warnings for the tag's TSSE/TENC frames (ffmpeg writes "Lavf..." there), the signature of a transcoded/acquired file. It is shared by the container codecs that embed ID3v2 (MP3 and WAV) so the check lives in one place. A nil tag yields no warnings.

func GenreIndex

func GenreIndex(name string) int

GenreIndex exposes genreIndex to other codecs (the MP4 numeric "gnre" atom), mirroring the exported GenreName. It returns the 0-based ID3v1 genre index of a name (case-insensitive), or -1 if the name is not a standard genre.

func GenreName

func GenreName(n int) (string, bool)

GenreName exposes the numeric-genre table to the other codecs that share it (MP4's legacy "gnre" atom resolves a 1-based ID3v1 genre number to a name). It keeps the vendored 192-entry list in one place rather than duplicated per codec; n is the 0-based index.

func LooksLikeID3v1

func LooksLikeID3v1(b []byte) bool

LooksLikeID3v1 reports whether b is very likely a genuine 128-byte ID3v1/v1.1 trailer rather than audio bytes that merely begin with "TAG" at size-128. The bare 3-byte magic is far too weak to gate an essence boundary: a false positive pulls the audio end back 128 bytes and drops real audio from the essence digest and structural fingerprint. This adds cheap structural checks that every real Latin-1/Windows-1252 tag passes but a random audio tail almost never does - the year field is digits/space/NUL, and the text fields carry no binary control bytes - so the essence-affecting detection path can require them while the lenient display path (ParseV1) keeps showing an already-detected tag.

The strict year is a deliberate trade-off: a genuine but non-standard year at the trailing position (a legacy writer's "90s" or "200?") is not auto-detected here and its 128 bytes are treated as audio. That rare miss is preferred over the far more common false positive - four printable-ASCII audio bytes landing in the year field - which would silently pull the essence boundary back. ParseV1 stays lenient for the display path, so a tag detected by other means still renders such a year.

func PerFieldCapabilities

func PerFieldCapabilities(writeVersion byte, numericGenre, genreViaID3 bool) map[tag.Key]core.Capability

PerFieldCapabilities builds the per-key capability overrides shared by every ID3-backed codec (MP3/AAC/AIFF/WAV). The two value-mutating ID3 cases are declared once here rather than copied into each codec:

  • ORIGINALDATE is AccessPartial when the codec writes ID3v2.3 (writeVersion == 3), whose TORY frame keeps only the year. This drives both the transfer grade and the value-reduced edit warning (editor.appendValueReducedWarnings), the latter confirming the value actually changed before warning.
  • GENRE is AccessPartial when --numeric-genre is set and the ID3 tag is the authoritative genre store for this codec (genreViaID3). That holds always for MP3/AAC/AIFF (no native genre slot wins over ID3), but for WAV only when an id3 chunk is present - a bare WAV's native LIST/INFO IGNR keeps the genre as text, losslessly.

Returns nil when neither applies, so a codec with a lossless write passes no overrides.

func ProjectChapters

func ProjectChapters(t *Tag) ([]core.Chapter, []core.Warning)

ProjectChapters decodes a tag's CHAP/CTOC frames into a start-ordered, flat chapter list plus any read warnings (a flattened nested table of contents). It returns nil chapters when the tag carries none. Chapters are stable-sorted by start time, so a source that stored CHAP/CTOC out of start order still projects in time order, making a load->store round-trip a no-op (mirrors the Vorbis and Matroska projectors). The CTOC/CHAP order built below only breaks ties between equal-start chapters: the top-level CTOC's child element-ID list when present, falling back to the first CTOC, then to the CHAP frames' file order; a CHAP not referenced by the chosen CTOC is appended in file order so no chapter is lost.

func ProjectSyncedLyrics

func ProjectSyncedLyrics(t *Tag) ([]core.SyncedLyrics, []core.Warning)

ProjectSyncedLyrics decodes a tag's SYLT frames into synced-lyrics sets plus read warnings for unsupported timestamp formats or content types. Each lyrics SYLT becomes one set. A non-lyric or MPEG-frames-timestamped SYLT is skipped with a warning. Returns nil when the tag carries no projecting SYLT.

func RebuildError

func RebuildError(info RebuildInfo) error

RebuildError returns a hard error for a rebuild loss that must fail the write rather than only warn: a synced-lyrics line or descriptor carrying an embedded NUL, which the NUL-terminated SYLT field would silently truncate. It returns nil when no such loss occurred. Each ID3-backed codec calls it right where it calls CheckSize, so one sentinel and one message cover MP3, AAC, WAV, and AIFF - the same waxerr.ErrInvalidData a faithful copy already produces for such text.

func RebuildFrames

func RebuildFrames(orig []Frame, base, edited tag.TagSet, version byte,
	se StructuredEdit, opts WriteOpts) ([]Frame, RebuildInfo)

RebuildFrames produces the new frame list for an edited tag, preserving unchanged and unmodelled frames in place and re-rendering only the frames a changed canonical key affects. Pictures and chapters are reconciled here as well, since APIC and CHAP/CTOC frames are interleaved with text frames.

func Render

func Render(writeVersion byte, frames []Frame, padding int) []byte

Render assembles a full ID3v2 tag: the 10-byte header (no unsynchronisation, no extended header), the frames, and padding zeros. writeVersion is 3 or 4.

func RenderedSize

func RenderedSize(frames []Frame) int64

RenderedSize returns the on-disk byte length Render would emit for frames with no padding - a 10-byte tag header plus each frame's 10-byte header and body - without materializing the bytes. Codecs that size padding by reuse-in-place use it to avoid rendering the whole tag (picture bodies and all) a throwaway second time just to measure it. The length is independent of the write version: v2.3 and v2.4 frame headers are both 10 bytes; only the size field's encoding differs.

func RewriteBase

func RewriteBase(base tag.TagSet, srcTag *Tag, id3Present, stripNative bool) tag.TagSet

RewriteBase selects the tag set used as the diff base when rebuilding an ID3 chunk inside a container that also has native metadata, such as WAV LIST/INFO or AIFF text chunks. WAV and AIFF share this helper so their rewrite rules stay aligned:

  • no existing ID3 chunk: use an empty base so the full promoted tag set is written;
  • native metadata is being stripped: use only the parsed ID3 frames, so values from the stripped native container are treated as additions;
  • otherwise: use the merged projection, so unchanged frames are reused.

srcTag is read only in the stripNative case. That case implies id3Present, so srcTag is the parsed chunk, not the empty placeholder used for tagless files.

func SyncedLyricsCapability

func SyncedLyricsCapability() core.Capability

SyncedLyricsCapability is the synced-lyrics capability shared by every ID3-backed codec (MP3/AAC/AIFF/WAV). SYLT stores the language, descriptor, per-line millisecond timestamps, and text losslessly, and several SYLT frames may coexist, so there is no item cap and no loss. The shared helper keeps the four codecs identical by construction, mirroring the per-codec chapter capability.

func TagSize

func TagSize(header []byte) (int64, bool)

TagSize returns the total on-disk length of the ID3v2 tag whose 10-byte header begins header, and whether header is a valid ID3v2 header. The size field is sync-safe (7 bits per byte) and excludes the 10-byte header; a v2.4 footer adds another 10 bytes.

func TransferClassifier

func TransferClassifier(key tag.Key, _ []string, all tag.TagSet) (core.Disposition, string, bool)

TransferClassifier grades the fields whose ID3 transfer fate the format-level capability cannot express: a TRACKTOTAL or DISCTOTAL whose sibling number is non-numeric. ID3 stores a total only as the second half of a "number/total" frame, so when the number cannot form a valid numeric value the total has nowhere to go and the writer drops it (see AppendRebuildWarnings). A copy that carries such a total - a TRACKTOTAL beside a non-numeric TRACKNUMBER - must report it Dropped rather than a clean carry. It calls the same composeNumTotal decision the writer uses, so the copy report and the write drop cannot drift; composeNumTotal composes a lone total cleanly ("/total"), so a standalone TRACKTOTAL is not falsely dropped. The four ID3-backed codecs (MP3, AAC, AIFF, WAV) share it; every other field is left to the format-level grade. It is a plain core.FieldClassifier (registered by value, not called), so it captures nothing and allocates no closure.

func WriteVersionFor

func WriteVersionFor(m *core.Media, f core.Format) byte

WriteVersionFor returns the ID3 minor version codec format f would write for media m: the file's own tag version when present, else the format default. m.Native must implement id3Doc (each ID3-backed codec's doc does); a nil m or a foreign Native yields the default.

func WriteVersionOr

func WriteVersionOr(t *Tag, f core.Format) byte

WriteVersionOr returns t's write version, or the format default when t is nil. It is the single home for the "which ID3 minor version will this codec write" rule (the v2.3-vs-v2.4 split that governs the date-frame and numeric-genre fidelity).

Types

type Frame

type Frame struct {
	ID     string
	Flags  [2]byte
	Body   []byte
	Opaque bool
}

Frame is one ID3v2 frame. ID is always the 4-character v2.3/v2.4 identifier (a v2.2 3-character ID is upgraded on read). For a normal frame Body holds the clean, re-renderable payload (de-unsynchronised, with any grouping byte and data-length indicator stripped) and Flags is zero; for an opaque frame (compressed, encrypted, or otherwise not safe to reinterpret) Body and Flags are kept exactly as read so the frame round-trips byte-for-byte.

func (Frame) Clone

func (f Frame) Clone() Frame

Clone returns a deep copy of the frame.

type FrontTag

type FrontTag struct {
	Bytes      []byte         // rendered tag (header + frames + padding); nil to drop the tag
	Tag        *Tag           // the new tag for the result document; nil when dropped
	Padding    int64          // padding bytes written (0 when dropped)
	Operations []string       // report operation lines this emission adds, in order
	Warnings   []core.Warning // report warnings this emission adds
}

FrontTag is the rendered leading ID3v2 tag a front-tag-only codec (MP3, AAC) emits, plus the report fragments the emission contributes. Bytes/Tag are nil when the tag is dropped (no frame survives) - the caller writes no tag segment and hands its result builder a nil tag (so the output re-parses with no front tag, audioStart 0).

func RenderFrontTag

func RenderFrontTag(srcTag *Tag, version byte, newFrames []Frame, info RebuildInfo, pad core.PaddingPolicy,
	srcTagLen int64, hadTag, tagsChanged, picturesChanged bool, pictureCount int,
	chaptersChanged bool, chapterCount int, syncedLyricsChanged bool, syncedLyricsCount int) FrontTag

RenderFrontTag sizes and renders the leading ID3v2 tag for a codec that stores tags only as a single front tag (MP3, AAC), centralizing the drop-empty-tag policy so the two cannot diverge. It emits the tag only when at least one frame survives: an edit (or a --legacy strip) that leaves no frames drops the tag entirely rather than fabricating an empty, padding-only container, matching WAV/AIFF's len(frames)>0 chunk guard. hadTag is whether the source carried a front tag (so a full clear records an "ID3v2 removal" op instead of a bare rewrite); srcTagLen is its byte length for in-place padding reuse; pictureCount, chapterCount, and syncedLyricsCount are used for the picture, chapter, and synced-lyrics operation lines.

type Projection

type Projection struct {
	Tags         tag.TagSet
	Pictures     []core.Picture
	Chapters     []core.Chapter
	SyncedLyrics []core.SyncedLyrics
	Families     []core.FamilyValue
	NumericGenre bool
	Warnings     []core.Warning
}

Projection is the canonical view decoded from an ID3v2 tag: tags, embedded pictures, navigation chapters, family/source entries, and parse facts the caller turns into warnings.

func Project

func Project(t *Tag) Projection

Project decodes an ID3v2 tag into the canonical model. A nil tag projects to the empty Projection: a write that drops the front ID3v2 entirely (an edit clearing every frame, or a --legacy strip on a tagless file) passes nil here for the result document, which must equal a fresh parse of the now-tagless output.

type RebuildInfo

type RebuildInfo struct {
	// UsedV23Multi is true when a v2.3 tag was written with NUL-separated
	// multi-values (a nonstandard extension whose compatibility impact is flagged).
	UsedV23Multi bool
	// DroppedDates lists year-anchored date keys whose edited value had no extractable
	// numeric year and so rendered no v2.3 frame at all - a silent drop the caller
	// surfaces as a value-dropped warning. Empty on v2.4 (TDRC/TDOR store the full
	// string) and for values that do carry a year (only the sub-year precision is lost,
	// which is not a drop). See detectDroppedDates.
	DroppedDates []tag.Key
	// ReducedDates lists date keys whose v2.3 rendering silently lost precision finer than
	// the rendered frames capture: a month with no full date drops to the year (TDAT needs a
	// full DDMM), and an hour with no minute drops to the date (TIME needs a full HHMM). Each
	// entry pairs the key with the attempted edited value (e.g. "2021-03", "2021-03-15T10")
	// for the warning text and the precision-aware suppression. Scoped to RecordingDate;
	// OriginalDate's v2.3 reductions are reported through the capability-based value-reduced
	// path (its TORY field is AccessPartial), so listing it here would double-warn. See
	// detectReducedDates and reducesDatePrecision.
	ReducedDates []ReducedDate
	// HasDroppedMalformedPicture is set when a picture edit replaced the APIC frames and
	// at least one original APIC could not be decoded (a malformed cover). Those raw
	// bytes are not carried forward, so the loss is surfaced rather than left silent.
	HasDroppedMalformedPicture bool
	// NumericGenres lists the GENRE values this edit set that are a bare number naming a
	// standard genre by index (e.g. "17"). Written verbatim to TCON, such a value reads
	// back as the genre NAME on the pure-ID3 formats, so the caller surfaces it as a
	// write-time numeric-genre warning - symmetric with the read-time one - suppressed
	// where a native container keeps the literal number (WAV/AIFF INFO/text). See
	// detectNumericGenres.
	NumericGenres []string
	// DroppedTotals lists the TRACKTOTAL/DISCTOTAL keys whose canonical value cannot be composed
	// into a valid "n/total" TRCK/TPOS frame because the number field is non-numeric (e.g.
	// TRACKNUMBER="A1"): the reader would read "A1/12" as one literal value with the total merged
	// in and lost, so renderNumTotal preserves the number verbatim and drops the total. The caller
	// surfaces it as a value-dropped warning keyed to the total. An embedded total in the number
	// itself ("A1/12" with no canonical TRACKTOTAL) is preserved verbatim and is not a drop. See
	// detectDroppedTotals.
	DroppedTotals []tag.Key
	// DroppedTrailingValues lists the keys an edit touched whose trailing empty element the write
	// cannot represent: a NUL-separated frame emits no trailing terminator, and the read path strips
	// a trailing empty (see the read-strip floor in decodeStringsTracked), so the empty vanishes with
	// no byte-level delta to reveal it. Only populated when the values are written NUL-separated (all
	// of v2.4, or v2.3 under ID3MultiNullSep); repeat-frame and slash-join do not drop it. The caller
	// surfaces it as a value-dropped warning keyed to each affected key. See detectDroppedTrailingValues.
	DroppedTrailingValues []tag.Key
	// DroppedInvolvedEmpties lists the involved-people role keys an edit touched whose value carried
	// an empty element the TIPL/IPLS frame cannot store. Unlike a plain multi-value text frame, which
	// keeps interior empties, the involved-people body is function/name pairs and a nameless pair is
	// dropped on both write and read (Picard's name-required guard), so an empty at any position
	// vanishes. The caller surfaces it as a value-dropped warning keyed to each affected role. See
	// detectDroppedInvolvedEmpties.
	DroppedInvolvedEmpties []tag.Key
	// ChapterOverflow is set when a chapter edit clamped a start or end past the CHAP
	// frame's 32-bit millisecond field (~49.7 days). The caller surfaces it as a
	// chapter-start-overflow warning.
	ChapterOverflow bool
	// DroppedChapterSubframes is set when a chapter edit dropped a source CHAP's subframe
	// other than the TIT2 title (a per-chapter image or URL the flat model cannot hold),
	// so that loss is surfaced rather than left silent.
	DroppedChapterSubframes bool
	// SyncedLyricsOverflow is set when a synced-lyrics edit clamped a line's timestamp past
	// the SYLT frame's 32-bit millisecond field (~49.7 days). The caller surfaces it as a
	// synced-lyrics-timestamp-clamped warning.
	SyncedLyricsOverflow bool
	// SyncedLyricsInvalidNUL is set when a synced-lyrics edit's modeled line text or descriptor
	// carries an embedded NUL, which the NUL-terminated SYLT field would silently truncate. Unlike
	// the warnings above, this is a hard error: the caller turns it into waxerr.ErrInvalidData via
	// RebuildError and refuses the write, rather than writing a truncated frame.
	SyncedLyricsInvalidNUL bool
	// SyncedLyricsLangUndefined is set when an authored synced-lyrics set carried a non-empty
	// language that normalizes to the ID3 "undefined" marker ("xxx"/"XXX"): the value is
	// stored (exit 0) but reads back with no language, so the caller surfaces it as a
	// metadata-dropped warning rather than letting the downgrade go unnoticed. Only an
	// explicitly-authored language triggers it - a faithful carry of a no-language source set
	// has an empty language and reads back empty, so it is not flagged.
	SyncedLyricsLangUndefined bool
}

RebuildInfo reports facts about a rebuild the caller surfaces in the write report.

type ReducedDate

type ReducedDate struct {
	Key   tag.Key
	Value string
}

ReducedDate pairs a date key with the value an edit attempted to store before a lower-fidelity v2.3 rendering reduced its precision.

type StructuredEdit

type StructuredEdit struct {
	Pictures            []core.Picture
	PicturesChanged     bool
	Chapters            []core.Chapter
	ChaptersChanged     bool
	SyncedLyrics        []core.SyncedLyrics
	SyncedLyricsChanged bool
	// SyncedLyricsCarried marks the synced-lyrics edit as a faithful cross-format carry, so
	// the empty-language fallback to the destination's existing SYLT language is skipped: a
	// carry of a no-language set (FLAC/Ogg store none) must read back with no language, not
	// silently inherit the destination's. An authored line-only edit leaves this false and
	// keeps the documented CLI convenience of preserving the file's existing language.
	SyncedLyricsCarried bool
	// SyncedLyricsCleared marks the synced-lyrics set as explicitly cleared before this edit
	// authored a new one, so the same language/descriptor fallback is skipped: a clear means
	// "start fresh," so an authored set with no language reads back with none instead of
	// inheriting the cleared one. It is distinct from SyncedLyricsCarried (a faithful transfer)
	// so the edit is not mislabeled; both suppress the fallback.
	SyncedLyricsCleared bool
	// MediaDuration is the file's playable length, used only to bound a trailing open-ended
	// chapter (End == 0) at CHAP serialization time so a spec-conforming reader sees a
	// concrete end instead of the 0xFFFFFFFF "unused" sentinel (~49.7 days). Zero (unknown
	// duration) leaves the trailing chapter open, emitting the sentinel as before. The
	// canonical core.Chapter{End:0} "open" model is unchanged; the fill is ID3-local.
	MediaDuration time.Duration
}

StructuredEdit carries the non-tag structures a frame rebuild owns. A structure is dropped and re-emitted only when its change flag is set; otherwise the source frames are preserved as-is.

type Tag

type Tag struct {
	// contains filtered or unexported fields
}

Tag is a parsed ID3v2 tag: the decoded frames in original order plus the version metadata needed to write them back at the right version. srcVersion records what was read (2, 3, or 4) for reporting; writeVersion is the version actually emitted (3 or 4 - a v2.2 source is modernised to v2.3).

func NewEmpty

func NewEmpty(writeVersion byte) *Tag

NewEmpty returns an empty tag that will be written at the given version (clamped to v2.3 or v2.4), for files that have no ID3v2 tag yet.

func ParseTag

func ParseTag(data []byte, maxElements int) (*Tag, error)

ParseTag decodes a complete ID3v2 tag region (starting at the "ID3" header). It tolerates truncation (parsing what is present) but rejects a missing or reserved header. The caller bounds the size of data when reading it from the source; maxElements additionally caps the frame count (0 = unlimited) so a body of minimum-size frames cannot accumulate descriptors past the shared limit.

func ReadFront

func ReadFront(src core.ReaderAtSized, size, limit int64, maxElements int) (*Tag, int64, error)

ReadFront reads a leading ID3v2 tag from the start of src, returning the parsed tag and its on-disk length, or (nil, 0, nil) when src has no readable leading ID3. It is the shared front-tag read for the codecs whose authoritative container is a front ID3v2 tag (MP3 and raw AAC); FLAC, which only preserves a stray leading ID3 verbatim, reads the raw bytes itself. size is the source size (the tag must fit within it); limit bounds the allocation.

func (*Tag) Clone

func (t *Tag) Clone() *Tag

Clone returns a deep copy of the tag.

func (*Tag) Frames

func (t *Tag) Frames() []Frame

Frames returns the decoded frames in order.

func (*Tag) SrcVersion

func (t *Tag) SrcVersion() byte

SrcVersion reports the ID3v2 minor version that was parsed (2, 3, or 4).

func (*Tag) WithFrames

func (t *Tag) WithFrames(frames []Frame) *Tag

WithFrames returns a copy of the tag carrying frames, for building the post-write document.

func (*Tag) WriteVersion

func (t *Tag) WriteVersion() byte

WriteVersion reports the version a rewrite will emit (3 or 4).

type V1

type V1 struct {
	Title   string
	Artist  string
	Album   string
	Year    string
	Comment string
	Track   int    // ID3v1.1 only (0 if absent)
	Genre   string // resolved name, or "" if unset/unknown
}

V1 is a decoded ID3v1 / ID3v1.1 tag - the fixed 128-byte trailer. It is read for the family view and preserved verbatim; ID3v2 is authoritative.

func ParseV1

func ParseV1(b []byte) (*V1, bool)

ParseV1 decodes a 128-byte ID3v1 trailer. ok is false if b is not a tag.

func (*V1) Pairs

func (v *V1) Pairs() []struct {
	Key   tag.Key
	Value string
}

Pairs returns the canonical key/value pairs ID3v1 supplies, in a stable order, skipping empty fields. Used to build the family/source view.

type WriteOpts

type WriteOpts struct {
	Multi        core.ID3MultiValuePolicy
	NumericGenre bool // write TCON as a numeric reference when the genre is standard
}

WriteOpts are the inputs to a frame rebuild. The multi-value policy is the shared core type so it can be a public write option without duplication.

Jump to

Keyboard shortcuts

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