audex 0.2.0

Audio metadata reading and writing library with flexible I/O and easy wrappers
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
//! ID3v2 tag support for MP3 and other audio files
//!
//! This module provides comprehensive ID3v2 tag reading and writing capabilities
//! for audio files. ID3v2 is the de facto standard for metadata tagging in MP3 files,
//! though it can also be used with other formats like AIFF, WAV, and TTA.
//!
//! # Overview
//!
//! ID3v2 tags store metadata as a collection of **frames**, where each frame contains
//! a specific piece of information (title, artist, artwork, etc.). Unlike ID3v1 which
//! has fixed fields, ID3v2 is extensible and supports a wide variety of data types.
//!
//! ## Supported Versions
//!
//! This library supports reading and writing all major ID3v2 versions:
//!
//! - **ID3v2.2**: Legacy format with 3-character frame IDs (e.g., `TT2`, `TP1`, `PIC`)
//! - **ID3v2.3**: Most common format with 4-character frame IDs (e.g., `TIT2`, `TPE1`, `APIC`)
//! - **ID3v2.4**: Latest standard with UTF-8 support and improved features
//!
//! When writing tags, ID3v2.4 is used by default, but you can specify other versions
//! if needed for compatibility with older players.
//!
//! ## Frame Types
//!
//! ID3v2 frames are categorized into several types:
//!
//! ### Text Frames
//! Store textual information like title, artist, album, genre, etc.
//! - `TIT2`: Title/Song name
//! - `TPE1`: Lead artist/Performer
//! - `TALB`: Album title
//! - `TCON`: Genre/Content type
//! - `TRCK`: Track number
//! - `TYER`/`TDRC`: Year/Recording time
//! - And many more...
//!
//! ### URL Frames
//! Store web links and online resources.
//! - `WOAR`: Official artist webpage
//! - `WCOM`: Commercial information
//! - `WPAY`: Payment information
//!
//! ### Binary Frames
//! Store non-textual data.
//! - `APIC`: Attached picture (cover art)
//! - `GEOB`: General encapsulated object
//! - `PRIV`: Private data
//!
//! ### Special Frames
//! Provide advanced functionality.
//! - `COMM`: Comments with language and description
//! - `USLT`: Unsynchronized lyrics
//! - `SYLT`: Synchronized lyrics with timing
//! - `TXXX`: User-defined text frames
//! - `POPM`: Popularimeter (rating/play count)
//! - `RVA2`: Relative volume adjustment (ReplayGain)
//! - `CHAP`/`CTOC`: Chapter markers and table of contents
//!
//! # Basic Usage
//!
//! ## Reading Tags
//!
//! ```no_run
//! use audex::id3::ID3;
//!
//! // Load ID3 tags from a file
//! let id3 = ID3::load_from_file("song.mp3").unwrap();
//!
//! // Get text frames
//! let title_frames = id3.tags.getall("TIT2");
//! if let Some(title) = title_frames.first() {
//!     if let Some(values) = title.text_values() {
//!         println!("Title: {}", values.join("; "));
//!     }
//! }
//!
//! // Get all frames of a certain type
//! for frame in id3.tags.getall("COMM") {
//!     println!("Comment: {}", frame.description());
//! }
//! ```
//!
//! ## Writing Tags
//!
//! ```no_run
//! use audex::id3::ID3Tags;
//!
//! // Create new tag collection
//! let mut tags = ID3Tags::new();
//!
//! // Add text frames
//! tags.add_text_frame("TIT2", vec!["Song Title".to_string()]).unwrap();
//! tags.add_text_frame("TPE1", vec!["Artist Name".to_string()]).unwrap();
//! tags.add_text_frame("TALB", vec!["Album Name".to_string()]).unwrap();
//!
//! // Save to file
//! tags.save("song.mp3", 1, 4, None, None).unwrap();
//! ```
//!
//! ## Working with Pictures
//!
//! ```no_run
//! use audex::id3::{ID3Tags, APIC, PictureType};
//! use audex::id3::specs::TextEncoding;
//! use std::fs;
//!
//! // Create new ID3 tag collection
//! let mut tags = ID3Tags::new();
//!
//! // Read image data from file
//! let image_data = fs::read("cover.jpg").unwrap();
//!
//! // Create picture frame with proper String types
//! // Note: mime and desc parameters must be String, not &str
//! let picture = APIC::new(
//!     TextEncoding::Utf8,                  // Text encoding for description field
//!     "image/jpeg".to_string(),            // MIME type (must be String)
//!     PictureType::CoverFront,             // Picture type (front cover)
//!     "Album Cover".to_string(),           // Description (must be String)
//!     image_data,                          // Raw image bytes
//! );
//!
//! // Add the picture frame to the tag collection
//! tags.add(Box::new(picture));
//!
//! // Save tags to file (version 2.4)
//! tags.save("song.mp3", 1, 4, None, None).unwrap();
//! ```
//!
//! ## Working with Comments
//!
//! ```no_run
//! use audex::id3::{ID3Tags, COMM};
//! use audex::id3::specs::TextEncoding;
//!
//! // Create new ID3 tag collection
//! let mut tags = ID3Tags::new();
//!
//! // Create comment frame with language code and description
//! // COMM frames support multiple comments distinguished by description
//! let comment = COMM::new(
//!     TextEncoding::Utf8,                  // Text encoding for strings
//!     *b"eng",                             // ISO 639-2 language code (3 bytes)
//!     "Description".to_string(),           // Short content description
//!     "This is my comment".to_string(),    // Actual comment text
//! );
//!
//! // Add the comment frame to the tag collection
//! tags.add(Box::new(comment));
//! ```
//!
//! ## Modifying Existing Tags
//!
//! ```no_run
//! use audex::id3::ID3;
//!
//! // Load existing tags
//! let mut id3 = ID3::load_from_file("song.mp3").unwrap();
//!
//! // Remove all frames of a specific type
//! id3.tags.delall("TIT2");
//!
//! // Add new value
//! id3.tags
//!     .add_text_frame("TIT2", vec!["New Title".to_string()])
//!     .unwrap();
//!
//! // Save changes
//! id3.save().unwrap();
//! ```
//!
//! # Advanced Features
//!
//! ## Unsynchronization
//!
//! ID3v2 supports unsynchronization to prevent false MPEG sync signals within tag data.
//! This is handled automatically during reading and writing.
//!
//! ## Extended Headers
//!
//! ID3v2.3 and 2.4 support extended headers for additional tag-level metadata.
//! This includes CRC checksums, restrictions, and tag update flags.
//!
//! ## Padding
//!
//! Tags can include padding to allow in-place updates without rewriting the entire file.
//! You can control padding size when saving tags.
//!
//! # Version Compatibility
//!
//! When reading tags, all versions are automatically detected and parsed. When writing:
//!
//! - **ID3v2.4** (default): Full Unicode support, improved date handling
//! - **ID3v2.3**: Better compatibility with older software
//! - **ID3v2.2**: Maximum compatibility, limited features
//!
//! Frame IDs are automatically converted between versions when possible.
//!
//! # Error Handling
//!
//! Operations return `Result<T, AudexError>` for proper error handling:
//!
//! ```no_run
//! use audex::id3::ID3;
//!
//! match ID3::load_from_file("song.mp3") {
//!     Ok(id3) => {
//!         // Process tags
//!         let _ = id3.tags.getall("TIT2");
//!     }
//!     Err(e) => {
//!         eprintln!("Failed to load tags: {}", e);
//!     }
//! }
//! ```
//!
//! # See Also
//!
//! - [`ID3Tags`](crate::id3::ID3Tags): Main container for ID3v2 frames
//! - `TextFrame`: Text-based frames (most common)
//! - `APIC`: Picture/artwork frames
//! - `COMM`: Comment frames
//! - Frame types and specifications in the ID3v2 standard

