예제 #1
0
 def _downloaded(self, msg: Soup.Message, result: Any, data: Tuple) -> None:
     path, song = data
     try:
         headers = msg.get_property('response-headers')
         size = int(headers.get('content-length'))
         content_type = headers.get('content-type')
         print_d(
             f"Downloaded {format_size(size)} of {content_type}: {song('title')}"
         )
         _, ext = splitext(urlparse(song("~uri")).path)
         fn = (escape_filename(song("~artist~title")[:100], safe=b" ,';")
               or song("~basename")
               or f"download-{hash(song('~filename'))}")
         path = path / Path(fn + ext)
         if path.is_file() and path.stat():
             print_w(f"{path!s} already exists. Skipping download")
             self.success(song)
             return
         with open(path, "wb") as f:
             f.write(result)
         self.success(song)
         print_d(f"Downloaded to {path} successfully!")
     except Exception as e:
         print_e(f"Failed download ({e})")
         self.failure(song)
예제 #2
0
    def _finished(self, manager, results):
        if not any(results.values()):
            print_w("Nothing found from %d provider(s)" % len(self._groups))

            self.button.set_sensitive(False)
            if not self.headless:
                self._quit(results)
예제 #3
0
 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:
                         subs[k] = quote_plus(unicode(vals).encode('utf-8'))
                     # 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)
예제 #4
0
 def parse_body(self, body, query_path_=None):
     if body is None:
         raise QueryPluginError
     body = body.strip().lower()
     # Use provided query file for testing
     if query_path_:
         query_path = query_path_
     else:
         query_path = os.path.join(get_user_dir(), 'lists', 'queries.saved')
     try:
         with open(query_path, 'r', encoding="utf-8") as query_file:
             for query_string in query_file:
                 name = next(query_file).strip().lower()
                 if name == body:
                     try:
                         return Query(query_string.strip())
                     except QueryError:
                         raise QueryPluginError
         # We've searched the whole file and haven't found a match
         print_w(f"None found for {body}")
         raise QueryPluginError
     except IOError:
         raise QueryPluginError
     except StopIteration:
         # The file has an odd number of lines. This shouldn't happen unless
         # it has been externally modified
         raise QueryPluginError
예제 #5
0
 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:
                         subs[k] = quote_plus(unicode(vals).encode('utf-8'))
                     # 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)
예제 #6
0
    def _on_bus_message(self, bus, message):
        if message.type == Gst.MessageType.ERROR:
            error, debug = message.parse_error()
            print_d("Error received from element {name}: {error}".format(
                name=message.src.get_name(), error=error))
            print_d("Debugging information: {}".format(debug))
        elif message.type == Gst.MessageType.ELEMENT:
            structure = message.get_structure()
            if structure.get_name() == "level":
                rms_db = structure.get_value("rms")
                if rms_db:
                    # Calculate average of all channels (usually 2)
                    rms_db_avg = sum(rms_db) / len(rms_db)
                    # Normalize dB value to value between 0 and 1
                    rms = pow(10, (rms_db_avg / 20))
                    self._new_rms_vals.append(rms)
            else:
                print_w("Got unexpected message of type {}"
                        .format(message.type))
        elif message.type == Gst.MessageType.EOS:
            self._clean_pipeline()

            # Update the waveform with the new data
            self._rms_vals = self._new_rms_vals
            self._waveform_scale.reset(self._rms_vals)
            self._waveform_scale.set_placeholder(False)
            self._update_redraw_interval()

            # Clear temporary reference to the waveform data
            del self._new_rms_vals
예제 #7
0
    def __request(self, line, raw=False, want_reply=True):
        """
        Send a request to the server, if connected, and return its response
        """
        line = line.strip()

        if not (self.is_connected or line.split()[0] == 'login'):
            print_d("Can't do '%s' - not connected" % line.split()[0], self)
            return None

        if self._debug:
            print_(">>>> \"%s\"" % line)
        try:
            self.telnet.write(line + "\n")
            if not want_reply:
                return None
            raw_response = self.telnet.read_until("\n").strip()
        except socket.error as e:
            print_w("Couldn't communicate with squeezebox (%s)" % e)
            self.failures += 1
            if self.failures >= self._MAX_FAILURES:
                print_w("Too many Squeezebox failures. Disconnecting")
                self.is_connected = False
            return None
        response = raw_response if raw else urllib.unquote(raw_response)
        if self._debug:
            print_("<<<< \"%s\"" % (response,))
        return response[len(line) - 1:] if line.endswith("?")\
            else response[len(line) + 1:]
