def test_embedded_special_cover_words(self): """Tests that words incidentally containing embedded "special" words album keywords (e.g. cover, disc, back) don't trigger See Issue 818""" song = AudioFile({ "~filename": "tests/data/asong.ogg", "album": "foobar", "title": "Ode to Baz", "artist": "Q-Man", }) files = [ self.full_path(f) for f in [ 'back.jpg', 'discovery.jpg', "Pharell - frontin'.jpg", 'nickelback - Curb.jpg', 'foobar.jpg', 'folder.jpg', # Though this is debatable 'Q-Man - foobar.jpg', 'Q-man - foobar (cover).jpg' ] ] for f in files: file(f, "w").close() self.files.append(f) cover = song.find_cover() if cover: actual = os.path.abspath(cover.name) self.failUnlessEqual( actual, f, "\"%s\" should trump \"%s\"" % (f, actual)) else: self.failUnless(f, self.full_path('back.jpg'))
def test_to_dump_unicode(self): b = AudioFile(bar_1_1) b[u"öäü"] = u"öäü" dump = b.to_dump() n = AudioFile() n.from_dump(dump) self.assertEqual(n[u"öäü"], u"öäü")
def test(self): lib = SongFileLibrary() with temp_filename() as song_fn: song = AudioFile({"~filename": song_fn}) song.sanitize() lib.add([song]) with temp_filename() as xml_fn: with open(xml_fn, "wb") as h: x = get_example_xml(song("~filename"), 1, 1371802107) h.write(x) handler = self.mod.RBDBContentHandler(lib) xml.sax.parse(xml_fn, handler) self.assertEqual(song("~#rating"), 0.2) self.assertEqual(song("~#lastplayed"), 1371802107) self.assertEqual(song("~#playcount"), 1) with open(xml_fn, "wb") as h: x = get_example_xml(song("~filename"), 2, 1371802107 - 1) h.write(x) handler = self.mod.RBDBContentHandler(lib) xml.sax.parse(xml_fn, handler) self.assertEqual(song("~#rating"), 0.4) self.assertEqual(song("~#lastplayed"), 1371802107)
def test_remove_all(self): song = AudioFile() song.add("foo", "bar") song.add("foo", "another") song.add("foo", "one more") song.remove("foo") self.assertFalse("foo" in song)
def test_embedded_special_cover_words(self): """Tests that words incidentally containing embedded "special" words album keywords (e.g. cover, disc, back) don't trigger See Issue 818""" song = AudioFile( {"~filename": "tests/data/asong.ogg", "album": "foobar", "title": "Ode to Baz", "artist": "Q-Man"} ) files = [ self.full_path(f) for f in [ "back.jpg", "discovery.jpg", "Pharell - frontin'.jpg", "nickelback - Curb.jpg", "foobar.jpg", "folder.jpg", # Though this is debatable "Q-Man - foobar.jpg", "Q-man - foobar (cover).jpg", ] ] for f in files: file(f, "w").close() self.files.append(f) cover = song.find_cover() if cover: actual = os.path.abspath(cover.name) self.failUnlessEqual(actual, f, '"%s" should trump "%s"' % (f, actual)) else: self.failUnless(f, self.full_path("back.jpg"))
def test_album_key(self): album_key_tests = [ ({}, ('', '', '')), ({ 'album': 'foo' }, (('foo', ), '', '')), ({ 'labelid': 'foo' }, ('', '', 'foo')), ({ 'musicbrainz_albumid': 'foo' }, ('', '', 'foo')), ({ 'album': 'foo', 'labelid': 'bar' }, (('foo', ), '', 'bar')), ({ 'album': 'foo', 'labelid': 'bar', 'musicbrainz_albumid': 'quux' }, (('foo', ), '', 'bar')), ({ 'albumartist': 'a' }, ('', ('a', ), '')), ] for tags, expected in album_key_tests: afile = AudioFile(**tags) afile.sanitize(fsnative(u'/dir/fn')) self.failUnlessEqual(afile.album_key, expected)
def test_people(self): q = AudioFile([("performer:vocals", "A"), ("performer:guitar", "B"), ("performer", "C"), ("arranger", "A"), ("albumartist", "B"), ("artist", "C")]) self.failUnlessEqual(q.list("~people"), ["C", "B", "A"]) self.failUnlessEqual(q.list("~people:roles"), ["C", "B (Guitar)", "A (Arrangement, Vocals)"])
def setUp(self): self.rg_data = {"replaygain_album_gain": "-1.00 dB", "replaygain_album_peak": "1.1", "replaygain_track_gain": "+1.0000001 dB", "replaygain_track_peak": "0.9"} self.song = AudioFile(self.rg_data) self.no_rg_song = AudioFile()
def test_masked_handling(self): if os.name == "nt": # FIXME: masking isn't properly implemented on Windows return # playlists can contain songs and paths for masked handling.. lib = FileLibrary("foobar") pl = Playlist(self.temp, "playlist", lib) song = Fakesong({"date": "2038", "~filename": fsnative(u"/fake")}) song.sanitize() lib.add([song]) # mask and update lib.mask("/") pl.append(song) pl.remove_songs([song]) self.failUnless("/fake" in pl) pl.extend(self.TWO_SONGS) # check if collections can handle the mix self.failUnlessEqual(pl("date"), "2038") # unmask and update lib.unmask("/") pl.add_songs(["/fake"], lib) self.failUnless(song in pl) pl.delete() lib.destroy()
def test_masked_handling(self): if os.name == "nt": # FIXME: masking isn't properly implemented on Windows return # playlists can contain songs and paths for masked handling.. lib = FileLibrary("foobar") pl = Playlist(self.temp, "playlist", lib) song = Fakesong({"date": "2038", "~filename": "/fake"}) song.sanitize() lib.add([song]) # mask and update lib.mask("/") pl.append(song) pl.remove_songs([song]) self.failUnless("/fake" in pl) pl.extend(self.TWO_SONGS) # check if collections can handle the mix self.failUnlessEqual(pl("date"), "2038") # unmask and update lib.unmask("/") pl.add_songs(["/fake"], lib) self.failUnless(song in pl) pl.delete() lib.destroy()
def setUp(self): config.init() player = NullPlayer() song = AudioFile() song.bookmarks = [(10, "bla")] song.sanitize("/") player.song = song self.player = player
def test_msic(self): with realized(self.b): self.b.activate() self.b.statusbar(1000) self.b.statusbar(1) song = AudioFile({"~filename": fsnative(u"/fake")}) song.sanitize() self.b.scroll(song)
def test(self): album = [ AudioFile({"tracknumber": "7"}), AudioFile({"tracknumber": "garbage"}), AudioFile({"tracknumber": "10/42"}), ] self.assertEqual(brainz.get_trackcount([]), 0) self.assertEqual(brainz.get_trackcount(album), 42)
def setUp(self): self.rg_data = { "replaygain_album_gain": "-1.00 dB", "replaygain_album_peak": "1.1", "replaygain_track_gain": "+1.0000001 dB", "replaygain_track_peak": "0.9" } self.song = AudioFile(self.rg_data)
def test_performers(self): q = AudioFile([("performer:vocals", "A"), ("performer:guitar", "B"), ("performer", "C")]) self.failUnless("A (Vocals)" in q.list("~performers")) self.failUnless("B (Guitar)" in q.list("~performers")) self.failUnless("C" in q.list("~performers")) self.failUnless("A (Vocals)" in q.list("~people")) self.failUnless("B (Guitar)" in q.list("~people")) self.failUnlessEqual(len(q.list("~performers")), 3)
def _print_playing(app, fstring="<artist~album~tracknumber~title>"): from quodlibet.formats._audio import AudioFile from quodlibet.pattern import Pattern song = app.player.song if song is None: song = AudioFile({"~filename": fsnative(u"/")}) song.sanitize() return Pattern(fstring).format(song) + "\n"
def test_menuitem(self): library = SongLibrary() library.librarian = SongLibrarian() a = AudioFile({"~filename": fsnative(u"/foo")}) a.sanitize() x = RatingsMenuItem([a], library) x.set_rating(0, [a], library) x.destroy() library.destroy() library.librarian.destroy()
def test_menuitem(self): library = SongLibrary() library.librarian = SongLibrarian() a = AudioFile({"~filename": "/foo"}) a.sanitize() x = RatingsMenuItem([a], library) x.set_rating(0, [a], library) x.destroy() library.destroy() library.librarian.destroy()
def test_people_mix(self): q = AudioFile([ ("performer:arrangement", "A"), ("arranger", "A"), ("performer", "A"), ("performer:foo", "A"), ]) self.failUnlessEqual(q.list("~people"), ["A"]) self.failUnlessEqual(q.list("~people:roles"), ["A (Arrangement, Arrangement, Foo)"])
def test_people_multi_value(self): q = AudioFile([ ("arranger", "A\nX"), ("performer", "A\nY"), ("performer:foo", "A\nX"), ]) self.failUnlessEqual(q.list("~people"), ["A", "Y", "X"]) self.failUnlessEqual( q.list("~people:roles"), ["A (Arrangement, Foo)", "Y", "X (Arrangement, Foo)"])
def setUp(self): self.p = NullPlayer() self.w = SongLibrary() self.s1 = AudioFile( {"~#playcount": 0, "~#skipcount": 0, "~#lastplayed": 10, "~filename": "foo", "~#length": 1.5}) self.s2 = AudioFile( {"~#playcount": 0, "~#skipcount": 0, "~#lastplayed": 10, "~filename": "foo", "~#length": 1.5}) self.cm = SongTracker(self.w, self.p, self) self.current = None
def test_peoplesort(self): q = AudioFile([("performer:vocals", "The A"), ("performersort:vocals", "A, The"), ("performer:guitar", "The B"), ("performersort:guitar", "B, The"), ("performer", "The C"), ("performersort", "C, The"), ("albumartist", "The B"), ("albumartistsort", "B, The")]) self.failUnlessEqual(q.list("~peoplesort"), ["B, The", "C, The", "A, The"]) self.failUnlessEqual(q.list("~peoplesort:roles"), ["B, The (Guitar)", "C, The", "A, The (Vocals)"])
def test_set_songs_restore_select(self): song = AudioFile({"~filename": "/dev/null"}) self.songlist.add_songs([song]) sel = self.songlist.get_selection() sel.select_path(Gtk.TreePath.new_first()) self.songlist.set_songs([song], scroll_select=True) self.assertEqual(self.songlist.get_selected_songs(), [song]) song2 = AudioFile({"~filename": "/dev/null"}) self.songlist.set_songs([song2], scroll_select=True) self.assertEqual(self.songlist.get_selected_songs(), [])
def test_uri(self): # On windows where we have unicode paths (windows encoding is utf-16) # we need to encode to utf-8 first, then escape. # On linux we take the byte stream and escape it. # see g_filename_to_uri if os.name == "nt": f = AudioFile({"~filename": u"/\xf6\xe4.mp3", "title": "win"}) self.failUnlessEqual(f("~uri"), "file:///%C3%B6%C3%A4.mp3") else: f = AudioFile({"~filename": "/\x87\x12.mp3", "title": "linux"}) self.failUnlessEqual(f("~uri"), "file:///%87%12.mp3")
def test_people_individuals(self): q = AudioFile({"artist": "A\nX", "albumartist": "Various Artists"}) self.failUnlessEqual(q.list("~people:real"), ["A", "X"]) lonely = AudioFile({"artist": "various artists", "title": "blah"}) self.failUnlessEqual(lonely.list("~people:real"), ["various artists"]) lots = AudioFile({"artist": "Various Artists", "albumartist": "V.A."}) self.failUnlessEqual(lots.list("~people:real"), ["Various Artists"])
def test_weighted(self): self.pl.order = ORDERS[2](self.pl) r0 = AudioFile({'~#rating': 0}) r1 = AudioFile({'~#rating': 1}) r2 = AudioFile({'~#rating': 2}) r3 = AudioFile({'~#rating': 3}) self.pl.set([r0, r1, r2, r3]) Gtk.main_iteration_do(False) songs = [self.pl.current for i in range(1000) if self.pl.next() or True] self.assert_(songs.count(r1) > songs.count(r0)) self.assert_(songs.count(r2) > songs.count(r1)) self.assert_(songs.count(r3) > songs.count(r2))
def test_album_key(self): album_key_tests = [ ({}, ("", "")), ({"album": "foo"}, (("foo",), "")), ({"labelid": "foo"}, ("", "foo")), ({"musicbrainz_albumid": "foo"}, ("", "foo")), ({"album": "foo", "labelid": "bar"}, (("foo",), "bar")), ({"album": "foo", "labelid": "bar", "musicbrainz_albumid": "quux"}, (("foo",), "bar")), ] for tags, expected in album_key_tests: afile = AudioFile(**tags) afile.sanitize("/dir/fn") self.failUnlessEqual(afile.album_key, expected)
def test_to_dump(self): dump = bar_1_1.to_dump() num = len(set(bar_1_1.keys()) | INTERN_NUM_DEFAULT) self.failUnlessEqual(dump.count("\n"), num + 2) for key, value in bar_1_1.items(): self.failUnless(key in dump) self.failUnless(value in dump) for key in INTERN_NUM_DEFAULT: self.failUnless(key in dump) n = AudioFile() n.from_dump(dump) self.failUnless(set(dump.split("\n")) == set(n.to_dump().split("\n")))
def _render_column(self, column, **kwargs): view = Gtk.TreeView() model = ObjectStore() view.set_model(model) song = AudioFile({"~filename": "/dev/null"}) song.update(kwargs) model.append(row=[song]) view.append_column(column) if column.get_resizable(): column.set_expand(True) with visible(view): view.columns_autosize()
def getline(key, value): song = AudioFile({"~filename": "/dev/null"}) song.sanitize() song[key] = value lines = format_tags(song).splitlines() if not lines: return "" if len(lines) == 1: return lines[0] # hackery since title defaults to the filename.. for l in lines: if not l.startswith("Title"): return l
def print_playing(fstring="<artist~album~tracknumber~title>"): from quodlibet.formats._audio import AudioFile from quodlibet.parse import Pattern try: text = open(const.CURRENT, "rb").read() song = AudioFile() song.from_dump(text) print_(Pattern(fstring).format(song)) quodlibet.exit() except (OSError, IOError): print_(_("No song is currently playing.")) quodlibet.exit(True)
def test_sanitize(self): q = AudioFile(quux) b = AudioFile(bar_1_1) q.sanitize() b.pop("~filename") self.failUnlessRaises(ValueError, b.sanitize) n = AudioFile({"artist": u"foo\0bar", "title": u"baz\0", "~filename": "whatever"}) n.sanitize() self.failUnlessEqual(n["artist"], "foo\nbar") self.failUnlessEqual(n["title"], "baz")
def test_album_id_values_with_artist(self): album_key_tests = [ # The artist should now appear ({"album": "foo", "artist": "bar", "title": "baz"}, {"album": "foo", "artist": "bar"}), # But albumartist > album still ({"album": "foo", "albumartist": "bar", "artist": "baz"}, {"album": "foo", "albumartist": "bar"}), ] for tags, expected in album_key_tests: if expected is None: expected = tags afile = AudioFile(**tags) afile.sanitize("/dir/fn") # Use alternate version, that includes artist in dict self.failUnlessEqual(afile._album_id_values(True), HashableDict(expected))
def test_cover_path(self): song = AudioFile({"musicbrainz_albumid": u"foobar"}) song2 = AudioFile() # missing Soup if "lastfm-cover" in self.plugins: cls = self.plugins["lastfm-cover"].cls self.assertTrue(is_fsnative(cls(song).cover_path)) self.assertTrue(is_fsnative(cls(song2).cover_path)) # missing Soup if "musicbrainz-cover" in self.plugins: cls = self.plugins["musicbrainz-cover"].cls self.assertTrue(is_fsnative(cls(song).cover_path)) self.assertTrue(is_fsnative(cls(song2).cover_path))
def print_playing(fstring="<artist~album~tracknumber~title>"): import quodlibet from quodlibet.formats._audio import AudioFile from quodlibet.parse import Pattern from quodlibet import const try: text = open(const.CURRENT, "rb").read() song = AudioFile() song.from_dump(text) print_(Pattern(fstring).format(song)) quodlibet.exit() except (OSError, IOError): print_(_("No song is currently playing.")) quodlibet.exit(True)
def test_album_key(self): album_key_tests = [ ({}, ('', '', '')), ({'album': 'foo'}, (('foo',), '', '')), ({'labelid': 'foo'}, ('', '', 'foo')), ({'musicbrainz_albumid': 'foo'}, ('', '', 'foo')), ({'album': 'foo', 'labelid': 'bar'}, (('foo',), '', 'bar')), ({'album': 'foo', 'labelid': 'bar', 'musicbrainz_albumid': 'quux'}, (('foo',), '', 'bar')), ({'albumartist': 'a'}, ('', ('a',), '')), ] for tags, expected in album_key_tests: afile = AudioFile(**tags) afile.sanitize(fsnative(u'/dir/fn')) self.failUnlessEqual(afile.album_key, expected)
def __get_song_sort_key_func(self, order): last_tag = None last_order = None first = True key_func = [] for tag, reverse in order: tag = get_sort_tag(tag) # always sort using the default sort key first if first: first = False key_func.append((lambda s: s.sort_key, reverse)) last_order = reverse last_tag = "" # no need to sort twice in a row with the same key/order if tag == last_tag and last_order == reverse: continue last_order = reverse last_tag = tag if tag == "": key_func.append((lambda s: s.sort_key, reverse)) else: sort_func = AudioFile.sort_by_func(tag) key_func.append((sort_func, reverse)) return key_func
def _sort_songs(self, songs): """Sort passed songs in place based on the column sort orders""" last_tag = None last_order = None first = True for tag, reverse in self.get_sort_orders(): tag = get_sort_tag(tag) # always sort using the default sort key first if first: first = False songs.sort(key=lambda s: s.sort_key, reverse=reverse) last_order = reverse last_tag = "" # no need to sort twice in a row with the same key/order if tag == last_tag and last_order == reverse: continue last_order = reverse last_tag = tag if tag == "": songs.sort(key=lambda s: s.sort_key, reverse=reverse) else: sort_func = AudioFile.sort_by_func(tag) songs.sort(key=sort_func, reverse=reverse)
def set_songs(self, songs, sorted=False): model = self.get_model() if not sorted: tag, reverse = self.get_sort_by() tag = self.__get_sort_tag(tag) #try to set a sort indicator that matches the default order if not self.is_sorted(): self.set_sort_by_tag(tag, reverse) if not tag: songs.sort(key=lambda s: s.sort_key, reverse=reverse) else: sort_func = AudioFile.sort_by_func(tag) songs.sort(key=lambda s: s.sort_key) songs.sort(key=sort_func, reverse=reverse) else: self.set_sort_by(None, refresh=False) with self.without_model() as model: model.set(songs) # the song selection has queued a change now, cancel that and # pass the songs manually self.info._update_songs(songs)
def add_songs(self, songs): """Add songs to the list in the right order and position""" if not songs: return model = self.get_model() if not len(model): self.set_songs(songs) return tag, reverse = self.get_sort_by() tag = self.__get_sort_tag(tag) if not self.is_sorted(): self.set_sort_by_tag(tag, reverse) # FIXME: Replace with something fast old_songs = self.get_songs() old_songs.extend(songs) if not tag: old_songs.sort(key=lambda s: s.sort_key, reverse=reverse) else: sort_func = AudioFile.sort_by_func(tag) old_songs.sort(key=lambda s: s.sort_key) old_songs.sort(key=sort_func, reverse=reverse) for index, song in sorted(zip(map(old_songs.index, songs), songs)): model.insert(index, row=[song])
def set_songs(self, songs, sorted=False): model = self.get_model() if not sorted: tag, reverse = self.get_sort_by() tag = self.__get_sort_tag(tag) #try to set a sort indicator that matches the default order if not self.is_sorted(): self.set_sort_by_tag(tag, reverse) if not tag: songs.sort(key=lambda s: s.sort_key, reverse=reverse) else: sort_func = AudioFile.sort_by_func(tag) songs.sort(key=lambda s: s.sort_key) songs.sort(key=sort_func, reverse=reverse) else: self.set_sort_by(None, refresh=False) # Doing set_model(None) resets the sort indicator, so we need to # remember it before doing that. sorts = map(gtk.TreeViewColumn.get_sort_indicator, self.get_columns()) print_d("Detaching model.", context=self) self.set_model(None) model.set(songs) print_d("Attaching model.", context=self) self.set_model(model) print_d("Model attached.", context=self) map(gtk.TreeViewColumn.set_sort_indicator, self.get_columns(), sorts)
def test_set_song(self): c = CoverImage() c.set_song(AudioFile({"~filename": "woo"})) event = Gdk.Event.new(Gdk.EventType.BUTTON_PRESS) event.type.button = 1 c.emit("button-press-event", event) c.destroy()
def plugin_songs(self, songs): # Check this is a launch, not a configure if self.chosen_site: url_pat = self.get_url_pattern(self.chosen_site) pat = Pattern(url_pat) urls = set() for song in songs: # Generate a sanitised AudioFile; allow through most tags subs = AudioFile() for k in (USER_TAGS + MACHINE_TAGS): vals = song.comma(k) if vals: try: subs[k] = quote_plus(unicode(vals).encode('utf-8')) # Dodgy unicode problems except KeyError: print_d("Problem with %s tag values: %r" % (k, vals)) url = str(pat.format(subs)) if not url: print_w("Couldn't build URL using \"%s\"." "Check your pattern?" % url_pat) return # Grr, set.add() should return boolean... if url not in urls: urls.add(url) website(url)
def _render_column(self, column, **kwargs): view = Gtk.TreeView() model = ObjectStore() view.set_model(model) song = AudioFile({"~filename": "/dev/null", "~#rating": 0.6666}) song.update(kwargs) model.append(row=[song]) view.append_column(column) if column.get_resizable(): column.set_expand(True) with visible(view): view.columns_autosize() text = column.get_cells()[0].get_property("text") self.assertIsNot(text, None) return text
def test_sort_func(self): tags = [lambda s: s("foo"), "artistsort", "albumsort", "~filename", "~format", "discnumber", "~#track"] for tag in tags: f = AudioFile.sort_by_func(tag) f(bar_1_1) f(bar_1_2) f(bar_2_1)
def test_header_menu(self): from quodlibet import browsers from quodlibet.library import SongLibrary, SongLibrarian song = AudioFile({"~filename": fsnative(u"/dev/null")}) song.sanitize() self.songlist.set_songs([song]) library = SongLibrary() library.librarian = SongLibrarian() browser = browsers.get("EmptyBar")(library) self.songlist.set_column_headers(["foo"]) self.assertFalse(self.songlist.Menu("foo", browser, library)) sel = self.songlist.get_selection() sel.select_all() self.assertTrue(self.songlist.Menu("foo", browser, library))
def test_performers_multi_value(self): q = AudioFile([ ("performer:vocals", "X\nA\nY"), ("performer:guitar", "Y\nB\nA"), ("performer", "C\nF\nB\nA"), ]) self.failUnlessEqual( set(q.list("~performer")), set(["A", "B", "C", "F", "X", "Y"])) self.failUnlessEqual( set(q.list("~performer:roles")), set([ "A (Guitar, Vocals)", "C", "B (Guitar)", "X (Vocals)", "Y (Guitar, Vocals)", "F", ]))
def test_website(self): song = AudioFile() song["comment"] = "www.foo" song["contact"] = "*****@*****.**" self.failUnlessEqual(song.website(), "www.foo") song["contact"] = "https://www.foo.org" self.failUnlessEqual(song.website(), "https://www.foo.org") song["website"] = "foo\nhttps://another.com" self.failUnlessEqual(song.website(), "foo") song = AudioFile({"artist": "Artist", "album": "Album"}) for value in song.values(): self.failUnless(value in song.website()) song["labelid"] = "QL-12345" self.failIf(song["artist"] in song.website()) self.failUnless(song["labelid"] in song.website())