|
|
@@ -185,8 +185,8 @@ class MPRISInterface: # TODO emit Seeked if needed
|
|
|
|
"HasTrackList": (GLib.Variant("b", False), None),
|
|
|
|
"HasTrackList": (GLib.Variant("b", False), None),
|
|
|
|
"Identity": (GLib.Variant("s", "mpdevil"), None),
|
|
|
|
"Identity": (GLib.Variant("s", "mpdevil"), None),
|
|
|
|
"DesktopEntry": (GLib.Variant("s", "org.mpdevil.mpdevil"), None),
|
|
|
|
"DesktopEntry": (GLib.Variant("s", "org.mpdevil.mpdevil"), None),
|
|
|
|
"SupportedUriSchemes": (GLib.Variant("s", "None"), None),
|
|
|
|
"SupportedUriSchemes": (GLib.Variant("as", []), None),
|
|
|
|
"SupportedMimeTypes": (GLib.Variant("s", "None"), None)},
|
|
|
|
"SupportedMimeTypes": (GLib.Variant("as", []), None)},
|
|
|
|
self._MPRIS_PLAYER_IFACE:
|
|
|
|
self._MPRIS_PLAYER_IFACE:
|
|
|
|
{"PlaybackStatus": (self._get_playback_status, None),
|
|
|
|
{"PlaybackStatus": (self._get_playback_status, None),
|
|
|
|
"LoopStatus": (self._get_loop_status, self._set_loop_status),
|
|
|
|
"LoopStatus": (self._get_loop_status, self._set_loop_status),
|
|
|
@@ -656,6 +656,7 @@ class Client(MPDClient):
|
|
|
|
self._start_idle_id=None
|
|
|
|
self._start_idle_id=None
|
|
|
|
self.music_directory=None
|
|
|
|
self.music_directory=None
|
|
|
|
self.current_cover=None
|
|
|
|
self.current_cover=None
|
|
|
|
|
|
|
|
self._bus=Gio.bus_get_sync(Gio.BusType.SESSION, None) # used for "show in file manager"
|
|
|
|
|
|
|
|
|
|
|
|
# connect
|
|
|
|
# connect
|
|
|
|
self._settings.connect("changed::socket-connection", lambda *args: self.reconnect())
|
|
|
|
self._settings.connect("changed::socket-connection", lambda *args: self.reconnect())
|
|
|
@@ -868,13 +869,18 @@ class Client(MPDClient):
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def can_show_in_file_manager(self, uri):
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
self._bus.call_sync("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "StartServiceByName",
|
|
|
|
|
|
|
|
GLib.Variant("(su)",("org.freedesktop.FileManager1",0)), GLib.VariantType("(u)"), Gio.DBusCallFlags.NONE, -1, None)
|
|
|
|
|
|
|
|
except GLib.GError:
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
return self.get_absolute_path(uri) is not None
|
|
|
|
|
|
|
|
|
|
|
|
def show_in_file_manager(self, uri):
|
|
|
|
def show_in_file_manager(self, uri):
|
|
|
|
if (path:=self.get_absolute_path(uri)) is not None:
|
|
|
|
file=Gio.File.new_for_path(self.get_absolute_path(uri))
|
|
|
|
file=Gio.File.new_for_path(path)
|
|
|
|
self._bus.call_sync("org.freedesktop.FileManager1", "/org/freedesktop/FileManager1", "org.freedesktop.FileManager1",
|
|
|
|
bus=Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
|
|
|
"ShowItems", GLib.Variant("(ass)", ((file.get_uri(),),"")), None, Gio.DBusCallFlags.NONE, -1, None)
|
|
|
|
proxy=Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None, "org.freedesktop.FileManager1",
|
|
|
|
|
|
|
|
"/org/freedesktop/FileManager1", "org.freedesktop.FileManager1", None)
|
|
|
|
|
|
|
|
proxy.call_sync("ShowItems", GLib.Variant("(ass)", ((file.get_uri(),),"")), Gio.DBusCallFlags.NONE, 500, None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def toggle_play(self):
|
|
|
|
def toggle_play(self):
|
|
|
|
status=self.status()
|
|
|
|
status=self.status()
|
|
|
@@ -1358,7 +1364,7 @@ class SongsList(TreeView):
|
|
|
|
rect=Gdk.Rectangle()
|
|
|
|
rect=Gdk.Rectangle()
|
|
|
|
rect.x,rect.y=x,y
|
|
|
|
rect.x,rect.y=x,y
|
|
|
|
self._menu.set_pointing_to(rect)
|
|
|
|
self._menu.set_pointing_to(rect)
|
|
|
|
self._show_action.set_enabled(self._client.get_absolute_path(uri) is not None)
|
|
|
|
self._show_action.set_enabled(self._client.can_show_in_file_manager(uri))
|
|
|
|
self._menu.popup()
|
|
|
|
self._menu.popup()
|
|
|
|
|
|
|
|
|
|
|
|
def _on_row_activated(self, widget, path, view_column):
|
|
|
|
def _on_row_activated(self, widget, path, view_column):
|
|
|
@@ -2155,23 +2161,20 @@ class PlaylistView(TreeView):
|
|
|
|
self._playlist_version=None
|
|
|
|
self._playlist_version=None
|
|
|
|
self._inserted_path=None # needed for drag and drop
|
|
|
|
self._inserted_path=None # needed for drag and drop
|
|
|
|
|
|
|
|
|
|
|
|
# rest label
|
|
|
|
|
|
|
|
self.label=Gtk.Label(halign=Gtk.Align.CENTER, valign=Gtk.Align.END)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# selection
|
|
|
|
# selection
|
|
|
|
self._selection=self.get_selection()
|
|
|
|
self._selection=self.get_selection()
|
|
|
|
self._selection.set_select_function(self._select_function)
|
|
|
|
self._selection.set_select_function(self._select_function)
|
|
|
|
|
|
|
|
|
|
|
|
# store
|
|
|
|
# store
|
|
|
|
# (track, title, duration, file, search, raw duration)
|
|
|
|
# (track, title, duration, file, search)
|
|
|
|
self._store=Gtk.ListStore(str, str, str, str, str, float)
|
|
|
|
self._store=Gtk.ListStore(str, str, str, str, str)
|
|
|
|
self.set_model(self._store)
|
|
|
|
self.set_model(self._store)
|
|
|
|
|
|
|
|
|
|
|
|
# columns
|
|
|
|
# columns
|
|
|
|
renderer_text=Gtk.CellRendererText(ellipsize=Pango.EllipsizeMode.END, ellipsize_set=True)
|
|
|
|
renderer_text=Gtk.CellRendererText(ellipsize=Pango.EllipsizeMode.END, ellipsize_set=True)
|
|
|
|
attrs=Pango.AttrList()
|
|
|
|
attrs=Pango.AttrList()
|
|
|
|
attrs.insert(Pango.AttrFontFeatures.new("tnum 1"))
|
|
|
|
attrs.insert(Pango.AttrFontFeatures.new("tnum 1"))
|
|
|
|
renderer_text_ralign_tnum=Gtk.CellRendererText(xalign=1, alignment=Pango.Alignment.RIGHT, attributes=attrs)
|
|
|
|
renderer_text_ralign_tnum=Gtk.CellRendererText(xalign=1, attributes=attrs)
|
|
|
|
renderer_text_centered_tnum=Gtk.CellRendererText(xalign=0.5, attributes=attrs)
|
|
|
|
renderer_text_centered_tnum=Gtk.CellRendererText(xalign=0.5, attributes=attrs)
|
|
|
|
columns=(
|
|
|
|
columns=(
|
|
|
|
Gtk.TreeViewColumn(_("No"), renderer_text_centered_tnum, text=0),
|
|
|
|
Gtk.TreeViewColumn(_("No"), renderer_text_centered_tnum, text=0),
|
|
|
@@ -2226,7 +2229,7 @@ class PlaylistView(TreeView):
|
|
|
|
rect=Gdk.Rectangle()
|
|
|
|
rect=Gdk.Rectangle()
|
|
|
|
rect.x,rect.y=x,y
|
|
|
|
rect.x,rect.y=x,y
|
|
|
|
self._menu.set_pointing_to(rect)
|
|
|
|
self._menu.set_pointing_to(rect)
|
|
|
|
self._show_action.set_enabled(self._client.get_absolute_path(uri) is not None)
|
|
|
|
self._show_action.set_enabled(self._client.can_show_in_file_manager(uri))
|
|
|
|
self._menu.popup()
|
|
|
|
self._menu.popup()
|
|
|
|
|
|
|
|
|
|
|
|
def _clear(self, *args):
|
|
|
|
def _clear(self, *args):
|
|
|
@@ -2239,31 +2242,20 @@ class PlaylistView(TreeView):
|
|
|
|
self._store.handler_unblock(self._row_inserted)
|
|
|
|
self._store.handler_unblock(self._row_inserted)
|
|
|
|
self._store.handler_unblock(self._row_deleted)
|
|
|
|
self._store.handler_unblock(self._row_deleted)
|
|
|
|
|
|
|
|
|
|
|
|
def _update_rest_time(self, path):
|
|
|
|
|
|
|
|
int_path=path.get_indices()[0]
|
|
|
|
|
|
|
|
duration=Duration(sum((row[5] for i, row in enumerate(self._store) if i > int_path)))
|
|
|
|
|
|
|
|
self.label.set_markup(f"<small>↓{duration}</small>")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _unset_rest_time(self, path):
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
self._store.set(self._store.get_iter(path), 2, self._store[path][2].split("\n")[0])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _select(self, path):
|
|
|
|
def _select(self, path):
|
|
|
|
self._unselect()
|
|
|
|
self._unselect()
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
self.set_property("selected-path", path)
|
|
|
|
self.set_property("selected-path", path)
|
|
|
|
self._update_rest_time(path)
|
|
|
|
|
|
|
|
self._selection.select_path(path)
|
|
|
|
self._selection.select_path(path)
|
|
|
|
except (IndexError, ValueError): # invalid path # TODO
|
|
|
|
except IndexError: # invalid path
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def _unselect(self):
|
|
|
|
def _unselect(self):
|
|
|
|
if (path:=self.get_property("selected-path")) is not None:
|
|
|
|
if (path:=self.get_property("selected-path")) is not None:
|
|
|
|
self.set_property("selected-path", None)
|
|
|
|
self.set_property("selected-path", None)
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
self._unset_rest_time(path)
|
|
|
|
|
|
|
|
self._selection.unselect_path(path)
|
|
|
|
self._selection.unselect_path(path)
|
|
|
|
except (IndexError, ValueError): # invalid path # TODO
|
|
|
|
except IndexError: # invalid path
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def _delete(self, path):
|
|
|
|
def _delete(self, path):
|
|
|
@@ -2344,14 +2336,12 @@ class PlaylistView(TreeView):
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
treeiter=self._store.get_iter(song["pos"])
|
|
|
|
treeiter=self._store.get_iter(song["pos"])
|
|
|
|
except ValueError:
|
|
|
|
except ValueError:
|
|
|
|
self._store.insert_with_valuesv(-1, range(6), [
|
|
|
|
self._store.insert_with_valuesv(-1, range(5),
|
|
|
|
song["track"][0], title, str(song["duration"]),
|
|
|
|
[song["track"][0], title, str(song["duration"]), song["file"], song["title"][0]]
|
|
|
|
song["file"], song["title"][0], float(song["duration"])
|
|
|
|
)
|
|
|
|
])
|
|
|
|
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
self._store.set(treeiter,
|
|
|
|
self._store.set(treeiter,
|
|
|
|
0, song["track"][0], 1, title, 2, str(song["duration"]),
|
|
|
|
0, song["track"][0], 1, title, 2, str(song["duration"]), 3, song["file"], 4, song["title"][0]
|
|
|
|
3, song["file"], 4, song["title"][0], 5, float(song["duration"])
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.thaw_child_notify()
|
|
|
|
self.thaw_child_notify()
|
|
|
|
for i in reversed(range(int(self._client.status()["playlistlength"]), len(self._store))):
|
|
|
|
for i in reversed(range(int(self._client.status()["playlistlength"]), len(self._store))):
|
|
|
@@ -2395,13 +2385,6 @@ class PlaylistWindow(Gtk.Overlay):
|
|
|
|
self._treeview=PlaylistView(client, settings)
|
|
|
|
self._treeview=PlaylistView(client, settings)
|
|
|
|
scroll=Gtk.ScrolledWindow(child=self._treeview)
|
|
|
|
scroll=Gtk.ScrolledWindow(child=self._treeview)
|
|
|
|
|
|
|
|
|
|
|
|
self._provider=Gtk.CssProvider()
|
|
|
|
|
|
|
|
self._provider.load_from_data(b"label { padding: 1px;background-color: @theme_base_color;border-width: 1px;border-style: solid solid none;border-color: @borders;border-radius: 8px 8px 0 0;}") # red icon
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self._treeview.label.get_style_context().add_provider(self._provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.add_overlay(self._treeview.label)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 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)
|
|
|
|