reworked strings

This commit is contained in:
Martin Wagner 2020-09-24 21:17:10 +02:00
parent ef57d30153
commit 313e109f9d
3 changed files with 312 additions and 303 deletions

View File

@ -19,8 +19,8 @@
# USA # USA
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version("Gtk", "3.0")
gi.require_version('Notify', '0.7') gi.require_version("Notify", "0.7")
from gi.repository import Gtk, Gio, Gdk, GdkPixbuf, Pango, GObject, GLib, Notify from gi.repository import Gtk, Gio, Gdk, GdkPixbuf, Pango, GObject, GLib, Notify
from mpd import MPDClient, base as MPDBase from mpd import MPDClient, base as MPDBase
import requests import requests
@ -28,7 +28,7 @@ from bs4 import BeautifulSoup
import threading import threading
import locale import locale
import gettext import gettext
gettext.textdomain('mpdevil') gettext.textdomain("mpdevil")
_=gettext.gettext _=gettext.gettext
import datetime import datetime
import os import os
@ -41,8 +41,8 @@ import dbus.service
from dbus.mainloop.glib import DBusGMainLoop from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True) DBusGMainLoop(set_as_default=True)
VERSION='0.9.1-dev' # sync with setup.py VERSION="0.9.1-dev" # sync with setup.py
COVER_REGEX="^\.?(album|cover|folder|front).*\.(gif|jpeg|jpg|png)$" COVER_REGEX=r"^\.?(album|cover|folder|front).*\.(gif|jpeg|jpg|png)$"
######### #########
@ -57,27 +57,27 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
# MPRIS allowed metadata tags # MPRIS allowed metadata tags
allowed_tags={ allowed_tags={
'mpris:trackid': dbus.ObjectPath, "mpris:trackid": dbus.ObjectPath,
'mpris:length': dbus.Int64, "mpris:length": dbus.Int64,
'mpris:artUrl': str, "mpris:artUrl": str,
'xesam:album': str, "xesam:album": str,
'xesam:albumArtist': list, "xesam:albumArtist": list,
'xesam:artist': list, "xesam:artist": list,
'xesam:asText': str, "xesam:asText": str,
'xesam:audioBPM': int, "xesam:audioBPM": int,
'xesam:comment': list, "xesam:comment": list,
'xesam:composer': list, "xesam:composer": list,
'xesam:contentCreated': str, "xesam:contentCreated": str,
'xesam:discNumber': int, "xesam:discNumber": int,
'xesam:firstUsed': str, "xesam:firstUsed": str,
'xesam:genre': list, "xesam:genre": list,
'xesam:lastUsed': str, "xesam:lastUsed": str,
'xesam:lyricist': str, "xesam:lyricist": str,
'xesam:title': str, "xesam:title": str,
'xesam:trackNumber': int, "xesam:trackNumber": int,
'xesam:url': str, "xesam:url": str,
'xesam:useCount': int, "xesam:useCount": int,
'xesam:userRating': float, "xesam:userRating": float,
} }
def __init__(self, window, client, settings): def __init__(self, window, client, settings):
@ -120,74 +120,74 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
mpd_meta=self._client.wrapped_call("currentsong") mpd_meta=self._client.wrapped_call("currentsong")
self._metadata={} self._metadata={}
for tag in ('album', 'title'): for tag in ("album", "title"):
if tag in mpd_meta: if tag in mpd_meta:
self._metadata['xesam:%s' % tag]=mpd_meta[tag] self._metadata["xesam:{}".format(tag)]=mpd_meta[tag]
if 'id' in mpd_meta: if "id" in mpd_meta:
self._metadata['mpris:trackid']="/org/mpris/MediaPlayer2/Track/%s" % mpd_meta['id'] self._metadata["mpris:trackid"]="/org/mpris/MediaPlayer2/Track/{}".format(mpd_meta["id"])
if 'time' in mpd_meta: if "time" in mpd_meta:
self._metadata['mpris:length']=int(mpd_meta['time']) * 1000000 self._metadata["mpris:length"]=int(mpd_meta["time"]) * 1000000
if 'date' in mpd_meta: if "date" in mpd_meta:
self._metadata['xesam:contentCreated']=mpd_meta['date'][0:4] self._metadata["xesam:contentCreated"]=mpd_meta["date"][0:4]
if 'track' in mpd_meta: if "track" in mpd_meta:
# TODO: Is it even *possible* for mpd_meta['track'] to be a list? # TODO: Is it even *possible* for mpd_meta["track"] to be a list?
if type(mpd_meta['track']) == list and len(mpd_meta['track']) > 0: if type(mpd_meta["track"]) == list and len(mpd_meta["track"]) > 0:
track=str(mpd_meta['track'][0]) track=str(mpd_meta["track"][0])
else: else:
track=str(mpd_meta['track']) track=str(mpd_meta["track"])
m=re.match('^([0-9]+)', track) m=re.match("^([0-9]+)", track)
if m: if m:
self._metadata['xesam:trackNumber']=int(m.group(1)) self._metadata["xesam:trackNumber"]=int(m.group(1))
# Ensure the integer is signed 32bit # Ensure the integer is signed 32bit
if self._metadata['xesam:trackNumber'] & 0x80000000: if self._metadata["xesam:trackNumber"] & 0x80000000:
self._metadata['xesam:trackNumber'] += -0x100000000 self._metadata["xesam:trackNumber"] += -0x100000000
else: else:
self._metadata['xesam:trackNumber']=0 self._metadata["xesam:trackNumber"]=0
if 'disc' in mpd_meta: if "disc" in mpd_meta:
# TODO: Same as above. When is it a list? # TODO: Same as above. When is it a list?
if type(mpd_meta['disc']) == list and len(mpd_meta['disc']) > 0: if type(mpd_meta["disc"]) == list and len(mpd_meta["disc"]) > 0:
disc=str(mpd_meta['disc'][0]) disc=str(mpd_meta["disc"][0])
else: else:
disc=str(mpd_meta['disc']) disc=str(mpd_meta["disc"])
m=re.match('^([0-9]+)', disc) m=re.match("^([0-9]+)", disc)
if m: if m:
self._metadata['xesam:discNumber']=int(m.group(1)) self._metadata["xesam:discNumber"]=int(m.group(1))
if 'artist' in mpd_meta: if "artist" in mpd_meta:
if type(mpd_meta['artist']) == list: if type(mpd_meta["artist"]) == list:
self._metadata['xesam:artist']=mpd_meta['artist'] self._metadata["xesam:artist"]=mpd_meta["artist"]
else: else:
self._metadata['xesam:artist']=[mpd_meta['artist']] self._metadata["xesam:artist"]=[mpd_meta["artist"]]
if 'composer' in mpd_meta: if "composer" in mpd_meta:
if type(mpd_meta['composer']) == list: if type(mpd_meta["composer"]) == list:
self._metadata['xesam:composer']=mpd_meta['composer'] self._metadata["xesam:composer"]=mpd_meta["composer"]
else: else:
self._metadata['xesam:composer']=[mpd_meta['composer']] self._metadata["xesam:composer"]=[mpd_meta["composer"]]
# Stream: populate some missings tags with stream's name # Stream: populate some missings tags with stream's name
if 'name' in mpd_meta: if "name" in mpd_meta:
if 'xesam:title' not in self._metadata: if "xesam:title" not in self._metadata:
self._metadata['xesam:title']=mpd_meta['name'] self._metadata["xesam:title"]=mpd_meta["name"]
elif 'xesam:album' not in self._metadata: elif "xesam:album" not in self._metadata:
self._metadata['xesam:album']=mpd_meta['name'] self._metadata["xesam:album"]=mpd_meta["name"]
if 'file' in mpd_meta: if "file" in mpd_meta:
song_file=mpd_meta['file'] song_file=mpd_meta["file"]
lib_path=self._settings.get_value("paths")[self._settings.get_int("active-profile")] lib_path=self._settings.get_value("paths")[self._settings.get_int("active-profile")]
self._metadata['xesam:url']="file://"+os.path.join(lib_path, song_file) self._metadata["xesam:url"]="file://{}".format(os.path.join(lib_path, song_file))
cover=Cover(self._settings, mpd_meta) cover=Cover(self._settings, mpd_meta)
if cover.path is None: if cover.path is None:
self._metadata['mpris:artUrl']=None self._metadata["mpris:artUrl"]=None
else: else:
self._metadata['mpris:artUrl']="file://"+cover.path self._metadata["mpris:artUrl"]="file://{}".format(cover.path)
# 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():
@ -209,7 +209,7 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
def __get_playback_status(self): def __get_playback_status(self):
status=self._client.wrapped_call("status") status=self._client.wrapped_call("status")
return {'play': 'Playing', 'pause': 'Paused', 'stop': 'Stopped'}[status['state']] return {"play": "Playing", "pause": "Paused", "stop": "Stopped"}[status["state"]]
def __set_loop_status(self, value): def __set_loop_status(self, value):
if value == "Playlist": if value == "Playlist":
@ -222,13 +222,13 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
self._client.wrapped_call("repeat", 0) self._client.wrapped_call("repeat", 0)
self._client.wrapped_call("single", 0) self._client.wrapped_call("single", 0)
else: else:
raise dbus.exceptions.DBusException("Loop mode %r not supported" % value) raise dbus.exceptions.DBusException("Loop mode '{}' not supported".format(value))
return return
def __get_loop_status(self): def __get_loop_status(self):
status=self._client.wrapped_call("status") status=self._client.wrapped_call("status")
if int(status['repeat']) == 1: if int(status["repeat"]) == 1:
if int(status.get('single', 0)) == 1: if int(status.get("single", 0)) == 1:
return "Track" return "Track"
else: else:
return "Playlist" return "Playlist"
@ -240,16 +240,16 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
return return
def __get_shuffle(self): def __get_shuffle(self):
if int(self._client.wrapped_call("status")['random']) == 1: if int(self._client.wrapped_call("status")["random"]) == 1:
return True return True
else: else:
return False return False
def __get_metadata(self): def __get_metadata(self):
return dbus.Dictionary(self._metadata, signature='sv') return dbus.Dictionary(self._metadata, signature="sv")
def __get_volume(self): def __get_volume(self):
vol=float(self._client.wrapped_call("status").get('volume', 0)) vol=float(self._client.wrapped_call("status").get("volume", 0))
if vol > 0: if vol > 0:
return vol / 100.0 return vol / 100.0
else: else:
@ -262,15 +262,15 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
def __get_position(self): def __get_position(self):
status=self._client.wrapped_call("status") status=self._client.wrapped_call("status")
if 'time' in status: if "time" in status:
current, end=status['time'].split(':') current, end=status["time"].split(":")
return dbus.Int64((int(current) * 1000000)) return dbus.Int64((int(current) * 1000000))
else: else:
return dbus.Int64(0) return dbus.Int64(0)
def __get_can_next_prev(self): def __get_can_next_prev(self):
status=self._client.wrapped_call("status") status=self._client.wrapped_call("status")
if status['state'] == "stop": if status["state"] == "stop":
return False return False
else: else:
return True return True
@ -336,103 +336,103 @@ class MPRISInterface(dbus.service.Object): # TODO emit Seeked if needed
return value return value
# Root methods # Root methods
@dbus.service.method(__root_interface, in_signature='', out_signature='') @dbus.service.method(__root_interface, in_signature="", out_signature="")
def Raise(self): def Raise(self):
self._window.present() self._window.present()
return return
@dbus.service.method(__root_interface, in_signature='', out_signature='') @dbus.service.method(__root_interface, in_signature="", out_signature="")
def Quit(self): def Quit(self):
return return
# Player methods # Player methods
@dbus.service.method(__player_interface, in_signature='', out_signature='') @dbus.service.method(__player_interface, in_signature="", out_signature="")
def Next(self): def Next(self):
self._client.wrapped_call("next") self._client.wrapped_call("next")
return return
@dbus.service.method(__player_interface, in_signature='', out_signature='') @dbus.service.method(__player_interface, in_signature="", out_signature="")
def Previous(self): def Previous(self):
self._client.wrapped_call("previous") self._client.wrapped_call("previous")
return return
@dbus.service.method(__player_interface, in_signature='', out_signature='') @dbus.service.method(__player_interface, in_signature="", out_signature="")
def Pause(self): def Pause(self):
self._client.wrapped_call("pause", 1) self._client.wrapped_call("pause", 1)
return return
@dbus.service.method(__player_interface, in_signature='', out_signature='') @dbus.service.method(__player_interface, in_signature="", out_signature="")
def PlayPause(self): def PlayPause(self):
status=self._client.wrapped_call("status") status=self._client.wrapped_call("status")
if status['state'] == 'play': if status["state"] == "play":
self._client.wrapped_call("pause", 1) self._client.wrapped_call("pause", 1)
else: else:
self._client.wrapped_call("play") self._client.wrapped_call("play")
return return
@dbus.service.method(__player_interface, in_signature='', out_signature='') @dbus.service.method(__player_interface, in_signature="", out_signature="")
def Stop(self): def Stop(self):
self._client.wrapped_call("stop") self._client.wrapped_call("stop")
return return
@dbus.service.method(__player_interface, in_signature='', out_signature='') @dbus.service.method(__player_interface, in_signature="", out_signature="")
def Play(self): def Play(self):
self._client.wrapped_call("play") self._client.wrapped_call("play")
return return
@dbus.service.method(__player_interface, in_signature='x', out_signature='') @dbus.service.method(__player_interface, in_signature="x", out_signature="")
def Seek(self, offset): # TODO def Seek(self, offset): # TODO
status=self._client.wrapped_call("status") status=self._client.wrapped_call("status")
current, end=status['time'].split(':') current, end=status["time"].split(":")
current=int(current) current=int(current)
end=int(end) end=int(end)
offset=int(offset) / 1000000 offset=int(offset) / 1000000
if current + offset <= end: if current+offset <= end:
position=current + offset position=current+offset
if position < 0: if position < 0:
position=0 position=0
self._client.wrapped_call("seekid", int(status['songid']), position) self._client.wrapped_call("seekid", int(status["songid"]), position)
self.Seeked(position * 1000000) self.Seeked(position * 1000000)
return return
@dbus.service.method(__player_interface, in_signature='ox', out_signature='') @dbus.service.method(__player_interface, in_signature="ox", out_signature="")
def SetPosition(self, trackid, position): def SetPosition(self, trackid, position):
song=self._client.wrapped_call("currentsong") song=self._client.wrapped_call("currentsong")
# FIXME: use real dbus objects # FIXME: use real dbus objects
if str(trackid) != '/org/mpris/MediaPlayer2/Track/%s' % song['id']: if str(trackid) != "/org/mpris/MediaPlayer2/Track/{}".format(song["id"]):
return return
# Convert position to seconds # Convert position to seconds
position=int(position) / 1000000 position=int(position) / 1000000
if position <= int(song['time']): if position <= int(song["time"]):
self._client.wrapped_call("seekid", int(song['id']), position) self._client.wrapped_call("seekid", int(song["id"]), position)
self.Seeked(position * 1000000) self.Seeked(position * 1000000)
return return
@dbus.service.signal(__player_interface, signature='x') @dbus.service.signal(__player_interface, signature="x")
def Seeked(self, position): def Seeked(self, position):
return float(position) return float(position)
@dbus.service.method(__player_interface, in_signature='', out_signature='') @dbus.service.method(__player_interface, in_signature="", out_signature="")
def OpenUri(self): def OpenUri(self):
return return
def _on_state_changed(self, *args): def _on_state_changed(self, *args):
self.update_property('org.mpris.MediaPlayer2.Player', 'PlaybackStatus') self.update_property("org.mpris.MediaPlayer2.Player", "PlaybackStatus")
self.update_property('org.mpris.MediaPlayer2.Player', 'CanGoNext') self.update_property("org.mpris.MediaPlayer2.Player", "CanGoNext")
self.update_property('org.mpris.MediaPlayer2.Player', 'CanGoPrevious') self.update_property("org.mpris.MediaPlayer2.Player", "CanGoPrevious")
def _on_song_changed(self, *args): def _on_song_changed(self, *args):
self.update_metadata() self.update_metadata()
self.update_property('org.mpris.MediaPlayer2.Player', 'Metadata') self.update_property("org.mpris.MediaPlayer2.Player", "Metadata")
def _on_volume_changed(self, *args): def _on_volume_changed(self, *args):
self.update_property('org.mpris.MediaPlayer2.Player', 'Volume') self.update_property("org.mpris.MediaPlayer2.Player", "Volume")
def _on_loop_changed(self, *args): def _on_loop_changed(self, *args):
self.update_property('org.mpris.MediaPlayer2.Player', 'LoopStatus') self.update_property("org.mpris.MediaPlayer2.Player", "LoopStatus")
def _on_random_changed(self, *args): def _on_random_changed(self, *args):
self.update_property('org.mpris.MediaPlayer2.Player', 'Shuffle') self.update_property("org.mpris.MediaPlayer2.Player", "Shuffle")
def _on_reconnected(self, *args): def _on_reconnected(self, *args):
self.acquire_name() self.acquire_name()
@ -458,7 +458,7 @@ class ClientHelper():
return_song=song return_song=song
for tag, value in return_song.items(): for tag, value in return_song.items():
if type(value) == list: if type(value) == list:
return_song[tag]=(', '.join(value)) return_song[tag]=(", ".join(value))
return return_song return return_song
def song_to_first_str_dict(song): # extracts the first value of multiple value tags def song_to_first_str_dict(song): # extracts the first value of multiple value tags
@ -491,21 +491,21 @@ class ClientHelper():
class MpdEventEmitter(GObject.Object): class MpdEventEmitter(GObject.Object):
__gsignals__={ __gsignals__={
'update': (GObject.SignalFlags.RUN_FIRST, None, ()), "update": (GObject.SignalFlags.RUN_FIRST, None, ()),
'disconnected': (GObject.SignalFlags.RUN_FIRST, None, ()), "disconnected": (GObject.SignalFlags.RUN_FIRST, None, ()),
'reconnected': (GObject.SignalFlags.RUN_FIRST, None, ()), "reconnected": (GObject.SignalFlags.RUN_FIRST, None, ()),
'connection_error': (GObject.SignalFlags.RUN_FIRST, None, ()), "connection_error": (GObject.SignalFlags.RUN_FIRST, None, ()),
'current_song_changed': (GObject.SignalFlags.RUN_FIRST, None, ()), "current_song_changed": (GObject.SignalFlags.RUN_FIRST, None, ()),
'state': (GObject.SignalFlags.RUN_FIRST, None, (str,)), "state": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
'elapsed_changed': (GObject.SignalFlags.RUN_FIRST, None, (float,float,)), "elapsed_changed": (GObject.SignalFlags.RUN_FIRST, None, (float,float,)),
'volume_changed': (GObject.SignalFlags.RUN_FIRST, None, (float,)), "volume_changed": (GObject.SignalFlags.RUN_FIRST, None, (float,)),
'playlist_changed': (GObject.SignalFlags.RUN_FIRST, None, (int,)), "playlist_changed": (GObject.SignalFlags.RUN_FIRST, None, (int,)),
'repeat': (GObject.SignalFlags.RUN_FIRST, None, (bool,)), "repeat": (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
'random': (GObject.SignalFlags.RUN_FIRST, None, (bool,)), "random": (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
'single': (GObject.SignalFlags.RUN_FIRST, None, (bool,)), "single": (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
'consume': (GObject.SignalFlags.RUN_FIRST, None, (bool,)), "consume": (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
'audio': (GObject.SignalFlags.RUN_FIRST, None, (str,str,str,)), "audio": (GObject.SignalFlags.RUN_FIRST, None, (str,str,str,)),
'bitrate': (GObject.SignalFlags.RUN_FIRST, None, (float,)) "bitrate": (GObject.SignalFlags.RUN_FIRST, None, (float,))
} }
def __init__(self): def __init__(self):
@ -606,7 +606,7 @@ class Client(MPDClient):
def album_to_playlist(self, album, artist, year, mode="default"): def album_to_playlist(self, album, artist, year, mode="default"):
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], mode) self.files_to_playlist([song["file"] for song in songs], mode)
def comp_list(self, *args): # simulates listing behavior of python-mpd2 1.0 def comp_list(self, *args): # simulates listing behavior of python-mpd2 1.0
native_list=self.list(*args) native_list=self.list(*args)
@ -639,7 +639,7 @@ class Client(MPDClient):
self.emitter.emit("state", val) self.emitter.emit("state", val)
elif key == "audio": elif key == "audio":
# see: https://www.musicpd.org/doc/html/user.html#audio-output-format # see: https://www.musicpd.org/doc/html/user.html#audio-output-format
samplerate, bits, channels=val.split(':') samplerate, bits, channels=val.split(":")
if bits == "f": if bits == "f":
bits="32fp" bits="32fp"
self.emitter.emit("audio", samplerate, bits, channels) self.emitter.emit("audio", samplerate, bits, channels)
@ -687,12 +687,12 @@ class Settings(Gio.Settings):
if len(self.get_value("profiles")) < (self.get_int("active-profile")+1): if len(self.get_value("profiles")) < (self.get_int("active-profile")+1):
self.set_int("active-profile", 0) self.set_int("active-profile", 0)
profile_keys=[ profile_keys=[
('as', "profiles", "new profile"), ("as", "profiles", "new profile"),
('as', "hosts", "localhost"), ("as", "hosts", "localhost"),
('ai', "ports", 6600), ("ai", "ports", 6600),
('as', "passwords", ""), ("as", "passwords", ""),
('as', "paths", ""), ("as", "paths", ""),
('as', "regex", "") ("as", "regex", "")
] ]
profile_arrays=[] profile_arrays=[]
for vtype, key, default in profile_keys: for vtype, key, default in profile_keys:
@ -756,7 +756,7 @@ class GeneralSettings(Gtk.Box):
int_settings[key][1].set_value(self._settings.get_int(key)) int_settings[key][1].set_value(self._settings.get_int(key))
int_settings[key][1].connect("value-changed", self._on_int_changed, key) int_settings[key][1].connect("value-changed", self._on_int_changed, key)
self._settings_handlers.append( self._settings_handlers.append(
self._settings.connect("changed::"+key, self._on_int_settings_changed, int_settings[key][1]) self._settings.connect("changed::{}".format(key), self._on_int_settings_changed, int_settings[key][1])
) )
# combo_settings # combo_settings
@ -775,7 +775,7 @@ class GeneralSettings(Gtk.Box):
combo_settings[key][1].set_active(0) combo_settings[key][1].set_active(0)
combo_settings[key][1].connect("changed", self._on_combo_changed, key) combo_settings[key][1].connect("changed", self._on_combo_changed, key)
self._settings_handlers.append( self._settings_handlers.append(
self._settings.connect("changed::"+key, self._on_combo_settings_changed, combo_settings[key][1]) self._settings.connect("changed::{}".format(key), self._on_combo_settings_changed, combo_settings[key][1])
) )
# check buttons # check buttons
@ -798,7 +798,7 @@ class GeneralSettings(Gtk.Box):
check_buttons[key].set_margin_start(12) check_buttons[key].set_margin_start(12)
check_buttons[key].connect("toggled", self._on_toggled, key) check_buttons[key].connect("toggled", self._on_toggled, key)
self._settings_handlers.append( self._settings_handlers.append(
self._settings.connect("changed::"+key, self._on_check_settings_changed, check_buttons[key]) self._settings.connect("changed::{}".format(key), self._on_check_settings_changed, check_buttons[key])
) )
# headings # headings
@ -997,24 +997,24 @@ class ProfileSettings(Gtk.Grid):
def _on_add_button_clicked(self, *args): def _on_add_button_clicked(self, *args):
model=self._profiles_combo.get_model() model=self._profiles_combo.get_model()
self._settings.array_append('as', "profiles", "new profile ("+str(len(model))+")") self._settings.array_append("as", "profiles", "new profile ({})".format(len(model)))
self._settings.array_append('as', "hosts", "localhost") self._settings.array_append("as", "hosts", "localhost")
self._settings.array_append('ai', "ports", 6600) self._settings.array_append("ai", "ports", 6600)
self._settings.array_append('as', "passwords", "") self._settings.array_append("as", "passwords", "")
self._settings.array_append('as', "paths", "") self._settings.array_append("as", "paths", "")
self._settings.array_append('as', "regex", "") self._settings.array_append("as", "regex", "")
self._profiles_combo_reload() self._profiles_combo_reload()
new_pos=len(model)-1 new_pos=len(model)-1
self._profiles_combo.set_active(new_pos) self._profiles_combo.set_active(new_pos)
def _on_delete_button_clicked(self, *args): def _on_delete_button_clicked(self, *args):
pos=self._profiles_combo.get_active() pos=self._profiles_combo.get_active()
self._settings.array_delete('as', "profiles", pos) self._settings.array_delete("as", "profiles", pos)
self._settings.array_delete('as', "hosts", pos) self._settings.array_delete("as", "hosts", pos)
self._settings.array_delete('ai', "ports", pos) self._settings.array_delete("ai", "ports", pos)
self._settings.array_delete('as', "passwords", pos) self._settings.array_delete("as", "passwords", pos)
self._settings.array_delete('as', "paths", pos) self._settings.array_delete("as", "paths", pos)
self._settings.array_delete('as', "regex", pos) self._settings.array_delete("as", "regex", pos)
if len(self._settings.get_value("profiles")) == 0: if len(self._settings.get_value("profiles")) == 0:
self._on_add_button_clicked() self._on_add_button_clicked()
else: else:
@ -1029,29 +1029,29 @@ class ProfileSettings(Gtk.Grid):
def _on_profile_entry_changed(self, *args): def _on_profile_entry_changed(self, *args):
self._gui_modification=True self._gui_modification=True
pos=self._profiles_combo.get_active() pos=self._profiles_combo.get_active()
self._settings.array_modify('as', "profiles", pos, self._profile_entry.get_text()) self._settings.array_modify("as", "profiles", pos, self._profile_entry.get_text())
self._profiles_combo_reload() self._profiles_combo_reload()
self._profiles_combo.set_active(pos) self._profiles_combo.set_active(pos)
def _on_host_entry_changed(self, *args): def _on_host_entry_changed(self, *args):
self._gui_modification=True self._gui_modification=True
self._settings.array_modify('as', "hosts", self._profiles_combo.get_active(), self._host_entry.get_text()) self._settings.array_modify("as", "hosts", self._profiles_combo.get_active(), self._host_entry.get_text())
def _on_port_entry_changed(self, *args): def _on_port_entry_changed(self, *args):
self._gui_modification=True self._gui_modification=True
self._settings.array_modify('ai', "ports", self._profiles_combo.get_active(), int(self._port_entry.get_value())) self._settings.array_modify("ai", "ports", self._profiles_combo.get_active(), int(self._port_entry.get_value()))
def _on_password_entry_changed(self, *args): def _on_password_entry_changed(self, *args):
self._gui_modification=True self._gui_modification=True
self._settings.array_modify('as', "passwords", self._profiles_combo.get_active(), self._password_entry.get_text()) self._settings.array_modify("as", "passwords", self._profiles_combo.get_active(), self._password_entry.get_text())
def _on_path_entry_changed(self, *args): def _on_path_entry_changed(self, *args):
self._gui_modification=True self._gui_modification=True
self._settings.array_modify('as', "paths", self._profiles_combo.get_active(), self._path_entry.get_text()) self._settings.array_modify("as", "paths", self._profiles_combo.get_active(), self._path_entry.get_text())
def _on_regex_entry_changed(self, *args): def _on_regex_entry_changed(self, *args):
self._gui_modification=True self._gui_modification=True
self._settings.array_modify('as', "regex", self._profiles_combo.get_active(), self._regex_entry.get_text()) self._settings.array_modify("as", "regex", self._profiles_combo.get_active(), self._regex_entry.get_text())
def _on_path_select_button_clicked(self, widget, parent): def _on_path_select_button_clicked(self, widget, parent):
dialog=Gtk.FileChooserDialog(title=_("Choose directory"), transient_for=parent, action=Gtk.FileChooserAction.SELECT_FOLDER) dialog=Gtk.FileChooserDialog(title=_("Choose directory"), transient_for=parent, action=Gtk.FileChooserAction.SELECT_FOLDER)
@ -1062,7 +1062,7 @@ class ProfileSettings(Gtk.Grid):
response=dialog.run() response=dialog.run()
if response == Gtk.ResponseType.OK: if response == Gtk.ResponseType.OK:
self._gui_modification=True self._gui_modification=True
self._settings.array_modify('as', "paths", self._profiles_combo.get_active(), dialog.get_filename()) self._settings.array_modify("as", "paths", self._profiles_combo.get_active(), dialog.get_filename())
self._path_entry.set_text(dialog.get_filename()) self._path_entry.set_text(dialog.get_filename())
dialog.destroy() dialog.destroy()
@ -1186,7 +1186,7 @@ class PlaylistSettings(Gtk.Box):
def _on_cell_toggled(self, widget, path): def _on_cell_toggled(self, widget, path):
self._store[path][0]=not self._store[path][0] self._store[path][0]=not self._store[path][0]
self._settings.array_modify('ab', "column-visibilities", self._store[path][2], self._store[path][0]) self._settings.array_modify("ab", "column-visibilities", self._store[path][2], self._store[path][0])
def _on_up_button_clicked(self, *args): def _on_up_button_clicked(self, *args):
treeiter=self._selection.get_selected()[1] treeiter=self._selection.get_selected()[1]
@ -1409,8 +1409,8 @@ class SongPopover(Gtk.Popover):
if tag == "time": if tag == "time":
store.append([tag+":", str(datetime.timedelta(seconds=int(value))), tooltip]) store.append([tag+":", str(datetime.timedelta(seconds=int(value))), tooltip])
elif tag == "last-modified": elif tag == "last-modified":
time=datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') time=datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ")
store.append([tag+":", time.strftime('%a %d %B %Y, %H:%M UTC'), tooltip]) store.append([tag+":", time.strftime("%a %d %B %Y, %H:%M UTC"), tooltip])
else: else:
store.append([tag+":", value, tooltip]) store.append([tag+":", value, tooltip])
frame.show_all() frame.show_all()
@ -1427,14 +1427,15 @@ class Cover(object):
regex_str=settings.get_value("regex")[active_profile] regex_str=settings.get_value("regex")[active_profile]
if regex_str == "": if regex_str == "":
regex=re.compile(r''+COVER_REGEX+'', flags=re.IGNORECASE) regex=re.compile(COVER_REGEX, flags=re.IGNORECASE)
else: else:
regex_str=regex_str.replace("%AlbumArtist%", song.get("albumartist", "")) regex_str=regex_str.replace("%AlbumArtist%", song.get("albumartist", ""))
regex_str=regex_str.replace("%Album%", song.get("album", "")) regex_str=regex_str.replace("%Album%", song.get("album", ""))
try: try:
regex=re.compile(r''+regex_str+'', flags=re.IGNORECASE) regex=re.compile(regex_str, flags=re.IGNORECASE)
except: except:
print("illegal regex:", regex_str) print("illegal regex:", regex_str)
return
if song_file is not None: if song_file is not None:
head, tail=os.path.split(song_file) head, tail=os.path.split(song_file)
@ -1710,14 +1711,14 @@ class SearchWindow(Gtk.Box):
song["human_duration"], song["file"], song["human_duration"], song["file"],
int(song.get("track", 0)) int(song.get("track", 0))
]) ])
self._hits_label.set_text(_("%i hits") % (self._songs_view.count())) self._hits_label.set_text(_("{num} hits").format(num=self._songs_view.count()))
if self._songs_view.count() == 0: if self._songs_view.count() == 0:
self._action_bar.set_sensitive(False) self._action_bar.set_sensitive(False)
else: else:
self._action_bar.set_sensitive(True) self._action_bar.set_sensitive(True)
class GenreSelect(Gtk.ComboBoxText): class GenreSelect(Gtk.ComboBoxText):
__gsignals__={'genre_changed': (GObject.SignalFlags.RUN_FIRST, None, ())} __gsignals__={"genre_changed": (GObject.SignalFlags.RUN_FIRST, None, ())}
def __init__(self, client): def __init__(self, client):
super().__init__() super().__init__()
@ -1766,7 +1767,7 @@ class GenreSelect(Gtk.ComboBoxText):
self.set_sensitive(True) self.set_sensitive(True)
class ArtistWindow(FocusFrame): class ArtistWindow(FocusFrame):
__gsignals__={'artists_changed': (GObject.SignalFlags.RUN_FIRST, None, ())} __gsignals__={"artists_changed": (GObject.SignalFlags.RUN_FIRST, None, ())}
def __init__(self, client, settings, genre_select): def __init__(self, client, settings, genre_select):
super().__init__() super().__init__()
@ -1926,11 +1927,11 @@ class AlbumDialog(Gtk.Dialog): # also used by 'MainCover'
self.set_default_size(w, h) self.set_default_size(w, h)
# title # title
album_duration=ClientHelper.calc_display_length(songs) duration=ClientHelper.calc_display_length(songs)
if year == "": if year == "":
self.set_title(album_artist+" - "+album+" ("+album_duration+")") self.set_title("{} - {} ({})".format(album_artist, album, duration))
else: else:
self.set_title(album_artist+" - "+album+" ("+year+") ("+album_duration+")") self.set_title("{} - {} ({}) ({})".format(album_artist, album, year, duration))
# store # store
# (track, title (artist), duration, file) # (track, title (artist), duration, file)
@ -1938,7 +1939,7 @@ class AlbumDialog(Gtk.Dialog): # also used by 'MainCover'
for s in songs: for s in songs:
song=ClientHelper.extend_song_for_display(s) song=ClientHelper.extend_song_for_display(s)
if type(song["title"]) == list: # could be impossible if type(song["title"]) == list: # could be impossible
title=(', '.join(song["title"])) title=(", ".join(song["title"]))
else: else:
title=song["title"] title=song["title"]
if type(song["artist"]) == list: if type(song["artist"]) == list:
@ -1946,14 +1947,13 @@ class AlbumDialog(Gtk.Dialog): # also used by 'MainCover'
song["artist"].remove(album_artist) song["artist"].remove(album_artist)
except: except:
pass pass
artist=(', '.join(song["artist"])) artist=(", ".join(song["artist"]))
else: else:
artist=song["artist"] artist=song["artist"]
if artist == album_artist: if artist == album_artist:
title_artist="<b>"+title+"</b>" title_artist="<b>{}</b>".format(title)
else: else:
title_artist="<b>"+title+"</b> - "+artist title_artist="<b>{}</b> - {}".format(title, artist)
title_artist=title_artist.replace("&", "&amp;") title_artist=title_artist.replace("&", "&amp;")
store.append([song["track"], title_artist, song["human_duration"], song["file"]]) store.append([song["track"], title_artist, song["human_duration"], song["file"]])
@ -2136,9 +2136,9 @@ class AlbumWindow(FocusFrame):
return return
# display albums # display albums
if self._settings.get_boolean("sort-albums-by-year"): if self._settings.get_boolean("sort-albums-by-year"):
albums=sorted(albums, key=lambda k: k['year']) albums=sorted(albums, key=lambda k: k["year"])
else: else:
albums=sorted(albums, key=lambda k: k['album']) albums=sorted(albums, key=lambda k: k["album"])
size=self._settings.get_int("album-cover") size=self._settings.get_int("album-cover")
for i, album in enumerate(albums): for i, album in enumerate(albums):
if self.stop_flag: if self.stop_flag:
@ -2149,15 +2149,16 @@ class AlbumWindow(FocusFrame):
length_human_readable=ClientHelper.calc_display_length(album["songs"]) length_human_readable=ClientHelper.calc_display_length(album["songs"])
discs=int(album["songs"][-1].get("disc", 1)) discs=int(album["songs"][-1].get("disc", 1))
if discs > 1: if discs > 1:
tooltip=(_("%(total_tracks)i titles on %(discs)i discs (%(total_length)s)") tooltip=_("{titles} titles on {discs} discs ({length})").format(
%{"total_tracks": len(album["songs"]), "discs": discs, "total_length": length_human_readable}) titles=len(album["songs"]), discs=discs, length=length_human_readable)
else: else:
tooltip=(_("%(total_tracks)i titles (%(total_length)s)") tooltip=_("{titles} titles ({length})").format(
%{"total_tracks": len(album["songs"]), "total_length": length_human_readable}) titles=len(album["songs"]), length=length_human_readable)
# album label # album label
display_label="<b>"+album["album"]+"</b>" if album["year"] == "":
if album["year"] != "": display_label="<b>{}</b>".format(album["album"])
display_label=display_label+" ("+album["year"]+")" else:
display_label="<b>{}</b> ({})".format(album["album"], album["year"])
display_label_artist=display_label+"\n"+album["artist"] display_label_artist=display_label+"\n"+album["artist"]
display_label=display_label.replace("&", "&amp;") display_label=display_label.replace("&", "&amp;")
display_label_artist=display_label_artist.replace("&", "&amp;") display_label_artist=display_label_artist.replace("&", "&amp;")
@ -2257,7 +2258,7 @@ class AlbumWindow(FocusFrame):
GLib.idle_add(callback) GLib.idle_add(callback)
class Browser(Gtk.Paned): class Browser(Gtk.Paned):
__gsignals__={'search_focus_changed': (GObject.SignalFlags.RUN_FIRST, None, (bool,))} __gsignals__={"search_focus_changed": (GObject.SignalFlags.RUN_FIRST, None, (bool,))}
def __init__(self, client, settings, window): def __init__(self, client, settings, window):
super().__init__(orientation=Gtk.Orientation.HORIZONTAL) # paned1 super().__init__(orientation=Gtk.Orientation.HORIZONTAL) # paned1
@ -2337,7 +2338,7 @@ class Browser(Gtk.Paned):
artist=song.get("artist", "") artist=song.get("artist", "")
# deactivate genre filter to show all artists (if needed) # deactivate genre filter to show all artists (if needed)
try: try:
if song['genre'] != self.genre_select.get_selected_genre(): if song["genre"] != self.genre_select.get_selected_genre():
self.genre_select.deactivate() self.genre_select.deactivate()
except: except:
self.genre_select.deactivate() self.genre_select.deactivate()
@ -2441,12 +2442,12 @@ class LyricsWindow(FocusFrame):
self._client.emitter.handler_block(self._song_changed) self._client.emitter.handler_block(self._song_changed)
def _get_lyrics(self, title, artist): def _get_lyrics(self, title, artist):
replaces=((' ', '+'),('.', '_'),('@', '_'),(',', '_'),(';', '_'),('&', '_'),('\\', '_'),('/', '_'),('"', '_'),('(', '_'),(')', '_')) replaces=((" ", "+"),(".", "_"),("@", "_"),(",", "_"),(";", "_"),("&", "_"),("\\", "_"),("/", "_"),('"', "_"),("(", "_"),(")", "_"))
for char1, char2 in replaces: for char1, char2 in replaces:
title=title.replace(char1, char2) title=title.replace(char1, char2)
artist=artist.replace(char1, char2) artist=artist.replace(char1, char2)
req=requests.get('https://www.letras.mus.br/winamp.php?musica={0}&artista={1}'.format(title,artist)) req=requests.get("https://www.letras.mus.br/winamp.php?musica={0}&artista={1}".format(title,artist))
soup=BeautifulSoup(req.text, 'html.parser') soup=BeautifulSoup(req.text, "html.parser")
soup=soup.find(id="letra-cnt") soup=soup.find(id="letra-cnt")
if soup is None: if soup is None:
raise ValueError("Not found") raise ValueError("Not found")
@ -2454,8 +2455,8 @@ class LyricsWindow(FocusFrame):
lyrics="" lyrics=""
for paragraph in paragraphs: for paragraph in paragraphs:
for line in paragraph.stripped_strings: for line in paragraph.stripped_strings:
lyrics+=line+'\n' lyrics+=line+"\n"
lyrics+='\n' lyrics+="\n"
output=lyrics[:-2] # omit last two newlines output=lyrics[:-2] # omit last two newlines
if output == "": # assume song is instrumental when lyrics are empty if output == "": # assume song is instrumental when lyrics are empty
return "Instrumental" return "Instrumental"
@ -2518,8 +2519,8 @@ class AudioType(Gtk.Label):
self.file_type="" self.file_type=""
def _refresh(self, *args): def _refresh(self, *args):
string=(_("%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s channels, %(file_type)s") string=_("{bitrate} kb/s, {frequency} kHz, {resolution} bit, {channels} channels, {file_type}").format(
%{"bitrate": self.brate, "frequency": self.freq, "resolution": self.res, "channels": self.chan, "file_type": self.file_type}) bitrate=self.brate, frequency=self.freq, resolution=self.res, channels=self.chan, file_type=self.file_type)
self.set_text(string) self.set_text(string)
def _on_audio(self, emitter, freq, res, chan): def _on_audio(self, emitter, freq, res, chan):
@ -2537,7 +2538,7 @@ class AudioType(Gtk.Label):
def _on_song_changed(self, *args): def _on_song_changed(self, *args):
try: try:
self.file_type=self._client.wrapped_call("currentsong")["file"].split('.')[-1] self.file_type=self._client.wrapped_call("currentsong")["file"].split(".")[-1]
self._refresh() self._refresh()
except: except:
pass pass
@ -2778,9 +2779,8 @@ class PlaylistWindow(Gtk.Box):
if songs == []: if songs == []:
self._playlist_info.set_text("") self._playlist_info.set_text("")
else: else:
whole_length_human_readable=ClientHelper.calc_display_length(songs) length_human_readable=ClientHelper.calc_display_length(songs)
self._playlist_info.set_text(_("%(total_tracks)i titles (%(total_length)s)") self._playlist_info.set_text(_("{titles} titles ({length})").format(titles=len(songs), length=length_human_readable))
%{"total_tracks": len(songs), "total_length": whole_length_human_readable})
def _scroll_to_selected_title(self, *args): def _scroll_to_selected_title(self, *args):
treeview, treeiter=self._selection.get_selected() treeview, treeiter=self._selection.get_selected()
@ -3475,10 +3475,11 @@ class ConnectionNotify(Gtk.Revealer):
def _on_connection_error(self, *args): def _on_connection_error(self, *args):
active=self._settings.get_int("active-profile") active=self._settings.get_int("active-profile")
profile=self._settings.get_value("profiles")[active] string=_("Connection to '{profile}' ({host}:{port}) failed").format(
host=self._settings.get_value("hosts")[active] profile=self._settings.get_value("profiles")[active],
port=self._settings.get_value("ports")[active] host=self._settings.get_value("hosts")[active],
string=_('Connection to "%(profile)s" (%(host)s:%(port)s) failed') % {"profile": profile, "host": host, "port": port} port=self._settings.get_value("ports")[active]
)
self._label.set_text(string) self._label.set_text(string)
self.set_reveal_child(True) self.set_reveal_child(True)
@ -3800,7 +3801,7 @@ class mpdevil(Gtk.Application):
self._client.wrapped_call("stop") self._client.wrapped_call("stop")
self.quit() self.quit()
if __name__ == '__main__': if __name__ == "__main__":
app=mpdevil() app=mpdevil()
app.run(sys.argv) app.run(sys.argv)

