Any appetite for a 'normalisation' layer over non-std tags?
michaelhelmling at posteo.de
michaelhelmling at posteo.de
Mon Apr 10 07:36:04 UTC 2017
Dear Adam,
sorry, I've made a few typos in the haste. :-) The following should
work:
auto file = FileRef::create("/path/to/file.mp3"); // create() gives you
a full-featured File object
auto props = file.tag().properties();
for (auto &composer : props["COMPOSER"])
std::cout << "composer: " << composer << std::endl;
The PropertyMap interface does not care about specific tags, be it
"standard" or "exotic"; instead you can access an arbitrary tag by
passing its name as a string. The return value is a list because many
formats support multiple values for the same tag (e.g., multiple
composers).
For the same reason, accessing "TRACKNUMBER" through the generic
propertymap interface gives you the raw textual value as it is in the
file ("03/04"), while the Tag::track() method knows that this should be
a number, so it parses the string "03/04" into the int 3.
Best regards,
Michael
Am 10.04.2017 01:12 schrieb Adam Szmigin:
> Dear Michael,
>
> Many thanks for your reply. Code like the example you pasted would be
> most useful, but what version of TagLib actually has it?
>
> The API docs don't mention any methods like composers():
> http://taglib.org/api/classTagLib_1_1PropertyMap.html
>
> Neither does the PropertyMap header:
> https://github.com/taglib/taglib/blob/master/taglib/toolkit/tpropertymap.h
>
> In fact, I just cloned master and can't find methods like that
> anywhere:
> ~/src/taglib-master$ grep -r composers *
> ~/src/taglib-master$
>
>
>
> Additonally (not sure if relevant to the discussion),
> FileRef::Tag::properties() doesn't actually contain all properties.
> Instead, I have to use xx::File::properties():
>
> string path{"example.mp3"};
>
> TagLib::FileRef fr{path.c_str()};
> cout << "Props using FileRef:" << endl;
> for (auto &prop_kvp : fr.tag()->properties()) {
> for (auto &val : prop_kvp.second) {
> cout << "Prop: " << prop_kvp.first << " -> " << val <<
> endl;
> }
> }
> cout << endl;
>
> TagLib::MPEG::File f{path.c_str()};
> cout << "Props using MPEG::File:" << endl;
> for (auto &prop_kvp : f.properties()) {
> for (auto &val : prop_kvp.second) {
> cout << "Prop: " << prop_kvp.first << " -> " << val <<
> endl;
> }
> }
>
> Prints:
>
> Props using FileRef:
> Prop: ALBUM -> Album field
> Prop: ARTIST -> Artist field
> Prop: COMMENT -> Comment field
> Prop: DATE -> 2017
> Prop: GENRE -> Pop
> Prop: TITLE -> Title field
> Prop: TRACKNUMBER -> 3
>
> Props using MPEG::File:
> Prop: ALBUM -> Album field
> Prop: ALBUMARTIST -> Album artist field
> Prop: ARTIST -> Artist field
> Prop: COMMENT -> Comment field
> Prop: COMPOSER -> Composer field
> Prop: COPYRIGHT -> Copyright field
> Prop: DATE -> 2017
> Prop: DISCNUMBER -> 1/2
> Prop: ENCODEDBY -> Encoded by field
> Prop: GENRE -> Pop
> Prop: ORIGINALARTIST -> Original artist field
> Prop: TITLE -> Title field
> Prop: TRACKNUMBER -> 03/04
> Prop: URL -> URL field
>
> Using xx::File::Tag::properties() similarly contains only a truncated
> list, not all properties that xx::File::properties() has. Also note
> that some properties are expressed differently (e.g. TRACKNUMBER).
> This behaviour is using version 1.9.1-2.4ubuntu1 (it doesn't look from
> initial glance like Debian/Ubuntu have contributed any patches that
> would cause this behaviour).
>
>
> Many thanks,
>
> --
> Adam Szmigin
>
>
> On 09/04/17 22:37, Michael Helmling wrote:
>> Dear Adam,
>>
>> this functionality is already included in taglib - see
>> Tag::properties(), e.g.:
>> FileRef file("/path/to/file.mp3");
>> auto props = file.tag().properties();
>> for (auto &composer : props.composers())
>> std::cout << "composer: " << composer << std::endl;
>>
>>
>> Best regards,
>> Michael
>>
>> On 09.04.2017 11:54, Adam Szmigin wrote:
>>> Dear TagLib devs,
>>>
>>> Firstly, many thanks to all the contributors on TagLib - it is an
>>> excellent library and it has benefited me greatly in a number of
>>> projects I've undertaken.
>>>
>>> For a TagLib user wishing to read "exotic" tags, i.e. those that are
>>> not available via FileRef::tag(), it looks like the user has to
>>> construct a specific File sub-class and work with the different tag()
>>> (or ID3v2Tag() / xiphComment() etc!) methods to get what they want.
>>> How to proceed varies based on the file format, and also to some
>>> extent what tagging application was used that wrote the original
>>> tags.
>>>
>>> The issue I find is that the tags I often work with for music are not
>>> so "exotic", but essential: they include things like album-artist,
>>> disc number, composer. There is no universal standard for these
>>> fields across all file types that I know of.
>>>
>>>
>>> So, I have found myself writing a normalisation layer over the top of
>>> TagLib, which both instantiates the appropriate File sub-class given
>>> a
>>> path, and also tries a few different approaches to get values for
>>> this
>>> extended range of common but non-standardised tag fields. Here is
>>> the
>>> broad interface to give an idea:
>>>
>>> class metadata
>>> {
>>> public:
>>> metadata(const boost::filesystem::path &path);
>>> ~metadata();
>>>
>>> bool has_tag() const;
>>>
>>> std::string album() const;
>>> std::string album_artist() const;
>>> std::string artist() const;
>>> std::string comment() const;
>>> std::string composer() const;
>>> std::string copyright() const;
>>> std::string date() const;
>>> std::string disc_number() const;
>>> std::string disc_total() const;
>>> std::string encoded_by() const;
>>> std::string genre() const;
>>> std::string original_artist() const;
>>> std::string title() const;
>>> std::string track_number() const;
>>> std::string track_total() const;
>>> std::string url() const;
>>> ..
>>> };
>>>
>>> Examples of format-specific manipulation include things like this:
>>>
>>> std::string disc_number() const
>>> {
>>> auto val = prop("DISCNUMBER"); // Uses
>>> TagLib::xx::File::properties()
>>>
>>> // Remove anything after forward slash, if format is "DISC/TOTAL".
>>> auto pos = val.find_first_of('/');
>>> if (pos != std::string::npos)
>>> val.erase(pos);
>>> // Remove leading zeroes
>>> val.erase(0, std::min(val.find_first_not_of('0'), val.size()-1));
>>> return val;
>>> }
>>>
>>>
>>> *My question is:* from a read-only perspective (and maybe
>>> read/write),
>>> is there any appetite for a normalisation layer like this over the
>>> top
>>> of TagLib? It could either be a part of TagLib, or a separate
>>> project.
>>>
>>> (Ignore the use of boost and std::string vs String in the example..
>>> It's just illustrative. The example was taken from a recent project
>>> I'm working on where the "normalisation" layer is not designed to be
>>> separated out..)
>>>
>>>
>>> I'd also appreciate any feedback from the TagLib gurus on whether I'm
>>> "doing it right" regarding the above process of normalisation.
>>>
>>>
>>> Many thanks,
>>>
>>
>>
More information about the taglib-devel
mailing list