Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Id3Taginator is a ID3v1, ID3v2.2/3/4 tag reader and writer fully written in Ruby and does not rely on TagLib or any other 3rd party library to read/write iD3 Tags. It aims to offer a simple way to read and write ID3Tags. It follows the specifications as seen here:
It can write ID3v1 and ID3v2 to the same file. For ID3v2 only 1 version can be used. It is recommended to stick to ID3v2.3, because for now, it seems to be the most supported version.
from rubygems
gem 'id3taginator'
or from the sources using
gem build id3taginator.gemspec
and then execute
gem install id3taginator-x.x.x.gem
The following frames are implemented and fully supported. Not implemented frames can still be added by using a custom frame. See more about custom frames in the Usage part. Most Frames are self explaining. For other documentation about the frames and their meaning, please check the documentation - ID3v1, ID3v2.2, Id3v2.3, Id3v2.4
Hint: If a frame is currently not implemented, it can still be read and written using Custom Frames. In this case, if an MP3 file is written that contains a not implemented frame, that frame will simply be saved as a Custom Frame, can be modified as such and will be written as it was once the file is saved.
Implemented | Frame | Description | ID3v2.2 | ID3v2.3 | ID3v2.4 |
---|---|---|---|---|---|
x | AENC/CRA | Audio encryption | x | x | x |
x | APIC/PIC | Attached picture | x | x | x |
ASPI | Audio seek point index | x | |||
x | COMM/COM | Comments | x | x | x |
COMR | Commercial frame | x | x | ||
CRM | Encrypted meta frame | x | |||
x | ENCR | Encryption method registration | x | x | |
EQUA/EQU | Equalization | x | x | ||
EQU2 | Equalization (2) | x | |||
ETCO/ETC | Event timing codes | x | x | x | |
x | GEOB/GEO | General encapsulated object | x | x | x |
x | GRID | Group identification registration | x | x | |
x | GRP1 | iTunes Grouping | x | x | |
x | IPLS/PLS | Involved people list | x | x | x |
LINK/LNK | Linked information | x | x | x | |
x | MCDI/MCI | Music CD identifier | x | x | x |
MLLT/MLL | MPEG location lookup table | x | x | x | |
x | PCNT/CNT | Play counter | x | x | x |
x | POPM/POP | Popularimeter | x | x | x |
POSS | Position synchronisation frame | x | x | ||
x | RBUF/BUF | Recommended buffer size | x | x | x |
RVAD/RVA | Relative volume adjustment | x | x | ||
RVA2 | Relative volume adjustment (2) | x | |||
RVRB/REV | Reverb | x | x | x | |
SEEK | Seek frame | x | |||
SIGN | Signature frame | x | |||
SYLT/SLT | Synchronized lyric/text | x | x | x | |
SYTC/STC | Synchronized tempo codes | x | x | x | |
x | OWNE | Ownership frame | x | x | |
x | PRIV | Private frame | x | x | |
x | TALB/TAL | Album/Movie/Show title | x | x | x |
x | TBPM/TBP | BPM (beats per minute) | x | x | x |
x | TCOM/TCM | Composer | x | x | x |
x | TCON/TCO | Content type | x | x | x |
x | TCOP/TCR | Copyright message | x | x | x |
x | TDAT/TDA | Date | x | x | x |
TDEN | Encoding time | x | |||
x | TDLY/TDY | Playlist delay | x | x | x |
TDOR | Original release time | x | |||
TDRC | Recording time | x | |||
TDRL | Release time | x | |||
TDTG | Tagging time | x | |||
x | TENC/TEN | Encoded by | x | x | x |
x | TEXT/TXT | Lyricist/Text writer | x | x | x |
x | TFLT/TFT | File type | x | x | x |
x | TIME/TIM | Time | x | x | x |
x | TIT1/TT1 | Content group description | x | x | x |
x | TIT2/TT2 | Title/songname/content description | x | x | x |
x | TIT3/TT3 | Subtitle/Description refinement | x | x | x |
x | TKEY/TKE | Initial key | x | x | x |
x | TLAN/TLA | Language(s) | x | x | x |
x | TLEN/TLE | Length | x | x | x |
TMCL | Musician credits list | x | |||
x | TMED/TMT | Media type | x | x | x |
TMOO | Mood | x | |||
x | TOAL/TOT | Original album/movie/show title | x | x | x |
x | TOFN/TOF | Original filename | x | x | x |
x | TOLY/TOL | Original lyricist(s)/text writer(s) | x | x | x |
x | TOPE/TOA | Original artist(s)/performer(s) | x | x | x |
x | TORY/TOR | Original release year | x | x | x |
x | TOWN | File owner/licensee | x | x | |
x | TPE1/TP1 | Lead performer(s)/Soloist(s) | x | x | x |
x | TPE2/TP2 | Band/orchestra/accompaniment | x | x | x |
x | TPE3/TP3 | Conductor/performer refinement | x | x | x |
x | TPE4/TP4 | Interpreted, remixed, or otherwise modified by | x | x | x |
x | TPOS/TPA | Part of a set | x | x | x |
TPRO | Produced notice | x | |||
x | TPUB/TPB | Publisher | x | x | x |
x | TRCK/TRK | Track number/Position in set | x | x | x |
x | TRDA/TRD | Recording dates | x | x | x |
x | TRSN | Internet radio station name | x | x | |
TRSO | Internet radio station owner | x | |||
x | TSIZ/TSI | Size | x | x | |
x | TSOA | Album sort order | x | ||
x | TSOP | Performer sort order | x | ||
x | TSOT | Title sort order | x | ||
x | TSRC/TRC | ISRC (international standard recording code) | x | x | x |
x | TSSE/TSS | Software/Hardware and settings used for encoding | x | x | x |
TSST | Set subtitle | x | |||
x | TXXX/TXX | User defined text information frame | x | x | x |
x | TYER/TYE | Year | x | x | x |
x | UFID/UFI | Unique file identifier | x | x | x |
x | USER | Terms of use | x | x | |
x | USLT/ULT | Unsynchronized lyric/text transcription | x | x | x |
x | WCOM/WCM | Commercial information | x | x | x |
x | WCOP/WCP | Copyright/Legal information | x | x | x |
x | WOAF/WAF | Official audio file webpage | x | x | x |
x | WOAR/WAR | Official artist/performer webpage | x | x | x |
x | WOAS/WAS | Official audio source webpage | x | x | x |
x | WORS | Official internet radio station homepage | x | x | |
x | WPAY | Payment | x | x | |
x | WPUB/WPB | Publishers official webpage | x | x | x |
x | WXXX/WXX | User defined URL link frame | x | x | x |
The usage is pretty straight forward. The audio file will be read and parsed, and provides access to all available frames, allows to add or remove frames and finally, write the file.
To modify and/or read a tag, simply create an audio file:
audio_file = Id3Taginator.build_by_path('path/to/audio_file')
this will automatically parse the ID3 tags, if an ID3tag is present.
v1_tag = audio_file.id3v1_tag
v1_tag.title # => 'ID3v1 Title'
v1_tag.artist # => 'ID3v1 Artist'
v1_tag.album # => 'ID3v1 Album'
# this can either be a ID3v2.2, ID3v2.3 or ID3v2.4 tag
v2_tag = audio_file.id3v2_tag
v2_tag.version # => 2.3.0
v2_tag.title # => 'ID3v2 Title'
v2_tag.artists # => ['ID3v2 Artist1', 'ID3v2 Artist2']
v2_tag.album # => 'ID3v2 Album'
existing tags can either be modified, or a new tag can be created, or an existing one removed.
audio_file.remove_id3v1_tag
audio_file.remove_id3v2_tag
will remove the ID3vX tags.
There are already methods defined to modify a frame. Each frame has one. A Frame that is only allowed once, has only one Getter and one Setter. If a Frame is present, the Setter will update the existing Frame. If no Frame it is present, it will create a new Frame.
If the Frame can be present multiple times, the Setter Method will update the existing frame, if a specific field with the unique identifier is the same (see the ruby doc to find out which field is the unique field).
In those cases, the Setter is field=
or alternatively add_field
, e.g. comment=
and add_comment(...)
are the same.
If no Frame with the unique field is present, a new Frame will be added.
The Getter methods will return nil if the Frame is not present or en empty array, if the frame is not present, but the result is an array.
v1_tag = audio_file.id3v1_tag
v1_tag.title = 'My new Title'
v1_tag.title # => 'My new Title'
v1_tag.title = nil
v1_tag.title # => nil
v2_tag = audio_file.id3v2_tag
v2_tag.title = 'My new Title'
v2_tag.title # => 'My new Title'
v2_tag.remove_title
v2_tag.title # => nil
v2_tag.languages # => []
v2_tag.languages = %w[eng ger]
v2_tag.languages # ['eng', 'ger']
v2_tag.languages = %w[eng]
v2_tag.languages # ['eng']
v2_tag.remove_languages
v2_tag.languages # => []
v2_tag.comments # => []
v2_tag.add_comment(Id3Taginator.create_comment('eng', 'my descriptor', 'my comment'))
v2_tag.comments # => [Comment('eng', 'my descriptor', 'my comment')]
v2_tag.add_comment(Id3Taginator.create_comment('eng', 'another descriptor', 'my other comment'))
v2_tag.comments # => [Comment('eng', 'my descriptor', 'my comment'), Comment('eng', 'another descriptor', 'my other comment')]
v2_tag.add_comment(Id3Taginator.create_comment('eng', 'my descriptor', 'my modified comment'))
# descriptor and language are already present, so it will update the comment instead of creating a new one
v2_tag.comments # => [Comment('eng', 'my descriptor', 'my modified comment'), Comment('eng', 'another descriptor', 'my other comment')]
v2_tag.remove_comment('eng', 'my descriptor')
v2_tag.comments # => [Comment('eng', 'another descriptor', 'my other comment')]
If no tag is present, or a new one should be created, a few simple methods are present:
audio_file.create_id3v1_tag # => the id3v1_tag
audio_file.create_id3v2_2_tag # => the id3v22_tag
audio_file.create_id3v2_3_tag # => the id3v23_tag
audio_file.create_id3v2_4_tag # => the id3v24_tag
As mentioned before, it is recommended to use ID3v2.3, because it seems to be the most supported for version.
ID3v2.2 is the oldest ID3v2 tag, and for example only uses 3 characters for the Frame ID and offers far less Frames as the successors. It should be avoided, because it is kinda outdated.
ID3v2.3 and ID3v2.4 are very similar. ID3v2.4 added a few more frames, and allows 2 more encodings in comparison to ID3v2.3,
but most of the new added frames or changes are not game breaking and most probably not used by most people.
Some of the useful new frames like Album Sort Order
or Title Sort Order
are officially not part of ID3v2.3, but most players
will interpret them nevertheless, as long as they are present. Per default ID3Taginator allows to add those ID3v2.4 only frames even to
ID3v2.3 Tags.
Once a file is modified, it must be written to save the changes. To do this, Id3Taginator provides to 2 methods:
audio_file.write_audio_file('path/to/new/file')
Which writes the modified file to the specified path. The audio data will be cached, so the file can be written to the same path that is used to read the MP3 file, essentially overwriting it.
The other alternative is to write the result to a byte array represented as a String:
audio_file.audio_file_to_bytes
This will return a byte array represented as a String (str.bytes) with the modified audio file, and the decision how to handle it, is completely yours.
The following options are available:
This is the encoding that should be used to encode the data if applicable. For example most Text Frames are encoded and the encoding byte is prefixed to the encoded data.
Default is UTF-16
This is more or less only for internal use and doesn't affect the written tag. This encoding is used to decode the tag if an encoding
is applied and later on, when the tag is written the default_encode_dest
is used again.
Default is UTF-8
After the last Frame is written, optionally padding null bytes can be written. Padding bytes can for example be used to add additionally information,
e.g. an ID3 Footer
without rewriting the whole tag.
Default is 20
Some Frames are available for ID3v2.4 but not for ID3v2.3, for example the Sort Order frames like TSOT. But some application still interpret them correctly, hence they can be used in ID3v2.3 even if they are not in the specifications. When this flag is true, no error will be raised if an invalid Frame should be added to the Tag. In those cases it is for example possible to add an ID3v2.4 only Tag to an ID3v2.3 Tag.
Default is true
Same as ignore_v23_frame_error
just for ID3v2.4 Tag.
Default is true
ID3v2.3 has a size (TSIZ) frame that contains the file size in bytes without ID3v2 Tag size. When this flag is true this Frame will be added automatically if it is not present.
Note: In ID3v2.4 this Frame was removed. So in order to use this flag for ID3v2.4 Tags, the flag ignore_v24_frame_error
must be true
too.
Default is false
Options can be set on a global level, then those options will be applied to all Audio Files that are created, or the option is only applied to one Audio File.
To define global options, simply create an Id3Taginator
instance and apply the options:
id3taginator = Id3Taginator.global_options
.default_encode_for_destination(Encoding::UTF_16)
.default_decode_for_destination(Encoding::UTF_8)
.tag_padding(42)
.ignore_v23_frame_error(true)
.ignore_v24_frame_error(false)
.add_size_frame(true)
audio_file = id3taginator.build_by_path('path/to/file')
# => options are applied
another_audio_file = id3taginator.build_by_path('path/to/another/file')
# => options are applied too
if options should be applied to one audio file, create the file and apply the options directly to this file
audio_file = Id3Taginator.build_by_path('path/to/file')
audio_file.default_encode_for_destination(Encoding::UTF_16)
.default_decode_for_destination(Encoding::UTF_8)
.tag_padding(42)
.ignore_v23_frame_error(true)
.ignore_v24_frame_error(false)
.add_size_frame(true)
Here, the options are only applied to this Audio File instance. All other created instance use the default options.
Some Setter such as copyright=
or add_picture
require an Entity Instance as an argument. All arguments can be created
like this
Id3Taginator.create_buffer(42, false, 5)
Id3Taginator.create_picture_from_data('image/png', :COVER_FRONT, 'Description', 'Some picture data')
There is a create method for all Entities.
If a frame is currently not implemented, but is required, it can still be added or modified using Custom Frames. If the Frame is already present in the ID3v2 Tag, then it will be available as a Custom Frame. Custom Frames can be added and modified as all other Frames
# CSTM is the frame id of the frame we want to fetch
my_custom_frame = v2_tag.custom_frame('CSTM')
frame_content = my_custom_frame.content
# modify the content
v2_tag.add_custom_frame('CSTM', frame_content)
v2_tag.add_custom_frame('ABCD', 'my new frame content')
Here, the old content is replaced by the new content, and a new custom Frame is added.
If multiple custom frames with the same Frame ID should exist, an optional selector_lambda
can be passed as an argument.
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
.
Bug reports and pull requests are welcome on GitHub at https://github.com/cfe86/id3taginator. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the Id3taginator project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.
FAQs
Unknown package
We found that id3taginator demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.