1# -*- coding: utf-8 -*- 2 3import os 4import pickle 5 6from mutagen import MutagenError 7from mutagen.id3 import ID3FileType, ID3, RVA2, CHAP, TDRC, CTOC 8from mutagen.easyid3 import EasyID3, error as ID3Error 9from mutagen._compat import PY3 10 11from tests import TestCase, DATA_DIR, get_temp_copy 12 13 14class TEasyID3(TestCase): 15 16 def setUp(self): 17 self.filename = get_temp_copy(os.path.join(DATA_DIR, 'emptyfile.mp3')) 18 self.id3 = EasyID3() 19 self.realid3 = self.id3._EasyID3__id3 20 21 def tearDown(self): 22 os.unlink(self.filename) 23 24 def test_size_attr(self): 25 assert self.id3.size == self.realid3.size 26 27 def test_load_filename(self): 28 self.id3.save(self.filename) 29 self.id3.load(self.filename) 30 assert self.id3.filename == self.filename 31 32 path = os.path.join(DATA_DIR, 'silence-44-s.mp3') 33 new = EasyID3(path) 34 assert new.filename == path 35 36 def test_txxx_latin_first_then_non_latin(self): 37 self.id3["performer"] = [u"foo"] 38 self.id3["performer"] = [u"\u0243"] 39 self.id3.save(self.filename) 40 new = EasyID3(self.filename) 41 self.assertEqual(new["performer"], [u"\u0243"]) 42 43 def test_remember_ctr(self): 44 empty = os.path.join(DATA_DIR, 'emptyfile.mp3') 45 mp3 = ID3FileType(empty, ID3=EasyID3) 46 self.failIf(mp3.tags) 47 mp3["artist"] = ["testing"] 48 self.failUnless(mp3.tags) 49 mp3.pprint() 50 self.failUnless(isinstance(mp3.tags, EasyID3)) 51 52 def test_save_23(self): 53 self.id3.save(self.filename, v2_version=3) 54 self.assertEqual(ID3(self.filename).version, (2, 3, 0)) 55 self.id3.save(self.filename, v2_version=4) 56 self.assertEqual(ID3(self.filename).version, (2, 4, 0)) 57 58 def test_save_date_v23(self): 59 self.id3["date"] = "2004" 60 assert self.realid3.getall("TDRC")[0] == u"2004" 61 self.id3.save(self.filename, v2_version=3) 62 assert self.realid3.getall("TDRC")[0] == u"2004" 63 assert not self.realid3.getall("TYER") 64 new = ID3(self.filename, translate=False) 65 assert new.version == (2, 3, 0) 66 assert new.getall("TYER")[0] == u"2004" 67 68 def test_save_v23_error_restore(self): 69 self.id3["date"] = "2004" 70 with self.assertRaises(MutagenError): 71 self.id3.save("", v2_version=3) 72 assert self.id3["date"] == ["2004"] 73 74 def test_save_v23_recurse_restore(self): 75 self.realid3.add(CHAP(sub_frames=[TDRC(text="2006")])) 76 self.realid3.add(CTOC(sub_frames=[TDRC(text="2006")])) 77 self.id3.save(self.filename, v2_version=3) 78 79 for frame_id in ["CHAP", "CTOC"]: 80 chap = self.realid3.getall(frame_id)[0] 81 assert chap.sub_frames.getall("TDRC")[0] == "2006" 82 new = ID3(self.filename, translate=False) 83 assert new.version == (2, 3, 0) 84 chap = new.getall(frame_id)[0] 85 assert not chap.sub_frames.getall("TDRC") 86 assert chap.sub_frames.getall("TYER")[0] == "2006" 87 88 def test_delete(self): 89 self.id3["artist"] = "foobar" 90 self.id3.save(self.filename) 91 self.failUnless(os.path.getsize(self.filename)) 92 self.id3.delete(self.filename) 93 self.failIf(os.path.getsize(self.filename)) 94 self.failIf(self.id3) 95 96 def test_pprint(self): 97 self.id3["artist"] = "baz" 98 self.id3.pprint() 99 100 def test_in(self): 101 self.failIf("foo" in self.id3) 102 103 if not PY3: 104 def test_has_key(self): 105 self.failIf(self.id3.has_key("foo")) 106 107 def test_empty_file(self): 108 empty = os.path.join(DATA_DIR, 'emptyfile.mp3') 109 self.assertRaises(ID3Error, EasyID3, filename=empty) 110 111 def test_nonexistent_file(self): 112 empty = os.path.join(DATA_DIR, 'does', 'not', 'exist') 113 self.assertRaises(MutagenError, EasyID3, filename=empty) 114 115 def test_write_single(self): 116 for key in EasyID3.valid_keys: 117 if (key == "date") or (key == "originaldate"): 118 continue 119 elif key.startswith("replaygain_"): 120 continue 121 122 # Test creation 123 self.id3[key] = "a test value" 124 self.id3.save(self.filename) 125 id3 = EasyID3(self.filename) 126 self.failUnlessEqual(id3[key], ["a test value"]) 127 self.failUnlessEqual(id3.keys(), [key]) 128 129 # And non-creation setting. 130 self.id3[key] = "a test value" 131 self.id3.save(self.filename) 132 id3 = EasyID3(self.filename) 133 self.failUnlessEqual(id3[key], ["a test value"]) 134 self.failUnlessEqual(id3.keys(), [key]) 135 136 del(self.id3[key]) 137 138 def test_write_double(self): 139 for key in EasyID3.valid_keys: 140 if (key == "date") or (key == "originaldate"): 141 continue 142 elif key.startswith("replaygain_"): 143 continue 144 elif key == "musicbrainz_trackid": 145 continue 146 147 self.id3[key] = ["a test", "value"] 148 self.id3.save(self.filename) 149 id3 = EasyID3(self.filename) 150 # some keys end up in multiple frames and ID3.getall returns 151 # them in undefined order 152 self.failUnlessEqual(sorted(id3.get(key)), ["a test", "value"]) 153 self.failUnlessEqual(id3.keys(), [key]) 154 155 self.id3[key] = ["a test", "value"] 156 self.id3.save(self.filename) 157 id3 = EasyID3(self.filename) 158 self.failUnlessEqual(sorted(id3.get(key)), ["a test", "value"]) 159 self.failUnlessEqual(id3.keys(), [key]) 160 161 del(self.id3[key]) 162 163 def test_write_date(self): 164 self.id3["date"] = "2004" 165 self.id3.save(self.filename) 166 id3 = EasyID3(self.filename) 167 self.failUnlessEqual(id3["date"], ["2004"]) 168 169 self.id3["date"] = "2004" 170 self.id3.save(self.filename) 171 id3 = EasyID3(self.filename) 172 self.failUnlessEqual(id3["date"], ["2004"]) 173 174 def test_date_delete(self): 175 self.id3["date"] = "2004" 176 self.failUnlessEqual(self.id3["date"], ["2004"]) 177 del(self.id3["date"]) 178 self.failIf("date" in self.id3.keys()) 179 180 def test_write_date_double(self): 181 self.id3["date"] = ["2004", "2005"] 182 self.id3.save(self.filename) 183 id3 = EasyID3(self.filename) 184 self.failUnlessEqual(id3["date"], ["2004", "2005"]) 185 186 self.id3["date"] = ["2004", "2005"] 187 self.id3.save(self.filename) 188 id3 = EasyID3(self.filename) 189 self.failUnlessEqual(id3["date"], ["2004", "2005"]) 190 191 def test_write_original_date(self): 192 self.id3["originaldate"] = "2004" 193 self.id3.save(self.filename) 194 id3 = EasyID3(self.filename) 195 self.failUnlessEqual(id3["originaldate"], ["2004"]) 196 197 self.id3["originaldate"] = "2004" 198 self.id3.save(self.filename) 199 id3 = EasyID3(self.filename) 200 self.failUnlessEqual(id3["originaldate"], ["2004"]) 201 202 def test_original_date_delete(self): 203 self.id3["originaldate"] = "2004" 204 self.failUnlessEqual(self.id3["originaldate"], ["2004"]) 205 del(self.id3["originaldate"]) 206 self.failIf("originaldate" in self.id3.keys()) 207 208 def test_write_original_date_double(self): 209 self.id3["originaldate"] = ["2004", "2005"] 210 self.id3.save(self.filename) 211 id3 = EasyID3(self.filename) 212 self.failUnlessEqual(id3["originaldate"], ["2004", "2005"]) 213 214 self.id3["originaldate"] = ["2004", "2005"] 215 self.id3.save(self.filename) 216 id3 = EasyID3(self.filename) 217 self.failUnlessEqual(id3["originaldate"], ["2004", "2005"]) 218 219 def test_write_invalid(self): 220 self.failUnlessRaises(ValueError, self.id3.__getitem__, "notvalid") 221 self.failUnlessRaises(ValueError, self.id3.__delitem__, "notvalid") 222 self.failUnlessRaises( 223 ValueError, self.id3.__setitem__, "notvalid", "tests") 224 225 def test_perfomer(self): 226 self.id3["performer:coder"] = ["piman", "mu"] 227 self.id3.save(self.filename) 228 id3 = EasyID3(self.filename) 229 self.failUnlessEqual(id3["performer:coder"], ["piman", "mu"]) 230 231 def test_no_performer(self): 232 self.failIf("performer:foo" in self.id3) 233 234 def test_performer_delete(self): 235 self.id3["performer:foo"] = "Joe" 236 self.id3["performer:bar"] = "Joe" 237 self.failUnless("performer:foo" in self.id3) 238 self.failUnless("performer:bar" in self.id3) 239 del(self.id3["performer:foo"]) 240 self.failIf("performer:foo" in self.id3) 241 self.failUnless("performer:bar" in self.id3) 242 del(self.id3["performer:bar"]) 243 self.failIf("performer:bar" in self.id3) 244 self.failIf("TMCL" in self.realid3) 245 246 def test_performer_delete_dne(self): 247 self.failUnlessRaises(KeyError, self.id3.__delitem__, "performer:bar") 248 self.id3["performer:foo"] = "Joe" 249 self.failUnlessRaises(KeyError, self.id3.__delitem__, "performer:bar") 250 251 def test_txxx_empty(self): 252 # https://github.com/quodlibet/mutagen/issues/135 253 self.id3["asin"] = "" 254 255 def test_txxx_set_get(self): 256 self.failIf("asin" in self.id3.keys()) 257 self.id3["asin"] = "Hello" 258 self.failUnless("asin" in self.id3.keys()) 259 self.failUnlessEqual(self.id3["asin"], ["Hello"]) 260 self.failUnless("TXXX:ASIN" in self.realid3) 261 262 def test_txxx_del_set_del(self): 263 self.failIf("asin" in self.id3.keys()) 264 self.failUnlessRaises(KeyError, self.id3.__delitem__, "asin") 265 self.id3["asin"] = "Hello" 266 self.failUnless("asin" in self.id3.keys()) 267 self.failUnlessEqual(self.id3["asin"], ["Hello"]) 268 del(self.id3["asin"]) 269 self.failIf("asin" in self.id3.keys()) 270 self.failUnlessRaises(KeyError, self.id3.__delitem__, "asin") 271 272 def test_txxx_save(self): 273 self.id3["asin"] = "Hello" 274 self.id3.save(self.filename) 275 id3 = EasyID3(self.filename) 276 self.failUnlessEqual(id3["asin"], ["Hello"]) 277 278 def test_txxx_unicode(self): 279 self.id3["asin"] = u"He\u1234llo" 280 self.failUnlessEqual(self.id3["asin"], [u"He\u1234llo"]) 281 282 def test_bad_trackid(self): 283 self.failUnlessRaises(ValueError, self.id3.__setitem__, 284 "musicbrainz_trackid", ["a", "b"]) 285 self.failIf(self.realid3.getall("RVA2")) 286 287 def test_gain_bad_key(self): 288 self.failIf("replaygain_foo_gain" in self.id3) 289 self.failIf(self.realid3.getall("RVA2")) 290 291 def test_gain_bad_value(self): 292 self.failUnlessRaises( 293 ValueError, self.id3.__setitem__, "replaygain_foo_gain", []) 294 self.failUnlessRaises( 295 ValueError, self.id3.__setitem__, "replaygain_foo_gain", ["foo"]) 296 self.failUnlessRaises( 297 ValueError, 298 self.id3.__setitem__, "replaygain_foo_gain", ["1", "2"]) 299 self.failIf(self.realid3.getall("RVA2")) 300 301 def test_peak_bad_key(self): 302 self.failIf("replaygain_foo_peak" in self.id3) 303 self.failIf(self.realid3.getall("RVA2")) 304 305 def test_peak_bad_value(self): 306 self.failUnlessRaises( 307 ValueError, self.id3.__setitem__, "replaygain_foo_peak", []) 308 self.failUnlessRaises( 309 ValueError, self.id3.__setitem__, "replaygain_foo_peak", ["foo"]) 310 self.failUnlessRaises( 311 ValueError, 312 self.id3.__setitem__, "replaygain_foo_peak", ["1", "1"]) 313 self.failUnlessRaises( 314 ValueError, self.id3.__setitem__, "replaygain_foo_peak", ["3"]) 315 self.failIf(self.realid3.getall("RVA2")) 316 317 def test_gain_peak_get(self): 318 self.id3["replaygain_foo_gain"] = "+3.5 dB" 319 self.id3["replaygain_bar_peak"] = "0.5" 320 self.failUnlessEqual( 321 self.id3["replaygain_foo_gain"], ["+3.500000 dB"]) 322 self.failUnlessEqual(self.id3["replaygain_foo_peak"], ["0.000000"]) 323 self.failUnlessEqual( 324 self.id3["replaygain_bar_gain"], ["+0.000000 dB"]) 325 self.failUnlessEqual(self.id3["replaygain_bar_peak"], ["0.500000"]) 326 327 def test_gain_peak_set(self): 328 self.id3["replaygain_foo_gain"] = "+3.5 dB" 329 self.id3["replaygain_bar_peak"] = "0.5" 330 self.id3.save(self.filename) 331 id3 = EasyID3(self.filename) 332 self.failUnlessEqual(id3["replaygain_foo_gain"], ["+3.500000 dB"]) 333 self.failUnlessEqual(id3["replaygain_foo_peak"], ["0.000000"]) 334 self.failUnlessEqual(id3["replaygain_bar_gain"], ["+0.000000 dB"]) 335 self.failUnlessEqual(id3["replaygain_bar_peak"], ["0.500000"]) 336 337 def test_gain_peak_delete(self): 338 self.id3["replaygain_foo_gain"] = "+3.5 dB" 339 self.id3["replaygain_bar_peak"] = "0.5" 340 del(self.id3["replaygain_bar_gain"]) 341 del(self.id3["replaygain_foo_peak"]) 342 self.failUnless("replaygain_foo_gain" in self.id3.keys()) 343 self.failUnless("replaygain_bar_gain" in self.id3.keys()) 344 345 del(self.id3["replaygain_foo_gain"]) 346 del(self.id3["replaygain_bar_peak"]) 347 self.failIf("replaygain_foo_gain" in self.id3.keys()) 348 self.failIf("replaygain_bar_gain" in self.id3.keys()) 349 350 del(self.id3["replaygain_foo_gain"]) 351 del(self.id3["replaygain_bar_peak"]) 352 self.failIf("replaygain_foo_gain" in self.id3.keys()) 353 self.failIf("replaygain_bar_gain" in self.id3.keys()) 354 355 def test_gain_peak_capitalization(self): 356 frame = RVA2(desc=u"Foo", gain=1.0, peak=1.0, channel=0) 357 self.assertFalse(len(self.realid3)) 358 self.realid3.add(frame) 359 self.assertTrue("replaygain_Foo_peak" in self.id3) 360 self.assertTrue("replaygain_Foo_peak" in self.id3.keys()) 361 self.assertTrue("replaygain_Foo_gain" in self.id3) 362 self.assertTrue("replaygain_Foo_gain" in self.id3.keys()) 363 364 self.id3["replaygain_Foo_gain"] = ["0.5"] 365 self.id3["replaygain_Foo_peak"] = ["0.25"] 366 367 frames = self.realid3.getall("RVA2") 368 self.assertEqual(len(frames), 1) 369 self.assertEqual(frames[0].desc, u"Foo") 370 self.assertEqual(frames[0].gain, 0.5) 371 self.assertEqual(frames[0].peak, 0.25) 372 373 def test_case_insensitive(self): 374 self.id3["date"] = [u"2004"] 375 self.assertEqual(self.id3["DATE"], [u"2004"]) 376 del self.id3["DaTe"] 377 self.assertEqual(len(self.id3), 0) 378 379 self.id3["asin"] = [u"foo"] 380 self.assertEqual(self.id3["Asin"], [u"foo"]) 381 del self.id3["AsIn"] 382 self.assertEqual(len(self.id3), 0) 383 384 def test_pickle(self): 385 # https://github.com/quodlibet/mutagen/issues/102 386 pickle.dumps(self.id3) 387 388 def test_get_fallback(self): 389 called = [] 390 391 def get_func(id3, key): 392 id3.getall("") 393 self.failUnlessEqual(key, "nope") 394 called.append(1) 395 self.id3.GetFallback = get_func 396 self.id3["nope"] 397 self.failUnless(called) 398 399 def test_set_fallback(self): 400 called = [] 401 402 def set_func(id3, key, value): 403 id3.getall("") 404 self.failUnlessEqual(key, "nope") 405 self.failUnlessEqual(value, ["foo"]) 406 called.append(1) 407 self.id3.SetFallback = set_func 408 self.id3["nope"] = "foo" 409 self.failUnless(called) 410 411 def test_del_fallback(self): 412 called = [] 413 414 def del_func(id3, key): 415 id3.getall("") 416 self.failUnlessEqual(key, "nope") 417 called.append(1) 418 self.id3.DeleteFallback = del_func 419 del self.id3["nope"] 420 self.failUnless(called) 421 422 def test_list_fallback(self): 423 def list_func(id3, key): 424 id3.getall("") 425 self.failIf(key) 426 return ["somekey"] 427 428 self.id3.ListFallback = list_func 429 self.failUnlessEqual(self.id3.keys(), ["somekey"]) 430 431 def test_text_tags(self): 432 for tag in ["albumartist", "performer"]: 433 self.id3[tag] = u"foo" 434 self.id3.save(self.filename) 435 id3 = EasyID3(self.filename) 436 self.failUnlessEqual(id3[tag], [u"foo"]) 437