From 3debd31b1281672bd31f3becc5efc6615c6d2ecd Mon Sep 17 00:00:00 2001 From: Kendall Garner <17521368+kgarner7@users.noreply.github.com> Date: Wed, 15 Nov 2023 01:25:18 +0000 Subject: [PATCH] Add more replaygain tests, fix wma (#2356) * add more replaygain tests, fix wma * Convert individual specs to a table spec * Fix pipeline, by commenting incompatible tests --------- Co-authored-by: Deluan --- scanner/metadata/metadata_test.go | 6 +- scanner/metadata/taglib/taglib_test.go | 61 ++++++++++++++++-- scanner/metadata/taglib/taglib_wrapper.cpp | 23 +++++++ scanner/tag_scanner_test.go | 9 ++- scanner/walk_dir_tree_test.go | 2 +- .../01 Invisible (RED) Edit Version.m4a | Bin 17011 -> 18051 bytes tests/fixtures/test.aiff | Bin 0 -> 89458 bytes tests/fixtures/test.flac | Bin 0 -> 21225 bytes tests/fixtures/test.ogg | Bin 5178 -> 6333 bytes tests/fixtures/test.wav | Bin 0 -> 89590 bytes tests/fixtures/test.wma | Bin 0 -> 21431 bytes tests/fixtures/test.wv | Bin 0 -> 22949 bytes 12 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 tests/fixtures/test.aiff create mode 100644 tests/fixtures/test.flac create mode 100644 tests/fixtures/test.wav create mode 100644 tests/fixtures/test.wma create mode 100644 tests/fixtures/test.wv diff --git a/scanner/metadata/metadata_test.go b/scanner/metadata/metadata_test.go index 0b1a7855..dba52ed7 100644 --- a/scanner/metadata/metadata_test.go +++ b/scanner/metadata/metadata_test.go @@ -56,15 +56,15 @@ var _ = Describe("Tags", func() { m = mds["tests/fixtures/test.ogg"] Expect(err).To(BeNil()) - Expect(m.Title()).To(BeEmpty()) + Expect(m.Title()).To(Equal("Title")) Expect(m.HasPicture()).To(BeFalse()) Expect(m.Duration()).To(BeNumerically("~", 1.04, 0.01)) Expect(m.Suffix()).To(Equal("ogg")) Expect(m.FilePath()).To(Equal("tests/fixtures/test.ogg")) - Expect(m.Size()).To(Equal(int64(5178))) + Expect(m.Size()).To(Equal(int64(6333))) // TabLib 1.12 returns 18, previous versions return 39. // See https://github.com/taglib/taglib/commit/2f238921824741b2cfe6fbfbfc9701d9827ab06b - Expect(m.BitRate()).To(BeElementOf(18, 39, 40)) + Expect(m.BitRate()).To(BeElementOf(18, 39, 40, 49)) }) }) }) diff --git a/scanner/metadata/taglib/taglib_test.go b/scanner/metadata/taglib/taglib_test.go index 342cb03c..bc0cfd63 100644 --- a/scanner/metadata/taglib/taglib_test.go +++ b/scanner/metadata/taglib/taglib_test.go @@ -52,6 +52,10 @@ var _ = Describe("Extractor", func() { Expect(m).To(HaveKeyWithValue("comment", []string{"Comment1\nComment2"})) Expect(m).To(HaveKeyWithValue("lyrics", []string{"Lyrics 1\rLyrics 2"})) Expect(m).To(HaveKeyWithValue("bpm", []string{"123"})) + Expect(m).To(HaveKeyWithValue("replaygain_album_gain", []string{"+3.21518 dB"})) + Expect(m).To(HaveKeyWithValue("replaygain_album_peak", []string{"0.9125"})) + Expect(m).To(HaveKeyWithValue("replaygain_track_gain", []string{"-1.48 dB"})) + Expect(m).To(HaveKeyWithValue("replaygain_track_peak", []string{"0.4512"})) Expect(m).To(HaveKeyWithValue("tracknumber", []string{"2/10"})) m = m.Map(e.CustomMappings()) @@ -59,7 +63,6 @@ var _ = Describe("Extractor", func() { m = mds["tests/fixtures/test.ogg"] Expect(err).To(BeNil()) - Expect(m).ToNot(HaveKey("title")) Expect(m).ToNot(HaveKey("has_picture")) Expect(m).To(HaveKeyWithValue("duration", []string{"1.04"})) Expect(m).To(HaveKeyWithValue("fbpm", []string{"141.7"})) @@ -67,11 +70,11 @@ var _ = Describe("Extractor", func() { // TabLib 1.12 returns 18, previous versions return 39. // See https://github.com/taglib/taglib/commit/2f238921824741b2cfe6fbfbfc9701d9827ab06b Expect(m).To(HaveKey("bitrate")) - Expect(m["bitrate"][0]).To(BeElementOf("18", "39", "40")) + Expect(m["bitrate"][0]).To(BeElementOf("18", "39", "40", "49")) }) - DescribeTable("ReplayGain", - func(file, albumGain, albumPeak, trackGain, trackPeak string) { + DescribeTable("Format-Specific tests", + func(file, duration, channels, albumGain, albumPeak, trackGain, trackPeak string) { file = "tests/fixtures/" + file mds, err := e.Parse(file) Expect(err).NotTo(HaveOccurred()) @@ -83,10 +86,54 @@ var _ = Describe("Extractor", func() { Expect(m).To(HaveKeyWithValue("replaygain_album_peak", []string{albumPeak})) Expect(m).To(HaveKeyWithValue("replaygain_track_gain", []string{trackGain})) Expect(m).To(HaveKeyWithValue("replaygain_track_peak", []string{trackPeak})) + + Expect(m).To(HaveKeyWithValue("title", []string{"Title", "Title"})) + Expect(m).To(HaveKeyWithValue("album", []string{"Album", "Album"})) + Expect(m).To(HaveKeyWithValue("artist", []string{"Artist", "Artist"})) + Expect(m).To(HaveKeyWithValue("albumartist", []string{"Album Artist"})) + Expect(m).To(HaveKeyWithValue("compilation", []string{"1"})) + Expect(m).To(HaveKeyWithValue("genre", []string{"Rock"})) + Expect(m).To(HaveKeyWithValue("date", []string{"2014", "2014"})) + + Expect(m).To(HaveKey("discnumber")) + discno := m["discnumber"] + Expect(discno).To(HaveLen(1)) + Expect(discno[0]).To(BeElementOf([]string{"1", "1/2"})) + + Expect(m).NotTo(HaveKeyWithValue("has_picture", []string{"true"})) + Expect(m).To(HaveKeyWithValue("duration", []string{duration})) + + Expect(m).To(HaveKeyWithValue("channels", []string{channels})) + Expect(m).To(HaveKeyWithValue("comment", []string{"Comment1\nComment2"})) + Expect(m).To(HaveKeyWithValue("lyrics", []string{"Lyrics1\nLyrics 2"})) + Expect(m).To(HaveKeyWithValue("bpm", []string{"123"})) + + Expect(m).To(HaveKey("tracknumber")) + trackNo := m["tracknumber"] + Expect(trackNo).To(HaveLen(1)) + Expect(trackNo[0]).To(BeElementOf([]string{"3", "3/10"})) }, - Entry("Correctly parses m4a (aac) gain tags", "01 Invisible (RED) Edit Version.m4a", "0.37", "0.48", "0.37", "0.48"), - Entry("correctly parses mp3 tags", "test.mp3", "+3.21518 dB", "0.9125", "-1.48 dB", "0.4512"), - Entry("correctly parses ogg (vorbis) tags", "test.ogg", "+7.64 dB", "0.11772506", "+7.64 dB", "0.11772506"), + + // ffmpeg -f lavfi -i "sine=frequency=1200:duration=1" test.flac + Entry("correctly parses flac tags", "test.flac", "1.00", "1", "+4.06 dB", "0.12496948", "+4.06 dB", "0.12496948"), + + Entry("Correctly parses m4a (aac) gain tags", "01 Invisible (RED) Edit Version.m4a", "1.04", "2", "0.37", "0.48", "0.37", "0.48"), + + Entry("correctly parses ogg (vorbis) tags", "test.ogg", "1.04", "2", "+7.64 dB", "0.11772506", "+7.64 dB", "0.11772506"), + + // ffmpeg -f lavfi -i "sine=frequency=900:duration=1" test.wma + Entry("correctly parses wma/asf tags", "test.wma", "1.02", "1", "3.27 dB", "0.132914", "3.27 dB", "0.132914"), + + // ffmpeg -f lavfi -i "sine=frequency=800:duration=1" test.wv + Entry("correctly parses wv (wavpak) tags", "test.wv", "1.00", "1", "3.43 dB", "0.125061", "3.43 dB", "0.125061"), + + // TODO - these breaks in the pipeline as it uses TabLib 1.11. Once Ubuntu 24.04 is released we can uncomment these tests + // ffmpeg -f lavfi -i "sine=frequency=1000:duration=1" test.wav + //Entry("correctly parses wav tags", "test.wav", "1.00", "1", "3.06 dB", "0.125056", "3.06 dB", "0.125056"), + + // ffmpeg -f lavfi -i "sine=frequency=1400:duration=1" test.aiff + //Entry("correctly parses aiff tags", "test.aiff", "1.00", "1", "2.00 dB", "0.124972", "2.00 dB", "0.124972"), + ) }) diff --git a/scanner/metadata/taglib/taglib_wrapper.cpp b/scanner/metadata/taglib/taglib_wrapper.cpp index 3f111c13..b5ea6056 100644 --- a/scanner/metadata/taglib/taglib_wrapper.cpp +++ b/scanner/metadata/taglib/taglib_wrapper.cpp @@ -100,6 +100,29 @@ int taglib_read(const FILENAME_CHAR_T *filename, unsigned long id) { } } + // WMA/ASF files may have additional tags not captured by the general iterator + TagLib::ASF::File *asfFile(dynamic_cast(f.file())); + if (asfFile != NULL) + { + const TagLib::ASF::Tag *asfTags{asfFile->tag()}; + const auto itemListMap = asfTags->attributeListMap(); + for (const auto item : itemListMap) { + char *key = ::strdup(item.first.toCString(true)); + char *val = ::strdup(item.second.front().toString().toCString()); + go_map_put_str(id, key, val); + free(key); + free(val); + } + + // Compilation tag needs to be handled differently + const auto compilation = asfTags->attribute("WM/IsCompilation"); + if (!compilation.isEmpty()) { + char *val = ::strdup(compilation.front().toString().toCString()); + go_map_put_str(id, (char *)"compilation", val); + free(val); + } + } + if (has_cover(f)) { go_map_put_str(id, (char *)"has_picture", (char *)"true"); } diff --git a/scanner/tag_scanner_test.go b/scanner/tag_scanner_test.go index 215e06bc..a798570c 100644 --- a/scanner/tag_scanner_test.go +++ b/scanner/tag_scanner_test.go @@ -10,9 +10,14 @@ var _ = Describe("TagScanner", func() { It("return all audio files from the folder", func() { files, err := loadAllAudioFiles("tests/fixtures") Expect(err).ToNot(HaveOccurred()) - Expect(files).To(HaveLen(5)) - Expect(files).To(HaveKey("tests/fixtures/test.ogg")) + Expect(files).To(HaveLen(10)) + Expect(files).To(HaveKey("tests/fixtures/test.aiff")) + Expect(files).To(HaveKey("tests/fixtures/test.flac")) Expect(files).To(HaveKey("tests/fixtures/test.mp3")) + Expect(files).To(HaveKey("tests/fixtures/test.ogg")) + Expect(files).To(HaveKey("tests/fixtures/test.wav")) + Expect(files).To(HaveKey("tests/fixtures/test.wma")) + Expect(files).To(HaveKey("tests/fixtures/test.wv")) Expect(files).To(HaveKey("tests/fixtures/test_no_read_permission.ogg")) Expect(files).To(HaveKey("tests/fixtures/01 Invisible (RED) Edit Version.mp3")) Expect(files).To(HaveKey("tests/fixtures/01 Invisible (RED) Edit Version.m4a")) diff --git a/scanner/walk_dir_tree_test.go b/scanner/walk_dir_tree_test.go index 163754a6..e0cea007 100644 --- a/scanner/walk_dir_tree_test.go +++ b/scanner/walk_dir_tree_test.go @@ -35,7 +35,7 @@ var _ = Describe("walk_dir_tree", func() { Expect(collected[baseDir]).To(MatchFields(IgnoreExtras, Fields{ "Images": BeEmpty(), "HasPlaylist": BeFalse(), - "AudioFilesCount": BeNumerically("==", 6), + "AudioFilesCount": BeNumerically("==", 11), })) Expect(collected[filepath.Join(baseDir, "artist", "an-album")]).To(MatchFields(IgnoreExtras, Fields{ "Images": ConsistOf("cover.jpg", "front.png", "artist.png"), diff --git a/tests/fixtures/01 Invisible (RED) Edit Version.m4a b/tests/fixtures/01 Invisible (RED) Edit Version.m4a index 55d360d89459e81d0b7944ae349dc6b223db5ece..80474aa35150c1ff913d3564c5dfb1618348dae6 100644 GIT binary patch delta 624 zcmey|!r0u)I6;ny+hn7HATtwp#AIV;3oiEOr70ze3=Hh|Czmp7$FZnn<`kDOFfhoj z%uCD#(xNGeAZZ3hAZ7^3EXhd)(sC;ugF=9`7_y{eQAs9LYhq3kP!>h2V@^_O?&KyG z^Ll0mE}$gPkd(~gY#6e2|Px%H(VosmVrcmXkNIvQ1(Qoh-l>4Ps7?W|aX7 zM^Aps7CO0y%?c={KDn1Ip3ohpCr70ze3=GWYCzmp7*E285%qcEmU|=v= znU|Ogq;*mfLDCG2K+NEoSC(0vnUs^Npb_NiqN(7Tl3AhOfY_rOXwAJXEQK5WWaaA>8c^+rG=kukTKyql3MnX>4X~Y4vM%Ps{$+%+~4l z^ZA#_gXC>;Gk-m|()MD@u}zmU_ZH%;L88fVOAz4Y4616s=NokQ9G(8|E)QFhTLQ+af zNhv8MrKFUUl2TGiN=YdxC8ea4l#)_XN=iv7DJ7+(l$4TEQc6lmDJdnTq?DABQc_Av zNhv8MrKFUUl2TGiN=YdxC8ea4l#)_XN=iv7DJ7+(l$4TEQc6lmDJdnTq?DABQc_Av zNhv8MrKFUUl2TGiN=YdxC8ea4l#)_XN=iv7DJ7+(l$4TEQc6lmDJdnTq?DABQc_Av zNhv8MrKFUUl2TGiO1EBYKc9d3*|gBDKSb7+^!9L?tzikNNc1u(i&-v zv`^EWX>LQFGprfb3~Pop!)fZOg7zUzV{LS7g(~AtRySRO0tryBrC~EvXZRswDsk# z{I6G8kt(H1sZy$xDy2%PQmT|HrAn#tKyosv|Hs$vlc*(XiCUtTs3mHNTB4SyC2EOU zqJEz2Zh!W{uZFEnXcO9mHla;u6WW9}p-pHL+JrWtO*C3(TWTNh-a49;CZ$PfQks+| zrAcX0nv^D`Noi7=lqNMF9niAgVvxmXboC})}S?L4O)ZN zpfzX>T7%Z0HE4Q3PN)$vqlKPpL`q31DJ7+(l$4TEQc6lmDJdnTq?DABQc_AvNhv8M zrKFUUl2TGiN=YdxC8ea4l#)_XN=iv7DJ7+(l$4TEQc6lmDJdnTq?DABQc_AvNhv8M zrKFUUl2TGiN=YdxC8ea4l#)_XN=iv7Dcv9`?eFdC2%-6I{N5bmsrBn>f3+CTYz`q? zoti#95vs$73US4jxT17ndg|hIsE(A6#5FDPvuj_WGgM0hJ?j@Ko&NFHGw~urqjANi z_*vLj4AtI|ay%Qi&lWoi-Jx0@8eCrU>aDNd zoqSw;cikQ9_u=U1KsB!aGOiyR8w&%ME=-MIEVQkC?zyXVMp9xG=1h{=pORH#u54v3fQX_jY!6{M7Rg8&*e3rr`PVJuJ#?}M?14Y+f!_hOXA_A4 literal 0 HcmV?d00001 diff --git a/tests/fixtures/test.flac b/tests/fixtures/test.flac new file mode 100644 index 0000000000000000000000000000000000000000..a3cf5e986cadc5fc4796fabf13ec6d82917b3de5 GIT binary patch literal 21225 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 DngVFq delta 90 zcmdmMxJzR~09)tJhsStsPmESJV`E@o-~wVdrvP7DLlZ+ia|IwTH7_|oCAG-bC$TKq s)I!hD*hJ6Jz(645opO z(Pni33wg}DvTpsRVDS_}h(-Thdy`<(MF#{pZHbq2YdKA7AQ ze=2$;Yz-Y&Ur>I_Jj}wr!G6Iyl<%p3);huuMN8sKl6~nvW(DhFTaROlbBX&ip;g)< z*U=H0q1WhUdQv_lHHc^3pE(uBhqeROHd2(ilzbz8Jo;X^Bs8oJC>Pkn%*wvT`q>i2 zul`c245uUE*geU`>Hh2o7TH$i_>S{F_XS~|v{J61!!%9D=}vkYK_XR?BQ`8he^~luGp(%@rPv48{JQkkiYuKet@sEDq5* z;W{A5(n7hIo~228llIdq@|d(=6om%YYQEY|xl!_Bram=2A;n4}KL~N!2g(iB!gzKM z+sy7)rm3s7YoUS2?%2`9-%?Yv?Un)V9s3pjZC6mZDbA85`Z|qKg}zE}$x&%gtP*y( zcJtfqOE}&#l<7+?OL${#kt3lw+UH7|J>9hEsA_kpD56EP@siYr z%qgN+XWJL?6|SiJN8-EEU*s3)4Vt3Ar{AJW<(blH!S0^pyx*a5XDq$hx#>vaYHU0* zGrU);Q6EvBXVV$S7O_!wLU~_Z7`h%Fj$Vu_$R$HqElznWL{*yMJoo9QL)9P14 zgW)~V{qf%=L+OXfv(`6lmmGg}9(FGfe<~f8*V8c_8Sl|%x>N3yvO-onTKXd(;W7KfEToCf=7kmj>d1I3Ny)1IK~m zz6>1qGB^&Z5~|WfRSCo)jzb)0W*i5^0dYVa5QkhPa+QUes|3e^TD#3B6t3)NU(Mo0@uEPyg z2~}yHss!SIICSfn*{uV_0dYVa5NB?G98@J#B~&GHmB>{hS9!PQ1i4D&Dv_&1uG09_ z1*pn=R+U2qHs=JH6J$>EJ97fWDORXTs7hn1^2Y{dc>!@i9A53 z%3%ZZDuFm4E;p|dnG++K6Clpmyh?B!5C@J!%?WBw@_!0CI1U_VbS5)YB~&Gz{5C_BoaT-G$hd2 O5{{aR+RO;}FNqlhfiZ;UgE~I8=F!Q&f2!qWN9r zg*Xmz9O5{{akCiWIK**?;}FLojuVBuaooF@o@Z#57gQxwr7=|r#36Hn%!!$q6Ce(V z1LAvei z(bs8=D)d!)OO8r|VwJGNwVU5=U&8U0p-f+DS;8BGI{2j|0bn;|h=C2J(19ED#680dY{3P?b=X6Iy?GO>|BCIRmr2z;Tp=9|u3q zm>&nkp{^3$I!3#7GQ$79D=`oU!~toPgs{<%LRSGb@>aI3Ny) z1LArda0lQH6s9`>jswSm1SF2GUQJ!bh8OIi}QFcOkUtJiw z9v+Tfj4R2~Oppv(Cv0xs>KbtaaX=gpr$vD{)SNucJW8edj0Qi>JU{LY1HA*_xXWo& zd7;Y7*c5ai4t15_xD^Z>hnf@AoD}BN1#lcV4jc!L+sD9h;5cwxZ+3FB5HcsooaFWa zM}M4={y3;gs7k0xs7k2Hk`Pqoq^g{)eXgY0ZVm*o1Qdct% zSh~6W_GA2B*HYo4XqWSS3OYCr9GBl52gF^CA&x^FXDp5zF)))Ehy&tg5FieS+a;jp zWT)IIp<5@PyLG^E8#0LF5XYGp#{qFb9OhM;**gHl0dYVa5a(p@gJ=L-y zO&z&IQc+q~Roq?uUoH&zy)_MAcwzAuy<%&fw}F(EddlaQ&9A!qlK*z)GP)$KbG@zwZibF16;DSg!xDAHH|dH3)I{0&z9kqYt; Di<}Yl literal 0 HcmV?d00001 diff --git a/tests/fixtures/test.wma b/tests/fixtures/test.wma new file mode 100644 index 0000000000000000000000000000000000000000..5d08ab3c4b8bee9c17f6bf7aad10f97d0c2e677f GIT binary patch literal 21431 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 I&wd2(e>O|ThyVZp literal 0 HcmV?d00001