优化mpd交互

master
yutent 2023-08-23 20:36:52 +08:00
parent b27c816047
commit 1b4834d022
6 changed files with 275 additions and 259 deletions

63
main.py
View File

@ -13,7 +13,7 @@ from gi.repository import Gtk, Gdk, GLib, GdkPixbuf, GObject
from window import SonistWindow from window import SonistWindow
from about_app import AboutWindow from about_app import AboutWindow
from mpd.base import MPDClient from mpd import MPDClient
app_id = 'fun.wkit.sonist' app_id = 'fun.wkit.sonist'
home_dir = os.getenv('HOME') home_dir = os.getenv('HOME')
@ -28,6 +28,17 @@ def run_async(func):
return wrapper return wrapper
def set_timeout(timeout = 0.5):
def decorator(callback):
def wrapper(*args):
t = threading.Timer(timeout, callback, args=args)
t.start()
return t
return wrapper
return decorator
def get_music_dir(): def get_music_dir():
with open(f'{home_dir}/.mpd/mpd.conf', 'r') as f: with open(f'{home_dir}/.mpd/mpd.conf', 'r') as f:
data = f.read() data = f.read()
@ -51,8 +62,7 @@ class Application(Gtk.Application):
def __init__(self): def __init__(self):
Gtk.Application.__init__(self, application_id = app_id) Gtk.Application.__init__(self, application_id = app_id)
self.mpd_state = None self.timer = None
self.mpd_curr_song = None
self.mpd = MPDClient() self.mpd = MPDClient()
@ -60,44 +70,7 @@ class Application(Gtk.Application):
self.connect('window-removed', self.on_window_removed) self.connect('window-removed', self.on_window_removed)
self.mpd.connect()
@run_async
def ping(self):
if self.mpd_is_online:
while True:
try:
self.mpd.ping()
stat = self.mpd.status()
song = self.mpd.currentsong() or {}
state = stat.get('state')
if self.mpd_curr_song != song.get('id'):
self.mpd_curr_song = song.get('id')
self.emit('song_changed', self.mpd_curr_song)
if state == 'play':
self.emit('playing', False)
if self.mpd_state != state:
self.emit('state_changed', state)
self.mpd_state = state
time.sleep(0.5)
except Exception as e:
self.mpd.kill()
time.sleep(2)
self.mpd_is_online = self.mpd_connect()
def mpd_connect(self):
try:
self.mpd.connect("127.0.0.1", 6600)
return True
except:
return False
def do_activate(self): def do_activate(self):
@ -105,24 +78,22 @@ class Application(Gtk.Application):
self.set_app_menu(None) self.set_app_menu(None)
self.set_menubar(None) self.set_menubar(None)
self.mpd_is_online = self.mpd_connect()
self.window = SonistWindow(self) self.window = SonistWindow(self)
self.about = AboutWindow() self.about = AboutWindow()
self.add_window(self.window) self.add_window(self.window)
self.window.show_all() self.window.show_all()
# self.about.show_all() # self.about.show_all()
self.ping()
def on_window_removed(self, app, win): def on_window_removed(self, app, win):
if len(self.get_windows()) == 0: if len(self.get_windows()) == 0:
if self.timer is not None:
self.timer.cancel()
self.mpd.destroy()
print('朕要休息了~~~') print('朕要休息了~~~')
""" class ApplicationService(dbus.service.Object): """ class ApplicationService(dbus.service.Object):
def __init__(self, app): def __init__(self, app):
self.app = app self.app = app

View File

