mirror of
https://github.com/navidrome/navidrome.git
synced 2025-12-23 23:18:05 -05:00
fix(scanner): read cover art from dsf, wavpak, fix wma test (#4296)
* fix(taglib): read cover art from dsf * address feedback and alsi realize wma/wavpack are missing * feedback * more const char and remove unused import
This commit is contained in:
@@ -168,11 +168,49 @@ var _ = Describe("Extractor", func() {
|
|||||||
Entry("FLAC format", "flac"),
|
Entry("FLAC format", "flac"),
|
||||||
Entry("M4a format", "m4a"),
|
Entry("M4a format", "m4a"),
|
||||||
Entry("OGG format", "ogg"),
|
Entry("OGG format", "ogg"),
|
||||||
Entry("WMA format", "wv"),
|
Entry("WV format", "wv"),
|
||||||
|
|
||||||
Entry("MP3 format", "mp3"),
|
Entry("MP3 format", "mp3"),
|
||||||
Entry("WAV format", "wav"),
|
Entry("WAV format", "wav"),
|
||||||
Entry("AIFF format", "aiff"),
|
Entry("AIFF format", "aiff"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
It("should parse wma", func() {
|
||||||
|
path := "tests/fixtures/test.wma"
|
||||||
|
mds, err := e.Parse(path)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
info := mds[path]
|
||||||
|
fileInfo, _ := os.Stat(path)
|
||||||
|
info.FileInfo = testFileInfo{FileInfo: fileInfo}
|
||||||
|
|
||||||
|
metadata := metadata.New(path, info)
|
||||||
|
mf := metadata.ToMediaFile(1, "folderID")
|
||||||
|
|
||||||
|
for _, data := range roles {
|
||||||
|
role := data.Role
|
||||||
|
artists := data.ParticipantList
|
||||||
|
actual := mf.Participants[role]
|
||||||
|
|
||||||
|
// WMA has no Arranger role
|
||||||
|
if role == model.RoleArranger {
|
||||||
|
Expect(actual).To(HaveLen(0))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
Expect(actual).To(HaveLen(len(artists)), role.String())
|
||||||
|
|
||||||
|
// For some bizarre reason, the order is inverted. We also don't get
|
||||||
|
// sort names or MBIDs
|
||||||
|
for i := range artists {
|
||||||
|
idx := len(artists) - 1 - i
|
||||||
|
|
||||||
|
actualArtist := actual[i]
|
||||||
|
expectedArtist := artists[idx]
|
||||||
|
|
||||||
|
Expect(actualArtist.Name).To(Equal(expectedArtist.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ var _ = Describe("Extractor", func() {
|
|||||||
Entry("correctly parses wma/asf tags", "test.wma", "1.02s", 1, 44100, 16, "3.27 dB", "0.132914", "3.27 dB", "0.132914", false, true),
|
Entry("correctly parses wma/asf tags", "test.wma", "1.02s", 1, 44100, 16, "3.27 dB", "0.132914", "3.27 dB", "0.132914", false, true),
|
||||||
|
|
||||||
// ffmpeg -f lavfi -i "sine=frequency=800:duration=1" test.wv
|
// ffmpeg -f lavfi -i "sine=frequency=800:duration=1" test.wv
|
||||||
Entry("correctly parses wv (wavpak) tags", "test.wv", "1s", 1, 44100, 16, "3.43 dB", "0.125061", "3.43 dB", "0.125061", false, false),
|
Entry("correctly parses wv (wavpak) tags", "test.wv", "1s", 1, 44100, 16, "3.43 dB", "0.125061", "3.43 dB", "0.125061", false, true),
|
||||||
|
|
||||||
// ffmpeg -f lavfi -i "sine=frequency=1000:duration=1" test.wav
|
// ffmpeg -f lavfi -i "sine=frequency=1000:duration=1" test.wav
|
||||||
Entry("correctly parses wav tags", "test.wav", "1s", 1, 44100, 16, "3.06 dB", "0.125056", "3.06 dB", "0.125056", true, true),
|
Entry("correctly parses wav tags", "test.wav", "1s", 1, 44100, 16, "3.06 dB", "0.125056", "3.06 dB", "0.125056", true, true),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <typeinfo>
|
|
||||||
|
|
||||||
#define TAGLIB_STATIC
|
#define TAGLIB_STATIC
|
||||||
#include <apeproperties.h>
|
#include <apeproperties.h>
|
||||||
@@ -113,7 +112,7 @@ int taglib_read(const FILENAME_CHAR_T *filename, unsigned long id) {
|
|||||||
strncpy(language, bv.data(), 3);
|
strncpy(language, bv.data(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *val = (char *)frame->text().toCString(true);
|
char *val = const_cast<char*>(frame->text().toCString(true));
|
||||||
|
|
||||||
goPutLyrics(id, language, val);
|
goPutLyrics(id, language, val);
|
||||||
}
|
}
|
||||||
@@ -132,7 +131,7 @@ int taglib_read(const FILENAME_CHAR_T *filename, unsigned long id) {
|
|||||||
if (format == TagLib::ID3v2::SynchronizedLyricsFrame::AbsoluteMilliseconds) {
|
if (format == TagLib::ID3v2::SynchronizedLyricsFrame::AbsoluteMilliseconds) {
|
||||||
|
|
||||||
for (const auto &line: frame->synchedText()) {
|
for (const auto &line: frame->synchedText()) {
|
||||||
char *text = (char *)line.text.toCString(true);
|
char *text = const_cast<char*>(line.text.toCString(true));
|
||||||
goPutLyricLine(id, language, text, line.time);
|
goPutLyricLine(id, language, text, line.time);
|
||||||
}
|
}
|
||||||
} else if (format == TagLib::ID3v2::SynchronizedLyricsFrame::AbsoluteMpegFrames) {
|
} else if (format == TagLib::ID3v2::SynchronizedLyricsFrame::AbsoluteMpegFrames) {
|
||||||
@@ -141,7 +140,7 @@ int taglib_read(const FILENAME_CHAR_T *filename, unsigned long id) {
|
|||||||
if (sampleRate != 0) {
|
if (sampleRate != 0) {
|
||||||
for (const auto &line: frame->synchedText()) {
|
for (const auto &line: frame->synchedText()) {
|
||||||
const int timeInMs = (line.time * 1000) / sampleRate;
|
const int timeInMs = (line.time * 1000) / sampleRate;
|
||||||
char *text = (char *)line.text.toCString(true);
|
char *text = const_cast<char*>(line.text.toCString(true));
|
||||||
goPutLyricLine(id, language, text, timeInMs);
|
goPutLyricLine(id, language, text, timeInMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,9 +159,9 @@ int taglib_read(const FILENAME_CHAR_T *filename, unsigned long id) {
|
|||||||
if (m4afile != NULL) {
|
if (m4afile != NULL) {
|
||||||
const auto itemListMap = m4afile->tag()->itemMap();
|
const auto itemListMap = m4afile->tag()->itemMap();
|
||||||
for (const auto item: itemListMap) {
|
for (const auto item: itemListMap) {
|
||||||
char *key = (char *)item.first.toCString(true);
|
char *key = const_cast<char*>(item.first.toCString(true));
|
||||||
for (const auto value: item.second.toStringList()) {
|
for (const auto value: item.second.toStringList()) {
|
||||||
char *val = (char *)value.toCString(true);
|
char *val = const_cast<char*>(value.toCString(true));
|
||||||
goPutM4AStr(id, key, val);
|
goPutM4AStr(id, key, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,17 +173,24 @@ int taglib_read(const FILENAME_CHAR_T *filename, unsigned long id) {
|
|||||||
const TagLib::ASF::Tag *asfTags{asfFile->tag()};
|
const TagLib::ASF::Tag *asfTags{asfFile->tag()};
|
||||||
const auto itemListMap = asfTags->attributeListMap();
|
const auto itemListMap = asfTags->attributeListMap();
|
||||||
for (const auto item : itemListMap) {
|
for (const auto item : itemListMap) {
|
||||||
tags.insert(item.first, item.second.front().toString());
|
char *key = const_cast<char*>(item.first.toCString(true));
|
||||||
|
|
||||||
|
for (auto j = item.second.begin();
|
||||||
|
j != item.second.end(); ++j) {
|
||||||
|
|
||||||
|
char *val = const_cast<char*>(j->toString().toCString(true));
|
||||||
|
goPutStr(id, key, val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send all collected tags to the Go map
|
// Send all collected tags to the Go map
|
||||||
for (TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end();
|
for (TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end();
|
||||||
++i) {
|
++i) {
|
||||||
char *key = (char *)i->first.toCString(true);
|
char *key = const_cast<char*>(i->first.toCString(true));
|
||||||
for (TagLib::StringList::ConstIterator j = i->second.begin();
|
for (TagLib::StringList::ConstIterator j = i->second.begin();
|
||||||
j != i->second.end(); ++j) {
|
j != i->second.end(); ++j) {
|
||||||
char *val = (char *)(*j).toCString(true);
|
char *val = const_cast<char*>((*j).toCString(true));
|
||||||
goPutStr(id, key, val);
|
goPutStr(id, key, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,7 +248,19 @@ char has_cover(const TagLib::FileRef f) {
|
|||||||
// ----- WMA
|
// ----- WMA
|
||||||
else if (TagLib::ASF::File * asfFile{dynamic_cast<TagLib::ASF::File *>(f.file())}) {
|
else if (TagLib::ASF::File * asfFile{dynamic_cast<TagLib::ASF::File *>(f.file())}) {
|
||||||
const TagLib::ASF::Tag *tag{ asfFile->tag() };
|
const TagLib::ASF::Tag *tag{ asfFile->tag() };
|
||||||
hasCover = tag && asfFile->tag()->attributeListMap().contains("WM/Picture");
|
hasCover = tag && tag->attributeListMap().contains("WM/Picture");
|
||||||
|
}
|
||||||
|
// ----- DSF
|
||||||
|
else if (TagLib::DSF::File * dsffile{ dynamic_cast<TagLib::DSF::File *>(f.file())}) {
|
||||||
|
const TagLib::ID3v2::Tag *tag { dsffile->tag() };
|
||||||
|
hasCover = tag && !tag->frameListMap()["APIC"].isEmpty();
|
||||||
|
}
|
||||||
|
// ----- WAVPAK (APE tag)
|
||||||
|
else if (TagLib::WavPack::File * wvFile{dynamic_cast<TagLib::WavPack::File *>(f.file())}) {
|
||||||
|
if (wvFile->hasAPETag()) {
|
||||||
|
// This is the particular string that Picard uses
|
||||||
|
hasCover = !wvFile->APETag()->itemListMap()["COVER ART (FRONT)"].isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasCover;
|
return hasCover;
|
||||||
|
|||||||
Reference in New Issue
Block a user