/// The ID3 file handler (loads/saves ID3 tags from/to files)
pub use file::{ID3, ID3FileType};
/// ID3v2 tag container holding all frames
pub use tags::{ID3SaveConfig, ID3Tags};

// Error types
pub use crate::{AudexError, Result};
/// Type alias for ID3 header-not-found errors (maps to `AudexError`)
pub type ID3NoHeaderError = crate::AudexError;
/// Type alias for ID3 unsynchronization errors (maps to `AudexError`)
pub type ID3BadUnsynchData = crate::AudexError;

// Frame types - ALL frame classes for complete API compatibility
pub use frames::{
    AENC,
    APIC,
    ASPI,
    // ID3v2.2 Special frames - complete set (legacy compatibility)
    BUF,
    CHAP,
    CNT,
    COM,
    COMM,
    COMR,
    CRA,
    CRM,
    CTOC,
    ENCR,
    EQU,
    EQU2,
    ETC,
    ETCO,
    FrameRegistry,

    GEO,
    GEOB,
    GRID,
    GRP1,
    IPL,
    LINK,
    LNK,
    MCDI,
    MCI,
    MLL,
    MLLT,
    MVIN,
    MVNM,
    OWNE,
    PCNT,
    PCST,

    PIC,
    POP,
    POPM,
    POSS,
    PRIV,
    RBUF,
    REV,
    RVA,
    RVA2,
    RVAD,
    RVRB,
    SEEK,
    SIGN,
    SLT,
    STC,
    // ID3v2.4/v2.3 Special frames - complete set
    SYLT,
    SYTC,
    // ID3v2.2 Text frames - complete set (legacy compatibility)
    TAL,
    // ID3v2.4/v2.3 Text frames - complete set
    TALB,
    TBP,
    TBPM,
    TCAT,
    TCM,
    TCMP,
    TCO,
    TCOM,
    TCON,

    TCOP,
    TCR,
    TDA,
    TDAT,
    TDEN,
    TDES,
    TDLY,
    TDOR,
    TDRC,
    TDRL,
    TDTG,
    TDY,
    TEN,
    TENC,
    TEXT,
    TFLT,
    TFT,
    TGID,
    TIM,
    TIME,
    TIPL,
    TIT1,
    TIT2,
    TIT3,
    TKE,
    TKEY,
    TKWD,
    TLA,
    TLAN,
    TLE,
    TLEN,
    TMCL,
    TMED,
    TMOO,
    TMT,
    TOA,
    TOAL,
    TOF,
    TOFN,
    TOL,
    TOLY,
    TOPE,
    TOR,
    TORY,
    TOT,
    TOWN,
    TP1,
    TP2,
    TP3,
    TP4,
    TPA,
    TPB,
    TPE1,
    TPE2,
    TPE3,
    TPE4,
    TPOS,
    TPRO,
    TPUB,
    TRC,
    TRCK,
    TRD,
    TRDA,
    TRK,
    TRSN,
    TRSO,
    TSI,
    TSIZ,
    TSO2,
    TSOA,
    TSOC,
    TSOP,
    TSOT,
    TSRC,
    TSS,
    TSSE,
    TSST,
    TT1,
    TT2,
    TT3,
    TXX,
    TXXX,
    TYE,

    TYER,
    // Core frame base types
    TextFrame,
    UFID,
    ULT,

    USER,
    USLT,
    // ID3v2.2 URL frames - complete set (legacy compatibility)
    WAF,
    WAR,
    WAS,
    WCM,
    // ID3v2.4/v2.3 URL frames - complete set
    WCOM,
    WCOP,
    WCP,
    WFED,
    WOAF,
    WOAR,
    WOAS,
    WORS,
    WPAY,
    WPB,
    WPUB,

    WXX,
    WXXX,
};

