tinytag 2.2.1__tar.gz → 2.3.1__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.
- tinytag-2.3.1/.editorconfig +19 -0
- tinytag-2.3.1/CONTRIBUTING.md +24 -0
- tinytag-2.3.1/LICENSES/CC0-1.0.txt +121 -0
- tinytag-2.3.1/LICENSES/MIT.txt +1 -0
- tinytag-2.2.1/README.md → tinytag-2.3.1/PKG-INFO +266 -80
- tinytag-2.2.1/PKG-INFO → tinytag-2.3.1/README.md +223 -119
- tinytag-2.3.1/SECURITY.md +17 -0
- {tinytag-2.2.1 → tinytag-2.3.1}/pyproject.toml +25 -5
- {tinytag-2.2.1 → tinytag-2.3.1}/tinytag/__init__.py +1 -1
- {tinytag-2.2.1 → tinytag-2.3.1}/tinytag/tinytag.py +1282 -810
- {tinytag-2.2.1 → tinytag-2.3.1}/LICENSE +0 -0
- {tinytag-2.2.1 → tinytag-2.3.1}/tinytag/__main__.py +0 -0
- {tinytag-2.2.1 → tinytag-2.3.1}/tinytag/py.typed +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 tinytag Contributors
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
root = true
|
|
5
|
+
|
|
6
|
+
[*]
|
|
7
|
+
charset = utf-8
|
|
8
|
+
end_of_line = lf
|
|
9
|
+
indent_size = 4
|
|
10
|
+
indent_style = space
|
|
11
|
+
insert_final_newline = true
|
|
12
|
+
max_line_length = 79
|
|
13
|
+
trim_trailing_whitespace = true
|
|
14
|
+
|
|
15
|
+
[*.md]
|
|
16
|
+
trim_trailing_whitespace = false
|
|
17
|
+
|
|
18
|
+
[*.yml]
|
|
19
|
+
indent_size = 2
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 tinytag Contributors
|
|
3
|
+
SPDX-License-Identifier: MIT
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Contributing
|
|
7
|
+
|
|
8
|
+
Before submitting a PR, please discuss the change you wish to make in an issue
|
|
9
|
+
report or discussion thread, in order to see if it fits the direction of the
|
|
10
|
+
project. If you skip this step, a PR containing anything other than a trivial
|
|
11
|
+
bug fix will likely be rejected.
|
|
12
|
+
|
|
13
|
+
Contributions generated or co-authored by LLMs or AI coding tools are not
|
|
14
|
+
allowed. Your contribution must be licensed as original work that is personally
|
|
15
|
+
authored by you under the terms of the
|
|
16
|
+
[MIT License](https://github.com/tinytag/tinytag/blob/HEAD/LICENSE).
|
|
17
|
+
Every part of your contribution must be written, thoroughly tested and fully
|
|
18
|
+
understood by you so that any queries about the logic of your contribution can
|
|
19
|
+
be answered during the review process.
|
|
20
|
+
|
|
21
|
+
You may only use AI assistants or tooling for learning purposes, including
|
|
22
|
+
guided research of the codebase and exploring concepts. Your contribution must
|
|
23
|
+
not include any content produced by generative AI (code, text snippets, images
|
|
24
|
+
nor other media).
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Creative Commons Legal Code
|
|
2
|
+
|
|
3
|
+
CC0 1.0 Universal
|
|
4
|
+
|
|
5
|
+
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
|
6
|
+
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
|
7
|
+
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
|
8
|
+
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
|
9
|
+
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
|
10
|
+
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
|
11
|
+
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
|
12
|
+
HEREUNDER.
|
|
13
|
+
|
|
14
|
+
Statement of Purpose
|
|
15
|
+
|
|
16
|
+
The laws of most jurisdictions throughout the world automatically confer
|
|
17
|
+
exclusive Copyright and Related Rights (defined below) upon the creator
|
|
18
|
+
and subsequent owner(s) (each and all, an "owner") of an original work of
|
|
19
|
+
authorship and/or a database (each, a "Work").
|
|
20
|
+
|
|
21
|
+
Certain owners wish to permanently relinquish those rights to a Work for
|
|
22
|
+
the purpose of contributing to a commons of creative, cultural and
|
|
23
|
+
scientific works ("Commons") that the public can reliably and without fear
|
|
24
|
+
of later claims of infringement build upon, modify, incorporate in other
|
|
25
|
+
works, reuse and redistribute as freely as possible in any form whatsoever
|
|
26
|
+
and for any purposes, including without limitation commercial purposes.
|
|
27
|
+
These owners may contribute to the Commons to promote the ideal of a free
|
|
28
|
+
culture and the further production of creative, cultural and scientific
|
|
29
|
+
works, or to gain reputation or greater distribution for their Work in
|
|
30
|
+
part through the use and efforts of others.
|
|
31
|
+
|
|
32
|
+
For these and/or other purposes and motivations, and without any
|
|
33
|
+
expectation of additional consideration or compensation, the person
|
|
34
|
+
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
|
35
|
+
is an owner of Copyright and Related Rights in the Work, voluntarily
|
|
36
|
+
elects to apply CC0 to the Work and publicly distribute the Work under its
|
|
37
|
+
terms, with knowledge of his or her Copyright and Related Rights in the
|
|
38
|
+
Work and the meaning and intended legal effect of CC0 on those rights.
|
|
39
|
+
|
|
40
|
+
1. Copyright and Related Rights. A Work made available under CC0 may be
|
|
41
|
+
protected by copyright and related or neighboring rights ("Copyright and
|
|
42
|
+
Related Rights"). Copyright and Related Rights include, but are not
|
|
43
|
+
limited to, the following:
|
|
44
|
+
|
|
45
|
+
i. the right to reproduce, adapt, distribute, perform, display,
|
|
46
|
+
communicate, and translate a Work;
|
|
47
|
+
ii. moral rights retained by the original author(s) and/or performer(s);
|
|
48
|
+
iii. publicity and privacy rights pertaining to a person's image or
|
|
49
|
+
likeness depicted in a Work;
|
|
50
|
+
iv. rights protecting against unfair competition in regards to a Work,
|
|
51
|
+
subject to the limitations in paragraph 4(a), below;
|
|
52
|
+
v. rights protecting the extraction, dissemination, use and reuse of data
|
|
53
|
+
in a Work;
|
|
54
|
+
vi. database rights (such as those arising under Directive 96/9/EC of the
|
|
55
|
+
European Parliament and of the Council of 11 March 1996 on the legal
|
|
56
|
+
protection of databases, and under any national implementation
|
|
57
|
+
thereof, including any amended or successor version of such
|
|
58
|
+
directive); and
|
|
59
|
+
vii. other similar, equivalent or corresponding rights throughout the
|
|
60
|
+
world based on applicable law or treaty, and any national
|
|
61
|
+
implementations thereof.
|
|
62
|
+
|
|
63
|
+
2. Waiver. To the greatest extent permitted by, but not in contravention
|
|
64
|
+
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
|
65
|
+
irrevocably and unconditionally waives, abandons, and surrenders all of
|
|
66
|
+
Affirmer's Copyright and Related Rights and associated claims and causes
|
|
67
|
+
of action, whether now known or unknown (including existing as well as
|
|
68
|
+
future claims and causes of action), in the Work (i) in all territories
|
|
69
|
+
worldwide, (ii) for the maximum duration provided by applicable law or
|
|
70
|
+
treaty (including future time extensions), (iii) in any current or future
|
|
71
|
+
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
|
72
|
+
including without limitation commercial, advertising or promotional
|
|
73
|
+
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
|
74
|
+
member of the public at large and to the detriment of Affirmer's heirs and
|
|
75
|
+
successors, fully intending that such Waiver shall not be subject to
|
|
76
|
+
revocation, rescission, cancellation, termination, or any other legal or
|
|
77
|
+
equitable action to disrupt the quiet enjoyment of the Work by the public
|
|
78
|
+
as contemplated by Affirmer's express Statement of Purpose.
|
|
79
|
+
|
|
80
|
+
3. Public License Fallback. Should any part of the Waiver for any reason
|
|
81
|
+
be judged legally invalid or ineffective under applicable law, then the
|
|
82
|
+
Waiver shall be preserved to the maximum extent permitted taking into
|
|
83
|
+
account Affirmer's express Statement of Purpose. In addition, to the
|
|
84
|
+
extent the Waiver is so judged Affirmer hereby grants to each affected
|
|
85
|
+
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
|
86
|
+
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
|
87
|
+
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
|
88
|
+
maximum duration provided by applicable law or treaty (including future
|
|
89
|
+
time extensions), (iii) in any current or future medium and for any number
|
|
90
|
+
of copies, and (iv) for any purpose whatsoever, including without
|
|
91
|
+
limitation commercial, advertising or promotional purposes (the
|
|
92
|
+
"License"). The License shall be deemed effective as of the date CC0 was
|
|
93
|
+
applied by Affirmer to the Work. Should any part of the License for any
|
|
94
|
+
reason be judged legally invalid or ineffective under applicable law, such
|
|
95
|
+
partial invalidity or ineffectiveness shall not invalidate the remainder
|
|
96
|
+
of the License, and in such case Affirmer hereby affirms that he or she
|
|
97
|
+
will not (i) exercise any of his or her remaining Copyright and Related
|
|
98
|
+
Rights in the Work or (ii) assert any associated claims and causes of
|
|
99
|
+
action with respect to the Work, in either case contrary to Affirmer's
|
|
100
|
+
express Statement of Purpose.
|
|
101
|
+
|
|
102
|
+
4. Limitations and Disclaimers.
|
|
103
|
+
|
|
104
|
+
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
|
105
|
+
surrendered, licensed or otherwise affected by this document.
|
|
106
|
+
b. Affirmer offers the Work as-is and makes no representations or
|
|
107
|
+
warranties of any kind concerning the Work, express, implied,
|
|
108
|
+
statutory or otherwise, including without limitation warranties of
|
|
109
|
+
title, merchantability, fitness for a particular purpose, non
|
|
110
|
+
infringement, or the absence of latent or other defects, accuracy, or
|
|
111
|
+
the present or absence of errors, whether or not discoverable, all to
|
|
112
|
+
the greatest extent permissible under applicable law.
|
|
113
|
+
c. Affirmer disclaims responsibility for clearing rights of other persons
|
|
114
|
+
that may apply to the Work or any use thereof, including without
|
|
115
|
+
limitation any person's Copyright and Related Rights in the Work.
|
|
116
|
+
Further, Affirmer disclaims responsibility for obtaining any necessary
|
|
117
|
+
consents, permissions or other rights required for any use of the
|
|
118
|
+
Work.
|
|
119
|
+
d. Affirmer understands and acknowledges that Creative Commons is not a
|
|
120
|
+
party to this document and has no duty or obligation with respect to
|
|
121
|
+
this CC0 or use of the Work.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../LICENSE
|
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tinytag
|
|
3
|
+
Version: 2.3.1
|
|
4
|
+
Summary: Read audio file metadata
|
|
5
|
+
Keywords: metadata,audio,music,mp3,m4a,wav,ogg,opus,flac,wma,aiff
|
|
6
|
+
Author: Tom Wallroth, Mat (mathiascode)
|
|
7
|
+
Requires-Python: >=3.7
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Classifier: Programming Language :: Python
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Python :: Free Threading :: 4 - Resilient
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
22
|
+
Classifier: Environment :: Web Environment
|
|
23
|
+
Classifier: Intended Audience :: Developers
|
|
24
|
+
Classifier: Operating System :: OS Independent
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
|
+
Classifier: Topic :: Multimedia
|
|
27
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
28
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
|
|
29
|
+
Classifier: Typing :: Typed
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: build ; extra == "dev"
|
|
32
|
+
Requires-Dist: coverage ; extra == "dev"
|
|
33
|
+
Requires-Dist: flit_core ; extra == "dev"
|
|
34
|
+
Requires-Dist: mypy ; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy<1.19.0 ; extra == "dev" and ( platform_python_implementation == 'PyPy')
|
|
36
|
+
Requires-Dist: pycodestyle ; extra == "dev"
|
|
37
|
+
Requires-Dist: pylint ; extra == "dev"
|
|
38
|
+
Requires-Dist: pyright ; extra == "dev"
|
|
39
|
+
Requires-Dist: wheel ; extra == "dev"
|
|
40
|
+
Project-URL: Homepage, https://github.com/tinytag/tinytag
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
|
|
1
43
|
<!--
|
|
2
44
|
SPDX-FileCopyrightText: 2014-2026 tinytag Contributors
|
|
3
45
|
SPDX-License-Identifier: MIT
|
|
@@ -26,12 +68,13 @@ python3 -m pip install tinytag
|
|
|
26
68
|
|
|
27
69
|
* Read tags, images and properties of audio files
|
|
28
70
|
* Supported formats:
|
|
29
|
-
*
|
|
30
|
-
*
|
|
71
|
+
* ID3 v1, v1.1, v2.2, v2.3, v2.4
|
|
72
|
+
* MP3 / MP2 / MP1
|
|
73
|
+
* MP4 (MPEG-4 Audio / AAC / ALAC)
|
|
31
74
|
* WAVE / WAV
|
|
32
75
|
* OGG (FLAC / Opus / Speex / Vorbis)
|
|
33
76
|
* FLAC
|
|
34
|
-
* WMA
|
|
77
|
+
* ASF (WMA)
|
|
35
78
|
* AIFF / AIFF-C
|
|
36
79
|
* Same API for all formats
|
|
37
80
|
* Small, portable library
|
|
@@ -41,7 +84,8 @@ python3 -m pip install tinytag
|
|
|
41
84
|
|
|
42
85
|
> [!IMPORTANT]
|
|
43
86
|
> Support for changing/writing metadata will not be added. Use another library
|
|
44
|
-
> such as [Mutagen](https://mutagen.readthedocs.io/)
|
|
87
|
+
> such as [Mutagen](https://mutagen.readthedocs.io/) or [pytaglib](https://github.com/supermihi/pytaglib)
|
|
88
|
+
> for this.
|
|
45
89
|
|
|
46
90
|
|
|
47
91
|
## Usage
|
|
@@ -64,34 +108,40 @@ print(f'It is {tag.duration:.2f} seconds long.')
|
|
|
64
108
|
|
|
65
109
|
Alternatively you can use tinytag directly on the command line:
|
|
66
110
|
|
|
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
|
-
|
|
111
|
+
```console
|
|
112
|
+
$ python3 -m tinytag /some/music.mp3
|
|
113
|
+
{
|
|
114
|
+
"filename": "/some/music.mp3",
|
|
115
|
+
"filesize": 3243226,
|
|
116
|
+
"mime_type": "audio/mpeg",
|
|
117
|
+
"is_lossless": false,
|
|
118
|
+
"duration": 173.52,
|
|
119
|
+
"channels": 2,
|
|
120
|
+
"bitrate": 128,
|
|
121
|
+
"samplerate": 44100,
|
|
122
|
+
"artist": [
|
|
123
|
+
"artist name"
|
|
124
|
+
],
|
|
125
|
+
"album": [
|
|
126
|
+
"album name"
|
|
127
|
+
],
|
|
128
|
+
"title": [
|
|
129
|
+
"track name"
|
|
130
|
+
],
|
|
131
|
+
"track": [
|
|
132
|
+
"4"
|
|
133
|
+
],
|
|
134
|
+
"genre": [
|
|
135
|
+
"Jazz"
|
|
136
|
+
],
|
|
137
|
+
"year": [
|
|
138
|
+
"2010"
|
|
139
|
+
],
|
|
140
|
+
"comment": [
|
|
141
|
+
"Some comment here"
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
95
145
|
|
|
96
146
|
Check `python3 -m tinytag --help` for all CLI options, for example other
|
|
97
147
|
output formats.
|
|
@@ -118,12 +168,17 @@ These are helpful when you need quick access to common metadata.
|
|
|
118
168
|
|
|
119
169
|
#### File/Audio Properties
|
|
120
170
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
171
|
+
```python
|
|
172
|
+
tag.filename: str | None # file name
|
|
173
|
+
tag.filesize: int # file size in bytes
|
|
174
|
+
tag.bitdepth: int | None # audio bit depth
|
|
175
|
+
tag.bitrate: float | None # audio bitrate in kbps
|
|
176
|
+
tag.channels: int | None # number of audio channels
|
|
177
|
+
tag.duration: float | None # audio duration in seconds
|
|
178
|
+
tag.is_lossless: bool | None # is audio lossless? (added in tinytag 2.3.0)
|
|
179
|
+
tag.mime_type: str | None # audio MIME type (added in tinytag 2.3.0)
|
|
180
|
+
tag.samplerate: int | None # audio samples per second
|
|
181
|
+
```
|
|
127
182
|
|
|
128
183
|
> [!WARNING]
|
|
129
184
|
> The `tag.audio_offset` attribute is obsolete as of tinytag 2.0.0, and will
|
|
@@ -131,25 +186,75 @@ These are helpful when you need quick access to common metadata.
|
|
|
131
186
|
|
|
132
187
|
#### Metadata Fields
|
|
133
188
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
189
|
+
```python
|
|
190
|
+
tag.album: str | None # album name
|
|
191
|
+
tag.albumartist: str | None # album artist name
|
|
192
|
+
tag.artist: str | None # artist name
|
|
193
|
+
tag.comment: str | None # file comment
|
|
194
|
+
tag.composer: str | None # composer name
|
|
195
|
+
tag.disc: int | None # disc number
|
|
196
|
+
tag.disc_total: int | None # total number of discs
|
|
197
|
+
tag.genre: str | None # genre
|
|
198
|
+
tag.title: str | None # title
|
|
199
|
+
tag.track: int | None # track number
|
|
200
|
+
tag.track_total: int | None # total number of tracks
|
|
201
|
+
tag.year: str | None # year/date
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Audio MIME Types
|
|
205
|
+
|
|
206
|
+
At present, tinytag may return the audio MIME types documented in this section.
|
|
207
|
+
When available, a `codecs` parameter as described in the [RFC 6381](https://www.rfc-editor.org/info/rfc6381)
|
|
208
|
+
standard is included.
|
|
209
|
+
|
|
210
|
+
For standard codec values, see:
|
|
211
|
+
* `audio/mp4`: [RFC 6381](https://www.rfc-editor.org/info/rfc6381)
|
|
212
|
+
* `audio/ogg`: [RFC 5334](https://www.rfc-editor.org/info/rfc5334)
|
|
213
|
+
and [RFC 7845](https://www.rfc-editor.org/info/rfc7845)
|
|
214
|
+
* `audio/wav`: [RFC 2361](https://www.rfc-editor.org/info/rfc2361)
|
|
215
|
+
|
|
216
|
+
No official standards exists for `codecs` values for the
|
|
217
|
+
`application/vnd.ms-asf` and `audio/aiff` MIME types. The most appropriate
|
|
218
|
+
value provided by the format is used.
|
|
219
|
+
|
|
220
|
+
# ASF
|
|
221
|
+
application/vnd.ms-asf
|
|
222
|
+
application/vnd.ms-asf; codecs="<1..>" # WAVE format tag, e.g. "161" (WMA)
|
|
223
|
+
|
|
224
|
+
# AIFF
|
|
225
|
+
audio/aiff
|
|
226
|
+
audio/aiff; codecs="<FourCC>" # AIFF-C compression type, e.g. "alaw"
|
|
227
|
+
|
|
228
|
+
# FLAC
|
|
229
|
+
audio/flac
|
|
230
|
+
|
|
231
|
+
# MP4
|
|
232
|
+
audio/mp4
|
|
233
|
+
audio/mp4; codecs="alac"
|
|
234
|
+
audio/mp4; codecs="mp4a.oo[.A]" # E.g. "mp4a.40.2" for AAC-LC
|
|
235
|
+
|
|
236
|
+
# MP3 / MP2 / MP1
|
|
237
|
+
audio/mpeg
|
|
238
|
+
|
|
239
|
+
# OGG
|
|
240
|
+
audio/ogg
|
|
241
|
+
audio/ogg; codecs="flac"
|
|
242
|
+
audio/ogg; codecs="opus"
|
|
243
|
+
audio/ogg; codecs="speex"
|
|
244
|
+
audio/ogg; codecs="vorbis"
|
|
245
|
+
|
|
246
|
+
# WAV
|
|
247
|
+
audio/wav
|
|
248
|
+
audio/wav; codecs="<1..>" # WAVE format tag, e.g. "1" for PCM
|
|
146
249
|
|
|
147
250
|
### Additional Metadata
|
|
148
251
|
|
|
149
252
|
For additional values of the same field type, uncommon metadata fields, or
|
|
150
253
|
metadata specific to certain file formats, use `other`:
|
|
151
254
|
|
|
152
|
-
|
|
255
|
+
```python
|
|
256
|
+
tag.other: OtherFields # a dictionary of additional fields
|
|
257
|
+
```
|
|
153
258
|
|
|
154
259
|
> [!WARNING]
|
|
155
260
|
> The `other` dictionary has replaced the `extra` dictionary in tinytag 2.0.0.
|
|
@@ -182,6 +287,7 @@ present when files provide such metadata:
|
|
|
182
287
|
show_movement
|
|
183
288
|
url
|
|
184
289
|
work
|
|
290
|
+
xmp
|
|
185
291
|
|
|
186
292
|
Additional `other` field names not documented above may be present, but are
|
|
187
293
|
format-specific and may change or disappear in future tinytag releases. If
|
|
@@ -214,8 +320,10 @@ print(catalog_numbers)
|
|
|
214
320
|
|
|
215
321
|
Output:
|
|
216
322
|
|
|
217
|
-
|
|
218
|
-
|
|
323
|
+
```console
|
|
324
|
+
> 10
|
|
325
|
+
> ['10']
|
|
326
|
+
```
|
|
219
327
|
|
|
220
328
|
When a file contains multiple values for a [common metadata field](#common-metadata)
|
|
221
329
|
(e.g. `artist`), the primary value is accessed through the common attribute
|
|
@@ -237,8 +345,10 @@ print(additional_artists)
|
|
|
237
345
|
|
|
238
346
|
Output:
|
|
239
347
|
|
|
240
|
-
|
|
241
|
-
|
|
348
|
+
```console
|
|
349
|
+
> main artist
|
|
350
|
+
> ['another artist', 'yet another artist']
|
|
351
|
+
```
|
|
242
352
|
|
|
243
353
|
### All Metadata
|
|
244
354
|
|
|
@@ -246,10 +356,12 @@ If you need to receive all available metadata as key-value pairs in a flat
|
|
|
246
356
|
dictionary, use the `as_dict()` method. This combines the common attributes
|
|
247
357
|
and `other` dictionary, which can be more convenient in some cases.
|
|
248
358
|
|
|
249
|
-
|
|
359
|
+
```python
|
|
360
|
+
from tinytag import TinyTag
|
|
250
361
|
|
|
251
|
-
|
|
252
|
-
|
|
362
|
+
tag: TinyTag = TinyTag.get('/some/music.mp3')
|
|
363
|
+
metadata: dict = tag.as_dict()
|
|
364
|
+
```
|
|
253
365
|
|
|
254
366
|
### Images
|
|
255
367
|
|
|
@@ -259,14 +371,18 @@ keyword argument to `TinyTag.get()`.
|
|
|
259
371
|
If you need to receive an image of a specific kind, including its description,
|
|
260
372
|
use `images`:
|
|
261
373
|
|
|
262
|
-
|
|
374
|
+
```python
|
|
375
|
+
tag.images: Images # available embedded images
|
|
376
|
+
```
|
|
263
377
|
|
|
264
378
|
The following common image attributes are available, providing the first
|
|
265
|
-
located
|
|
379
|
+
located `Image` object of each kind:
|
|
266
380
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
381
|
+
```python
|
|
382
|
+
tag.images.front_cover: Image # front cover
|
|
383
|
+
tag.images.back_cover: Image # back cover
|
|
384
|
+
tag.images.media: Image # media (e.g. CD label)
|
|
385
|
+
```
|
|
270
386
|
|
|
271
387
|
When present, any additional images are available in an `images.other`
|
|
272
388
|
dictionary, using the following standardized key names:
|
|
@@ -298,10 +414,13 @@ Provided values are always lists containing at least one `Image` object.
|
|
|
298
414
|
|
|
299
415
|
The `Image` object provides the following attributes:
|
|
300
416
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
417
|
+
```python
|
|
418
|
+
image.data: bytes # image data
|
|
419
|
+
image.size: int # image size in bytes (added in tinytag 2.3.0)
|
|
420
|
+
image.name: str # image name/kind
|
|
421
|
+
image.mime_type: str | None # image MIME type
|
|
422
|
+
image.description: str | None # image description
|
|
423
|
+
```
|
|
305
424
|
|
|
306
425
|
To receive any available image, prioritizing the front cover, use `images.any`:
|
|
307
426
|
|
|
@@ -313,11 +432,13 @@ image: Image | None = tag.images.any
|
|
|
313
432
|
|
|
314
433
|
if image is not None:
|
|
315
434
|
data: bytes = image.data
|
|
435
|
+
size: int = image.size
|
|
316
436
|
name: str = image.name
|
|
317
|
-
mime_type: str = image.mime_type
|
|
318
|
-
description: str = image.description
|
|
437
|
+
mime_type: str | None = image.mime_type
|
|
438
|
+
description: str | None = image.description
|
|
319
439
|
|
|
320
440
|
print(len(data))
|
|
441
|
+
print(size)
|
|
321
442
|
print(name)
|
|
322
443
|
print(mime_type)
|
|
323
444
|
print(description)
|
|
@@ -325,10 +446,13 @@ if image is not None:
|
|
|
325
446
|
|
|
326
447
|
Output:
|
|
327
448
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
449
|
+
```console
|
|
450
|
+
> 74452
|
|
451
|
+
> 74452
|
|
452
|
+
> front_cover
|
|
453
|
+
> image/jpeg
|
|
454
|
+
> some image description
|
|
455
|
+
```
|
|
332
456
|
|
|
333
457
|
> [!WARNING]
|
|
334
458
|
> `tag.images.any` has replaced `tag.get_image()` in tinytag 2.0.0.
|
|
@@ -345,7 +469,7 @@ cover_image: Image = images.front_cover
|
|
|
345
469
|
|
|
346
470
|
if cover_image is not None:
|
|
347
471
|
data: bytes = cover_image.data
|
|
348
|
-
description: str = cover_image.description
|
|
472
|
+
description: str | None = cover_image.description
|
|
349
473
|
```
|
|
350
474
|
|
|
351
475
|
To receive an additional image, e.g. `bright_colored_fish`:
|
|
@@ -363,6 +487,19 @@ if fish_images:
|
|
|
363
487
|
description = image.description
|
|
364
488
|
```
|
|
365
489
|
|
|
490
|
+
### Magic Byte Detection
|
|
491
|
+
|
|
492
|
+
By default, tinytag will determine the file type by 1. checking the file name
|
|
493
|
+
extension, and 2. reading and inspecting magic bytes in the file header.
|
|
494
|
+
|
|
495
|
+
In case you want to disable magic byte detection, e.g. to minimize read
|
|
496
|
+
operations when many non-audio files exist in a folder, pass a
|
|
497
|
+
`check_magic_bytes` argument with a value of `False` (added in tinytag 2.3.0).
|
|
498
|
+
|
|
499
|
+
```python
|
|
500
|
+
tag: TinyTag = TinyTag.get('invalid_file.jpg', check_magic_bytes=False)
|
|
501
|
+
```
|
|
502
|
+
|
|
366
503
|
### Encoding
|
|
367
504
|
|
|
368
505
|
To open files using a specific encoding, you can use the `encoding` parameter.
|
|
@@ -370,7 +507,7 @@ This parameter is however only used for formats where the encoding is not
|
|
|
370
507
|
explicitly specified.
|
|
371
508
|
|
|
372
509
|
```python
|
|
373
|
-
TinyTag.get('a_file_with_gbk_encoding.mp3', encoding='gbk')
|
|
510
|
+
tag: TinyTag = TinyTag.get('a_file_with_gbk_encoding.mp3', encoding='gbk')
|
|
374
511
|
```
|
|
375
512
|
|
|
376
513
|
### File-like Objects
|
|
@@ -379,18 +516,66 @@ To use a file-like object (e.g. BytesIO) instead of a file path, pass a
|
|
|
379
516
|
`file_obj` keyword argument:
|
|
380
517
|
|
|
381
518
|
```python
|
|
382
|
-
TinyTag.get(file_obj=your_file_obj)
|
|
519
|
+
tag: TinyTag = TinyTag.get(file_obj=your_file_obj)
|
|
383
520
|
```
|
|
384
521
|
|
|
385
522
|
### Exceptions
|
|
386
523
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
524
|
+
```python
|
|
525
|
+
TinyTagException # Base class for exceptions
|
|
526
|
+
ParseError # Parsing an audio file failed
|
|
527
|
+
UnsupportedFormatError # File format is not supported
|
|
528
|
+
```
|
|
390
529
|
|
|
391
530
|
|
|
392
531
|
## Changelog
|
|
393
532
|
|
|
533
|
+
### 2.3.1 (2026-08-30)
|
|
534
|
+
|
|
535
|
+
- Don't open unrecognized files when disabling magic byte detection
|
|
536
|
+
- MPEG: Fix parsing of some files with incorrect headers
|
|
537
|
+
- OGG/WAV: Read sizes as little endian instead of native byte order
|
|
538
|
+
|
|
539
|
+
### 2.3.0 (2026-07-31)
|
|
540
|
+
|
|
541
|
+
- Add 'mime_type' attribute for audio MIME type/codec
|
|
542
|
+
- Add 'is_lossless' attribute to indicate lossless audio
|
|
543
|
+
- Add 'size' attribute to images for size in bytes
|
|
544
|
+
- Add option to disable magic byte detection
|
|
545
|
+
- Make XMP metadata available under 'other.xmp' field
|
|
546
|
+
- Make bit depth available for lossy files providing it
|
|
547
|
+
- Add .asf, .ogv and .wmv to list of supported file extensions
|
|
548
|
+
- Mark deprecated 'extra' dictionary as obsolete
|
|
549
|
+
- Fix incorrect key when adding common images to 'other' dictionary
|
|
550
|
+
- Fix disc/track numbers not being returned as lists in 'as_dict()'
|
|
551
|
+
- Only set audio properties with a positive value
|
|
552
|
+
- Always read tags and audio properties in a single pass
|
|
553
|
+
- ID3: Parse multi-value text frames correctly
|
|
554
|
+
- ID3: Parse COMM and USLT frames correctly
|
|
555
|
+
- ID3: Fix some errors in parsed size calculations
|
|
556
|
+
- ID3: Exclude a few frames with custom structures
|
|
557
|
+
- MPEG: Read VBRI and Xing Info headers for duration/bitrate
|
|
558
|
+
- MPEG: Fix parsing of layer I and MPEG-2 layer III audio properties
|
|
559
|
+
- MPEG: Fix slightly incorrect duration estimation
|
|
560
|
+
- MPEG/MP4: Raise ParseError if header is invalid
|
|
561
|
+
- MP4: Add 'image/bmp' and 'image/gif' MIME types
|
|
562
|
+
- MP4: Fix infinite loop if size of certain sub-atoms is zero
|
|
563
|
+
- MP4: Fix incorrect parsing of descriptor size
|
|
564
|
+
- MP4: Don't read image data when image reading is disabled
|
|
565
|
+
- OGG: Parse multi-stream files correctly
|
|
566
|
+
- Speex: Don't read vendor string as comment
|
|
567
|
+
- FLAC: Provide actual audio bitrate instead of overall bitrate
|
|
568
|
+
- FLAC: Parse VORBIS_COMMENT and PICTURE blocks with invalid size
|
|
569
|
+
- FLAC: Ignore subsequent STREAMINFO blocks
|
|
570
|
+
- WAV: Include unknown fields in 'other' dictionary
|
|
571
|
+
- WAV: Calculate bitrate/duration correctly
|
|
572
|
+
- AIFF/WAV: Read whole ID3 chunk before parsing
|
|
573
|
+
- ASF: Support parsing additional metadata objects
|
|
574
|
+
- ASF: Stop parsing if object size is smaller than header
|
|
575
|
+
- ASF: Make magic header detection more strict
|
|
576
|
+
- Various performance improvements
|
|
577
|
+
- Various code refactors
|
|
578
|
+
|
|
394
579
|
### 2.2.1 (2026-03-15)
|
|
395
580
|
|
|
396
581
|
- ID3: Prevent infinite loop due to malformed SYLT strings
|
|
@@ -602,6 +787,7 @@ TinyTag.get(file_obj=your_file_obj)
|
|
|
602
787
|
|
|
603
788
|
- fixed decoding of UTF-16LE ID3v2 Tags, improved overall stability
|
|
604
789
|
|
|
605
|
-
### 0.14.0 (2016-06-05)
|
|
790
|
+
### 0.14.0 (2016-06-05)
|
|
606
791
|
|
|
607
792
|
- MP4/M4A and Opus support
|
|
793
|
+
|