def enabled(self): if not self.running: wm = WatchManager() self.event_handler = LibraryEvent(app.library) # Choose event types to watch for # FIXME: watch for IN_CREATE or for some reason folder copies # are missed, --nickb FLAGS = ['IN_DELETE', 'IN_CLOSE_WRITE',# 'IN_MODIFY', 'IN_MOVED_FROM', 'IN_MOVED_TO', 'IN_CREATE'] mask = reduce(lambda x, s: x | EventsCodes.ALL_FLAGS[s], FLAGS, 0) if self.USE_THREADS: print_d("Using threaded notifier") self.notifier = ThreadedNotifier(wm, self.event_handler) # Daemonize to ensure thread dies on exit self.notifier.daemon = True self.notifier.start() else: self.notifier = Notifier(wm, self.event_handler, timeout=100) GLib.timeout_add(1000, self.unthreaded_callback) for path in get_scan_dirs(): print_d('Watching directory %s for %s' % (path, FLAGS)) # See https://github.com/seb-m/pyinotify/wiki/ # Frequently-Asked-Questions wm.add_watch(path, mask, rec=True, auto_add=True) self.running = True
def __get_selection_filter(self): """Retuns a filter object for the current selection or None if nothing should be filtered""" selection = self.view.get_selection() model, rows = selection.get_selected_rows() filter_ = None for row in rows: type_ = model[row][self.TYPE] if type_ == self.TYPE_FILTER: key = model[row][self.KEY] current_filter = self.filters.query(key) if current_filter: if filter_: filter_ |= current_filter else: filter_ = current_filter elif type_ == self.TYPE_NOCAT: # if notcat is selected, combine all filters, negate and merge all_ = [self.filters.query(k) for k in self.filters.keys()] nocat_filter = all_ and -reduce(lambda x, y: x | y, all_) if nocat_filter: if filter_: filter_ |= nocat_filter else: filter_ = nocat_filter elif type_ == self.TYPE_ALL: filter_ = None break return filter_
def __get_selection_filter(self): """Returns a filter object for the current selection or None if nothing should be filtered""" selection = self.view.get_selection() model, rows = selection.get_selected_rows() filter_ = None for row in rows: type_ = model[row][self.TYPE] if type_ == self.TYPE_FILTER: key = model[row][self.KEY] current_filter = self.filters.query(key) if current_filter: if filter_: filter_ |= current_filter else: filter_ = current_filter elif type_ == self.TYPE_NOCAT: # if notcat is selected, combine all filters, negate and merge all_ = [self.filters.query(k) for k in self.filters.keys()] nocat_filter = all_ and -reduce(lambda x, y: x | y, all_) if nocat_filter: if filter_: filter_ |= nocat_filter else: filter_ = nocat_filter elif type_ == self.TYPE_ALL: filter_ = None break return filter_
def enabled(self): if not self.running: wm = WatchManager() self.event_handler = LibraryEvent(app.library) # Choose event types to watch for # FIXME: watch for IN_CREATE or for some reason folder copies # are missed, --nickb FLAGS = [ 'IN_DELETE', 'IN_CLOSE_WRITE', # 'IN_MODIFY', 'IN_MOVED_FROM', 'IN_MOVED_TO', 'IN_CREATE' ] mask = reduce(lambda x, s: x | EventsCodes.ALL_FLAGS[s], FLAGS, 0) if self.USE_THREADS: print_d("Using threaded notifier") self.notifier = ThreadedNotifier(wm, self.event_handler) # Daemonize to ensure thread dies on exit self.notifier.daemon = True self.notifier.start() else: self.notifier = Notifier(wm, self.event_handler, timeout=100) GLib.timeout_add(1000, self.unthreaded_callback) for path in get_scan_dirs(): print_d('Watching directory %s for %s' % (path, FLAGS)) # See https://github.com/seb-m/pyinotify/wiki/ # Frequently-Asked-Questions wm.add_watch(path, mask, rec=True, auto_add=True) self.running = True
def enabled(self): if not self.running: wm = WatchManager() self.event_handler = LibraryEvent(library=app.library) FLAGS = ['IN_DELETE', 'IN_CLOSE_WRITE', # 'IN_MODIFY', 'IN_MOVED_FROM', 'IN_MOVED_TO', 'IN_CREATE'] masks = [EventsCodes.FLAG_COLLECTIONS['OP_FLAGS'][s] for s in FLAGS] mask = reduce(operator.or_, masks, 0) if self.USE_THREADS: print_d("Using threaded notifier") self.notifier = ThreadedNotifier(wm, self.event_handler) # Daemonize to ensure thread dies on exit self.notifier.daemon = True self.notifier.start() else: self.notifier = Notifier(wm, self.event_handler, timeout=100) GLib.timeout_add(1000, self.unthreaded_callback) for path in get_scan_dirs(): real_path = os.path.realpath(path) print_d('Watching directory %s for %s (mask: %x)' % (real_path, FLAGS, mask)) # See https://github.com/seb-m/pyinotify/wiki/ # Frequently-Asked-Questions wm.add_watch(real_path, mask, rec=True, auto_add=True) self.running = True
def parse_time(timestr, err=object()): """Parse a time string in hh:mm:ss, mm:ss, or ss format.""" if timestr[0:1] == "-": m = -1 timestr = timestr[1:] else: m = 1 try: return m * reduce(lambda s, a: s * 60 + int(a), re.split(r":|\.", timestr), 0) except (ValueError, re.error): if err is None: raise return 0
def enabled(self): if not self.running: wm = WatchManager() self.event_handler = LibraryEvent(library=app.library) FLAGS = [ 'IN_DELETE', 'IN_CLOSE_WRITE', # 'IN_MODIFY', 'IN_MOVED_FROM', 'IN_MOVED_TO', 'IN_CREATE' ] masks = [ EventsCodes.FLAG_COLLECTIONS['OP_FLAGS'][s] for s in FLAGS ] mask = reduce(operator.or_, masks, 0) if self.USE_THREADS: print_d("Using threaded notifier") self.notifier = ThreadedNotifier(wm, self.event_handler) # Daemonize to ensure thread dies on exit self.notifier.daemon = True self.notifier.start() else: self.notifier = Notifier(wm, self.event_handler, timeout=100) GLib.timeout_add(1000, self.unthreaded_callback) for path in get_scan_dirs(): real_path = os.path.realpath(path) print_d('Watching directory %s for %s (mask: %x)' % (real_path, FLAGS, mask)) # See https://github.com/seb-m/pyinotify/wiki/ # Frequently-Asked-Questions wm.add_watch(real_path, mask, rec=True, auto_add=True) self.running = True
def __update_done(self, stations): if not stations: print_w("Loading remote station list failed.") return # filter stations based on quality, listenercount def filter_stations(station): peak = station.get("~#listenerpeak", 0) if peak < 10: return False aac = "AAC" in station("~format") bitrate = station("~#bitrate", 50) if (aac and bitrate < 40) or (not aac and bitrate < 60): return False return True stations = filter(filter_stations, stations) # group them based on the title groups = {} for s in stations: key = s("~title~artist") groups.setdefault(key, []).append(s) # keep at most 2 URLs for each group stations = [] for key, sub in iteritems(groups): sub.sort(key=lambda s: s.get("~#listenerpeak", 0), reverse=True) stations.extend(sub[:2]) # only keep the ones in at least one category all_ = [self.filters.query(k) for k in self.filters.keys()] assert all_ anycat_filter = reduce(lambda x, y: x | y, all_) stations = list(filter(anycat_filter.search, stations)) # remove listenerpeak for s in stations: s.pop("~#listenerpeak", None) # update the libraries stations = dict(((s.key, s) for s in stations)) # don't add ones that are in the fav list for fav in iterkeys(self.__fav_stations): stations.pop(fav, None) # separate o, n = set(iterkeys(self.__stations)), set(stations) to_add, to_change, to_remove = n - o, o & n, o - n del o, n # migrate stats to_change = [stations.pop(k) for k in to_change] for new in to_change: old = self.__stations[new.key] # clear everything except stats AudioFile.reload(old) # add new metadata except stats for k in (x for x in iterkeys(new) if x not in MIGRATE): old[k] = new[k] to_add = [stations.pop(k) for k in to_add] to_remove = [self.__stations[k] for k in to_remove] self.__stations.remove(to_remove) self.__stations.changed(to_change) self.__stations.add(to_add)
def __update_done(self, stations): if not stations: print_w("Loading remote station list failed.") return # filter stations based on quality, listenercount def filter_stations(station): peak = station.get("~#listenerpeak", 0) if peak < 10: return False aac = "AAC" in station("~format") bitrate = station("~#bitrate", 50) if (aac and bitrate < 40) or (not aac and bitrate < 60): return False return True stations = filter(filter_stations, stations) # group them based on the title groups = {} for s in stations: key = s("~title~artist") groups.setdefault(key, []).append(s) # keep at most 2 URLs for each group stations = [] for key, sub in groups.iteritems(): sub.sort(key=lambda s: s.get("~#listenerpeak", 0), reverse=True) stations.extend(sub[:2]) # only keep the ones in at least one category all_ = [self.filters.query(k) for k in self.filters.keys()] assert all_ anycat_filter = reduce(lambda x, y: x | y, all_) stations = filter(anycat_filter.search, stations) # remove listenerpeak for s in stations: s.pop("~#listenerpeak", None) # update the libraries stations = dict(((s.key, s) for s in stations)) # don't add ones that are in the fav list for fav in self.__fav_stations.iterkeys(): stations.pop(fav, None) # separate o, n = set(self.__stations.iterkeys()), set(stations) to_add, to_change, to_remove = n - o, o & n, o - n del o, n # migrate stats to_change = [stations.pop(k) for k in to_change] for new in to_change: old = self.__stations[new.key] # clear everything except stats AudioFile.reload(old) # add new metadata except stats for k in (x for x in new.iterkeys() if x not in MIGRATE): old[k] = new[k] to_add = [stations.pop(k) for k in to_add] to_remove = [self.__stations[k] for k in to_remove] self.__stations.remove(to_remove) self.__stations.changed(to_change) self.__stations.add(to_add)
def __getmenu(self, column): menu = Gtk.Menu() def selection_done_cb(menu): menu.destroy() menu.connect("selection-done", selection_done_cb) current = SongList.headers[:] current_set = set(current) def tag_title(tag): if tag.startswith("<"): return util.pattern(tag) return util.tag(tag) current = zip(map(tag_title, current), current) def add_header_toggle(menu, pair, active, column=column): header, tag = pair item = Gtk.CheckMenuItem(label=header) item.tag = tag item.set_active(active) item.connect("activate", self.__toggle_header_item, column) item.show() item.set_tooltip_text(tag) menu.append(item) for header in current: add_header_toggle(menu, header, True) sep = SeparatorMenuItem() sep.show() menu.append(sep) trackinfo = """title genre ~title~version ~#track ~#playcount ~#skipcount ~rating ~#length""".split() peopleinfo = """artist ~people performer arranger author composer conductor lyricist originalartist""".split() albuminfo = """album ~album~discsubtitle labelid ~#disc ~#discs ~#tracks albumartist""".split() dateinfo = """date originaldate recordingdate ~#laststarted ~#lastplayed ~#added ~#mtime""".split() fileinfo = """~format ~#bitrate ~#filesize ~filename ~basename ~dirname ~uri ~codec ~encoding""".split() copyinfo = """copyright organization location isrc contact website""".split() all_headers = reduce(lambda x, y: x + y, [trackinfo, peopleinfo, albuminfo, dateinfo, fileinfo, copyinfo]) for name, group in [ (_("All _Headers"), all_headers), (_("_Track Headers"), trackinfo), (_("_Album Headers"), albuminfo), (_("_People Headers"), peopleinfo), (_("_Date Headers"), dateinfo), (_("_File Headers"), fileinfo), (_("_Production Headers"), copyinfo), ]: item = Gtk.MenuItem(label=name, use_underline=True) item.show() menu.append(item) submenu = Gtk.Menu() item.set_submenu(submenu) for header in sorted(zip(map(util.tag, group), group)): add_header_toggle(submenu, header, header[1] in current_set) sep = SeparatorMenuItem() sep.show() menu.append(sep) custom = Gtk.MenuItem(label=_(u"_Customize Headers…"), use_underline=True) custom.show() custom.connect("activate", self.__add_custom_column) menu.append(custom) item = Gtk.CheckMenuItem(label=_("_Expand Column"), use_underline=True) item.set_active(column.get_expand()) item.set_sensitive(column.get_resizable()) def set_expand_cb(item, column): do_expand = item.get_active() if not do_expand: # in case we unexpand, get the current width and set it # so the column doesn't give up all its space # to the left over expanded columns column.set_fixed_width(column.get_width()) else: # in case we expand this seems to trigger a re-distribution # between all expanded columns column.set_fixed_width(-1) column.set_expand(do_expand) self.columns_autosize() sep = SeparatorMenuItem() sep.show() menu.append(sep) item.connect("activate", set_expand_cb, column) item.show() menu.append(item) return menu
def __getmenu(self, column): menu = Gtk.Menu() def selection_done_cb(menu): menu.destroy() menu.connect('selection-done', selection_done_cb) current = SongList.headers[:] current_set = set(current) def tag_title(tag): if tag.startswith("<"): return util.pattern(tag) return util.tag(tag) current = zip(map(tag_title, current), current) def add_header_toggle(menu, pair, active, column=column): header, tag = pair item = Gtk.CheckMenuItem(label=header) item.tag = tag item.set_active(active) item.connect('activate', self.__toggle_header_item, column) item.show() item.set_tooltip_text(tag) menu.append(item) for header in current: add_header_toggle(menu, header, True) sep = SeparatorMenuItem() sep.show() menu.append(sep) trackinfo = """title genre ~title~version ~#track ~#playcount ~#skipcount ~rating ~#length""".split() peopleinfo = """artist ~people performer arranger author composer conductor lyricist originalartist""".split() albuminfo = """album ~album~discsubtitle labelid ~#disc ~#discs ~#tracks albumartist""".split() dateinfo = """date originaldate recordingdate ~#laststarted ~#lastplayed ~#added ~#mtime""".split() fileinfo = """~format ~#bitrate ~#filesize ~filename ~basename ~dirname ~uri ~codec ~encoding""".split() copyinfo = """copyright organization location isrc contact website""".split() all_headers = reduce( lambda x, y: x + y, [trackinfo, peopleinfo, albuminfo, dateinfo, fileinfo, copyinfo]) for name, group in [ (_("All _Headers"), all_headers), (_("_Track Headers"), trackinfo), (_("_Album Headers"), albuminfo), (_("_People Headers"), peopleinfo), (_("_Date Headers"), dateinfo), (_("_File Headers"), fileinfo), (_("_Production Headers"), copyinfo), ]: item = Gtk.MenuItem(label=name, use_underline=True) item.show() menu.append(item) submenu = Gtk.Menu() item.set_submenu(submenu) for header in sorted(zip(map(util.tag, group), group)): add_header_toggle(submenu, header, header[1] in current_set) sep = SeparatorMenuItem() sep.show() menu.append(sep) custom = Gtk.MenuItem(label=_(u"_Customize Headers…"), use_underline=True) custom.show() custom.connect('activate', self.__add_custom_column) menu.append(custom) item = Gtk.CheckMenuItem(label=_("_Expand Column"), use_underline=True) item.set_active(column.get_expand()) item.set_sensitive(column.get_resizable()) def set_expand_cb(item, column): do_expand = item.get_active() if not do_expand: # in case we unexpand, get the current width and set it # so the column doesn't give up all its space # to the left over expanded columns column.set_fixed_width(column.get_width()) else: # in case we expand this seems to trigger a re-distribution # between all expanded columns column.set_fixed_width(-1) column.set_expand(do_expand) self.columns_autosize() sep = SeparatorMenuItem() sep.show() menu.append(sep) item.connect('activate', set_expand_cb, column) item.show() menu.append(item) return menu