예제 #8
0
    def __request(self, line, raw=False, want_reply=True):
        """
        Send a request to the server, if connected, and return its response
        """
        line = line.strip()

        if not (self.is_connected or line.split()[0] == 'login'):
            print_d("Can't do '%s' - not connected" % line.split()[0], self)
            return None

        if self._debug:
            print_(">>>> \"%s\"" % line)
        try:
            self.telnet.write(line + "\n")
            if not want_reply:
                return None
            raw_response = self.telnet.read_until("\n").strip()
        except socket.error as e:
            print_w("Couldn't communicate with squeezebox (%s)" % e)
            self.failures += 1
            if self.failures >= self._MAX_FAILURES:
                print_w("Too many Squeezebox failures. Disconnecting")
                self.is_connected = False
            return None
        response = raw_response if raw else urllib.unquote(raw_response)
        if self._debug:
            print_("<<<< \"%s\"" % (response, ))
        return response[len(line) - 1:] if line.endswith("?")\
            else response[len(line) + 1:]
예제 #9
0
    def _on_bus_message(self, bus, message):
        if message.type == Gst.MessageType.ERROR:
            error, debug = message.parse_error()
            print_d("Error received from element {name}: {error}".format(
                name=message.src.get_name(), error=error))
            print_d("Debugging information: {}".format(debug))
        elif message.type == Gst.MessageType.ELEMENT:
            structure = message.get_structure()
            if structure.get_name() == "level":
                rms_db = structure.get_value("rms")
                if rms_db:
                    # Calculate average of all channels (usually 2)
                    rms_db_avg = sum(rms_db) / len(rms_db)
                    # Normalize dB value to value between 0 and 1
                    rms = pow(10, (rms_db_avg / 20))
                    self._new_rms_vals.append(rms)
            else:
                print_w("Got unexpected message of type {}"
                        .format(message.type))
        elif message.type == Gst.MessageType.EOS:
            self._clean_pipeline()

            # Update the waveform with the new data
            self._rms_vals = self._new_rms_vals
            self._waveform_scale.reset(self._rms_vals)
            self._waveform_scale.set_placeholder(False)
            self._update_redraw_interval()

            # Clear temporary reference to the waveform data
            del self._new_rms_vals
예제 #10
0
def _do_trash_songs(parent, songs, librarian):
    dialog = TrashDialog.for_songs(parent, songs)
    resp = dialog.run()
    if resp != TrashDialog.RESPONSE_TRASH:
        return

    window_title = _("Moving %(current)d/%(total)d.")

    w = WaitLoadWindow(parent, len(songs), window_title)
    w.show()

    ok = []
    failed = []
    for song in songs:
        filename = song("~filename")
        try:
            trash.trash(filename)
        except trash.TrashError as e:
            print_w("Couldn't trash file (%s)" % e)
            failed.append(song)
        else:
            ok.append(song)
        w.step()
    w.destroy()

    if failed:
        ErrorMessage(parent, _("Unable to move to trash"),
                     _("Moving one or more files to the trash failed.")).run()

    if ok:
        librarian.remove(ok)
예제 #11
0
def _do_trash_songs(parent, songs, librarian):
    dialog = TrashDialog.for_songs(parent, songs)
    resp = dialog.run()
    if resp != TrashDialog.RESPONSE_TRASH:
        return

    window_title = _("Moving %(current)d/%(total)d.")

    w = WaitLoadWindow(parent, len(songs), window_title)
    w.show()

    ok = []
    failed = []
    for song in songs:
        filename = song("~filename")
        try:
            trash.trash(filename)
        except trash.TrashError as e:
            print_w("Couldn't trash file (%s)" % e)
            failed.append(song)
        else:
            ok.append(song)
        w.step()
    w.destroy()

    if failed:
        ErrorMessage(parent,
            _("Unable to move to trash"),
            _("Moving one or more files to the trash failed.")
        ).run()

    if ok:
        librarian.remove(ok)
예제 #12
0
 def cfg_get(cls, name, default=None):
     try:
         key = __name__ + "_" + name
         return config.get("plugins", key)
     except (ValueError, ConfigParser.Error):
         print_w("Config entry '%s' not found. Using '%s'" %
                 (key, default,))
         return default
예제 #13
0
    def _finished(self, manager, results):
        self.__cancellable.cancel()
        if not any(results.values()):
            print_w(f"Nothing found from {len(self._groups)} provider(s)")

            self.button.set_sensitive(False)
            if not self.headless:
                self._quit(results)
