Unified interface for complex properties like pictures (#94)

Provides a dynamic interface for properties which cannot be represented
with simple strings, e.g. pictures. The keys of such properties can
be queried using `complexPropertyKeys()`, which could return for example
["PICTURE"]. The property can then be read using
`complexProperties("PICTURE")`, which will return a list of variant maps
containing the picture data and attributes. Adding a picture is as
easy as

    t->setComplexProperties("PICTURE", {
      {
        {"data", data},
        {"pictureType", "Front Cover"},
        {"mimeType", "image/jpeg"}
      }
    });
This commit is contained in:
Urs Fleisch
2023-10-07 09:42:25 +02:00
parent 75d4252480
commit 6be03b7ae1
28 changed files with 1300 additions and 135 deletions

View File

@ -27,6 +27,7 @@
#define TAGLIB_ATTACHEDPICTUREFRAME_H
#include "taglib_export.h"
#include "tpicturetype.h"
#include "id3v2frame.h"
#include "id3v2header.h"
@ -52,50 +53,7 @@ namespace TagLib {
/*!
* This describes the function or content of the picture.
*/
enum Type {
//! A type not enumerated below
Other = 0x00,
//! 32x32 PNG image that should be used as the file icon
FileIcon = 0x01,
//! File icon of a different size or format
OtherFileIcon = 0x02,
//! Front cover image of the album
FrontCover = 0x03,
//! Back cover image of the album
BackCover = 0x04,
//! Inside leaflet page of the album
LeafletPage = 0x05,
//! Image from the album itself
Media = 0x06,
//! Picture of the lead artist or soloist
LeadArtist = 0x07,
//! Picture of the artist or performer
Artist = 0x08,
//! Picture of the conductor
Conductor = 0x09,
//! Picture of the band or orchestra
Band = 0x0A,
//! Picture of the composer
Composer = 0x0B,
//! Picture of the lyricist or text writer
Lyricist = 0x0C,
//! Picture of the recording location or studio
RecordingLocation = 0x0D,
//! Picture of the artists during recording
DuringRecording = 0x0E,
//! Picture of the artists during performance
DuringPerformance = 0x0F,
//! Picture from a movie or video related to the track
MovieScreenCapture = 0x10,
//! Picture of a large, coloured fish
ColouredFish = 0x11,
//! Illustration related to the track
Illustration = 0x12,
//! Logo of the band or performer
BandLogo = 0x13,
//! Logo of the publisher (record company)
PublisherLogo = 0x14
};
DECLARE_PICTURE_TYPE_ENUM(Type)
/*!
* Constructs an empty picture frame. The description, content and text

View File

@ -37,6 +37,8 @@
#include "id3v2footer.h"
#include "id3v2synchdata.h"
#include "id3v1genres.h"
#include "frames/attachedpictureframe.h"
#include "frames/generalencapsulatedobjectframe.h"
#include "frames/textidentificationframe.h"
#include "frames/commentsframe.h"
#include "frames/urllinkframe.h"
@ -451,6 +453,84 @@ PropertyMap ID3v2::Tag::setProperties(const PropertyMap &origProps)
return PropertyMap(); // ID3 implements the complete PropertyMap interface, so an empty map is returned
}
StringList ID3v2::Tag::complexPropertyKeys() const
{
StringList keys;
if(d->frameListMap.contains("APIC")) {
keys.append("PICTURE");
}
if(d->frameListMap.contains("GEOB")) {
keys.append("GENERALOBJECT");
}
return keys;
}
List<VariantMap> ID3v2::Tag::complexProperties(const String &key) const
{
List<VariantMap> properties;
const String uppercaseKey = key.upper();
if(uppercaseKey == "PICTURE") {
const FrameList pictures = d->frameListMap.value("APIC");
for(const Frame *frame : pictures) {
auto picture = static_cast<const AttachedPictureFrame *>(frame);
VariantMap property;
property.insert("data", picture->picture());
property.insert("mimeType", picture->mimeType());
property.insert("description", picture->description());
property.insert("pictureType",
AttachedPictureFrame::typeToString(picture->type()));
properties.append(property);
}
}
else if(uppercaseKey == "GENERALOBJECT") {
const FrameList geobs = d->frameListMap.value("GEOB");
for(const Frame *frame : geobs) {
auto geob = static_cast<const GeneralEncapsulatedObjectFrame *>(frame);
VariantMap property;
property.insert("data", geob->object());
property.insert("mimeType", geob->mimeType());
property.insert("description", geob->description());
property.insert("fileName", geob->fileName());
properties.append(property);
}
}
return properties;
}
bool ID3v2::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
{
const String uppercaseKey = key.upper();
if(uppercaseKey == "PICTURE") {
removeFrames("APIC");
for(auto property : value) {
auto picture = new AttachedPictureFrame;
picture->setPicture(property.value("data").value<ByteVector>());
picture->setMimeType(property.value("mimeType").value<String>());
picture->setDescription(property.value("description").value<String>());
picture->setType(AttachedPictureFrame::typeFromString(
property.value("pictureType").value<String>()));
addFrame(picture);
}
}
else if(uppercaseKey == "GENERALOBJECT") {
removeFrames("GEOB");
for(auto property : value) {
auto geob = new GeneralEncapsulatedObjectFrame;
geob->setObject(property.value("data").value<ByteVector>());
geob->setMimeType(property.value("mimeType").value<String>());
geob->setDescription(property.value("description").value<String>());
geob->setFileName(property.value("fileName").value<String>());
addFrame(geob);
}
}
else {
return false;
}
return true;
}
ByteVector ID3v2::Tag::render() const
{
return render(ID3v2::v4);

View File

@ -329,6 +329,10 @@ namespace TagLib {
*/
PropertyMap setProperties(const PropertyMap &) override;
StringList complexPropertyKeys() const override;
List<VariantMap> complexProperties(const String &key) const override;
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
/*!
* Render the tag back to binary data, suitable to be written to disk.
*/