@ -17,15 +17,11 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with python-mpd2. If not, see <http://www.gnu.org/licenses/>. # along with python-mpd2. If not, see <http://www.gnu.org/licenses/>.
import logging import re, socket, sys, warnings, threading, time
import re
import socket
import sys
import warnings
from enum import Enum from enum import Enum
VERSION = (3, 1, 0) VERSION = (3, 1, 0)
HELLO_PREFIX = "OK MPD " HELLO_PREFIX = "OK MPD "
ERROR_PREFIX = "ACK " ERROR_PREFIX = "ACK "
@ -34,22 +30,29 @@ SUCCESS = "OK"
NEXT = "list_OK" NEXT = "list_OK"
def run_async(func):
def wrapper(*args, **kwargs):
thread = threading.Thread(target=func, args=args, kwargs=kwargs)
thread.daemon = True
thread.start()
return thread
return wrapper
def set_timeout(timeout = 0.5):
def decorator(callback):
def wrapper(*args):
t = threading.Timer(timeout, callback, args=args)
t.start()
return t
return wrapper
return decorator
def escape(text): def escape(text):
return text.replace("\\", "\\\\").replace('"', '\\"') return text.replace("\\", "\\\\").replace('"', '\\"')
try:
from logging import NullHandler
except ImportError: # NullHandler was introduced in python2.7
class NullHandler(logging.Handler):
def emit(self, record):
pass
logger = logging.getLogger(__name__)
logger.addHandler(NullHandler())
# MPD Protocol errors as found in CommandError exceptions # MPD Protocol errors as found in CommandError exceptions
# https://github.com/MusicPlayerDaemon/MPD/blob/master/src/protocol/Ack.hxx # https://github.com/MusicPlayerDaemon/MPD/blob/master/src/protocol/Ack.hxx
@ -177,26 +180,10 @@ class MPDClientBase(object):
subclasses. subclasses.
""" """
def __init__(self, use_unicode=None): def __init__(self):
self.iterate = False self.iterate = False
if use_unicode is not None:
warnings.warn(
"use_unicode parameter to ``MPDClientBase`` constructor is "
"deprecated",
DeprecationWarning,
stacklevel=2,
)
self._reset() self._reset()
@property
def use_unicode(self):
warnings.warn(
"``use_unicode`` is deprecated: python-mpd 2.x always uses "
"Unicode",
DeprecationWarning,
stacklevel=2,
)
return True
@classmethod @classmethod
def add_command(cls, name, callback): def add_command(cls, name, callback):
@ -490,6 +477,17 @@ class _NotConnected(object):
@mpd_command_provider @mpd_command_provider
class MPDClient(MPDClientBase): class MPDClient(MPDClientBase):
__events__ = {}
connected = False
try_conn_timer = None
heart_beat_timer = None
current_song_id = None
current_state = 'stop'
idletimeout = None idletimeout = None
_timeout = None _timeout = None
_wrap_iterator_parsers = [ _wrap_iterator_parsers = [
@ -508,22 +506,28 @@ class MPDClient(MPDClientBase):
MPDClientBase._parse_plugins, MPDClientBase._parse_plugins,
] ]
def __init__(self, use_unicode=None): def __init__(self, host = '127.0.0.1', port = 6600):
if use_unicode is not None:
warnings.warn(
"use_unicode parameter to ``MPDClient`` constructor is "
"deprecated",
DeprecationWarning,
stacklevel=2,
)
super(MPDClient, self).__init__() super(MPDClient, self).__init__()
self.host = host
self.port = port
def _reset(self): def _reset(self):
super(MPDClient, self)._reset() super(MPDClient, self)._reset()
self.connected = False
self._iterating = False self._iterating = False
self._sock = None self._sock = None
self._rbfile = _NotConnected() self._rbfile = None
self._wfile = _NotConnected() self._wfile = None
def bind(self, event, callback):
self.__events__[event] = callback
@run_async
def emit(self, event, *args):
# print('emit: ', event, args)
callback = self.__events__.get(event)
if callback is not None:
callback(*args)
def _execute(self, command, args, retval): def _execute(self, command, args, retval):
if self._iterating: if self._iterating:
@ -543,11 +547,14 @@ class MPDClient(MPDClientBase):
def _write_line(self, line): def _write_line(self, line):
try: try:
if self._wfile == None:
print('MPD server is not connected!!!')
else:
self._wfile.write("{}\n".format(line)) self._wfile.write("{}\n".format(line))
self._wfile.flush() self._wfile.flush()
except socket.error as e: except socket.error as e:
error_message = "Connection to server was reset" error_message = "Connection to server was reset"
logger.info(error_message)
self._reset() self._reset()
e = ConnectionError(error_message) e = ConnectionError(error_message)
raise e.with_traceback(sys.exc_info()[2]) raise e.with_traceback(sys.exc_info()[2])
@ -565,11 +572,7 @@ class MPDClient(MPDClientBase):
else: else:
parts.append('"{}"'.format(escape(str(arg)))) parts.append('"{}"'.format(escape(str(arg))))
# Minimize logging cost if the logging is not activated. # Minimize logging cost if the logging is not activated.
if logger.isEnabledFor(logging.DEBUG):
if command == "password":
logger.debug("Calling MPD password(******)")
else:
logger.debug("Calling MPD %s%r", command, args)
cmd = " ".join(parts) cmd = " ".join(parts)
self._write_line(cmd) self._write_line(cmd)
@ -716,31 +719,35 @@ class MPDClient(MPDClientBase):
self._iterating = True self._iterating = True
return self._iterator_wrapper(iterator) return self._iterator_wrapper(iterator)
def _hello(self, line): def _check_is_mpd_server(self, line):
if not line.endswith("\n"): if not line.endswith("\n"):
self.disconnect() self.disconnect()
raise ConnectionError("Connection lost while reading MPD hello") return
line = line.rstrip("\n") line = line.rstrip("\n")
if not line.startswith(HELLO_PREFIX): if not line.startswith(HELLO_PREFIX):
raise ProtocolError("Got invalid MPD hello: '{}'".format(line)) return
self.connected = True
self.mpd_version = line[len(HELLO_PREFIX) :].strip() self.mpd_version = line[len(HELLO_PREFIX) :].strip()
def _connect_unix(self, path): def _connect_unix(self):
if not hasattr(socket, "AF_UNIX"): if not hasattr(socket, "AF_UNIX"):
raise ConnectionError("Unix domain sockets not supported on this platform") raise ConnectionError("Unix domain sockets not supported on this platform")
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(self.timeout) sock.settimeout(self.timeout)
sock.connect(path) sock.connect(self.host)
return sock return sock
def _connect_tcp(self, host, port): def _connect_tcp(self):
try: try:
flags = socket.AI_ADDRCONFIG flags = socket.AI_ADDRCONFIG
except AttributeError: except AttributeError:
flags = 0 flags = 0
err = None
for res in socket.getaddrinfo( for res in socket.getaddrinfo(
host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, socket.IPPROTO_TCP, flags self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM, socket.IPPROTO_TCP, flags
): ):
af, socktype, proto, canonname, sa = res af, socktype, proto, canonname, sa = res
sock = None sock = None
@ -751,14 +758,16 @@ class MPDClient(MPDClientBase):
sock.settimeout(self.timeout) sock.settimeout(self.timeout)
sock.connect(sa) sock.connect(sa)
return sock return sock
except socket.error as e: except Exception as e:
err = e
if e.strerror == 'Connection refused':
self.emit('offline')
else:
self.emit('error', e)
if sock is not None: if sock is not None:
sock.close() sock.close()
if err is not None:
raise err
else:
raise ConnectionError("getaddrinfo returns an empty list")
@mpd_commands("idle") @mpd_commands("idle")
def _parse_idle(self, lines): def _parse_idle(self, lines):
@ -777,47 +786,95 @@ class MPDClient(MPDClientBase):
if self._sock is not None: if self._sock is not None:
self._sock.settimeout(timeout) self._sock.settimeout(timeout)
def connect(self, host, port=None, timeout=None): @set_timeout(2)
logger.info("Calling MPD connect(%r, %r, timeout=%r)", host, port, timeout) def _try_connect(self):
if self._sock is not None: if self._sock is not None:
raise ConnectionError("Already connected") raise ConnectionError("Already connected")
if timeout is not None:
warnings.warn( if self.host.startswith(("/", "\0")):
"The timeout parameter in connect() is deprecated! " self._sock = self._connect_unix()
"Use MPDClient.timeout = yourtimeout instead.",
DeprecationWarning,
)
self.timeout = timeout
if host.startswith("@"):
host = "\0" + host[1:]
if host.startswith(("/", "\0")):
self._sock = self._connect_unix(host)
else: else:
if port is None: self._sock = self._connect_tcp()
raise ValueError(
"port argument must be specified when connecting via tcp"
)
self._sock = self._connect_tcp(host, port)
# - Force UTF-8 encoding, since this is dependant from the LC_CTYPE # - Force UTF-8 encoding, since this is dependant from the LC_CTYPE
# locale. # locale.
# - by setting newline explicit, we force to send '\n' also on # - by setting newline explicit, we force to send '\n' also on
# windows # windows
if self._sock is None:
self.connected = False
self.connect()
return
self._rbfile = self._sock.makefile("rb", newline="\n") self._rbfile = self._sock.makefile("rb", newline="\n")
self._wfile = self._sock.makefile("w", encoding="utf-8", newline="\n") self._wfile = self._sock.makefile("w", encoding="utf-8", newline="\n")
try: try:
helloline = self._rbfile.readline().decode("utf-8") helloline = self._rbfile.readline().decode("utf-8")
self._hello(helloline) self._check_is_mpd_server(helloline)
except Exception: if self.connected:
self.emit('online')
self.heart_beat_timer = self.heart_beat()
else:
self.emit('error', ProtocolError('Connected server is not mpd server.'))
except Exception as e:
self.connected = False
self.emit('error', e)
self.disconnect() self.disconnect()
raise
@set_timeout(0.2)
def heart_beat(self):
if self.heart_beat_timer is not None:
self.heart_beat_timer.cancel()
if self.connected:
try:
status = self.status()
song = self.currentsong()
state = status.get('state')
if state != self.current_state:
self.emit('state_changed', state)
self.current_state = state
if song.get('id') != self.current_song_id:
if self.current_song_id is not None:
self.emit('song_changed', status, song)
self.current_song_id = song.get('id')
if state == 'play':
self.emit('playing', status, song)
self.heart_beat_timer = self.heart_beat()
except Exception as err:
print(err)
self.disconnect()
self.connect()
def connect(self):
self.try_conn_timer = self._try_connect()
def destroy(self):
if self.try_conn_timer is not None:
self.try_conn_timer.cancel()
if self.heart_beat_timer is not None:
self.heart_beat_timer.cancel()
self._reset()
def disconnect(self): def disconnect(self):
logger.info("Calling MPD disconnect()") if self._rbfile is not None:
if self._rbfile is not None and not isinstance(self._rbfile, _NotConnected):
self._rbfile.close() self._rbfile.close()
if self._wfile is not None and not isinstance(self._wfile, _NotConnected): if self._wfile is not None:
self._wfile.close() self._wfile.close()
if self._sock is not None: if self._sock is not None:
self._sock.close() self._sock.close()

