def test_main(self): self.assertEqual(decode_value("~#foo", 0.25), u"0.25") self.assertEqual(decode_value("~#foo", 4), u"4") self.assertEqual(decode_value("~#foo", "bar"), u"bar") self.assertTrue(isinstance(decode_value("~#foo", "bar"), text_type)) path = fsnative(u"/foobar") self.assertEqual(decode_value("~filename", path), fsn2text(path))
def sort_key(song): """Sort by path so untagged albums have a good start order. Also take into account the directory in case it's split in different folders by medium. """ return util.human_sort_key(fsn2text(song("~filename")))
def test_path(self): try: path = bytes2fsn(b"\xff\xff", "utf-8") except ValueError: return assert decode_value("~filename", path) == fsn2text(path)
def arg2text(arg): """Like fsn2text but is strict by default and raises CommandError""" try: return fsn2text(arg, strict=True) except ValueError as e: raise CommandError(e)
def print_exc(exc_info=None, context=None): """Prints the stack trace of the current exception or the passed one. Depending on the configuration will either print a short summary or the whole stacktrace. """ if exc_info is None: exc_info = sys.exc_info() etype, value, tb = exc_info if const.DEBUG: string = u"".join(format_exception(etype, value, tb)) else: # try to get a short error message pointing at the cause of # the exception text = u"".join(format_exception_only(etype, value)) try: filename, lineno, name, line = extract_tb(tb)[-1] except IndexError: # no stack string = text else: string = u"%s:%s:%s: %s" % ( fsn2text(path2fsn(os.path.basename(filename))), lineno, name, text) _print_message(string, context, False, "E", "red", "errors")
def get_available_languages(domain): """Returns a list of available translations for a given gettext domain. Args: domain (str) Returns: List[text_type] """ locale_dir = gettext.bindtextdomain(domain) if locale_dir is None: return [] try: entries = os.listdir(locale_dir) except OSError: return [] langs = [u"C"] for lang in entries: mo_path = os.path.join( locale_dir, lang, "LC_MESSAGES", "%s.mo" % domain) if os.path.exists(mo_path): langs.append(fsn2text(path2fsn(lang))) return langs
def _init_gettext(no_translations=False): """Call before using gettext helpers""" if no_translations: language = u"C" else: language = config.gettext("settings", "language") if not language: language = None i18n.init(language) # Use the locale dir in ../build/share/locale if there is one base_dir = get_base_dir() localedir = os.path.dirname(base_dir) localedir = os.path.join(localedir, "build", "share", "locale") if not os.path.isdir(localedir) and os.name == "nt": # py2exe case localedir = os.path.join( base_dir, "..", "..", "share", "locale") i18n.register_translation("quodlibet", localedir) debug_text = environ.get("QUODLIBET_TEST_TRANS") if debug_text is not None: i18n.set_debug_text(fsn2text(debug_text))
def scan(self, paths, exclude=[], cofuncid=None): def need_yield(last_yield=[0]): current = time.time() if abs(current - last_yield[0]) > 0.015: last_yield[0] = current return True return False def need_added(last_added=[0]): current = time.time() if abs(current - last_added[0]) > 1.0: last_added[0] = current return True return False # first scan each path for new files paths_to_load = [] for scan_path in paths: print_d("Scanning %r." % scan_path) desc = _("Scanning %s") % (fsn2text(unexpand(scan_path))) with Task(_("Library"), desc) as task: if cofuncid: task.copool(cofuncid) for real_path in iter_paths(scan_path, exclude=exclude): if need_yield(): task.pulse() yield # skip unknown file extensions if not formats.filter(real_path): continue # already loaded if self.contains_filename(real_path): continue paths_to_load.append(real_path) yield # then (try to) load all new files with Task(_("Library"), _("Loading files")) as task: if cofuncid: task.copool(cofuncid) added = [] for real_path in task.gen(paths_to_load): item = self.add_filename(real_path, False) if item is not None: added.append(item) if len(added) > 100 or need_added(): self.add(added) added = [] yield if added and need_yield(): yield if added: self.add(added) added = [] yield True
def cdf(column, cell, model, iter, data): row = model[iter] filename = fsn2text(unexpand(row[0])) function = row[1] line = row[2] cell.set_property( "markup", "<b>%s</b> line %d\n\t%s" % ( util.escape(function), line, util.escape(filename)))
def label_path(path): l = Gtk.Label(label="<a href='%s'>%s</a>" % ( fsn2uri(path), escape(fsn2text(unexpand(path)))), use_markup=True, ellipsize=Pango.EllipsizeMode.MIDDLE, xalign=0, selectable=True) l.connect("activate-link", show_uri) return l
def __init__(self, parent, song): title = _("Unable to save song") fn_format = "<b>%s</b>" % util.escape(fsn2text(song("~basename"))) description = _("Saving %(file-name)s failed. The file may be " "read-only, corrupted, or you do not have " "permission to edit it.") % {"file-name": fn_format} super(WriteFailedError, self).__init__( parent, title, description)
def _execute(self, options, args): if len(args) < 3: raise CommandError(_("Not enough arguments")) tag = fsn2text(args[0]) value = fsn2text(args[1]) paths = args[2:] songs = [] for path in paths: song = self.load_song(path) if not song.can_change(tag): raise CommandError(_("Can not set %r") % tag) self.log("Add %r to %r" % (value, tag)) song.add(tag, value) songs.append(song) self.save_songs(songs)
def __init__(self, parent, path): title = _("File exists") fn_format = "<b>%s</b>" % util.escape(fsn2text(path2fsn(path))) description = _("Replace %(file-name)s?") % {"file-name": fn_format} super(ConfirmFileReplace, self).__init__( parent, title, description, buttons=Gtk.ButtonsType.NONE) self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) self.add_icon_button(_("_Replace File"), Icons.DOCUMENT_SAVE, self.RESPONSE_REPLACE) self.set_default_response(Gtk.ResponseType.CANCEL)
def __preview(self, pattern, songs): rows = [] for song in songs: match = pattern.match(song) row = [fsn2text(song("~basename"))] for header in pattern.headers: row.append(match.get(header, u"")) rows.append(row) headers = [_("File")] + pattern.headers nicks = ["file"] + pattern.headers print_table(rows, headers, nicks, nicks)
def unescape_filename(s): """Unescape a string in a manner suitable for a filename. Args: filename (fsnative) Returns: text_type """ assert isinstance(s, fsnative) return fsn2text(unquote(s))
def search(self, data): for name in self._names: val = data.get(name) if val is None: # filename is the only real entry that's a path if name == "filename": val = fsn2text(data.get("~filename", fsnative())) else: val = data.get("~" + name, "") if self.res.search(unicode(val)): return True for name in self.__intern: if self.res.search(unicode(data(name))): return True for name in self.__fs: if self.res.search(fsn2text(data(name))): return True return False
def parse_m3u(filename, library=None): plname = fsn2text(path2fsn(os.path.basename( os.path.splitext(filename)[0]))) filenames = [] with open(filename, "rb") as h: for line in h: line = line.strip() if line.startswith("#"): continue else: filenames.append(line) return __parse_playlist(plname, filename, filenames, library)
def decode_value(tag, value): """Returns a unicode representation of the passed value, based on the type and the tag it originated from. Not reversible. """ if isinstance(value, text_type): return value elif isinstance(value, float): return u"%.2f" % value elif tag in FILESYSTEM_TAGS: return fsn2text(value) return text_type(value)
def fsdecode(path, note=True): """Takes a native path and returns unicode for displaying it. Can not fail and can't be reversed. """ if isinstance(path, text_type): return path # XXX: glib paths on Windows if os.name == "nt" and isinstance(path, bytes): path = path.decode("utf-8") return fsn2text(path)
def __init__(self, paths): super(FileListExpander, self).__init__(label=_("Files:")) self.set_resize_toplevel(True) paths = [fsn2text(unexpand(p)) for p in paths] lab = Gtk.Label(label="\n".join(paths)) lab.set_alignment(0.0, 0.0) lab.set_selectable(True) win = Gtk.ScrolledWindow() win.add_with_viewport(Align(lab, border=6)) win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) win.set_shadow_type(Gtk.ShadowType.ETCHED_OUT) win.set_size_request(-1, 100) self.add(win) win.show_all()
def search(self, data): search = self.res.search fs_default = fsnative() for name in self._names: val = data.get(name) if val is None: if name in ("filename", "mountpoint"): val = fsn2text(data.get("~" + name, fs_default)) else: val = data.get("~" + name, u"") if search(val): return True for name in self.__intern: if search(data(name)): return True for name in self.__fs: if search(fsn2text(data(name, fs_default))): return True return False
def sort_by_func(tag): """Returns a fast sort function for a specific tag (or pattern). Some keys are already in the sort cache, so we can use them.""" def artist_sort(song): return song.sort_key[1][2] if callable(tag): return lambda song: human(tag(song)) elif tag == "artistsort": return artist_sort elif tag in FILESYSTEM_TAGS: return lambda song: fsn2text(song(tag)) elif tag.startswith("~#") and "~" not in tag[2:]: return lambda song: song(tag) return lambda song: human(song(tag))
def _song_to_text(self, song): # to text lines = [] for key in sorted(song.realkeys(), key=sortkey): for value in song.list(key): lines.append(u"%s=%s" % (key, value)) lines += [ u"", u"#" * 80, u"# Lines that are empty or start with '#' will be ignored", u"# File: %r" % fsn2text(song("~filename")), ] return u"\n".join(lines)
def __init__(self, parent, song): title = _("Tag may not be accurate") fn_format = "<b>%s</b>" % util.escape(fsn2text(song("~basename"))) description = _( "%(file-name)s changed while the program was running. " "Saving without refreshing your library may " "overwrite other changes to the song." ) % {"file-name": fn_format} super(OverwriteWarning, self).__init__(parent, title, description, buttons=Gtk.ButtonsType.NONE) self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) self.add_icon_button(_("_Save"), Icons.DOCUMENT_SAVE, self.RESPONSE_SAVE) self.set_default_response(Gtk.ResponseType.CANCEL)
def __init__(self, filename): with translate_errors(): with open(filename, "rb") as h: head = h.read(46) if len(head) != 46 or \ head[:27] != b'SNES-SPC700 Sound File Data': raise IOError("Not a valid SNES-SPC700 file") if getbyte(head, 35) == b'\x1a': data = h.read(210) if len(data) == 210: self.update(parse_id666(data)) self.setdefault( "title", fsn2text(path2fsn(os.path.basename(filename)[:-4]))) self.sanitize(filename)
def parse_m3u(filename, library=None): plname = fsn2text(path2fsn(os.path.basename( os.path.splitext(filename)[0]))) filenames = [] with open(filename, "rb") as h: for line in h: line = line.strip() if line.startswith(b"#"): continue else: try: filenames.append(bytes2fsn(line, "utf-8")) except ValueError: continue return __parse_playlist(plname, filename, filenames, library)
def match_path(self, path): assert isinstance(path, fsnative) tail = os.path.splitdrive(path)[-1] # only match on the last n pieces of a filename, dictated by pattern # this means no pattern may effectively cross a /, despite .* doing so sep = os.path.sep matchon = sep + sep.join(tail.split(sep)[-self.slashes:]) # work on unicode matchon = fsn2text(matchon) match = self.pattern.search(matchon) # dicts for all! if match is None: return {} else: return match.groupdict()
def parse_pls(filename, name="", library=None): plname = fsn2text(path2fsn(os.path.basename( os.path.splitext(filename)[0]))) filenames = [] with open(filename) as h: for line in h: line = line.strip() if not line.lower().startswith("file"): continue else: try: line = line[line.index("=") + 1:].strip() except ValueError: pass else: filenames.append(line) return __parse_playlist(plname, filename, filenames, library)
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 comma(self, key): """Get all values of a tag, separated by commas. Synthetic tags are supported, but will be slower. All list items will be unicode. If the value is numeric, that is returned rather than a list. """ if "~" in key or key == "title": v = self(key, u"") if key in FILESYSTEM_TAGS: v = fsn2text(v) else: v = self.get(key, u"") if isinstance(v, number_types): return v else: return v.replace("\n", ", ")
def _name_for(filename): if not filename: return _("New Playlist") name = os.path.basename(os.path.splitext(filename)[0]) return fsn2text(path2fsn(name))
def cell_data(column, cell, model, iter_, userdata): value = model.get_value(iter_) cell.set_property('text', fsn2text(os.path.basename(value)))
def name(self): return fsn2text(self.song("~basename"))
def format_exception(*args, **kwargs): """Returns a list of text_type""" result_lines = traceback.format_exception(*args, **kwargs) return [fsn2text(path2fsn(l)) for l in result_lines]
def cell_data(column, cell, model, iter_, userdata): value = model.get_value(iter_) if value is not None: text = fsn2text(os.path.basename(value) or value) cell.set_property('text', text)
def cover(self): # TODO: Deserves some refactoring if not self.song.is_file: return None base = self.song('~dirname') images = [] # Issue 374: Specify artwork filename if config.getboolean("albumart", "force_filename"): path = os.path.join(base, config.get("albumart", "filename")) if os.path.isfile(path): images = [(100, path)] else: entries = [] try: entries = os.listdir(base) except EnvironmentError: print_w("Can't list album art directory %s" % base) fns = [] for entry in entries: lentry = entry.lower() if get_ext(lentry) in self.cover_exts: fns.append((None, entry)) if lentry in self.cover_subdirs: subdir = os.path.join(base, entry) sub_entries = [] try: sub_entries = os.listdir(subdir) except EnvironmentError: pass for sub_entry in sub_entries: lsub_entry = sub_entry.lower() if get_ext(lsub_entry) in self.cover_exts: fns.append((entry, sub_entry)) for sub, fn in fns: dec_lfn = fsn2text(fn).lower() score = 0 # check for the album label number labelid = self.song.get("labelid", "").lower() if labelid and labelid in dec_lfn: score += 20 # Track-related keywords values = self.song.list("~people") + [self.song("album")] lowers = [ value.lower().strip() for value in values if len(value) > 1 ] score += 2 * sum([value in dec_lfn for value in lowers]) # Generic keywords score += 3 * sum( r.search(dec_lfn) is not None for r in self.cover_positive_regexes) score -= 2 * sum( r.search(dec_lfn) is not None for r in self.cover_negative_regexes) # print("[%s - %s]: Album art \"%s\" scores %d." % # (self.song("artist"), self.song("title"), fn, score)) if score > 0: if sub is not None: fn = os.path.join(sub, fn) images.append((score, os.path.join(base, fn))) images.sort(reverse=True) for score, path in images: # could be a directory if not os.path.isfile(path): continue try: return open(path, "rb") except IOError: print_w("Failed reading album art \"%s\"" % path) return None
def _apply_value(self, model, iter_, cell, value): cell.set_property('text', fsn2text(unexpand(value)))
def cover(self): # TODO: Deserves some refactoring if not self.song.is_file: return None base = self.song('~dirname') images = [] if config.getboolean("albumart", "force_filename"): score = 100 for filename in config.get("albumart", "filename").split(","): # Remove white space to avoid confusion (e.g. "name, name2") filename = filename.strip() escaped_path = os.path.join(glob.escape(base), filename) try: for path in glob.glob(escaped_path): images.append((score, path)) except sre_constants.error: # Use literal filename if globbing causes errors path = os.path.join(base, filename) # We check this here, so we can search for alternative # files in case no preferred file was found. if os.path.isfile(path): images.append((score, path)) # So names and patterns at the start are preferred score -= 1 if not images: entries = [] try: entries = os.listdir(base) except EnvironmentError: print_w("Can't list album art directory %s" % base) fns = [] for entry in entries: lentry = entry.lower() if get_ext(lentry) in self.cover_exts: fns.append((None, entry)) if lentry in self.cover_subdirs: subdir = os.path.join(base, entry) sub_entries = [] try: sub_entries = os.listdir(subdir) except EnvironmentError: pass for sub_entry in sub_entries: lsub_entry = sub_entry.lower() if get_ext(lsub_entry) in self.cover_exts: fns.append((entry, sub_entry)) for sub, fn in fns: dec_lfn = os.path.splitext(fsn2text(fn))[0].lower() score = 0 # check for the album label number labelid = self.song.get("labelid", "").lower() if labelid and labelid in dec_lfn: score += 20 # Track-related keywords values = set(self.song.list("~people")) | {self.song("album")} lowers = [value.lower().strip() for value in values if len(value) > 1] total_terms = sum(len(s.split()) for s in lowers) total_words = len([word for word in dec_lfn.split() if len(word) > 1]) # Penalise for many extra words in filename (wrong file?) length_penalty = (- int((total_words - 1) / total_terms) if total_terms else 0) # Matching tag values are very good score += 3 * sum([value in dec_lfn for value in lowers]) # Well known names matching exactly (folder.jpg) score += 4 * sum(r.search(dec_lfn) is not None for r in self.cover_name_regexes) # Generic keywords score += 2 * sum(r.search(dec_lfn) is not None for r in self.cover_positive_regexes) score -= 3 * sum(r.search(dec_lfn) is not None for r in self.cover_negative_regexes) sub_text = f" (in {sub!r})" if sub else "" if self.DEBUG: print(f"[{self.song('~~people~title')}]: " f"Album art {fn!r}{sub_text} " f"scores {score} ({length_penalty})") score += length_penalty # Let's only match if we're quite sure. # This allows other sources to kick in if score > 2: if sub is not None: fn = os.path.join(sub, fn) images.append((score, os.path.join(base, fn))) images.sort(reverse=True) for score, path in images: # could be a directory if not os.path.isfile(path): continue try: return open(path, "rb") except IOError: print_w("Failed reading album art \"%s\"" % path) return None
def copy(self, parent_widget, song): if self.__load_db() is None: return False track = gpod.itdb_track_new() # All values should be utf-8 encoded strings # Filepaths should be encoded with the fs encoding # Either combine tags with comma, or only take the first value if self['all_tags']: tag = song.comma else: tag = lambda key: (song.list(key) or ('', ))[0] title = tag('title') if self['title_version'] and song('version'): title = " - ".join([title, song('version')]) track.title = encode(title) album = tag('album') if self['album_part'] and song('discsubtitle'): album = " - ".join([album, song('discsubtitle')]) track.album = encode(album) # String keys for key in ['artist', 'genre', 'grouping', 'composer', 'albumartist']: if hasattr(track, key): # albumartist since libgpod-0.4.2 setattr(track, key, encode(tag(key))) # Sort keys (since libgpod-0.5.0) for key in ['artist', 'album', 'albumartist']: if hasattr(track, 'sort_' + key): setattr(track, 'sort_' + key, encode(tag(key + 'sort'))) # Numeric keys for key in ['bitrate', 'playcount', 'year']: try: setattr(track, key, int(song('~#' + key))) except ValueError: continue # Numeric keys where the names differ for key, value in { 'cd_nr': song('~#disc'), 'cds': song('~#discs'), 'rating': min(100, song('~#rating') * 100), 'time_added': self.__mactime(time.time()), 'time_modified': self.__mactime(mtime(song('~filename'))), 'track_nr': song('~#track'), 'tracklen': song('~#length') * 1000, 'tracks': song('~#tracks'), 'size': filesize(song('~filename')), 'soundcheck': self.__soundcheck(song), }.items(): try: setattr(track, key, int(value)) except ValueError: continue track.filetype = encode(song('~format')) track.comment = encode(fsn2text(song('~filename'))) # Associate a cover with the track if self['covers']: cover = app.cover_manager.get_cover(song) if cover: # libgpod will copy the file later when the iTunesDB # is saved, so we have to keep a reference around in # case the cover is a temporary file. self.__covers.append(cover) gpod.itdb_track_set_thumbnails(track, fsn2glib(cover.name)) # Add the track to the master playlist gpod.itdb_track_add(self.__itdb, track, -1) master = gpod.itdb_playlist_mpl(self.__itdb) gpod.itdb_playlist_add_track(master, track, -1) # Copy the actual file if gpod.itdb_cp_track_to_ipod(track, song['~filename'], None) == 1: return IPodSong(track) else: return False
def filename(self): if self._song is not None: return fsn2text(self._song('~filename')) else: return self._filename
def cdf(column, cell, model, iter, data): row = model[iter] cell.set_property('text', fsn2text(row[0]))
def __name_datafunc(self, col, cell, model, itr, data): song = model[itr][0] if song: cell.set_property('text', fsn2text(song("~basename"))) else: cell.set_property('text', '')
def scan(self, paths: Iterable[fsnative], exclude: Optional[Iterable[fsnative]] = None, cofuncid=None): def need_yield(last_yield=[0]): current = time.time() if abs(current - last_yield[0]) > 0.015: last_yield[0] = current return True return False def need_added(last_added=[0]): current = time.time() if abs(current - last_added[0]) > 1.0: last_added[0] = current return True return False # first scan each path for new files paths_to_load = [] for scan_path in paths: print_d(f"Scanning {scan_path}", self._name) desc = _("Scanning %s") % (fsn2text(unexpand(scan_path))) with Task(_("Library"), desc) as task: if cofuncid: task.copool(cofuncid) for real_path in iter_paths(scan_path, exclude=exclude): if need_yield(): task.pulse() yield # skip unknown file extensions if not formats.filter(real_path): continue # already loaded if self.contains_filename(real_path): continue paths_to_load.append(real_path) yield # then (try to) load all new files with Task(_("Library"), _("Loading files")) as task: if cofuncid: task.copool(cofuncid) added = [] for real_path in task.gen(paths_to_load): item = self.add_filename(real_path, False) if item is not None: added.append(item) if len(added) > 100 or need_added(): self.add(added) added = [] yield if added and need_yield(): yield if added: self.add(added) added = [] yield True
def format_exception(etype, value, tb, limit=None): """Returns a list of text_type""" result_lines = traceback.format_exception(etype, value, tb, limit) return [fsn2text(path2fsn(l)) for l in result_lines]
def format_exc(*args, **kwargs): """Returns text_type""" # stack traces can contain byte paths under py2 return fsn2text(path2fsn(traceback.format_exc(*args, **kwargs)))
def format_exception_only(etype, value): """Returns a list of str""" result_lines = traceback.format_exception_only(etype, value) return [fsn2text(path2fsn(l)) for l in result_lines]
def sanitize(self, filename): super(WAVEFile, self).sanitize(filename) self["title"] = fsn2text( os.path.splitext(os.path.basename(self["~filename"]))[0])
def notfsnative(text=u""): fsn = fsnative(text) if isinstance(fsn, bytes): return fsn2text(fsn) else: return fsn2bytes(fsn, "utf-8")
def scan(self, paths, exclude=[], scan_dots=False, cofuncid=None): added = [] exclude = [expanduser(path) for path in exclude if path] def need_yield(last_yield=[0]): current = time.time() if abs(current - last_yield[0]) > 0.015: last_yield[0] = current return True return False def need_added(last_added=[0]): current = time.time() if abs(current - last_added[0]) > 1.0: last_added[0] = current return True return False for fullpath in paths: print_d("Scanning %r." % fullpath, self) desc = _("Scanning %s") % (unexpand(fsn2text(fullpath))) with Task(_("Library"), desc) as task: if cofuncid: task.copool(cofuncid) fullpath = expanduser(fullpath) if filter(fullpath.startswith, exclude): continue for path, dnames, fnames in os.walk(fullpath): if not scan_dots: index = 0 while index < len(dnames): if dnames[index].startswith('.'): del (dnames[index]) else: index += 1 for filename in fnames: if not scan_dots and filename.startswith('.'): continue fullfilename = os.path.join(path, filename) if filter(fullfilename.startswith, exclude): continue if fullfilename not in self._contents: fullfilename = os.path.realpath(fullfilename) # skip unknown file extensions if not formats.filter(fullfilename): continue if filter(fullfilename.startswith, exclude): continue if fullfilename not in self._contents: item = self.add_filename(fullfilename, False) if item is not None: added.append(item) if len(added) > 100 or need_added(): self.add(added) added = [] task.pulse() yield if added and need_yield(): yield if added: self.add(added) added = [] task.pulse() yield True
def process_arguments(argv): from quodlibet.util.path import uri_is_valid from quodlibet import util from quodlibet import const actions = [] controls = [ "next", "previous", "play", "pause", "play-pause", "stop", "hide-window", "show-window", "toggle-window", "focus", "quit", "unfilter", "refresh", "force-previous" ] controls_opt = [ "seek", "repeat", "query", "volume", "filter", "set-rating", "set-browser", "open-browser", "shuffle", "song-list", "queue", "stop-after", "random", "repeat-type", "shuffle-type" ] options = util.OptionParser("Quod Libet", const.VERSION, _("a music library and player"), _("[option]")) options.add("print-playing", help=_("Print the playing song and exit")) options.add("start-playing", help=_("Begin playing immediately")) options.add("start-hidden", help=_("Don't show any windows on start")) for opt, help in [ ("next", _("Jump to next song")), ("previous", _("Jump to previous song or restart if near the beginning")), ("force-previous", _("Jump to previous song")), ("play", _("Start playback")), ("pause", _("Pause playback")), ("play-pause", _("Toggle play/pause mode")), ("stop", _("Stop playback")), ("volume-up", _("Turn up volume")), ("volume-down", _("Turn down volume")), ("status", _("Print player status")), ("hide-window", _("Hide main window")), ("show-window", _("Show main window")), ("toggle-window", _("Toggle main window visibility")), ("focus", _("Focus the running player")), ("unfilter", _("Remove active browser filters")), ("refresh", _("Refresh and rescan library")), ("list-browsers", _("List available browsers")), ("print-playlist", _("Print the current playlist")), ("print-queue", _("Print the contents of the queue")), ("print-query-text", _("Print the active text query")), ("no-plugins", _("Start without plugins")), ("run", _("Start Quod Libet if it isn't running")), ("quit", _("Exit Quod Libet")), ]: options.add(opt, help=help) for opt, help, arg in [ ("seek", _("Seek within the playing song"), _("[+|-][HH:]MM:SS")), ("shuffle", _("Set or toggle shuffle mode"), "0|1|t"), ("shuffle-type", _("Set shuffle mode type"), "random|weighted|off"), ("repeat", _("Turn repeat off, on, or toggle it"), "0|1|t"), ("repeat-type", _("Set repeat mode type"), "current|all|one|off"), ("volume", _("Set the volume"), "(+|-|)0..100"), ("query", _("Search your audio library"), _("query")), ("play-file", _("Play a file"), C_("command", "filename")), ("set-rating", _("Rate the playing song"), "0.0..1.0"), ("set-browser", _("Set the current browser"), "BrowserName"), ("stop-after", _("Stop after the playing song"), "0|1|t"), ("open-browser", _("Open a new browser"), "BrowserName"), ("queue", _("Show or hide the queue"), "on|off|t"), ("song-list", _("Show or hide the main song list (deprecated)"), "on|off|t"), ("random", _("Filter on a random value"), C_("command", "tag")), ("filter", _("Filter on a tag value"), _("tag=value")), ("enqueue", _("Enqueue a file or query"), "%s|%s" % (C_("command", "filename"), _("query"))), ("enqueue-files", _("Enqueue comma-separated files"), "%s[,%s..]" % (_("filename"), _("filename"))), ("print-query", _("Print filenames of results of query to stdout"), _("query")), ("unqueue", _("Unqueue a file or query"), "%s|%s" % (C_("command", "filename"), _("query"))), ]: options.add(opt, help=help, arg=arg) options.add("sm-config-prefix", arg="dummy") options.add("sm-client-id", arg="prefix") options.add("screen", arg="dummy") def is_vol(str): if len(str) == 1 and str[0] in '+-': return True return is_float(str) def is_time(str): if str[0] not in "+-0123456789": return False elif str[0] in "+-": str = str[1:] parts = str.split(":") if len(parts) > 3: return False else: return not (False in [p.isdigit() for p in parts]) def is_float(str): try: float(str) except ValueError: return False else: return True validators = { "shuffle": ["0", "1", "t", "on", "off", "toggle"].__contains__, "shuffle-type": ["random", "weighted", "off", "0"].__contains__, "repeat": ["0", "1", "t", "on", "off", "toggle"].__contains__, "repeat-type": ["current", "all", "one", "off", "0"].__contains__, "volume": is_vol, "seek": is_time, "set-rating": is_float, "stop-after": ["0", "1", "t"].__contains__, } cmds_todo = [] def queue(*args): cmds_todo.append(args) # XXX: to make startup work in case the desktop file isn't passed # a file path/uri if argv[-1] == "--play-file": argv = argv[:-1] opts, args = options.parse(argv[1:]) for command, arg in opts.items(): if command in controls: queue(command) elif command in controls_opt: if command in validators and not validators[command](arg): print_e(_("Invalid argument for '%s'.") % command) print_e(_("Try %s --help.") % fsn2text(argv[0])) exit_(True, notify_startup=True) else: queue(command, arg) elif command == "status": queue("status") elif command == "print-playlist": queue("dump-playlist") elif command == "print-queue": queue("dump-queue") elif command == "list-browsers": queue("dump-browsers") elif command == "volume-up": queue("volume +") elif command == "volume-down": queue("volume -") elif command == "enqueue" or command == "unqueue": try: filename = uri2fsn(arg) except ValueError: filename = arg queue(command, filename) elif command == "enqueue-files": queue(command, arg) elif command == "play-file": if uri_is_valid(arg) and arg.startswith("quodlibet://"): # TODO: allow handling of URIs without --play-file queue("uri-received", arg) else: try: filename = uri2fsn(arg) except ValueError: filename = arg filename = os.path.abspath(util.path.expanduser(arg)) queue("play-file", filename) elif command == "print-playing": try: queue("print-playing", args[0]) except IndexError: queue("print-playing") elif command == "print-query": queue(command, arg) elif command == "print-query-text": queue(command) elif command == "start-playing": actions.append(command) elif command == "start-hidden": actions.append(command) elif command == "no-plugins": actions.append(command) elif command == "run": actions.append(command) if cmds_todo: for cmd in cmds_todo: control(*cmd, **{"ignore_error": "run" in actions}) else: # this will exit if it succeeds control('focus', ignore_error=True) return actions, cmds_todo
def test_text_fsn_roudntrip(text): if u"\x00" in text: return assert isinstance(fsn2text(text2fsn(text)), text_type)
def cdf(column, cell, model, iter_, data): path = model.get_value(iter_) cell.set_property('text', fsn2text(unexpand(path)))