/// Alias for `APIC` (compatibility)
pub use APIC as PictureFrame;
/// Alias for `COMM` (compatibility)
pub use COMM as CommentFrame;

// Specifications and enums
pub use frames::PictureType;
pub use specs::{CTOCFlags, FrameFlags, FrameHeader, ID3Header, ID3TimeStamp, TextEncoding};

/// ID3v2 version identifier
///
/// Represents the different ID3v2 tag versions supported by this library.
/// Each version has different capabilities and compatibility characteristics.
///
/// # Versions
///
/// - **V2_2**: ID3v2.2.0 - Legacy format with 3-character frame IDs
/// - **V2_3**: ID3v2.3.0 - Most widely supported format
/// - **V2_4**: ID3v2.4.0 - Latest standard with UTF-8 and improved features
///
/// # Examples
///
/// ```
/// use audex::id3::ID3Version;
///
/// let version = ID3Version::V2_4;
/// assert_eq!(version, ID3Version::V2_4);
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ID3Version {
    /// ID3v2.2.0: 3-character frame IDs (e.g., TT2, TP1, PIC)
    V2_2,
    /// ID3v2.3.0: 4-character frame IDs, widely compatible
    V2_3,
    /// ID3v2.4.0: Latest version with UTF-8 support
    V2_4,
}

