28 Commits

Author SHA1 Message Date
Martin Wagner
79b828511b Update configure.ac 2020-02-09 21:00:52 +01:00
Martin Wagner
596bcec194 fixed metadata popover 2020-02-08 16:27:52 +01:00
Martin Wagner
d9e518c687 reworked profile selection 2020-02-07 20:13:38 +01:00
Martin Wagner
2e5ce9bf2b fixed space key glitch 2020-02-05 21:35:19 +01:00
Martin Wagner
8f1678579f enabled media keys 2020-02-05 20:29:34 +01:00
Martin Wagner
6374fe1c28 Update README.md 2020-02-05 17:44:23 +01:00
Martin Wagner
b833cb3ca2 avoid DeprecationWarning 2020-02-02 23:55:16 +01:00
Martin Wagner
08ff73d797 fixed lyrics fetching when no song is playing 2020-02-02 23:13:21 +01:00
Martin Wagner
0c9635921d changed home button behavior 2020-02-02 23:10:33 +01:00
Martin Wagner
06e690b8a3 Update configure.ac 2020-02-02 18:15:25 +01:00
Martin Wagner
b167f69333 changed default icon size 2020-02-02 18:13:53 +01:00
Martin Wagner
88534a7054 small gui improvements 2020-02-02 18:08:59 +01:00
Martin Wagner
fff76e00ee fixed profile settings 2020-02-02 14:20:25 +01:00
Martin Wagner
fe92ae094e fixed password settings 2020-02-02 10:38:05 +01:00
Martin Wagner
fbf6afd358 translation update 2020-02-01 15:53:30 +01:00
Martin Wagner
e72625879c Update configure.ac 2020-02-01 15:46:33 +01:00
Martin Wagner
839d13e84a Update README.md 2020-02-01 15:46:03 +01:00
Martin Wagner
277ddbabaa updated about dialog 2020-02-01 15:44:51 +01:00
Martin Wagner
af25d49119 minor fixes 2020-02-01 15:37:13 +01:00
Martin Wagner
49b277d38f small consolidation 2020-02-01 14:36:57 +01:00
Martin Wagner
c950aff51b fixed bug on connection loss 2020-02-01 14:12:56 +01:00
Martin Wagner
b7f95b5ca9 added password support 2020-02-01 13:27:46 +01:00
Martin Wagner
981c1b0f30 reworked browser (fixes some minor bugs) 2020-02-01 12:45:34 +01:00
Martin Wagner
c388cc7342 fixed playlist info 2020-01-29 17:29:47 +01:00
Martin Wagner
36a94350ac added LARGE_TOOLBAR icon size 2020-01-29 16:42:45 +01:00
Martin Wagner
c9165cf742 added new screenshot 2020-01-28 20:36:55 +01:00
Martin Wagner
074016a686 Delete mainwindow.png 2020-01-28 20:32:29 +01:00
Martin Wagner
3b3ccf1cbd Add files via upload 2020-01-28 20:31:46 +01:00
7 changed files with 523 additions and 349 deletions

View File

@@ -19,8 +19,8 @@ Features
TODO
----
1. MPRIS interface
2. connecting to mpd servers with password
1. Support media keys
2. MPRIS interface
Building and installation
-------------------------

View File

