tinytag 2.2.0__tar.gz → 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tinytag might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2014-2025 Tom Wallroth, Mat (mathiascode), et al.
3
+ Copyright (c) 2014-2026 Tom Wallroth, Mat (mathiascode), et al.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinytag
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Read audio file metadata
5
5
  Keywords: metadata,audio,music,mp3,m4a,wav,ogg,opus,flac,wma,aiff
6
6
  Author: Tom Wallroth, Mat (mathiascode)
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Programming Language :: Python :: Free Threading :: 4 - Resilient
19
20
  Classifier: License :: OSI Approved :: MIT License
20
21
  Classifier: Development Status :: 5 - Production/Stable
21
22
  Classifier: Environment :: Web Environment
@@ -37,7 +38,7 @@ Project-URL: Homepage, https://github.com/tinytag/tinytag
37
38
  Provides-Extra: tests
38
39
 
39
40
  <!--
40
- SPDX-FileCopyrightText: 2014-2024 tinytag Contributors
41
+ SPDX-FileCopyrightText: 2014-2026 tinytag Contributors
41
42
  SPDX-License-Identifier: MIT
42
43
  -->
43
44
 
@@ -64,12 +65,13 @@ python3 -m pip install tinytag
64
65
 
65
66
  * Read tags, images and properties of audio files
66
67
  * Supported formats:
67
- * MP3 / MP2 / MP1 (ID3 v1, v1.1, v2.2, v2.3+)
68
- * M4A (AAC / ALAC)
68
+ * ID3 v1, v1.1, v2.2, v2.3, v2.4
69
+ * MP3 / MP2 / MP1
70
+ * MP4 (MPEG-4 Audio / AAC / ALAC)
69
71
  * WAVE / WAV
70
72
  * OGG (FLAC / Opus / Speex / Vorbis)
71
73
  * FLAC
72
- * WMA
74
+ * ASF (WMA)
73
75
  * AIFF / AIFF-C
74
76
  * Same API for all formats
75
77
  * Small, portable library
@@ -79,7 +81,8 @@ python3 -m pip install tinytag
79
81
 
80
82
  > [!IMPORTANT]
81
83
  > Support for changing/writing metadata will not be added. Use another library
