musikr: do custom picture handling
TagLib's picture handling is inadequate for our use case.
This commit is contained in:
parent
b3f4fdfb4a
commit
e94b74edd4
3 changed files with 57 additions and 22 deletions
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include <taglib/mp4tag.h>
|
#include <taglib/mp4tag.h>
|
||||||
#include <taglib/textidentificationframe.h>
|
#include <taglib/textidentificationframe.h>
|
||||||
|
#include <taglib/attachedpictureframe.h>
|
||||||
|
|
||||||
#include <taglib/tpropertymap.h>
|
#include <taglib/tpropertymap.h>
|
||||||
|
|
||||||
|
@ -33,7 +34,10 @@ void JVMMetadataBuilder::setMimeType(const std::string_view type) {
|
||||||
this->mimeType = type;
|
this->mimeType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JVMMetadataBuilder::setId3v2(const TagLib::ID3v2::Tag &tag) {
|
void JVMMetadataBuilder::setId3v2(TagLib::ID3v2::Tag &tag) {
|
||||||
|
// We want to ideally find the front cover, fall back to the first picture otherwise.
|
||||||
|
std::optional<TagLib::ID3v2::AttachedPictureFrame*> firstPic;
|
||||||
|
std::optional<TagLib::ID3v2::AttachedPictureFrame*> frontCoverPic;
|
||||||
for (auto frame : tag.frameList()) {
|
for (auto frame : tag.frameList()) {
|
||||||
if (auto txxxFrame =
|
if (auto txxxFrame =
|
||||||
dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>(frame)) {
|
dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>(frame)) {
|
||||||
|
@ -50,18 +54,37 @@ void JVMMetadataBuilder::setId3v2(const TagLib::ID3v2::Tag &tag) {
|
||||||
TagLib::String key = frame->frameID();
|
TagLib::String key = frame->frameID();
|
||||||
TagLib::StringList frameText = textFrame->fieldList();
|
TagLib::StringList frameText = textFrame->fieldList();
|
||||||
id3v2.add_id(key, frameText);
|
id3v2.add_id(key, frameText);
|
||||||
|
} else if (auto pictureFrame =
|
||||||
|
dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(frame)) {
|
||||||
|
if (!firstPic) {
|
||||||
|
firstPic = pictureFrame;
|
||||||
|
}
|
||||||
|
if (!frontCoverPic
|
||||||
|
&& pictureFrame->type()
|
||||||
|
== TagLib::ID3v2::AttachedPictureFrame::FrontCover) {
|
||||||
|
frontCoverPic = pictureFrame;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (frontCoverPic) {
|
||||||
|
auto pic = *frontCoverPic;
|
||||||
|
cover = pic->picture();
|
||||||
|
} else if (firstPic) {
|
||||||
|
auto pic = *firstPic;
|
||||||
|
cover = pic->picture();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JVMMetadataBuilder::setXiph(const TagLib::Ogg::XiphComment &tag) {
|
void JVMMetadataBuilder::setXiph(TagLib::Ogg::XiphComment &tag) {
|
||||||
for (auto field : tag.fieldListMap()) {
|
for (auto field : tag.fieldListMap()) {
|
||||||
auto key = field.first.upper();
|
auto key = field.first.upper();
|
||||||
auto values = field.second;
|
auto values = field.second;
|
||||||
xiph.add_custom(key, values);
|
xiph.add_custom(key, values);
|
||||||
}
|
}
|
||||||
|
auto pics = tag.pictureList();
|
||||||
|
setFlacPictures(pics);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -77,11 +100,27 @@ void mp4AddImpl(JVMTagMap &map, TagLib::String &itemName, T itemValue) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JVMMetadataBuilder::setMp4(const TagLib::MP4::Tag &tag) {
|
void JVMMetadataBuilder::setMp4(TagLib::MP4::Tag &tag) {
|
||||||
auto map = tag.itemMap();
|
auto map = tag.itemMap();
|
||||||
|
std::optional < TagLib::MP4::CoverArt > firstCover;
|
||||||
for (auto item : map) {
|
for (auto item : map) {
|
||||||
auto itemName = item.first;
|
auto itemName = item.first;
|
||||||
auto itemValue = item.second;
|
auto itemValue = item.second;
|
||||||
|
if (itemName == "covr") {
|
||||||
|
// Special cover case.
|
||||||
|
// MP4 has no types, so just prioritize easier to decode covers (PNG, JPEG)
|
||||||
|
auto pics = itemValue.toCoverArtList();
|
||||||
|
for (auto &pic : pics) {
|
||||||
|
auto format = pic.format();
|
||||||
|
if (format == TagLib::MP4::CoverArt::PNG
|
||||||
|
|| format == TagLib::MP4::CoverArt::JPEG) {
|
||||||
|
cover = pic.data();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cover = pics.front().data();
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto type = itemValue.type();
|
auto type = itemValue.type();
|
||||||
std::string serializedValue;
|
std::string serializedValue;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -114,23 +153,18 @@ void JVMMetadataBuilder::setMp4(const TagLib::MP4::Tag &tag) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JVMMetadataBuilder::setCover(
|
void JVMMetadataBuilder::setFlacPictures(
|
||||||
const TagLib::List<TagLib::VariantMap> covers) {
|
TagLib::List<TagLib::FLAC::Picture*> &pics) {
|
||||||
if (covers.isEmpty()) {
|
// Find the front cover image. If it doesn't exist, fall back to the first image.
|
||||||
return;
|
for (auto pic : pics) {
|
||||||
}
|
if (pic->type() == TagLib::FLAC::Picture::FrontCover) {
|
||||||
// Find the cover with a "front cover" type
|
cover = pic->data();
|
||||||
for (auto cover : covers) {
|
|
||||||
auto type = cover["pictureType"].toString();
|
|
||||||
if (type == "Front Cover") {
|
|
||||||
this->cover = cover["data"].toByteVector();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// No front cover, just pick first.
|
if (!pics.isEmpty()) {
|
||||||
// TODO: Consider having cascading fallbacks to increasingly less
|
cover = pics.front()->data();
|
||||||
// relevant covers perhaps
|
}
|
||||||
this->cover = covers.front()["data"].toByteVector();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JVMMetadataBuilder::setProperties(TagLib::AudioProperties *properties) {
|
void JVMMetadataBuilder::setProperties(TagLib::AudioProperties *properties) {
|
||||||
|
|
|
@ -35,10 +35,10 @@ public:
|
||||||
JVMMetadataBuilder(JNIEnv *env);
|
JVMMetadataBuilder(JNIEnv *env);
|
||||||
|
|
||||||
void setMimeType(const std::string_view type);
|
void setMimeType(const std::string_view type);
|
||||||
void setId3v2(const TagLib::ID3v2::Tag &tag);
|
void setId3v2(TagLib::ID3v2::Tag &tag);
|
||||||
void setXiph(const TagLib::Ogg::XiphComment &tag);
|
void setXiph(TagLib::Ogg::XiphComment &tag);
|
||||||
void setMp4(const TagLib::MP4::Tag &tag);
|
void setMp4(TagLib::MP4::Tag &tag);
|
||||||
void setCover(const TagLib::List<TagLib::VariantMap> covers);
|
void setFlacPictures(TagLib::List<TagLib::FLAC::Picture*> &pics);
|
||||||
void setProperties(TagLib::AudioProperties *properties);
|
void setProperties(TagLib::AudioProperties *properties);
|
||||||
|
|
||||||
jobject build();
|
jobject build();
|
||||||
|
|
|
@ -66,6 +66,8 @@ Java_org_oxycblt_musikr_metadata_TagLibJNI_openNative(JNIEnv *env,
|
||||||
if (xiphComment != nullptr) {
|
if (xiphComment != nullptr) {
|
||||||
builder.setXiph(*xiphComment);
|
builder.setXiph(*xiphComment);
|
||||||
}
|
}
|
||||||
|
auto pics = flacFile->pictureList();
|
||||||
|
builder.setFlacPictures(pics);
|
||||||
} else if (auto *opusFile = dynamic_cast<TagLib::Ogg::Opus::File *>(file)) {
|
} else if (auto *opusFile = dynamic_cast<TagLib::Ogg::Opus::File *>(file)) {
|
||||||
builder.setMimeType("audio/opus");
|
builder.setMimeType("audio/opus");
|
||||||
auto tag = opusFile->tag();
|
auto tag = opusFile->tag();
|
||||||
|
@ -92,7 +94,6 @@ Java_org_oxycblt_musikr_metadata_TagLibJNI_openNative(JNIEnv *env,
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setProperties(file->audioProperties());
|
builder.setProperties(file->audioProperties());
|
||||||
builder.setCover(file->tag()->complexProperties("PICTURE"));
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
} catch (std::runtime_error e) {
|
} catch (std::runtime_error e) {
|
||||||
LOGE("Error opening file: %s", e.what());
|
LOGE("Error opening file: %s", e.what());
|
||||||
|
|
Loading…
Reference in a new issue