@@ -82,11 +82,16 @@ class Client(MPDClient):
def __init__(self, settings):
MPDClient.__init__(self)
self.settings = settings
self.song_to_delete=""
def try_connect_default(self):
active=self.settings.get_int("active-profile")
try:
self.connect(self.settings.get_value("hosts")[active], self.settings.get_value("ports")[active])
if self.settings.get_value("passwords")[active] == "":
self.password(None)
else:
self.password(self.settings.get_value("passwords")[active])
except:
pass
@@ -97,10 +102,44 @@ class Client(MPDClient):
except:
return False
def album_to_playlist(self, album, artist, year, append, force=False):
if append:
songs=self.find("album", album, "date", year, "albumartist", artist)
if not songs == []:
for song in songs:
self.add(song["file"])
else:
if self.settings.get_boolean("add-album") and not force and not self.status()["state"] == "stop":
status=self.status()
self.moveid(status["songid"], 0)
self.song_to_delete=self.playlistinfo()[0]["file"]
try:
self.delete((1,)) # delete all songs, but the first. #bad song index possible
except:
pass
songs=self.find("album", album, "date", year, "albumartist", artist)
if not songs == []:
for song in songs:
if not song["file"] == self.song_to_delete:
self.add(song["file"])
else:
self.move(0, (len(self.playlist())-1))
self.song_to_delete=""
else:
songs=self.find("album", album, "date", year, "albumartist", artist)
if not songs == []:
self.stop()
self.clear()
for song in songs:
self.add(song["file"])
self.play()
class Settings(Gio.Settings):
BASE_KEY = "org.mpdevil"
def __init__(self):
super().__init__(schema=self.BASE_KEY)
if len(self.get_value("profiles")) < (self.get_int("active-profile")+1):
self.set_int("active-profile", 0)
def array_append(self, vtype, key, value): #append to Gio.Settings (self.settings) array
array=self.get_value(key).unpack()
@@ -121,11 +160,14 @@ class Settings(Gio.Settings):
icon_size=self.get_int(key)
if icon_size == 16:
return Gtk.IconSize.BUTTON
elif icon_size == 24:
return Gtk.IconSize.LARGE_TOOLBAR
elif icon_size == 32:
return Gtk.IconSize.DND
elif icon_size == 48:
return Gtk.IconSize.DIALOG
else:
# return Gtk.IconSize.INVALID
raise ValueError
class AlbumDialog(Gtk.Dialog):
@@ -247,7 +289,7 @@ class ArtistView(Gtk.ScrolledWindow):
#artistSelection
self.selection = self.treeview.get_selection()
self.selection.set_mode(Gtk.SelectionMode.SINGLE)
self.selection.set_mode(Gtk.SelectionMode.MULTIPLE)
#Old Name Column
renderer_text = Gtk.CellRendererText()
@@ -257,10 +299,17 @@ class ArtistView(Gtk.ScrolledWindow):
self.column_name.set_sort_column_id(0)
self.treeview.append_column(self.column_name)
#connect
self.treeview.connect("enter-notify-event", self.on_enter_event)
self.refresh()
self.add(self.treeview)
def clear(self):
self.store.clear()
self.albumartists=[]
def refresh(self): #returns True if refresh was actually performed
if self.client.connected():
if self.albumartists != self.client.list("albumartist"):
@@ -272,35 +321,56 @@ class ArtistView(Gtk.ScrolledWindow):
else:
return False
else:
self.store.clear()
self.albumartists=[]
self.clear()
return True
class AlbumView(Gtk.ScrolledWindow):
def __init__(self, client, settings):
Gtk.ScrolledWindow.__init__(self)
self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
def get_selected_artists(self):
paths=self.selection.get_selected_rows()[1]
artists=[]
for path in paths:
treeiter = self.store.get_iter(path)
if not treeiter == None:
selected_artist=self.store.get_value(treeiter, 0)
artists.append(selected_artist)
return artists
def on_enter_event(self, widget, event):
self.treeview.grab_focus()
class AlbumIconView(Gtk.IconView):
def __init__(self, client, settings, window):
Gtk.IconView.__init__(self)
#adding vars
self.settings=settings
self.client=client
self.window=window
#cover, display_label, tooltip(titles), album, year
self.store = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str, str, str)
#cover, display_label, tooltip(titles), album, year, artist
self.store = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str, str, str, str)
self.store.set_sort_column_id(4, Gtk.SortType.ASCENDING)
#iconview
self.iconview = Gtk.IconView.new()
self.iconview.set_model(self.store)
self.iconview.set_pixbuf_column(0)
self.iconview.set_text_column(1)
self.iconview.set_tooltip_column(2)
self.iconview.set_item_width(0)
self.set_model(self.store)
self.set_pixbuf_column(0)
self.set_text_column(1)
self.set_item_width(0)
self.tooltip_settings()
self.add(self.iconview)
#connect
self.album_change=self.connect("selection-changed", self.on_album_selection_change)
self.album_item_activated=self.connect("item-activated", self.on_album_item_activated)
self.connect("button-press-event", self.on_album_view_button_press_event)
self.settings.connect("changed::show-album-view-tooltips", self.tooltip_settings)
self.connect("motion-notify-event", self.on_move_event)
def tooltip_settings(self, *args):
if self.settings.get_boolean("show-album-view-tooltips"):
self.set_tooltip_column(2)
else:
self.set_tooltip_column(-1)
def gen_tooltip(self, album, artist, year):
if self.settings.get_boolean("show-album-view-tooltips"):
songs=self.client.find("album", album, "date", year, "albumartist", artist)
length=float(0)
for song in songs:
@@ -312,17 +382,16 @@ class AlbumView(Gtk.ScrolledWindow):
length_human_readable=str(datetime.timedelta(seconds=int(length)))
tooltip=(_("%(total_tracks)i titles (%(total_length)s)") % {"total_tracks": len(songs), "total_length": length_human_readable})
return tooltip
else:
return None
def refresh(self, artist):
self.store.clear()
def populate(self, artists):
for artist in artists:
size=self.settings.get_int("album-cover")
albums=[]
for album in self.client.list("album", "albumartist", artist):
albums.append({"album": album, "year": self.client.list("date", "album", album, "albumartist", artist)[0]})
albums = sorted(albums, key=lambda k: k['year'])
for album in albums:
if self.get_visible():
songs=self.client.find("album", album["album"], "date", album["year"], "albumartist", artist)
if songs == []:
song_file=None
@@ -331,21 +400,107 @@ class AlbumView(Gtk.ScrolledWindow):
cover=Cover(client=self.client, lib_path=self.settings.get_value("paths")[self.settings.get_int("active-profile")], song_file=song_file)
img=cover.get_pixbuf(size)
if album["year"] == "":
self.store.append([img, album["album"], self.gen_tooltip(album["album"], artist, album["year"]), album["album"], album["year"]])
self.store.append([img, album["album"], self.gen_tooltip(album["album"], artist, album["year"]), album["album"], album["year"], artist])
else:
self.store.append([img, album["album"]+" ("+album["year"]+")", self.gen_tooltip(album["album"], artist, album["year"]), album["album"], album["year"]])
self.store.append([img, album["album"]+" ("+album["year"]+")", self.gen_tooltip(album["album"], artist, album["year"]), album["album"], album["year"], artist])
while Gtk.events_pending():
Gtk.main_iteration_do(True)
else:
break
def scroll_to_selected_album(self):
songid=self.client.status()["songid"]
song=self.client.playlistid(songid)[0]
self.handler_block(self.album_change)
self.unselect_all()
row_num=len(self.store)
for i in range(0, row_num):
path=Gtk.TreePath(i)
treeiter = self.store.get_iter(path)
if self.store.get_value(treeiter, 3) == song["album"]:
self.select_path(path)
self.scroll_to_path(path, True, 0, 0)
break
self.handler_unblock(self.album_change)
def on_album_view_button_press_event(self, widget, event):
path = widget.get_path_at_pos(int(event.x), int(event.y))
if not path == None:
if not event.button == 1:
treeiter=self.store.get_iter(path)
selected_album=self.store.get_value(treeiter, 3)
selected_album_year=self.store.get_value(treeiter, 4)
selected_artist=self.store.get_value(treeiter, 5)
if event.button == 2:
self.client.album_to_playlist(selected_album, selected_artist, selected_album_year, True)
elif event.button == 3:
if self.client.connected():
album = AlbumDialog(self.window, self.client, selected_album, selected_artist, selected_album_year)
response = album.run()
if response == Gtk.ResponseType.OK:
self.select_path(path)
elif response == Gtk.ResponseType.ACCEPT:
self.client.album_to_playlist(selected_album, selected_artist, selected_album_year, True)
elif response == Gtk.ResponseType.YES:
self.client.album_to_playlist(selected_album, selected_artist, selected_album_year, False, True)
album.destroy()
def on_album_selection_change(self, widget):
paths=widget.get_selected_items()
if not len(paths) == 0:
treeiter=self.store.get_iter(paths[0])
selected_album=self.store.get_value(treeiter, 3)
selected_album_year=self.store.get_value(treeiter, 4)
selected_artist=self.store.get_value(treeiter, 5)
self.client.album_to_playlist(selected_album, selected_artist, selected_album_year, False)
def on_album_item_activated(self, widget, path):
treeiter=self.store.get_iter(path)
selected_album=self.store.get_value(treeiter, 3)
selected_album_year=self.store.get_value(treeiter, 4)
selected_artist=self.store.get_value(treeiter, 5)
self.client.album_to_playlist(selected_album, selected_artist, selected_album_year, False, True)
def on_move_event(self, widget, event):
self.grab_focus()
class AlbumView(Gtk.ScrolledWindow):
def __init__(self, client, settings, window):
Gtk.ScrolledWindow.__init__(self)
self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
#adding vars
self.settings=settings
self.client=client
self.window=window
self.iconview=None
def clear(self):
if self.iconview:
self.remove(self.iconview)
self.iconview.destroy()
self.iconview=None
def refresh(self, artists):
self.clear()
self.iconview=AlbumIconView(self.client, self.settings, self.window)
self.add(self.iconview)
self.iconview.show_all()
self.iconview.populate(artists)
def scroll_to_selected_album(self):
self.iconview.scroll_to_selected_album()
class TrackView(Gtk.Box):
def __init__(self, client, settings):
def __init__(self, client, settings, window):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
self.settings = settings
#adding vars
self.client=client
self.window=window
self.playlist=[]
self.song_to_delete=""
self.hovered_songpos=None
self.song_file=None
@@ -393,6 +548,8 @@ class TrackView(Gtk.Box):
#cover
self.cover=Gtk.Image.new()
self.cover.set_from_pixbuf(Cover(client=self.client, lib_path=self.settings.get_value("paths")[self.settings.get_int("active-profile")], song_file=None).get_pixbuf(self.settings.get_int("track-cover"))) #set to fallback cover
cover_event_box=Gtk.EventBox()
cover_event_box.add(self.cover)
#audio infos
audio=AudioType(self.client)
@@ -404,11 +561,12 @@ class TrackView(Gtk.Box):
#status bar
status_bar=Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
status_bar.set_margin_start(4)
status_bar.set_margin_end(4)
status_bar.pack_start(self.playlist_info, True, True, 0)
status_bar.pack_end(audio, False, False, 0)
#timeouts
GLib.timeout_add(1000, self.update_cover)
GLib.timeout_add(100, self.refresh)
#connect
@@ -417,13 +575,20 @@ class TrackView(Gtk.Box):
self.treeview.connect("motion-notify-event", self.on_move_event)
self.treeview.connect("leave-notify-event", self.on_leave_event)
self.key_press_event=self.treeview.connect("key-press-event", self.on_key_press_event)
cover_event_box.connect("button-press-event", self.on_button_press_event)
#packing
self.pack_start(self.cover, False, False, 0)
self.pack_start(cover_event_box, False, False, 0)
self.pack_start(scroll, True, True, 0)
self.pack_end(status_bar, False, False, 0)
def update_cover(self):
def scroll_to_selected_title(self):
treeview, treeiter=self.selection.get_selected()
if not treeiter == None:
path=treeview.get_path(treeiter)
self.treeview.scroll_to_cell(path)
def refresh_cover(self):
try:
song_file=self.client.currentsong()["file"]
except:
@@ -433,39 +598,29 @@ class TrackView(Gtk.Box):
self.song_file=song_file
return True
def album_to_playlist(self, album, artist, year, append, force=False):
if append:
songs=self.client.find("album", album, "date", year, "albumartist", artist)
def refresh_playlist_info(self):
songs=self.client.playlistinfo()
if not songs == []:
whole_length=float(0)
for song in songs:
self.client.add(song["file"])
else:
if self.settings.get_boolean("add-album") and not force and not self.client.status()["state"] == "stop":
self.selection.handler_block(self.title_change)
status=self.client.status()
self.client.moveid(status["songid"], 0)
self.song_to_delete=self.client.playlistinfo()[0]["file"]
self.selection.handler_unblock(self.title_change)
try:
self.client.delete((1,)) # delete all songs, but the first. #bad song index possible
dura=float(song["duration"])
except:
pass
songs=self.client.find("album", album, "date", year, "albumartist", artist)
if not songs == []:
for song in songs:
if not song["file"] == self.song_to_delete:
self.client.add(song["file"])
dura=0.0
whole_length=whole_length+dura
whole_length_human_readable=str(datetime.timedelta(seconds=int(whole_length)))
self.playlist_info.set_text(_("%(total_tracks)i titles (%(total_length)s)") % {"total_tracks": len(songs), "total_length": whole_length_human_readable})
else:
self.client.move(0, (len(self.client.playlist())-1))
self.song_to_delete=""
else:
songs=self.client.find("album", album, "date", year, "albumartist", artist)
if not songs == []:
self.client.stop()
self.client.clear()
for song in songs:
self.client.add(song["file"])
self.client.play()
self.playlist_info.set_text("")
def refresh_selection(self):
try:
song=self.client.status()["song"]
path = Gtk.TreePath(int(song))
self.selection.select_path(path)
except:
self.selection.unselect_all()
self.refresh_cover()
def refresh(self):
self.selection.handler_block(self.title_change)
@@ -475,7 +630,6 @@ class TrackView(Gtk.Box):
self.store.clear()
songs=self.client.playlistinfo()
if not songs == []:
whole_length=float(0)
for song in songs:
try:
title=song["title"]
@@ -497,31 +651,28 @@ class TrackView(Gtk.Box):
dura=float(song["duration"])
except:
dura=0.0
whole_length=whole_length+dura
duration=str(datetime.timedelta(seconds=int(dura )))
self.store.append([track, title, artist, album, duration, song["file"].replace("&", "")])
whole_length_human_readable=str(datetime.timedelta(seconds=int(whole_length)))
self.playlist_info.set_text(_("%(total_tracks)i titles (%(total_length)s)") % {"total_tracks": len(songs), "total_length": whole_length_human_readable})
self.refresh_playlist_info()
self.playlist=self.client.playlist()
self.refresh_selection()
self.scroll_to_selected_title()
else:
if not self.song_to_delete == "":
if not self.client.song_to_delete == "":
status=self.client.status()
if not status["song"] == "0":
if self.client.playlistinfo()[0]["file"] == self.song_to_delete:
if self.client.playlistinfo()[0]["file"] == self.client.song_to_delete:
self.client.delete(0)
self.playlist=self.client.playlist()
self.store.remove(self.store.get_iter_first())
self.song_to_delete=""
try:
song=self.client.status()["song"]
path = Gtk.TreePath(int(song))
self.selection.select_path(path)
except:
self.selection.unselect_all()
self.refresh_playlist_info()
self.client.song_to_delete=""
self.refresh_selection()
else:
self.playlist_info.set_text("")
self.store.clear()
self.playlist=[]
self.refresh_cover()
self.selection.handler_unblock(self.title_change)
return True
@@ -534,12 +685,15 @@ class TrackView(Gtk.Box):
self.client.delete(self.hovered_songpos) #bad song index possible
self.playlist=self.client.playlist()
self.store.remove(self.store.get_iter(self.hovered_songpos))
self.refresh_playlist_info()
except:
self.hovered_songpos == None
self.selection.handler_unblock(self.title_change)
self.treeview.handler_unblock(self.key_press_event)
def on_move_event(self, widget, event):
treeiter=self.selection.get_selected()[1]
if not treeiter == None:
self.treeview.grab_focus()
return_tuple = self.treeview.get_path_at_pos(int(event.x), int(event.y))
if not return_tuple == None:
@@ -555,15 +709,47 @@ class TrackView(Gtk.Box):
if not treeiter == None:
selected_title=self.store.get_path(treeiter)
self.client.play(selected_title)
self.refresh_cover()
def on_row_activated(self, widget, path, view_column):
treeiter=self.store.get_iter(path)
selected_title=self.store.get_path(treeiter)
self.client.play(selected_title)
def on_button_press_event(self, widget, event):
if self.client.connected():
song=self.client.currentsong()
if not song == {}:
try:
artist=song["albumartist"]
except:
artist=""
try:
album=song["album"]
except:
album=""
try:
album_year=song["date"]
except:
album_year=""
if event.button == 1:
self.client.album_to_playlist(album, artist, album_year, False)
elif event.button == 2:
self.client.album_to_playlist(album, artist, album_year, True)
elif event.button == 3:
album_dialog = AlbumDialog(self.window, self.client, album, artist, album_year)
response = album_dialog.run()
if response == Gtk.ResponseType.OK:
self.client.album_to_playlist(album, artist, album_year, False)
elif response == Gtk.ResponseType.ACCEPT:
self.client.album_to_playlist(album, artist, album_year, True)
elif response == Gtk.ResponseType.YES:
self.client.album_to_playlist(album, artist, album_year, False, True)
album_dialog.destroy()
class Browser(Gtk.Box):
def __init__(self, client, settings, window):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=3)
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=4)
#adding vars
self.client=client
@@ -572,23 +758,20 @@ class Browser(Gtk.Box):
#widgets
self.artist_list=ArtistView(self.client)
self.album_list=AlbumView(self.client, self.settings)
self.title_list=TrackView(self.client, self.settings)
self.album_list=AlbumView(self.client, self.settings, self.window)
self.title_list=TrackView(self.client, self.settings, self.window)
#connect
self.artist_change=self.artist_list.selection.connect("changed", self.on_artist_selection_change)
self.album_change=self.album_list.iconview.connect("selection-changed", self.on_album_selection_change)
self.album_item_activated=self.album_list.iconview.connect("item-activated", self.on_album_item_activated)
self.album_list.iconview.connect("button-press-event", self.on_album_view_button_press_event)
#timeouts
GLib.timeout_add(1000, self.refresh)
self.go_home(self, first_run=True)
#packing
self.paned1=Gtk.Paned.new(Gtk.Orientation.HORIZONTAL)
self.paned1.set_wide_handle(True)
self.paned2=Gtk.Paned.new(Gtk.Orientation.HORIZONTAL)
self.paned2.set_wide_handle(True)
self.paned1.pack1(self.artist_list, False, False)
self.paned1.pack2(self.album_list, True, False)
self.paned2.pack1(self.paned1, True, False)
@@ -604,110 +787,49 @@ class Browser(Gtk.Box):
self.paned1.set_position(self.settings.get_int("paned1"))
self.paned2.set_position(self.settings.get_int("paned2"))
def refresh(self):
def refresh(self, *args):
if self.client.connected():
self.artist_list.selection.handler_block(self.artist_change)
return_val=self.artist_list.refresh()
self.artist_list.selection.handler_unblock(self.artist_change)
if return_val:
self.go_home(self, first_run=True)
self.go_home(self)
else:
self.artist_list.selection.handler_block(self.artist_change)
self.artist_list.clear()
self.artist_list.selection.handler_unblock(self.artist_change)
self.album_list.clear()
return True
def go_home(self, widget, first_run=False): #TODO
def go_home(self, widget): #TODO
try:
songid=self.client.status()["songid"]
song=self.client.playlistid(songid)[0]
row_num=len(self.artist_list.store)
for i in range(0, row_num):
path=Gtk.TreePath(i)
treeiter = self.artist_list.store.get_iter(path)
if self.artist_list.store.get_value(treeiter, 0) == song["albumartist"]:
if not self.artist_list.selection.iter_is_selected(treeiter):
self.artist_list.selection.unselect_all()
self.artist_list.selection.select_iter(treeiter)
self.artist_list.treeview.scroll_to_cell(path)
break
if not self.settings.get_boolean("add-album") or first_run:
self.album_list.iconview.handler_block(self.album_change)
self.album_list.iconview.unselect_all()
row_num=len(self.album_list.store)
for i in range(0, row_num):
path=Gtk.TreePath(i)
treeiter = self.album_list.store.get_iter(path)
if self.album_list.store.get_value(treeiter, 3) == song["album"]:
self.album_list.iconview.select_path(path)
self.album_list.iconview.scroll_to_path(path, True, 0, 0)
break
if not self.settings.get_boolean("add-album") or first_run:
self.album_list.iconview.handler_unblock(self.album_change)
self.album_list.scroll_to_selected_album()
except:
self.artist_list.selection.unselect_all()
self.album_list.store.clear()
treeview, treeiter=self.title_list.selection.get_selected()
if not treeiter == None:
path=treeview.get_path(treeiter)
self.title_list.treeview.scroll_to_cell(path) #TODO multiple home-button presses needed
pass
self.title_list.scroll_to_selected_title()
def on_album_view_button_press_event(self, widget, event):
path = widget.get_path_at_pos(int(event.x), int(event.y))
if not path == None:
if not event.button == 1:
treeiter=self.album_list.store.get_iter(path)
selected_album=self.album_list.store.get_value(treeiter, 3)
selected_album_year=self.album_list.store.get_value(treeiter, 4)
treeiter=self.artist_list.selection.get_selected()[1]
selected_artist=self.artist_list.store.get_value(treeiter, 0)
if event.button == 2:
self.title_list.album_to_playlist(selected_album, selected_artist, selected_album_year, True)
elif event.button == 3:
if self.client.connected():
album = AlbumDialog(self.window, self.client, selected_album, selected_artist, selected_album_year)
response = album.run()
if response == Gtk.ResponseType.OK:
self.album_list.iconview.select_path(path)
elif response == Gtk.ResponseType.ACCEPT:
self.title_list.album_to_playlist(selected_album, selected_artist, selected_album_year, True)
elif response == Gtk.ResponseType.YES:
self.title_list.album_to_playlist(selected_album, selected_artist, selected_album_year, False, True)
album.destroy()
def on_album_selection_change(self, widget):
paths=widget.get_selected_items()
if not len(paths) == 0:
treeiter=self.album_list.store.get_iter(paths[0])
selected_album=self.album_list.store.get_value(treeiter, 3)
selected_album_year=self.album_list.store.get_value(treeiter, 4)
treeiter=self.artist_list.selection.get_selected()[1]
selected_artist=self.artist_list.store.get_value(treeiter, 0)
self.title_list.album_to_playlist(selected_album, selected_artist, selected_album_year, False)
def on_album_item_activated(self, widget, path):
treeiter=self.album_list.store.get_iter(path)
selected_album=self.album_list.store.get_value(treeiter, 3)
selected_album_year=self.album_list.store.get_value(treeiter, 4)
treeiter=self.artist_list.selection.get_selected()[1]
selected_artist=self.artist_list.store.get_value(treeiter, 0)
self.title_list.album_to_playlist(selected_album, selected_artist, selected_album_year, False, True)
def on_artist_selection_change(self, widget):
treeiter=widget.get_selected()[1]
if not treeiter == None:
def test(*args):
return False
selected_artist=self.artist_list.store.get_value(treeiter, 0)
self.artist_list.selection.handler_block(self.artist_change)
self.artist_list.selection.set_select_function(test)
self.album_list.refresh(selected_artist)
self.artist_list.selection.set_select_function(None)
self.artist_list.selection.select_iter(treeiter)
self.artist_list.selection.handler_unblock(self.artist_change)
else:
self.album_list.refresh(None)
def on_artist_selection_change(self, *args):
artists=self.artist_list.get_selected_artists()
self.album_list.refresh(artists)
class ProfileSettings(Gtk.Grid):
def __init__(self, parent, settings):
Gtk.Grid.__init__(self)
self.set_row_spacing(3)
self.set_column_spacing(3)
self.set_property("border-width", 3)
self.set_row_spacing(4)
self.set_column_spacing(4)
self.set_property("border-width", 4)
#adding vars
self.settings = settings
@@ -722,6 +844,8 @@ class ProfileSettings(Gtk.Grid):
self.profile_entry=Gtk.Entry()
self.host_entry=Gtk.Entry()
self.port_entry=IntEntry(0, 0, 65535)
self.password_entry=Gtk.Entry()
self.password_entry.set_visibility(False)
self.path_select_button=Gtk.Button(label=_("Select"), image=Gtk.Image(stock=Gtk.STOCK_OPEN))
profiles_label=Gtk.Label(label=_("Profile:"))
@@ -732,13 +856,16 @@ class ProfileSettings(Gtk.Grid):
host_label.set_xalign(1)
port_label=Gtk.Label(label=_("Port:"))
port_label.set_xalign(1)
password_label=Gtk.Label(label=_("Password:"))
password_label.set_xalign(1)
path_label=Gtk.Label(label=_("Music lib:"))
path_label.set_xalign(1)
#connect
self.profile_entry_changed=self.profile_entry.connect("activate", self.on_profile_entry_changed)
self.host_entry_changed=self.host_entry.connect("activate", self.on_host_entry_changed)
self.profile_entry_changed=self.profile_entry.connect("changed", self.on_profile_entry_changed)
self.host_entry_changed=self.host_entry.connect("changed", self.on_host_entry_changed)
self.port_entry_changed=self.port_entry.connect("value-changed", self.on_port_entry_changed)
self.password_entry_changed=self.password_entry.connect("changed", self.on_password_entry_changed)
self.path_select_button.connect("clicked", self.on_path_select_button_clicked, parent)
add_button.connect("clicked", self.on_add_button_clicked)
delete_button.connect("clicked", self.on_delete_button_clicked)
@@ -752,13 +879,15 @@ class ProfileSettings(Gtk.Grid):
self.attach_next_to(profile_label, profiles_label, Gtk.PositionType.BOTTOM, 1, 1)
self.attach_next_to(host_label, profile_label, Gtk.PositionType.BOTTOM, 1, 1)
self.attach_next_to(port_label, host_label, Gtk.PositionType.BOTTOM, 1, 1)
self.attach_next_to(path_label, port_label, Gtk.PositionType.BOTTOM, 1, 1)
self.attach_next_to(password_label, port_label, Gtk.PositionType.BOTTOM, 1, 1)
self.attach_next_to(path_label, password_label, Gtk.PositionType.BOTTOM, 1, 1)
self.attach_next_to(self.profiles_combo, profiles_label, Gtk.PositionType.RIGHT, 1, 1)
self.attach_next_to(add_button, self.profiles_combo, Gtk.PositionType.RIGHT, 1, 1)
self.attach_next_to(delete_button, add_button, Gtk.PositionType.RIGHT, 1, 1)
self.attach_next_to(self.profile_entry, profile_label, Gtk.PositionType.RIGHT, 1, 1)
self.attach_next_to(self.host_entry, host_label, Gtk.PositionType.RIGHT, 1, 1)
self.attach_next_to(self.port_entry, port_label, Gtk.PositionType.RIGHT, 1, 1)
self.attach_next_to(self.password_entry, password_label, Gtk.PositionType.RIGHT, 1, 1)
self.attach_next_to(self.path_select_button, path_label, Gtk.PositionType.RIGHT, 1, 1)
def profiles_combo_reload(self, *args):
@@ -781,6 +910,7 @@ class ProfileSettings(Gtk.Grid):
self.settings.array_append('as', "profiles", "new profile")
self.settings.array_append('as', "hosts", "localhost")
self.settings.array_append('ai', "ports", 6600)
self.settings.array_append('as', "passwords", "")
self.settings.array_append('as', "paths", "")
self.profiles_combo_reload()
self.profiles_combo.set_active(pos)
@@ -790,7 +920,11 @@ class ProfileSettings(Gtk.Grid):
self.settings.array_delete('as', "profiles", pos)
self.settings.array_delete('as', "hosts", pos)
self.settings.array_delete('ai', "ports", pos)
self.settings.array_delete('as', "passwords", pos)
self.settings.array_delete('as', "paths", pos)
if len(self.settings.get_value("profiles")) == 0:
self.on_add_button_clicked()
else:
self.profiles_combo_reload()
self.profiles_combo.set_active(0)
@@ -806,6 +940,9 @@ class ProfileSettings(Gtk.Grid):
def on_port_entry_changed(self, *args):
self.settings.array_modify('ai', "ports", self.profiles_combo.get_active(), self.port_entry.get_int())
def on_password_entry_changed(self, *args):
self.settings.array_modify('as', "passwords", self.profiles_combo.get_active(), self.password_entry.get_text())
def on_path_select_button_clicked(self, widget, parent):
dialog = Gtk.FileChooserDialog(title=_("Choose directory"), transient_for=parent, action=Gtk.FileChooserAction.SELECT_FOLDER)
dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
@@ -815,6 +952,7 @@ class ProfileSettings(Gtk.Grid):
response = dialog.run()
if response == Gtk.ResponseType.OK:
self.settings.array_modify('as', "paths", self.profiles_combo.get_active(), dialog.get_filename())
self.path_select_button.set_tooltip_text(dialog.get_filename())
dialog.destroy()
def on_profiles_changed(self, *args):
@@ -822,22 +960,25 @@ class ProfileSettings(Gtk.Grid):
self.profile_entry.handler_block(self.profile_entry_changed)
self.host_entry.handler_block(self.host_entry_changed)
self.port_entry.handler_block(self.port_entry_changed)
self.password_entry.handler_block(self.password_entry_changed)
self.profile_entry.set_text(self.settings.get_value("profiles")[active])
self.host_entry.set_text(self.settings.get_value("hosts")[active])
self.port_entry.set_int(self.settings.get_value("ports")[active])
self.password_entry.set_text(self.settings.get_value("passwords")[active])
self.path_select_button.set_tooltip_text(self.settings.get_value("paths")[active])
self.profile_entry.handler_unblock(self.profile_entry_changed)
self.host_entry.handler_unblock(self.host_entry_changed)
self.port_entry.handler_unblock(self.port_entry_changed)
self.password_entry.handler_unblock(self.password_entry_changed)
class GeneralSettings(Gtk.Grid):
def __init__(self, settings):
Gtk.Grid.__init__(self)
self.set_row_spacing(3)
self.set_column_spacing(3)
self.set_property("border-width", 3)
self.set_row_spacing(4)
self.set_column_spacing(4)
self.set_property("border-width", 4)
#adding vars
self.settings = settings
@@ -855,7 +996,7 @@ class GeneralSettings(Gtk.Grid):
icon_size_label.set_xalign(1)
icon_size_combo=Gtk.ComboBoxText()
icon_size_combo.set_entry_text_column(0)
sizes=[16, 32, 48]
sizes=[16, 24, 32, 48]
for i in sizes:
icon_size_combo.append_text(str(i))
icon_size_combo.set_active(sizes.index(self.settings.get_int("icon-size")))
@@ -932,7 +1073,7 @@ class SettingsDialog(Gtk.Dialog):
class ClientControl(Gtk.ButtonBox):
def __init__(self, client, settings):
Gtk.ButtonBox.__init__(self, spacing=3)
Gtk.ButtonBox.__init__(self, spacing=4)
#adding vars
self.client=client
@@ -1031,6 +1172,7 @@ class SeekBar(Gtk.Box):
self.rest.set_width_chars(8)
self.scale=Gtk.Scale.new_with_range(orientation=Gtk.Orientation.HORIZONTAL, min=0, max=100, step=0.001)
self.scale.set_draw_value(False)
self.scale.set_can_focus(False)
#connect
self.scale.connect("change-value", self.seek)
@@ -1179,7 +1321,7 @@ class PlaybackOptions(Gtk.Box):
class AudioType(Gtk.EventBox):
def __init__(self, client):
Gtk.EventBox.__init__(self)
self.set_tooltip_text(_("Right click to show additional information"))
self.set_tooltip_text(_("Click to show additional information"))
#adding vars
self.client=client
@@ -1189,6 +1331,7 @@ class AudioType(Gtk.EventBox):
self.label.set_xalign(1)
self.label.set_ellipsize(Pango.EllipsizeMode.END)
self.popover=Gtk.Popover()
self.popover.set_relative_to(self)
#Store
#(tag, value)
@@ -1204,19 +1347,21 @@ class AudioType(Gtk.EventBox):
renderer_text = Gtk.CellRendererText()
self.column_tag = Gtk.TreeViewColumn(_("MPD-Tag"), renderer_text, text=0)
self.column_tag.set_property("resizable", False)
self.treeview.append_column(self.column_tag)
self.column_value = Gtk.TreeViewColumn(_("Value"), renderer_text, text=1)
self.column_value.set_property("resizable", False)
self.treeview.append_column(self.column_value)
self.popover.add(self.treeview)
#timeouts
GLib.timeout_add(1000, self.update)
#connect
self.connect("button-press-event", self.on_button_press_event)
#packing
self.popover.add(self.treeview)
self.add(self.label)
def update(self):
@@ -1236,11 +1381,9 @@ class AudioType(Gtk.EventBox):
return True
def on_button_press_event(self, widget, event):
if event.button == 3:
self.popover.remove(self.treeview) #workaround
self.store.clear()
self.popover.add(self.treeview) #workaround
if event.button == 1 or event.button == 2 or event.button == 3:
try:
self.store.clear()
song=self.client.status()["song"]
tags=self.client.playlistinfo(song)[0]
for key in tags:
@@ -1248,9 +1391,8 @@ class AudioType(Gtk.EventBox):
self.store.append([key, str(datetime.timedelta(seconds=int(tags[key])))])
else:
self.store.append([key, tags[key]])
self.popover.set_relative_to(self)
self.popover.show_all()
self.popover.popup()
self.treeview.queue_resize()
except:
pass
@@ -1267,18 +1409,17 @@ class ProfileSelect(Gtk.ComboBoxText):
self.settings.connect("changed::profiles", self.on_settings_changed)
self.settings.connect("changed::hosts", self.on_settings_changed)
self.settings.connect("changed::ports", self.on_settings_changed)
self.settings.connect("changed::passwords", self.on_settings_changed)
self.settings.connect("changed::paths", self.on_settings_changed)
self.reload()
self.handler_block(self.changed)
self.set_active(self.settings.get_int("active-profile"))
self.handler_unblock(self.changed)
def reload(self, *args):
self.handler_block(self.changed)
self.remove_all()
for profile in self.settings.get_value("profiles"):
self.append_text(profile)
self.set_active(self.settings.get_int("active-profile"))
self.handler_unblock(self.changed)
def on_settings_changed(self, *args):
@@ -1485,6 +1626,7 @@ class LyricsWindow(Gtk.Window): #Lyrics view with own client because MPDClient i
while not self.stop:
if self.client.connected():
cs=self.client.currentsong()
if not cs == {}:
cs.pop("pos") #avoid unnecessary reloads caused by position change of current title
if cs != self.current_song:
GLib.idle_add(update_label, _("searching..."))
@@ -1609,6 +1751,7 @@ class MainWindow(Gtk.ApplicationWindow):
self.search_button.connect("clicked", self.on_search_clicked)
self.lyrics_button.connect("toggled", self.on_lyrics_toggled)
self.info_bar.connect("response", self.on_info_bar_response)
self.settings.connect("changed::profiles", self.on_settings_changed)
#unmap space
binding_set=Gtk.binding_set_find('GtkTreeView')
Gtk.binding_entry_remove(binding_set, 32, Gdk.ModifierType.MOD2_MASK)
@@ -1619,8 +1762,11 @@ class MainWindow(Gtk.ApplicationWindow):
GLib.timeout_add(1000, self.update, app)
#packing
self.vbox=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=3)
self.hbox=Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=3)
self.vbox=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
self.hbox=Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=4)
self.hbox.set_margin_start(2)
self.hbox.set_margin_end(2)
self.hbox.set_margin_bottom(2)
self.vbox.pack_start(self.info_bar, False, False, 0)
self.vbox.pack_start(self.browser, True, True, 0)
self.vbox.pack_start(self.hbox, False, False, 0)
@@ -1629,6 +1775,7 @@ class MainWindow(Gtk.ApplicationWindow):
self.hbox.pack_start(self.go_home_button, False, False, 0)
self.hbox.pack_start(self.search_button, False, False, 0)
self.hbox.pack_start(self.lyrics_button, False, False, 0)
if len(self.settings.get_value("profiles")) > 1:
self.hbox.pack_start(self.profiles, False, False, 0)
self.hbox.pack_start(self.play_opts, False, False, 0)
self.hbox.pack_end(menu_button, False, False, 0)
@@ -1698,8 +1845,14 @@ class MainWindow(Gtk.ApplicationWindow):
self.lyrics_win.destroy()
def on_key_press_event(self, widget, event):
if event.keyval == 32:
if event.keyval == 32: #space
self.control.play_button.grab_focus()
if event.keyval == 269025044: #AudioPlay
self.control.play_button.emit("clicked")
elif event.keyval == 269025047: #AudioNext
self.control.next_button.emit("clicked")
elif event.keyval == 269025046: #AudioPrev
self.control.prev_button.emit("clicked")
def on_save(self, action, param):
size=self.get_size()
@@ -1722,6 +1875,13 @@ class MainWindow(Gtk.ApplicationWindow):
if self.client.connected():
self.client.update()
def on_settings_changed(self, *args):
self.hbox.remove(self.profiles)
if len(self.settings.get_value("profiles")) > 1:
self.hbox.pack_start(self.profiles, False, False, 0)
self.hbox.reorder_child(self.profiles, 5)
self.profiles.show()
class mpdevil(Gtk.Application):
def __init__(self, *args, **kwargs):
super().__init__(*args, application_id="org.mpdevil", flags=Gio.ApplicationFlags.FLAGS_NONE, **kwargs)
@@ -1758,6 +1918,7 @@ class mpdevil(Gtk.Application):
dialog.set_comments(_("A small MPD client written in python"))
dialog.set_authors(["Martin Wagner"])
dialog.set_website("https://github.com/SoongNoonien/mpdevil")
dialog.set_copyright("\xa9 2020 Martin Wagner")
dialog.set_logo_icon_name(PACKAGE)
dialog.run()
dialog.destroy()

