Skip to content

Commit

Permalink
Use TagLib to detect whether a media file has embedded cover or not
Browse files Browse the repository at this point in the history
  • Loading branch information
deluan committed Jul 24, 2021
1 parent 9132507 commit 6c55081
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 30 deletions.
29 changes: 0 additions & 29 deletions scanner/metadata/taglib.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package metadata

import (
"os"

"github.com/dhowden/tag"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/scanner/metadata/taglib"
)
Expand All @@ -25,10 +22,6 @@ func (e *taglibExtractor) extractMetadata(filePath string) (*Tags, error) {
parsedTags, err := taglib.Read(filePath)
if err != nil {
log.Warn("Error reading metadata from file. Skipping", "filePath", filePath, err)
} else {
if hasEmbeddedImage(filePath) {
parsedTags["has_picture"] = []string{"true"}
}
}

tags := NewTags(filePath, parsedTags, map[string][]string{
Expand All @@ -41,25 +34,3 @@ func (e *taglibExtractor) extractMetadata(filePath string) (*Tags, error) {

return tags, nil
}

func hasEmbeddedImage(path string) bool {
defer func() {
if r := recover(); r != nil {
log.Error("Panic while checking for images. Please report this error with a copy of the file", "path", path, r)
}
}()
f, err := os.Open(path)
if err != nil {
log.Warn("Error opening file", "filePath", path, err)
return false
}
defer f.Close()

m, err := tag.ReadFrom(f)
if err != nil {
log.Warn("Error reading picture tag from file", "filePath", path, err)
return false
}

return m.Picture() != nil
}
55 changes: 54 additions & 1 deletion scanner/metadata/taglib/taglib_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
#include <typeinfo>

#define TAGLIB_STATIC
#include <asffile.h>
#include <fileref.h>
#include <flacfile.h>
#include <id3v2tag.h>
#include <mp4file.h>
#include <mpegfile.h>
#include <opusfile.h>
#include <tpropertymap.h>
#include <vorbisfile.h>

#include "taglib_parser.h"

char has_cover(const TagLib::FileRef f);

int taglib_read(const char *filename, unsigned long id) {
TagLib::FileRef f(filename, true, TagLib::AudioProperties::Fast);

Expand Down Expand Up @@ -54,13 +61,17 @@ int taglib_read(const char *filename, unsigned long id) {
if (mp3File->ID3v2Tag()) {
const auto &frameListMap(mp3File->ID3v2Tag()->frameListMap());

for (const auto& kv : frameListMap) {
for (const auto &kv : frameListMap) {
if (!kv.second.isEmpty())
tags.insert(kv.first, kv.second.front()->toString());
}
}
}

if (has_cover(f)) {
go_map_put_str(id, (char *)"has_picture", (char *)"true");
}

for (TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end();
++i) {
for (TagLib::StringList::ConstIterator j = i->second.begin();
Expand All @@ -75,3 +86,45 @@ int taglib_read(const char *filename, unsigned long id) {

return 0;
}

char has_cover(const TagLib::FileRef f) {
char hasCover = 0;
// ----- MP3
if (TagLib::MPEG::File *
mp3File{dynamic_cast<TagLib::MPEG::File *>(f.file())}) {
if (mp3File->ID3v2Tag()) {
const auto &frameListMap{mp3File->ID3v2Tag()->frameListMap()};
hasCover = !frameListMap["APIC"].isEmpty();
}
}
// ----- FLAC
else if (TagLib::FLAC::File *
flacFile{dynamic_cast<TagLib::FLAC::File *>(f.file())}) {
hasCover = !flacFile->pictureList().isEmpty();
}
// ----- MP4
else if (TagLib::MP4::File *
mp4File{dynamic_cast<TagLib::MP4::File *>(f.file())}) {
auto &coverItem{mp4File->tag()->itemMap()["covr"]};
TagLib::MP4::CoverArtList coverArtList{coverItem.toCoverArtList()};
hasCover = !coverArtList.isEmpty();
}
// ----- Ogg
else if (TagLib::Ogg::Vorbis::File *
vorbisFile{dynamic_cast<TagLib::Ogg::Vorbis::File *>(f.file())}) {
hasCover = !vorbisFile->tag()->pictureList().isEmpty();
}
// ----- Opus
else if (TagLib::Ogg::Opus::File *
opusFile{dynamic_cast<TagLib::Ogg::Opus::File *>(f.file())}) {
hasCover = !opusFile->tag()->pictureList().isEmpty();
}
// ----- WMA
if (TagLib::ASF::File *
asfFile{dynamic_cast<TagLib::ASF::File *>(f.file())}) {
const TagLib::ASF::Tag *tag{asfFile->tag()};
hasCover = tag && tag->attributeListMap().contains("WM/Picture");
}

return hasCover;
}

0 comments on commit 6c55081

Please sign in to comment.