122
po/de.po
View File

@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-09-24 16:49+0200\n" "POT-Creation-Date: 2020-09-24 21:14+0200\n"
"PO-Revision-Date: 2020-09-24 16:50+0200\n" "PO-Revision-Date: 2020-09-24 21:16+0200\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: de\n" "Language: de\n"
@ -158,39 +158,39 @@ msgstr ""
"Lege die Reihenfolge fest, in der Informationen in der Wiedergabeliste " "Lege die Reihenfolge fest, in der Informationen in der Wiedergabeliste "
"angezeigt werden sollen:" "angezeigt werden sollen:"
#: mpdevil:1114 mpdevil:1631 mpdevil:1970 mpdevil:2687 #: mpdevil:1114 mpdevil:1632 mpdevil:1970 mpdevil:2688
msgid "No" msgid "No"
msgstr "Nr." msgstr "Nr."
#: mpdevil:1114 mpdevil:2688 #: mpdevil:1114 mpdevil:2689
msgid "Disc" msgid "Disc"
msgstr "CD" msgstr "CD"
#: mpdevil:1114 mpdevil:1636 mpdevil:1975 mpdevil:2689 #: mpdevil:1114 mpdevil:1637 mpdevil:1975 mpdevil:2690
msgid "Title" msgid "Title"
msgstr "Titel" msgstr "Titel"
#: mpdevil:1114 mpdevil:1642 mpdevil:1861 mpdevil:2690 #: mpdevil:1114 mpdevil:1643 mpdevil:1862 mpdevil:2691
msgid "Artist" msgid "Artist"
msgstr "Interpret" msgstr "Interpret"
#: mpdevil:1114 mpdevil:1648 mpdevil:2691 #: mpdevil:1114 mpdevil:1649 mpdevil:2692
msgid "Album" msgid "Album"
msgstr "Album" msgstr "Album"
#: mpdevil:1114 mpdevil:1654 mpdevil:1981 mpdevil:2692 #: mpdevil:1114 mpdevil:1655 mpdevil:1981 mpdevil:2693
msgid "Length" msgid "Length"
msgstr "Länge" msgstr "Länge"
#: mpdevil:1114 mpdevil:2693 #: mpdevil:1114 mpdevil:2694
msgid "Year" msgid "Year"
msgstr "Jahr" msgstr "Jahr"
#: mpdevil:1114 mpdevil:2694 #: mpdevil:1114 mpdevil:2695
msgid "Genre" msgid "Genre"
msgstr "Genre" msgstr "Genre"
#: mpdevil:1230 mpdevil:1238 mpdevil:3543 #: mpdevil:1230 mpdevil:1238 mpdevil:3544
msgid "Settings" msgid "Settings"
msgstr "Einstellungen" msgstr "Einstellungen"
@ -222,27 +222,27 @@ msgstr "MPD-Tag"
msgid "Value" msgid "Value"
msgstr "Wert" msgstr "Wert"
#: mpdevil:1552 #: mpdevil:1553
msgid "Append" msgid "Append"
msgstr "Anhängen" msgstr "Anhängen"
#: mpdevil:1553 #: mpdevil:1554
msgid "Add all titles to playlist" msgid "Add all titles to playlist"
msgstr "Alle Titel der Wiedergabeliste anhängen" msgstr "Alle Titel der Wiedergabeliste anhängen"
#: mpdevil:1554 #: mpdevil:1555
msgid "Play" msgid "Play"
msgstr "Abspielen" msgstr "Abspielen"
#: mpdevil:1555 #: mpdevil:1556
msgid "Directly play all titles" msgid "Directly play all titles"
msgstr "Alle Titel sofort abspielen" msgstr "Alle Titel sofort abspielen"
#: mpdevil:1556 #: mpdevil:1557
msgid "Enqueue" msgid "Enqueue"
msgstr "Einreihen" msgstr "Einreihen"
#: mpdevil:1557 #: mpdevil:1558
msgid "" msgid ""
"Append all titles after the currently playing track and clear the playlist " "Append all titles after the currently playing track and clear the playlist "
"from all other songs" "from all other songs"
@ -250,20 +250,20 @@ msgstr ""
"Alle Titel hinter dem aktuellen Stück einreihen und die weitere " "Alle Titel hinter dem aktuellen Stück einreihen und die weitere "
"Wiedergabeliste leeren" "Wiedergabeliste leeren"
#: mpdevil:1713 #: mpdevil:1714
#, python-format #, python-brace-format
msgid "%i hits" msgid "{num} hits"
msgstr "%i Treffer" msgstr "{num} Treffer"
#: mpdevil:1751 #: mpdevil:1752
msgid "all genres" msgid "all genres"
msgstr "Alle Genres" msgstr "Alle Genres"
#: mpdevil:1859 #: mpdevil:1860
msgid "Album Artist" msgid "Album Artist"
msgstr "Albuminterpret" msgstr "Albuminterpret"
#: mpdevil:1862 #: mpdevil:1863
msgid "all artists" msgid "all artists"
msgstr "Alle Interpreten" msgstr "Alle Interpreten"
@ -272,49 +272,49 @@ msgid "Close"
msgstr "Schließen" msgstr "Schließen"
#: mpdevil:2152 #: mpdevil:2152
#, python-format #, python-brace-format
msgid "%(total_tracks)i titles on %(discs)i discs (%(total_length)s)" msgid "{titles} titles on {discs} discs ({length})"
msgstr "%(total_tracks)i Titel auf %(discs)i CDs (%(total_length)s)" msgstr "{titles} Titel auf {discs} CDs ({length})"
#: mpdevil:2155 mpdevil:2782 #: mpdevil:2155 mpdevil:2783
#, python-format #, python-brace-format
msgid "%(total_tracks)i titles (%(total_length)s)" msgid "{titles} titles ({length})"
msgstr "%(total_tracks)i Titel (%(total_length)s)" msgstr "{titles} Titel ({length})"
#: mpdevil:2281 mpdevil:3377 #: mpdevil:2282 mpdevil:3377
msgid "Back to current album" msgid "Back to current album"
msgstr "Zurück zu aktuellem Album" msgstr "Zurück zu aktuellem Album"
#: mpdevil:2282 #: mpdevil:2283
msgid "Search" msgid "Search"
msgstr "Suche" msgstr "Suche"
#: mpdevil:2466 #: mpdevil:2467
msgid "searching..." msgid "searching..."
msgstr "suche..." msgstr "suche..."
#: mpdevil:2471 #: mpdevil:2472
msgid "connection error" msgid "connection error"
msgstr "Verbindungsfehler" msgstr "Verbindungsfehler"
#: mpdevil:2473 #: mpdevil:2474
msgid "lyrics not found" msgid "lyrics not found"
msgstr "Liedtext nicht gefunden" msgstr "Liedtext nicht gefunden"
#: mpdevil:2521 #: mpdevil:2522
#, python-format #, python-brace-format
msgid "" msgid ""
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s " "{bitrate} kb/s, {frequency} kHz, {resolution} bit, {channels} channels, "
"channels, %(file_type)s" "{file_type}"
msgstr "" msgstr ""
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s " "{bitrate} kb/s, {frequency} kHz, {resolution} bit, {channels} Kanäle, "
"Kanäle, %(file_type)s" "{file_type}"
#: mpdevil:2656 #: mpdevil:2657
msgid "Scroll to current song" msgid "Scroll to current song"
msgstr "Gehe zu aktuellem Lied" msgstr "Gehe zu aktuellem Lied"
#: mpdevil:2663 #: mpdevil:2664
msgid "Clear playlist" msgid "Clear playlist"
msgstr "Wiedergabeliste leeren" msgstr "Wiedergabeliste leeren"
@ -366,7 +366,7 @@ msgstr "Suche ein-/ausblenden"
msgid "Open online help" msgid "Open online help"
msgstr "Onlinehilfe öffnen" msgstr "Onlinehilfe öffnen"
#: mpdevil:3379 mpdevil:3547 #: mpdevil:3379 mpdevil:3548
msgid "Quit" msgid "Quit"
msgstr "Beenden" msgstr "Beenden"
@ -390,7 +390,7 @@ msgstr "Vorspulen"
msgid "Seek backward" msgid "Seek backward"
msgstr "Zurückspulen" msgstr "Zurückspulen"
#: mpdevil:3385 mpdevil:3550 #: mpdevil:3385 mpdevil:3551
msgid "Update database" msgid "Update database"
msgstr "Datenbank aktualisieren" msgstr "Datenbank aktualisieren"
@ -434,39 +434,47 @@ msgstr "Ausgewählten Titel entfernen"
msgid "Select profile" msgid "Select profile"
msgstr "Profil auswählen" msgstr "Profil auswählen"
#: mpdevil:3481 #: mpdevil:3478
#, python-format #, python-brace-format
msgid "Connection to \"%(profile)s\" (%(host)s:%(port)s) failed" msgid "Connection to '{profile}' ({host}:{port}) failed"
msgstr "Verbindung zu \"%(profile)s\" (%(host)s:%(port)s) fehlgeschlagen" msgstr "Verbindung zu \"{profile}\" ({host}:{port}) fehlgeschlagen"
#: mpdevil:3544 #: mpdevil:3545
msgid "Keyboard shortcuts" msgid "Keyboard shortcuts"
msgstr "Tastenkürzel" msgstr "Tastenkürzel"
#: mpdevil:3545 #: mpdevil:3546
msgid "Help" msgid "Help"
msgstr "Hilfe" msgstr "Hilfe"
#: mpdevil:3546 #: mpdevil:3547
msgid "About" msgid "About"
msgstr "Über" msgstr "Über"
#: mpdevil:3551 #: mpdevil:3552
msgid "Server stats" msgid "Server stats"
msgstr "Serverstatistik" msgstr "Serverstatistik"
#: mpdevil:3554 #: mpdevil:3555
msgid "Save window layout" msgid "Save window layout"
msgstr "Fensterlayout speichern" msgstr "Fensterlayout speichern"
#: mpdevil:3555 #: mpdevil:3556
msgid "Mini player" msgid "Mini player"
msgstr "Miniplayer" msgstr "Miniplayer"
#: mpdevil:3559 #: mpdevil:3560
msgid "Menu" msgid "Menu"
msgstr "Menü" msgstr "Menü"
#, python-format
#~ msgid "%i hits"
#~ msgstr "%i Treffer"
#, python-format
#~ msgid "%(total_tracks)i titles (%(total_length)s)"
#~ msgstr "%(total_tracks)i Titel (%(total_length)s)"
#~ msgid "Unknown Artist" #~ msgid "Unknown Artist"
#~ msgstr "Unbekannter Interpret" #~ msgstr "Unbekannter Interpret"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-09-24 16:49+0200\n" "POT-Creation-Date: 2020-09-24 21:14+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -152,39 +152,39 @@ msgstr ""
msgid "Choose the order of information to appear in the playlist:" msgid "Choose the order of information to appear in the playlist:"
msgstr "" msgstr ""
#: mpdevil:1114 mpdevil:1631 mpdevil:1970 mpdevil:2687 #: mpdevil:1114 mpdevil:1632 mpdevil:1970 mpdevil:2688
msgid "No" msgid "No"
msgstr "" msgstr ""
#: mpdevil:1114 mpdevil:2688 #: mpdevil:1114 mpdevil:2689
msgid "Disc" msgid "Disc"
msgstr "" msgstr ""
#: mpdevil:1114 mpdevil:1636 mpdevil:1975 mpdevil:2689 #: mpdevil:1114 mpdevil:1637 mpdevil:1975 mpdevil:2690
msgid "Title" msgid "Title"
msgstr "" msgstr ""
#: mpdevil:1114 mpdevil:1642 mpdevil:1861 mpdevil:2690 #: mpdevil:1114 mpdevil:1643 mpdevil:1862 mpdevil:2691
msgid "Artist" msgid "Artist"
msgstr "" msgstr ""
#: mpdevil:1114 mpdevil:1648 mpdevil:2691 #: mpdevil:1114 mpdevil:1649 mpdevil:2692
msgid "Album" msgid "Album"
msgstr "" msgstr ""
#: mpdevil:1114 mpdevil:1654 mpdevil:1981 mpdevil:2692 #: mpdevil:1114 mpdevil:1655 mpdevil:1981 mpdevil:2693
msgid "Length" msgid "Length"
msgstr "" msgstr ""
#: mpdevil:1114 mpdevil:2693 #: mpdevil:1114 mpdevil:2694
msgid "Year" msgid "Year"
msgstr "" msgstr ""
#: mpdevil:1114 mpdevil:2694 #: mpdevil:1114 mpdevil:2695
msgid "Genre" msgid "Genre"
msgstr "" msgstr ""
#: mpdevil:1230 mpdevil:1238 mpdevil:3543 #: mpdevil:1230 mpdevil:1238 mpdevil:3544
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
@ -216,46 +216,46 @@ msgstr ""
msgid "Value" msgid "Value"
msgstr "" msgstr ""
#: mpdevil:1552 #: mpdevil:1553
msgid "Append" msgid "Append"
msgstr "" msgstr ""
#: mpdevil:1553 #: mpdevil:1554
msgid "Add all titles to playlist" msgid "Add all titles to playlist"
msgstr "" msgstr ""
#: mpdevil:1554 #: mpdevil:1555
msgid "Play" msgid "Play"
msgstr "" msgstr ""
#: mpdevil:1555 #: mpdevil:1556
msgid "Directly play all titles" msgid "Directly play all titles"
msgstr "" msgstr ""
#: mpdevil:1556 #: mpdevil:1557
msgid "Enqueue" msgid "Enqueue"
msgstr "" msgstr ""
#: mpdevil:1557 #: mpdevil:1558
msgid "" msgid ""
"Append all titles after the currently playing track and clear the playlist " "Append all titles after the currently playing track and clear the playlist "
"from all other songs" "from all other songs"
msgstr "" msgstr ""
#: mpdevil:1713 #: mpdevil:1714
#, python-format #, python-brace-format
msgid "%i hits" msgid "{num} hits"
msgstr "" msgstr ""
#: mpdevil:1751 #: mpdevil:1752
msgid "all genres" msgid "all genres"
msgstr "" msgstr ""
#: mpdevil:1859 #: mpdevil:1860
msgid "Album Artist" msgid "Album Artist"
msgstr "" msgstr ""
#: mpdevil:1862 #: mpdevil:1863
msgid "all artists" msgid "all artists"
msgstr "" msgstr ""
@ -264,47 +264,47 @@ msgid "Close"
msgstr "" msgstr ""
#: mpdevil:2152 #: mpdevil:2152
#, python-format #, python-brace-format
msgid "%(total_tracks)i titles on %(discs)i discs (%(total_length)s)" msgid "{titles} titles on {discs} discs ({length})"
msgstr "" msgstr ""
#: mpdevil:2155 mpdevil:2782 #: mpdevil:2155 mpdevil:2783
#, python-format #, python-brace-format
msgid "%(total_tracks)i titles (%(total_length)s)" msgid "{titles} titles ({length})"
msgstr "" msgstr ""
#: mpdevil:2281 mpdevil:3377 #: mpdevil:2282 mpdevil:3377
msgid "Back to current album" msgid "Back to current album"
msgstr "" msgstr ""
#: mpdevil:2282 #: mpdevil:2283
msgid "Search" msgid "Search"
msgstr "" msgstr ""
#: mpdevil:2466 #: mpdevil:2467
msgid "searching..." msgid "searching..."
msgstr "" msgstr ""
#: mpdevil:2471 #: mpdevil:2472
msgid "connection error" msgid "connection error"
msgstr "" msgstr ""
#: mpdevil:2473 #: mpdevil:2474
msgid "lyrics not found" msgid "lyrics not found"
msgstr "" msgstr ""
#: mpdevil:2521 #: mpdevil:2522
#, python-format #, python-brace-format
msgid "" msgid ""
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s " "{bitrate} kb/s, {frequency} kHz, {resolution} bit, {channels} channels, "
"channels, %(file_type)s" "{file_type}"
msgstr "" msgstr ""
#: mpdevil:2656 #: mpdevil:2657
msgid "Scroll to current song" msgid "Scroll to current song"
msgstr "" msgstr ""
#: mpdevil:2663 #: mpdevil:2664
msgid "Clear playlist" msgid "Clear playlist"
msgstr "" msgstr ""
@ -356,7 +356,7 @@ msgstr ""
msgid "Open online help" msgid "Open online help"
msgstr "" msgstr ""
#: mpdevil:3379 mpdevil:3547 #: mpdevil:3379 mpdevil:3548
msgid "Quit" msgid "Quit"
msgstr "" msgstr ""
@ -380,7 +380,7 @@ msgstr ""
msgid "Seek backward" msgid "Seek backward"
msgstr "" msgstr ""
#: mpdevil:3385 mpdevil:3550 #: mpdevil:3385 mpdevil:3551
msgid "Update database" msgid "Update database"
msgstr "" msgstr ""
@ -424,35 +424,35 @@ msgstr ""
msgid "Select profile" msgid "Select profile"
msgstr "" msgstr ""
#: mpdevil:3481 #: mpdevil:3478
#, python-format #, python-brace-format
msgid "Connection to \"%(profile)s\" (%(host)s:%(port)s) failed" msgid "Connection to '{profile}' ({host}:{port}) failed"
msgstr ""
#: mpdevil:3544
msgid "Keyboard shortcuts"
msgstr "" msgstr ""
#: mpdevil:3545 #: mpdevil:3545
msgid "Help" msgid "Keyboard shortcuts"
msgstr "" msgstr ""
#: mpdevil:3546 #: mpdevil:3546
msgid "Help"
msgstr ""
#: mpdevil:3547
msgid "About" msgid "About"
msgstr "" msgstr ""
#: mpdevil:3551 #: mpdevil:3552
msgid "Server stats" msgid "Server stats"
msgstr "" msgstr ""
#: mpdevil:3554 #: mpdevil:3555
msgid "Save window layout" msgid "Save window layout"
msgstr "" msgstr ""
#: mpdevil:3555 #: mpdevil:3556
msgid "Mini player" msgid "Mini player"
msgstr "" msgstr ""
#: mpdevil:3559 #: mpdevil:3560
msgid "Menu" msgid "Menu"
msgstr "" msgstr ""