예제 #14
0
 def slider_changed(_slider):
     new_size = slider.get_value()
     try:
         for child in self.flow_box.get_children():
             img = self.__image_from_child(child)
             img.resize(new_size)
     except AttributeError as e:
         print_w(f"Couldn't set picture size(s) ({e})")
예제 #15
0
 def disabled(self):
     if not self._pid:
         return
     print_d("Shutting down %s" % self.PLUGIN_NAME)
     try:
         os.kill(self._pid, signal.SIGTERM)
         os.kill(self._pid, signal.SIGKILL)
     except Exception as e:
         print_w("Couldn't shut down cleanly (%s)" % e)
예제 #16
0
 def runner(self, cache):
     changed = True
     try:
         changed = cache.update_charts(self.progress)
     except Exception as e:
         print_w(f"Couldn't update cache ({e})")
     if changed:
         self.cache_shelf[cache.username] = cache
     self.cache_shelf.close()
예제 #17
0
 def disabled(self):
     if not self._pid:
         return
     print_d("Shutting down %s" % self.PLUGIN_NAME)
     try:
         os.kill(self._pid, signal.SIGTERM)
         os.kill(self._pid, signal.SIGKILL)
     except Exception as e:
         print_w("Couldn't shut down cleanly (%s)" % e)
예제 #18
0
 def parse_body(self, body):
     if body is None:
         raise QueryPluginError
     self._raw_body = body.strip()
     self._reported.clear()
     try:
         self._globals.update(_ts=time.time())
         return compile(body.strip(), 'query', 'eval')
     except SyntaxError as e:
         print_w("Couldn't compile query (%s)" % e)
         raise QueryPluginError
예제 #19
0
파일: file.py 프로젝트: Vistaus/quodlibet
    def move_root(self, old_root: str, new_root: fsnative) \
        -> Generator[None, None, None]:
        """
        Move the root for all songs in a given (scan) directory.

        We avoid dereferencing the destination, to allow users things like:
          1. Symlink new_path -> old_root
          2. Move QL root to new_path
          3. Remove symlink
          4. Move audio files: old_root -> new_path

        """
        old_path = Path(normalize_path(old_root,
                                       canonicalise=True)).expanduser()
        new_path = Path(normalize_path(new_root)).expanduser()
        if not old_path.is_dir():
            raise ValueError(f"Source {old_path!r} is not a directory")
        if not new_path.is_dir():
            raise ValueError(f"Destination {new_path!r} is not a directory")
        print_d(f"{self._name}: checking entire library for {old_path!r}")
        missing: Set[AudioFile] = set()
        changed = set()
        total = len(self)
        if not total:
            return
        with Task(_("Library"), _("Moving library files")) as task:
            yield
            for i, song in enumerate(list(self.values())):
                task.update(i / total)
                key = normalize_path(song.key)
                path = Path(key)
                if old_path in path.parents:
                    # TODO: more Pathlib-friendly dir replacement...
                    new_key = key.replace(str(old_path), str(new_path), 1)
                    new_key = normalize_path(new_key, canonicalise=False)
                    if new_key == key:
                        print_w(f"Substitution failed for {key!r}")
                    # We need to update ~filename and ~mountpoint
                    song.sanitize()
                    song.write()
                    if self.move_song(song, new_key):
                        changed.add(song)
                    else:
                        missing.add(song)
                elif not (i % 1000):
                    print_d(f"Not moved, for example: {key!r}")
                if not i % 100:
                    yield
            self.changed(changed)
            if missing:
                print_w(f"Couldn't find {len(list(missing))} files: {missing}")
        yield
        self.save()
        print_d(f"Done moving to {new_path!r}.")
예제 #20
0
 def _set_bookmarks(self, model, a, b, library, song):
     def stringify(s):
         return s.decode('utf-8') if isinstance(s, bytes) else s
     try:
         song.bookmarks = [(t, stringify(l)) for t, l in model]
     except (AttributeError, ValueError) as e:
         print_w("Couldn't save bookmark for %s (%s)"
                 % (song("~filename"), e))
     else:
         if library is not None:
             library.changed([song])
예제 #21
0
 def parse_body(self, body):
     if body is None:
         raise QueryPluginError
     self._raw_body = body.strip()
     self._reported.clear()
     try:
         self._globals.update(_ts=time.time())
         return compile(body.strip(), 'query', 'eval')
     except SyntaxError as e:
         print_w("Couldn't compile query (%s)" % e)
         raise QueryPluginError
