Beispiel #1
0
    def from_dump(self, text):
        """Parses the text created with to_dump and adds the found tags.

        Args:
            text (bytes)
        """

        for line in text.split(b"\n"):
            if not line:
                continue
            parts = line.split(b"=")
            key = decode(parts[0])
            val = b"=".join(parts[1:])
            if key == "~format":
                pass
            elif key in FILESYSTEM_TAGS:
                self.add(key, bytes2fsn(val, "utf-8"))
            elif key.startswith("~#"):
                try:
                    self.add(key, int(val))
                except ValueError:
                    try:
                        self.add(key, float(val))
                    except ValueError:
                        pass
            else:
                self.add(key, decode(val))
Beispiel #2
0
        def decode_key(key):
            """str if ascii, otherwise decode using utf-8"""

            if PY3:
                return decode(key)

            try:
                key.decode("ascii")
            except ValueError:
                return decode(key)
            return key
Beispiel #3
0
def parse_taglist(data):
    """Parses a dump file like list of tags and returns a list of IRFiles

    uri=http://...
    tag=value1
    tag2=value
    tag=value2
    uri=http://...
    ...

    """

    stations = []
    station = None

    for l in data.split(b"\n"):
        if not l:
            continue
        key = l.split(b"=")[0]
        value = l.split(b"=", 1)[1]
        key = decode(key)
        value = decode(value)
        if key == "uri":
            if station:
                stations.append(station)
            station = IRFile(value)
            continue

        san = list(sanitize_tags({key: value}, stream=True).items())
        if not san:
            continue

        key, value = san[0]
        if key == "~listenerpeak":
            key = "~#listenerpeak"
            value = int(value)

        if not station:
            continue

        if isinstance(value, text_type):
            if value not in station.list(key):
                station.add(key, value)
        else:
            station[key] = value

    if station:
        stations.append(station)

    return stations
Beispiel #4
0
 def decode_key(key):
     """str if ascii, otherwise decode using utf-8"""
     try:
         key.decode("ascii")
     except ValueError:
         return decode(key)
     return key
Beispiel #5
0
    def from_dump(self, text):
        """Parses the text created with to_dump and adds the found tags."""
        def decode_key(key):
            """str if ascii, otherwise decode using utf-8"""
            try:
                key.decode("ascii")
            except ValueError:
                return decode(key)
            return key

        for line in text.split("\n"):
            if not line:
                continue
            parts = line.split("=")
            key = parts[0]
            val = "=".join(parts[1:])
            if key == "~format":
                pass
            elif key.startswith("~#"):
                try:
                    self.add(key, int(val))
                except ValueError:
                    try:
                        self.add(key, float(val))
                    except ValueError:
                        pass
            else:
                self.add(decode_key(key), decode(val))
Beispiel #6
0
    def __init__(self, songs):
        keys = {}
        first = {}
        all = {}
        total = len(songs)
        self.songs = songs

        for song in songs:
            self.is_file &= song.is_file
            self.multiple_values &= song.multiple_values
            for comment, val in song.iteritems():
                keys[comment] = keys.get(comment, 0) + 1
                first.setdefault(comment, val)
                all[comment] = all.get(comment, True) and first[comment] == val

        # collect comment representations
        for comment, count in keys.iteritems():
            if count < total:
                if all[comment]:
                    value = self.PartialSharedComment(first[comment])
                else:
                    value = self.PartialUnsharedComment(first[comment])
            else:
                decoded = first[comment]
                if isinstance(decoded, str):
                    decoded = decode(decoded)
                if all[comment]:
                    value = self.SharedComment(decoded)
                else:
                    value = self.UnsharedComment(decoded)
            value.have = count
            value.total = total
            value.missing = total - count

            self[comment] = value
Beispiel #7
0
    def from_dump(self, text):
        """Parses the text created with to_dump and adds the found tags."""

        def decode_key(key):
            """str if ascii, otherwise decode using utf-8"""
            try:
                key.decode("ascii")
            except ValueError:
                return decode(key)
            return key

        for line in text.split("\n"):
            if not line:
                continue
            parts = line.split("=")
            key = parts[0]
            val = "=".join(parts[1:])
            if key == "~format":
                pass
            elif key.startswith("~#"):
                try:
                    self.add(key, int(val))
                except ValueError:
                    try:
                        self.add(key, float(val))
                    except ValueError:
                        pass
            else:
                self.add(decode_key(key), decode(val))
Beispiel #8
0
    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)
