def __init__(self, filename): with translate_errors(): audio = Musepack(filename) super(MPCFile, self).__init__(filename, audio) self["~#length"] = audio.info.length self["~#bitrate"] = int(audio.info.bitrate / 1000) version = audio.info.version self["~codec"] = u"%s SV%d" % (self.format, version) try: if audio.info.title_gain: track_g = u"%+0.2f dB" % audio.info.title_gain self.setdefault("replaygain_track_gain", track_g) if audio.info.album_gain: album_g = u"%+0.2f dB" % audio.info.album_gain self.setdefault("replaygain_album_gain", album_g) if audio.info.title_peak: track_p = text_type(audio.info.title_peak * 2) self.setdefault("replaygain_track_peak", track_p) if audio.info.album_peak: album_p = text_type(audio.info.album_peak * 2) self.setdefault("replaygain_album_peak", album_p) except AttributeError: pass self.sanitize(filename)
def send_feedback(dsn, event_id, name, email, comment, timeout): """Send feedback, blocking. Args: dsn (str): The DSN event_id (str): The event ID this feedback should be attached to name (text_type): The user name email (text_type): The user email comment (text_type): The feedback text timeout (float): The timeout for this request Raises: SentryError: In case of timeout or other errors """ name = text_type(name).encode("utf-8") email = text_type(email).encode("utf-8") comment = text_type(comment).encode("utf-8") data = urlencode( [('name', name), ('email', email), ('comments', comment)]) if not isinstance(data, bytes): # py3 data = data.encode("utf-8") headers = {"Referer": "https://quodlibet.github.io"} params = urlencode([("dsn", dsn), ("eventId", event_id)]) try: req = Request( "https://sentry.io/api/embed/error-page/?" + params, data=data, headers=headers) urlopen(req, timeout=timeout).close() except EnvironmentError as e: raise SentryError(e)
def _execute(self, options, args): if len(args) < 1: raise CommandError(_("Not enough arguments")) elif len(args) > 1: raise CommandError(_("Too many arguments")) path = args[0] song = self.load_song(path) headers = [_("Description"), _("Value")] nicks = ["desc", "value"] if not options.columns: order = nicks else: order = [n.strip() for n in options.columns.split(",")] if not options.terse: tags = [] for key in ["~format", "~codec", "~encoding", "~length", "~bitrate", "~filesize"]: tags.append((util.tag(key), text_type(song.comma(key)))) print_table(tags, headers, nicks, order) else: tags = [] for key in ["~format", "~codec", "~encoding", "~#length", "~#bitrate", "~#filesize"]: tags.append((key.lstrip("#~"), text_type(song(key)))) print_terse_table(tags, nicks, order)
def send_feedback(dsn, event_id, name, email, comment, timeout): """Send feedback, blocking. Args: dsn (str): The DSN event_id (str): The event ID this feedback should be attached to name (text_type): The user name email (text_type): The user email comment (text_type): The feedback text timeout (float): The timeout for this request Raises: SentryError: In case of timeout or other errors """ name = text_type(name).encode("utf-8") email = text_type(email).encode("utf-8") comment = text_type(comment).encode("utf-8") data = urlencode([('name', name), ('email', email), ('comments', comment)]) if not isinstance(data, bytes): # py3 data = data.encode("utf-8") headers = {"Referer": "https://quodlibet.github.io"} params = urlencode([("dsn", dsn), ("eventId", event_id)]) try: req = Request("https://sentry.io/api/embed/error-page/?" + params, data=data, headers=headers) urlopen(req, timeout=timeout).close() except EnvironmentError as e: raise SentryError(e)
def upgettext(self, context, msgid): context = text_type(context) msgid = text_type(msgid) real_msgid = u"%s\x04%s" % (context, msgid) result = self.ugettext(real_msgid) if result == real_msgid: return msgid return result
def ugettext(self, message): # force unicode here since __contains__ (used in gettext) ignores # our changed defaultencoding for coercion, so utf-8 encoded strings # fail at lookup. message = text_type(message) if PY2: return text_type(gettext.GNUTranslations.ugettext(self, message)) else: return text_type(gettext.GNUTranslations.gettext(self, message))
def ungettext(self, msgid1, msgid2, n): # see ugettext msgid1 = text_type(msgid1) msgid2 = text_type(msgid2) if PY2: return text_type( gettext.GNUTranslations.ungettext(self, msgid1, msgid2, n)) else: return text_type( gettext.GNUTranslations.ngettext(self, msgid1, msgid2, n))
def unpgettext(self, context, msgid, msgidplural, n): context = text_type(context) msgid = text_type(msgid) msgidplural = text_type(msgidplural) real_msgid = u"%s\x04%s" % (context, msgid) real_msgidplural = u"%s\x04%s" % (context, msgidplural) result = self.ngettext(real_msgid, real_msgidplural, n) if result == real_msgid: return msgid elif result == real_msgidplural: return msgidplural return result
def __init__(self, pattern, mod_string): self.pattern = text_type(pattern) self.mod_string = text_type(mod_string) ignore_case = "c" not in self.mod_string or "i" in self.mod_string dot_all = "s" in self.mod_string asym = "d" in self.mod_string try: self.search = compile(self.pattern, ignore_case, dot_all, asym) except ValueError: raise ParseError( "The regular expression /%s/ is invalid." % self.pattern)
def setstringlist(self, section, option, values): """Saves a list of unicode strings using the csv module""" if PY2: sw = cBytesIO() values = [text_type(v).encode('utf-8') for v in values] else: sw = StringIO() values = [text_type(v) for v in values] writer = csv.writer(sw, lineterminator='\n', quoting=csv.QUOTE_MINIMAL) writer.writerow(values) self.set(section, option, sw.getvalue())
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 tag in FILESYSTEM_TAGS: return fsn2text(value) elif tag[:2] == "~#": if isinstance(value, float): return u"%.2f" % value else: return text_type(value) return text_type(value)
def build_issue_url(title, body): """Returns an URL which provides a pre-filled github issue. Args: title (text_type): The issue title body (text_type): The issue content Returns: str: the URL to open """ title = text_type(title).encode("utf-8") body = text_type(body).encode("utf-8") params = urlencode([("title", title), ("body", body)]) return "https://github.com/quodlibet/quodlibet/issues/new?%s" % params
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: encoded = text_type(vals).encode('utf-8') subs[k] = (encoded if k == 'website' else quote_plus(encoded)) # 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 add_station(uri): """Fetches the URI content and extracts IRFiles Returns None in error, else a possibly filled list of stations""" irfs = [] if uri.lower().endswith(".pls") or uri.lower().endswith(".m3u"): if not re.match('^([^/:]+)://', uri): # Assume HTTP if no protocol given. See #2731 uri = 'http://' + uri print_d("Assuming http: %s" % uri) try: sock = urlopen(uri) except EnvironmentError as err: err = "%s\n\nURL: %s" % (text_type(err), uri) print_d("Got %s from %s" % (err, uri)) ErrorMessage(None, _("Unable to add station"), escape(err)).run() return None if uri.lower().endswith(".pls"): irfs = ParsePLS(sock) elif uri.lower().endswith(".m3u"): irfs = ParseM3U(sock) sock.close() else: try: irfs = [IRFile(uri)] except ValueError as err: ErrorMessage(None, _("Unable to add station"), err).run() return irfs
def get_grouping_key(self): """Given a stacktrace produced by the faulthandler module returns a short string for grouping similar stacktraces together. Args: stacktrace (text_type) Returns: text_type """ stacktrace = text_type(self) if isinstance(stacktrace, bytes): stacktrace = stacktrace.decode("utf-8", "replace") assert isinstance(stacktrace, text_type) # Extract the basename and the function name for each line and hash # them. Could be smarter, but let's try this for now.. reg = re.compile('.*?"([^"]+).*?(\w+$)') values = [] for l in stacktrace.splitlines(): m = reg.match(l) if m is not None: path, func = m.groups() path = os.path.basename(path) values.extend([path, func]) return u"|".join(values)
def get_grouping_key(self): """Given a stacktrace produced by the faulthandler module returns a short string for grouping similar stacktraces together. Args: stacktrace (text_type) Returns: text_type """ stacktrace = text_type(self) if isinstance(stacktrace, bytes): stacktrace = stacktrace.decode("utf-8", "replace") assert isinstance(stacktrace, text_type) # Extract the basename and the function name for each line and hash # them. Could be smarter, but let's try this for now.. reg = re.compile(r'.*?"([^"]+).*?(\w+$)') values = [] for l in stacktrace.splitlines(): m = reg.match(l) if m is not None: path, func = m.groups() path = os.path.basename(path) values.extend([path, func]) return u"|".join(values)
def add_station(uri): """Fetches the URI content and extracts IRFiles Returns None in error, else a possibly filled list of stations""" irfs = [] if uri.lower().endswith(".pls") or uri.lower().endswith(".m3u"): try: sock = urlopen(uri) except EnvironmentError as err: err = text_type(err) print_d("Got %s from %s" % (uri, err)) ErrorMessage(None, _("Unable to add station"), escape(err)).run() return None if uri.lower().endswith(".pls"): irfs = ParsePLS(sock) elif uri.lower().endswith(".m3u"): irfs = ParseM3U(sock) sock.close() else: try: irfs = [IRFile(uri)] except ValueError as err: ErrorMessage(None, _("Unable to add station"), err).run() return irfs
def _try_build_device(self, object_path, block, fs): """Returns a Device instance or None. None if it wasn't a media player etc.. """ drive = self._drives.get(block["Drive"]) if not drive: # I think this shouldn't happen, but check anyway return dev_path = dbus_barray_to_bytes(block["Device"]) print_d("Found device: %r" % dev_path) media_player_id = get_media_player_id(self._udev, dev_path) if not media_player_id: print_d("%r not a media player" % dev_path) return protocols = get_media_player_protocols(media_player_id) device_id = drive["Id"] dev = self.create_device(object_path, text_type(device_id), protocols) icon_name = block["HintIconName"] if icon_name: dev.icon = icon_name return dev
def __init__(self, filename): with translate_errors(): audio = MP4(filename) self["~codec"] = audio.info.codec_description self["~#length"] = audio.info.length self["~#bitrate"] = int(audio.info.bitrate / 1000) if audio.info.channels: self["~#channels"] = audio.info.channels self["~#samplerate"] = audio.info.sample_rate self["~#bitdepth"] = audio.info.bits_per_sample for key, values in audio.items(): if key in self.__tupletranslate: if values: name = self.__tupletranslate[key] cur, total = values[0] if total: self[name] = u"%d/%d" % (cur, total) else: self[name] = text_type(cur) elif key in self.__translate: name = self.__translate[key] if key == "tmpo": self[name] = u"\n".join(map(text_type, values)) elif key.startswith("----"): self[name] = "\n".join( map(lambda v: decode(v).strip("\x00"), values)) else: self[name] = "\n".join(values) elif key == "covr": self.has_images = True self.sanitize(filename)
def __init__(self, filename): with translate_errors(): audio = MP4(filename) self["~codec"] = audio.info.codec_description self["~#length"] = audio.info.length self["~#bitrate"] = int(audio.info.bitrate / 1000) if audio.info.channels: self["~#channels"] = audio.info.channels for key, values in audio.items(): if key in self.__tupletranslate: if values: name = self.__tupletranslate[key] cur, total = values[0] if total: self[name] = u"%d/%d" % (cur, total) else: self[name] = text_type(cur) elif key in self.__translate: name = self.__translate[key] if key == "tmpo": self[name] = u"\n".join(map(text_type, values)) elif key.startswith("----"): self[name] = "\n".join( map(lambda v: decode(v).strip("\x00"), values)) else: self[name] = "\n".join(values) elif key == "covr": self.has_images = True self.sanitize(filename)
def get_github_issue_url(exc_info): """Gives an URL for a pre-filled github issue based on an exception Returns: str """ error_title = (text_type(exc_info[1]).strip() or u"\n").splitlines()[0] title = u"[Error] %s: %s" % (exc_info[0].__name__, error_title) error_text = u"\n".join(format_exception(*exc_info)) body = u"""\ * What did you try to do when the error occurred? Error Details: ``` %s Version: %s Python: %s Platform: %s ``` """ % (error_text, quodlibet.get_build_description(), sys.version, platform.platform()) return build_issue_url(title, body)
def _apply_value(self, model, iter_, cell, value): if isinstance(value, float): text = u"%.2f" % round(value, 2) else: text = text_type(value) cell.set_property('text', text) self._recalc_width(model.get_path(iter_), text)
def _validated_name(self, new_name): """Returns a transformed (or not) name, or raises a `ValueError` if the name is not allowed """ new_name = text_type(new_name) if not new_name: raise ValueError(_("Playlists must have a name")) return new_name
def settext(self, section, option, value): value = text_type(value) if PY2: value = value.encode("utf-8") else: # make sure there are no surrogates value.encode("utf-8") self.set(section, option, value)
def setUp(self): config.init() self.library = SongLibrary() backend = quodlibet.player.init_backend("nullbe") self.device = backend.init(self.library) self.songs = [AudioFile({"title": x}) for x in ["song1", "song2", "song3"]] for song in self.songs: song.sanitize(fsnative(text_type(song["title"])))
def build_song_data(release, track): """Returns a dict of tags to apply to a song. All the values are unicode. If the value is empty it means the tag should be deleted. """ meta = {} join = lambda l: "\n".join(l) # track/disc data meta["tracknumber"] = "%s/%d" % (track.tracknumber, track.track_count) if release.disc_count > 1: meta["discnumber"] = "%s/%d" % (track.discnumber, release.disc_count) else: meta["discnumber"] = "" meta["title"] = track.title meta["musicbrainz_releasetrackid"] = track.id meta["musicbrainz_trackid"] = u"" # we used to write those, so delete # disc data meta["discsubtitle"] = track.disctitle # release data meta["album"] = release.title meta["date"] = release.date meta["musicbrainz_albumid"] = release.id meta["labelid"] = release.labelid if not release.is_single_artist and not release.is_various_artists: artists = release.artists meta["albumartist"] = join([a.name for a in artists]) meta["albumartistsort"] = join([a.sort_name for a in artists]) meta["musicbrainz_albumartistid"] = join([a.id for a in artists]) else: meta["albumartist"] = "" meta["albumartistsort"] = "" meta["musicbrainz_albumartistid"] = "" meta["artist"] = join([a.name for a in track.artists]) meta["artistsort"] = join([a.sort_name for a in track.artists]) meta["musicbrainz_artistid"] = join([a.id for a in track.artists]) meta["musicbrainz_releasetrackid"] = track.id # clean up "redundant" data if meta["albumartist"] == meta["albumartistsort"]: meta["albumartistsort"] = "" if meta["artist"] == meta["artistsort"]: meta["artistsort"] = "" # finally, as musicbrainzngs returns str values if it's ascii, we force # everything to unicode now for key, value in iteritems(meta): meta[key] = text_type(value) return meta
def N_(message): """ Args: message (text_type) Returns: text_type Only marks a string for translation """ return text_type(message)
def __init__(self, name, library=None): super(Playlist, self).__init__() self.__inhibit_library_signals = False self.__instances.append(self) name = text_type(name) if not name: raise ValueError("Playlists must have a name") self.name = name self.library = library self._list = HashedList()
def setUp(self): config.init() self.library = SongLibrary() backend = quodlibet.player.init_backend("nullbe") self.device = backend.init(self.library) self.songs = [ AudioFile({"title": x}) for x in ["song1", "song2", "song3"] ] for song in self.songs: song.sanitize(fsnative(text_type(song["title"])))
def __init__(self, songs, real_keys_only=True): keys = {} first = {} all = {} total = len(songs) self.songs = songs self.is_file = True can_multi = True can_change = True for song in songs: self.is_file &= song.is_file if real_keys_only: iter_func = song.iterrealitems else: iter_func = song.items for comment, val in iter_func(): keys[comment] = keys.get(comment, 0) + 1 first.setdefault(comment, val) all[comment] = all.get(comment, True) and first[comment] == val song_can_multi = song.can_multiple_values() if song_can_multi is not True: if can_multi is True: can_multi = set(song_can_multi) else: can_multi.intersection_update(song_can_multi) song_can_change = song.can_change() if song_can_change is not True: if can_change is True: can_change = set(song_can_change) else: can_change.intersection_update(song_can_change) self._can_multi = can_multi self._can_change = can_change # collect comment representations for tag, count in iteritems(keys): first_value = first[tag] if not isinstance(first_value, string_types): first_value = text_type(first_value) shared = all[tag] complete = count == total if shared and complete: values = first_value.split("\n") else: values = [first_value] for v in values: self.setdefault(tag, []).append(Comment(v, count, total, shared))
def __init__(self, songs, real_keys_only=True): keys = {} first = {} all = {} total = len(songs) self.songs = songs self.is_file = True can_multi = True can_change = True for song in songs: self.is_file &= song.is_file if real_keys_only: iter_func = song.iterrealitems else: iter_func = song.iteritems for comment, val in iter_func(): keys[comment] = keys.get(comment, 0) + 1 first.setdefault(comment, val) all[comment] = all.get(comment, True) and first[comment] == val song_can_multi = song.can_multiple_values() if song_can_multi is not True: if can_multi is True: can_multi = set(song_can_multi) else: can_multi.intersection_update(song_can_multi) song_can_change = song.can_change() if song_can_change is not True: if can_change is True: can_change = set(song_can_change) else: can_change.intersection_update(song_can_change) self._can_multi = can_multi self._can_change = can_change # collect comment representations for tag, count in iteritems(keys): first_value = first[tag] if not isinstance(first_value, string_types): first_value = text_type(first_value) shared = all[tag] complete = count == total if shared and complete: values = first_value.split("\n") else: values = [first_value] for v in values: self.setdefault(tag, []).append( Comment(v, count, total, shared))
def test_lyrics_from_file(self): with temp_filename() as filename: af = AudioFile(artist='Motörhead', title='this: again') af.sanitize(filename) lyrics = "blah!\nblasé 😬\n" lyrics_dir = os.path.dirname(af.lyric_filename) mkdir(lyrics_dir) with io.open(af.lyric_filename, "w", encoding='utf-8') as lf: lf.write(text_type(lyrics)) self.failUnlessEqual( af("~lyrics").splitlines(), lyrics.splitlines()) os.remove(af.lyric_filename) os.rmdir(lyrics_dir)
def validate(self, value): value = value.strip() try: f = float(value) except (TypeError, ValueError): try: f = locale.atof(value) except (TypeError, ValueError): raise ValidationError else: if f < 0 or f >= 2: raise ValidationError return text_type(f)
def test_lyrics_from_file(self): with temp_filename() as filename: af = AudioFile(artist='Motörhead', title='this: again') af.sanitize(filename) lyrics = "blah!\nblasé 😬\n" lyrics_dir = os.path.dirname(af.lyric_filename) mkdir(lyrics_dir) with io.open(af.lyric_filename, "w", encoding='utf-8') as lf: lf.write(text_type(lyrics)) self.failUnlessEqual(af("~lyrics").splitlines(), lyrics.splitlines()) os.remove(af.lyric_filename) os.rmdir(lyrics_dir)
def run_error_dialogs(exc_info, sentry_error): error_text = u"%s: %s" % (exc_info[0].__name__, (text_type(exc_info[1]).strip() or u"\n").splitlines()[0]) error_text += u"\n------\n" error_text += u"\n".join(format_exception(*exc_info)) # Don't reshow the error dialog in case the user wanted to quit the app # but due to the error state more errors pile up.. if app.is_quitting: return window = find_active_window() if window is None: return # XXX: This does blocking IO and uses nested event loops... but it's simple dialog = ErrorDialog(window, error_text, show_bug_report=(sentry_error is None)) while 1: response = dialog.run() if response == ErrorDialog.RESPONSE_QUIT: dialog.destroy() app.quit() elif response == ErrorDialog.RESPONSE_SUBMIT: dialog.hide() submit_dialog = SubmitErrorDialog(window, sentry_error.get_report()) submit_response = submit_dialog.run() if submit_response == SubmitErrorDialog.RESPONSE_SUBMIT: sentry_error.set_comment(submit_dialog.get_comment()) timeout_seconds = 5 try: sentry_error.send(timeout_seconds) except SentryError: print_exc() submit_dialog.destroy() dialog.destroy() else: submit_dialog.destroy() dialog.show() continue elif response == ErrorDialog.RESPONSE_BUGREPORT: url = get_github_issue_url(exc_info) website(url) dialog.destroy() else: dialog.destroy() break
def escape_filename(s): """Escape a string in a manner suitable for a filename. Args: s (text_type) Returns: fsnative """ s = text_type(s) s = quote(s.encode("utf-8"), safe=b"") if isinstance(s, text_type): s = s.encode("ascii") return bytes2fsn(s, "utf-8")
def __preview_tracks(self, ctx, start, total, model, save, revert): start = start.get_value_as_int() total = total.get_value_as_int() for row in model: if total: s = u"%d/%d" % (row.path.get_indices()[0] + start, total) else: s = text_type(row.path.get_indices()[0] + start) entry = row[0] entry.tracknumber = s model.row_changed(row.path, row.iter) save.set_sensitive(True) revert.set_sensitive(True)
def __init__(self, row_pattern): parts = re.split(r"(?<!\\):", row_pattern) parts = list(map(lambda p: p.replace(r"\:", ":"), parts)) is_numeric = lambda s: s[:2] == "~#" and "~" not in s[2:] is_pattern = lambda s: '<' in s f_round = lambda s: (isinstance(s, float) and "%.2f" % s) or s disp = (len(parts) >= 2 and parts[1]) or r"[i](<~#tracks>)[/i]" cat = parts[0] if is_pattern(cat): title = util.pattern(cat, esc=True, markup=True) try: pc = XMLFromPattern(cat) except ValueError: pc = XMLFromPattern("") tags = pc.tags format = pc.format_list has_markup = True else: title = util.tag(cat) tags = util.tagsplit(cat) has_markup = False if is_numeric(cat): def format(song): v = text_type(f_round(song(cat))) return [(v, v)] else: format = lambda song: song.list_separate(cat) if is_pattern(disp): try: pd = XMLFromPattern(disp) except ValueError: pd = XMLFromPattern("") format_display = pd.format else: if is_numeric(disp): format_display = lambda coll: text_type(f_round(coll(disp))) else: format_display = lambda coll: util.escape(coll.comma(disp)) self.title = title self.tags = set(tags) self.format = format self.format_display = format_display self.has_markup = has_markup
def __init__(self, name, library=None): super(Playlist, self).__init__() self.__inhibit_library_signals = False self.__instances.append(self) name = text_type(name) if not name: raise ValueError("Playlists must have a name") # we require a file library here with masking assert library is None or hasattr(library, "masked") self.name = name self.library = library self._list = HashedList()