def test_nofakes(self): pat = TagsFromPattern('<~#track> - <title>') self.assertEquals(pat.match_path(self.f1), self.nomatch) self.assertEquals(pat.match_path(self.f2), self.nomatch) self.assertEquals(pat.match_path(self.f3), self.nomatch) self.assertEquals(pat.match_path(self.b1), self.nomatch) self.assertEquals(pat.match_path(self.b2), self.nomatch)
def test_empty(self): pat = TagsFromPattern('') self.assertEquals(pat.match_path(self.f1), self.nomatch) self.assertEquals(pat.match_path(self.f2), self.nomatch) self.assertEquals(pat.match_path(self.f3), self.nomatch) self.assertEquals(pat.match_path(self.b1), self.nomatch) self.assertEquals(pat.match_path(self.b2), self.nomatch)
def test_disctrack(self): pat = TagsFromPattern('<discnumber><tracknumber>. <title>') self.assertEquals(pat.match_path(fsnative(u'101. T1.ogg')), dict(discnumber='1', tracknumber='01', title='T1')) self.assertEquals(pat.match_path(fsnative(u'1318. T18.ogg')), dict(discnumber='13', tracknumber='18', title='T18')) self.assertEquals(pat.match_path(fsnative(u'24. T4.ogg')), dict(discnumber='2', tracknumber='4', title='T4'))
def test_tracktitle(self): tracktitle = {'tracknumber': '01', 'title': 'Title'} btracktitle = {'tracknumber': '01', 'title': 'Artist - Title'} pat = TagsFromPattern('<tracknumber> - <title>') self.assertEquals(pat.match_path(self.f1), tracktitle) self.assertEquals(pat.match_path(self.f2), self.nomatch) self.assertEquals(pat.match_path(self.f3), btracktitle) self.assertEquals(pat.match_path(self.b1), self.nomatch) self.assertEquals(pat.match_path(self.b2), self.nomatch)
def test_skip(self): if os.name == "nt": pat = TagsFromPattern('<path>\\<~>\\<~>\\<tracknumber> - <title>') else: pat = TagsFromPattern('<path>/<~>/<~>/<tracknumber> - <title>') self.failUnlessEqual(len(pat.headers), 3) song = pat.match({"~filename": self.f1}) self.failUnlessEqual(song.get("path"), "path") self.failUnlessEqual(song.get("title"), "Title") self.failIf(song.get("album")) self.failIf(song.get("artist"))
def test_all(self): all = {'tracknumber': '01', 'title': 'Title', 'album': 'Album', 'artist': 'Artist'} if os.name == "nt": pat = TagsFromPattern('<artist>\\<album>\\<tracknumber> - <title>') else: pat = TagsFromPattern('<artist>/<album>/<tracknumber> - <title>') self.assertEquals(pat.match_path(self.f1), all) self.assertEquals(pat.match_path(self.f2), self.nomatch) self.assertEquals(pat.match_path(self.f3), self.nomatch) self.assertEquals(pat.match_path(self.b1), self.nomatch) self.assertEquals(pat.match_path(self.b2), self.nomatch)
def test_songtypes(self): from quodlibet import formats pat = TagsFromPattern('<tracknumber>. <title>') tracktitle = {'tracknumber': '01', 'title': 'Title'} for ext, kind in formats.loaders.items(): f = formats._audio.AudioFile() if not isinstance(kind, type): continue f.__class__ = kind if os.name == "nt": f["~filename"] = u'C:\\path\\Artist - Album\\01. Title' + ext else: f["~filename"] = '/path/Artist - Album/01. Title' + ext self.assertEquals(pat.match(f), tracktitle, ext)
def test_songtypes(self): from quodlibet import formats pat = TagsFromPattern('<tracknumber>. <title>') tracktitle = {'tracknumber': '01', 'title': 'Title'} for ext, kind in formats._infos.iteritems(): f = formats._audio.AudioFile() if not isinstance(kind, type): continue f.__class__ = kind if os.name == "nt": f["~filename"] = u'C:\\path\\Artist - Album\\01. Title' + ext else: f["~filename"] = '/path/Artist - Album/01. Title' + ext self.assertEquals(pat.match(f), tracktitle, ext)
def __save(self, addreplace, library): pattern_text = self.combo.get_child().get_text().decode('utf-8') pattern = TagsFromPattern(pattern_text) model = self.view.get_model() add = bool(addreplace.get_active()) win = WritingWindow(self, len(model)) win.show() was_changed = set() all_done = False for entry in ((model and model.itervalues()) or []): song = entry.song changed = False if not song.valid(): win.hide() dialog = OverwriteWarning(self, song) resp = dialog.run() win.show() if resp != OverwriteWarning.RESPONSE_SAVE: break for i, h in enumerate(pattern.headers): text = entry.get_match(h) if text: can_multiple = song.can_multiple_values(h) if not add or h not in song or not can_multiple: song[h] = text changed = True else: for val in text.split("\n"): if val not in song.list(h): song.add(h, val) changed = True if changed: try: song.write() except: util.print_exc() WriteFailedError(self, song).run() library.reload(song, changed=was_changed) break was_changed.add(song) if win.step(): break else: all_done = True win.destroy() library.changed(was_changed) self.save.set_sensitive(not all_done)
def test_all(self): all = { 'tracknumber': '01', 'title': 'Title', 'album': 'Album', 'artist': 'Artist' } if os.name == "nt": pat = TagsFromPattern('<artist>\\<album>\\<tracknumber> - <title>') else: pat = TagsFromPattern('<artist>/<album>/<tracknumber> - <title>') self.assertEquals(pat.match_path(self.f1), all) self.assertEquals(pat.match_path(self.f2), self.nomatch) self.assertEquals(pat.match_path(self.f3), self.nomatch) self.assertEquals(pat.match_path(self.b1), self.nomatch) self.assertEquals(pat.match_path(self.b2), self.nomatch)
def _execute(self, options, args): if len(args) < 2: raise CommandError("Not enough arguments") pattern_text = args[0] self.log("Using pattern: %r" % pattern_text) paths = args[1:] pattern = TagsFromPattern(pattern_text) songs = [] for path in paths: song = self.load_song(path) for header in pattern.headers: if not song.can_change(header): raise CommandError(_("Can not set %r") % header) songs.append(song) if options.dry_run: self.__preview(pattern, songs) else: self.__apply(pattern, songs)
def test_dict(self): tracktitle = {'tracknumber': '01', 'title': 'Title'} pat = TagsFromPattern('<tracknumber> - <title>') self.assertEquals(pat.match({"~filename": self.f1}), tracktitle)
def test_nongreedy(self): pat = TagsFromPattern('<artist> - <title>') dic = pat.match_path( fsnative(u"Prefuse 73 - The End of Biters - International.ogg")) self.assertEquals(dic["artist"], "Prefuse 73") self.assertEquals(dic["title"], "The End of Biters - International")
def __preview(self, songs): if songs is None: songs = [row[0].song for row in (self.view.get_model() or [])] if songs: pattern_text = gdecode(self.combo.get_child().get_text()) else: pattern_text = "" try: pattern = TagsFromPattern(pattern_text) except re.error: qltk.ErrorMessage( self, _("Invalid pattern"), _("The pattern\n\t<b>%s</b>\nis invalid. " "Possibly it contains the same tag twice or " "it has unbalanced brackets (< / >).") % ( util.escape(pattern_text))).run() return else: if pattern_text: self.combo.prepend_text(pattern_text) self.combo.write(TBP) invalid = [] for header in pattern.headers: if not min([song.can_change(header) for song in songs]): invalid.append(header) if len(invalid) and songs: if len(invalid) == 1: title = _("Invalid tag") msg = _("Invalid tag <b>%s</b>\n\nThe files currently" " selected do not support editing this tag.") else: title = _("Invalid tags") msg = _("Invalid tags <b>%s</b>\n\nThe files currently" " selected do not support editing these tags.") qltk.ErrorMessage( self, title, msg % ", ".join(invalid)).run() pattern = TagsFromPattern("") self.view.set_model(None) model = ObjectStore() for col in self.view.get_columns(): self.view.remove_column(col) render = Gtk.CellRendererText() col = TreeViewColumn(title=_('File')) col.pack_start(render, True) col.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) def cell_data_file(column, cell, model, iter_, data): entry = model.get_value(iter_) cell.set_property("text", entry.name) col.set_cell_data_func(render, cell_data_file) def cell_data_header(column, cell, model, iter_, header): entry = model.get_value(iter_) cell.set_property("text", entry.get_match(header)) self.view.append_column(col) for i, header in enumerate(pattern.headers): render = Gtk.CellRendererText() render.set_property('editable', True) render.connect('edited', self.__row_edited, model, header) escaped_title = header.replace("_", "__") col = Gtk.TreeViewColumn(escaped_title, render) col.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) col.set_cell_data_func(render, cell_data_header, header) self.view.append_column(col) for song in songs: entry = ListEntry(song) match = pattern.match(song) for h in pattern.headers: text = match.get(h, '') for f in self.filter_box.filters: if f.active: text = f.filter(h, text) if not song.can_multiple_values(h): text = u", ".join(text.split("\n")) entry.matches[h] = text model.append([entry]) # save for last to potentially save time if songs: self.view.set_model(model) self.preview.set_sensitive(False) self.save.set_sensitive(len(pattern.headers) > 0)
def __preview(self, songs): if songs is None: songs = [row[0].song for row in (self.view.get_model() or [])] if songs: pattern_text = self.combo.get_child().get_text().decode("utf-8") else: pattern_text = "" try: pattern = TagsFromPattern(pattern_text) except re.error: qltk.ErrorMessage( self, _("Invalid pattern"), _("The pattern\n\t<b>%s</b>\nis invalid. " "Possibly it contains the same tag twice or " "it has unbalanced brackets (< / >).") % (util.escape(pattern_text))).run() return else: if pattern_text: self.combo.prepend_text(pattern_text) self.combo.write(const.TBP) invalid = [] for header in pattern.headers: if not min([song.can_change(header) for song in songs]): invalid.append(header) if len(invalid) and songs: if len(invalid) == 1: title = _("Invalid tag") msg = _("Invalid tag <b>%s</b>\n\nThe files currently" " selected do not support editing this tag.") else: title = _("Invalid tags") msg = _("Invalid tags <b>%s</b>\n\nThe files currently" " selected do not support editing these tags.") qltk.ErrorMessage(self, title, msg % ", ".join(invalid)).run() pattern = TagsFromPattern("") self.view.set_model(None) model = ObjectStore() for col in self.view.get_columns(): self.view.remove_column(col) render = Gtk.CellRendererText() col = TreeViewColumn(_('File'), render) col.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) def cell_data_file(column, cell, model, iter_, data): entry = model.get_value(iter_) cell.set_property("text", entry.name) col.set_cell_data_func(render, cell_data_file) def cell_data_header(column, cell, model, iter_, header): entry = model.get_value(iter_) cell.set_property("text", entry.get_match(header)) self.view.append_column(col) for i, header in enumerate(pattern.headers): render = Gtk.CellRendererText() render.set_property('editable', True) render.connect('edited', self.__row_edited, model, header) escaped_title = header.replace("_", "__") col = Gtk.TreeViewColumn(escaped_title, render) col.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) col.set_cell_data_func(render, cell_data_header, header) self.view.append_column(col) for song in songs: entry = ListEntry(song) match = pattern.match(song) for h in pattern.headers: text = match.get(h, '') for f in self.filter_box.filters: if f.active: text = f.filter(h, text) if not song.can_multiple_values(h): text = u", ".join(text.split("\n")) entry.matches[h] = text model.append([entry]) # save for last to potentially save time if songs: self.view.set_model(model) self.preview.set_sensitive(False) self.save.set_sensitive(len(pattern.headers) > 0)