View File

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

View File

@@ -32,7 +32,7 @@
<description></description>
</key>
<key type="i" name="icon-size">
<default>16</default>
<default>24</default>
<summary>Size of button icons in control bar</summary>
<description></description>
</key>
@@ -81,6 +81,11 @@
<summary>List of ports</summary>
<description></description>
</key>
<key type="as" name="passwords">
<default>[""]</default>
<summary>List of passwords</summary>
<description></description>
</key>
<key type="as" name="paths">
<default>[""]</default>
<summary>List of library paths</summary>

124
po/de.po
View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-01-28 19:56+0100\n"
"PO-Revision-Date: 2020-01-28 19:58+0100\n"
"POT-Creation-Date: 2020-02-02 18:07+0100\n"
"PO-Revision-Date: 2020-02-02 18:08+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: de\n"
@@ -18,144 +18,148 @@ msgstr ""
"X-Generator: Poedit 2.2.4\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: mpdevil.py:159 mpdevil.py:368 mpdevil.py:1366
#: mpdevil.py:199 mpdevil.py:522 mpdevil.py:1474
msgid "No"
msgstr "Nr."
#: mpdevil.py:164 mpdevil.py:373 mpdevil.py:1372
#: mpdevil.py:204 mpdevil.py:527 mpdevil.py:1480
msgid "Title"
msgstr "Titel"
#: mpdevil.py:169 mpdevil.py:378 mpdevil.py:1378
#: mpdevil.py:209 mpdevil.py:532 mpdevil.py:1486
msgid "Artist"
msgstr "Interpret"
#: mpdevil.py:174 mpdevil.py:383 mpdevil.py:1390
#: mpdevil.py:214 mpdevil.py:537 mpdevil.py:1498
msgid "Length"
msgstr "Länge"
#: mpdevil.py:214 mpdevil.py:483 mpdevil.py:1427
#: mpdevil.py:254 mpdevil.py:633 mpdevil.py:1535
msgid "Unknown Title"
msgstr "Unbekannter Titel"
#: mpdevil.py:218 mpdevil.py:491 mpdevil.py:1435
#: mpdevil.py:258 mpdevil.py:641 mpdevil.py:1543
msgid "Unknown Artist"
msgstr "Unbekannter Künstler"
#: mpdevil.py:254
#: mpdevil.py:294
msgid "Album Artist"
msgstr "Albuminterpret"
#: mpdevil.py:313 mpdevil.py:504
#: mpdevil.py:381 mpdevil.py:608
#, python-format
msgid "%(total_tracks)i titles (%(total_length)s)"
msgstr "%(total_tracks)i Titel (%(total_length)s)"
#: mpdevil.py:495 mpdevil.py:1439
#: mpdevil.py:645 mpdevil.py:1547
msgid "Unknown Album"
msgstr "Unbekanntes Album"
#: mpdevil.py:725
#: mpdevil.py:814
msgid "Select"
msgstr "Auswählen"
#: mpdevil.py:727
#: mpdevil.py:816
msgid "Profile:"
msgstr "Profil:"
#: mpdevil.py:729
#: mpdevil.py:818
msgid "Name:"
msgstr "Name:"
#: mpdevil.py:731
#: mpdevil.py:820
msgid "Host:"
msgstr "Host:"
#: mpdevil.py:733
#: mpdevil.py:822
msgid "Port:"
msgstr "Port:"
#: mpdevil.py:735
#: mpdevil.py:824
msgid "Password:"
msgstr "Passwort:"
#: mpdevil.py:826
msgid "Music lib:"
msgstr "Musikverzeichnis:"
#: mpdevil.py:810
#: mpdevil.py:912
msgid "Choose directory"
msgstr "Verzeichnis Wählen"
#: mpdevil.py:846
#: mpdevil.py:952
msgid "Main cover size:"
msgstr "Größe des Haupt-Covers:"
#: mpdevil.py:848
#: mpdevil.py:954
msgid "Album-view cover size:"
msgstr "Covergröße in Albumansicht:"
#: mpdevil.py:854
#: mpdevil.py:960
msgid "Button icon size (restart required):"
msgstr "Symbolgröße der Knöpfe (Neustart erforderlich):"
#: mpdevil.py:863
#: mpdevil.py:969
msgid "Show stop button"
msgstr "Zeige Stopp-Knopf"
#: mpdevil.py:866
#: mpdevil.py:972
msgid "Show tooltips in album view"
msgstr "Zeige Tooltips in Albumansicht"
#: mpdevil.py:869
#: mpdevil.py:975
msgid "Send notification on title change"
msgstr "Sende Benachrichtigung bei Titelwechsel"
#: mpdevil.py:872
#: mpdevil.py:978
msgid "Stop playback on quit"
msgstr "Wiedergabe beim Beenden stoppen"
#: mpdevil.py:875
#: mpdevil.py:981
msgid "Play selected album after current title"
msgstr "Ausgewähltes Album hinter aktuellem Titel einreihen"
#: mpdevil.py:913 mpdevil.py:1596
#: mpdevil.py:1019 mpdevil.py:1704
msgid "Settings"
msgstr "Einstellungen"
#: mpdevil.py:926
#: mpdevil.py:1032
msgid "General"
msgstr "Allgemein"
#: mpdevil.py:927
#: mpdevil.py:1033
msgid "Profiles"
msgstr "Profile"
#: mpdevil.py:1081
#: mpdevil.py:1188
msgid "Random mode"
msgstr "Zufallsmodus"
#: mpdevil.py:1083
#: mpdevil.py:1190
msgid "Repeat mode"
msgstr "Dauerschleife"
#: mpdevil.py:1085
#: mpdevil.py:1192
msgid "Single mode"
msgstr "Einzelstückmodus"
#: mpdevil.py:1087
#: mpdevil.py:1194
msgid "Consume mode"
msgstr "Playliste verbrauchen"
#: mpdevil.py:1182
msgid "Right click to show additional information"
msgstr "Rechtsclick für weitere Informationen"
#: mpdevil.py:1289
msgid "Click to show additional information"
msgstr "Klicken für weitere Informationen"
#: mpdevil.py:1206
#: mpdevil.py:1313
msgid "MPD-Tag"
msgstr "MPD-Tag"
#: mpdevil.py:1209 mpdevil.py:1316
#: mpdevil.py:1316 mpdevil.py:1424
msgid "Value"
msgstr "Wert"
#: mpdevil.py:1230
#: mpdevil.py:1337
#, python-format
msgid ""
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s "
@@ -164,88 +168,88 @@ msgstr ""
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s "
"Kanäle, %(file_type)s"
#: mpdevil.py:1295
#: mpdevil.py:1403
msgid "Stats"
msgstr "Statistik"
#: mpdevil.py:1313
#: mpdevil.py:1421
msgid "Tag"
msgstr "Tag"
#: mpdevil.py:1333
#: mpdevil.py:1441
msgid "Search"
msgstr "Suche"
#: mpdevil.py:1384
#: mpdevil.py:1492
msgid "Album"
msgstr "Album"
#: mpdevil.py:1446
#: mpdevil.py:1554
#, python-format
msgid "Hits: %i"
msgstr "Treffer: %i"
#: mpdevil.py:1450
#: mpdevil.py:1558
msgid "Lyrics"
msgstr "Liedtext"
#: mpdevil.py:1490
#: mpdevil.py:1598
msgid "searching..."
msgstr "suche..."
#: mpdevil.py:1494
#: mpdevil.py:1602
msgid "not found"
msgstr "nicht gefunden"
#: mpdevil.py:1499
#: mpdevil.py:1607
msgid "not connected"
msgstr "nicht verbunden"
#: mpdevil.py:1576
#: mpdevil.py:1684
msgid "Select profile"
msgstr "Profil auswählen"
#: mpdevil.py:1580
#: mpdevil.py:1688
msgid "Return to album of current title"
msgstr "Zu Album des aktuellen Titels zurückkehren"
#: mpdevil.py:1582
#: mpdevil.py:1690
msgid "Title search"
msgstr "Titelsuche"
#: mpdevil.py:1584
#: mpdevil.py:1692
msgid "Show lyrics"
msgstr "Zeige Liedtext"
#: mpdevil.py:1591
#: mpdevil.py:1699
msgid "Not connected to MPD-server. Reconnect?"
msgstr "Nicht mit MPD-Server verbunden. Verbindung wiederherstellen?"
#: mpdevil.py:1595
#: mpdevil.py:1703
msgid "Save window size"
msgstr "Fenstergröße speichern"
#: mpdevil.py:1597
#: mpdevil.py:1705
msgid "Update database"
msgstr "Datenbank aktualisieren"
#: mpdevil.py:1598
#: mpdevil.py:1706
msgid "Server stats"
msgstr "Serverstatistik"
#: mpdevil.py:1599
#: mpdevil.py:1707
msgid "About"
msgstr "Über"
#: mpdevil.py:1600
#: mpdevil.py:1708
msgid "Quit"
msgstr "Beenden"
#: mpdevil.py:1605
#: mpdevil.py:1713
msgid "Main menu"
msgstr "Hauptmenu"
#: mpdevil.py:1758
#: mpdevil.py:1869
msgid "A small MPD client written in python"
msgstr ""

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-01-28 19:56+0100\n"
"POT-Creation-Date: 2020-02-02 18:07+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,231 +17,235 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: mpdevil.py:159 mpdevil.py:368 mpdevil.py:1366
#: mpdevil.py:199 mpdevil.py:522 mpdevil.py:1474
msgid "No"
msgstr ""
#: mpdevil.py:164 mpdevil.py:373 mpdevil.py:1372
#: mpdevil.py:204 mpdevil.py:527 mpdevil.py:1480
msgid "Title"
msgstr ""
#: mpdevil.py:169 mpdevil.py:378 mpdevil.py:1378
#: mpdevil.py:209 mpdevil.py:532 mpdevil.py:1486
msgid "Artist"
msgstr ""
#: mpdevil.py:174 mpdevil.py:383 mpdevil.py:1390
#: mpdevil.py:214 mpdevil.py:537 mpdevil.py:1498
msgid "Length"
msgstr ""
#: mpdevil.py:214 mpdevil.py:483 mpdevil.py:1427
#: mpdevil.py:254 mpdevil.py:633 mpdevil.py:1535
msgid "Unknown Title"
msgstr ""
#: mpdevil.py:218 mpdevil.py:491 mpdevil.py:1435
#: mpdevil.py:258 mpdevil.py:641 mpdevil.py:1543
msgid "Unknown Artist"
msgstr ""
#: mpdevil.py:254
#: mpdevil.py:294
msgid "Album Artist"
msgstr ""
#: mpdevil.py:313 mpdevil.py:504
#: mpdevil.py:381 mpdevil.py:608
#, python-format
msgid "%(total_tracks)i titles (%(total_length)s)"
msgstr ""
#: mpdevil.py:495 mpdevil.py:1439
#: mpdevil.py:645 mpdevil.py:1547
msgid "Unknown Album"
msgstr ""
#: mpdevil.py:725
#: mpdevil.py:814
msgid "Select"
msgstr ""
#: mpdevil.py:727
#: mpdevil.py:816
msgid "Profile:"
msgstr ""
#: mpdevil.py:729
#: mpdevil.py:818
msgid "Name:"
msgstr ""
#: mpdevil.py:731
#: mpdevil.py:820
msgid "Host:"
msgstr ""
#: mpdevil.py:733
#: mpdevil.py:822
msgid "Port:"
msgstr ""
#: mpdevil.py:735
#: mpdevil.py:824
msgid "Password:"
msgstr ""
#: mpdevil.py:826
msgid "Music lib:"
msgstr ""
#: mpdevil.py:810
#: mpdevil.py:912
msgid "Choose directory"
msgstr ""
#: mpdevil.py:846
#: mpdevil.py:952
msgid "Main cover size:"
msgstr ""
#: mpdevil.py:848
#: mpdevil.py:954
msgid "Album-view cover size:"
msgstr ""
#: mpdevil.py:854
#: mpdevil.py:960
msgid "Button icon size (restart required):"
msgstr ""
#: mpdevil.py:863
#: mpdevil.py:969
msgid "Show stop button"
msgstr ""
#: mpdevil.py:866
#: mpdevil.py:972
msgid "Show tooltips in album view"
msgstr ""
#: mpdevil.py:869
#: mpdevil.py:975
msgid "Send notification on title change"
msgstr ""
#: mpdevil.py:872
#: mpdevil.py:978
msgid "Stop playback on quit"
msgstr ""
#: mpdevil.py:875
#: mpdevil.py:981
msgid "Play selected album after current title"
msgstr ""
#: mpdevil.py:913 mpdevil.py:1596
#: mpdevil.py:1019 mpdevil.py:1704
msgid "Settings"
msgstr ""
#: mpdevil.py:926
#: mpdevil.py:1032
msgid "General"
msgstr ""
#: mpdevil.py:927
#: mpdevil.py:1033
msgid "Profiles"
msgstr ""
#: mpdevil.py:1081
#: mpdevil.py:1188
msgid "Random mode"
msgstr ""
#: mpdevil.py:1083
#: mpdevil.py:1190
msgid "Repeat mode"
msgstr ""
#: mpdevil.py:1085
#: mpdevil.py:1192
msgid "Single mode"
msgstr ""
#: mpdevil.py:1087
#: mpdevil.py:1194
msgid "Consume mode"
msgstr ""
#: mpdevil.py:1182
msgid "Right click to show additional information"
#: mpdevil.py:1289
msgid "Click to show additional information"
msgstr ""
#: mpdevil.py:1206
#: mpdevil.py:1313
msgid "MPD-Tag"
msgstr ""
#: mpdevil.py:1209 mpdevil.py:1316
#: mpdevil.py:1316 mpdevil.py:1424
msgid "Value"
msgstr ""
#: mpdevil.py:1230
#: mpdevil.py:1337
#, python-format
msgid ""
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s "
"channels, %(file_type)s"
msgstr ""
#: mpdevil.py:1295
#: mpdevil.py:1403
msgid "Stats"
msgstr ""
#: mpdevil.py:1313
#: mpdevil.py:1421
msgid "Tag"
msgstr ""
#: mpdevil.py:1333
#: mpdevil.py:1441
msgid "Search"
msgstr ""
#: mpdevil.py:1384
#: mpdevil.py:1492
msgid "Album"
msgstr ""
#: mpdevil.py:1446
#: mpdevil.py:1554
#, python-format
msgid "Hits: %i"
msgstr ""
#: mpdevil.py:1450
#: mpdevil.py:1558
msgid "Lyrics"
msgstr ""
#: mpdevil.py:1490
#: mpdevil.py:1598
msgid "searching..."
msgstr ""
#: mpdevil.py:1494
#: mpdevil.py:1602
msgid "not found"
msgstr ""
#: mpdevil.py:1499
#: mpdevil.py:1607
msgid "not connected"
msgstr ""
#: mpdevil.py:1576
#: mpdevil.py:1684
msgid "Select profile"
msgstr ""
#: mpdevil.py:1580
#: mpdevil.py:1688
msgid "Return to album of current title"
msgstr ""
#: mpdevil.py:1582
#: mpdevil.py:1690
msgid "Title search"
msgstr ""
#: mpdevil.py:1584
#: mpdevil.py:1692
msgid "Show lyrics"
msgstr ""
#: mpdevil.py:1591
#: mpdevil.py:1699
msgid "Not connected to MPD-server. Reconnect?"
msgstr ""
#: mpdevil.py:1595
#: mpdevil.py:1703
msgid "Save window size"
msgstr ""
#: mpdevil.py:1597
#: mpdevil.py:1705
msgid "Update database"
msgstr ""
#: mpdevil.py:1598
#: mpdevil.py:1706
msgid "Server stats"
msgstr ""
#: mpdevil.py:1599
#: mpdevil.py:1707
msgid "About"
msgstr ""
#: mpdevil.py:1600
#: mpdevil.py:1708
msgid "Quit"
msgstr ""
#: mpdevil.py:1605
#: mpdevil.py:1713
msgid "Main menu"
msgstr ""
#: mpdevil.py:1758
#: mpdevil.py:1869
msgid "A small MPD client written in python"
msgstr ""

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 KiB

After

Width:  |  Height:  |  Size: 1002 KiB