Documentation
¶
Overview ¶
Package waxlabel is a pure-Go library for reading and writing audio-file metadata (tags plus embedded cover art).
Scope ¶
WaxLabel is the metadata member of the "Wax" family. Its design goals are preservation-first editing, a public writable canonical key vocabulary, a plan-before-write workflow (Editor.Prepare producing a Plan whose Plan.Report matches exactly what Plan.Execute will do), and versioned audio-essence identity for library-wide deduplication.
WaxLabel is built for music-organization tools that need complete metadata for libraries sourced from uneven inputs such as YouTube. Those files are usually sparse or inconsistently tagged rather than blank: source metadata propagates, and transcoders often stamp an "encoder=Lavf..." comment. WaxLabel treats inherited and generated metadata as data to read, preserve, override, and deduplicate.
Object model ¶
Parse, ParseFile, and OpenSource return an immutable, detached Document: it holds no OS resources and has no Close method, so a caller may scan, cache, and discard it freely. Accessors return detached deep copies of structural data - Picture payloads included, so a caller may mutate anything an accessor returns without affecting the Document or a later call. Document.Inspect skips picture bytes entirely for bulk scans.
Editing flows through Document.Edit, which yields an Editor. The editor records mutations against a presence-aware canonical tag.TagSet; calling Editor.Prepare resolves them into a Plan. Executing the plan against a Destination streams the result.
Frozen contracts ¶
The following contracts are stable across the v1 line; other surface may still evolve:
- The Document is immutable, detached, and serializable.
- The presence-aware canonical tag.TagSet/tag.TagPatch is authoritative; the typed tag.Tags struct is a convenience projection.
- The canonical key vocabulary (tag.Key) is public and writable.
- Editing is preservation-first: the native document is the base and unaffected data (including legacy tags) is preserved and warned, never stripped silently.
- Prepare then Execute share state so the plan and the write cannot disagree; a no-op SaveBack writes nothing.
- AudioDigest carries an algorithm and a versioned extent so persisted dedup hashes survive across library-wide refinements.
Acknowledgements ¶
All code is reimplemented from public specifications (ID3v2, the Vorbis comment format, FLAC, ISO/IEC 14496-12, RIFF/WAVE, RFC 3533/7845/9559). Reference implementations were consulted for design but not copied; see the README acknowledgements.
Index ¶
- Constants
- func EqualChapters(a, b []Chapter) bool
- func EqualChaptersModuloEnds(a, b []Chapter, durA, durB time.Duration) bool
- func EqualPictures(a, b []Picture) bool
- func EqualSyncedLyrics(a, b []SyncedLyrics) bool
- func ExtensionsFor(f Format) []string
- func FormatChapterTime(d time.Duration) string
- func FormatLRC(lines []SyncedLine) string
- func HumanBytes(n int64) string
- func IsRecognizedImage(data []byte) bool
- func NewTempCreateError(dir string, err error) error
- func ResolveAlias(key tag.Key) tag.Key
- func ResolveWriteTarget(path string) string
- func ValidWritableText(s string) error
- func WritableTextReason(s string) string
- type AccessLevel
- type AudioDigest
- type AudioTrack
- type Capabilities
- type Capability
- type Chapter
- type Destination
- type Disposition
- type Document
- func (d *Document) Capabilities(opts ...WriteOption) Capabilities
- func (d *Document) Chapters() []Chapter
- func (d *Document) Edit() *Editor
- func (d *Document) Families() []FamilyValue
- func (d *Document) Fields() tag.Tags
- func (d *Document) Format() Format
- func (d *Document) Get(key tag.Key) ([]string, bool)
- func (d *Document) HasOpaqueLegacyContent() bool
- func (d *Document) HashAudioEssence(ctx context.Context, opts ...HashOption) (AudioDigest, error)
- func (d *Document) HashFile(ctx context.Context, opts ...HashOption) (AudioDigest, error)
- func (d *Document) Identity() Identity
- func (d *Document) Inspect() Snapshot
- func (d *Document) LegacyOnlyKeys() []tag.Key
- func (d *Document) Lint() []Finding
- func (d *Document) Native() NativeDoc
- func (d *Document) Padding() int64
- func (d *Document) Pictures() []Picture
- func (d *Document) PlanLintFix() LintFix
- func (d *Document) PlanTransfer(dst Format, opts ...WriteOption) (TransferReport, error)
- func (d *Document) PrepareTransfer(dst *Document, opts ...WriteOption) (*Plan, TransferReport, error)
- func (d *Document) Properties() Properties
- func (d *Document) SyncedLyrics() []SyncedLyrics
- func (d *Document) Tags() tag.TagSet
- func (d *Document) Warnings() []Warning
- type Editor
- func (e *Editor) Add(key tag.Key, vals ...string) *Editor
- func (e *Editor) AddPicture(p Picture) *Editor
- func (e *Editor) Apply(p tag.TagPatch) *Editor
- func (e *Editor) Clear(key tag.Key) *Editor
- func (e *Editor) ClearChapters() *Editor
- func (e *Editor) ClearPictures() *Editor
- func (e *Editor) ClearSyncedLyrics() *Editor
- func (e *Editor) Native() NativeEditor
- func (e *Editor) NotePictureSelectorMiss(roles ...string) *Editor
- func (e *Editor) NoteSyncedLyricsDropped(lines ...int) *Editor
- func (e *Editor) Prepare(opts ...WriteOption) (*Plan, error)
- func (e *Editor) RemovePictures(match func(Picture) bool) *Editor
- func (e *Editor) Set(key tag.Key, vals ...string) *Editor
- func (e *Editor) SetChapters(chs ...Chapter) *Editor
- func (e *Editor) SetSyncedLyrics(sls ...SyncedLyrics) *Editor
- func (e *Editor) SetTags(t tag.Tags) *Editor
- type Family
- type FamilyValue
- type Finding
- type Format
- type HashOption
- type ID3MultiValuePolicy
- type Identity
- type LegacyPolicy
- type Limits
- type LintFix
- type LintSeverity
- type NativeDoc
- type NativeEditor
- type NativeEntry
- type PaddingPolicy
- type ParseOption
- type Picture
- type PictureType
- type Plan
- type Properties
- type ReaderAtSized
- type SaveResult
- type Scope
- type Snapshot
- type Source
- type SyncedLine
- type SyncedLyrics
- type TransferItem
- type TransferKind
- type TransferReport
- type Warning
- type WarningCode
- type WriteOption
- func WithAllowUnsupportedDrop() WriteOption
- func WithID3MultiValue(p ID3MultiValuePolicy) WriteOption
- func WithLegacyPolicy(p LegacyPolicy) WriteOption
- func WithNumericGenre() WriteOption
- func WithPadding(p PaddingPolicy) WriteOption
- func WithPreserveModTime() WriteOption
- func WithStripEncoderStamp() WriteOption
- func WithUnrecognizedPictures() WriteOption
- func WithVerifyEssence() WriteOption
- func WithWebMSubset() WriteOption
- type WriteReport
Constants ¶
const ( TransferField = core.TransferField TransferPicture = core.TransferPicture TransferChapter = core.TransferChapter TransferSyncedLyric = core.TransferSyncedLyric )
TransferKind values.
const ( Carried = core.Carried Lossy = core.Lossy Dropped = core.Dropped Excluded = core.Excluded )
Disposition values.
const ( FormatUnknown = core.FormatUnknown FormatFLAC = core.FormatFLAC FormatOggVorbis = core.FormatOggVorbis FormatOggOpus = core.FormatOggOpus FormatMP3 = core.FormatMP3 FormatWAV = core.FormatWAV FormatMP4 = core.FormatMP4 FormatAAC = core.FormatAAC FormatMatroska = core.FormatMatroska FormatAIFF = core.FormatAIFF )
Format values.
const ( PicOther = core.PicOther PicFileIcon = core.PicFileIcon PicOtherFileIcon = core.PicOtherFileIcon PicFrontCover = core.PicFrontCover PicBackCover = core.PicBackCover PicLeaflet = core.PicLeaflet PicMedia = core.PicMedia PicLeadArtist = core.PicLeadArtist PicArtist = core.PicArtist PicConductor = core.PicConductor PicBand = core.PicBand PicComposer = core.PicComposer PicLyricist = core.PicLyricist PicRecordingLocation = core.PicRecordingLocation PicDuringRecording = core.PicDuringRecording PicDuringPerformance = core.PicDuringPerformance PicVideoScreenCapture = core.PicVideoScreenCapture PicBrightFish = core.PicBrightFish PicIllustration = core.PicIllustration PicBandLogo = core.PicBandLogo PicPublisherLogo = core.PicPublisherLogo )
PictureType values (matching ID3 APIC / FLAC PICTURE type IDs).
const ( AccessNone = core.AccessNone AccessPartial = core.AccessPartial AccessFull = core.AccessFull )
AccessLevel values.
const ( LegacyPreserve = core.LegacyPreserve LegacyStrip = core.LegacyStrip )
LegacyPolicy values.
const ( ID3MultiNullSep = core.ID3MultiNullSep ID3MultiRepeatFrame = core.ID3MultiRepeatFrame ID3MultiSlash = core.ID3MultiSlash )
ID3MultiValuePolicy values.
const ( FamilyVorbis = core.FamilyVorbis FamilyID3v2 = core.FamilyID3v2 FamilyID3v1 = core.FamilyID3v1 FamilyAPEv2 = core.FamilyAPEv2 FamilyMP4 = core.FamilyMP4 FamilyRIFF = core.FamilyRIFF FamilyMatroska = core.FamilyMatroska FamilyAIFF = core.FamilyAIFF )
Family values.
const ( ScopeTrack = core.ScopeTrack ScopeAlbum = core.ScopeAlbum ScopeEdition = core.ScopeEdition ScopeChapter = core.ScopeChapter )
Scope values annotate the target a family value applies to. Most formats are track-scoped; Matroska's targets make album/edition/chapter scopes meaningful.
const ( WarnStrayLeadingID3 = core.WarnStrayLeadingID3 WarnTrailingID3v1 = core.WarnTrailingID3v1 WarnLegacyAPE = core.WarnLegacyAPE WarnMultipleVorbisComment = core.WarnMultipleVorbisComment WarnInheritedEncoder = core.WarnInheritedEncoder WarnDistrustedBlockSize = core.WarnDistrustedBlockSize WarnUnknownBlock = core.WarnUnknownBlock WarnInvalidPicture = core.WarnInvalidPicture WarnConflictingFamilies = core.WarnConflictingFamilies WarnNumericGenre = core.WarnNumericGenre WarnChainedStream = core.WarnChainedStream WarnID3MultiValue = core.WarnID3MultiValue WarnDuplicateTagBlock = core.WarnDuplicateTagBlock WarnChapterSourceConflict = core.WarnChapterSourceConflict WarnChaptersStale = core.WarnChaptersStale WarnChapterTitleTruncated = core.WarnChapterTitleTruncated WarnChaptersFlattened = core.WarnChaptersFlattened WarnNoAudioFrames = core.WarnNoAudioFrames WarnTruncatedAudio = core.WarnTruncatedAudio WarnChapterPastDuration = core.WarnChapterPastDuration WarnDuplicateChapter = core.WarnDuplicateChapter WarnSingleValuedMulti = core.WarnSingleValuedMulti WarnDuplicatePicture = core.WarnDuplicatePicture WarnMultipleFrontCovers = core.WarnMultipleFrontCovers WarnPictureMetadataDropped = core.WarnPictureMetadataDropped WarnLegacyConflict = core.WarnLegacyConflict WarnValueDropped = core.WarnValueDropped WarnNativeValueReduced = core.WarnNativeValueReduced WarnValueReduced = core.WarnValueReduced WarnChapterEndsDropped = core.WarnChapterEndsDropped WarnPaddingClamped = core.WarnPaddingClamped WarnTagStructureDropped = core.WarnTagStructureDropped WarnChapterStartOverflow = core.WarnChapterStartOverflow WarnChapterMetadataDropped = core.WarnChapterMetadataDropped WarnOversizedChunk = core.WarnOversizedChunk WarnSyncedLyricsTimestampFormat = core.WarnSyncedLyricsTimestampFormat WarnSyncedLyricsContentType = core.WarnSyncedLyricsContentType WarnSyncedLyricsMetadataDropped = core.WarnSyncedLyricsMetadataDropped WarnSyncedLyricsTimestampClamped = core.WarnSyncedLyricsTimestampClamped WarnSyncedLyricsTruncated = core.WarnSyncedLyricsTruncated WarnSyncedLyricsUnsupported = core.WarnSyncedLyricsUnsupported WarnPictureUnsupported = core.WarnPictureUnsupported WarnChaptersUnsupported = core.WarnChaptersUnsupported WarnMP4MultiValue = core.WarnMP4MultiValue WarnInvalidTagKey = core.WarnInvalidTagKey WarnNumberTotalConflict = core.WarnNumberTotalConflict WarnValueCoerced = core.WarnValueCoerced WarnChapterOverlapReconciled = core.WarnChapterOverlapReconciled WarnSyncedLyricsLineDropped = core.WarnSyncedLyricsLineDropped WarnPictureSelectorMiss = core.WarnPictureSelectorMiss )
WarningCode values.
const DefaultMaxSourceBytes = core.DefaultMaxSourceBytes
DefaultMaxSourceBytes is the default ceiling OpenSource applies to a non-seekable stream it buffers whole into memory (2 GiB). A stream larger than this fails with waxerr.ErrSizeTooLarge; pass WithMaxSourceBytes(0) to lift the cap entirely. The CLI applies the same default to buffered standard input via its --max-size flag.
Variables ¶
This section is empty.
Functions ¶
func EqualChapters ¶
EqualChapters reports whether two chapter slices are identical by content (start, end, and title), in order. This is the chapter analogue of EqualPictures.
func EqualChaptersModuloEnds ¶
EqualChaptersModuloEnds reports whether two chapter lists are equal after normalizing away ends a codec would itself reconstruct (a gapless interior end, or a trailing end that runs to end-of-file). Byte-identical lists are always equal regardless of duration. durA/durB are the two files' media durations, used only for the trailing rule. It is what [diff] uses: the interior gapless rule matches how copy grades that end (reconstructable), while the trailing run-to-EOF rule is diff-specific and intentionally diverges from copy's format-based trailing grade. EqualChapters (literal ends) still backs codec change-detection.
func EqualPictures ¶
EqualPictures reports whether two picture slices are identical by content (type, MIME, description, dimensions, and bytes), in order. It is the same equality a codec uses to detect a picture edit, so a comparison and an edit cannot disagree on what "the same pictures" means.
func EqualSyncedLyrics ¶
func EqualSyncedLyrics(a, b []SyncedLyrics) bool
EqualSyncedLyrics reports whether two synced-lyrics slices are identical by content (language, description, and timed lines), in order. SyncedLyrics contains a slice, so it is not comparable with ==; this is the equality codecs use to detect edits.
func ExtensionsFor ¶
ExtensionsFor returns the lowercase file extensions (each with a leading dot) associated with format f, or nil for an unknown or unimplemented format. It lets a caller warn when an output path's extension does not match the data being written, since WaxLabel never transcodes.
func FormatChapterTime ¶
FormatChapterTime renders a chapter offset as H:MM:SS.mmm (millisecond precision), the same format WaxLabel's chapter listing uses, exposed so a consumer renders chapter timestamps consistently with it. It is also what the chapter sanity warnings use, so a warning's timestamp matches the listing.
func FormatLRC ¶
func FormatLRC(lines []SyncedLine) string
FormatLRC renders timed lyric lines as an LRC document ("[mm:ss.mmm]text" per line, in order). It round-trips losslessly through ParseLRC; the per-set language and descriptor are not representable in LRC and are not emitted.
func HumanBytes ¶
HumanBytes formats a byte count with a binary-magnitude unit (B, KiB, MiB, ...) - the same formatting WaxLabel uses in its own text output and size-limit error messages, exposed so a consumer can render sizes consistently with it. Sub-1024 counts stay exact ("57 B"); larger counts round to one decimal place and promote at a unit boundary, so 1 MiB - 1 byte reads "1.0 MiB", not "1024.0 KiB".
func IsRecognizedImage ¶
IsRecognizedImage reports whether data begins with the header of an image format WaxLabel can identify (PNG, JPEG, GIF, WebP, BMP, or TIFF). It is a header sniff, not a full decode, so it cannot recognize every valid image (AVIF/HEIC/JXL and the like return false); a caller embedding a deliberately exotic cover should offer an explicit override rather than treat a false negative as corruption. The CLI uses it to reject a non-image file passed as cover art before embedding it, without reaching into internal packages.
func NewTempCreateError ¶
NewTempCreateError builds the same temp-create failure [writeAtomic] returns when a destination directory rejects a write: it names dir (not the random temp file) and unwraps to err so the failure classifies as local I/O. It is exported so a caller that probes a directory's writability up front - the CLI's -o pre-check - surfaces the identical error (same message and exit class) the late atomic write would, instead of re-implementing it.
func ResolveAlias ¶
ResolveAlias returns the canonical key for a recognized alternative tag spelling (DATE/YEAR -> RECORDINGDATE, TOTALTRACKS -> TRACKTOTAL, ORGANIZATION -> LABEL, ...), or key unchanged when it is not an alias. Front-ends use it before applying an edit so an alias targets the real field instead of creating a duplicate custom field.
func ResolveWriteTarget ¶
ResolveWriteTarget returns the path an atomic write will rename over: the symlink-resolved target when path resolves (so the rewrite updates the file a link points at, leaving the link in place), else path verbatim (a fresh target or a dangling link). It is the single resolution rule [writeAtomic] uses; a caller pre-checking an -o destination resolves the same way so its probe inspects the directory the write actually lands in.
func ValidWritableText ¶
ValidWritableText reports whether s can be written faithfully to every supported format: no NUL byte (which truncates a C-string field) and valid UTF-8 (the read path reprojects invalid UTF-8, so it would not round-trip). It returns nil, or an error wrapping waxerr.ErrInvalidData naming the problem. Editor edits already enforce this on authored text; a caller (or a front-end) may pre-check a value with it, or with WritableTextReason for the bare reason phrase, before building an edit.
func WritableTextReason ¶
WritableTextReason returns "" when s can be written faithfully to every supported format, else a short reason phrase ("contains a NUL byte" / "contains invalid UTF-8"). It is the single source of truth for the NUL / invalid-UTF-8 rule: the internal checkWritableText and the public ValidWritableText wrap it in an waxerr.ErrInvalidData error, and a front-end (the CLI) can read the bare phrase to build its own message without parsing an error string.
Types ¶
type AudioDigest ¶
AudioDigest is a content identity for audio. Algorithm and the named, versioned ExtentVersion travel with the Sum so a persisted dedup hash stays interpretable library-wide: refining the extent definition is an opt-in new version, not a silent change that invalidates old hashes.
func (AudioDigest) Equal ¶
func (d AudioDigest) Equal(other AudioDigest) bool
Equal reports whether two digests have the same algorithm, extent, TrackID, and sum.
func (AudioDigest) String ¶
func (d AudioDigest) String() string
String renders the digest as "algorithm/extent:hex". When TrackID is non-zero, it renders as "algorithm/extent#trackID:hex" so per-track digests do not collide with each other.
type AudioTrack ¶
type AudioTrack = core.AudioTrack
AudioTrack is one stream's technical properties.
type Capabilities ¶
type Capabilities = core.Capabilities
Capabilities reports what a format can do, per dimension.
func CapabilitiesFor ¶
func CapabilitiesFor(f Format, opts ...WriteOption) Capabilities
CapabilitiesFor reports what format f can do under the given write options, without a parsed file. It is the file-less, format-level query an edit form for a not-yet-created file of format f needs - the counterpart to Document.Capabilities, which answers the same question for a file already in hand. Both route through the same codec call, so the file-aware and file-less reports cannot drift. An unknown or unimplemented format reports read-only (mirroring Document.Capabilities's no-codec fallback); pair it with the per-key tag.Key.Multivalued and the [Capabilities.Field] detail to enumerate what is editable.
type Capability ¶
type Capability = core.Capability
Capability is one field's multidimensional support.
type Destination ¶
type Destination struct {
// contains filtered or unexported fields
}
Destination names where Plan.Execute writes. Construct one with SaveBack, SaveAsFile, or WriteTo.
func SaveAsFile ¶
func SaveAsFile(path string) Destination
SaveAsFile writes a complete file at path (atomically). Unlike SaveBack it is never a no-op: a fresh destination is always written whole. It OVERWRITES any existing file at path (atomically replacing it via a temp file and rename) - it does not refuse an existing target, so the caller is responsible for the "do not clobber" check. The CLI's copy -o guard is a CLI-level policy, not a library one. (If a hard guard is later wanted, the safe route is an additive SaveAsNewFile(path) that returns a waxerr-wrapped "exists" error, not a default-refuse flip of this function's contract.) Writing to a path that resolves to the document's own source file spends the plan just as SaveBack does (see Plan.Execute); writing to other paths leaves the plan reusable.
For a ParseFile document it verifies the source file has not changed since parse (waxerr.ErrSourceChanged otherwise), as SaveBack does: the copied byte offsets come from the source as parsed, so a changed source would produce a corrupt file. Writing in place (a target that resolves to the source) uses the full check; writing to another path uses the precise inode+size+fingerprint check, so a benign mtime-only touch does not block it. An OpenSource document reads stable in-memory bytes and is not checked.
It needs a document that can resolve its own source bytes - one from ParseFile or OpenSource. A detached document from Parse carries no source, so SaveAsFile fails with waxerr.ErrInvalidData; write it with WriteTo and an explicit source instead.
func SaveBack ¶
func SaveBack() Destination
SaveBack rewrites the original file in place, atomically (temp file, fsync, rename, directory fsync). It requires the document to have come from ParseFile, verifies the file has not changed since parse (waxerr.ErrSourceChanged otherwise), and writes nothing for a no-op plan.
func WriteTo ¶
func WriteTo(w io.Writer, source ReaderAtSized) Destination
WriteTo streams the complete output to w. The source bytes to copy come from source (required when the document is detached, i.e. from Parse); for a ParseFile or OpenSource document, pass nil to use its own source.
When it reopens a ParseFile document's own source (source is nil), it first verifies that file has not changed since parse (waxerr.ErrSourceChanged otherwise), as SaveBack does - a streaming write never clobbers the source, so it uses the precise inode+size+fingerprint check. An explicit source or an OpenSource document supplies stable bytes and is not checked.
type Disposition ¶
type Disposition = core.Disposition
Disposition grades how a value survives a transfer (carried/lossy/dropped/excluded).
type Document ¶
type Document struct {
// contains filtered or unexported fields
}
Document is an immutable, detached, serializable view of a parsed file. It holds no file descriptor and has no Close method, so a caller may scan it, cache it, and discard it freely. Accessors return detached deep copies, including each Picture's payload bytes (Document.Pictures), so a caller may mutate anything an accessor returns without affecting the Document or a later call. For bulk scans that do not need payloads, Document.Inspect skips them (and the native document) to stay cheap.
A Document is safe for concurrent reads.
func Parse ¶
func Parse(ctx context.Context, src ReaderAtSized, opts ...ParseOption) (*Document, error)
Parse reads metadata from src, returning a detached Document. src is used only during the call; the Document retains no reference to it, so to write the result you supply a source again via WriteTo. Use ParseFile when you have a path (it records source identity for save-back).
func ParseFile ¶
ParseFile opens path, parses it, and closes it before returning. The Document holds no file descriptor; it records a strong source identity so a later Plan.Execute with SaveBack can detect a changed file.
func (*Document) Capabilities ¶
func (d *Document) Capabilities(opts ...WriteOption) Capabilities
Capabilities reports what the format can do under the given write options (capabilities are option-dependent).
func (*Document) Chapters ¶
Chapters returns the navigation chapters as a detached copy, in file order. Chapters live beside the canonical tags (not inside the TagSet); a file with no chapters returns none. Document.Inspect deliberately omits them.
func (*Document) Edit ¶
Edit returns an Editor that records mutations against this Document without altering it. The Document remains immutable.
func (*Document) Families ¶
func (d *Document) Families() []FamilyValue
Families returns the tag-family/source view: which family supplied each canonical value, its scope, and whether it won the projection (unselected entries for a key signal a conflict).
func (*Document) Fields ¶
Fields returns the typed convenience projection of the canonical tags. It is lossy (a struct cannot express presence); use Document.Tags when that distinction matters.
func (*Document) HasOpaqueLegacyContent ¶
HasOpaqueLegacyContent reports whether a legacy container holds non-tag content (an APEv2 binary item, a leading ID3v2's picture/chapter/synced-lyric frames, or an unreadable such container) that the canonical projection does not fold in. A legacy strip cannot prove such a container redundant, so the safe fix keeps it.
func (*Document) HashAudioEssence ¶
func (d *Document) HashAudioEssence(ctx context.Context, opts ...HashOption) (AudioDigest, error)
HashAudioEssence computes the encoded-essence identity: a hash over the audio packets plus the decoder-critical configuration (sample rate, channel count, bit depth, and FLAC block-size bounds). Mixing the config means two files with byte-identical packets but different channel mapping are correctly distinct. This answers "is this the same audio?", independent of tags.
It is distinct from whole-file identity (Document.HashFile) and from a decoded-PCM hash (which needs a decoder and is test-only).
func (*Document) HashFile ¶
func (d *Document) HashFile(ctx context.Context, opts ...HashOption) (AudioDigest, error)
HashFile computes the whole-file identity (a hash of every byte). This is the strictest level: it changes whenever any byte, including tags, changes.
func (*Document) Identity ¶
Identity returns the recorded source identity (path, size, timestamps, and structural fingerprint) used for save-back change detection.
func (*Document) Inspect ¶
Inspect returns a Snapshot suitable for scanning large libraries: it skips picture payloads and the native document entirely, so it stays cheap.
func (*Document) LegacyOnlyKeys ¶
LegacyOnlyKeys returns canonical keys whose value exists only in a legacy container (an alternate, non-authoritative block such as MP3 ID3v1/APEv2 or FLAC's leading ID3v2 / trailing ID3v1) and not in the authoritative tag set. These are the values dump would otherwise omit and a legacy strip would destroy, so the safe auto-fix keeps their container and dump surfaces them.
func (*Document) Lint ¶
Lint inspects a document for issues a tagger would want to surface or fix: stale legacy containers, inherited encoder noise, conflicting family values, duplicate or invalid pictures, malformed dates and numbers, single-valued keys carrying several values, and custom (non-vocabulary) keys. It reads only the parsed document (no I/O) and never modifies it.
func (*Document) Native ¶
Native returns a deep copy of the format's native document for inspection. To edit it, use Editor.Native.
func (*Document) Padding ¶
Padding reports the total bytes of free padding the format reserves after its metadata, so a caller can see how much slack a metadata rewrite can grow into before the audio must move. Only FLAC models an explicit padding block today; every other format reports 0, so a caller should read 0 as "not reported" rather than "no slack". It sums the native base's PADDING entries directly (never through the deep-cloning Document.Native, which would copy multi-megabyte embedded picture bodies just to total padding); Describe copies no block bodies. A native entry's Size is a byte count only when its Unit is empty, so the Unit guard keeps a future non-byte "PADDING" quantity from being summed as bytes.
func (*Document) Pictures ¶
Pictures returns the embedded pictures as a fully detached deep copy: each returned Picture's Data is independent, so a caller may mutate it without affecting a later Pictures() call or any internal state.
func (*Document) PlanLintFix ¶
PlanLintFix maps a document's lint findings to the safe remediation. Two finding classes are auto-fixed, both non-destructive:
- inherited-encoder: clear the ENCODER software stamp (tag.Encoder);
- stray-leading-id3 / trailing-id3v1 / legacy-ape: strip the legacy ID3v1/APEv2/stray-ID3 containers (WithLegacyPolicy LegacyStrip), but only when WaxLabel can prove them fully redundant with the canonical set.
The legacy strip is LegacyStrip, which is all-or-nothing (it strips every legacy container). So it is skipped when any legacy container holds unique data the strip would destroy - a tag present only in a legacy container (Document.LegacyOnlyKeys) or non-tag content the projection does not fold in (Document.HasOpaqueLegacyContent: an APEv2 binary item, a leading ID3v2's pictures/chapters/lyrics, or an unreadable container). A mixed file (one redundant container plus one carrying unique data) conservatively keeps both; the pre-existing legacy warning still fires so lint exits non-zero, and the legacy-only-tags info explains why the container was preserved. An explicit WithLegacyPolicy LegacyStrip still strips unconditionally.
The finding codes are the canonical parse-warning codes (the same ones dump prints), so this keys off exactly what lint reports - no private alias to keep in step with the linter. No other finding is acted on: dropping an unsniffable-but-valid cover would be silent data loss, a malformed date cannot be guessed, conflicting families have no winner, and missing audio cannot be synthesized. The encoder fix clears the canonical ENCODER key and, via WithStripEncoderStamp, also handles native stamps the canonical key cannot reach: the WAV ISFT INFO item and the FLAC/Ogg/Opus comment-header vendor string. Vendor fields are mandatory, so they are rewritten to a neutral value instead of removed. This plan is derived from the parsed document only; the saved file's next lint is the final result.
func (*Document) PlanTransfer ¶
func (d *Document) PlanTransfer(dst Format, opts ...WriteOption) (TransferReport, error)
PlanTransfer simulates copying this document's canonical metadata (tags, pictures, chapters, and synced lyrics) into a file of format dst. It reports what each piece would carry, downgrade, or lose without writing or needing a destination file. It consults dst's capabilities under the given write options, so an option-dependent destination is judged as a real write would be.
A read-only destination format reports everything dropped; an unimplemented destination is an error. Use Document.PrepareTransfer when you have an actual destination file and want an executable plan as well.
func (*Document) PrepareTransfer ¶
func (d *Document) PrepareTransfer(dst *Document, opts ...WriteOption) (*Plan, TransferReport, error)
PrepareTransfer projects this document's canonical metadata onto dst and resolves the result into a ready-to-execute Plan that writes dst, returning the plan together with the TransferReport describing the projection. The report is computed from the same projection the plan applies: every carried or downgraded item is set on the destination edit, and every dropped item is left off.
The report grades the destination's representational capability per field/picture/chapter, including hard structural limits it models (such as the MP4 chapter-count cap, reported as a drop). A few codec validity checks that depend on the bytes themselves - an embedded image in a format the destination cannot label, or a structurally invalid picture set - are enforced when the plan is prepared and surface as an error from this call rather than as a per-item drop; in that case the returned report still describes the attempted projection.
The transfer overlays src onto dst: each canonical key present in the source replaces that key in the destination, the source's pictures replace the destination picture set whenever at least one source picture is representable in the destination (a source whose covers are all unrepresentable leaves the destination's own covers intact), and likewise for chapters and synced lyrics. Destination keys the source does not carry are kept. dst is not modified; only Plan.Execute writes.
func (*Document) Properties ¶
func (d *Document) Properties() Properties
Properties returns the audio stream properties (a copy).
func (*Document) SyncedLyrics ¶
func (d *Document) SyncedLyrics() []SyncedLyrics
SyncedLyrics returns the timed lyric sets as a detached deep copy, in file order. Like chapters, they live beside the canonical tags rather than inside the TagSet. A file with none returns nil. Unsynchronized lyrics remain in Document.Tags as the LYRICS field.
func (*Document) Tags ¶
Tags returns the authoritative, presence-aware canonical tag set as a deep copy. This is the source of truth; the typed Document.Fields is derived from it.
type Editor ¶
type Editor struct {
// contains filtered or unexported fields
}
Editor records mutations against a Document without changing it. Mutations accumulate as a presence-aware tag.TagPatch (for canonical fields) plus a working picture list; Editor.Prepare resolves them into a Plan. The editor methods return the editor for chaining.
func (*Editor) Add ¶
Add appends values to a key. The key is resolved through ResolveAlias, so adding under an alias spelling appends to the canonical field.
func (*Editor) AddPicture ¶
AddPicture appends a picture. Its MIME and dimensions are reconciled with the image bytes via an authoritative header sniff ([Picture.SniffAuthoritative]): when the bytes are a recognized image the sniffed MIME and dimensions win over any the caller set, so a mislabeled cover cannot be embedded under a MIME that contradicts it. (A file's stored picture, read by the decoders, keeps its own MIME - that path fills only, via [Picture.SniffInto].)
func (*Editor) Apply ¶
Apply records an explicit patch (set/clear/add operations) after any already recorded, so later edits win on conflicts. Each operation's key is resolved through ResolveAlias first, exactly as the key-taking methods below do, so a patch built with an alias spelling (e.g. DATE) lands on the canonical field rather than a custom key.
func (*Editor) Clear ¶
Clear removes a key (makes it absent). The key is resolved through ResolveAlias, so clearing an alias spelling removes the canonical field.
func (*Editor) ClearChapters ¶
ClearChapters removes all chapters.
func (*Editor) ClearPictures ¶
ClearPictures removes all pictures.
func (*Editor) ClearSyncedLyrics ¶
ClearSyncedLyrics removes all synced lyrics. It also marks the set as explicitly cleared, so a following Editor.SetSyncedLyrics authors a fresh set that does not inherit the destination's existing ID3 SYLT language or descriptor. A clear with no following set just removes the synced lyrics.
func (*Editor) Native ¶
func (e *Editor) Native() NativeEditor
Native returns the native inspection view for the original parsed document. It does not include pending editor changes; pictures, tags, or chapters added on the editor are visible only after a save and reparse. Structural native mutation, such as arbitrary block edits, multiple comment blocks, or vendor string edits, is not part of the public editing API.
func (*Editor) NotePictureSelectorMiss ¶
NotePictureSelectorMiss records cover-art role names a removal named that matched no picture in this file, so Editor.Prepare surfaces a WarnPictureSelectorMiss per role rather than the removal being a silent no-op. It is per-file (a role matched in one file may miss in another), so a front-end computes the misses against this file's pictures and hands them here. Passing no roles is a no-op.
func (*Editor) NoteSyncedLyricsDropped ¶
NoteSyncedLyricsDropped records the 1-based line numbers of authored LRC input that produced no timed lyric and were dropped, so Editor.Prepare surfaces a WarnSyncedLyricsLineDropped. It is a front-end diagnostic (the CLI's --synced-lyrics-file parse), carried on the editor rather than the SyncedLyrics content type so the library model stays free of a parse-time concern. Passing no line numbers is a no-op.
func (*Editor) Prepare ¶
func (e *Editor) Prepare(opts ...WriteOption) (*Plan, error)
Prepare resolves the recorded mutations into a Plan under the given write options. The plan's Plan.Report describes exactly what executing it will do; nothing is written yet, and Prepare performs no I/O (the parsed document holds everything the planner needs).
func (*Editor) RemovePictures ¶
RemovePictures drops every picture for which match returns true. match is evaluated exactly once per picture, and the parallel added-mask is filtered with the same verdicts, so an added-then-removed picture is not validated by Prepare and a side-effecting/non-deterministic matcher cannot double-fire or desync.
func (*Editor) Set ¶
Set replaces a key's values. The key is resolved through ResolveAlias, so an alternative spelling (Set(tag.Key("DATE"), ...)) lands on the canonical field (RECORDINGDATE) on every format instead of creating a custom key; a non-alias key is unchanged.
Calling Set with no values collapses the key to absent during Editor.Prepare, matching the empty-value cleanup. Editor.Clear is the explicit removal call. Set(key, "") is distinct: it stores one empty value. A format that cannot store that value may drop it, report a removed/no-op change, and let the CLI print an advisory stderr note.
A slash-combined "n/total" on tag.TrackNumber or tag.DiscNumber is normalized at Editor.Prepare into the canonical pair (e.g. Set(tag.TrackNumber, "3/12") becomes TRACKNUMBER=3 + TRACKTOTAL=12) so every format stores it identically; see splitNumberPairs for the precedence rules.
func (*Editor) SetChapters ¶
SetChapters replaces the whole chapter list. Chapters are a timeline, so the list is sorted by start time (stably, preserving the order of chapters that share a start) because an out-of-order argument can lose a start when a container encodes spans relative to the previous chapter. A format that cannot write chapters reports that through Capabilities. Lists above a format's hard count cap are rejected at Editor.Prepare; ID3 CTOC and MP4 Nero chpl are capped at 255 entries.
func (*Editor) SetSyncedLyrics ¶
func (e *Editor) SetSyncedLyrics(sls ...SyncedLyrics) *Editor
SetSyncedLyrics replaces all synced-lyrics sets. Lines within each set are sorted by Time with a stable sort, matching ParseLRC and Editor.SetChapters. The line slices are deep-copied so later caller mutations cannot change the pending edit. A format that cannot write synced lyrics reports that through Capabilities, and Editor.Prepare rejects the write (or, under WithAllowUnsupportedDrop, drops the set with a warning).
It leaves the explicit-clear marker untouched, so calling it after Editor.ClearSyncedLyrics authors a fresh set that does not inherit the destination's existing ID3 SYLT language, while a plain SetSyncedLyrics with no preceding clear keeps that inheritance convenience.
type FamilyValue ¶
type FamilyValue = core.FamilyValue
FamilyValue is one family's contribution to a key.
type Finding ¶
type Finding struct {
Severity LintSeverity
Code string
Message string
Key tag.Key // the field involved, or "" if not field-specific
}
Finding is one issue reported by Document.Lint.
func (Finding) String ¶
String renders the finding as "[severity] code: message (key)". The severity and code are fixed library vocabulary; the message and key can be file-derived (the inherited-encoder message carries the raw inherited stamp; a custom-key finding carries the raw field name), so those two are run through tag.SanitizeLine individually - the finding prints as one list item, so a newline or tab is escaped too (it cannot forge a line), not just the terminal-hijack class. A library consumer that prints this without the CLI's output boundary is then safe. The malformed-date message is already %q-escaped inside the Message, which SanitizeLine leaves intact (no double-escape).
type Format ¶
Format identifies a container/codec.
func Formats ¶
func Formats() []Format
Formats returns every container/codec format this build implements, in registration order. It lets a caller enumerate the formats - for example to gather all recognized file extensions via ExtensionsFor when scanning a directory tree - without hard-coding the list.
type HashOption ¶
type HashOption func(*hashOptions)
HashOption configures audio-essence hashing.
func WithHashSource ¶
func WithHashSource(src ReaderAtSized) HashOption
WithHashSource supplies the source bytes to hash for a detached document (one from Parse). Documents from ParseFile or OpenSource resolve their source automatically.
type ID3MultiValuePolicy ¶
type ID3MultiValuePolicy = core.ID3MultiValuePolicy
ID3MultiValuePolicy controls the ID3v2.3 multi-value representation.
type LegacyPolicy ¶
type LegacyPolicy = core.LegacyPolicy
LegacyPolicy controls handling of legacy/foreign tag containers.
type LintFix ¶
type LintFix struct {
Patch tag.TagPatch
Options []WriteOption
}
LintFix is the safe, non-destructive remediation derived from a document's lint findings: the tag patch and write options that, applied together and saved, clear what can be safely cleared. It is deliberately conservative - only the encoder stamp and provably-redundant legacy containers are touched - so applying it can never lose data a user might want to keep.
type LintSeverity ¶
type LintSeverity uint8
LintSeverity grades a Finding.
const ( // LintInfo notes something worth knowing but not wrong. LintInfo LintSeverity = iota // LintWarning flags a likely problem (stale legacy tags, encoder noise). LintWarning // LintError flags an invalid or contradictory state. LintError )
func (LintSeverity) String ¶
func (s LintSeverity) String() string
type NativeEditor ¶
type NativeEditor struct {
// contains filtered or unexported fields
}
NativeEditor exposes the native document's structure for inspection, so a caller can see exactly what is preserved. It does not mutate native metadata.
func (NativeEditor) Entries ¶
func (n NativeEditor) Entries() []NativeEntry
Entries summarizes the native metadata blocks.
type NativeEntry ¶
type NativeEntry = core.NativeEntry
NativeEntry summarizes one native metadata block.
type PaddingPolicy ¶
type PaddingPolicy = core.PaddingPolicy
PaddingPolicy controls post-metadata free space.
type ParseOption ¶
type ParseOption func(*core.ParseOptions)
ParseOption configures Parse, ParseFile, and OpenSource.
func WithLimits ¶
func WithLimits(l Limits) ParseOption
WithLimits sets the bounded-allocation and recursion limits for untrusted input. A zero field uses the default bound for that field (it is not unlimited): a partial Limits{MaxDepth: 8} keeps the default MaxAllocBytes and MaxElements rather than dropping them to zero, which would reject every allocation (or, for MaxElements, silently disable the element-count cap).
func WithMaxSourceBytes ¶
func WithMaxSourceBytes(n int64) ParseOption
WithMaxSourceBytes bounds how many bytes OpenSource buffers from a non-seekable stream before parsing. A stream that exceeds n fails with waxerr.ErrSizeTooLarge instead of exhausting memory as an endless stream is spooled. n <= 0 disables the cap, restoring the unbounded read. The default when the option is not supplied is DefaultMaxSourceBytes (2 GiB). It has no effect on Parse or ParseFile, which read from a sized source and never buffer a stream.
func WithSourceName ¶
func WithSourceName(name string) ParseOption
WithSourceName sets the display name used for the source in the "could not identify" diagnostics, so a caller that parses buffered or temp-file bytes (e.g. standard input) reports the original name instead of the temp path. It is display-only: detection sniffs bytes, never names or extensions. The option only affects the unidentified-format error; source identity and save-back checks are unchanged. Without it the name falls back to the path argument, or "" for Parse.
type Plan ¶
type Plan struct {
// contains filtered or unexported fields
}
Plan is a resolved, ready-to-execute write produced by Editor.Prepare. It owns the byte-level rewrite and its report together, so Plan.Report is exactly what Plan.Execute will carry out - the two cannot drift.
Plan.Execute may mutate the Plan (a committed SaveBack is recorded so further writes on it are refused), so a single Plan is not safe for concurrent Execute calls; prepare a fresh Plan per goroutine. Plan.Report/Plan.Changes and the rest of the read surface are pure and need no such care.
func (*Plan) Changes ¶
Changes reports the field-level delta this plan will apply: each canonical key added, removed, or changed, plus picture, chapter, and synced-lyrics count-deltas when those sets differ. It diffs the pre-edit tags against the plan's post-codec-projection result - what the write actually lands, including date and number normalization - so the preview matches reality and a no-op plan yields no changes. It performs no I/O.
func (*Plan) Execute ¶
func (p *Plan) Execute(ctx context.Context, dst Destination) (*Document, SaveResult, error)
Execute carries out the plan against dst, one of SaveBack, SaveAsFile, or WriteTo. It returns the post-write Document and a SaveResult; on error, the SaveResult still carries what is known (e.g. Committed=false).
A plan may be executed more than once as long as no execution writes over its own source file. Repeated WriteTo or SaveAsFile runs to other paths are valid while the source bytes stay stable, or while a stable source is passed to WriteTo. Once an execution writes over the source, [Execute] refuses later runs; re-edit the returned Document to write again.
func (*Plan) IsNoOp ¶
IsNoOp reports whether the plan would not change the file's bytes. A no-op SaveBack writes nothing; a no-op SaveAsFile or WriteTo still produces a complete output (a fresh destination must be whole). An uninitialized plan is not a no-op because it cannot be executed.
func (*Plan) Report ¶
func (p *Plan) Report() WriteReport
Report describes what executing the plan will do: the operations, the before/after sizes, the padding to be written, and any warnings. It performs no I/O. An uninitialized plan reports the empty WriteReport.
It returns a defensive copy: the Operations and Warnings slices (the only reference types in a report) are cloned, and the warnings are run through the deep core.CloneWarnings so a caller mutating a returned warning's Keys cannot reach back into the plan's own report. Without this a consumer iterating Report().Warnings and editing Keys would corrupt a later Report() of the same plan.
func (*Plan) String ¶
String renders the full human-readable preview of the plan: the field-level changes block (each line through the sanitizing tag.Change.String) followed by the WriteReport body (operations, size, padding, warnings). It is the complete preview a library consumer prints with fmt.Println(plan) - safe to send to a terminal by construction, since the only untrusted values (the change values) are sanitized. It carries no path header (that is CLI display context) and no trailing newline; a no-op plan renders just the report's "no changes" line.
type ReaderAtSized ¶
type ReaderAtSized = core.ReaderAtSized
ReaderAtSized is the internal source contract: random access plus size.
func BytesSource ¶
func BytesSource(b []byte) ReaderAtSized
BytesSource returns a ReaderAtSized backed by b (which must not be mutated while in use). It is handy for parsing or writing in-memory data.
type SaveResult ¶
SaveResult reports the outcome of a save. Committed is true once the new bytes are in place (the rename succeeded); a later directory-fsync error is still returned, but with Committed true. Dest is the resulting file's identity, and Doc is the post-write document (also returned directly).
type Snapshot ¶
type Snapshot struct {
Format Format
Fields tag.Tags
Properties Properties
PictureCount int
Warnings []Warning
}
Snapshot is the lightweight result of Document.Inspect: typed fields and properties for bulk scanning, deliberately omitting picture bytes and the native document.
type Source ¶
type Source struct {
// contains filtered or unexported fields
}
Source retains the complete bytes of a non-seekable stream that was parsed for editing. Unlike a Document (which is detached and holds nothing), a Source is closable: it owns the teed buffer. Edit the Source.Document and save it; the Source supplies the original bytes the rewrite copies.
func OpenSource ¶
OpenSource parses a non-seekable stream, teeing the complete stream into memory as it reads (you cannot spool bytes after they have passed). The returned Source is closable and its Document can be edited and saved.
type SyncedLine ¶
type SyncedLine = core.SyncedLine
SyncedLine is one timed lyric line (Time, Text) within a SyncedLyrics set.
func ParseLRC ¶
func ParseLRC(text string) []SyncedLine
ParseLRC parses an LRC document into timed lyric lines, applying the foobar2000 [offset:] convention (effective timestamp = timestamp - offset) and skipping metadata tags such as [ar:], [ti:], [al:], and [length:]. A line with several leading time tags yields one SyncedLine per tag; lines are returned sorted by timestamp. This is the parser behind the FLAC/Ogg SYNCEDLYRICS store and a convenience for building a SyncedLyrics from an LRC file. LRC has no per-set language field; set it on SyncedLyrics yourself when the destination can store it.
func ParseLRCFull ¶
func ParseLRCFull(text string) []SyncedLine
ParseLRCFull is ParseLRC without the per-set line cap, for trusted input already held whole in memory (such as a user-provided LRC file), so a downstream write-time cap is the single truncation-and-warning point rather than a silent drop at read. Parsing untrusted media keeps using the capped ParseLRC. The returned slice is bounded by the input size.
func ParseLRCReportFull ¶
func ParseLRCReportFull(text string) (lines []SyncedLine, droppedLines []int)
ParseLRCReportFull is ParseLRCFull plus the 1-based line numbers of input lines the parser dropped silently - non-blank lines that produced no timed lyric and are not recognized LRC structure (a malformed timestamp, or plain untimed text). Blank lines, ID metadata tags, [offset:]/[length:] tags, and bare [section] headers are recognized structure and are not reported. The CLI uses it to warn (and fail --strict) when a --synced-lyrics-file drops lines rather than storing them silently.
type SyncedLyrics ¶
type SyncedLyrics = core.SyncedLyrics
SyncedLyrics is one timed-lyrics set (Language, Description, Lines).
type TransferItem ¶
type TransferItem = core.TransferItem
TransferItem is one piece of metadata's fate in a transfer.
type TransferKind ¶
type TransferKind = core.TransferKind
TransferKind names a transferred item's category (field/picture/chapter).
type TransferReport ¶
type TransferReport = core.TransferReport
TransferReport describes a cross-format metadata copy: what each field, picture set, and chapter set would carry, downgrade, or lose.
type WriteOption ¶
type WriteOption func(*core.WriteOptions)
WriteOption configures Prepare and the save destinations.
var ( // Preserve is the default: keep legacy containers, reuse padding in place. Preserve WriteOption = func(o *core.WriteOptions) { o.Legacy = core.LegacyPreserve o.Padding = core.DefaultPadding } // Compatible favors maximum reader compatibility. Compatible WriteOption = func(o *core.WriteOptions) { o.Legacy = core.LegacyPreserve o.Padding = core.PaddingPolicy{Target: 4096, Max: 1 << 20, ReuseInPlace: true} o.PaddingExplicit = true } // Minimal writes the smallest reasonable file: no padding, strip legacy. Minimal WriteOption = func(o *core.WriteOptions) { o.Legacy = core.LegacyStrip o.Padding = core.PaddingPolicy{Target: 0, Max: 0} o.PaddingExplicit = true } )
Policy presets bundle write options into a named intent. Apply one first, then override individual options if needed:
plan, _:= ed.Prepare(waxlabel.Preserve, waxlabel.WithVerifyEssence())
func WithAllowUnsupportedDrop ¶
func WithAllowUnsupportedDrop() WriteOption
WithAllowUnsupportedDrop makes Editor.Prepare drop a whole structural edit the destination format cannot store at all - authored synced lyrics or chapters on a format that has no such store, or cover art on a WebM file - with a warning, rather than failing the write. It also drops just the individual covers whose image format the destination stores pictures but cannot label (a GIF added to an MP4, which labels only JPEG/PNG/BMP), keeping any it can, so a PNG added alongside survives. It mirrors how a cross-format copy silently drops what the destination cannot hold, so a set that combines a storable edit with an unstorable one still applies the storable part and succeeds. By default such an edit is a hard error. The CLI passes this for set and plan; --strict promotes the resulting drop warning back to a failure.
func WithID3MultiValue ¶
func WithID3MultiValue(p ID3MultiValuePolicy) WriteOption
WithID3MultiValue selects how multiple values for one field are stored in an ID3v2.3 tag, which has no standard multi-value text form. ID3v2.4 always NUL-separates regardless; the v2.3 compatibility impact is flagged in the write report.
func WithLegacyPolicy ¶
func WithLegacyPolicy(p LegacyPolicy) WriteOption
WithLegacyPolicy sets how legacy/foreign tag containers are handled.
func WithNumericGenre ¶
func WithNumericGenre() WriteOption
WithNumericGenre writes a recognized genre as its numeric reference (in formats that support one, such as ID3's TCON) instead of the name. By default the canonical name is written.
func WithPadding ¶
func WithPadding(p PaddingPolicy) WriteOption
WithPadding sets the post-metadata padding policy for writes. It marks the policy as an explicit request, so a padding-only change is still realized when no tag, picture, or legacy edit is pending.
func WithPreserveModTime ¶
func WithPreserveModTime() WriteOption
WithPreserveModTime keeps the file's modification time across a save-back (by default the mtime is updated so scanners notice the edit).
func WithStripEncoderStamp ¶
func WithStripEncoderStamp() WriteOption
WithStripEncoderStamp asks the writer to drop a removable inherited transcoder/encoder stamp that lives in a native field no canonical-tag edit can reach: the WAV ISFT INFO item (e.g. ffmpeg's "Lavf...") and the Ogg/Opus/FLAC comment-header vendor string. Each is acted on only when it [IsTranscoderStamp]. The ISFT item is dropped; the vendor string is a mandatory codec field, so it is rewritten to WaxLabel's neutral value rather than removed (NeutralizeVendor). Pair it with clearing or setting tag.Encoder so a canonical ENCODER stamp does not survive.
func WithUnrecognizedPictures ¶
func WithUnrecognizedPictures() WriteOption
WithUnrecognizedPictures allows a picture whose bytes IsRecognizedImage does not recognize (an empty payload, junk, or a deliberately exotic HEIC/AVIF/JXL cover the header sniff cannot identify) to be embedded by Editor.Prepare. By default such a picture is rejected (waxerr.ErrInvalidData) so a direct library caller cannot silently embed an application/octet-stream picture; pass this to opt a known-exotic cover back in. Only pictures added via Editor.AddPicture are validated - a file's pre-existing picture carried through a tags-only edit is never affected.
func WithVerifyEssence ¶
func WithVerifyEssence() WriteOption
WithVerifyEssence hashes the audio bytes while a rewrite copies them and compares the written output with that copy stream. It is a copy-consistency check: it proves the output matches the bytes read during the write, not a separate parse-time baseline. For SaveBack and SaveAsFile it re-reads the temporary output before commit, which checks the copy path through the page cache. A streaming WriteTo cannot be re-read, so it verifies only the bytes copied from the source. The CLI parses each file immediately before writing, so its source extent is fresh.
func WithWebMSubset ¶
func WithWebMSubset() WriteOption
WithWebMSubset narrows a file-less Matroska CapabilitiesFor query to the WebM subset, which excludes cover-art attachments - so the format-level answer for "webm" reports picture write as unsupported, matching what Document.Capabilities reports for a parsed.webm file. It affects only the Matroska capability query; every other codec ignores it, and it has no effect on a write.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
waxlabel
command
Command waxlabel is the command-line interface to the WaxLabel audio-metadata library.
|
Command waxlabel is the command-line interface to the WaxLabel audio-metadata library. |
|
internal
|
|
|
aac
Package aac implements reading and writing raw-AAC (ADTS) metadata for the public waxlabel package.
|
Package aac implements reading and writing raw-AAC (ADTS) metadata for the public waxlabel package. |
|
aiff
Package aiff implements reading and writing AIFF / AIFF-C metadata for the public waxlabel package.
|
Package aiff implements reading and writing AIFF / AIFF-C metadata for the public waxlabel package. |
|
ape
Package ape implements read-only parsing of APEv1/APEv2 tags for internal codecs.
|
Package ape implements read-only parsing of APEv1/APEv2 tags for internal codecs. |
|
bits
Package bits holds WaxLabel's low-level byte utilities: a bounded, sticky-error Cursor over an io.ReaderAt; a segment-based rewrite executor; the non-reflected Ogg CRC; image sniffing; and content hashing.
|
Package bits holds WaxLabel's low-level byte utilities: a bounded, sticky-error Cursor over an io.ReaderAt; a segment-based rewrite executor; the non-reflected Ogg CRC; image sniffing; and content hashing. |
|
core
Package core holds the value types shared between the public waxlabel package and the internal codecs, plus the Codec contract itself.
|
Package core holds the value types shared between the public waxlabel package and the internal codecs, plus the Codec contract itself. |
|
flac
Package flac implements reading and writing FLAC metadata for the public waxlabel package.
|
Package flac implements reading and writing FLAC metadata for the public waxlabel package. |
|
id3
Package id3 implements reading and writing ID3v2 tags (v2.2, v2.3, v2.4) and reading ID3v1.
|
Package id3 implements reading and writing ID3v2 tags (v2.2, v2.3, v2.4) and reading ID3v1. |
|
iff
Package iff walks the top-level chunk structure shared by the RIFF (WAV) and IFF (AIFF) container families: a 12-byte header, then a sequence of [4-byte id][4-byte size][body][optional pad] chunks.
|
Package iff walks the top-level chunk structure shared by the RIFF (WAV) and IFF (AIFF) container families: a 12-byte header, then a sequence of [4-byte id][4-byte size][body][optional pad] chunks. |
|
mapping
Package mapping translates between canonical [tag.Key]s and native tag names.
|
Package mapping translates between canonical [tag.Key]s and native tag names. |
|
matroska
Package matroska implements reading and tag-writing Matroska / WebM (.mka / .webm / .mkv) metadata.
|
Package matroska implements reading and tag-writing Matroska / WebM (.mka / .webm / .mkv) metadata. |
|
mp3
Package mp3 implements reading and writing MP3 (MPEG audio) metadata for the public waxlabel package.
|
Package mp3 implements reading and writing MP3 (MPEG audio) metadata for the public waxlabel package. |
|
mp4
Package mp4 implements reading and writing MP4 / iTunes (M4A) metadata for the public waxlabel package.
|
Package mp4 implements reading and writing MP4 / iTunes (M4A) metadata for the public waxlabel package. |
|
ogg
Package ogg implements reading and writing metadata for Ogg Vorbis and Ogg Opus for the public waxlabel package.
|
Package ogg implements reading and writing metadata for Ogg Vorbis and Ogg Opus for the public waxlabel package. |
|
vorbis
Package vorbis implements the byte-level Vorbis comment list codec, the FLAC-style PICTURE block codec, and the canonical projection / minimal-change rebuild shared by every format that stores tags as Vorbis comments - FLAC and Ogg Vorbis/Opus.
|
Package vorbis implements the byte-level Vorbis comment list codec, the FLAC-style PICTURE block codec, and the canonical projection / minimal-change rebuild shared by every format that stores tags as Vorbis comments - FLAC and Ogg Vorbis/Opus. |
|
wav
Package wav implements reading and writing WAV (RIFF/WAVE) metadata for the public waxlabel package.
|
Package wav implements reading and writing WAV (RIFF/WAVE) metadata for the public waxlabel package. |
|
Package tag defines WaxLabel's canonical, format-neutral tag model: the validated Key vocabulary, the presence-aware TagSet/TagPatch that are authoritative for editing, the typed Tags projection used for convenient reads and sugar writes, and Merge.
|
Package tag defines WaxLabel's canonical, format-neutral tag model: the validated Key vocabulary, the presence-aware TagSet/TagPatch that are authoritative for editing, the typed Tags projection used for convenient reads and sugar writes, and Merge. |
|
Package waxerr defines the sentinel errors shared across WaxLabel.
|
Package waxerr defines the sentinel errors shared across WaxLabel. |