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:
Kendall Garner
2025-07-03 02:04:27 +00:00
committed by GitHub
parent ee34433cc5
commit d4f869152b
3 changed files with 68 additions and 12 deletions

View File

@@ -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))
}
}
})
}) })
}) })

View File

@@ -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),

View File

@@ -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;