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