mirror of
https://github.com/SoongNoonien/mpdevil.git
synced 2023-08-10 21:12:44 +03:00
shrank ClientHelper
This commit is contained in:
parent
19acf66a16
commit
5e0c61109c
304
bin/mpdevil
304
bin/mpdevil
@ -26,6 +26,7 @@ import requests
|
||||
from bs4 import BeautifulSoup
|
||||
import threading
|
||||
import datetime
|
||||
import collections
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
@ -372,8 +373,7 @@ class MPRISInterface: # TODO emit Seeked if needed
|
||||
Translate metadata returned by MPD to the MPRIS v2 syntax.
|
||||
http://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata
|
||||
"""
|
||||
mpd_meta=self._client.currentsong() # raw values needed for cover
|
||||
song=ClientHelper.song_to_list_dict(mpd_meta)
|
||||
song=self._client.currentsong()
|
||||
self._metadata={}
|
||||
for tag, xesam_tag in (("album","album"),("title","title"),("date","contentCreated")):
|
||||
if tag in song:
|
||||
@ -385,17 +385,17 @@ class MPRISInterface: # TODO emit Seeked if needed
|
||||
if tag in song:
|
||||
self._metadata[f"xesam:{xesam_tag}"]=GLib.Variant("as", song[tag])
|
||||
if "id" in song:
|
||||
self._metadata["mpris:trackid"]=GLib.Variant("o", f"{self._MPRIS_PATH}/Track/{song['id'][0]}")
|
||||
self._metadata["mpris:trackid"]=GLib.Variant("o", f"{self._MPRIS_PATH}/Track/{song['id']}")
|
||||
if "duration" in song:
|
||||
self._metadata["mpris:length"]=GLib.Variant("x", float(song["duration"][0])*1000000)
|
||||
self._metadata["mpris:length"]=GLib.Variant("x", float(song["duration"])*1000000)
|
||||
if "file" in song:
|
||||
song_file=song["file"][0]
|
||||
song_file=song["file"]
|
||||
if "://" in song_file: # remote file
|
||||
self._metadata["xesam:url"]=GLib.Variant("s", song_file)
|
||||
else:
|
||||
lib_path=self._settings.get_value("paths")[self._settings.get_int("active-profile")]
|
||||
self._metadata["xesam:url"]=GLib.Variant("s", f"file://{os.path.join(lib_path, song_file)}")
|
||||
cover_path=self._client.get_cover_path(mpd_meta)
|
||||
cover_path=self._client.get_cover_path(song)
|
||||
if cover_path is not None:
|
||||
self._metadata["mpris:artUrl"]=GLib.Variant("s", f"file://{cover_path}")
|
||||
|
||||
@ -441,9 +441,9 @@ class MPRISInterface: # TODO emit Seeked if needed
|
||||
# MPD client wrapper #
|
||||
######################
|
||||
|
||||
class ClientHelper():
|
||||
def seconds_to_display_time(seconds):
|
||||
delta=datetime.timedelta(seconds=seconds)
|
||||
class Duration(float):
|
||||
def __str__(self):
|
||||
delta=datetime.timedelta(seconds=int(self))
|
||||
if delta.days > 0:
|
||||
days=ngettext("{days} day", "{days} days", delta.days).format(days=delta.days)
|
||||
time_string=f"{days}, {datetime.timedelta(seconds=delta.seconds)}"
|
||||
@ -451,9 +451,24 @@ class ClientHelper():
|
||||
time_string=str(delta).lstrip("0").lstrip(":")
|
||||
return time_string.replace(":", "∶") # use 'ratio' as delimiter
|
||||
|
||||
def convert_audio_format(audio_format):
|
||||
class LastModified():
|
||||
def __init__(self, date):
|
||||
self._date=date
|
||||
|
||||
def __str__(self):
|
||||
time=datetime.datetime.strptime(self._date, "%Y-%m-%dT%H:%M:%SZ")
|
||||
return time.strftime("%a %d %B %Y, %H∶%M UTC")
|
||||
|
||||
def raw(self):
|
||||
return self._date
|
||||
|
||||
class Format():
|
||||
def __init__(self, audio_format):
|
||||
self._format=audio_format
|
||||
|
||||
def __str__(self):
|
||||
# see: https://www.musicpd.org/doc/html/user.html#audio-output-format
|
||||
samplerate, bits, channels=audio_format.split(":")
|
||||
samplerate, bits, channels=self._format.split(":")
|
||||
if bits == "f":
|
||||
bits="32fp"
|
||||
try:
|
||||
@ -467,52 +482,40 @@ class ClientHelper():
|
||||
channels=ngettext("{channels} channel", "{channels} channels", int_chan).format(channels=int_chan)
|
||||
return f"{freq} kHz • {bits} bit • {channels}"
|
||||
|
||||
def song_to_str_dict(song): # converts tags with multiple values to comma separated strings
|
||||
return_song={}
|
||||
for tag, value in song.items():
|
||||
if isinstance(value, list):
|
||||
return_song[tag]=(", ".join(value))
|
||||
def raw(self):
|
||||
return self._format
|
||||
|
||||
class MultiTag(list):
|
||||
def __str__(self):
|
||||
return ", ".join(self)
|
||||
|
||||
class Song(collections.UserDict):
|
||||
def __setitem__(self, key, value):
|
||||
# time is deprecated https://mpd.readthedocs.io/en/latest/protocol.html#other-metadata
|
||||
if key != "time":
|
||||
if key == "duration":
|
||||
super().__setitem__(key, Duration(value))
|
||||
elif key == "format":
|
||||
super().__setitem__(key, Format(value))
|
||||
elif key == "last-modified":
|
||||
super().__setitem__(key, LastModified(value))
|
||||
elif key in ("range", "file", "pos", "id"):
|
||||
super().__setitem__(key, value)
|
||||
else:
|
||||
return_song[tag]=value
|
||||
return return_song
|
||||
if isinstance(value, list):
|
||||
super().__setitem__(key, MultiTag(value))
|
||||
else:
|
||||
super().__setitem__(key, MultiTag([value]))
|
||||
|
||||
def song_to_first_str_dict(song): # extracts the first value of multiple value tags
|
||||
return_song={}
|
||||
for tag, value in song.items():
|
||||
if isinstance(value, list):
|
||||
return_song[tag]=value[0]
|
||||
else:
|
||||
return_song[tag]=value
|
||||
return return_song
|
||||
|
||||
def song_to_list_dict(song): # converts all values to lists
|
||||
return_song={}
|
||||
for tag, value in song.items():
|
||||
if isinstance(value, list):
|
||||
return_song[tag]=value
|
||||
else:
|
||||
return_song[tag]=[value]
|
||||
return return_song
|
||||
|
||||
def pepare_song_for_display(song):
|
||||
base_song={
|
||||
"title": os.path.basename(song["file"]),
|
||||
"track": "",
|
||||
"disc": "",
|
||||
"artist": "",
|
||||
"album": "",
|
||||
"duration": "0.0",
|
||||
"date": "",
|
||||
"genre": ""
|
||||
}
|
||||
base_song.update(song)
|
||||
base_song["human_duration"]=ClientHelper.seconds_to_display_time(int(float(base_song["duration"])))
|
||||
for tag in ("disc", "track"): # remove confusing multiple tags
|
||||
if tag in song:
|
||||
if isinstance(song[tag], list):
|
||||
base_song[tag]=song[tag][0]
|
||||
return base_song
|
||||
def __missing__(self, key):
|
||||
if key == "title":
|
||||
return MultiTag([os.path.basename(self.data["file"])])
|
||||
elif key == "duration":
|
||||
return "0"
|
||||
else:
|
||||
return MultiTag([""])
|
||||
|
||||
class ClientHelper():
|
||||
def binary_to_pixbuf(binary, size):
|
||||
loader=GdkPixbuf.PixbufLoader()
|
||||
try:
|
||||
@ -570,6 +573,18 @@ class Client(MPDClient):
|
||||
# connect
|
||||
self._settings.connect("changed::active-profile", self._on_active_profile_changed)
|
||||
|
||||
# overloads
|
||||
def currentsong(self, *args):
|
||||
return Song(super().currentsong(*args))
|
||||
def search(self, *args):
|
||||
return [Song(song) for song in super().search(*args)]
|
||||
def find(self, *args):
|
||||
return [Song(song) for song in super().find(*args)]
|
||||
def playlistinfo(self):
|
||||
return [Song(song) for song in super().playlistinfo()]
|
||||
def plchanges(self, version):
|
||||
return [Song(song) for song in super().plchanges(version)]
|
||||
|
||||
def start(self):
|
||||
self.emitter.emit("disconnected") # bring player in defined state
|
||||
active=self._settings.get_int("active-profile")
|
||||
@ -704,10 +719,9 @@ class Client(MPDClient):
|
||||
else:
|
||||
return([])
|
||||
|
||||
def get_cover_path(self, raw_song):
|
||||
def get_cover_path(self, song):
|
||||
path=None
|
||||
song=ClientHelper.song_to_first_str_dict(raw_song)
|
||||
song_file=song.get("file")
|
||||
song_file=song["file"]
|
||||
active_profile=self._settings.get_int("active-profile")
|
||||
lib_path=self._settings.get_lib_path()
|
||||
if lib_path is not None:
|
||||
@ -715,14 +729,14 @@ class Client(MPDClient):
|
||||
if regex_str == "":
|
||||
regex=re.compile(COVER_REGEX, flags=re.IGNORECASE)
|
||||
else:
|
||||
regex_str=regex_str.replace("%AlbumArtist%", song.get("albumartist", ""))
|
||||
regex_str=regex_str.replace("%Album%", song.get("album", ""))
|
||||
regex_str=regex_str.replace("%AlbumArtist%", song["albumartist"][0])
|
||||
regex_str=regex_str.replace("%Album%", song["album"][0])
|
||||
try:
|
||||
regex=re.compile(regex_str, flags=re.IGNORECASE)
|
||||
except:
|
||||
print("illegal regex:", regex_str)
|
||||
return (None, None)
|
||||
if song_file is not None:
|
||||
if song_file:
|
||||
song_dir=os.path.join(lib_path, os.path.dirname(song_file))
|
||||
if song_dir.endswith(".cue"):
|
||||
song_dir=os.path.dirname(song_dir) # get actual directory of .cue file
|
||||
@ -758,7 +772,7 @@ class Client(MPDClient):
|
||||
pixbuf=ClientHelper.file_to_pixbuf(cover_path, size)
|
||||
return pixbuf
|
||||
|
||||
def get_metadata(self, uri):
|
||||
def get_metadata(self, uri): # TODO Song
|
||||
meta_base=self.lsinfo(uri)[0]
|
||||
try: # .cue files produce an error here
|
||||
meta_extra=self.readcomments(uri) # contains comment tag
|
||||
@ -791,19 +805,20 @@ class Client(MPDClient):
|
||||
years=self.comp_list("date", "album", album, artist_type, artist, *genre_filter)
|
||||
for year in years:
|
||||
count=self.count(artist_type, artist, "album", album, "date", year, *genre_filter)
|
||||
duration=Duration(count["playtime"])
|
||||
song=self.find("album", album, "date", year, artist_type, artist, *genre_filter, "window", "0:1")[0]
|
||||
cover_path=self.get_cover_path(song)
|
||||
if cover_path is None:
|
||||
cover_binary=self.get_cover_binary(song.get("file"))
|
||||
cover_binary=self.get_cover_binary(song["file"])
|
||||
if cover_binary is None:
|
||||
albums.append({"artist": artist,"album": album,"year": year,
|
||||
"length": count["songs"],"duration": count["playtime"]})
|
||||
"length": count["songs"],"duration": duration})
|
||||
else:
|
||||
albums.append({"artist": artist,"album": album,"year": year,
|
||||
"length": count["songs"],"duration": count["playtime"],"cover_binary": cover_binary})
|
||||
"length": count["songs"],"duration": duration,"cover_binary": cover_binary})
|
||||
else:
|
||||
albums.append({"artist": artist,"album": album,"year": year,
|
||||
"length": count["songs"],"duration": count["playtime"], "cover_path": cover_path})
|
||||
"length": count["songs"],"duration": duration, "cover_path": cover_path})
|
||||
self.tagtypes("all")
|
||||
return albums
|
||||
|
||||
@ -1371,7 +1386,7 @@ class ServerStats(Gtk.Dialog):
|
||||
stats=client.stats()
|
||||
stats["protocol"]=str(client.mpd_version)
|
||||
for key in ("uptime","playtime","db_playtime"):
|
||||
stats[key]=ClientHelper.seconds_to_display_time(int(stats[key]))
|
||||
stats[key]=str(Duration(stats[key]))
|
||||
stats["db_update"]=str(datetime.datetime.fromtimestamp(int(stats["db_update"]))).replace(":", "∶")
|
||||
|
||||
for i, key in enumerate(("protocol","uptime","playtime","db_update","db_playtime","artists","albums","songs")):
|
||||
@ -1573,18 +1588,14 @@ class SongPopover(Gtk.Popover):
|
||||
window=self.get_toplevel()
|
||||
self._scroll.set_max_content_height(window.get_size()[1]//2)
|
||||
self._store.clear()
|
||||
song=ClientHelper.song_to_str_dict(self._client.get_metadata(uri))
|
||||
song.pop("time", None)
|
||||
song=Song(self._client.get_metadata(uri)) # TODO Song
|
||||
for tag, value in song.items():
|
||||
if tag == "duration":
|
||||
self._store.append([tag+":", ClientHelper.seconds_to_display_time(int(float(value))), locale.str(float(value))])
|
||||
elif tag == "last-modified":
|
||||
time=datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ")
|
||||
self._store.append([tag+":", time.strftime("%a %d %B %Y, %H∶%M UTC"), value])
|
||||
elif tag == "format":
|
||||
self._store.append([tag+":", ClientHelper.convert_audio_format(value), value])
|
||||
self._store.append([tag+":", str(value), locale.str(value)])
|
||||
elif tag in ("last-modified", "format"):
|
||||
self._store.append([tag+":", str(value), value.raw()])
|
||||
else:
|
||||
self._store.append([tag+":", value, GLib.markup_escape_text(value)])
|
||||
self._store.append([tag+":", str(value), GLib.markup_escape_text(str(value))])
|
||||
abs_path=self._client.get_absolute_path(uri)
|
||||
if abs_path is None: # show open with button when song is on the same computer
|
||||
self._open_button_revealer.set_reveal_child(False)
|
||||
@ -1785,8 +1796,7 @@ class AlbumPopover(Gtk.Popover):
|
||||
self._client.restrict_tagtypes("track", "title", "artist")
|
||||
songs=self._client.find("album", album, "date", date, self._settings.get_artist_type(), album_artist, *genre_filter)
|
||||
self._client.tagtypes("all")
|
||||
for s in songs:
|
||||
song=ClientHelper.song_to_list_dict(ClientHelper.pepare_song_for_display(s))
|
||||
for song in songs:
|
||||
track=song["track"][0]
|
||||
title=(", ".join(song["title"]))
|
||||
# only show artists =/= albumartist
|
||||
@ -1794,12 +1804,12 @@ class AlbumPopover(Gtk.Popover):
|
||||
song["artist"].remove(album_artist)
|
||||
except:
|
||||
pass
|
||||
artist=(", ".join(song["artist"]))
|
||||
artist=str(song["artist"])
|
||||
if artist == album_artist or artist == "":
|
||||
title_artist=f"<b>{GLib.markup_escape_text(title)}</b>"
|
||||
else:
|
||||
title_artist=f"<b>{GLib.markup_escape_text(title)}</b> • {GLib.markup_escape_text(artist)}"
|
||||
self._store.append([track, title_artist, song["human_duration"][0], song["file"][0], title])
|
||||
self._store.append([track, title_artist, str(song["duration"]), song["file"], title])
|
||||
self._songs_view.set_model(self._store)
|
||||
self.popup()
|
||||
self._songs_view.columns_autosize()
|
||||
@ -1990,19 +2000,18 @@ class SearchWindow(Gtk.Box):
|
||||
stripe_start=stripe_size
|
||||
while songs:
|
||||
hits+=len(songs)
|
||||
for s in songs:
|
||||
for song in songs:
|
||||
if self._stop_flag:
|
||||
self._done_callback()
|
||||
return
|
||||
song=ClientHelper.song_to_str_dict(ClientHelper.pepare_song_for_display(s))
|
||||
try:
|
||||
int_track=int(song["track"])
|
||||
int_track=int(song["track"][0])
|
||||
except:
|
||||
int_track=0
|
||||
self._store.insert_with_valuesv(-1, range(7), [
|
||||
song["track"], song["title"],
|
||||
song["artist"], song["album"],
|
||||
song["human_duration"], song["file"],
|
||||
str(song["track"]), str(song["title"]),
|
||||
str(song["artist"]), str(song["album"]),
|
||||
str(song["duration"]), song["file"],
|
||||
int_track
|
||||
])
|
||||
self.search_entry.progress_pulse()
|
||||
@ -2193,11 +2202,11 @@ class ArtistWindow(SelectionList):
|
||||
if genre is not None:
|
||||
self.select_all()
|
||||
else:
|
||||
song=ClientHelper.song_to_first_str_dict(self._client.currentsong())
|
||||
if song != {}:
|
||||
artist=song.get(self._settings.get_artist_type())
|
||||
if artist is None:
|
||||
artist=song.get("artist", "")
|
||||
song=self._client.currentsong()
|
||||
if song:
|
||||
artist=song[self._settings.get_artist_type()][0]
|
||||
if not artist:
|
||||
artist=song["artist"][0]
|
||||
self.select(artist)
|
||||
else:
|
||||
if self.length() > 0:
|
||||
@ -2315,8 +2324,8 @@ class AlbumWindow(FocusFrame):
|
||||
|
||||
def scroll_to_current_album(self):
|
||||
def callback():
|
||||
song=ClientHelper.song_to_first_str_dict(self._client.currentsong())
|
||||
album=song.get("album", "")
|
||||
song=self._client.currentsong()
|
||||
album=song["album"][0]
|
||||
self._iconview.unselect_all()
|
||||
row_num=len(self._store)
|
||||
for i in range(0, row_num):
|
||||
@ -2381,7 +2390,7 @@ class AlbumWindow(FocusFrame):
|
||||
fallback_cover=GdkPixbuf.Pixbuf.new_from_file_at_size(FALLBACK_COVER, size, size)
|
||||
for i, album in enumerate(albums):
|
||||
# tooltip
|
||||
duration=ClientHelper.seconds_to_display_time(int(album["duration"]))
|
||||
duration=str(album["duration"])
|
||||
length=int(album["length"])
|
||||
tooltip=ngettext("{number} song ({duration})", "{number} songs ({duration})", length).format(
|
||||
number=length, duration=duration)
|
||||
@ -2564,16 +2573,16 @@ class Browser(Gtk.Paned):
|
||||
self.pack2(self._albums_search_stack, True, False)
|
||||
|
||||
def _back_to_current_album(self, force=False):
|
||||
song=ClientHelper.song_to_first_str_dict(self._client.currentsong())
|
||||
if song != {}:
|
||||
song=self._client.currentsong()
|
||||
if song:
|
||||
self.search_button.set_active(False)
|
||||
self._genres_button.set_active(False)
|
||||
# get artist name
|
||||
artist=song.get(self._settings.get_artist_type())
|
||||
if artist is None:
|
||||
artist=song.get("artist", "")
|
||||
artist=song[self._settings.get_artist_type()][0]
|
||||
if not artist:
|
||||
artist=song["artist"][0]
|
||||
# deactivate genre filter to show all artists (if needed)
|
||||
if song.get("genre", "") != self._genre_select.get_selected() or force:
|
||||
if song["genre"][0] != self._genre_select.get_selected() or force:
|
||||
self._genre_select.deactivate()
|
||||
# select artist
|
||||
if self._artist_window.get_selected() is None and not force: # all artists selected
|
||||
@ -2717,7 +2726,7 @@ class LyricsWindow(FocusFrame):
|
||||
def _display_lyrics(self, current_song):
|
||||
GLib.idle_add(self._text_buffer.set_text, _("searching…"), -1)
|
||||
try:
|
||||
text=self._get_lyrics(current_song.get("title", ""), current_song.get("artist", ""))
|
||||
text=self._get_lyrics(current_song["title"][0], current_song["artist"][0])
|
||||
except requests.exceptions.ConnectionError:
|
||||
self._displayed_song_file=None
|
||||
text=_("connection error")
|
||||
@ -2727,17 +2736,17 @@ class LyricsWindow(FocusFrame):
|
||||
|
||||
def _refresh(self, *args):
|
||||
current_song=self._client.currentsong()
|
||||
if current_song == {}:
|
||||
self._displayed_song_file=None
|
||||
self._text_buffer.set_text("", -1)
|
||||
else:
|
||||
if current_song:
|
||||
self._displayed_song_file=current_song["file"]
|
||||
update_thread=threading.Thread(
|
||||
target=self._display_lyrics,
|
||||
kwargs={"current_song": ClientHelper.song_to_first_str_dict(current_song)},
|
||||
kwargs={"current_song": current_song},
|
||||
daemon=True
|
||||
)
|
||||
update_thread.start()
|
||||
else:
|
||||
self._displayed_song_file=None
|
||||
self._text_buffer.set_text("", -1)
|
||||
|
||||
def _on_disconnected(self, *args):
|
||||
self._displayed_song_file=None
|
||||
@ -2763,14 +2772,13 @@ class CoverEventBox(Gtk.EventBox):
|
||||
window.begin_move_drag(1, event.x_root, event.y_root, Gdk.CURRENT_TIME)
|
||||
else:
|
||||
if self._client.connected():
|
||||
song=ClientHelper.song_to_first_str_dict(self._client.currentsong())
|
||||
if song != {}:
|
||||
try:
|
||||
artist=song[self._settings.get_artist_type()]
|
||||
except:
|
||||
artist=song.get("artist", "")
|
||||
album=song.get("album", "")
|
||||
year=song.get("date", "")
|
||||
song=self._client.currentsong()
|
||||
if song:
|
||||
artist=song[self._settings.get_artist_type()][0]
|
||||
if not artist:
|
||||
artist=song["artist"][0]
|
||||
album=song["album"][0]
|
||||
year=song["date"][0]
|
||||
if event.button == 1 and event.type == Gdk.EventType.BUTTON_PRESS:
|
||||
self._client.album_to_playlist(album, artist, year, None)
|
||||
elif event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS:
|
||||
@ -3031,32 +3039,31 @@ class PlaylistWindow(Gtk.Overlay):
|
||||
else:
|
||||
songs=self._client.playlistinfo()
|
||||
self._client.tagtypes("all")
|
||||
if songs != []:
|
||||
if songs:
|
||||
self._treeview.freeze_child_notify()
|
||||
self._set_playlist_info("")
|
||||
for s in songs:
|
||||
song=ClientHelper.song_to_str_dict(ClientHelper.pepare_song_for_display(s))
|
||||
for song in songs:
|
||||
try:
|
||||
treeiter=self._store.get_iter(song["pos"])
|
||||
self._store.set(treeiter,
|
||||
0, song["track"],
|
||||
1, song["disc"],
|
||||
2, song["title"],
|
||||
3, song["artist"],
|
||||
4, song["album"],
|
||||
5, song["human_duration"],
|
||||
6, song["date"],
|
||||
7, song["genre"],
|
||||
0, str(song["track"]),
|
||||
1, str(song["disc"]),
|
||||
2, str(song["title"]),
|
||||
3, str(song["artist"]),
|
||||
4, str(song["album"]),
|
||||
5, str(song["duration"]),
|
||||
6, str(song["date"]),
|
||||
7, str(song["genre"]),
|
||||
8, song["file"],
|
||||
9, Pango.Weight.BOOK,
|
||||
10, float(song["duration"])
|
||||
)
|
||||
except:
|
||||
self._store.insert_with_valuesv(-1, range(11), [
|
||||
song["track"], song["disc"],
|
||||
song["title"], song["artist"],
|
||||
song["album"], song["human_duration"],
|
||||
song["date"], song["genre"],
|
||||
str(song["track"]), str(song["disc"]),
|
||||
str(song["title"]), str(song["artist"]),
|
||||
str(song["album"]), str(song["duration"]),
|
||||
str(song["date"]), str(song["genre"]),
|
||||
song["file"], Pango.Weight.BOOK,
|
||||
float(song["duration"])
|
||||
])
|
||||
@ -3068,9 +3075,9 @@ class PlaylistWindow(Gtk.Overlay):
|
||||
if playlist_length == 0:
|
||||
self._set_playlist_info("")
|
||||
else:
|
||||
duration_human_readable=ClientHelper.seconds_to_display_time(int(sum([row[10] for row in self._store])))
|
||||
duration=Duration(sum([row[10] for row in self._store]))
|
||||
translated_string=ngettext("{number} song ({duration})", "{number} songs ({duration})", playlist_length)
|
||||
self._set_playlist_info(translated_string.format(number=playlist_length, duration=duration_human_readable))
|
||||
self._set_playlist_info(translated_string.format(number=playlist_length, duration=duration))
|
||||
self._refresh_selection()
|
||||
if self._playlist_version != version:
|
||||
self._scroll_to_selected_title()
|
||||
@ -3312,12 +3319,12 @@ class SeekBar(Gtk.Box):
|
||||
self._adjustment.set_upper(duration)
|
||||
if self._update:
|
||||
self._scale.set_value(elapsed)
|
||||
self._elapsed.set_text(ClientHelper.seconds_to_display_time(int(elapsed)))
|
||||
self._rest.set_text("-"+ClientHelper.seconds_to_display_time(int(duration-elapsed)))
|
||||
self._elapsed.set_text(str(Duration(elapsed)))
|
||||
self._rest.set_text(f"-{Duration(duration-elapsed)}")
|
||||
self._scale.set_fill_level(elapsed)
|
||||
else:
|
||||
self._disable()
|
||||
self._elapsed.set_text(ClientHelper.seconds_to_display_time(int(elapsed)))
|
||||
self._elapsed.set_text(str(Duration(elapsed)))
|
||||
|
||||
def _disable(self, *args):
|
||||
self.set_sensitive(False)
|
||||
@ -3354,8 +3361,8 @@ class SeekBar(Gtk.Box):
|
||||
elapsed=duration
|
||||
else:
|
||||
elapsed=value
|
||||
self._elapsed.set_text(ClientHelper.seconds_to_display_time(int(elapsed)))
|
||||
self._rest.set_text("-"+ClientHelper.seconds_to_display_time(int(duration-elapsed)))
|
||||
self._elapsed.set_text(str(Duration(elapsed)))
|
||||
self._rest.set_text(f"-{Duration(duration-elapsed)}")
|
||||
self._jumped=True
|
||||
|
||||
def _on_elapsed_button_release_event(self, widget, event):
|
||||
@ -3410,7 +3417,7 @@ class AudioFormat(Gtk.Box):
|
||||
if audio_format is None:
|
||||
self._format_label.set_markup("<small> </small>")
|
||||
else:
|
||||
self._format_label.set_markup(f"<small>{ClientHelper.convert_audio_format(audio_format)}</small>")
|
||||
self._format_label.set_markup(f"<small>{Format(audio_format)}</small>")
|
||||
|
||||
def _on_bitrate(self, emitter, brate):
|
||||
# handle unknown bitrates: https://github.com/MusicPlayerDaemon/MPD/issues/428#issuecomment-442430365
|
||||
@ -3987,29 +3994,28 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
|
||||
def _on_song_changed(self, *args):
|
||||
song=self._client.currentsong()
|
||||
if song == {}:
|
||||
self.set_title("mpdevil")
|
||||
if self._use_csd:
|
||||
self._header_bar.set_subtitle("")
|
||||
else:
|
||||
song=ClientHelper.song_to_str_dict(ClientHelper.pepare_song_for_display(song))
|
||||
if song["date"] == "":
|
||||
date=""
|
||||
else:
|
||||
if song:
|
||||
if "date" in song:
|
||||
date=f"({song['date']})"
|
||||
album_with_date=" ".join(filter(None, (song["album"], date)))
|
||||
else:
|
||||
date=""
|
||||
album_with_date=" ".join(filter(None, (str(song["album"]), date)))
|
||||
if self._use_csd:
|
||||
self.set_title(" • ".join(filter(None, (song["title"], song["artist"]))))
|
||||
self.set_title(" • ".join(filter(None, (str(song["title"]), str(song["artist"])))))
|
||||
self._header_bar.set_subtitle(album_with_date)
|
||||
else:
|
||||
self.set_title(" • ".join(filter(None, (song["title"], song["artist"], album_with_date))))
|
||||
self.set_title(" • ".join(filter(None, (str(song["title"]), str(song["artist"]), album_with_date))))
|
||||
if self._settings.get_boolean("send-notify"):
|
||||
if not self.is_active() and self._client.status()["state"] == "play":
|
||||
self._notify.close() # clear previous notifications
|
||||
self._notify.update(song["title"], f"{song['artist']}\n{song['album']}{date}")
|
||||
self._notify.update(str(song["title"]), f"{song['artist']}\n{song['album']}{date}")
|
||||
pixbuf=self._client.get_cover(song, 400)
|
||||
self._notify.set_image_from_pixbuf(pixbuf)
|
||||
self._notify.show()
|
||||
else:
|
||||
self.set_title("mpdevil")
|
||||
if self._use_csd:
|
||||
self._header_bar.set_subtitle("")
|
||||
|
||||
def _on_reconnected(self, *args):
|
||||
for action in ("stats","toggle-lyrics","back-to-current-album","toggle-search"):
|
||||
|
Loading…
Reference in New Issue
Block a user