9 Commits

Author SHA1 Message Date
Martin Wagner
3f42f62be6 Update configure.ac 2020-04-07 18:16:30 +02:00
Martin Wagner
eff74509da fixed typo 2020-04-07 18:10:42 +02:00
Martin Wagner
f8a9aa0fc0 removed MPRIS global variabels 2020-04-07 18:02:43 +02:00
Martin Wagner
03b34d1c29 improved cover fetching 2020-04-07 17:57:16 +02:00
Martin Wagner
86f439c891 removed redundancy 2020-04-07 12:38:38 +02:00
Martin Wagner
d36c4beca4 minor fixes 2020-04-07 01:37:53 +02:00
Martin Wagner
6e88ce3e57 support multiple value tags (fixes #4) 2020-04-07 01:25:53 +02:00
Martin Wagner
0acadd7103 Added link to wiki 2020-04-06 21:20:55 +02:00
Martin Wagner
ed143dab55 fixed bug in 'PlaylistView' 2020-04-04 16:23:09 +02:00
3 changed files with 189 additions and 227 deletions

View File

@@ -17,6 +17,8 @@ Features
- fetches lyrics from the web (based on PyLyrics) - fetches lyrics from the web (based on PyLyrics)
- MPRIS interface (based on mpDris2) - MPRIS interface (based on mpDris2)
See: https://github.com/SoongNoonien/mpdevil/wiki/Usage
Building and installation Building and installation
------------------------- -------------------------

View File

@@ -33,13 +33,13 @@ import gettext
import datetime import datetime
import os import os
import sys import sys
import re
#MPRIS modules #MPRIS modules
import dbus import dbus
import dbus.service import dbus.service
from dbus.mainloop.glib import DBusGMainLoop from dbus.mainloop.glib import DBusGMainLoop
import base64 import base64
import re
DATADIR = '@datadir@' DATADIR = '@datadir@'
NAME = 'mpdevil' NAME = 'mpdevil'
@@ -58,139 +58,6 @@ except locale.Error:
gettext.textdomain(PACKAGE) gettext.textdomain(PACKAGE)
gettext.install(PACKAGE, localedir='@datadir@/locale') gettext.install(PACKAGE, localedir='@datadir@/locale')
# MPRIS allowed metadata tags
allowed_tags = {
'mpris:trackid': dbus.ObjectPath,
'mpris:length': dbus.Int64,
'mpris:artUrl': str,
'xesam:album': str,
'xesam:albumArtist': list,
'xesam:artist': list,
'xesam:asText': str,
'xesam:audioBPM': int,
'xesam:comment': list,
'xesam:composer': list,
'xesam:contentCreated': str,
'xesam:discNumber': int,
'xesam:firstUsed': str,
'xesam:genre': list,
'xesam:lastUsed': str,
'xesam:lyricist': str,
'xesam:title': str,
'xesam:trackNumber': int,
'xesam:url': str,
'xesam:useCount': int,
'xesam:userRating': float,
}
# python dbus bindings don't include annotations and properties
MPRIS2_INTROSPECTION = """<node name="/org/mpris/MediaPlayer2">
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg direction="out" name="xml_data" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg direction="in" name="interface_name" type="s"/>
<arg direction="in" name="property_name" type="s"/>
<arg direction="out" name="value" type="v"/>
</method>
<method name="GetAll">
<arg direction="in" name="interface_name" type="s"/>
<arg direction="out" name="properties" type="a{sv}"/>
</method>
<method name="Set">
<arg direction="in" name="interface_name" type="s"/>
<arg direction="in" name="property_name" type="s"/>
<arg direction="in" name="value" type="v"/>
</method>
<signal name="PropertiesChanged">
<arg name="interface_name" type="s"/>
<arg name="changed_properties" type="a{sv}"/>
<arg name="invalidated_properties" type="as"/>
</signal>
</interface>
<interface name="org.mpris.MediaPlayer2">
<method name="Raise"/>
<method name="Quit"/>
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
<property name="CanQuit" type="b" access="read"/>
<property name="CanRaise" type="b" access="read"/>
<property name="HasTrackList" type="b" access="read"/>
<property name="Identity" type="s" access="read"/>
<property name="DesktopEntry" type="s" access="read"/>
<property name="SupportedUriSchemes" type="as" access="read"/>
<property name="SupportedMimeTypes" type="as" access="read"/>
</interface>
<interface name="org.mpris.MediaPlayer2.Player">
<method name="Next"/>
<method name="Previous"/>
<method name="Pause"/>
<method name="PlayPause"/>
<method name="Stop"/>
<method name="Play"/>
<method name="Seek">
<arg direction="in" name="Offset" type="x"/>
</method>
<method name="SetPosition">
<arg direction="in" name="TrackId" type="o"/>
<arg direction="in" name="Position" type="x"/>
</method>
<method name="OpenUri">
<arg direction="in" name="Uri" type="s"/>
</method>
<signal name="Seeked">
<arg name="Position" type="x"/>
</signal>
<property name="PlaybackStatus" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="LoopStatus" type="s" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="Rate" type="d" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="Shuffle" type="b" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="Metadata" type="a{sv}" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="Volume" type="d" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="Position" type="x" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="MinimumRate" type="d" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="MaximumRate" type="d" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanGoNext" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanGoPrevious" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanPlay" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanPause" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanSeek" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanControl" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
</interface>
</node>"""
class IntEntry(Gtk.SpinButton): class IntEntry(Gtk.SpinButton):
def __init__(self, default, lower, upper, step): def __init__(self, default, lower, upper, step):
Gtk.SpinButton.__init__(self) Gtk.SpinButton.__init__(self)
@@ -230,19 +97,18 @@ class FocusFrame(Gtk.Frame):
self.style_context.remove_provider(self.provider) self.style_context.remove_provider(self.provider)
class Cover(object): class Cover(object):
regex=re.compile(r'^\.?(album|cover|folder|front).*\.(gif|jpeg|jpg|png)$', flags=re.IGNORECASE)
def __init__(self, lib_path, song_file): def __init__(self, lib_path, song_file):
self.lib_path=lib_path or "/" self.lib_path=lib_path or ""
self.path=None self.path=None
if not song_file == None: if not song_file == None:
head_tail=os.path.split(song_file) head, tail=os.path.split(song_file)
if self.lib_path[-1] == "/": song_dir=os.path.join(self.lib_path, head)
path=(self.lib_path+head_tail[0]+"/") if os.path.exists(song_dir):
else: for f in os.listdir(song_dir):
path=(self.lib_path+"/"+head_tail[0]+"/") if self.regex.match(f):
if os.path.exists(path): self.path=os.path.join(song_dir, f)
filelist=[file for file in os.listdir(path) if file.endswith('.jpg') or file.endswith('.png') or file.endswith('.gif')] break
if not filelist == []:
self.path=(path+filelist[0])
def get_pixbuf(self, size): def get_pixbuf(self, size):
if self.path == None: if self.path == None:
@@ -411,6 +277,25 @@ class Client(AutoSettingsClient):
songs=self.find("album", album, "date", year, self.settings.get_artist_type(), artist) songs=self.find("album", album, "date", year, self.settings.get_artist_type(), artist)
self.files_to_playlist([song['file'] for song in songs], append, force) self.files_to_playlist([song['file'] for song in songs], append, force)
def song_to_str_dict(self, song): #converts tags with multiple values to comma separated strings
return_song=song
for tag, value in return_song.items():
if type(value) == list:
return_song[tag]=(', '.join(value))
return return_song
def song_to_first_str_dict(self, song): #extracts the first value of multiple value tags
return_song=song
for tag, value in return_song.items():
if type(value) == list:
return_song[tag]=value[0]
return return_song
def extend_song_for_display(self, song):
base_song={"title": _("Unknown Title"), "track": "0", "disc": "", "artist": _("Unknown Artist"), "album": _("Unknown Album"), "duration": "0.0", "date": "", "genre": ""}
base_song.update(song)
return base_song
def on_reconnected(self, *args): def on_reconnected(self, *args):
self.try_connect_default() self.try_connect_default()
self.emitter.emit("playlist") self.emitter.emit("playlist")
@@ -423,6 +308,139 @@ class MPRISInterface(dbus.service.Object): #TODO emit Seeked if needed
__introspect_interface = "org.freedesktop.DBus.Introspectable" __introspect_interface = "org.freedesktop.DBus.Introspectable"
__prop_interface = dbus.PROPERTIES_IFACE __prop_interface = dbus.PROPERTIES_IFACE
# python dbus bindings don't include annotations and properties
MPRIS2_INTROSPECTION = """<node name="/org/mpris/MediaPlayer2">
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg direction="out" name="xml_data" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg direction="in" name="interface_name" type="s"/>
<arg direction="in" name="property_name" type="s"/>
<arg direction="out" name="value" type="v"/>
</method>
<method name="GetAll">
<arg direction="in" name="interface_name" type="s"/>
<arg direction="out" name="properties" type="a{sv}"/>
</method>
<method name="Set">
<arg direction="in" name="interface_name" type="s"/>
<arg direction="in" name="property_name" type="s"/>
<arg direction="in" name="value" type="v"/>
</method>
<signal name="PropertiesChanged">
<arg name="interface_name" type="s"/>
<arg name="changed_properties" type="a{sv}"/>
<arg name="invalidated_properties" type="as"/>
</signal>
</interface>
<interface name="org.mpris.MediaPlayer2">
<method name="Raise"/>
<method name="Quit"/>
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
<property name="CanQuit" type="b" access="read"/>
<property name="CanRaise" type="b" access="read"/>
<property name="HasTrackList" type="b" access="read"/>
<property name="Identity" type="s" access="read"/>
<property name="DesktopEntry" type="s" access="read"/>
<property name="SupportedUriSchemes" type="as" access="read"/>
<property name="SupportedMimeTypes" type="as" access="read"/>
</interface>
<interface name="org.mpris.MediaPlayer2.Player">
<method name="Next"/>
<method name="Previous"/>
<method name="Pause"/>
<method name="PlayPause"/>
<method name="Stop"/>
<method name="Play"/>
<method name="Seek">
<arg direction="in" name="Offset" type="x"/>
</method>
<method name="SetPosition">
<arg direction="in" name="TrackId" type="o"/>
<arg direction="in" name="Position" type="x"/>
</method>
<method name="OpenUri">
<arg direction="in" name="Uri" type="s"/>
</method>
<signal name="Seeked">
<arg name="Position" type="x"/>
</signal>
<property name="PlaybackStatus" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="LoopStatus" type="s" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="Rate" type="d" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="Shuffle" type="b" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="Metadata" type="a{sv}" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="Volume" type="d" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="Position" type="x" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="MinimumRate" type="d" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="MaximumRate" type="d" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanGoNext" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanGoPrevious" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanPlay" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanPause" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanSeek" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
</property>
<property name="CanControl" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
</interface>
</node>"""
# MPRIS allowed metadata tags
allowed_tags = {
'mpris:trackid': dbus.ObjectPath,
'mpris:length': dbus.Int64,
'mpris:artUrl': str,
'xesam:album': str,
'xesam:albumArtist': list,
'xesam:artist': list,
'xesam:asText': str,
'xesam:audioBPM': int,
'xesam:comment': list,
'xesam:composer': list,
'xesam:contentCreated': str,
'xesam:discNumber': int,
'xesam:firstUsed': str,
'xesam:genre': list,
'xesam:lastUsed': str,
'xesam:lyricist': str,
'xesam:title': str,
'xesam:trackNumber': int,
'xesam:url': str,
'xesam:useCount': int,
'xesam:userRating': float,
}
def __init__(self, window, client, settings): def __init__(self, window, client, settings):
dbus.service.Object.__init__(self, dbus.SessionBus(), "/org/mpris/MediaPlayer2") dbus.service.Object.__init__(self, dbus.SessionBus(), "/org/mpris/MediaPlayer2")
self._name = "org.mpris.MediaPlayer2.mpdevil" self._name = "org.mpris.MediaPlayer2.mpdevil"
@@ -539,7 +557,7 @@ class MPRISInterface(dbus.service.Object): #TODO emit Seeked if needed
# Cast self.metadata to the correct type, or discard it # Cast self.metadata to the correct type, or discard it
for key, value in self.metadata.items(): for key, value in self.metadata.items():
try: try:
self.metadata[key] = allowed_tags[key](value) self.metadata[key] = self.allowed_tags[key](value)
except ValueError: except ValueError:
del self.metadata[key] del self.metadata[key]
@@ -663,7 +681,7 @@ class MPRISInterface(dbus.service.Object): #TODO emit Seeked if needed
@dbus.service.method(__introspect_interface) @dbus.service.method(__introspect_interface)
def Introspect(self): def Introspect(self):
return MPRIS2_INTROSPECTION return self.MPRIS2_INTROSPECTION
@dbus.service.signal(__prop_interface, signature="sa{sv}as") @dbus.service.signal(__prop_interface, signature="sa{sv}as")
def PropertiesChanged(self, interface, changed_properties, invalidated_properties): def PropertiesChanged(self, interface, changed_properties, invalidated_properties):
@@ -920,29 +938,11 @@ class SongsView(Gtk.ScrolledWindow):
self.treeview.handler_unblock(self.key_press_event) self.treeview.handler_unblock(self.key_press_event)
def populate(self, songs): def populate(self, songs):
for song in songs: for s in songs:
try: song=self.client.extend_song_for_display(self.client.song_to_str_dict(s))
title=song["title"] dura=float(song["duration"])
except:
title=_("Unknown Title")
try:
track=song["track"]
except:
track="0"
try:
artist=song["artist"]
except:
artist=_("Unknown Artist")
try:
album=song["album"]
except:
album=_("Unknown Album")
try:
dura=float(song["duration"])
except:
dura=0.0
duration=str(datetime.timedelta(seconds=int(dura))) duration=str(datetime.timedelta(seconds=int(dura)))
self.store.append([track, title, artist, album, duration, song["file"]]) self.store.append([song["track"], song["title"], song["artist"], song["album"], duration, song["file"]])
def clear(self): def clear(self):
self.store.clear() self.store.clear()
@@ -1253,8 +1253,7 @@ class AlbumIconView(Gtk.IconView):
GLib.idle_add(self.emit, "done") GLib.idle_add(self.emit, "done")
def scroll_to_selected_album(self): def scroll_to_selected_album(self):
songid=self.client.status()["songid"] song=self.client.song_to_first_str_dict(self.client.currentsong())
song=self.client.playlistid(songid)[0]
self.unselect_all() self.unselect_all()
row_num=len(self.store) row_num=len(self.store)
for i in range(0, row_num): for i in range(0, row_num):
@@ -1429,7 +1428,7 @@ class MainCover(Gtk.Frame):
def on_button_press_event(self, widget, event): def on_button_press_event(self, widget, event):
if self.client.connected(): if self.client.connected():
song=self.client.currentsong() song=self.client.song_to_first_str_dict(self.client.currentsong())
if not song == {}: if not song == {}:
try: try:
artist=song[self.settings.get_artist_type()] artist=song[self.settings.get_artist_type()]
@@ -1467,7 +1466,6 @@ class PlaylistView(Gtk.Box):
self.client=client self.client=client
self.settings=settings self.settings=settings
self.playlist_version=None self.playlist_version=None
self.last_song_path=None
#Store #Store
#(track, disc, title, artist, album, duration, date, genre, file, weight) #(track, disc, title, artist, album, duration, date, genre, file, weight)
@@ -1598,22 +1596,15 @@ class PlaylistView(Gtk.Box):
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)
self.treeview.set_cursor(Gtk.TreePath(len(self.store)), None, False) self.treeview.set_cursor(Gtk.TreePath(len(self.store)), None, False)
for row in self.store: #reset bold text
row[9]=Pango.Weight.BOOK
try: try:
song=self.client.status()["song"] song=self.client.status()["song"]
path = Gtk.TreePath(int(song)) path = Gtk.TreePath(int(song))
self.selection.select_path(path) self.selection.select_path(path)
if self.last_song_path != None:
try:
self.store[self.last_song_path][9]=Pango.Weight.BOOK
except:
pass
self.store[path][9]=Pango.Weight.BOLD self.store[path][9]=Pango.Weight.BOLD
self.last_song_path=path
self.scroll_to_selected_title() self.scroll_to_selected_title()
except: except:
if self.last_song_path != None:
self.store[self.last_song_path][9]=Pango.Weight.BOOK
self.last_song_path=None
self.selection.unselect_all() self.selection.unselect_all()
def clear(self, *args): def clear(self, *args):
@@ -1657,45 +1648,15 @@ class PlaylistView(Gtk.Box):
songs=self.client.playlistinfo() songs=self.client.playlistinfo()
if not songs == []: if not songs == []:
self.playlist_info.set_text("") self.playlist_info.set_text("")
for song in songs: for s in songs:
try: song=self.client.extend_song_for_display(self.client.song_to_str_dict(s))
title=song["title"] dura=float(song["duration"])
except:
title=_("Unknown Title")
try:
track=song["track"]
except:
track="0"
try:
disc=song["disc"]
except:
disc=""
try:
artist=song["artist"]
except:
artist=_("Unknown Artist")
try:
album=song["album"]
except:
album=_("Unknown Album")
try:
dura=float(song["duration"])
except:
dura=0.0
try:
year=song["date"]
except:
year=""
try:
genre=song["genre"]
except:
genre=""
duration=str(datetime.timedelta(seconds=int(dura ))) duration=str(datetime.timedelta(seconds=int(dura )))
try: try:
treeiter=self.store.get_iter(song["pos"]) treeiter=self.store.get_iter(song["pos"])
self.store.set(treeiter, 0, track, 1, disc, 2, title, 3, artist, 4, album, 5, duration, 6, year, 7, genre, 8, song["file"], 9, Pango.Weight.BOOK) self.store.set(treeiter, 0, song["track"], 1, song["disc"], 2, song["title"], 3, song["artist"], 4, song["album"], 5, duration, 6, song["date"], 7, song["genre"], 8, song["file"], 9, Pango.Weight.BOOK)
except: except:
self.store.append([track, disc, title, artist, album, duration, year, genre, song["file"], Pango.Weight.BOOK]) self.store.append([song["track"], song["disc"], song["title"], song["artist"], song["album"], duration, song["date"], song["genre"], song["file"], Pango.Weight.BOOK])
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))):
treeiter=self.store.get_iter(i) treeiter=self.store.get_iter(i)
self.store.remove(treeiter) self.store.remove(treeiter)
@@ -1791,7 +1752,7 @@ class Browser(Gtk.Box):
def back_to_album(self, *args): def back_to_album(self, *args):
try: #since this can still be running when the connection is lost, various exceptions can occur try: #since this can still be running when the connection is lost, various exceptions can occur
song=self.client.currentsong() song=self.client.song_to_first_str_dict(self.client.currentsong())
try: try:
artist=song[self.settings.get_artist_type()] artist=song[self.settings.get_artist_type()]
except: except:
@@ -1803,7 +1764,7 @@ class Browser(Gtk.Box):
if not song['genre'] == self.genre_select.get_value(): if not song['genre'] == self.genre_select.get_value():
self.genre_select.deactivate() #deactivate genre filter to show all artists self.genre_select.deactivate() #deactivate genre filter to show all artists
except: except:
pass self.genre_select.deactivate() #deactivate genre filter to show all artists
if len(self.artist_view.get_selected_artists()) <= 1: if len(self.artist_view.get_selected_artists()) <= 1:
row_num=len(self.artist_view.store) row_num=len(self.artist_view.store)
for i in range(0, row_num): for i in range(0, row_num):
@@ -2708,13 +2669,12 @@ class AudioType(Gtk.Button):
def on_clicked(self, *args): def on_clicked(self, *args):
try: try:
self.store.clear() self.store.clear()
song=self.client.status()["song"] song=self.client.song_to_str_dict(self.client.currentsong())
tags=self.client.playlistinfo(song)[0] for tag, value in song.items():
for key in tags: if tag == "time":
if key == "time": self.store.append([tag, str(datetime.timedelta(seconds=int(value)))])
self.store.append([key, str(datetime.timedelta(seconds=int(tags[key])))])
else: else:
self.store.append([key, tags[key]]) self.store.append([tag, value])
self.popover.show_all() self.popover.show_all()
self.treeview.queue_resize() self.treeview.queue_resize()
except: except:
@@ -2872,7 +2832,7 @@ class LyricsWindow(Gtk.Window):
GLib.idle_add(self.label.set_text, text) GLib.idle_add(self.label.set_text, text)
def refresh(self, *args): def refresh(self, *args):
update_thread=threading.Thread(target=self.display_lyrics, kwargs={"current_song": self.client.currentsong()}, daemon=True) update_thread=threading.Thread(target=self.display_lyrics, kwargs={"current_song": self.client.song_to_first_str_dict(self.client.currentsong())}, daemon=True)
update_thread.start() update_thread.start()
def getLyrics(self, singer, song): #partially copied from PyLyrics 1.1.0 def getLyrics(self, singer, song): #partially copied from PyLyrics 1.1.0
@@ -2995,7 +2955,7 @@ class MainWindow(Gtk.ApplicationWindow):
def on_file_changed(self, *args): def on_file_changed(self, *args):
try: try:
song=self.client.currentsong() song=self.client.song_to_str_dict(self.client.currentsong())
self.set_title(song["artist"]+" - "+song["title"]+" - "+song["album"]) self.set_title(song["artist"]+" - "+song["title"]+" - "+song["album"])
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":

View File

@@ -1,7 +1,7 @@
dnl -*- Mode: autoconf -*- dnl -*- Mode: autoconf -*-
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
AC_PREREQ([2.68]) AC_PREREQ([2.68])
AC_INIT([mpdevil], [0.8.0]) AC_INIT([mpdevil], [0.8.1])
AC_CONFIG_SRCDIR([bin/mpdevil.py]) AC_CONFIG_SRCDIR([bin/mpdevil.py])
AM_INIT_AUTOMAKE AM_INIT_AUTOMAKE
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])