예제 #22
0
 def _save_lyrics(self, song, text):
     # First, try writing to the tags.
     song["lyrics"] = text
     try:
         song.write()
     except AudioFileError as e:
         print_w("Couldn't write embedded lyrics (%s)" % e)
         self._save_to_file(song, text)
     else:
         print_d("Wrote embedded lyrics into %s" % song("~filename"))
         app.librarian.emit('changed', [song])
         self._delete_file(song.lyric_filename)
예제 #23
0
 def __select_song(self, player, path, col):
     if len(path) == 1:
         if self.row_expanded(path):
             self.collapse_row(path)
         else:
             self.expand_row(path, False)
     else:
         songs = self.get_selected_songs()
         if songs and player.go_to(songs[0], True):
             player.paused = False
         else:
             print_w("Sorry, can't play song outside current list.")
예제 #24
0
 def _save_lyrics(self, song, text):
     # First, try writing to the tags.
     song["lyrics"] = text
     try:
         song.write()
     except AudioFileError as e:
         print_w("Couldn't write embedded lyrics (%s)" % e)
         self._save_to_file(song, text)
     else:
         print_d("Wrote embedded lyrics into %s" % song("~filename"))
         app.librarian.emit('changed', [song])
         self._delete_file(song.lyric_filename)
예제 #25
0
def destroy() -> None:
    """Destroy all registered libraries """

    print_d("Destroying all libraries...")

    librarian = SongFileLibrary.librarian
    if librarian:
        for lib in list(librarian.libraries.values()):
            try:
                lib.destroy()
            except Exception as e:
                print_w(f"Couldn't destroy {lib} ({e!r})")
        librarian.destroy()
예제 #26
0
 def _save_to_file(self, song, text):
     lyric_fn = song.lyric_filename
     if not lyric_fn:
         print_w("No lyrics file to save to, ignoring.")
         return
     try:
         os.makedirs(os.path.dirname(lyric_fn), exist_ok=True)
     except EnvironmentError:
         errorhook()
     try:
         with open(lyric_fn, "wb") as f:
             f.write(text.encode("utf-8"))
         print_d(f"Saved lyrics to file {lyric_fn!r}")
     except EnvironmentError:
         errorhook()
예제 #27
0
    def __search_thread(self, engine, query, replace):
        """Creates searching threads which call the callback function after
        they are finished"""

        clean_query = self.__cleanup_query(query, replace)
        result = []
        try:
            result = engine().start(clean_query, self.overall_limit)
        except Exception:
            print_w("[AlbumArt] %s: %r" % (engine.__name__, query))
            print_exc()

        self.finished += 1
        #progress is between 0..1
        progress = float(self.finished) / len(self.engine_list)
        GLib.idle_add(self.callback, result, progress)
예제 #28
0
 def conclude(self, fails, reason):
     from quodlibet import print_w
     if fails:
         def format_occurrences(e):
             occurences = [(self.lang + ".po", e.linenum)]
             occurences += e.occurrences
             return ', '.join('%s:%s' % o for o in occurences)
         messages = [
             '"%s" - "%s" (%s)' % (e.msgid, e.msgstr, format_occurrences(e))
             for e in fails
         ]
         for message in messages:
             print_w(message)
         self.fail(
             "One or more messages did not pass (%s).\n"
             "Please check the warning messages above." % reason)
예제 #29
0
 def search(self, data, body):
     try:
         self._globals['s'] = data
         # Albums can be queried too...
         self._globals['a'] = data
         # eval modifies the globals in place, it seems
         ret = eval(body, dict(self._globals))
         return ret
     except Exception as e:
         key = str(e)
         if key not in self._reported:
             self._reported.add(key)
             print_w("%s(%s) in expression '%s'. "
                     "Example failing data: %s" %
                     (type(e).__name__, key, self._raw_body, self._globals))
         return False
예제 #30
0
파일: info.py 프로젝트: azarmadr/quodlibet
 def _on_set_pattern(self, button, edit, player):
     self._pattern = edit.text.rstrip()
     if self._pattern == SongInfo._pattern:
         try:
             os.unlink(self._pattern_filename)
         except OSError:
             pass
     else:
         try:
             with open(self._pattern_filename, "wb") as h:
                 h.write(self._pattern.encode("utf-8") + b"\n")
         except EnvironmentError as e:
             print_w("Couldn't save display pattern '%s' (%s)"
                     % (self._pattern, e))
     self._compiled = XMLFromMarkupPattern(self._pattern)
     self._update_info(player)