View File

@ -1,29 +0,0 @@
# python-mpd2: Python MPD client library
#
# Copyright (C) 2008-2010 J. Alexander Treuman <jat@spatialrift.net>
# Copyright (C) 2012 J. Thalheim <jthalheim@gmail.com>
# Copyright (C) 2016 Robert Niederreiter <rnix@squarewave.at>
#
# python-mpd2 is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# python-mpd2 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with python-mpd2. If not, see <http://www.gnu.org/licenses/>.
from mpd.base import CommandError
from mpd.base import CommandListError
from mpd.base import ConnectionError
from mpd.base import FailureResponseCode
from mpd.base import IteratingError
from mpd.base import MPDClient
from mpd.base import MPDError
from mpd.base import PendingCommandError
from mpd.base import ProtocolError
from mpd.base import VERSION

View File

@ -16,7 +16,7 @@ class CtrlBox(Gtk.Box):
def __init__(self, spacing = 6): def __init__(self, spacing = 6):
Gtk.Box.__init__(self, spacing = spacing) Gtk.Box.__init__(self, spacing = spacing)
self.disabled = False self.disabled = True
self.modes = ['./usr/share/sonist/all.png','./usr/share/sonist/rand.png','./usr/share/sonist/single.png'] self.modes = ['./usr/share/sonist/all.png','./usr/share/sonist/rand.png','./usr/share/sonist/single.png']
self.curr_mode = 0 self.curr_mode = 0

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import gi import gi, threading
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject from gi.repository import Gtk, GObject
@ -25,6 +25,9 @@ class Timebar(Gtk.Fixed):
def __init__(self): def __init__(self):
Gtk.Fixed.__init__(self) Gtk.Fixed.__init__(self)
self.timer = None
self.pending = False
self._duration = 0 self._duration = 0
self._time = 0 self._time = 0
@ -37,7 +40,8 @@ class Timebar(Gtk.Fixed):
self.duration.set_xalign(1) self.duration.set_xalign(1)
self.duration.set_name('text') self.duration.set_name('text')
self.slider.connect('change-value', self.on_timeupdate) self.slider.connect('change-value', self.debounce)
self.slider.set_sensitive(False)
self.put(self.curr, 3, 0) self.put(self.curr, 3, 0)
self.put(self.duration, 233, 0) self.put(self.duration, 233, 0)
@ -47,14 +51,32 @@ class Timebar(Gtk.Fixed):
def update_time(self, curr = 0, duration = 0): def update_time(self, curr = 0, duration = 0):
self._duration = duration self._duration = duration
self._time = curr self._time = curr
self.slider.set_sensitive(duration > 0)
if duration == 0:
self.curr.set_text('')
self.duration.set_text('')
self.slider.set_value(0)
else:
progress = curr * 100 / duration progress = curr * 100 / duration
self.curr.set_text(time_to_str(curr)) self.curr.set_text(time_to_str(curr))
self.duration.set_text(time_to_str(duration)) self.duration.set_text(time_to_str(duration))
# 优化拖拽进度时的更新
if not self.pending:
self.slider.set_value(progress) self.slider.set_value(progress)
def on_timeupdate(self, slider, a, b):
# print(slider.get_value(), a, b) def on_timeupdate(self):
p = slider.get_value() self.pending = False
p = self.slider.get_value()
time = p * self._duration / 100 time = p * self._duration / 100
self.emit('seeked', time) self.emit('seeked', time)
def debounce(self, slider, a, b):
if self.timer is not None:
self.timer.cancel()
self.pending = True
self.timer = threading.Timer(0.2, self.on_timeupdate)
self.timer.start()

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import gi, sys, os, mutagen import gi, sys, os, mutagen
from pprint import pprint as print # from pprint import pprint as print
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
@ -22,16 +22,23 @@ from ui.option_menu import OptionMenu
class SonistWindow(Gtk.Window): class SonistWindow(Gtk.Window):
app = None
mpd = None
stat = {}
def __init__(self, app): def __init__(self, app):
Gtk.Window.__init__(self) Gtk.Window.__init__(self)
self.app = app self.app = app
self.mpd = app.mpd
self.connect("destroy", self.quit) self.connect("destroy", self.quited)
self.app.connect('state_changed', lambda a, x: self.update_play_stat(x == 'play')) self.mpd.bind('offline', lambda : self.reset_player())
self.app.connect('song_changed', lambda a, id: self.sync_state(False)) self.mpd.bind('online', lambda : self.sync_state(None, None, True))
self.app.connect('playing', lambda a, id: self.update_playtime()) self.mpd.bind('state_changed', lambda stat: self.update_play_stat(stat == 'play'))
self.mpd.bind('song_changed', lambda stat, song: self.sync_state(stat, song, False))
self.mpd.bind('playing', lambda stat, song: self.update_playtime(stat))
self.set_name('SonistWindow') self.set_name('SonistWindow')
@ -88,7 +95,7 @@ class SonistWindow(Gtk.Window):
# 播放进度 # 播放进度
self.timebar = Timebar() self.timebar = Timebar()
self.timebar.connect('seeked', lambda a,v: self.app.mpd.seekcur(v)) self.timebar.connect('seeked', lambda a,v: self.mpd.seekcur(v))
layout.put(self.timebar, 24, 270) layout.put(self.timebar, 24, 270)
@ -96,22 +103,17 @@ class SonistWindow(Gtk.Window):
self.ctrl_box = CtrlBox() self.ctrl_box = CtrlBox()
self.ctrl_box.connect('clicked', self.ctrl_clicked) self.ctrl_box.connect('clicked', self.ctrl_clicked)
self.ctrl_box.disabled = not self.app.mpd_is_online
layout.put(self.ctrl_box, 48, 312) layout.put(self.ctrl_box, 48, 312)
self.add(layout) self.add(layout)
self.sync_state(True)
def get_mpd_stat(self): def get_mpd_stat(self):
try: try:
self.stat = self.app.mpd.status() self.stat = self.mpd.status()
except: except:
self.app.ping() self.stat = {}
self.stat = self.app.mpd.status()
return self.stat return self.stat
@ -150,19 +152,19 @@ class SonistWindow(Gtk.Window):
case 'mode_btn': case 'mode_btn':
# repeat all # repeat all
if self.ctrl_box.curr_mode == 0: if self.ctrl_box.curr_mode == 0:
self.app.mpd.repeat(1) self.mpd.repeat(1)
self.app.mpd.random(0) self.mpd.random(0)
self.app.mpd.single(0) self.mpd.single(0)
# random # random
elif self.ctrl_box.curr_mode == 1: elif self.ctrl_box.curr_mode == 1:
self.app.mpd.repeat(0) self.mpd.repeat(0)
self.app.mpd.random(1) self.mpd.random(1)
self.app.mpd.single(0) self.mpd.single(0)
# single # single
else: else:
self.app.mpd.repeat(0) self.mpd.repeat(0)
self.app.mpd.random(0) self.mpd.random(0)
self.app.mpd.single(1) self.mpd.single(1)
case 'prev_btn': case 'prev_btn':
self.prev_song() self.prev_song()
@ -178,39 +180,30 @@ class SonistWindow(Gtk.Window):
def toggle_play(self): def toggle_play(self):
try: try:
if self.stat.get('state') == 'stop': if self.stat.get('state') == 'stop':
self.app.mpd.play() self.mpd.play()
else: else:
self.app.mpd.pause() self.mpd.pause()
except: except:
self.app.ping() pass
self.toggle_play()
return
# self.sync_state()
self.update_play_stat(self.stat.get('state') == 'play') self.update_play_stat(self.stat.get('state') == 'play')
def prev_song(self): def prev_song(self):
try: try:
self.app.mpd.previous() self.mpd.previous()
except: except:
self.app.ping() pass
self.prev_song()
return
# self.sync_state()
def next_song(self): def next_song(self):
try: try:
self.app.mpd.next() self.mpd.next()
except: except:
self.app.ping() pass
self.next_song()
return
# self.sync_state()
def update_play_stat(self, played = True): def update_play_stat(self, played = True):
if not self.app.mpd_is_online: if not self.mpd.connected:
return return
if played: if played:
@ -222,19 +215,16 @@ class SonistWindow(Gtk.Window):
self.ctrl_box.toggle_play_btn(played) self.ctrl_box.toggle_play_btn(played)
def update_playtime(self): def update_playtime(self, stat = {}):
stat = self.get_mpd_stat()
times = stat['time'].split(':') times = stat['time'].split(':')
self.timebar.update_time(int(times[0]), int(times[1])) self.timebar.update_time(int(times[0]), int(times[1]))
def sync_state(self, first = False): def sync_state(self, stat = None, song = None, first = False):
if not self.app.mpd_is_online: self.ctrl_box.disabled = False
return
self.stat = self.get_mpd_stat() self.stat = stat or self.get_mpd_stat()
played = self.stat.get('state') played = self.stat.get('state')
song = self.app.mpd.currentsong()
if first: if first:
self.update_play_stat(played == 'play') self.update_play_stat(played == 'play')
@ -246,14 +236,14 @@ class SonistWindow(Gtk.Window):
if played != 'stop': if played != 'stop':
song = song or self.mpd.currentsong()
# 更新歌曲信息 # 更新歌曲信息
self.title_box.set_text("%s - %s" % (song.get('artist'), song.get('title'))) self.title_box.set_text(f"{song.get('artist')} - {song.get('title')}")
self.update_playtime() self.update_playtime(self.stat)
filepath = f"./album/{song['title']}.png" filepath = f"./album/{song['title']}.png"
songpath = f"{self.app.music_dir}/{song['file']}" songpath = f"{self.app.music_dir}/{song['file']}"
if os.path.isfile(filepath): if os.path.isfile(filepath):
self.update_album(filepath) self.update_album(filepath)
else: else:
@ -272,11 +262,16 @@ class SonistWindow(Gtk.Window):
except: except:
pass pass
def reset_player(self):
self.ctrl_box.disabled = True
self.title_box.set_text('mpd is offline...')
self.timebar.update_time()
self.update_album('./usr/share/sonist/avatar.jpg')
def update_album(self, filepath): def update_album(self, filepath):
self.set_background_image(filepath) self.set_background_image(filepath)
self.album.reset(filepath).set_radius(64) self.album.reset(filepath).set_radius(64)
def quit(self, win): def quited(self, win):
self.app.remove_window(self) self.app.remove_window(self)