Compare commits

...

5 Commits

Author SHA1 Message Date
Martin Wagner
c148b2c678 notification fixes 2022-03-25 22:21:53 +01:00
Martin Wagner
9b841df3e4 avoid unintended button reveal 2022-03-25 22:14:27 +01:00
Martin Wagner
a90b45438f improved .cue support 2022-03-25 18:41:31 +01:00
Martin Wagner
cbc5735d52 harmonized SongPopover and AlbumPopover 2022-03-25 17:26:13 +01:00
Martin Wagner
8bc82fcd13 minor cleanups 2022-03-23 17:23:56 +01:00

View File

@ -291,7 +291,7 @@ class MPRISInterface: # TODO emit Seeked if needed
def _set_volume(self, value): def _set_volume(self, value):
if self._client.connected(): if self._client.connected():
if value >= 0 and value <= 1: if 0 <= value <= 1:
self._client.setvol(int(value * 100)) self._client.setvol(int(value * 100))
def _get_position(self): def _get_position(self):
@ -388,7 +388,7 @@ class MPRISInterface: # TODO emit Seeked if needed
if str(trackid).split("/")[-1] != song["id"]: if str(trackid).split("/")[-1] != song["id"]:
return return
mpd_pos=position/1000000 mpd_pos=position/1000000
if mpd_pos >= 0 and mpd_pos <= float(song["duration"]): if 0 <= mpd_pos <= float(song["duration"]):
self._client.seekcur(str(mpd_pos)) self._client.seekcur(str(mpd_pos))
def OpenUri(self, uri): def OpenUri(self, uri):
@ -423,12 +423,10 @@ class MPRISInterface: # TODO emit Seeked if needed
if "duration" in song: if "duration" in song:
self._metadata["mpris:length"]=GLib.Variant("x", float(song["duration"])*1000000) self._metadata["mpris:length"]=GLib.Variant("x", float(song["duration"])*1000000)
if "file" in song: if "file" in song:
song_file=song["file"] if "://" in (song_file:=song["file"]): # remote file
if "://" in song_file: # remote file
self._metadata["xesam:url"]=GLib.Variant("s", song_file) self._metadata["xesam:url"]=GLib.Variant("s", song_file)
else: else:
song_path=self._client.get_absolute_path(song_file) if (song_path:=self._client.get_absolute_path(song_file)) is not None:
if song_path is not None:
self._metadata["xesam:url"]=GLib.Variant("s", Gio.File.new_for_path(song_path).get_uri()) self._metadata["xesam:url"]=GLib.Variant("s", Gio.File.new_for_path(song_path).get_uri())
if isinstance(self._client.current_cover, FileCover): if isinstance(self._client.current_cover, FileCover):
self._metadata["mpris:artUrl"]=GLib.Variant("s", Gio.File.new_for_path(self._client.current_cover).get_uri()) self._metadata["mpris:artUrl"]=GLib.Variant("s", Gio.File.new_for_path(self._client.current_cover).get_uri())
@ -464,8 +462,7 @@ class MPRISInterface: # TODO emit Seeked if needed
self._update_property(self._MPRIS_PLAYER_IFACE, "Shuffle") self._update_property(self._MPRIS_PLAYER_IFACE, "Shuffle")
def _on_reconnected(self, *args): def _on_reconnected(self, *args):
properties=("CanPlay","CanPause","CanSeek") for p in ("CanPlay","CanPause","CanSeek"):
for p in properties:
self._update_property(self._MPRIS_PLAYER_IFACE, p) self._update_property(self._MPRIS_PLAYER_IFACE, p)
def _on_disconnected(self, *args): def _on_disconnected(self, *args):
@ -476,8 +473,7 @@ class MPRISInterface: # TODO emit Seeked if needed
def _on_connection_error(self, *args): def _on_connection_error(self, *args):
self._metadata={} self._metadata={}
self._tmp_cover_file.replace_contents(b"", None, False, Gio.FileCreateFlags.NONE, None) self._tmp_cover_file.replace_contents(b"", None, False, Gio.FileCreateFlags.NONE, None)
properties=("PlaybackStatus","CanGoNext","CanGoPrevious","Metadata","Volume","LoopStatus","Shuffle","CanPlay","CanPause","CanSeek") for p in ("PlaybackStatus","CanGoNext","CanGoPrevious","Metadata","Volume","LoopStatus","Shuffle","CanPlay","CanPause","CanSeek"):
for p in properties:
self._update_property(self._MPRIS_PLAYER_IFACE, p) self._update_property(self._MPRIS_PLAYER_IFACE, p)
###################### ######################
@ -879,7 +875,7 @@ class Client(MPDClient):
def get_absolute_path(self, uri): def get_absolute_path(self, uri):
if self.lib_path is not None: if self.lib_path is not None:
path=os.path.join(self.lib_path, uri) path=re.sub(r"(.*\.cue)\/track\d+$", r"\1", os.path.join(self.lib_path, uri), flags=re.IGNORECASE)
if os.path.isfile(path): if os.path.isfile(path):
return path return path
else: else:
@ -1058,21 +1054,21 @@ class SettingsList(Gtk.Frame):
class ViewSettings(SettingsList): class ViewSettings(SettingsList):
def __init__(self, settings): def __init__(self, settings):
super().__init__() super().__init__()
toggle_data=[ toggle_data=(
(_("Use Client-side decoration"), "use-csd", True), (_("Use Client-side decoration"), "use-csd", True),
(_("Show stop button"), "show-stop", False), (_("Show stop button"), "show-stop", False),
(_("Show audio format"), "show-audio-format", False), (_("Show audio format"), "show-audio-format", False),
(_("Show lyrics button"), "show-lyrics-button", False), (_("Show lyrics button"), "show-lyrics-button", False),
(_("Place playlist at the side"), "playlist-right", False), (_("Place playlist at the side"), "playlist-right", False),
] )
for label, key, restart_required in toggle_data: for label, key, restart_required in toggle_data:
row=ToggleRow(label, settings, key, restart_required) row=ToggleRow(label, settings, key, restart_required)
self.append(row) self.append(row)
int_data=[ int_data=(
(_("Main cover size"), (100, 1200, 10), "track-cover"), (_("Main cover size"), (100, 1200, 10), "track-cover"),
(_("Album view cover size"), (50, 600, 10), "album-cover"), (_("Album view cover size"), (50, 600, 10), "album-cover"),
(_("Action bar icon size"), (16, 64, 2), "icon-size"), (_("Action bar icon size"), (16, 64, 2), "icon-size"),
] )
for label, (vmin, vmax, step), key in int_data: for label, (vmin, vmax, step), key in int_data:
row=IntRow(label, vmin, vmax, step, settings, key) row=IntRow(label, vmin, vmax, step, settings, key)
self.append(row) self.append(row)
@ -1080,14 +1076,14 @@ class ViewSettings(SettingsList):
class BehaviorSettings(SettingsList): class BehaviorSettings(SettingsList):
def __init__(self, settings): def __init__(self, settings):
super().__init__() super().__init__()
toggle_data=[ toggle_data=(
(_("Support “MPRIS”"), "mpris", True), (_("Support “MPRIS”"), "mpris", True),
(_("Sort albums by year"), "sort-albums-by-year", False), (_("Sort albums by year"), "sort-albums-by-year", False),
(_("Send notification on title change"), "send-notify", False), (_("Send notification on title change"), "send-notify", False),
(_("Play selected albums and titles immediately"), "force-mode", False), (_("Play selected albums and titles immediately"), "force-mode", False),
(_("Rewind via previous button"), "rewind-mode", False), (_("Rewind via previous button"), "rewind-mode", False),
(_("Stop playback on quit"), "stop-on-quit", False), (_("Stop playback on quit"), "stop-on-quit", False),
] )
for label, key, restart_required in toggle_data: for label, key, restart_required in toggle_data:
row=ToggleRow(label, settings, key, restart_required) row=ToggleRow(label, settings, key, restart_required)
self.append(row) self.append(row)
@ -1315,31 +1311,25 @@ class SongPopover(Gtk.Popover):
self._client=client self._client=client
self._rect=Gdk.Rectangle() self._rect=Gdk.Rectangle()
self._uri=None self._uri=None
box=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, border_width=6, halign=Gtk.Align.END, valign=Gtk.Align.END) hbox=Gtk.Box(spacing=6)
# open-with button # open-with button
open_button=Gtk.Button(image=Gtk.Image.new_from_icon_name("folder-open-symbolic", Gtk.IconSize.BUTTON), self._open_button=Gtk.Button(image=Gtk.Image.new_from_icon_name("folder-open-symbolic", Gtk.IconSize.BUTTON),
tooltip_text=_("Show in file manager")) tooltip_text=_("Show in file manager"))
open_button.get_style_context().add_class("osd") hbox.pack_end(self._open_button, False, False, 0)
# open button revealer
self._open_button_revealer=Gtk.Revealer(child=open_button, transition_duration=0)
box.pack_end(self._open_button_revealer, False, False, 0)
# buttons # buttons
if show_buttons: if show_buttons:
button_box=Gtk.ButtonBox(orientation=Gtk.Orientation.VERTICAL, layout_style=Gtk.ButtonBoxStyle.EXPAND, halign=Gtk.Align.END) button_box=Gtk.ButtonBox(layout_style=Gtk.ButtonBoxStyle.EXPAND)
data=((_("Append"), "list-add-symbolic", "append"), data=((_("Append"), "list-add-symbolic", "append"),
(_("Play"), "media-playback-start-symbolic", "play"), (_("Play"), "media-playback-start-symbolic", "play"),
(_("Enqueue"), "insert-object-symbolic", "enqueue") (_("Enqueue"), "insert-object-symbolic", "enqueue")
) )
for tooltip, icon, mode in data: for tooltip, icon, mode in data:
button=Gtk.Button(tooltip_text=tooltip, image=Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON)) button=Gtk.Button(tooltip_text=tooltip, image=Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON))
button.get_style_context().add_class("osd")
button.connect("clicked", self._on_button_clicked, mode) button.connect("clicked", self._on_button_clicked, mode)
button_box.pack_start(button, True, True, 0) button_box.pack_start(button, True, True, 0)
button_box_revealer=Gtk.Revealer(child=button_box, transition_duration=0, reveal_child=True) # needed for tooltips hbox.pack_end(button_box, False, False, 0)
box.pack_end(button_box_revealer, False, False, 0)
# treeview # treeview
# (tag, display-value, tooltip) # (tag, display-value, tooltip)
@ -1361,17 +1351,16 @@ class SongPopover(Gtk.Popover):
self._scroll=Gtk.ScrolledWindow(child=self._treeview, propagate_natural_height=True) self._scroll=Gtk.ScrolledWindow(child=self._treeview, propagate_natural_height=True)
self._scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self._scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
# overlay
overlay=Gtk.Overlay(child=self._scroll)
overlay.add_overlay(box)
# connect # connect
open_button.connect("clicked", self._on_open_button_clicked) self._open_button.connect("clicked", self._on_open_button_clicked)
# packing # packing
frame=Gtk.Frame(child=overlay, border_width=6) frame=Gtk.Frame(child=self._scroll)
self.add(frame) vbox=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, border_width=6, spacing=6)
frame.show_all() vbox.pack_start(frame, True, True, 0)
vbox.pack_end(hbox, False, False, 0)
self.add(vbox)
vbox.show_all()
def open(self, uri, widget, x, y): def open(self, uri, widget, x, y):
self._uri=uri self._uri=uri
@ -1390,7 +1379,7 @@ class SongPopover(Gtk.Popover):
else: else:
self._store.append([tag+":", str(value), GLib.markup_escape_text(str(value))]) self._store.append([tag+":", str(value), GLib.markup_escape_text(str(value))])
abs_path=self._client.get_absolute_path(uri) abs_path=self._client.get_absolute_path(uri)
self._open_button_revealer.set_reveal_child(abs_path is not None) # show open with button when song is on the same computer self._open_button.set_sensitive(abs_path is not None) # show open with button when song is on the same computer
self.popup() self.popup()
self._treeview.columns_autosize() self._treeview.columns_autosize()
@ -1438,7 +1427,7 @@ class SongsList(TreeView):
self._selection=self.get_selection() self._selection=self.get_selection()
# buttons # buttons
self.buttons=Gtk.ButtonBox(layout_style=Gtk.ButtonBoxStyle.EXPAND, halign=Gtk.Align.END) self.buttons=Gtk.ButtonBox(layout_style=Gtk.ButtonBoxStyle.EXPAND)
data=((_("Add all titles to playlist"), "list-add-symbolic", "append"), data=((_("Add all titles to playlist"), "list-add-symbolic", "append"),
(_("Directly play all titles"), "media-playback-start-symbolic", "play"), (_("Directly play all titles"), "media-playback-start-symbolic", "play"),
(_("Append all titles after the currently playing track and clear the playlist from all other songs"), (_("Append all titles after the currently playing track and clear the playlist from all other songs"),
@ -1468,8 +1457,7 @@ class SongsList(TreeView):
self._client.files_to_playlist([self._store[path][3]]) self._client.files_to_playlist([self._store[path][3]])
def _on_button_press_event(self, widget, event): def _on_button_press_event(self, widget, event):
path_re=widget.get_path_at_pos(int(event.x), int(event.y)) if (path_re:=widget.get_path_at_pos(int(event.x), int(event.y))) is not None:
if path_re is not None:
path=path_re[0] path=path_re[0]
if event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS: if event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS:
self._client.files_to_playlist([self._store[path][3]], "play") self._client.files_to_playlist([self._store[path][3]], "play")
@ -1485,14 +1473,12 @@ class SongsList(TreeView):
self.emit("button-clicked") self.emit("button-clicked")
def show_info(self): def show_info(self):
treeview, treeiter=self._selection.get_selected() if (treeiter:=self._selection.get_selected()[1]) is not None:
if treeiter is not None:
path=self._store.get_path(treeiter) path=self._store.get_path(treeiter)
self._song_popover.open(self._store[path][3], self, *self.get_popover_point(path)) self._song_popover.open(self._store[path][3], self, *self.get_popover_point(path))
def add_to_playlist(self, mode): def add_to_playlist(self, mode):
treeview, treeiter=self._selection.get_selected() if (treeiter:=self._selection.get_selected()[1]) is not None:
if treeiter is not None:
self._client.files_to_playlist([self._store.get_value(treeiter, 3)], mode) self._client.files_to_playlist([self._store.get_value(treeiter, 3)], mode)
class AlbumPopover(Gtk.Popover): class AlbumPopover(Gtk.Popover):
@ -1521,9 +1507,9 @@ class AlbumPopover(Gtk.Popover):
hbox=Gtk.Box(spacing=6) hbox=Gtk.Box(spacing=6)
hbox.pack_end(self._songs_list.buttons, False, False, 0) hbox.pack_end(self._songs_list.buttons, False, False, 0)
hbox.pack_start(self._label, False, False, 6) hbox.pack_start(self._label, False, False, 6)
vbox.pack_start(hbox, False, False, 0)
frame=Gtk.Frame(child=self._scroll) frame=Gtk.Frame(child=self._scroll)
vbox.pack_end(frame, True, True, 0) vbox.pack_start(frame, True, True, 0)
vbox.pack_end(hbox, False, False, 0)
self.add(vbox) self.add(vbox)
vbox.show_all() vbox.show_all()
@ -1913,30 +1899,24 @@ class ArtistList(SelectionList):
self.set_items(filtered_artists) self.set_items(filtered_artists)
if genre is not None: if genre is not None:
self.select_all() self.select_all()
elif (song:=self._client.currentsong()):
artist=song["albumartist"][0]
self.select(artist)
elif self.length() > 0:
self.select_path(Gtk.TreePath(1))
else: else:
song=self._client.currentsong() self.select_path(Gtk.TreePath(0))
if song:
artist=song["albumartist"][0]
self.select(artist)
else:
if self.length() > 0:
self.select_path(Gtk.TreePath(1))
else:
self.select_path(Gtk.TreePath(0))
def _on_button_press_event(self, widget, event): def _on_button_press_event(self, widget, event):
if ((event.button in (2,3) and event.type == Gdk.EventType.BUTTON_PRESS) if (path_re:=widget.get_path_at_pos(int(event.x), int(event.y))) is not None:
or (event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS)): path=path_re[0]
path_re=widget.get_path_at_pos(int(event.x), int(event.y)) artist,genre=self.get_artist_at_path(path)
if path_re is not None: if event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS:
path=path_re[0] self._client.artist_to_playlist(artist, genre, "play")
artist,genre=self.get_artist_at_path(path) elif event.button == 2 and event.type == Gdk.EventType.BUTTON_PRESS:
if event.button == 1: self._client.artist_to_playlist(artist, genre, "append")
self._client.artist_to_playlist(artist, genre, "play") elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
elif event.button == 2: self._artist_popover.open(artist, genre, self, event.x, event.y)
self._client.artist_to_playlist(artist, genre, "append")
elif event.button == 3:
self._artist_popover.open(artist, genre, self, event.x, event.y)
def get_artist_at_path(self, path): def get_artist_at_path(self, path):
genre=self.genre_list.get_item_selected() genre=self.genre_list.get_item_selected()
@ -1954,8 +1934,7 @@ class ArtistList(SelectionList):
self._client.artist_to_playlist(artist, genre, mode) self._client.artist_to_playlist(artist, genre, mode)
def show_info(self): def show_info(self):
treeview, treeiter=self._selection.get_selected() if (treeiter:=self._selection.get_selected()[1]) is not None:
if treeiter is not None:
path=self._store.get_path(treeiter) path=self._store.get_path(treeiter)
artist,genre=self.get_artist_at_path(path) artist,genre=self.get_artist_at_path(path)
self._artist_popover.open(artist, genre, self, *self.get_popover_point(path)) self._artist_popover.open(artist, genre, self, *self.get_popover_point(path))
@ -2254,8 +2233,7 @@ class Browser(Gtk.Paned):
self.pack2(self.paned1, True, False) self.pack2(self.paned1, True, False)
def back_to_current_album(self, force=False): def back_to_current_album(self, force=False):
song=self._client.currentsong() if (song:=self._client.currentsong()):
if song:
artist,genre=self._artist_list.get_artist_selected() artist,genre=self._artist_list.get_artist_selected()
# deactivate genre filter to show all artists (if needed) # deactivate genre filter to show all artists (if needed)
if song["genre"][0] != genre or force: if song["genre"][0] != genre or force:
@ -2389,8 +2367,7 @@ class PlaylistsView(TreeView):
self._client.stored_to_playlist(self._store[path][1]) self._client.stored_to_playlist(self._store[path][1])
def _on_button_press_event(self, widget, event): def _on_button_press_event(self, widget, event):
path_re=widget.get_path_at_pos(int(event.x), int(event.y)) if (path_re:=widget.get_path_at_pos(int(event.x), int(event.y))) is not None:
if path_re is not None:
path=path_re[0] path=path_re[0]
if event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS: if event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS:
self._client.stored_to_playlist(self._store[path][1], "play") self._client.stored_to_playlist(self._store[path][1], "play")
@ -2408,14 +2385,12 @@ class PlaylistsView(TreeView):
self.columns_autosize() self.columns_autosize()
def show_info(self): def show_info(self):
treeview, treeiter=self._selection.get_selected() if (treeiter:=self._selection.get_selected()[1]) is not None:
if treeiter is not None:
path=self._store.get_path(treeiter) path=self._store.get_path(treeiter)
self._playlist_popover.open(self._store[path][1], self, *self.get_popover_point(path)) self._playlist_popover.open(self._store[path][1], self, *self.get_popover_point(path))
def add_to_playlist(self, mode): def add_to_playlist(self, mode):
treeview, treeiter=self._selection.get_selected() if (treeiter:=self._selection.get_selected()[1]) is not None:
if treeiter is not None:
self._client.stored_to_playlist(self._store.get_value(treeiter, 1), mode) self._client.stored_to_playlist(self._store.get_value(treeiter, 1), mode)
class PlaylistsPopover(Gtk.Popover): class PlaylistsPopover(Gtk.Popover):
@ -2557,9 +2532,8 @@ class PlaylistView(TreeView):
self.set_property("selected-path", None) self.set_property("selected-path", None)
def scroll_to_selected_title(self): def scroll_to_selected_title(self):
treeview, treeiter=self._selection.get_selected() if (treeiter:=self._selection.get_selected()[1]) is not None:
if treeiter is not None: path=self._store.get_path(treeiter)
path=treeview.get_path(treeiter)
self.scroll_to_cell(path, None, True, 0.25) self.scroll_to_cell(path, None, True, 0.25)
def _refresh_selection(self): # Gtk.TreePath(len(self._store) is used to generate an invalid TreePath (needed to unset cursor) def _refresh_selection(self): # Gtk.TreePath(len(self._store) is used to generate an invalid TreePath (needed to unset cursor)
@ -2577,8 +2551,7 @@ class PlaylistView(TreeView):
self.label.set_label(text) self.label.set_label(text)
def _on_button_press_event(self, widget, event): def _on_button_press_event(self, widget, event):
path_re=widget.get_path_at_pos(int(event.x), int(event.y)) if (path_re:=widget.get_path_at_pos(int(event.x), int(event.y))) is not None:
if path_re is not None:
path=path_re[0] path=path_re[0]
if event.button == 2 and event.type == Gdk.EventType.BUTTON_PRESS: if event.button == 2 and event.type == Gdk.EventType.BUTTON_PRESS:
self._store.remove(self._store.get_iter(path)) self._store.remove(self._store.get_iter(path))
@ -2588,8 +2561,7 @@ class PlaylistView(TreeView):
def _on_key_release_event(self, widget, event): def _on_key_release_event(self, widget, event):
if event.keyval == Gdk.keyval_from_name("Delete"): if event.keyval == Gdk.keyval_from_name("Delete"):
treeview, treeiter=self._selection.get_selected() if (treeiter:=self._selection.get_selected()[1]) is not None:
if treeiter is not None:
try: try:
self._store.remove(treeiter) self._store.remove(treeiter)
except: except:
@ -2663,7 +2635,7 @@ class PlaylistView(TreeView):
if playlist_length == 0: if playlist_length == 0:
self._set_playlist_info("") self._set_playlist_info("")
else: else:
duration=Duration(sum([row[4] for row in self._store])) duration=Duration(sum((row[4] for row in self._store)))
translated_string=ngettext("{number} song ({duration})", "{number} songs ({duration})", playlist_length) translated_string=ngettext("{number} song ({duration})", "{number} songs ({duration})", playlist_length)
self._set_playlist_info(translated_string.format(number=playlist_length, duration=duration)) self._set_playlist_info(translated_string.format(number=playlist_length, duration=duration))
self._refresh_selection() self._refresh_selection()
@ -2686,8 +2658,7 @@ class PlaylistView(TreeView):
self.set_sensitive(True) self.set_sensitive(True)
def show_info(self): def show_info(self):
treeview, treeiter=self._selection.get_selected() if (treeiter:=self._selection.get_selected()[1]) is not None:
if treeiter is not None:
path=self._store.get_path(treeiter) path=self._store.get_path(treeiter)
self._song_popover.open(self._store[path][3], self, *self.get_popover_point(path)) self._song_popover.open(self._store[path][3], self, *self.get_popover_point(path))
@ -2712,7 +2683,6 @@ class PlaylistWindow(Gtk.Overlay):
# connect # connect
self._back_to_current_song_button.connect("clicked", self._on_back_to_current_song_button_clicked) self._back_to_current_song_button.connect("clicked", self._on_back_to_current_song_button_clicked)
scroll.get_vadjustment().connect("value-changed", self._on_show_hide_back_button) scroll.get_vadjustment().connect("value-changed", self._on_show_hide_back_button)
self._treeview.connect("notify::selected-path", self._on_show_hide_back_button)
settings.bind("mini-player", self, "no-show-all", Gio.SettingsBindFlags.GET) settings.bind("mini-player", self, "no-show-all", Gio.SettingsBindFlags.GET)
settings.bind("mini-player", self, "visible", Gio.SettingsBindFlags.INVERT_BOOLEAN|Gio.SettingsBindFlags.GET) settings.bind("mini-player", self, "visible", Gio.SettingsBindFlags.INVERT_BOOLEAN|Gio.SettingsBindFlags.GET)
@ -2785,9 +2755,8 @@ class LyricsWindow(Gtk.ScrolledWindow):
self.add(self._text_view) self.add(self._text_view)
def enable(self, *args): def enable(self, *args):
current_song=self._client.currentsong() if (song:=self._client.currentsong()):
if current_song: if song["file"] != self._displayed_song_file:
if current_song["file"] != self._displayed_song_file:
self._refresh() self._refresh()
else: else:
if self._displayed_song_file is not None: if self._displayed_song_file is not None:
@ -2808,10 +2777,10 @@ class LyricsWindow(Gtk.ScrolledWindow):
raise ValueError("Not found") raise ValueError("Not found")
return parser.text.strip("\n ") return parser.text.strip("\n ")
def _display_lyrics(self, current_song): def _display_lyrics(self, song):
idle_add(self._text_buffer.set_text, _("searching…"), -1) idle_add(self._text_buffer.set_text, _("searching…"), -1)
try: try:
text=self._get_lyrics(current_song["title"][0], current_song["artist"][0]) text=self._get_lyrics(song["title"][0], song["artist"][0])
except urllib.error.URLError: except urllib.error.URLError:
self._displayed_song_file=None self._displayed_song_file=None
text=_("connection error") text=_("connection error")
@ -2820,12 +2789,11 @@ class LyricsWindow(Gtk.ScrolledWindow):
idle_add(self._text_buffer.set_text, text, -1) idle_add(self._text_buffer.set_text, text, -1)
def _refresh(self, *args): def _refresh(self, *args):
current_song=self._client.currentsong() if (song:=self._client.currentsong()):
if current_song: self._displayed_song_file=song["file"]
self._displayed_song_file=current_song["file"]
update_thread=threading.Thread( update_thread=threading.Thread(
target=self._display_lyrics, target=self._display_lyrics,
kwargs={"current_song": current_song}, kwargs={"song": song},
daemon=True daemon=True
) )
update_thread.start() update_thread.start()
@ -2857,8 +2825,7 @@ class CoverEventBox(Gtk.EventBox):
window.begin_move_drag(1, event.x_root, event.y_root, Gdk.CURRENT_TIME) window.begin_move_drag(1, event.x_root, event.y_root, Gdk.CURRENT_TIME)
else: else:
if self._client.connected(): if self._client.connected():
song=self._client.currentsong() if (song:=self._client.currentsong()):
if song:
tags=(song["albumartist"][0], song["album"][0], song["date"][0]) tags=(song["albumartist"][0], song["album"][0], song["date"][0])
if event.button == 1 and event.type == Gdk.EventType.BUTTON_PRESS: if event.button == 1 and event.type == Gdk.EventType.BUTTON_PRESS:
self._client.album_to_playlist(*tags) self._client.album_to_playlist(*tags)
@ -3170,9 +3137,8 @@ class AudioFormat(Gtk.Box):
self._brate_label.set_text(brate) self._brate_label.set_text(brate)
def _on_song_changed(self, *args): def _on_song_changed(self, *args):
current_song=self._client.currentsong() if (song:=self._client.currentsong()):
if current_song: file_type=song["file"].split(".")[-1].split("/")[0].upper()
file_type=current_song["file"].split(".")[-1].split("/")[0].upper()
self._separator_label.set_text("kbs • ") self._separator_label.set_text("kbs • ")
self._file_type_label.set_text(file_type) self._file_type_label.set_text(file_type)
else: else:
@ -3247,8 +3213,7 @@ class PlaybackOptions(Gtk.ButtonBox):
def _on_single_button_press_event(self, widget, event): def _on_single_button_press_event(self, widget, event):
if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS: if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
state=self._client.status()["single"] if self._client.status()["single"] == "oneshot":
if state == "oneshot":
self._client.single("0") self._client.single("0")
else: else:
self._client.single("oneshot") self._client.single("oneshot")
@ -3718,8 +3683,7 @@ class MainWindow(Gtk.ApplicationWindow):
self._browser.back_to_current_album(force=True) self._browser.back_to_current_album(force=True)
def _on_song_changed(self, *args): def _on_song_changed(self, *args):
song=self._client.currentsong() if (song:=self._client.currentsong()):
if song:
album=song.get_album_with_date() album=song.get_album_with_date()
title="".join(filter(None, (song["title"][0], str(song["artist"])))) title="".join(filter(None, (song["title"][0], str(song["artist"]))))
if self._use_csd: if self._use_csd:
@ -3730,8 +3694,8 @@ class MainWindow(Gtk.ApplicationWindow):
if self._settings.get_boolean("send-notify"): if self._settings.get_boolean("send-notify"):
if not self.is_active() and self._client.status()["state"] == "play": if not self.is_active() and self._client.status()["state"] == "play":
notify=Gio.Notification() notify=Gio.Notification()
notify.set_title(str(song["title"])) notify.set_title(title)
notify.set_body(f"{song['artist']}\n{album}") notify.set_body(album)
if isinstance(self._client.current_cover, FileCover): if isinstance(self._client.current_cover, FileCover):
notify.set_icon(Gio.FileIcon.new(Gio.File.new_for_path(self._client.current_cover))) notify.set_icon(Gio.FileIcon.new(Gio.File.new_for_path(self._client.current_cover)))
elif isinstance(self._client.current_cover, BinaryCover): elif isinstance(self._client.current_cover, BinaryCover):
@ -3743,6 +3707,7 @@ class MainWindow(Gtk.ApplicationWindow):
self.set_title("mpdevil") self.set_title("mpdevil")
if self._use_csd: if self._use_csd:
self._header_bar.set_subtitle("") self._header_bar.set_subtitle("")
self.get_application().withdraw_notification("title-change")
def _on_reconnected(self, *args): def _on_reconnected(self, *args):
for action in ("stats","toggle-lyrics","back-to-current-album","toggle-search"): for action in ("stats","toggle-lyrics","back-to-current-album","toggle-search"):
@ -3762,8 +3727,7 @@ class MainWindow(Gtk.ApplicationWindow):
def _on_size_allocate(self, widget, rect): def _on_size_allocate(self, widget, rect):
if not self.is_maximized() and not self._settings.get_boolean("mini-player"): if not self.is_maximized() and not self._settings.get_boolean("mini-player"):
size=self.get_size() if (size:=self.get_size()) != self._size: # prevent unneeded write operations
if size != self._size: # prevent unneeded write operations
self._settings.set_int("width", size[0]) self._settings.set_int("width", size[0])
self._settings.set_int("height", size[1]) self._settings.set_int("height", size[1])
self._size=size self._size=size