Beispiel #9
0
    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] = str(cur)
            elif key in self.__translate:
                name = self.__translate[key]
                if key == "tmpo":
                    self[name] = u"\n".join(map(str, 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)
Beispiel #10
0
 def __init__(self, filename):
     audio = MP4(filename)
     self["~format"] = "%s %s" % (
         self.format, getattr(audio.info, "codec_description", "AAC"))
     self["~#length"] = audio.info.length
     self["~#bitrate"] = int(audio.info.bitrate / 1000)
     for key, values in audio.items():
         if key in self.__tupletranslate:
             name = self.__tupletranslate[key]
             cur, total = values[0]
             if total:
                 self[name] = u"%d/%d" % (cur, total)
             else:
                 self[name] = unicode(cur)
         elif key in self.__translate:
             name = self.__translate[key]
             if key == "tmpo":
                 self[name] = "\n".join(map(unicode, 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)
Beispiel #11
0
    def __init__(self, filename):
        audio = MP4(filename)
        self["~codec"] = getattr(audio.info, "codec_description", u"AAC")
        self["~#length"] = audio.info.length
        self["~#bitrate"] = int(audio.info.bitrate / 1000)

        for key, values in audio.items():
            if key in self.__tupletranslate:
                name = self.__tupletranslate[key]
                cur, total = values[0]
                if total:
                    self[name] = u"%d/%d" % (cur, total)
                else:
                    self[name] = unicode(cur)
            elif key in self.__translate:
                name = self.__translate[key]
                if key == "tmpo":
                    self[name] = "\n".join(map(unicode, 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)
Beispiel #12
0
    def __init__(self, track):
        super(IPodSong, self).__init__()
        self.sanitize(gpod.itdb_filename_on_ipod(track))

        # String keys
        for key in [
                'artist', 'album', 'title', 'genre', 'grouping', 'composer',
                'albumartist'
        ]:
            # albumartist since libgpod-0.4.2
            value = getattr(track, key, None)
            if value:
                self[key] = decode(value)
        # Sort keys (since libgpod-0.5.0)
        for key in ['artist', 'album', 'albumartist']:
            value = getattr(track, 'sort_' + key, None)
            if value:
                self[key + 'sort'] = decode(value)
        # Numeric keys
        for key in ['bitrate', 'playcount']:
            value = getattr(track, key)
            if value:
                self['~#' + key] = value

        try:
            self["date"] = unicode(track.year)
        except AttributeError:
            pass

        if track.cds:
            self["discnumber"] = u"%d/%d" % (track.cd_nr, track.cds)
        elif track.cd_nr:
            self["discnumber"] = u"%d" % track.cd_nr

        if track.tracks:
            self['tracknumber'] = u"%d/%d" % (track.track_nr, track.tracks)
        elif track.track_nr:
            self['tracknumber'] = u"%d" % track.track_nr

        for key, value in {
                '~#rating': min(1.0, track.rating / 100.0),
                '~#length': track.tracklen / 1000.0,
        }.items():
            if value != 0:
                self[key] = value
        self['~format'] = u"iPod: %s" % track.filetype
Beispiel #13
0
def parse_gstreamer_taglist(tags):
    """Takes a GStreamer taglist and returns a dict containing only
    numeric and unicode values and str keys."""

    merged = {}
    for key in tags.keys():
        value = tags[key]
        # extended-comment sometimes containes a single vorbiscomment or
        # a list of them ["key=value", "key=value"]
        if key == "extended-comment":
            if not isinstance(value, list):
                value = [value]
            for val in value:
                if not isinstance(val, unicode):
                    continue
                split = val.split("=", 1)
                sub_key = decode(split[0])
                val = split[-1]
                if sub_key in merged:
                    sub_val = merged[sub_key]
                    if not isinstance(sub_val, unicode):
                        continue
                    if val not in sub_val.split("\n"):
                        merged[sub_key] += "\n" + val
                else:
                    merged[sub_key] = val
        elif isinstance(value, Gst.DateTime):
            value = value.to_iso8601_string()
            merged[key] = value
        else:
            if isinstance(value, (int, long, float)):
                merged[key] = value
                continue

            if isinstance(value, str):
                value = decode(value)

            if not isinstance(value, unicode):
                value = unicode(value)

            if key in merged:
                merged[key] += "\n" + value
            else:
                merged[key] = value

    return merged
Beispiel #14
0
def fsdecode(s, note=True):
    """Decoding a string according to the filesystem encoding.
    note specifies whether a note should be appended if decoding failed."""
    if isinstance(s, unicode):
        return s
    elif note:
        return decode(s, fscoding)
    else:
        return s.decode(fscoding, 'replace')
Beispiel #15
0
    def __init__(self, track):
        super(IPodSong, self).__init__()
        self.sanitize(gpod.itdb_filename_on_ipod(track))

        # String keys
        for key in ['artist', 'album', 'title', 'genre', 'grouping',
            'composer', 'albumartist']:
            # albumartist since libgpod-0.4.2
            value = getattr(track, key, None)
            if value:
                self[key] = decode(value)
        # Sort keys (since libgpod-0.5.0)
        for key in ['artist', 'album', 'albumartist']:
            value = getattr(track, 'sort_' + key, None)
            if value:
                self[key + 'sort'] = decode(value)
        # Numeric keys
        for key in ['bitrate', 'playcount']:
            value = getattr(track, key)
            if value:
                self['~#' + key] = value

        try:
            self["date"] = unicode(track.year)
        except AttributeError:
            pass

        if track.cds:
            self["discnumber"] = u"%d/%d" % (track.cd_nr, track.cds)
        elif track.cd_nr:
            self["discnumber"] = u"%d" % track.cd_nr

        if track.tracks:
            self['tracknumber'] = u"%d/%d" % (track.track_nr, track.tracks)
        elif track.track_nr:
            self['tracknumber'] = u"%d" % track.track_nr

        for key, value in {
            '~#rating': min(1.0, track.rating / 100.0),
            '~#length': track.tracklen / 1000.0,
        }.items():
            if value != 0:
                self[key] = value
        self['~format'] = u"iPod: %s" % track.filetype
Beispiel #16
0
    def list_input_plugins(self):
        """
        Returns:
            List[str]
        """

        plugins = []
        for plugin in xine_list_input_plugins(self._xine):
            if not plugin:
                break
            plugins.append(decode(plugin))
        return plugins
Beispiel #17
0
def fsdecode(s, note=True):
    """Takes a native path and returns unicode for displaying it.

    Can not fail and can't be reversed.
    """

    if isinstance(s, unicode):
        return s
    elif note:
        return decode(s, _FSCODING)
    else:
        return s.decode(_FSCODING, 'replace')
Beispiel #18
0
 def __error(self, player, song, error, librarian):
     newstr = u"%s: %s\n\n" % (
         decode(time.asctime(), const.ENCODING), error)
     self.__errors_in_a_row += 1
     if self.__errors_in_a_row > MAX_ERRORS:
         self.__errors_in_a_row = 0
         ErrorMessage(None, _("Too Many Errors"),
                      _("Stopping playback because there were %d errors "
                        "in a row.") % MAX_ERRORS).run()
         player.go_to(None)
         player.paused = True
     song["~errors"] = newstr + song.get("~errors", "")
Beispiel #19
0
    def list_input_plugins(self):
        """
        Returns:
            List[str]
        """

        plugins = []
        for plugin in xine_list_input_plugins(self._xine):
            if not plugin:
                break
            plugins.append(decode(plugin))
        return plugins
Beispiel #20
0
    def __init__(self, track):
        super(IPodSong, self).__init__()
        self.sanitize(gpod.itdb_filename_on_ipod(track))

        # String keys
        for key in ["artist", "album", "title", "genre", "grouping", "composer", "albumartist"]:
            # albumartist since libgpod-0.4.2
            value = getattr(track, key, None)
            if value:
                self[key] = decode(value)
        # Sort keys (since libgpod-0.5.0)
        for key in ["artist", "album", "albumartist"]:
            value = getattr(track, "sort_" + key, None)
            if value:
                self[key + "sort"] = decode(value)
        # Numeric keys
        for key in ["bitrate", "playcount"]:
            value = getattr(track, key)
            if value:
                self["~#" + key] = value

        try:
            self["date"] = unicode(track.year)
        except AttributeError:
            pass

        if track.cds:
            self["discnumber"] = u"%d/%d" % (track.cd_nr, track.cds)
        elif track.cd_nr:
            self["discnumber"] = u"%d" % track.cd_nr

        if track.tracks:
            self["tracknumber"] = u"%d/%d" % (track.track_nr, track.tracks)
        elif track.track_nr:
            self["tracknumber"] = u"%d" % track.track_nr

        for key, value in {"~#rating": min(1.0, track.rating / 100.0), "~#length": track.tracklen / 1000.0}.items():
            if value != 0:
                self[key] = value
        self["~format"] = u"iPod: %s" % track.filetype
Beispiel #21
0
    def __init__(self, driver, librarian):
        """May raise PlayerError"""

        super().__init__()
        self.name = "xine"
        self.version_info = "xine-lib: " + decode(xine_get_version_string())
        self._volume = 1.0
        self._handle = XineHandle()
        self._supports_gapless = xine_check_version(1, 1, 1) == 1
        self._event_queue = None

        self._new_stream(driver)
        self._librarian = librarian
        self._destroyed = False
Beispiel #22
0
    def __init__(self, driver, librarian):
        """May raise PlayerError"""

        super(XinePlaylistPlayer, self).__init__()
        self.name = "xine"
        self.version_info = "xine-lib: " + decode(xine_get_version_string())
        self._volume = 1.0
        self._handle = XineHandle()
        self._supports_gapless = xine_check_version(1, 1, 1) == 1
        self._event_queue = None

        self._new_stream(driver)
        self._librarian = librarian
        self._destroyed = False
Beispiel #23
0
    def __init__(self, driver, librarian):
        """May raise PlayerError"""

        super(XinePlaylistPlayer, self).__init__()
        self.name = "xine"
        self.version_info = "xine-lib: " + decode(xine_get_version_string())
        self._volume = 1.0
        self._handle = XineHandle()
        self._supports_gapless = xine_check_version(1, 1, 1) == 1
        self._event_queue = None

        if not isinstance(driver, bytes):
            driver = driver.encode("utf-8")

        self._new_stream(driver)
        self._librarian = librarian
        self._destroyed = False
Beispiel #24
0
def parse_taglist(data):
    """Parses a dump file like list of tags and returns a list of IRFiles

    uri=http://...
    tag=value1
    tag2=value
    tag=value2
    uri=http://...
    ...

    """

    stations = []
    station = None

    for l in data.split("\n"):
        key = l.split("=")[0]
        value = l.split("=", 1)[1]
        if key == "uri":
            if station:
                stations.append(station)
            station = IRFile(value)
            continue

        value = decode(value)
        san = sanitize_tags({key: value}, stream=True).items()
        if not san:
            continue

        key, value = san[0]
        if key == "~listenerpeak":
            key = "~#listenerpeak"
            value = int(value)

        if isinstance(value, str):
            value = value.decode("utf-8")
            if value not in station.list(key):
                station.add(key, value)
        else:
            station[key] = value

    if station:
        stations.append(station)

    return stations
def _config(section, option, label, tooltip, getter):
    def on_changed(entry, *args):
        config.set(section, option, entry.get_text())

    entry = UndoEntry()
    entry.set_tooltip_text(tooltip)
    entry.set_text(decode(config.get(section, option)))
    entry.connect("changed", on_changed)

    def on_reverted(*args):
        config.reset(section, option)
        entry.set_text(decode(config.get(section, option)))

    revert = Gtk.Button()
    revert.add(Gtk.Image.new_from_icon_name(Icons.DOCUMENT_REVERT, Gtk.IconSize.BUTTON))
    revert.connect("clicked", on_reverted)

    return (Gtk.Label(label=label), entry, revert)
Beispiel #26
0
def _config(section, option, label, tooltip, getter):
    def on_changed(entry, *args):
        config.set(section, option, entry.get_text())

    entry = UndoEntry()
    entry.set_tooltip_text(tooltip)
    entry.set_text(decode(config.get(section, option)))
    entry.connect("changed", on_changed)

    def on_reverted(*args):
        config.reset(section, option)
        entry.set_text(decode(config.get(section, option)))

    revert = Gtk.Button()
    revert.add(Gtk.Image.new_from_icon_name(
        Icons.DOCUMENT_REVERT, Gtk.IconSize.BUTTON))
    revert.connect("clicked", on_reverted)

    return (Gtk.Label(label=label), entry, revert)
Beispiel #27
0
    def from_dump(self, text):
        """Parses the text created with to_dump and adds the found tags.

        Args:
            text (bytes)
        """

        def decode_key(key):
            """str if ascii, otherwise decode using utf-8"""

            if PY3:
                return decode(key)

            try:
                key.decode("ascii")
            except ValueError:
                return decode(key)
            return key

        for line in text.split(b"\n"):
            if not line:
                continue
            parts = line.split(b"=")
            key = decode_key(parts[0])
            val = b"=".join(parts[1:])
            if key == "~format":
                pass
            elif key in FILESYSTEM_TAGS:
                self.add(key, bytes2fsn(val, "utf-8"))
            elif key.startswith("~#"):
                try:
                    self.add(key, int(val))
                except ValueError:
                    try:
                        self.add(key, float(val))
                    except ValueError:
                        pass
            else:
                self.add(key, decode(val))
Beispiel #28
0
    def from_dump(self, text):
        """Parses the text created with to_dump and adds the found tags.

        Args:
            text (bytes)
        """

        def decode_key(key):
            """str if ascii, otherwise decode using utf-8"""

            if PY3:
                return decode(key)

            try:
                key.decode("ascii")
            except ValueError:
                return decode(key)
            return key

        for line in text.split(b"\n"):
            if not line:
                continue
            parts = line.split(b"=")
            key = decode_key(parts[0])
            val = b"=".join(parts[1:])
            if key == "~format":
                pass
            elif key in FILESYSTEM_TAGS:
                self.add(key, bytes2fsn(val, "utf-8"))
            elif key.startswith("~#"):
                try:
                    self.add(key, int(val))
                except ValueError:
                    try:
                        self.add(key, float(val))
                    except ValueError:
                        pass
            else:
                self.add(key, decode(val))
Beispiel #29
0
 def test_invalid(self):
     self.failUnlessEqual(decode("fo\xde"), u'fo\ufffd [Invalid Encoding]')
Beispiel #30
0
 def test_invalid(self):
     self.failUnlessEqual(
         decode("fo\xde"), u'fo\ufffd [Invalid Encoding]')
Beispiel #31
0
 def test_empty(self):
     self.failUnlessEqual(decode(""), "")
Beispiel #32
0
 def test_safe(self):
     self.failUnlessEqual(decode("foo!"), "foo!")
Beispiel #33
0
def main(argv):
    import quodlibet

    quodlibet.init_cli()

    try:
        # we want basic commands not to import gtk (doubles process time)
        assert "gi.repository.Gtk" not in sys.modules
        sys.modules["gi.repository.Gtk"] = None
        startup_actions, cmds_todo = process_arguments(argv)
    finally:
        sys.modules.pop("gi.repository.Gtk", None)

    quodlibet.init()

    from quodlibet import app
    from quodlibet.qltk import add_signal_watch, Icons
    add_signal_watch(app.quit)

    import quodlibet.player
    import quodlibet.library
    from quodlibet import config
    from quodlibet import browsers
    from quodlibet import util
    from quodlibet.util.string import decode

    app.name = "Quod Libet"
    app.id = "quodlibet"
    quodlibet.set_application_info(Icons.QUODLIBET, app.id, app.name)

    config.init(os.path.join(quodlibet.get_user_dir(), "config"))

    library_path = os.path.join(quodlibet.get_user_dir(), "songs")

    print_d("Initializing main library (%s)" % (
            quodlibet.util.path.unexpand(library_path)))

    library = quodlibet.library.init(library_path)
    app.library = library

    # this assumes that nullbe will always succeed
    from quodlibet.player import PlayerError
    wanted_backend = os.environ.get(
        "QUODLIBET_BACKEND", config.get("player", "backend"))
    backend_traceback = None
    for backend in [wanted_backend, "nullbe"]:
        try:
            player = quodlibet.player.init_player(backend, app.librarian)
        except PlayerError:
            backend_traceback = decode(traceback.format_exc())
        else:
            break
    app.player = player

    os.environ["PULSE_PROP_media.role"] = "music"
    os.environ["PULSE_PROP_application.icon_name"] = "quodlibet"

    browsers.init()

    from quodlibet.qltk.songlist import SongList, get_columns

    from quodlibet.util.collection import Album
    try:
        cover_size = config.getint("browsers", "cover_size")
    except config.Error:
        pass
    else:
        if cover_size > 0:
            Album.COVER_SIZE = cover_size

    headers = get_columns()
    SongList.set_all_column_headers(headers)

    for opt in config.options("header_maps"):
        val = config.get("header_maps", opt)
        util.tags.add(opt, val)

    in_all = ("~filename ~uri ~#lastplayed ~#rating ~#playcount ~#skipcount "
              "~#added ~#bitrate ~current ~#laststarted ~basename "
              "~dirname").split()
    for Kind in browsers.browsers:
        if Kind.headers is not None:
            Kind.headers.extend(in_all)
        Kind.init(library)

    pm = quodlibet.init_plugins("no-plugins" in startup_actions)

    if hasattr(player, "init_plugins"):
        player.init_plugins()

    from quodlibet.qltk import unity
    unity.init("quodlibet.desktop", player)

    from quodlibet.qltk.songsmenu import SongsMenu
    SongsMenu.init_plugins()

    from quodlibet.util.cover import CoverManager
    app.cover_manager = CoverManager()
    app.cover_manager.init_plugins()

    from quodlibet.plugins.playlist import PLAYLIST_HANDLER
    PLAYLIST_HANDLER.init_plugins()

    from gi.repository import GLib

    def exec_commands(*args):
        for cmd in cmds_todo:
            try:
                resp = cmd_registry.run(app, *cmd)
            except CommandError:
                pass
            else:
                if resp is not None:
                    print_(resp, end="")

    from quodlibet.qltk.quodlibetwindow import QuodLibetWindow, PlayerOptions
    # Call exec_commands after the window is restored, but make sure
    # it's after the mainloop has started so everything is set up.
    app.window = window = QuodLibetWindow(
        library, player,
        restore_cb=lambda:
            GLib.idle_add(exec_commands, priority=GLib.PRIORITY_HIGH))

    app.player_options = PlayerOptions(window)

    from quodlibet.qltk.debugwindow import MinExceptionDialog
    from quodlibet.qltk.window import on_first_map
    if backend_traceback is not None:
        def show_backend_error(window):
            d = MinExceptionDialog(window,
                _("Audio Backend Failed to Load"),
                _("Loading the audio backend '%(name)s' failed. "
                  "Audio playback will be disabled.") %
                {"name": wanted_backend},
                backend_traceback)
            d.run()

        # so we show the main window first
        on_first_map(app.window, show_backend_error, app.window)

    from quodlibet.plugins.events import EventPluginHandler
    pm.register_handler(EventPluginHandler(library.librarian, player))

    from quodlibet.mmkeys import MMKeysHandler
    from quodlibet.remote import Remote, RemoteError
    from quodlibet.commands import registry as cmd_registry, CommandError
    from quodlibet.qltk.tracker import SongTracker, FSInterface
    try:
        from quodlibet.qltk.dbus_ import DBusHandler
    except ImportError:
        DBusHandler = lambda player, library: None

    mmkeys_handler = MMKeysHandler(app.name, window, player)
    if "QUODLIBET_NO_MMKEYS" not in os.environ:
        mmkeys_handler.start()
    current_path = os.path.join(quodlibet.get_user_dir(), "current")
    fsiface = FSInterface(current_path, player)
    remote = Remote(app, cmd_registry)
    try:
        remote.start()
    except RemoteError:
        exit_(1, True)

    DBusHandler(player, library)
    tracker = SongTracker(library.librarian, player, window.playlist)

    from quodlibet.qltk import session
    session.init("quodlibet")

    quodlibet.enable_periodic_save(save_library=True)

    if "start-playing" in startup_actions:
        player.paused = False

    # restore browser windows
    from quodlibet.qltk.browser import LibraryBrowser
    GLib.idle_add(LibraryBrowser.restore, library, player,
                  priority=GLib.PRIORITY_HIGH)

    def before_quit():
        print_d("Saving active browser state")
        try:
            app.browser.save()
        except NotImplementedError:
            pass

        print_d("Shutting down player device %r." % player.version_info)
        player.destroy()

    quodlibet.main(window, before_quit=before_quit)

    app.player_options.destroy()
    quodlibet.finish_first_session(app.id)
    mmkeys_handler.quit()
    remote.stop()
    fsiface.destroy()

    tracker.destroy()
    quodlibet.library.save()

    config.save()

    print_d("Finished shutdown.")
 def on_reverted(*args):
     config.reset(section, option)
     entry.set_text(decode(config.get(section, option)))
Beispiel #35
0
 def getter(section, option):
     return decode(config.get(section, option))
Beispiel #36
0
    def __save_files(self, save, revert, model, library):
        updated = {}
        deleted = {}
        added = {}
        renamed = {}
        for row in model:
            if row[EDITED] and not (row[DELETED] or row[RENAMED]):
                if row[ORIGVALUE] is not None:
                    updated.setdefault(row[TAG], [])
                    updated[row[TAG]].append(
                        (decode(row[VALUE]), decode(row[ORIGVALUE])))
                else:
                    added.setdefault(row[TAG], [])
                    added[row[TAG]].append(decode(row[VALUE]))
            if row[EDITED] and row[DELETED]:
                if row[ORIGVALUE] is not None:
                    deleted.setdefault(row[TAG], [])
                    deleted[row[TAG]].append(decode(row[ORIGVALUE]))

            if row[EDITED] and row[RENAMED] and not row[DELETED]:
                renamed.setdefault(row[TAG], [])
                renamed[row[TAG]].append(
                    (decode(row[ORIGTAG]), decode(row[VALUE]),
                     decode(row[ORIGVALUE])))

        was_changed = set()
        songs = self.__songinfo.songs
        win = WritingWindow(self, len(songs))
        win.show()
        for song in songs:
            if not song.valid() and not qltk.ConfirmAction(
                    self, _("Tag may not be accurate"),
                    _("<b>%s</b> changed while the program was running. "
                      "Saving without refreshing your library may "
                      "overwrite other changes to the song.\n\n"
                      "Save this song anyway?") %
                    util.escape(util.fsdecode(song("~basename")))).run():
                break

            changed = False
            for key, values in updated.iteritems():
                for (new_value, old_value) in values:
                    new_value = util.unescape(new_value)
                    if song.can_change(key):
                        if old_value is None:
                            song.add(key, new_value)
                        else:
                            song.change(key, old_value, new_value)
                        changed = True
            for key, values in added.iteritems():
                for value in values:
                    value = util.unescape(value)
                    if song.can_change(key):
                        song.add(key, value)
                        changed = True
            for key, values in deleted.iteritems():
                for value in values:
                    value = util.unescape(value)
                    if song.can_change(key) and key in song:
                        song.remove(key, value)
                        changed = True
            save_rename = []
            for new_tag, values in renamed.iteritems():
                for old_tag, new_value, old_value in values:
                    old_tag = util.unescape(old_tag)
                    old_value = util.unescape(old_value)
                    new_value = util.unescape(new_value)
                    if (song.can_change(new_tag) and song.can_change(old_tag)
                            and old_tag in song):
                        if not is_special(new_value):
                            song.remove(old_tag, old_value)
                            save_rename.append((new_tag, new_value))
                        elif is_missing(new_value):
                            value = strip_missing(old_value)
                            song.remove(old_tag, old_value)
                            save_rename.append((new_tag, new_value))
                        else:
                            save_rename.append((new_tag, song[old_tag]))
                            song.remove(old_tag, None)
                        changed = True
            for tag, value in save_rename:
                song.add(tag, value)

            if changed:
                try:
                    song.write()
                except:
                    util.print_exc()
                    qltk.ErrorMessage(
                        self, _("Unable to save song"),
                        _("Saving <b>%s</b> failed. The file "
                          "may be read-only, corrupted, or you "
                          "do not have permission to edit it.") %
                        (util.escape(fsdecode(song('~basename'))))).run()
                    library.reload(song, changed=was_changed)
                    break
                was_changed.add(song)

            if win.step():
                break

        win.destroy()
        library.changed(was_changed)
        for b in [save, revert]:
            b.set_sensitive(False)
Beispiel #37
0
 def test_empty(self):
     self.failUnlessEqual(decode(""), "")
Beispiel #38
0
 def test_safe(self):
     self.failUnlessEqual(decode("foo!"), "foo!")
 def on_reverted(*args):
     config.reset(section, option)
     entry.set_text(decode(config.get(section, option)))
Beispiel #40
0
 def getter(section, option):
     return decode(config.get(section, option))
Beispiel #41
0
def main(argv):
    import quodlibet

    quodlibet.init_cli()

    try:
        # we want basic commands not to import gtk (doubles process time)
        assert "gi.repository.Gtk" not in sys.modules
        sys.modules["gi.repository.Gtk"] = None
        startup_actions, cmds_todo = process_arguments(argv)
    finally:
        sys.modules.pop("gi.repository.Gtk", None)

    quodlibet.init()

    from quodlibet import app
    from quodlibet.qltk import add_signal_watch, Icons
    add_signal_watch(app.quit)

    import quodlibet.player
    import quodlibet.library
    from quodlibet import config
    from quodlibet import browsers
    from quodlibet import util
    from quodlibet.util.string import decode

    app.name = "Quod Libet"
    app.id = "quodlibet"
    quodlibet.set_application_info(Icons.QUODLIBET, app.id, app.name)

    config.init(os.path.join(quodlibet.get_user_dir(), "config"))

    library_path = os.path.join(quodlibet.get_user_dir(), "songs")

    print_d("Initializing main library (%s)" %
            (quodlibet.util.path.unexpand(library_path)))

    library = quodlibet.library.init(library_path)
    app.library = library

    # this assumes that nullbe will always succeed
    from quodlibet.player import PlayerError
    wanted_backend = os.environ.get("QUODLIBET_BACKEND",
                                    config.get("player", "backend"))
    backend_traceback = None
    for backend in [wanted_backend, "nullbe"]:
        try:
            player = quodlibet.player.init_player(backend, app.librarian)
        except PlayerError:
            backend_traceback = decode(traceback.format_exc())
        else:
            break
    app.player = player

    os.environ["PULSE_PROP_media.role"] = "music"
    os.environ["PULSE_PROP_application.icon_name"] = "quodlibet"

    browsers.init()

    from quodlibet.qltk.songlist import SongList, get_columns

    from quodlibet.util.collection import Album
    try:
        cover_size = config.getint("browsers", "cover_size")
    except config.Error:
        pass
    else:
        if cover_size > 0:
            Album.COVER_SIZE = cover_size

    headers = get_columns()
    SongList.set_all_column_headers(headers)

    for opt in config.options("header_maps"):
        val = config.get("header_maps", opt)
        util.tags.add(opt, val)

    in_all = ("~filename ~uri ~#lastplayed ~#rating ~#playcount ~#skipcount "
              "~#added ~#bitrate ~current ~#laststarted ~basename "
              "~dirname").split()
    for Kind in browsers.browsers:
        if Kind.headers is not None:
            Kind.headers.extend(in_all)
        Kind.init(library)

    pm = quodlibet.init_plugins("no-plugins" in startup_actions)

    if hasattr(player, "init_plugins"):
        player.init_plugins()

    from quodlibet.qltk import unity
    unity.init("quodlibet.desktop", player)

    from quodlibet.qltk.songsmenu import SongsMenu
    SongsMenu.init_plugins()

    from quodlibet.util.cover import CoverManager
    app.cover_manager = CoverManager()
    app.cover_manager.init_plugins()

    from quodlibet.plugins.playlist import PLAYLIST_HANDLER
    PLAYLIST_HANDLER.init_plugins()

    from quodlibet.plugins.query import QUERY_HANDLER
    QUERY_HANDLER.init_plugins()

    from gi.repository import GLib

    def exec_commands(*args):
        for cmd in cmds_todo:
            try:
                resp = cmd_registry.run(app, *cmd)
            except CommandError:
                pass
            else:
                if resp is not None:
                    print_(resp, end="")

    from quodlibet.qltk.quodlibetwindow import QuodLibetWindow, PlayerOptions
    # Call exec_commands after the window is restored, but make sure
    # it's after the mainloop has started so everything is set up.
    app.window = window = QuodLibetWindow(
        library,
        player,
        restore_cb=lambda: GLib.idle_add(exec_commands,
                                         priority=GLib.PRIORITY_HIGH))

    app.player_options = PlayerOptions(window)

    from quodlibet.qltk.debugwindow import MinExceptionDialog
    from quodlibet.qltk.window import on_first_map
    if backend_traceback is not None:

        def show_backend_error(window):
            d = MinExceptionDialog(
                window, _("Audio Backend Failed to Load"),
                _("Loading the audio backend '%(name)s' failed. "
                  "Audio playback will be disabled.") %
                {"name": wanted_backend}, backend_traceback)
            d.run()

        # so we show the main window first
        on_first_map(app.window, show_backend_error, app.window)

    from quodlibet.plugins.events import EventPluginHandler
    pm.register_handler(EventPluginHandler(library.librarian, player))

    from quodlibet.mmkeys import MMKeysHandler
    from quodlibet.remote import Remote, RemoteError
    from quodlibet.commands import registry as cmd_registry, CommandError
    from quodlibet.qltk.tracker import SongTracker, FSInterface
    try:
        from quodlibet.qltk.dbus_ import DBusHandler
    except ImportError:
        DBusHandler = lambda player, library: None

    mmkeys_handler = MMKeysHandler(app)
    mmkeys_handler.start()

    current_path = os.path.join(quodlibet.get_user_dir(), "current")
    fsiface = FSInterface(current_path, player)
    remote = Remote(app, cmd_registry)
    try:
        remote.start()
    except RemoteError:
        exit_(1, True)

    DBusHandler(player, library)
    tracker = SongTracker(library.librarian, player, window.playlist)

    from quodlibet.qltk import session
    session.init("quodlibet")

    quodlibet.enable_periodic_save(save_library=True)

    if "start-playing" in startup_actions:
        player.paused = False

    # restore browser windows
    from quodlibet.qltk.browser import LibraryBrowser
    GLib.idle_add(LibraryBrowser.restore,
                  library,
                  player,
                  priority=GLib.PRIORITY_HIGH)

    def before_quit():
        print_d("Saving active browser state")
        try:
            app.browser.save()
        except NotImplementedError:
            pass

        print_d("Shutting down player device %r." % player.version_info)
        player.destroy()

    quodlibet.main(window, before_quit=before_quit)

    app.player_options.destroy()
    quodlibet.finish_first_session(app.id)
    mmkeys_handler.quit()
    remote.stop()
    fsiface.destroy()

    tracker.destroy()
    quodlibet.library.save()

    config.save()

    print_d("Finished shutdown.")