82
- > such as [Mutagen](https://mutagen.readthedocs.io/) for this.
84
+ > such as [Mutagen](https://mutagen.readthedocs.io/) or [pytaglib](https://github.com/supermihi/pytaglib)
85
+ > for this.
83
86
 
84
87
 
85
88
  ## Usage
@@ -102,34 +105,40 @@ print(f'It is {tag.duration:.2f} seconds long.')
102
105
 
103
106
  Alternatively you can use tinytag directly on the command line:
104
107
 
105
- $ python3 -m tinytag /some/music.mp3
106
- {
107
- "filename": "/some/music.mp3",
108
- "filesize": 3243226,
109
- "duration": 173.52,
110
- "channels": 2,
111
- "bitrate": 128,
112
- "samplerate": 44100,
113
- "artist": [
114
- "artist name"
115
- ],
116
- "album": [
117
- "album name"
118
- ],
119
- "title": [
120
- "track name"
121
- ],
122
- "track": 4,
123
- "genre": [
124
- "Jazz"
125
- ],
126
- "year": [
127
- "2010"
128
- ],
129
- "comment": [
130
- "Some comment here"
131
- ]
132
- }
108
+ ```console
109
+ $ python3 -m tinytag /some/music.mp3
110
+ {
111
+ "filename": "/some/music.mp3",
112
+ "filesize": 3243226,
113
+ "mime_type": "audio/mpeg",
114
+ "is_lossless": false,
115
+ "duration": 173.52,
116
+ "channels": 2,
117
+ "bitrate": 128,
118
+ "samplerate": 44100,
119
+ "artist": [
120
+ "artist name"
121
+ ],
122
+ "album": [
123
+ "album name"
124
+ ],
125
+ "title": [
126
+ "track name"
127
+ ],
128
+ "track": [
129
+ "4"
130
+ ],
131
+ "genre": [
132
+ "Jazz"
133
+ ],
134
+ "year": [
135
+ "2010"
136
+ ],
137
+ "comment": [
138
+ "Some comment here"
139
+ ]
140
+ }
141
+ ```
133
142
 
134
143
  Check `python3 -m tinytag --help` for all CLI options, for example other
135
144
  output formats.
@@ -156,12 +165,17 @@ These are helpful when you need quick access to common metadata.
156
165
 
157
166
  #### File/Audio Properties
158
167
 
159
- tag.bitdepth # bit depth as integer (for lossless audio)
160
- tag.bitrate # bitrate in kBits/s as float
161
- tag.duration # audio duration in seconds as float
162
- tag.filename # filename as string
163
- tag.filesize # file size in bytes as integer
164
- tag.samplerate # samples per second as integer
168
+ ```python
169
+ tag.filename: str | None # file name
170
+ tag.filesize: int # file size in bytes
171
+ tag.bitdepth: int | None # audio bit depth
172
+ tag.bitrate: float | None # audio bitrate in kbps
173
+ tag.channels: int | None # number of audio channels
174
+ tag.duration: float | None # audio duration in seconds
175
+ tag.is_lossless: bool | None # is audio lossless? (added in tinytag 2.3.0)
176
+ tag.mime_type: str | None # audio MIME type (added in tinytag 2.3.0)
177
+ tag.samplerate: int | None # audio samples per second
178
+ ```
165
179
 
166
180
  > [!WARNING]
167
181
  > The `tag.audio_offset` attribute is obsolete as of tinytag 2.0.0, and will
@@ -169,25 +183,75 @@ These are helpful when you need quick access to common metadata.
169
183
 
170
184
  #### Metadata Fields
171
185
 
172
- tag.album # album as string
173
- tag.albumartist # album artist as string
174
- tag.artist # artist name as string
175
- tag.comment # file comment as string
176
- tag.composer # composer as string
177
- tag.disc # disc number as integer
178
- tag.disc_total # total number of discs as integer
179
- tag.genre # genre as string
180
- tag.title # title of the song as string
181
- tag.track # track number as integer
182
- tag.track_total # total number of tracks as integer
183
- tag.year # year or date as string
186
+ ```python
187
+ tag.album: str | None # album name
188
+ tag.albumartist: str | None # album artist name
189
+ tag.artist: str | None # artist name
190
+ tag.comment: str | None # file comment
191
+ tag.composer: str | None # composer name
192
+ tag.disc: int | None # disc number
193
+ tag.disc_total: int | None # total number of discs
194
+ tag.genre: str | None # genre
195
+ tag.title: str | None # title
196
+ tag.track: int | None # track number
197
+ tag.track_total: int | None # total number of tracks
198
+ tag.year: str | None # year/date
199
+ ```
200
+
201
+ #### Audio MIME Types
202
+
203
+ At present, tinytag may return the audio MIME types documented in this section.
204
+ When available, a `codecs` parameter as described in the [RFC 6381](https://www.rfc-editor.org/info/rfc6381)
205
+ standard is included.
206
+
207
+ For standard codec values, see:
208
+ * `audio/mp4`: [RFC 6381](https://www.rfc-editor.org/info/rfc6381)
209
+ * `audio/ogg`: [RFC 5334](https://www.rfc-editor.org/info/rfc5334)
210
+ and [RFC 7845](https://www.rfc-editor.org/info/rfc7845)
211
+ * `audio/wav`: [RFC 2361](https://www.rfc-editor.org/info/rfc2361)
212
+
213
+ No official standards exists for `codecs` values for the
214
+ `application/vnd.ms-asf` and `audio/aiff` MIME types. The most appropriate
215
+ value provided by the format is used.
216
+
217
+ # ASF
218
+ application/vnd.ms-asf
219
+ application/vnd.ms-asf; codecs="<1..>" # WAVE format tag, e.g. "161" (WMA)
220
+
221
+ # AIFF
222
+ audio/aiff
223
+ audio/aiff; codecs="<FourCC>" # AIFF-C compression type, e.g. "alaw"
224
+
225
+ # FLAC
226
+ audio/flac
227
+
228
+ # MP4
229
+ audio/mp4
230
+ audio/mp4; codecs="alac"
231
+ audio/mp4; codecs="mp4a.oo[.A]" # E.g. "mp4a.40.2" for AAC-LC
232
+
233
+ # MP3 / MP2 / MP1
234
+ audio/mpeg
235
+
236
+ # OGG
237
+ audio/ogg
238
+ audio/ogg; codecs="flac"
239
+ audio/ogg; codecs="opus"
240
+ audio/ogg; codecs="speex"
241
+ audio/ogg; codecs="vorbis"
242
+
243
+ # WAV
244
+ audio/wav
245
+ audio/wav; codecs="<1..>" # WAVE format tag, e.g. "1" for PCM
184
246
 
185
247
  ### Additional Metadata
186
248
 
187
249
  For additional values of the same field type, uncommon metadata fields, or
188
250
  metadata specific to certain file formats, use `other`:
189
251
 
190
- tag.other # a dictionary of additional fields
252
+ ```python
253
+ tag.other: OtherFields # a dictionary of additional fields
254
+ ```
191
255
 
192
256
  > [!WARNING]
193
257
  > The `other` dictionary has replaced the `extra` dictionary in tinytag 2.0.0.
@@ -220,6 +284,7 @@ present when files provide such metadata:
220
284
  show_movement
221
285
  url
222
286
  work
287
+ xmp
223
288
 
224
289
  Additional `other` field names not documented above may be present, but are
225
290
  format-specific and may change or disappear in future tinytag releases. If
@@ -252,8 +317,10 @@ print(catalog_numbers)
252
317
 
253
318
  Output:
254
319
 
255
- > 10
256
- > ['10']
320
+ ```console
321
+ > 10
322
+ > ['10']
323
+ ```
257
324
 
258
325
  When a file contains multiple values for a [common metadata field](#common-metadata)
259
326
  (e.g. `artist`), the primary value is accessed through the common attribute
@@ -275,8 +342,10 @@ print(additional_artists)
275
342
 
276
343
  Output:
277
344
 
278
- > main artist
279
- > ['another artist', 'yet another artist']
345
+ ```console
346
+ > main artist
347
+ > ['another artist', 'yet another artist']
348
+ ```
280
349
 
281
350
  ### All Metadata
282
351
 
@@ -284,10 +353,12 @@ If you need to receive all available metadata as key-value pairs in a flat
284
353
  dictionary, use the `as_dict()` method. This combines the common attributes
285
354
  and `other` dictionary, which can be more convenient in some cases.
286
355
 
287
- from tinytag import TinyTag
356
+ ```python
357
+ from tinytag import TinyTag
288
358
 
289
- tag: TinyTag = TinyTag.get('/some/music.mp3')
290
- metadata: dict = tag.as_dict()
359
+ tag: TinyTag = TinyTag.get('/some/music.mp3')
360
+ metadata: dict = tag.as_dict()
361
+ ```
291
362
 
292
363
  ### Images
293
364
 
@@ -297,14 +368,18 @@ keyword argument to `TinyTag.get()`.
297
368
  If you need to receive an image of a specific kind, including its description,
298
369
  use `images`:
299
370
 
300
- tag.images # available embedded images
371
+ ```python
372
+ tag.images: Images # available embedded images
373
+ ```
301
374
 
302
375
  The following common image attributes are available, providing the first
303
- located image of each kind:
376
+ located `Image` object of each kind:
304
377
 
305
- tag.images.front_cover # front cover as 'Image' object
306
- tag.images.back_cover # back cover as 'Image' object
307
- tag.images.media # media (e.g. CD label) as 'Image' object
378
+ ```python
379
+ tag.images.front_cover: Image # front cover
380
+ tag.images.back_cover: Image # back cover
381
+ tag.images.media: Image # media (e.g. CD label)
382
+ ```
308
383
 
309
384
  When present, any additional images are available in an `images.other`
310
385
  dictionary, using the following standardized key names:
@@ -336,10 +411,13 @@ Provided values are always lists containing at least one `Image` object.
336
411
 
337
412
  The `Image` object provides the following attributes:
338
413
 
339
- data # image data as bytes
340
- name # image name/kind as string
341
- mime_type # image MIME type as string
342
- description # image description as string
414
+ ```python
415
+ image.data: bytes # image data
416
+ image.size: int # image size in bytes (added in tinytag 2.3.0)
417
+ image.name: str # image name/kind
418
+ image.mime_type: str | None # image MIME type
419
+ image.description: str | None # image description
420
+ ```
343
421
 
344
422
  To receive any available image, prioritizing the front cover, use `images.any`:
345
423
 
@@ -351,11 +429,13 @@ image: Image | None = tag.images.any
351
429
 
352
430
  if image is not None:
353
431
  data: bytes = image.data
432
+ size: int = image.size
354
433
  name: str = image.name
355
- mime_type: str = image.mime_type
356
- description: str = image.description
434
+ mime_type: str | None = image.mime_type
435
+ description: str | None = image.description
357
436
 
358
437
  print(len(data))
438
+ print(size)
359
439
  print(name)
360
440
  print(mime_type)
361
441
  print(description)
@@ -363,10 +443,13 @@ if image is not None:
363
443
 
364
444
  Output:
365
445
 
366
- > 74452
367
- > front_cover
368
- > image/jpeg
369
- > some image description
446
+ ```console
447
+ > 74452
448
+ > 74452
449
+ > front_cover
450
+ > image/jpeg
451
+ > some image description
452
+ ```
370
453
 
371
454
  > [!WARNING]
372
455
  > `tag.images.any` has replaced `tag.get_image()` in tinytag 2.0.0.
@@ -383,7 +466,7 @@ cover_image: Image = images.front_cover
383
466
 
384
467
  if cover_image is not None:
385
468
  data: bytes = cover_image.data
386
- description: str = cover_image.description
469
+ description: str | None = cover_image.description
387
470
  ```
388
471
 
389
472
  To receive an additional image, e.g. `bright_colored_fish`:
@@ -401,6 +484,19 @@ if fish_images:
401
484
  description = image.description
402
485
  ```
403
486
 
487
+ ### Magic Byte Detection
488
+
489
+ By default, tinytag will determine the file type by 1. checking the file name
490
+ extension, and 2. reading and inspecting magic bytes in the file header.
491
+
492
+ In case you want to disable magic byte detection, e.g. to minimize read
493
+ operations when many non-audio files exist in a folder, pass a
494
+ `check_magic_bytes` argument with a value of `False` (added in tinytag 2.3.0).
495
+
496
+ ```python
497
+ tag: TinyTag = TinyTag.get('invalid_file.jpg', check_magic_bytes=False)
498
+ ```
499
+
404
500
  ### Encoding
405
501
 
406
502
  To open files using a specific encoding, you can use the `encoding` parameter.
@@ -408,7 +504,7 @@ This parameter is however only used for formats where the encoding is not
408
504
  explicitly specified.
409
505
 
410
506
  ```python
411
- TinyTag.get('a_file_with_gbk_encoding.mp3', encoding='gbk')
507
+ tag: TinyTag = TinyTag.get('a_file_with_gbk_encoding.mp3', encoding='gbk')
412
508
  ```
413
509
 
414
510
  ### File-like Objects
@@ -417,18 +513,64 @@ To use a file-like object (e.g. BytesIO) instead of a file path, pass a
417
513
  `file_obj` keyword argument:
418
514
 
419
515
  ```python
420
- TinyTag.get(file_obj=your_file_obj)
516
+ tag: TinyTag = TinyTag.get(file_obj=your_file_obj)
421
517
  ```
422
518
 
423
519
  ### Exceptions
424
520
 
425
- TinyTagException # Base class for exceptions
426
- ParseError # Parsing an audio file failed
427
- UnsupportedFormatError # File format is not supported
521
+ ```python
522
+ TinyTagException # Base class for exceptions
523
+ ParseError # Parsing an audio file failed
524
+ UnsupportedFormatError # File format is not supported
525
+ ```
428
526
 
429
527
 
430
528
  ## Changelog
431
529
 
530
+ ### 2.3.0 (2026-07-31)
531
+
532
+ - Add 'mime_type' attribute for audio MIME type/codec
533
+ - Add 'is_lossless' attribute to indicate lossless audio
534
+ - Add 'size' attribute to images for size in bytes
535
+ - Add option to disable magic byte detection
536
+ - Make XMP metadata available under 'other.xmp' field
537
+ - Make bit depth available for lossy files providing it
538
+ - Add .asf, .ogv and .wmv to list of supported file extensions
539
+ - Mark deprecated 'extra' dictionary as obsolete
540
+ - Fix incorrect key when adding common images to 'other' dictionary
541
+ - Fix disc/track numbers not being returned as lists in 'as_dict()'
542
+ - Only set audio properties with a positive value
543
+ - Always read tags and audio properties in a single pass
544
+ - ID3: Parse multi-value text frames correctly
545
+ - ID3: Parse COMM and USLT frames correctly
546
+ - ID3: Fix some errors in parsed size calculations
547
+ - ID3: Exclude a few frames with custom structures
548
+ - MPEG: Read VBRI and Xing Info headers for duration/bitrate
549
+ - MPEG: Fix parsing of layer I and MPEG-2 layer III audio properties
550
+ - MPEG: Fix slightly incorrect duration estimation
551
+ - MPEG/MP4: Raise ParseError if header is invalid
552
+ - MP4: Add 'image/bmp' and 'image/gif' MIME types
553
+ - MP4: Fix infinite loop if size of certain sub-atoms is zero
554
+ - MP4: Fix incorrect parsing of descriptor size
555
+ - MP4: Don't read image data when image reading is disabled
556
+ - OGG: Parse multi-stream files correctly
557
+ - Speex: Don't read vendor string as comment
558
+ - FLAC: Provide actual audio bitrate instead of overall bitrate
559
+ - FLAC: Parse VORBIS_COMMENT and PICTURE blocks with invalid size
560
+ - FLAC: Ignore subsequent STREAMINFO blocks
561
+ - WAV: Include unknown fields in 'other' dictionary
562
+ - WAV: Calculate bitrate/duration correctly
563
+ - AIFF/WAV: Read whole ID3 chunk before parsing
564
+ - ASF: Support parsing additional metadata objects
565
+ - ASF: Stop parsing if object size is smaller than header
566
+ - ASF: Make magic header detection more strict
567
+ - Various performance improvements
568
+ - Various code refactors
569
+
570
+ ### 2.2.1 (2026-03-15)
571
+
572
+ - ID3: Prevent infinite loop due to malformed SYLT strings
573
+
432
574
  ### 2.2.0 (2025-12-15)
433
575
 
434
576
  - Add support for movement, work and grouping fields
@@ -636,7 +778,7 @@ TinyTag.get(file_obj=your_file_obj)
636
778
 
637
779
  - fixed decoding of UTF-16LE ID3v2 Tags, improved overall stability
638
780
 
639
- ### 0.14.0 (2016-06-05):
781
+ ### 0.14.0 (2016-06-05)
640
782
 
641
783
  - MP4/M4A and Opus support
642
784