/// Frame type identifier
///
/// Categorizes ID3v2 frames by their primary data type.
/// Used internally for frame classification and processing.
///
/// # Frame Categories
///
/// - **TextFrame**: Frames containing text data (e.g., TIT2, TPE1, TALB)
/// - **UrlFrame**: Frames containing URL data (e.g., WOAR, WCOM, WPAY)
/// - **Other**: Special frames with custom structures (e.g., APIC, COMM, PRIV)
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FrameID {
    /// Text information frame
    TextFrame,
    /// URL link frame
    UrlFrame,
    /// Other frame type (binary, special structure, etc.)
    Other,
}

/// Text encoding byte identifier
///
/// Represents the encoding byte value used in ID3v2 frames to indicate
/// the character encoding of text data.
///
/// # Encoding Types
///
/// - **Latin1**: ISO-8859-1 encoding (single byte)
/// - **Utf16**: UTF-16 with BOM (byte order mark)
/// - **Utf16Be**: UTF-16 big-endian without BOM
/// - **Utf8**: UTF-8 encoding (ID3v2.4 only)
///
/// # Examples
///
/// ```
/// use audex::id3::EncodingByte;
///
/// // UTF-8 is recommended for ID3v2.4
/// let encoding = EncodingByte::Utf8;
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EncodingByte {
    /// ISO-8859-1 (Latin1) encoding - byte value 0x00
    Latin1,
    /// UTF-16 with BOM - byte value 0x01
    Utf16,
    /// UTF-16 big-endian without BOM - byte value 0x02
    Utf16Be,
    /// UTF-8 encoding (ID3v2.4 only) - byte value 0x03
    Utf8,
}

/// Convenience alias: re-exports [`TextFrame`] as `Frame` for simple usage.
///
/// Note: This is not a trait — it is a concrete type alias for `TextFrame`.
/// For the `Frame` trait (with `description()`, `text_values()`, etc.),
/// see [`frames::Frame`].
pub use frames::TextFrame as Frame;

// Specification types exported for compatibility
pub use specs::{
    ASPIIndexSpec, CTOCFlagsSpec, PictureTypeSpec, RVASpec, SynchronizedTextSpec, TimeStampSpec,
    VolumeAdjustmentSpec, VolumePeakSpec,
};

// Utility functions exported for testing
pub use util::*;

// Function aliases for compatibility with tests
pub use id3v1::make_id3v1_from_frames as MakeID3v1;
pub use id3v1::parse_id3v1_to_frames as ParseID3v1;

// Convenience functions

/// Create a new empty ID3Tags instance
///
/// Creates a new ID3v2.4 tag container with no frames. This is useful
/// when you want to create tags from scratch rather than loading from a file.
///
/// # Examples
///
/// ```
/// use audex::id3::new_id3_tags;
///
/// let mut tags = new_id3_tags();
/// tags.add_text_frame("TIT2", vec!["My Song".to_string()]).unwrap();
/// ```
pub fn new_id3_tags() -> ID3Tags {
    ID3Tags::new()
}

