def masked(self, item): """Return true if the item is in the library but masked.""" try: point = item.mountpoint except AttributeError: # Checking a key. for point in itervalues(self._masked): if item in point: return True else: # Checking a full item. return item in itervalues(self._masked.get(point, {}))
def __save_files(self, parent, model, library): win = WritingWindow(parent, len(model)) was_changed = set() all_done = False for entry in itervalues(model): song, track = entry.song, entry.tracknumber if song.get("tracknumber") == track: win.step() continue if not song.valid(): win.hide() dialog = OverwriteWarning(self, song) resp = dialog.run() win.show() if resp != OverwriteWarning.RESPONSE_SAVE: break song["tracknumber"] = track try: song.write() except AudioFileError: 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 library.changed(was_changed) win.destroy() self.save.set_sensitive(not all_done) self.revert.set_sensitive(not all_done)
def __update(self, songs, total, model, save, revert): if songs is None: songs = [e.song for e in itervalues(model)] else: songs = list(songs) def sort_key(song): return song("~#track", 0), song("~basename"), song songs.sort(key=sort_key) model.clear() total.set_value(len(songs)) for song in songs: if not song.can_change("tracknumber"): self.set_sensitive(False) break else: self.set_sensitive(True) for song in songs: model.append([Entry(song)]) save.set_sensitive(False) revert.set_sensitive(False)
def __init__(self, parent, library): DBusIntrospectable.__init__(self) DBusPropertyFilter.__init__(self) MediaObject.__init__(self, parent) MediaContainer.__init__(self) bus = dbus.SessionBus() self.ref = dbus.service.BusName(BUS_NAME, bus) dbus.service.FallbackObject.__init__(self, bus, self.PATH) parent.register_child(self) self.__library = library.albums self.__library.load() self.__map = dict((id(v), v) for v in itervalues(self.__library)) self.__reverse = dict((v, k) for k, v in iteritems(self.__map)) signals = [ ("changed", self.__albums_changed), ("removed", self.__albums_removed), ("added", self.__albums_added), ] self.__sigs = map(lambda x: self.__library.connect(x[0], x[1]), signals) self.__dummy = DummyAlbumObject(self)
def __invoke(self, librarian, event, *args): args = list(args) if args and args[0]: if isinstance(args[0], dict): args[0] = SongWrapper(args[0]) elif isinstance(args[0], (set, list)): args[0] = ListWrapper(args[0]) for plugin in itervalues(self.__plugins): method_name = 'plugin_on_' + event.replace('-', '_') handler = getattr(plugin, method_name, None) def overridden(obj, name): return name in type(obj).__dict__ if overridden(plugin, method_name): try: handler(*args) except Exception: print_e("Error during %s on %s" % (method_name, type(plugin))) util.print_exc() if event not in ["removed", "changed"] and args: from quodlibet import app songs = args[0] if not isinstance(songs, (set, list)): songs = [songs] songs = filter(None, songs) check_wrapper_changed(librarian, app.window, songs)
def Query(self, text): if text is not None: query = Query(text, star=SongList.star) if query.is_parsable: return [self.__dict(s) for s in itervalues(self.library) if query.search(s)] return None
def changed(self, items): """Alert other users that these items have changed. This causes a 'changed' signal. If a librarian is available this function will call its changed method instead, and all libraries that librarian manages may fire a 'changed' signal. The item list may be filtered to those items actually in the library. If a librarian is available, it will handle the filtering instead. That means if this method is delegated to the librarian, this library's changed signal may not fire, but another's might. """ if not items: return if self.librarian and self in itervalues(self.librarian.libraries): print_d("Changing %d items via librarian." % len(items), self) self.librarian.changed(items) else: items = {item for item in items if item in self} if not items: return print_d("Changing %d items directly." % len(items), self) self._changed(items)
def rename(self, song, newname, changed=None): """Rename the song in all libraries it belongs to. The 'changed' signal will fire for any library the song is in except if a set() is passed as changed. """ # This needs to poke around inside the library directly. If # it uses add/remove to handle the songs it fires incorrect # signals. If it uses the library's rename method, it breaks # the call for future libraries because the item's key has # changed. So, it needs to reimplement the method. re_add = [] print_d("Renaming %r to %r" % (song.key, newname), self) for library in itervalues(self.libraries): try: del library._contents[song.key] except KeyError: pass else: re_add.append(library) song.rename(newname) for library in re_add: library._contents[song.key] = song if changed is None: library._changed({song}) else: print_d("Delaying changed signal for %r." % library, self) changed.add(song)
def __init__(self, library, users): DBusIntrospectable.__init__(self) DBusProperty.__init__(self) MediaObject.__init__(self, None) MediaItem.__init__(self, optional=SUPPORTED_SONG_PROPERTIES) bus = dbus.SessionBus() self.ref = dbus.service.BusName(BUS_NAME, bus) dbus.service.FallbackObject.__init__(self, bus, self.PATH) self.__library = library self.__map = dict((id(v), v) for v in itervalues(self.__library)) self.__reverse = dict((v, k) for k, v in iteritems(self.__map)) self.__song = DummySongObject(self) self.__users = users signals = [ ("changed", self.__songs_changed), ("removed", self.__songs_removed), ("added", self.__songs_added), ] self.__sigs = map(lambda x: self.__library.connect(x[0], x[1]), signals)
def __init__(self, parent, library): DBusIntrospectable.__init__(self) DBusPropertyFilter.__init__(self) MediaObject.__init__(self, parent) MediaContainer.__init__(self) bus = dbus.SessionBus() self.ref = dbus.service.BusName(BUS_NAME, bus) dbus.service.FallbackObject.__init__(self, bus, self.PATH) parent.register_child(self) self.__library = library.albums self.__library.load() self.__map = dict((id(v), v) for v in itervalues(self.__library)) self.__reverse = dict((v, k) for k, v in iteritems(self.__map)) signals = [ ("changed", self.__albums_changed), ("removed", self.__albums_removed), ("added", self.__albums_added), ] self.__sigs = map( lambda x: self.__library.connect(x[0], x[1]), signals) self.__dummy = DummyAlbumObject(self)
def __init__(self, library, users): DBusIntrospectable.__init__(self) DBusProperty.__init__(self) MediaObject.__init__(self, None) MediaItem.__init__(self, optional=SUPPORTED_SONG_PROPERTIES) bus = dbus.SessionBus() self.ref = dbus.service.BusName(BUS_NAME, bus) dbus.service.FallbackObject.__init__(self, bus, self.PATH) self.__library = library self.__map = dict((id(v), v) for v in itervalues(self.__library)) self.__reverse = dict((v, k) for k, v in iteritems(self.__map)) self.__song = DummySongObject(self) self.__users = users signals = [ ("changed", self.__songs_changed), ("removed", self.__songs_removed), ("added", self.__songs_added), ] self.__sigs = map( lambda x: self.__library.connect(x[0], x[1]), signals)
def tag_values(self, tag): """Return a set of all values for the given tag.""" return { value for lib in itervalues(self.libraries) for value in lib.tag_values(tag) }
def __contains__(self, item): """Check if a key or item is in the library.""" for library in itervalues(self.libraries): if item in library: return True else: return False
def changed(self, items): """Triage the items and inform their real libraries.""" for library in itervalues(self.libraries): in_library = set(item for item in items if item in library) if in_library: library._changed(in_library)
def test_values(self): items = [] for i in range(20): items.append(self.Fake(i)) items[-1].key = i + 100 self.library.add(items) self.failUnlessEqual(sorted(self.library.values()), list(range(20))) self.failUnlessEqual(sorted(itervalues(self.library)), list(range(20)))
def _plugins(self): """All registered plugins""" plugins = [] for module in itervalues(self.__modules): for plugin in module.plugins: plugins.append(plugin) return plugins
def _get_all(sub, found=None): if found is None: found = set() if isinstance(sub, list): found.update(sub) return found for v in itervalues(sub): _get_all(v, found) return found
def mask(self, point): print_d("Masking %r." % point, self) removed = {} for item in itervalues(self): if item.mountpoint == point: removed[item.key] = item if removed: self.remove(removed.values()) self._masked.setdefault(point, {}).update(removed)
def Query(self, text): if text is not None: query = Query(text, star=SongList.star) if query.is_parsable: return [ self.__dict(s) for s in itervalues(self.library) if query.search(s) ] return None
def __getitem__(self, key): """Find a item given its key.""" for library in itervalues(self.libraries): try: return library[key] except KeyError: pass else: raise KeyError(key)
def test_values(self): items = [] for i in range(20): items.append(self.Fake(i)) items[-1].key = i + 100 self.library.add(items) self.failUnlessEqual(sorted(self.library.values()), list(range(20))) self.failUnlessEqual( sorted(itervalues(self.library)), list(range(20)))
def list(self, tag): """Return a list of unique values for the given tag. This needs to be here since not all browsers pull from the default library. """ library = app.library bg = background_filter() if bg: songs = filter(bg, itervalues(library)) return list({value for song in songs for value in song.list(tag)}) return list(library.tag_values(tag))
def Query(self, query): if query is not None: try: results = Query(query, star=SongList.star).search except Query.error: pass else: return [self.__dict(s) for s in itervalues(self.library) if results(s)] return None
def __rename(self, library): model = self.view.get_model() win = WritingWindow(self, len(model)) win.show() was_changed = set() skip_all = False self.view.freeze_child_notify() for entry in itervalues(model): song = entry.song new_name = entry.new_name old_name = entry.name if new_name is None: continue try: library.rename(song, text2fsn(new_name), changed=was_changed) except Exception: util.print_exc() if skip_all: continue RESPONSE_SKIP_ALL = 1 msg = qltk.Message( Gtk.MessageType.ERROR, win, _("Unable to rename file"), _("Renaming <b>%(old-name)s</b> to <b>%(new-name)s</b> " "failed. Possibly the target file already exists, " "or you do not have permission to make the " "new file or remove the old one.") % { "old-name": util.escape(old_name), "new-name": util.escape(new_name), }, buttons=Gtk.ButtonsType.NONE) msg.add_button(_("Ignore _All Errors"), RESPONSE_SKIP_ALL) msg.add_icon_button(_("_Stop"), Icons.PROCESS_STOP, Gtk.ResponseType.CANCEL) msg.add_button(_("_Continue"), Gtk.ResponseType.OK) msg.set_default_response(Gtk.ResponseType.OK) resp = msg.run() skip_all |= (resp == RESPONSE_SKIP_ALL) # Preserve old behavior: shift-click is Ignore All mods = Gdk.Display.get_default().get_pointer()[3] skip_all |= mods & Gdk.ModifierType.SHIFT_MASK library.reload(song, changed=was_changed) if resp != Gtk.ResponseType.OK and resp != RESPONSE_SKIP_ALL: break if win.step(): break self.view.thaw_child_notify() win.destroy() library.changed(was_changed) self.save.set_sensitive(False)
def __save(self, addreplace, library): pattern_text = gdecode(self.combo.get_child().get_text()) 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 itervalues(model)) 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 AudioFileError: 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 Query(self, query): if query is not None: try: results = Query(query, star=SongList.star).search except Query.error: pass else: return [ self.__dict(s) for s in itervalues(self.library) if results(s) ] return None
def __init__(self, library): super(AlbumModel, self).__init__() self.__library = library albums = library.albums self.__sigs = [ albums.connect("added", self._add_albums), albums.connect("removed", self._remove_albums), albums.connect("changed", self._change_albums) ] self.append(row=[AlbumItem(None)]) self.append_many((AlbumItem(a) for a in itervalues(albums)))
def compile(self, song_func, text_formatter=None): tags = [] queries = {} content = ["def f(s):", " x = s." + song_func, " r = []", " a = r.append"] content.extend(self.__tag(self.__root, {}, {}, tags, queries, text_formatter)) content.append(" return r") code = "\n".join(content) scope = dict(itervalues(queries)) if text_formatter: scope["_format"] = text_formatter exec_(compile(code, "<string>", "exec"), scope) return scope["f"], tags
def compile(self, song_func): tags = [] queries = {} content = [ "def f(s):", " x = s." + song_func, " r = []", " a = r.append" ] content.extend(self.__tag(self.__root, {}, {}, tags, queries)) content.append(" return r") code = "\n".join(content) scope = dict(itervalues(queries)) exec_(compile(code, "<string>", "exec"), scope) return scope["f"], tags
def _preview(self, songs): model = self.view.get_model() if songs is None: songs = [e.song for e in itervalues(model)] pattern_text = self.combo.get_child().get_text() try: pattern = FileFromPattern(pattern_text) except ValueError: qltk.ErrorMessage( self, _("Path is not absolute"), _("The pattern\n\t<b>%s</b>\ncontains / but " "does not start from root. To avoid misnamed " "folders, root your pattern by starting " "it with / or ~/.") % ( util.escape(pattern_text))).run() return else: if pattern: self.combo.prepend_text(pattern_text) self.combo.write(NBP) # native paths orignames = [song["~filename"] for song in songs] newnames = [fsn2text(pattern.format(song)) for song in songs] for f in self.filter_box.filters: if f.active: newnames = f.filter_list(orignames, newnames) model.clear() for song, newname in zip(songs, newnames): entry = Entry(song) entry.new_name = newname model.append(row=[entry]) self.preview.set_sensitive(False) self.save.set_sensitive(bool(pattern_text)) for song in songs: if not song.is_file: self.set_sensitive(False) break else: self.set_sensitive(True)
def __preview(self, songs): model = self.view.get_model() if songs is None: songs = [e.song for e in itervalues(model)] pattern_text = gdecode(self.combo.get_child().get_text()) try: pattern = FileFromPattern(pattern_text) except ValueError: qltk.ErrorMessage( self, _("Path is not absolute"), _("The pattern\n\t<b>%s</b>\ncontains / but " "does not start from root. To avoid misnamed " "folders, root your pattern by starting " "it with / or ~/.") % ( util.escape(pattern_text))).run() return else: if pattern: self.combo.prepend_text(pattern_text) self.combo.write(NBP) # native paths orignames = [song["~filename"] for song in songs] newnames = [fsn2text(pattern.format(song)) for song in songs] for f in self.filter_box.filters: if f.active: newnames = f.filter_list(orignames, newnames) model.clear() for song, newname in zip(songs, newnames): entry = Entry(song) entry.new_name = newname model.append(row=[entry]) self.preview.set_sensitive(False) self.save.set_sensitive(bool(pattern_text)) for song in songs: if not song.is_file: self.set_sensitive(False) break else: self.set_sensitive(True)
def reload(self, item, changed=None, removed=None): """Reload a song (for all libraries), possibly noting its status. If sets are given, it assumes the caller will handle signals, and only updates the sets. Otherwise, it handles signals itself. It *always* handles library contents, so do not try to remove (again) a song that appears in the removed set. """ had_item = [] print_d("Reloading %r" % item.key, self) for library in itervalues(self.libraries): try: del library._contents[item.key] except KeyError: pass else: had_item.append(library) try: library = had_item[0] except IndexError: return # Rely on the first library in the list to do the actual # load, then just inform the other libraries what happened. was_changed, was_removed = library._load_item(item, force=True) assert not (was_changed and was_removed) if was_removed: if removed is None: for library in had_item: library.emit('removed', {item}) else: removed.add(item) elif was_changed: for library in had_item: library._contents[item.key] = item if changed is None: for library in had_item: library.emit('changed', {item}) else: changed.add(item)
def __invoke(self, librarian, event, *args): args = list(args) if args and args[0]: if isinstance(args[0], dict): args[0] = SongWrapper(args[0]) elif isinstance(args[0], (set, list)): args[0] = ListWrapper(args[0]) for plugin in itervalues(self.__plugins): method_name = 'plugin_on_' + event.replace('-', '_') handler = getattr(plugin, method_name, None) if handler is not None: try: handler(*args) except Exception: util.print_exc() if event not in ["removed", "changed"] and args: from quodlibet import app songs = args[0] if not isinstance(songs, (set, list)): songs = [songs] songs = filter(None, songs) check_wrapper_changed(librarian, app.window, songs)
def tag_values(self, tag): """Return a set of all values for the given tag.""" return {value for song in itervalues(self) for value in song.list(tag)}
def itervalues(self): return itervalues(self._contents)
def _modules(self): return itervalues(self.__scanner.modules)
def tag_values(self, tag): """Return a list of all values for the given tag.""" tags = set() for library in itervalues(self.libraries): tags.update(library.tag_values(tag)) return list(tags)
def __iter__(self): """Iterate over the items in the library.""" return itervalues(self._contents)