예제 #31
0
 def conclude(self, fails, reason):
     from quodlibet import print_w
     if fails:
         def format_occurrences(e):
             occurences = [(self.lang + ".po", e.linenum)]
             occurences += e.occurrences
             return ', '.join('%s:%s' % o for o in occurences)
         messages = [
             '"%s" - "%s" (%s)' % (e.msgid, e.msgstr, format_occurrences(e))
             for e in fails
         ]
         for message in messages:
             print_w(message)
         self.fail(
             "One or more messages did not pass (%s).\n"
             "Please check the warning messages above." % reason)
예제 #32
0
파일: info.py 프로젝트: LudoBike/quodlibet
 def _on_set_pattern(self, button, edit, player):
     self._pattern = edit.text.rstrip()
     if self._pattern == SongInfo._pattern:
         try:
             os.unlink(self._pattern_filename)
         except OSError:
             pass
     else:
         try:
             with open(self._pattern_filename, "wb") as h:
                 h.write(self._pattern.encode("utf-8") + b"\n")
         except EnvironmentError as e:
             print_w("Couldn't save display pattern '%s' (%s)"
                     % (self._pattern, e))
     self._compiled = XMLFromMarkupPattern(self._pattern)
     self._update_info(player)
예제 #33
0
    def __search_thread(self, engine, query, replace):
        """Creates searching threads which call the callback function after
        they are finished"""

        clean_query = self.__cleanup_query(query, replace)
        result = []
        try:
            result = engine().start(clean_query, self.overall_limit)
        except Exception:
            print_w("[AlbumArt] %s: %r" % (engine.__name__, query))
            print_exc()

        self.finished += 1
        #progress is between 0..1
        progress = float(self.finished) / len(self.engine_list)
        GLib.idle_add(self.callback, result, progress)
예제 #34
0
파일: file.py 프로젝트: tralph3/quodlibet
    def _load_item(self, item, force=False):
        """Add an item, or refresh it if it's already in the library.
        No signals will be fired.
        Return a tuple of booleans: (changed, removed)
        """
        print_d(f"Loading {item.key!r}", self._name)
        valid = item.valid()

        # The item is fine; add it if it's not present.
        if not force and valid:
            print_d(f"{item.key!r} is valid.", self._name)
            self._contents[item.key] = item
            return False, False
        else:
            # Either we should force a load, or the item is not okay.
            # We're going to reload; this could change the key.  So
            # remove the item if it's currently in.
            try:
                del self._contents[item.key]
            except KeyError:
                present = False
            else:
                present = True
            # If the item still exists, reload it.
            if item.exists():
                try:
                    item.reload()
                except AudioFileError:
                    print_w(f"Error reloading {item.key!r}", self._name)
                    return False, True
                else:
                    print_d(f"Reloaded {item.key!r}.", self._name)
                    self._contents[item.key] = item
                    return True, False
            elif not item.mounted():
                # We don't know if the item is okay or not, since
                # it's not not mounted. If the item was present
                # we need to mark it as removed.
                print_d(f"Masking {item.key!r}", self._name)
                self._masked.setdefault(item.mountpoint, {})
                self._masked[item.mountpoint][item.key] = item
                return False, present
            else:
                # The item doesn't exist at all anymore. Mark it as
                # removed if it was present, otherwise nothing.
                print_d(f"Ignoring (so removing) {item.key!r}.", self._name)
                return False, present
예제 #35
0
 def _sent(self, msg, result, data):
     headers = self.message.get_property('response-headers')
     self.size = int(headers.get('content-length'))
     self._content_type = headers.get('content-type')
     self._original = result
     try:
         loader = GdkPixbuf.PixbufLoader()
     except GLib.GError as e:
         print_w("Couldn't create GdkPixbuf (%s)" % e)
     else:
         loader.write(result)
         loader.close()
         self._pixbuf = loader.get_pixbuf()
         self.emit("info-known", self._content_type, self.size,
                   self._pixbuf.props)
         self.resize()
         self.queue_draw()
예제 #36
0
    def monitor_dir(self, path: Path) -> None:
        """Monitors a single directory"""

        # Only add one monitor per absolute path...
        if path not in self._monitors:
            f = Gio.File.new_for_path(str(path))
            try:
                monitor = f.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, None)
            except GLib.GError as e:
                print_w(f"Couldn't watch {path} ({e})", self._name)
                monitor = None
            if not monitor:
                return
            handler_id = monitor.connect("changed", self.__file_changed)
            # Don't destroy references - http://stackoverflow.com/q/4535227
            self._monitors[path] = (monitor, handler_id)
            print_d(f"Monitoring {path!s}", self._name)
