diff --git a/src/__init__.py b/src/__init__.py index aae3b90..c498e0f 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -17,16 +17,3 @@ if os.path.exists(localedirdev): # Set GSettings schema dir (if not set already) if not os.environ.get('GSETTINGS_SCHEMA_DIR'): os.environ['GSETTINGS_SCHEMA_DIR'] = datadirdev - - -class Environment: - """Wrapper class to access environment settings.""" - - def get_srcdir(): - return srcdir - - def get_data(subdir): - return os.path.join(datadir, subdir) - - def get_locale(): - return localedir diff --git a/src/application.py b/src/application.py index 23f482e..c39b929 100644 --- a/src/application.py +++ b/src/application.py @@ -14,11 +14,12 @@ class Application(Gtk.Application): ID = 'xyz.suruatoel.mcg' DOMAIN = 'mcg' - def __init__(self): + def __init__(self, version): super().__init__(application_id=Application.ID, flags=Gio.ApplicationFlags.FLAGS_NONE) self._window = None self._info_dialog = None + self._version = version self._verbosity = logging.WARNING self.set_accels_for_action('window.close', ['q']) self.set_accels_for_action('win.show-help-overlay', ['k']) @@ -52,7 +53,7 @@ class Application(Gtk.Application): self._info_dialog = Adw.AboutDialog() self._info_dialog.set_application_icon("xyz.suruatoel.mcg") self._info_dialog.set_application_name("CoverGrid") - self._info_dialog.set_version("3.2.1") + self._info_dialog.set_version(self._version) self._info_dialog.set_comments( """CoverGrid is a client for the Music Player Daemon, focusing on \ albums instead of single tracks.""") @@ -77,10 +78,10 @@ albums instead of single tracks.""") style_manager.set_color_scheme(Adw.ColorScheme.PREFER_DARK) def _load_css(self): - styleProvider = Gtk.CssProvider() - styleProvider.load_from_resource(self._get_resource_path('gtk.css')) + style_provider = Gtk.CssProvider() + style_provider.load_from_resource(self._get_resource_path('gtk.css')) Gtk.StyleContext.add_provider_for_display( - Gdk.Display.get_default(), styleProvider, + Gdk.Display.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) def _setup_actions(self): diff --git a/src/client.py b/src/client.py index ac7235b..17b7522 100644 --- a/src/client.py +++ b/src/client.py @@ -22,7 +22,7 @@ class MPDException(Exception): def _parse_error(self, error): if error: - parts = re.match("\[(\d+)@(\d+)\]\s\{(\w+)\}\s(.*)", error) + parts = re.match(r"\[(\d+)@(\d+)\]\s\{(\w+)\}\s(.*)", error) if parts: self._error_number = int(parts.group(1)) self._command_number = int(parts.group(2)) @@ -298,7 +298,7 @@ class Client(Base): resources = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, socket.IPPROTO_TCP) for res in resources: - af, socktype, proto, canonname, sa = res + af, socktype, proto, _, sa = res try: sock = socket.socket(af, socktype, proto) sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) @@ -542,8 +542,6 @@ class Client(Base): def _playpause(self): """Action: Perform the real play/pause command.""" - #status = self._parse_dict(self._call('status')) - #if 'state' in status: if self._state == 'play': self._call('pause') else: @@ -653,7 +651,7 @@ class Client(Base): return future def _add_action_future(self, future, method, *args): - """Add an action to the action list based on a futre.""" + """Add an action to the action list based on a future.""" self._logger.debug("add action future %r (%r)", method.__name__, args) action = (future, method, args) self._actions.put(action) @@ -754,7 +752,6 @@ class Client(Base): data = None size = 1 offset = 0 - index = 0 # Read data until size is reached while offset < size: @@ -775,7 +772,7 @@ class Client(Base): self._logger.debug("size: %d", size) # For some commands the second line is the mimetype if has_mimetype: - mimetype = self._parse_dict([self._read_line()])['type'] + self._parse_dict([self._read_line()])['type'] # Next line is the count of bytes read binary = int(self._parse_dict([self._read_line()])['binary']) self._logger.debug("binary: %d", binary) @@ -788,7 +785,7 @@ class Client(Base): # Read actual bytes self._read_bytes(data_view, binary) offset += binary - # Read line break to complete previous repsonse + # Read line break to complete previous response self._read_line() # Read command completion end = self._read_line() @@ -831,12 +828,12 @@ class Client(Base): self._buffer = buf def _parse_dict(self, response): - dict = {} + dictionary = {} if response: for line in response: key, value = self._split_line(line) - dict[key] = value - return dict + dictionary[key] = value + return dictionary def _parse_list(self, response, delimiters): entry = {} @@ -846,11 +843,6 @@ class Client(Base): if entry and key in delimiters: yield entry entry = {} - #if key in entry.keys(): - # if entry[key] is not list: - # entry[key] = [entry[key]] - # entry[key].append(value) - #else: entry[key] = value if entry: yield entry @@ -863,13 +855,13 @@ class Client(Base): album = None if 'album' not in song: song['album'] = MCGAlbum.DEFAULT_ALBUM - id = Utils.generate_id(song['album']) - if lookup and id in self._albums.keys(): - album = self._albums[id] + album_id = Utils.generate_id(song['album']) + if lookup and album_id in self._albums.keys(): + album = self._albums[album_id] else: album = MCGAlbum(song['album'], self._host) if lookup: - self._albums[id] = album + self._albums[album_id] = album return album def _extract_track(self, song): @@ -927,7 +919,7 @@ class MCGAlbum: def __init__(self, title, host): self._artists = [] self._albumartists = [] - self._pathes = [] + self._paths = [] if type(title) is list: title = title[0] self._title = title @@ -989,8 +981,8 @@ class MCGAlbum: ): self._dates.append(track.get_date()) path = os.path.dirname(track.get_file()) - if path not in self._pathes: - self._pathes.append(path) + if path not in self._paths: + self._paths.append(path) if track.get_last_modified(): if ( not self._last_modified @@ -1035,8 +1027,9 @@ class MCGAlbum: return False return True + @staticmethod def compare(album1, album2, criterion=None, reverse=False): - if criterion == None: + if criterion is None: criterion = SortOrder.TITLE if criterion == SortOrder.ARTIST: value_function = "get_artists" @@ -1047,22 +1040,22 @@ class MCGAlbum: elif criterion == SortOrder.MODIFIED: value_function = "get_last_modified" - reverseMultiplier = -1 if reverse else 1 + reverse_multiplier = -1 if reverse else 1 value1 = getattr(album1, value_function)() value2 = getattr(album2, value_function)() if value1 is None and value2 is None: return 0 elif value1 is None: - return -1 * reverseMultiplier + return -1 * reverse_multiplier elif value2 is None: - return 1 * reverseMultiplier + return 1 * reverse_multiplier if value1 < value2: - return -1 * reverseMultiplier + return -1 * reverse_multiplier elif value1 == value2: return 0 else: - return 1 * reverseMultiplier + return 1 * reverse_multiplier class MCGTrack: @@ -1147,7 +1140,7 @@ class MCGTrack: if date_string: try: self._last_modified = dateutil.parser.isoparse(date_string) - except ValueError as e: + except ValueError: self._logger.debug("Invalid date format: %s", date_string) def get_last_modified(self): diff --git a/src/coverpanel.py b/src/coverpanel.py index 88988b5..ba09e16 100644 --- a/src/coverpanel.py +++ b/src/coverpanel.py @@ -54,21 +54,22 @@ class CoverPanel(Gtk.Overlay): GObject.idle_add(self._enable_tracklist) # Click handler for image - clickController = Gtk.GestureClick() - clickController.connect('pressed', self.on_cover_box_pressed) - self.cover_box.add_controller(clickController) + click_controller = Gtk.GestureClick() + click_controller.connect('pressed', self.on_cover_box_pressed) + self.cover_box.add_controller(click_controller) # Button controller for songs scale - buttonController = Gtk.GestureClick() - buttonController.connect('pressed', self.on_songs_scale_pressed) - buttonController.connect('unpaired-release', - self.on_songs_scale_released) - self.songs_scale.add_controller(buttonController) + button_controller = Gtk.GestureClick() + button_controller.connect('pressed', self.on_songs_scale_pressed) + button_controller.connect('unpaired-release', + self.on_songs_scale_released) + self.songs_scale.add_controller(button_controller) def get_toolbar(self): return self.toolbar def set_selected(self, selected): + """The cover panel does not use selections""" pass def on_cover_box_pressed(self, widget, npress, x, y): @@ -152,7 +153,7 @@ class CoverPanel(Gtk.Overlay): # Load image and draw it try: self._cover_pixbuf = Utils.load_pixbuf(data) - except Exception as e: + except Exception: self._logger.exception("Failed to set albumart") self._cover_pixbuf = None else: @@ -203,9 +204,6 @@ class CoverPanel(Gtk.Overlay): self.cover_spinner.stop() def _resize_image(self): - """Diese Methode skaliert das geladene Bild aus dem Pixelpuffer - auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse - """ # Get size size_width = self.cover_stack.get_size(Gtk.Orientation.HORIZONTAL) size_height = self.cover_stack.get_size(Gtk.Orientation.HORIZONTAL) @@ -226,10 +224,10 @@ class CoverPanel(Gtk.Overlay): return # Skalierungswert für Breite und Höhe ermitteln - ratioW = float(size_width) / float(pixbuf.get_width()) - ratioH = float(size_height) / float(pixbuf.get_height()) + ratio_w = float(size_width) / float(pixbuf.get_width()) + ratio_h = float(size_height) / float(pixbuf.get_height()) # Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren - ratio = min(ratioW, ratioH) + ratio = min(ratio_w, ratio_h) ratio = min(ratio, 1) # Neue Breite und Höhe berechnen width = int(math.floor(pixbuf.get_width() * ratio)) diff --git a/src/librarypanel.py b/src/librarypanel.py index 8027620..488528b 100644 --- a/src/librarypanel.py +++ b/src/librarypanel.py @@ -116,10 +116,10 @@ class LibraryPanel(Adw.Bin): } # Button controller for grid scale - buttonController = Gtk.GestureClick() - buttonController.connect('unpaired-release', - self.on_grid_scale_released) - self.grid_scale.add_controller(buttonController) + button_controller = Gtk.GestureClick() + button_controller.connect('unpaired-release', + self.on_grid_scale_released) + self.grid_scale.add_controller(button_controller) def get_headerbar_standalone(self): return self._headerbar_standalone @@ -151,8 +151,8 @@ class LibraryPanel(Adw.Bin): def on_grid_scale_released(self, widget, x, y, npress, sequence): size = math.floor(self.grid_scale.get_value()) - range = self.grid_scale.get_adjustment() - if size < range.get_lower() or size > range.get_upper(): + grid_range = self.grid_scale.get_adjustment() + if size < grid_range.get_lower() or size > grid_range.get_upper(): return self._item_size = size self.emit('item-size-changed', size) @@ -162,8 +162,8 @@ class LibraryPanel(Adw.Bin): @Gtk.Template.Callback() def on_grid_scale_changed(self, widget): size = math.floor(self.grid_scale.get_value()) - range = widget.get_adjustment() - if size < range.get_lower() or size > range.get_upper(): + grid_range = widget.get_adjustment() + if size < grid_range.get_lower() or size > grid_range.get_upper(): return self._set_widget_grid_size(self.library_grid, size, True) @@ -200,9 +200,9 @@ class LibraryPanel(Adw.Bin): # Get selected album item = self._library_grid_filter.get_item(position) album = item.get_album() - id = album.get_id() + album_id = album.get_id() self._selected_albums = [album] - self.emit('albumart', id) + self.emit('albumart', album_id) # Show standalone album if widget.get_model() == self._library_grid_selection_single: @@ -297,7 +297,7 @@ class LibraryPanel(Adw.Bin): # Load image and draw it try: self._standalone_pixbuf = Utils.load_pixbuf(data) - except Exception as e: + except Exception: self._logger.exception("Failed to set albumart") self._standalone_pixbuf = self._get_default_image() else: @@ -415,9 +415,9 @@ class LibraryPanel(Adw.Bin): lower = int(self.grid_scale.get_adjustment().get_lower()) upper = int(self.grid_scale.get_adjustment().get_upper()) - countMin = max(int(width / upper), 1) - countMax = max(int(width / lower), 1) - for index in range(countMin, countMax): + count_min = max(int(width / upper), 1) + count_max = max(int(width / lower), 1) + for index in range(count_min, count_max): pixel = int(width / index) pixel = pixel - (2 * int(pixel / 100)) self.grid_scale.add_mark(pixel, Gtk.PositionType.BOTTOM, None) @@ -431,9 +431,6 @@ class LibraryPanel(Adw.Bin): self.emit('close-standalone') def _resize_standalone_image(self): - """Diese Methode skaliert das geladene Bild aus dem Pixelpuffer - auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse - """ # Get size size_width = self.standalone_stack.get_width() size_height = self.standalone_stack.get_height() @@ -445,10 +442,10 @@ class LibraryPanel(Adw.Bin): return # Skalierungswert für Breite und Höhe ermitteln - ratioW = float(size_width) / float(pixbuf.get_width()) - ratioH = float(size_height) / float(pixbuf.get_height()) + ratio_w = float(size_width) / float(pixbuf.get_width()) + ratio_h = float(size_height) / float(pixbuf.get_height()) # Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren - ratio = min(ratioW, ratioH) + ratio = min(ratio_w, ratio_h) ratio = min(ratio, 1) # Neue Breite und Höhe berechnen width = int(math.floor(pixbuf.get_width() * ratio)) diff --git a/src/main.py b/src/main.py index bc1476c..319beff 100644 --- a/src/main.py +++ b/src/main.py @@ -4,5 +4,5 @@ from .application import Application def main(version): - app = Application() + app = Application(version) return app.run(sys.argv) diff --git a/src/playlistpanel.py b/src/playlistpanel.py index 02526e0..34abfee 100644 --- a/src/playlistpanel.py +++ b/src/playlistpanel.py @@ -110,9 +110,9 @@ class PlaylistPanel(Adw.Bin): # Get selected album item = self._playlist_grid_model.get_item(position) album = item.get_album() - id = album.get_id() + album_id = album.get_id() self._selected_albums = [album] - self.emit('albumart', id) + self.emit('albumart', album_id) # Show standalone album if widget.get_model() == self._playlist_grid_selection_single: @@ -175,7 +175,7 @@ class PlaylistPanel(Adw.Bin): # Load image and draw it try: self._standalone_pixbuf = Utils.load_pixbuf(data) - except Exception as e: + except Exception: self._logger.exception("Failed to set albumart") self._cover_pixbuf = self._get_default_image() else: @@ -239,9 +239,6 @@ class PlaylistPanel(Adw.Bin): self.emit('close-standalone') def _resize_standalone_image(self): - """Diese Methode skaliert das geladene Bild aus dem Pixelpuffer - auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse - """ # Get size size_width = self.standalone_stack.get_width() size_height = self.standalone_stack.get_height() @@ -253,10 +250,10 @@ class PlaylistPanel(Adw.Bin): return # Skalierungswert für Breite und Höhe ermitteln - ratioW = float(size_width) / float(pixbuf.get_width()) - ratioH = float(size_height) / float(pixbuf.get_height()) + ratio_w = float(size_width) / float(pixbuf.get_width()) + ratio_h = float(size_height) / float(pixbuf.get_height()) # Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren - ratio = min(ratioW, ratioH) + ratio = min(ratio_w, ratio_h) ratio = min(ratio, 1) # Neue Breite und Höhe berechnen width = int(math.floor(pixbuf.get_width() * ratio)) diff --git a/src/serverpanel.py b/src/serverpanel.py index be7bd97..f29c7c4 100644 --- a/src/serverpanel.py +++ b/src/serverpanel.py @@ -31,7 +31,7 @@ class ServerPanel(Adw.Bin): stats_dbplaytime = Gtk.Template.Child() stats_playtime = Gtk.Template.Child() stats_uptime = Gtk.Template.Child() - # Audio ouptut devices widgets + # Audio output devices widgets output_devices = Gtk.Template.Child() def __init__(self, **kwargs): @@ -103,8 +103,8 @@ class ServerPanel(Adw.Bin): button = Gtk.CheckButton.new_with_label(device.get_name()) if device.is_enabled(): button.set_active(True) - handler = button.connect('toggled', - self.on_output_device_toggled, device) + button.connect('toggled', self.on_output_device_toggled, + device) self.output_devices.insert(button, -1) self._output_buttons[device.get_id()] = button @@ -113,4 +113,3 @@ class ServerPanel(Adw.Bin): if id not in device_ids: self.output_devices.remove( self._output_buttons[id].get_parent()) - diff --git a/src/utils.py b/src/utils.py index 1a9e6e7..ed8a04f 100644 --- a/src/utils.py +++ b/src/utils.py @@ -13,6 +13,7 @@ class Utils: CSS_SELECTION = 'selection' STOCK_ICON_DEFAULT = 'image-x-generic-symbolic' + @staticmethod def load_pixbuf(data): loader = GdkPixbuf.PixbufLoader() try: @@ -21,6 +22,7 @@ class Utils: loader.close() return loader.get_pixbuf() + @staticmethod def load_thumbnail(cache, client, album, size): cache_url = cache.create_filename(album) pixbuf = None @@ -38,6 +40,7 @@ class Utils: pixbuf.savev(cache_url, 'jpeg', [], []) return pixbuf + @staticmethod def create_artists_label(album): label = ', '.join(album.get_albumartists()) if album.get_artists(): @@ -45,12 +48,14 @@ class Utils: label, ", ".join(album.get_artists())) return label + @staticmethod def create_length_label(album): minutes = album.get_length() // 60 seconds = album.get_length() - minutes * 60 return locale.gettext("{}:{} minutes").format(minutes, seconds) + @staticmethod def create_track_title(track): title = track.get_title() if track.get_artists(): @@ -58,6 +63,7 @@ class Utils: title, ", ".join(track.get_artists())) return title + @staticmethod def generate_id(values): if type(values) is not list: values = [values] diff --git a/src/window.py b/src/window.py index 1c6d25c..648853d 100644 --- a/src/window.py +++ b/src/window.py @@ -23,10 +23,10 @@ from .zeroconf import ZeroconfProvider class WindowState(GObject.Object): - WIDTH = 'width' - HEIGHT = 'height' - IS_MAXIMIZED = 'is_maximized' - IS_FULLSCREENED = 'is_fullscreened' + PROP_WIDTH = 'width' + PROP_HEIGHT = 'height' + PROP_MAXIMIZED = 'is_maximized' + PROP_FULLSCREENED = 'is_fullscreened' width = GObject.Property(type=int, default=800) height = GObject.Property(type=int, default=600) is_maximized = GObject.Property(type=bool, default=False) @@ -206,16 +206,18 @@ class Window(Adw.ApplicationWindow): self._settings.connect('changed::' + Window.SETTING_SORT_TYPE, self.on_settings_sort_type_changed) self._settings.bind(Window.SETTING_WINDOW_WIDTH, self._state, - WindowState.WIDTH, Gio.SettingsBindFlags.DEFAULT) + WindowState.PROP_WIDTH, + Gio.SettingsBindFlags.DEFAULT) self._settings.bind(Window.SETTING_WINDOW_HEIGHT, self._state, - WindowState.HEIGHT, Gio.SettingsBindFlags.DEFAULT) + WindowState.PROP_HEIGHT, + Gio.SettingsBindFlags.DEFAULT) self._settings.bind(Window.SETTING_WINDOW_MAXIMIZED, self._state, - WindowState.IS_MAXIMIZED, + WindowState.PROP_MAXIMIZED, Gio.SettingsBindFlags.DEFAULT) # Actions self.set_default_size(self._state.width, self._state.height) - if self._state.get_property(WindowState.IS_MAXIMIZED): + if self._state.get_property(WindowState.PROP_MAXIMIZED): self.maximize() self.content_stack.set_visible_child(self._connection_panel) if self._settings.get_boolean(Window.SETTING_CONNECTED): @@ -274,7 +276,7 @@ class Window(Adw.ApplicationWindow): def on_menu_toggle_fullscreen(self, action, value): self.panel_stack.set_visible_child(self._cover_panel) - if not self._state.get_property(WindowState.IS_FULLSCREENED): + if not self._state.get_property(WindowState.PROP_FULLSCREENED): self.fullscreen() else: self.unfullscreen() @@ -290,14 +292,14 @@ class Window(Adw.ApplicationWindow): height = self.get_size(Gtk.Orientation.VERTICAL) if width > 0: self._cover_panel.set_width(width) - if not self._state.get_property(WindowState.IS_MAXIMIZED): - self._state.set_property(WindowState.WIDTH, width) - self._state.set_property(WindowState.HEIGHT, height) + if not self._state.get_property(WindowState.PROP_MAXIMIZED): + self._state.set_property(WindowState.PROP_WIDTH, width) + self._state.set_property(WindowState.PROP_HEIGHT, height) GObject.idle_add(self._playlist_panel.set_size, width, height) GObject.idle_add(self._library_panel.set_size, width, height) def on_maximized(self, widget, maximized): - self._state.set_property(WindowState.IS_MAXIMIZED, maximized is True) + self._state.set_property(WindowState.PROP_MAXIMIZED, maximized is True) def on_fullscreened(self, widget, fullscreened): self._fullscreen(self.is_fullscreen()) @@ -371,7 +373,7 @@ class Window(Adw.ApplicationWindow): self._mcg.enable_output_device(device, enabled) def on_cover_panel_toggle_fullscreen(self, widget): - if not self._state.get_property(WindowState.IS_FULLSCREENED): + if not self._state.get_property(WindowState.PROP_FULLSCREENED): self.fullscreen() else: self.unfullscreen() @@ -433,7 +435,10 @@ class Window(Adw.ApplicationWindow): bitrate, error): # Album GObject.idle_add(self._cover_panel.set_album, album) - if not album and self._state.get_property(WindowState.IS_FULLSCREENED): + if ( + not album + and self._state.get_property(WindowState.PROP_FULLSCREENED) + ): self._fullscreen(False) # State if state == 'play': @@ -534,10 +539,11 @@ class Window(Adw.ApplicationWindow): def _fullscreen(self, fullscreened_new): if fullscreened_new != self._state.get_property( - WindowState.IS_FULLSCREENED): - self._state.set_property(WindowState.IS_FULLSCREENED, + WindowState.PROP_FULLSCREENED + ): + self._state.set_property(WindowState.PROP_FULLSCREENED, fullscreened_new) - if self._state.get_property(WindowState.IS_FULLSCREENED): + if self._state.get_property(WindowState.PROP_FULLSCREENED): self.headerbar.hide() self._cover_panel.set_fullscreen(True) self.set_cursor(Gdk.Cursor.new_from_name("none", None)) diff --git a/src/zeroconf.py b/src/zeroconf.py index 5fa3363..6dc1f00 100644 --- a/src/zeroconf.py +++ b/src/zeroconf.py @@ -29,7 +29,6 @@ class ZeroconfProvider(client.Base): def on_new_service(self, browser, interface, protocol, name, type, domain, flags): - #if not (flags & Avahi.LookupResultFlags.GA_LOOKUP_RESULT_LOCAL): service_resolver = Avahi.ServiceResolver( interface=interface, protocol=protocol,