/// Remove all ID3 tags from a file
///
/// This function removes both ID3v2 (at the beginning) and ID3v1 (at the end)
/// tags from the specified audio file. The file is modified in-place.
///
/// # Parameters
///
/// * `filething` - Path to the audio file to clear tags from
///
/// # Returns
///
/// Returns `Ok(())` if successful, or an error if the file cannot be accessed
/// or modified.
///
/// # Examples
///
/// ```no_run
/// use audex::id3::clear;
///
/// // Remove all ID3 tags from a file
/// clear("song.mp3").unwrap();
/// ```
pub fn clear<P: AsRef<std::path::Path>>(filething: P) -> Result<()> {
    ID3Tags::clear_file(filething, true, true)
}

/// Load ID3 tags from a file.
///
/// This convenience function delegates to [`ID3Tags::load`]. At present, that
/// lower-level file-loading path is still stubbed and returns
/// [`AudexError::NotImplementedMethod`].
/// For working file-based loading, use [`ID3::load_from_file`](crate::id3::ID3::load_from_file).
///
/// # Parameters
///
/// * `filething` - Path to the audio file to read tags from
///
/// # Examples
///
/// ```no_run
/// // Preferred approach — use ID3 directly:
/// use audex::id3::ID3;
///
/// let id3 = ID3::load_from_file("song.mp3").unwrap();
/// for frame in id3.tags.getall("TIT2") {
///     if let Some(values) = frame.text_values() {
///         println!("Title: {}", values.join("; "));
///     }
/// }
/// ```
pub fn load<P: AsRef<std::path::Path>>(filething: P) -> Result<ID3Tags> {
    ID3Tags::load(filething, None, true, 4, true)
}

pub mod file;
pub mod frames;
pub mod id3v1;
pub mod specs;
pub mod tags;
pub mod util;

/// ID3v2 version constants
///
/// This module provides constants for the supported ID3v2 versions.
/// Each constant is a tuple of (major_version, revision) values.
pub mod version {
    /// ID3v2.2.0 version identifier
    ///
    /// Legacy format with 3-character frame IDs. Limited features but
    /// maximum compatibility with very old players.
    ///
    /// Format: (major_version=2, revision=0)
    pub const ID3V22: (u8, u8) = (2, 0);

    /// ID3v2.3.0 version identifier
    ///
    /// Most widely supported ID3v2 version. 4-character frame IDs,
    /// good balance of features and compatibility.
    ///
    /// Format: (major_version=3, revision=0)
    pub const ID3V23: (u8, u8) = (3, 0);

    /// ID3v2.4.0 version identifier
    ///
    /// Latest ID3v2 standard with full UTF-8 support, improved date handling,
    /// and additional features. Used as default for new tags.
    ///
    /// Format: (major_version=4, revision=0)
    pub const ID3V24: (u8, u8) = (4, 0);
}

/// ID3v2 header flag constants
///
/// This module defines bit flags used in the ID3v2 tag header to indicate
/// various tag-level features and properties.
pub mod flags {
    /// Unsynchronization flag (bit 7)
    ///
    /// Indicates that unsynchronization has been applied to prevent false
    /// MPEG sync signals within the tag data. When set, 0x00 is inserted
    /// after any 0xFF that is followed by a byte >= 0xE0 or == 0x00, and
    /// after any trailing 0xFF at the end of the data.
    pub const UNSYNCHRONIZATION: u8 = 0x80;

    /// Extended header flag (bit 6)
    ///
    /// Indicates the presence of an extended header immediately after the
    /// main tag header. The extended header contains additional tag-level
    /// information like CRC-32, restrictions, and update flags.
    pub const EXTENDED_HEADER: u8 = 0x40;

    /// Experimental flag (bit 5)
    ///
    /// Indicates that the tag is in an experimental stage. Applications
    /// should be cautious when processing tags with this flag set, as
    /// the data might not follow the standard format exactly.
    pub const EXPERIMENTAL: u8 = 0x20;

    /// Footer present flag (bit 4)
    ///
    /// Indicates that a footer is present at the end of the tag. The footer
    /// is a copy of the header located after all frames and padding, allowing
    /// the tag to be found by scanning from the end of the file.
    pub const FOOTER_PRESENT: u8 = 0x10;
}