예제 #37
0
 def search(self, data, body):
     try:
         self._globals['s'] = data
         # Albums can be queried too...
         self._globals['a'] = data
         # eval modifies the globals in place, it seems
         ret = eval(body, dict(self._globals))
         return ret
     except Exception as e:
         key = str(e)
         if key not in self._reported:
             self._reported.add(key)
             print_w("%s(%s) in expression '%s'. "
                     "Example failing data: %s"
                     % (type(e).__name__, key, self._raw_body,
                        self._globals))
         return False
예제 #38
0
 def _save_lyrics(self, song, text):
     # First, try writing to the tags.
     if "lyrics" not in song and "unsyncedlyrics" in song:
         tag = "unsyncedlyrics"
     else:
         tag = "lyrics"
     song[tag] = text
     try:
         song.write()
     except AudioFileError as e:
         print_w(f"Couldn't write embedded lyrics ({e!r})")
         self._save_to_file(song, text)
     else:
         print_d(f"Wrote embedded lyrics into {song('~filename')}")
         app.librarian.emit('changed', [song])
         fn = song.lyric_filename
         if fn:
             self._delete_file(fn)
예제 #39
0
 def save(self, filename=None):
     """Save the library to the given filename."""
     self._save_lock.acquire()
     if filename is None:
         filename = self.filename
     print_d("Saving contents to %r." % filename, self)
     if not os.path.isdir(os.path.dirname(filename)):
         os.makedirs(os.path.dirname(filename))
     # Issue 479. Catch problem early
     if os.path.isdir(filename):
         msg = _("Cannot save library contents to %s (it's a directory). "
                 "Please remove it and try again.") % filename
         print_w(msg)
         # TODO: Better handling of this edge-case...
         ErrorMessage(None, _("Library Error"), msg).run()
         self._save_lock.release()
         return
     fileobj = file(filename + ".tmp", "wb")
     if fcntl is not None:
         fcntl.flock(fileobj.fileno(), fcntl.LOCK_EX)
     items = self.values()
     for masked in self._masked.values():
         items.extend(masked.values())
     # Item keys are often based on filenames, in which case
     # sorting takes advantage of the filesystem cache when we
     # reload/rescan the files.
     items.sort(key=lambda item: item.key)
     # While protocol 2 is usually faster it uses __setitem__
     # for unpickle and we override it to clear the sort cache.
     # This roundtrip makes it much slower, so we use protocol 1
     # unpickle numbers (py2.7):
     #   2: 0.66s / 2 + __set_item__: 1.18s / 1 + __set_item__: 0.72s
     # see: http://bugs.python.org/issue826897
     pickle.dump(items, fileobj, 1)
     fileobj.flush()
     os.fsync(fileobj.fileno())
     fileobj.close()
     if os.name == "nt":
         try: os.remove(filename)
         except EnvironmentError: pass
     os.rename(filename + ".tmp", filename)
     self.dirty = False
     print_d("Done saving contents to %r." % filename, self)
     self._save_lock.release()
예제 #40
0
 def _write(self, _widget: Gtk.Widget, model: QueueModel, force=False):
     diff = time.time() - self._updated_time
     if not self._should_write(force, diff):
         self._pending += 1
         return
     print_d(f"Saving play queue after {diff:.1f}s")
     filenames = [row[0]["~filename"] for row in model]
     try:
         with open(QUEUE, "wb") as f:
             for filename in filenames:
                 try:
                     line = fsn2bytes(filename, "utf-8")
                 except ValueError as e:
                     print_w(f"Ignoring queue save error ({e})")
                     continue
                 f.write(line + b"\n")
     except EnvironmentError as e:
         print_e(f"Error saving queue ({e})")
     self._updated_time = time.time()
     self._pending = 0
예제 #41
0
    def close(self):
        if self._client_priv is None:
            return

        self._client_priv.disconnect(self._sig_id)
        self._sig_id = None

        try:
            bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            session_mgr = Gio.DBusProxy.new_sync(
                bus, Gio.DBusProxyFlags.NONE, None,
                self.DBUS_NAME, self.DBUS_OBJECT_PATH,
                self.DBUS_MAIN_INTERFACE, None)
            session_mgr.UnregisterClient('(o)', self._client_path)
        except GLib.Error as e:
            print_w(str(e))

        print_d("Disconnected from gnome session manager: %s" %
                self._client_path)
        self._client_path = None
예제 #42
0
 def __move(self, widget):
     selection = self.view.get_selection()
     model, paths = selection.get_selected_rows()
     rows = [model[p] for p in paths or []]
     if len(rows) > 1:
         print_w("Can't do multiple moves at once")
         return
     elif not rows:
         return
     base_dir = rows[0][0]
     chooser = _get_chooser(_("Select This Directory"), _("_Cancel"))
     chooser.set_title(
         _("Select Actual / New Directory for {dir!r}").format(
             dir=base_dir))
     chooser.set_action(Gtk.FileChooserAction.SELECT_FOLDER)
     chooser.set_local_only(True)
     chooser.set_select_multiple(False)
     results = _run_chooser(self, chooser)
     if not results:
         return
     new_dir = results[0]
     desc = (_("This will move QL metadata:\n\n"
               "{old!r} -> {new!r}\n\n"
               "The audio files themselves are not moved by this.\n"
               "Nonetheless, a backup is recommended "
               "(including the Quodlibet 'songs' file)").format(
                   old=base_dir, new=new_dir))
     title = _("Move scan root {dir!r}?").format(dir=base_dir)
     value = ConfirmationPrompt(self,
                                title=title,
                                description=desc,
                                ok_button_text=_("OK, move it!")).run()
     if value != ConfirmationPrompt.RESPONSE_INVOKE:
         print_d("User aborted")
         return
     print_d(f"Migrate from {base_dir} -> {new_dir}")
     copool.add(app.librarian.move_root, base_dir, new_dir)
     path = paths[0]
     self.model[path] = [new_dir]
     self.model.row_changed(path, rows[0].iter)
     self.__save()
예제 #43
0
    def _on_bus_message(self, bus, message):
        if message.type == Gst.MessageType.ERROR:
            error, debug = message.parse_error()
            print_d("Error received from element {name}: {error}".format(
                name=message.src.get_name(), error=error))
            print_d("Debugging information: {}".format(debug))
        elif message.type == Gst.MessageType.ELEMENT:
            structure = message.get_structure()
            if structure.get_name() == "level":
                rms_db = structure.get_value("rms")
                # Calculate average of all channels (usually 2)
                rms_db_avg = sum(rms_db) / len(rms_db)
                # Normalize dB value to value between 0 and 1
                rms = pow(10, (rms_db_avg / 20))
                self._rms_vals.append(rms)
            else:
                print_w("Got unexpected message of type {}"
                        .format(message.type))
        elif message.type == Gst.MessageType.EOS:
            self._pipeline.set_state(Gst.State.NULL)

            if self._player.info:
                self._waveform_scale.reset(self._rms_vals, self._player)
                self._waveform_scale.set_placeholder(False)
예제 #44
0
    def __init__(self, parent, song):
        super(CoverArea, self).__init__()
        self.song = song

        self.dirname = song("~dirname")
        self.main_win = parent

        self.data_cache = []
        self.current_data = None
        self.current_pixbuf = None

        self.image = Gtk.Image()
        self.button = Button(_("_Save"), Icons.DOCUMENT_SAVE)
        self.button.set_sensitive(False)
        self.button.connect('clicked', self.__save)

        close_button = Button(_("_Close"), Icons.WINDOW_CLOSE)
        close_button.connect('clicked', lambda x: self.main_win.destroy())

        self.window_fit = self.ConfigCheckButton(_('Fit image to _window'),
                                                 'fit', True)
        self.window_fit.connect('toggled', self.__scale_pixbuf)

        self.name_combo = Gtk.ComboBoxText()

        self.cmd = qltk.entry.ValidatingEntry(iscommand)

        # Both labels
        label_open = Gtk.Label(label=_('_Program:'))
        label_open.set_use_underline(True)
        label_open.set_mnemonic_widget(self.cmd)
        label_open.set_justify(Gtk.Justification.LEFT)

        self.open_check = self.ConfigCheckButton(_('_Edit image after saving'),
                                                 'edit', False)
        label_name = Gtk.Label(label=_('File_name:'), use_underline=True)
        label_name.set_use_underline(True)
        label_name.set_mnemonic_widget(self.name_combo)
        label_name.set_justify(Gtk.Justification.LEFT)

        self.cmd.set_text(self.config_get('edit_cmd', 'gimp'))

        # Create the filename combo box
        fn_list = ['cover.jpg', 'folder.jpg', '.folder.jpg']

        # Issue 374 - add dynamic file names
        artist = song("artist")
        alartist = song("albumartist")
        album = song("album")
        labelid = song("labelid")
        if album:
            fn_list.append("<album>.jpg")
            if alartist:
                fn_list.append("<albumartist> - <album>.jpg")
            else:
                fn_list.append("<artist> - <album>.jpg")
        else:
            print_w(u"No album for \"%s\". Could be difficult "
                    u"finding art…" % song("~filename"))
            title = song("title")
            if title and artist:
                fn_list.append("<artist> - <title>.jpg")
        if labelid:
            fn_list.append("<labelid>.jpg")

        set_fn = self.config_get('fn', fn_list[0])

        for i, fn in enumerate(fn_list):
                self.name_combo.append_text(fn)
                if fn == set_fn:
                    self.name_combo.set_active(i)

        if self.name_combo.get_active() < 0:
            self.name_combo.set_active(0)

        table = Gtk.Table(n_rows=2, n_columns=2, homogeneous=False)
        table.set_row_spacing(0, 5)
        table.set_row_spacing(1, 5)
        table.set_col_spacing(0, 5)
        table.set_col_spacing(1, 5)

        table.attach(label_open, 0, 1, 0, 1)
        table.attach(label_name, 0, 1, 1, 2)

        table.attach(self.cmd, 1, 2, 0, 1)
        table.attach(self.name_combo, 1, 2, 1, 2)

        self.scrolled = Gtk.ScrolledWindow()
        self.scrolled.add_with_viewport(self.image)
        self.scrolled.set_policy(Gtk.PolicyType.AUTOMATIC,
                                 Gtk.PolicyType.AUTOMATIC)

        bbox = Gtk.HButtonBox()
        bbox.set_spacing(6)
        bbox.set_layout(Gtk.ButtonBoxStyle.END)
        bbox.pack_start(self.button, True, True, 0)
        bbox.pack_start(close_button, True, True, 0)

        bb_align = Align(valign=Gtk.Align.END, right=6)
        bb_align.add(bbox)

        main_hbox = Gtk.HBox()
        main_hbox.pack_start(table, False, True, 6)
        main_hbox.pack_start(bb_align, True, True, 0)

        top_hbox = Gtk.HBox()
        top_hbox.pack_start(self.open_check, True, True, 0)
        top_hbox.pack_start(self.window_fit, False, True, 0)

        main_vbox = Gtk.VBox()
        main_vbox.pack_start(top_hbox, True, True, 2)
        main_vbox.pack_start(main_hbox, True, True, 0)

        self.pack_start(self.scrolled, True, True, 0)
        self.pack_start(main_vbox, False, True, 5)

        # 5 MB image cache size
        self.max_cache_size = 1024 * 1024 * 5

        # For managing fast selection switches of covers..
        self.stop_loading = False
        self.loading = False
        self.current_job = 0

        self.connect('destroy', self.__save_config)
예제 #45
0
    def __set_async(self, url):
        """Manages various things:
        Fast switching of covers (aborting old HTTP requests),
        The image cache, etc."""

        self.current_job += 1
        job = self.current_job

        self.stop_loading = True
        while self.loading:
            time.sleep(0.05)
        self.stop_loading = False

        if job != self.current_job:
            return

        self.loading = True

        GLib.idle_add(self.button.set_sensitive, False)
        self.current_pixbuf = None

        pbloader = GdkPixbuf.PixbufLoader()
        pbloader.connect('closed', self.__close)

        # Look for cached images
        raw_data = None
        for entry in self.data_cache:
            if entry[0] == url:
                raw_data = entry[1]
                break

        if not raw_data:
            pbloader.connect('area-updated', self.__update)

            data_store = StringIO()

            try:
                request = urllib2.Request(url)
                request.add_header('User-Agent', USER_AGENT)
                url_sock = urllib2.urlopen(request)
            except urllib2.HTTPError:
                print_w(_("[albumart] HTTP Error: %s") % url)
            else:
                while not self.stop_loading:
                    tmp = url_sock.read(1024 * 10)
                    if not tmp:
                            break
                    pbloader.write(tmp)
                    data_store.write(tmp)

                url_sock.close()

                if not self.stop_loading:
                    raw_data = data_store.getvalue()

                    self.data_cache.insert(0, (url, raw_data))

                    while 1:
                        cache_sizes = [len(data[1]) for data in
                                       self.data_cache]
                        if sum(cache_sizes) > self.max_cache_size:
                            del self.data_cache[-1]
                        else:
                            break

            data_store.close()
        else:
            # Sleep for fast switching of cached images
            time.sleep(0.05)
            if not self.stop_loading:
                pbloader.write(raw_data)

        try:
            pbloader.close()
        except GLib.GError:
            pass

        self.current_data = raw_data

        if not self.stop_loading:
            GLib.idle_add(self.button.set_sensitive, True)

        self.loading = False
예제 #46
0
 def check_unused(self):
     if self.unused:
         from quodlibet import print_w
         print_w('ListWithUnused has unused items: %s' % self.unused)