24 Commits

Author SHA1 Message Date
Martin Wagner
eb8edecfda perparations for 0.7.0 2020-03-12 21:48:09 +01:00
Martin Wagner
d059a1c9eb improved visual sort index 2020-03-12 17:09:24 +01:00
Martin Wagner
6196f83ffc improved cover-size settings 2020-03-10 13:50:36 +01:00
Martin Wagner
4f273a8a1f translation update 2020-03-06 11:37:15 +01:00
Martin Wagner
67ec5ad695 small settings dialog code review 2020-03-06 11:27:05 +01:00
Martin Wagner
f5b911f2cf reworked playlist settings 2020-03-06 09:38:05 +01:00
Martin Wagner
6a673f2bab made track and disc column right aligned 2020-03-05 23:29:48 +01:00
Martin Wagner
f7e6c23696 fixed selection move on force album play by doubleclicking 2020-03-05 15:14:49 +01:00
Martin Wagner
0fb67698cd fixed column width save and restore 2020-03-04 18:55:43 +01:00
Martin Wagner
ae3ac76b3d made playlist columns adjustable 2020-03-04 16:39:59 +01:00
Martin Wagner
e9805bbe70 disabled unwanted keyboard focus 2020-03-04 10:04:47 +01:00
Martin Wagner
2fdb9658dd added Gtk.Frame around the main cover 2020-03-04 09:58:26 +01:00
Martin Wagner
60173ade3a fixed error in go_home 2020-03-04 08:51:18 +01:00
Martin Wagner
b5ac0583ba added visual sort index in artist view 2020-03-03 23:54:20 +01:00
Martin Wagner
24f026ff61 added alternative layout 2020-03-03 23:43:44 +01:00
Martin Wagner
32157bb609 fixed autoremove 2020-03-03 22:54:55 +01:00
Martin Wagner
3be9589b75 fixed Gtk-CRITICAL 2020-03-03 17:57:23 +01:00
Martin Wagner
36df7cf7dd fixed error in autoremove 2020-03-03 17:20:58 +01:00
Martin Wagner
28e526777e highlight current song with bold text 2020-03-03 17:09:56 +01:00
Martin Wagner
ac53fc2136 added seeking hot-keys 2020-03-03 16:11:30 +01:00
Martin Wagner
d909f351d3 improved notification 2020-03-03 15:59:18 +01:00
Martin Wagner
8756e317d2 detached main cover from playlist 2020-03-03 13:41:46 +01:00
Martin Wagner
db5fbef06e fixed playlist refresh 2020-03-02 15:48:10 +01:00
Martin Wagner
da1bb25d1d fixed playlist refresh error on profile switch 2020-03-02 11:49:55 +01:00
8 changed files with 648 additions and 361 deletions

View File

@@ -2,7 +2,7 @@ README for mpdevil
==================
mpdevil is focused on playing your local music directly instead of managing playlists or playing network streams. So it neither supports saving playlists nor restoring them. Therefore mpdevil is mainly a music browser which aims to be easy to use. mpdevil dosen't store any client side database of your music library. Instead all tags and covers get presented to you in real time. So you'll never see any outdated information in your browser. mpdevil strongly relies on tags.
![ScreenShot](screenshots/mainwindow_0.6.0.png)
![ScreenShot](screenshots/mainwindow_0.7.0.png)
Features
--------

View File

@@ -20,7 +20,8 @@
import gi #python-gobject dev-python/pygobject:3[${PYTHON_USEDEP}]
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio, Gdk, GdkPixbuf, Pango, GObject, GLib
gi.require_version('Notify', '0.7')
from gi.repository import Gtk, Gio, Gdk, GdkPixbuf, Pango, GObject, GLib, Notify
from mpd import MPDClient
import requests #dev-python/requests
from bs4 import BeautifulSoup, Comment #, NavigableString #dev-python/beautifulsoup
@@ -49,9 +50,9 @@ except locale.Error:
gettext.install(PACKAGE, localedir='@datadir@/locale')
class IntEntry(Gtk.SpinButton):
def __init__(self, default, lower, upper):
def __init__(self, default, lower, upper, step):
Gtk.SpinButton.__init__(self)
adj = Gtk.Adjustment(value=default, lower=lower, upper=upper, step_increment=1)
adj = Gtk.Adjustment(value=default, lower=lower, upper=upper, step_increment=step)
self.set_adjustment(adj)
def get_int(self):
@@ -127,6 +128,7 @@ class Client(MPDClient):
self.move(0, (len(self.playlist())-1))
self.song_to_delete=""
else:
self.song_to_delete=""
songs=self.find("album", album, "date", year, self.settings.get_artist_type(), artist)
if not songs == []:
self.stop()
@@ -157,6 +159,7 @@ class MpdEventEmitter(GObject.Object):
except:
self.client.try_connect_default()
if self.client.connected():
self.emit("disconnected")
self.emit("reconnected")
self.connected=True
elif self.connected:
@@ -414,9 +417,8 @@ class ArtistView(Gtk.ScrolledWindow):
self.genre_select=genre_select
#artistStore
#(name)
self.store = Gtk.ListStore(str)
self.store.set_sort_column_id(0, Gtk.SortType.ASCENDING)
#(name, initial-letter, weight, font-scale)
self.store = Gtk.ListStore(str, str, Pango.Weight, float)
#TreeView
self.treeview = Gtk.TreeView(model=self.store)
@@ -427,17 +429,24 @@ class ArtistView(Gtk.ScrolledWindow):
self.selection = self.treeview.get_selection()
self.selection.set_mode(Gtk.SelectionMode.MULTIPLE)
#Old Name Column
#Columns
renderer_text_malign = Gtk.CellRendererText(xalign=0.5)
self.column_initials = Gtk.TreeViewColumn("", renderer_text_malign, text=1, weight=2, scale=3)
self.column_initials.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
self.column_initials.set_property("resizable", False)
self.column_initials.set_visible(self.settings.get_boolean("show-initials"))
self.treeview.append_column(self.column_initials)
renderer_text = Gtk.CellRendererText()
self.column_name = Gtk.TreeViewColumn("", renderer_text, text=0)
self.column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
self.column_name.set_property("resizable", False)
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.settings.connect("changed::show-all-artists", self.refresh)
self.settings.connect("changed::show-initials", self.on_show_initials_settings_changed)
self.update_signal=self.emitter.connect("update", self.refresh)
self.add(self.treeview)
@@ -457,8 +466,16 @@ class ArtistView(Gtk.ScrolledWindow):
artists=self.client.list(self.settings.get_artist_type())
else:
artists=self.client.list(self.settings.get_artist_type(), "genre", genre)
current_char=""
for artist in artists:
self.store.append([artist])
try:
if current_char != artist[0]:
self.store.append([artist, artist[0], Pango.Weight.BOLD, 1])
current_char=artist[0]
else:
self.store.append([artist, "", Pango.Weight.BOOK, 1])
except:
self.store.append([artist, "", Pango.Weight.BOOK, 1])
self.selection.set_mode(Gtk.SelectionMode.MULTIPLE)
def get_selected_artists(self):
@@ -474,6 +491,9 @@ class ArtistView(Gtk.ScrolledWindow):
def on_enter_event(self, widget, event):
self.treeview.grab_focus()
def on_show_initials_settings_changed(self, *args):
self.column_initials.set_visible(self.settings.get_boolean("show-initials"))
class AlbumIconView(Gtk.IconView):
def __init__(self, client, settings, genre_select, window):
Gtk.IconView.__init__(self)
@@ -642,6 +662,9 @@ class AlbumView(Gtk.ScrolledWindow):
self.iconview=AlbumIconView(self.client, self.settings, self.genre_select, self.window)
self.iconview.connect("stopped", self.update)
#connect
self.settings.connect("changed::album-cover", self.on_settings_changed)
self.add(self.iconview)
def update(self, *args):
@@ -667,105 +690,34 @@ class AlbumView(Gtk.ScrolledWindow):
def scroll_to_selected_album(self):
self.iconview.scroll_to_selected_album()
class TrackView(Gtk.Box):
def on_settings_changed(self, *args):
artists=self.artists
self.artists=[]
self.refresh(artists)
class MainCover(Gtk.EventBox):
def __init__(self, client, settings, emitter, window):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
Gtk.EventBox.__init__(self)
#adding vars
self.client=client
self.settings = settings
self.settings=settings
self.emitter=emitter
self.window=window
self.hovered_songpos=None
self.song_file=None
self.playlist_version=None
#Store
#(track, title, artist, album, duration, file)
self.store = Gtk.ListStore(str, str, str, str, str, str)
#TreeView
self.treeview = Gtk.TreeView(model=self.store)
self.treeview.set_search_column(-1)
self.treeview.columns_autosize()
self.treeview.set_property("activate-on-single-click", True)
#selection
self.selection = self.treeview.get_selection()
self.selection.set_mode(Gtk.SelectionMode.SINGLE)
#Column
renderer_text = Gtk.CellRendererText()
self.column_track = Gtk.TreeViewColumn(_("No"), renderer_text, text=0)
self.column_track.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
self.column_track.set_property("resizable", False)
self.treeview.append_column(self.column_track)
self.column_title = Gtk.TreeViewColumn(_("Title"), renderer_text, text=1)
self.column_title.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
self.column_title.set_property("resizable", True)
self.treeview.append_column(self.column_title)
self.column_artist = Gtk.TreeViewColumn(_("Artist"), renderer_text, text=2)
self.column_artist.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
self.column_artist.set_property("resizable", True)
self.treeview.append_column(self.column_artist)
self.column_duration = Gtk.TreeViewColumn(_("Length"), renderer_text, text=4)
self.column_duration.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
self.column_duration.set_property("resizable", False)
self.treeview.append_column(self.column_duration)
#scroll
scroll=Gtk.ScrolledWindow()
scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scroll.add(self.treeview)
#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)
#playlist info
self.playlist_info=Gtk.Label()
self.playlist_info.set_xalign(0)
self.playlist_info.set_ellipsize(Pango.EllipsizeMode.END)
#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)
#connect
self.treeview.connect("row-activated", self.on_row_activated)
self.treeview.connect("motion-notify-event", self.on_move_event)
self.treeview.connect("leave-notify-event", self.on_focus_out_event)
self.treeview.connect("focus-out-event", self.on_focus_out_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)
self.connect("button-press-event", self.on_button_press_event)
self.player_changed=self.emitter.connect("player", self.refresh)
self.settings.connect("changed::track-cover", self.on_settings_changed)
self.playlist_changed=self.emitter.connect("playlist", self.on_playlist_changed)
self.player_changed=self.emitter.connect("player", self.on_player_changed)
self.add(self.cover)
#packing
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 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):
def refresh(self, *args):
try:
song_file=self.client.currentsong()["file"]
except:
@@ -773,70 +725,6 @@ class TrackView(Gtk.Box):
if not song_file == self.song_file:
self.cover.set_from_pixbuf(Cover(client=self.client, lib_path=self.settings.get_value("paths")[self.settings.get_int("active-profile")], song_file=song_file).get_pixbuf(self.settings.get_int("track-cover")))
self.song_file=song_file
return True
def refresh_playlist_info(self):
songs=self.client.playlistinfo()
if not songs == []:
whole_length=float(0)
for song in songs:
try:
dura=float(song["duration"])
except:
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.playlist_info.set_text("")
def refresh_selection(self): #Gtk.TreePath(len(self.store) is used to generate an invalid TreePath (needed to unset cursor)
self.treeview.set_cursor(Gtk.TreePath(len(self.store)), None, False)
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 clear(self, *args):
self.playlist_info.set_text("")
self.store.clear()
self.playlist_version=None
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")))
self.song_file=None
def on_key_press_event(self, widget, event):
self.treeview.handler_block(self.key_press_event)
if event.keyval == 65535: #entf
if not self.hovered_songpos == None:
try:
self.client.delete(self.hovered_songpos) #bad song index possible
self.store.remove(self.store.get_iter(self.hovered_songpos))
self.playlist_version=self.client.status()["playlist"]
except:
self.hovered_songpos == None
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:
self.hovered_songpos=return_tuple[0]
else:
self.hovered_songpos=None
def on_focus_out_event(self, widget, event):
self.refresh_selection()
self.hovered_songpos=None
def on_row_activated(self, widget, path, view_column): #TODO
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():
@@ -869,6 +757,202 @@ class TrackView(Gtk.Box):
self.client.album_to_playlist(album, artist, album_year, False, True)
album_dialog.destroy()
def on_settings_changed(self, *args):
self.song_file=None
self.refresh()
def clear(self, *args):
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")))
self.song_file=None
class TrackView(Gtk.Box):
def __init__(self, client, settings, emitter):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
#adding vars
self.client=client
self.settings=settings
self.emitter=emitter
self.hovered_songpos=None
self.playlist_version=None
self.last_song_iter=None
#Store
#(track, disc, title, artist, album, duration, date, genre, file, weight)
self.store = Gtk.ListStore(str, str, str, str, str, str, str, str, str, Pango.Weight)
#TreeView
self.treeview = Gtk.TreeView(model=self.store)
self.treeview.set_search_column(-1)
self.treeview.set_property("activate-on-single-click", True)
#selection
self.selection = self.treeview.get_selection()
self.selection.set_mode(Gtk.SelectionMode.SINGLE)
#Column
renderer_text = Gtk.CellRendererText()
renderer_text_ralign = Gtk.CellRendererText(xalign=1.0)
self.columns=[None, None, None, None, None, None, None, None]
self.columns[0] = Gtk.TreeViewColumn(_("No"), renderer_text_ralign, text=0, weight=9)
self.columns[0].set_property("resizable", True)
self.columns[1] = Gtk.TreeViewColumn(_("Disc"), renderer_text_ralign, text=1, weight=9)
self.columns[1].set_property("resizable", True)
self.columns[2] = Gtk.TreeViewColumn(_("Title"), renderer_text, text=2, weight=9)
self.columns[2].set_property("resizable", True)
self.columns[3] = Gtk.TreeViewColumn(_("Artist"), renderer_text, text=3, weight=9)
self.columns[3].set_property("resizable", True)
self.columns[4] = Gtk.TreeViewColumn(_("Album"), renderer_text, text=4, weight=9)
self.columns[4].set_property("resizable", True)
self.columns[5] = Gtk.TreeViewColumn(_("Length"), renderer_text, text=5, weight=9)
self.columns[5].set_property("resizable", True)
self.columns[6] = Gtk.TreeViewColumn(_("Year"), renderer_text, text=6, weight=9)
self.columns[6].set_property("resizable", True)
self.columns[7] = Gtk.TreeViewColumn(_("Genre"), renderer_text, text=7, weight=9)
self.columns[7].set_property("resizable", True)
self.load_settings()
#scroll
scroll=Gtk.ScrolledWindow()
scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scroll.add(self.treeview)
#audio infos
audio=AudioType(self.client)
#playlist info
self.playlist_info=Gtk.Label()
self.playlist_info.set_xalign(0)
self.playlist_info.set_ellipsize(Pango.EllipsizeMode.END)
#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)
#connect
self.treeview.connect("row-activated", self.on_row_activated)
self.treeview.connect("motion-notify-event", self.on_move_event)
self.treeview.connect("leave-notify-event", self.on_focus_out_event)
self.treeview.connect("focus-out-event", self.on_focus_out_event)
self.key_press_event=self.treeview.connect("key-press-event", self.on_key_press_event)
self.playlist_changed=self.emitter.connect("playlist", self.on_playlist_changed)
self.player_changed=self.emitter.connect("player", self.on_player_changed)
self.disconnected_signal=self.emitter.connect("disconnected", self.on_disconnected)
self.settings.connect("changed::column-visibilities", self.load_settings)
self.settings.connect("changed::column-permutation", self.load_settings)
#packing
self.pack_start(scroll, True, True, 0)
self.pack_end(status_bar, False, False, 0)
def save_settings(self): #only saves the column sizes
columns=self.treeview.get_columns()
permutation=self.settings.get_value("column-permutation").unpack()
sizes=[0] * len(permutation)
for i in range(len(permutation)):
sizes[permutation[i]]=columns[i].get_width()
self.settings.set_value("column-sizes", GLib.Variant("ai", sizes))
def load_settings(self, *args):
columns=self.treeview.get_columns()
for column in columns:
self.treeview.remove_column(column)
sizes=self.settings.get_value("column-sizes").unpack()
visibilities=self.settings.get_value("column-visibilities").unpack()
for i in self.settings.get_value("column-permutation"):
if sizes[i] > 0:
self.columns[i].set_fixed_width(sizes[i])
self.columns[i].set_visible(visibilities[i])
self.treeview.append_column(self.columns[i])
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_playlist_info(self):
songs=self.client.playlistinfo()
if not songs == []:
whole_length=float(0)
for song in songs:
try:
dura=float(song["duration"])
except:
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.playlist_info.set_text("")
def refresh_selection(self): #Gtk.TreePath(len(self.store) is used to generate an invalid TreePath (needed to unset cursor)
self.treeview.set_cursor(Gtk.TreePath(len(self.store)), None, False)
try:
song=self.client.status()["song"]
path = Gtk.TreePath(int(song))
self.selection.select_path(path)
if self.last_song_iter != None and self.store.iter_is_valid(self.last_song_iter):
self.store.set_value(self.last_song_iter, 9, Pango.Weight.BOOK)
treeiter=self.store.get_iter(path)
self.store.set_value(treeiter, 9, Pango.Weight.BOLD)
self.last_song_iter=treeiter
except:
if self.last_song_iter != None:
self.store.set_value(self.last_song_iter, 9, Pango.Weight.BOOK)
self.last_song_iter=None
self.selection.unselect_all()
def clear(self, *args):
self.playlist_info.set_text("")
self.store.clear()
self.playlist_version=None
def on_key_press_event(self, widget, event):
self.treeview.handler_block(self.key_press_event)
if event.keyval == 65535: #entf
if not self.hovered_songpos == None:
try:
self.client.delete(self.hovered_songpos) #bad song index possible
self.store.remove(self.store.get_iter(self.hovered_songpos))
self.playlist_version=self.client.status()["playlist"]
except:
self.hovered_songpos == None
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:
self.hovered_songpos=return_tuple[0]
else:
self.hovered_songpos=None
def on_focus_out_event(self, widget, event):
self.refresh_selection()
self.hovered_songpos=None
def on_row_activated(self, widget, path, view_column): #TODO
treeiter=self.store.get_iter(path)
selected_title=self.store.get_path(treeiter)
self.client.play(selected_title)
def on_playlist_changed(self, *args):
songs=[]
if not self.playlist_version == None:
@@ -883,9 +967,13 @@ class TrackView(Gtk.Box):
except:
title=_("Unknown Title")
try:
track=song["track"].zfill(2)
track=song["track"]
except:
track="00"
track="0"
try:
disc=song["disc"]
except:
disc=""
try:
artist=song["artist"]
except:
@@ -898,30 +986,42 @@ class TrackView(Gtk.Box):
dura=float(song["duration"])
except:
dura=0.0
try:
year=song["date"]
except:
year=""
try:
genre=song["genre"]
except:
genre=""
duration=str(datetime.timedelta(seconds=int(dura )))
try:
treeiter=self.store.get_iter(song["pos"])
self.store.set(treeiter, 0, track, 1, title, 2, artist, 3, album, 4, duration, 5, song["file"])
self.store.set(treeiter, 0, track, 1, disc, 2, title, 3, artist, 4, album, 5, duration, 6, year, 7, genre, 8, song["file"], 9, Pango.Weight.BOOK)
except:
self.store.append([track, title, artist, album, duration, song["file"]])
for i in reversed(range(int(self.client.status()["playlistlength"]), len(self.store))):
treeiter=self.store.get_iter(i)
self.store.remove(treeiter)
self.refresh_playlist_info()
self.refresh_selection()
self.store.append([track, disc, title, artist, album, duration, year, genre, song["file"], Pango.Weight.BOOK])
for i in reversed(range(int(self.client.status()["playlistlength"]), len(self.store))):
treeiter=self.store.get_iter(i)
self.store.remove(treeiter)
self.refresh_playlist_info()
self.refresh_selection()
self.playlist_version=self.client.status()["playlist"]
def on_player_changed(self, *args):
if not self.client.song_to_delete == "": #TODO should be in Client class
status=self.client.status()
if not status["song"] == "0" and self.client.playlistinfo()[0]["file"] == self.client.song_to_delete:
self.client.delete(0)
if not status["song"] == "0":
if self.client.playlistinfo()[0]["file"] == self.client.song_to_delete:
self.client.delete(0)
self.client.song_to_delete=""
else:
self.refresh_selection()
self.client.song_to_delete=""
else:
self.refresh_selection()
def on_disconnected(self, *args):
self.playlist_version=None
class Browser(Gtk.Box):
def __init__(self, client, settings, emitter, window):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=4)
@@ -939,31 +1039,42 @@ class Browser(Gtk.Box):
self.genre_select.set_margin_top(2)
self.artist_list=ArtistView(self.client, self.settings, self.emitter, self.genre_select)
self.album_list=AlbumView(self.client, self.settings, self.genre_select, self.window)
self.title_list=TrackView(self.client, self.settings, self.emitter, self.window)
self.main_cover=MainCover(self.client, self.settings, self.emitter, self.window)
self.main_cover.set_property("border-width", 5)
cover_frame=Gtk.Frame()
cover_frame.add(self.main_cover)
self.title_list=TrackView(self.client, self.settings, self.emitter)
#connect
self.artist_change=self.artist_list.selection.connect("changed", self.on_artist_selection_change)
self.settings.connect("changed::show-genre-filter", self.on_settings_changed)
self.settings.connect("changed::show-genre-filter", self.on_show_genre_settings_changed)
self.settings.connect("changed::alt-layout", self.on_layout_settings_changed)
#packing
self.vbox=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
self.box1=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
if self.settings.get_boolean("show-genre-filter"):
self.vbox.pack_start(self.genre_select, False, False, 0)
self.vbox.pack_start(self.artist_list, True, True, 0)
self.box1.pack_start(self.genre_select, False, False, 0)
self.box1.pack_start(self.artist_list, True, True, 0)
self.box2=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
self.box2.pack_start(cover_frame, False, False, 0)
self.box2.pack_start(self.title_list, True, True, 0)
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.vbox, False, False)
self.paned1.pack1(self.box1, False, False)
self.paned1.pack2(self.album_list, True, False)
self.paned2.pack1(self.paned1, True, False)
self.paned2.pack2(self.title_list, False, False)
self.paned2.pack2(self.box2, False, False)
self.load_settings()
self.pack_start(self.paned2, True, True, 0)
self.on_layout_settings_changed()
def save_settings(self):
self.settings.set_int("paned1", self.paned1.get_position())
self.settings.set_int("paned2", self.paned2.get_position())
self.title_list.save_settings()
def load_settings(self):
self.paned1.set_position(self.settings.get_int("paned1"))
@@ -975,6 +1086,7 @@ class Browser(Gtk.Box):
self.artist_list.selection.handler_unblock(self.artist_change)
self.album_list.clear()
self.title_list.clear()
self.main_cover.clear()
def go_home(self, *args): #TODO
try:
@@ -1000,14 +1112,22 @@ class Browser(Gtk.Box):
artists=self.artist_list.get_selected_artists()
self.album_list.refresh(artists)
def on_settings_changed(self, *args):
def on_show_genre_settings_changed(self, *args):
if self.settings.get_boolean("show-genre-filter"):
self.vbox.pack_start(self.genre_select, False, False, 0)
self.vbox.reorder_child(self.genre_select, 0)
self.box1.pack_start(self.genre_select, False, False, 0)
self.box1.reorder_child(self.genre_select, 0)
self.genre_select.show_all()
else:
self.genre_select.deactivate()
self.vbox.remove(self.genre_select)
self.box1.remove(self.genre_select)
def on_layout_settings_changed(self, *args):
if self.settings.get_boolean("alt-layout"):
self.box2.set_orientation(Gtk.Orientation.HORIZONTAL)
self.paned2.set_orientation(Gtk.Orientation.VERTICAL)
else:
self.box2.set_orientation(Gtk.Orientation.VERTICAL)
self.paned2.set_orientation(Gtk.Orientation.HORIZONTAL)
class ProfileSettings(Gtk.Grid):
def __init__(self, parent, settings):
@@ -1028,7 +1148,7 @@ class ProfileSettings(Gtk.Grid):
self.profile_entry=Gtk.Entry()
self.host_entry=Gtk.Entry()
self.port_entry=IntEntry(0, 0, 65535)
self.port_entry=IntEntry(0, 0, 65535, 1)
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))
@@ -1174,8 +1294,8 @@ class GeneralSettings(Gtk.Grid):
album_cover_label=Gtk.Label(label=_("Album-view cover size:"))
album_cover_label.set_xalign(1)
track_cover_size=IntEntry(self.settings.get_int("track-cover"), 100, 1200)
album_cover_size=IntEntry(self.settings.get_int("album-cover"), 50, 600)
track_cover_size=IntEntry(self.settings.get_int("track-cover"), 100, 1200, 10)
album_cover_size=IntEntry(self.settings.get_int("album-cover"), 50, 600, 10)
icon_size_label=Gtk.Label(label=_("Button icon size (restart required):"))
icon_size_label.set_xalign(1)
@@ -1186,42 +1306,46 @@ class GeneralSettings(Gtk.Grid):
icon_size_combo.append_text(str(i))
icon_size_combo.set_active(sizes.index(self.settings.get_int("icon-size")))
show_stop=Gtk.CheckButton(label=_("Show stop button"))
show_stop.set_active(self.settings.get_boolean("show-stop"))
#Store
#(toggle, display-text, key)
self.store = Gtk.ListStore(bool, str, str)
show_genre_filter=Gtk.CheckButton(label=_("Show genre filter"))
show_genre_filter.set_active(self.settings.get_boolean("show-genre-filter"))
#TreeView
self.treeview = Gtk.TreeView(model=self.store)
self.treeview.set_search_column(-1)
self.treeview.set_property("activate-on-single-click", True)
self.treeview.set_headers_visible(False)
show_album_view_tooltips=Gtk.CheckButton(label=_("Show tooltips in album view"))
show_album_view_tooltips.set_active(self.settings.get_boolean("show-album-view-tooltips"))
#selection
self.selection = self.treeview.get_selection()
self.selection.set_mode(Gtk.SelectionMode.NONE)
sort_albums_by_year=Gtk.CheckButton(label=_("Sort albums by year"))
sort_albums_by_year.set_active(self.settings.get_boolean("sort-albums-by-year"))
#Column
renderer_text = Gtk.CellRendererText()
renderer_toggle = Gtk.CellRendererToggle()
show_all_artists=Gtk.CheckButton(label=_("Show all artists"))
show_all_artists.set_active(self.settings.get_boolean("show-all-artists"))
column_toggle=Gtk.TreeViewColumn("", renderer_toggle, active=0)
self.treeview.append_column(column_toggle)
send_notify=Gtk.CheckButton(label=_("Send notification on title change"))
send_notify.set_active(self.settings.get_boolean("send-notify"))
column_text=Gtk.TreeViewColumn("", renderer_text, text=1)
self.treeview.append_column(column_text)
stop_on_quit=Gtk.CheckButton(label=_("Stop playback on quit"))
stop_on_quit.set_active(self.settings.get_boolean("stop-on-quit"))
#fill store
settings_list=[(_("Use alternative layout"), "alt-layout"), (_("Show stop button"), "show-stop"), \
(_("Show genre filter"), "show-genre-filter"), (_("Show initials in artist view"), "show-initials"), \
(_("Show tooltips in album view"), "show-album-view-tooltips"), \
(_("Sort albums by year"), "sort-albums-by-year"), (_("Show all artists"), "show-all-artists"), \
(_("Send notification on title change"), "send-notify"), (_("Stop playback on quit"), "stop-on-quit"), \
(_("Play selected album after current title"), "add-album")]
add_album=Gtk.CheckButton(label=_("Play selected album after current title"))
add_album.set_active(self.settings.get_boolean("add-album"))
for data in settings_list:
self.store.append([self.settings.get_boolean(data[1]), data[0], data[1]])
#connect
track_cover_size.connect("value-changed", self.on_int_changed, "track-cover")
album_cover_size.connect("value-changed", self.on_int_changed, "album-cover")
icon_size_combo.connect("changed", self.on_icon_size_changed)
show_stop.connect("toggled", self.on_toggled, "show-stop")
show_genre_filter.connect("toggled", self.on_toggled, "show-genre-filter")
show_album_view_tooltips.connect("toggled", self.on_toggled, "show-album-view-tooltips")
sort_albums_by_year.connect("toggled", self.on_toggled, "sort-albums-by-year")
show_all_artists.connect("toggled", self.on_toggled, "show-all-artists")
send_notify.connect("toggled", self.on_toggled, "send-notify")
stop_on_quit.connect("toggled", self.on_toggled, "stop-on-quit")
add_album.connect("toggled", self.on_toggled, "add-album")
self.treeview.connect("row-activated", self.on_row_activated)
#packing
self.add(track_cover_label)
@@ -1230,25 +1354,79 @@ class GeneralSettings(Gtk.Grid):
self.attach_next_to(track_cover_size, track_cover_label, Gtk.PositionType.RIGHT, 1, 1)
self.attach_next_to(album_cover_size, album_cover_label, Gtk.PositionType.RIGHT, 1, 1)
self.attach_next_to(icon_size_combo, icon_size_label, Gtk.PositionType.RIGHT, 1, 1)
self.attach_next_to(show_stop, icon_size_label, Gtk.PositionType.BOTTOM, 2, 1)
self.attach_next_to(show_genre_filter, show_stop, Gtk.PositionType.BOTTOM, 2, 1)
self.attach_next_to(show_album_view_tooltips, show_genre_filter, Gtk.PositionType.BOTTOM, 2, 1)
self.attach_next_to(sort_albums_by_year, show_album_view_tooltips, Gtk.PositionType.BOTTOM, 2, 1)
self.attach_next_to(show_all_artists, sort_albums_by_year, Gtk.PositionType.BOTTOM, 2, 1)
self.attach_next_to(send_notify, show_all_artists, Gtk.PositionType.BOTTOM, 2, 1)
self.attach_next_to(add_album, send_notify, Gtk.PositionType.BOTTOM, 2, 1)
self.attach_next_to(stop_on_quit, add_album, Gtk.PositionType.BOTTOM, 2, 1)
self.attach_next_to(self.treeview, icon_size_label, Gtk.PositionType.BOTTOM, 2, 2)
def on_int_changed(self, widget, key):
self.settings.set_int(key, widget.get_int())
def on_toggled(self, widget, key):
self.settings.set_boolean(key, widget.get_active())
def on_icon_size_changed(self, box):
active_size=int(box.get_active_text())
self.settings.set_int("icon-size", active_size)
def on_row_activated(self, widget, path, view_column): #TODO
self.store[path][0] = not self.store[path][0]
self.settings.set_boolean(self.store[path][2], self.store[path][0])
class PlaylistSettings(Gtk.Box):
def __init__(self, settings):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=4)
self.set_property("border-width", 4)
#adding vars
self.settings = settings
#label
label=Gtk.Label(label=_("Drag & Drop to rearange"))
label.set_line_wrap(True)
#Store
#(toggle, header, index)
self.store = Gtk.ListStore(bool, str, int)
#TreeView
self.treeview = Gtk.TreeView(model=self.store)
self.treeview.set_search_column(-1)
self.treeview.set_reorderable(True)
#Column
renderer_text = Gtk.CellRendererText()
renderer_toggle = Gtk.CellRendererToggle()
column_toggle=Gtk.TreeViewColumn("", renderer_toggle, active=0)
self.treeview.append_column(column_toggle)
column_text=Gtk.TreeViewColumn(_("Column"), renderer_text, text=1)
self.treeview.append_column(column_text)
#fill store
self.headers=[_("No"), _("Disc"), _("Title"), _("Artist"), _("Album"), _("Length"), _("Year"), _("Genre")]
visibilities=self.settings.get_value("column-visibilities").unpack()
for index in self.settings.get_value("column-permutation"):
self.store.append([visibilities[index], self.headers[index], index])
#connect
self.store.connect("row-deleted", self.on_row_inserted)
renderer_toggle.connect("toggled", self.on_cell_toggled)
#scroll
scroll=Gtk.ScrolledWindow()
scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scroll.add(self.treeview)
self.pack_start(scroll, True, True, 0)
self.pack_start(label, False, False, 0)
def on_cell_toggled(self, widget, path):
self.store[path][0] = not self.store[path][0]
self.settings.array_modify('ab', "column-visibilities", self.store[path][2], self.store[path][0])
def on_row_inserted(self, *args):
permutation=[]
for row in self.store:
permutation.append(row[2])
self.settings.set_value("column-permutation", GLib.Variant("ai", permutation))
class SettingsDialog(Gtk.Dialog):
def __init__(self, parent, settings):
Gtk.Dialog.__init__(self, title=_("Settings"), transient_for=parent)
@@ -1261,11 +1439,13 @@ class SettingsDialog(Gtk.Dialog):
#widgets
general=GeneralSettings(self.settings)
profiles=ProfileSettings(parent, self.settings)
playlist=PlaylistSettings(self.settings)
#packing
tabs = Gtk.Notebook()
tabs.append_page(general, Gtk.Label(label=_("General")))
tabs.append_page(profiles, Gtk.Label(label=_("Profiles")))
tabs.append_page(playlist, Gtk.Label(label=_("Playlist")))
self.vbox.pack_start(tabs, True, True, 0) #vbox default widget of dialogs
self.vbox.set_spacing(6)
@@ -1407,17 +1587,23 @@ class SeekBar(Gtk.Box):
pos=(duration*factor)
self.client.seekcur(pos)
def seek_forward(self):
self.client.seekcur("+"+self.seek_time)
def seek_backward(self):
self.client.seekcur("-"+self.seek_time)
def on_elapsed_button_press_event(self, widget, event):
if event.button == 1:
self.client.seekcur("-"+self.seek_time)
self.seek_backward()
elif event.button == 3:
self.client.seekcur("+"+self.seek_time)
self.seek_forward()
def on_rest_button_press_event(self, widget, event):
if event.button == 1:
self.client.seekcur("+"+self.seek_time)
self.seek_forward()
elif event.button == 3:
self.client.seekcur("-"+self.seek_time)
self.seek_backward()
def update(self):
try:
@@ -1570,6 +1756,7 @@ class AudioType(Gtk.EventBox):
#TreeView
self.treeview = Gtk.TreeView(model=self.store)
self.treeview.set_can_focus(False)
self.treeview.set_search_column(-1)
sel = self.treeview.get_selection()
sel.set_mode(Gtk.SelectionMode.NONE)
@@ -1674,6 +1861,7 @@ class ServerStats(Gtk.Dialog):
#TreeView
self.treeview = Gtk.TreeView(model=self.store)
self.treeview.set_can_focus(False)
self.treeview.set_search_column(-1)
sel = self.treeview.get_selection()
sel.set_mode(Gtk.SelectionMode.NONE)
@@ -1909,6 +2097,7 @@ class LyricsWindow(Gtk.Window):
class MainWindow(Gtk.ApplicationWindow):
def __init__(self, app, client, settings, emitter):
Gtk.ApplicationWindow.__init__(self, title=("mpdevil"), application=app)
Notify.init("mpdevil")
self.set_icon_name("mpdevil")
self.settings = settings
self.set_default_size(self.settings.get_int("width"), self.settings.get_int("height"))
@@ -1918,6 +2107,7 @@ class MainWindow(Gtk.ApplicationWindow):
self.client=client
self.emitter=emitter
self.icon_size=self.settings.get_gtk_icon_size("icon-size")
self.song_file=None
#actions
save_action = Gio.SimpleAction.new("save", None)
@@ -1955,7 +2145,7 @@ class MainWindow(Gtk.ApplicationWindow):
#menu
menu = Gio.Menu()
menu.append(_("Save window size"), "win.save")
menu.append(_("Save window layout"), "win.save")
menu.append(_("Settings"), "win.settings")
menu.append(_("Update database"), "win.update")
menu.append(_("Server stats"), "win.stats")
@@ -2010,12 +2200,16 @@ class MainWindow(Gtk.ApplicationWindow):
if status["songid"] == None:
self.set_title("mpdevil")
else:
song=self.client.playlistid(status["songid"])[0]
self.set_title(song["artist"]+" - "+song["title"]+" - "+song["album"])
if not self.is_active() and self.settings.get_boolean("send-notify") and status["state"] == "play":
notify=Gio.Notification.new(title=song["title"])
notify.set_body(song["artist"]+"\n"+song["album"])
self.app.send_notification(None, notify)
song=self.client.currentsong()
if song["file"] != self.song_file:
self.set_title(song["artist"]+" - "+song["title"]+" - "+song["album"])
if self.settings.get_boolean("send-notify"):
if not self.is_active() and status["state"] == "play":
notify=Notify.Notification.new(song["title"], song["artist"]+"\n"+song["album"])
pixbuf=Cover(client=self.client, lib_path=self.settings.get_value("paths")[self.settings.get_int("active-profile")], song_file=song["file"]).get_pixbuf(400)
notify.set_image_from_pixbuf(pixbuf)
notify.show()
self.song_file=song["file"]
except:
self.set_title("mpdevil")
@@ -2078,6 +2272,10 @@ class MainWindow(Gtk.ApplicationWindow):
self.control.prev_button.emit("clicked")
elif event.keyval == 65307: #esc
self.go_home_button.emit("clicked")
elif event.keyval == 65450: #*
self.progress.seek_forward()
elif event.keyval == 65455: #/
self.progress.seek_backward()
def on_save(self, action, param):
size=self.get_size()

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.6.0])
AC_INIT([mpdevil], [0.7.0])
AC_CONFIG_SRCDIR([bin/mpdevil.py])
AM_INIT_AUTOMAKE
AC_CONFIG_MACRO_DIR([m4])

View File

@@ -46,6 +46,11 @@
<summary>Show genre filter</summary>
<description></description>
</key>
<key type="b" name="show-initials">
<default>true</default>
<summary>Show initial letters in artist-view</summary>
<description></description>
</key>
<key type="b" name="show-album-view-tooltips">
<default>true</default>
<summary>Show tooltips in album-view</summary>
@@ -76,6 +81,26 @@
<summary>Show all artists instead of albumartists</summary>
<description></description>
</key>
<key type="ai" name="column-permutation">
<default>[0, 1, 2, 3, 4, 5, 6, 7]</default>
<summary>Column order in playlist</summary>
<description></description>
</key>
<key type="ab" name="column-visibilities">
<default>[true, false, true, true, false, true, false, false]</default>
<summary>Visibility of columns in playlist</summary>
<description></description>
</key>
<key type="ai" name="column-sizes">
<default>[0, 0, 0, 0, 0, 0, 0, 0]</default>
<summary>Sizes of columns in playlist</summary>
<description></description>
</key>
<key type="b" name="alt-layout">
<default>false</default>
<summary>Use alternative layout</summary>
<description></description>
</key>
<key type="i" name="active-profile">
<default>0</default>
<summary>Active profile</summary>

164
po/de.po
View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-02-28 23:22+0100\n"
"PO-Revision-Date: 2020-02-28 23:23+0100\n"
"POT-Creation-Date: 2020-03-12 17:05+0100\n"
"PO-Revision-Date: 2020-03-12 17:06+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: de\n"
@@ -18,164 +18,200 @@ msgstr ""
"X-Generator: Poedit 2.2.4\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: mpdevil.py:290 mpdevil.py:700 mpdevil.py:1735
#: mpdevil.py:293 mpdevil.py:798 mpdevil.py:1402 mpdevil.py:1930
msgid "No"
msgstr "Nr."
#: mpdevil.py:295 mpdevil.py:705 mpdevil.py:1741
#: mpdevil.py:298 mpdevil.py:804 mpdevil.py:1402 mpdevil.py:1936
msgid "Title"
msgstr "Titel"
#: mpdevil.py:300 mpdevil.py:454 mpdevil.py:710 mpdevil.py:1747
#: mpdevil.py:303 mpdevil.py:463 mpdevil.py:807 mpdevil.py:1402 mpdevil.py:1942
msgid "Artist"
msgstr "Interpret"
#: mpdevil.py:305 mpdevil.py:715 mpdevil.py:1759
#: mpdevil.py:308 mpdevil.py:813 mpdevil.py:1402 mpdevil.py:1954
msgid "Length"
msgstr "Länge"
#: mpdevil.py:345 mpdevil.py:886 mpdevil.py:1796
#: mpdevil.py:348 mpdevil.py:968 mpdevil.py:1994
msgid "Unknown Title"
msgstr "Unbekannter Titel"
#: mpdevil.py:349 mpdevil.py:894 mpdevil.py:1804
#: mpdevil.py:352 mpdevil.py:980 mpdevil.py:2002
msgid "Unknown Artist"
msgstr "Unbekannter Interpret"
#: mpdevil.py:390
#: mpdevil.py:393
msgid "all genres"
msgstr "Alle Genres"
#: mpdevil.py:452
#: mpdevil.py:461
msgid "Album Artist"
msgstr "Albuminterpret"
#: mpdevil.py:533 mpdevil.py:789
#: mpdevil.py:553 mpdevil.py:899
#, python-format
msgid "%(total_tracks)i titles (%(total_length)s)"
msgstr "%(total_tracks)i Titel (%(total_length)s)"
#: mpdevil.py:898 mpdevil.py:1808
#: mpdevil.py:801 mpdevil.py:1402
msgid "Disc"
msgstr "CD"
#: mpdevil.py:810 mpdevil.py:1402 mpdevil.py:1948
msgid "Album"
msgstr "Album"
#: mpdevil.py:816 mpdevil.py:1402
msgid "Year"
msgstr "Jahr"
#: mpdevil.py:819 mpdevil.py:1402
msgid "Genre"
msgstr "Genre"
#: mpdevil.py:984 mpdevil.py:2006
msgid "Unknown Album"
msgstr "Unbekanntes Album"
#: mpdevil.py:1036
#: mpdevil.py:1154
msgid "Select"
msgstr "Auswählen"
#: mpdevil.py:1038
#: mpdevil.py:1156
msgid "Profile:"
msgstr "Profil:"
#: mpdevil.py:1040
#: mpdevil.py:1158
msgid "Name:"
msgstr "Name:"
#: mpdevil.py:1042
#: mpdevil.py:1160
msgid "Host:"
msgstr "Host:"
#: mpdevil.py:1044
#: mpdevil.py:1162
msgid "Port:"
msgstr "Port:"
#: mpdevil.py:1046
#: mpdevil.py:1164
msgid "Password:"
msgstr "Passwort:"
#: mpdevil.py:1048
#: mpdevil.py:1166
msgid "Music lib:"
msgstr "Musikverzeichnis:"
#: mpdevil.py:1134
#: mpdevil.py:1252
msgid "Choose directory"
msgstr "Verzeichnis Wählen"
#: mpdevil.py:1174
#: mpdevil.py:1292
msgid "Main cover size:"
msgstr "Größe des Haupt-Covers:"
#: mpdevil.py:1176
#: mpdevil.py:1294
msgid "Album-view cover size:"
msgstr "Covergröße in Albumansicht:"
#: mpdevil.py:1182
#: mpdevil.py:1300
msgid "Button icon size (restart required):"
msgstr "Symbolgröße der Knöpfe (Neustart erforderlich):"
#: mpdevil.py:1191
#: mpdevil.py:1334
msgid "Use alternative layout"
msgstr "Benutze alternatives Layout"
#: mpdevil.py:1334
msgid "Show stop button"
msgstr "Zeige Stopp-Knopf"
#: mpdevil.py:1194
#: mpdevil.py:1335
msgid "Show genre filter"
msgstr "Zeige Genre Filter"
#: mpdevil.py:1197
#: mpdevil.py:1335
msgid "Show initials in artist view"
msgstr "Zeige Anfangsbuchstaben in Interpretenspalte"
#: mpdevil.py:1336
msgid "Show tooltips in album view"
msgstr "Zeige Tooltips in Albumansicht"
#: mpdevil.py:1200
#: mpdevil.py:1337
msgid "Sort albums by year"
msgstr "Sortiere Alben nach Erscheinungsjahr"
#: mpdevil.py:1203
#: mpdevil.py:1337
msgid "Show all artists"
msgstr "Zeige alle Interpreten"
#: mpdevil.py:1206
#: mpdevil.py:1338
msgid "Send notification on title change"
msgstr "Sende Benachrichtigung bei Titelwechsel"
#: mpdevil.py:1209
#: mpdevil.py:1338
msgid "Stop playback on quit"
msgstr "Wiedergabe beim Beenden stoppen"
#: mpdevil.py:1212
#: mpdevil.py:1339
msgid "Play selected album after current title"
msgstr "Ausgewähltes Album hinter aktuellem Titel einreihen"
#: mpdevil.py:1256 mpdevil.py:1948
#: mpdevil.py:1379
msgid "Drag & Drop to rearange"
msgstr "Mit Drag & Drop sortieren"
#: mpdevil.py:1398
msgid "Column"
msgstr "Spalte"
#: mpdevil.py:1432 mpdevil.py:2149
msgid "Settings"
msgstr "Einstellungen"
#: mpdevil.py:1269
#: mpdevil.py:1446
msgid "General"
msgstr "Allgemein"
#: mpdevil.py:1270
#: mpdevil.py:1447
msgid "Profiles"
msgstr "Profile"
#: mpdevil.py:1454
#: mpdevil.py:1448
msgid "Playlist"
msgstr "Wiedergabeliste"
#: mpdevil.py:1642
msgid "Random mode"
msgstr "Zufallsmodus"
#: mpdevil.py:1457
#: mpdevil.py:1645
msgid "Repeat mode"
msgstr "Dauerschleife"
#: mpdevil.py:1460
#: mpdevil.py:1648
msgid "Single mode"
msgstr "Einzelstückmodus"
#: mpdevil.py:1463
#: mpdevil.py:1651
msgid "Consume mode"
msgstr "Playliste verbrauchen"
#: mpdevil.py:1553
#: mpdevil.py:1741
msgid "Click to show additional information"
msgstr "Klicken für weitere Informationen"
#: mpdevil.py:1578
#: mpdevil.py:1767
msgid "MPD-Tag"
msgstr "MPD-Tag"
#: mpdevil.py:1582 mpdevil.py:1685
#: mpdevil.py:1771 mpdevil.py:1875
msgid "Value"
msgstr "Wert"
#: mpdevil.py:1604
#: mpdevil.py:1793
#, python-format
msgid ""
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s "
@@ -184,80 +220,76 @@ msgstr ""
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s "
"Kanäle, %(file_type)s"
#: mpdevil.py:1664
#: mpdevil.py:1853
msgid "Stats"
msgstr "Statistik"
#: mpdevil.py:1682
#: mpdevil.py:1872
msgid "Tag"
msgstr "Tag"
#: mpdevil.py:1702
#: mpdevil.py:1892
msgid "Search"
msgstr "Suche"
#: mpdevil.py:1753
msgid "Album"
msgstr "Album"
#: mpdevil.py:1815
#: mpdevil.py:2013
#, python-format
msgid "Hits: %i"
msgstr "Treffer: %i"
#: mpdevil.py:1819
#: mpdevil.py:2017
msgid "Lyrics"
msgstr "Liedtext"
#: mpdevil.py:1852
#: mpdevil.py:2051
msgid "searching..."
msgstr "suche..."
#: mpdevil.py:1856
#: mpdevil.py:2055
msgid "not found"
msgstr "nicht gefunden"
#: mpdevil.py:1931
#: mpdevil.py:2132
msgid "Select profile"
msgstr "Profil auswählen"
#: mpdevil.py:1936
#: mpdevil.py:2137
msgid "Return to album of current title"
msgstr "Zu Album des aktuellen Titels zurückkehren"
#: mpdevil.py:1939
#: mpdevil.py:2140
msgid "Title search"
msgstr "Titelsuche"
#: mpdevil.py:1942
#: mpdevil.py:2143
msgid "Show lyrics"
msgstr "Zeige Liedtext"
#: mpdevil.py:1947
msgid "Save window size"
msgstr "Fenstergröße speichern"
#: mpdevil.py:2148
msgid "Save window layout"
msgstr "Fensterlayout speichern"
#: mpdevil.py:1949
#: mpdevil.py:2150
msgid "Update database"
msgstr "Datenbank aktualisieren"
#: mpdevil.py:1950
#: mpdevil.py:2151
msgid "Server stats"
msgstr "Serverstatistik"
#: mpdevil.py:1951
#: mpdevil.py:2152
msgid "About"
msgstr "Über"
#: mpdevil.py:1952
#: mpdevil.py:2153
msgid "Quit"
msgstr "Beenden"
#: mpdevil.py:1958
#: mpdevil.py:2159
msgid "Main menu"
msgstr "Hauptmenu"
#: mpdevil.py:2126
#: mpdevil.py:2342
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-02-28 23:22+0100\n"
"POT-Creation-Date: 2020-03-12 17:05+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,243 +17,275 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: mpdevil.py:290 mpdevil.py:700 mpdevil.py:1735
#: mpdevil.py:293 mpdevil.py:798 mpdevil.py:1402 mpdevil.py:1930
msgid "No"
msgstr ""
#: mpdevil.py:295 mpdevil.py:705 mpdevil.py:1741
#: mpdevil.py:298 mpdevil.py:804 mpdevil.py:1402 mpdevil.py:1936
msgid "Title"
msgstr ""
#: mpdevil.py:300 mpdevil.py:454 mpdevil.py:710 mpdevil.py:1747
#: mpdevil.py:303 mpdevil.py:463 mpdevil.py:807 mpdevil.py:1402 mpdevil.py:1942
msgid "Artist"
msgstr ""
#: mpdevil.py:305 mpdevil.py:715 mpdevil.py:1759
#: mpdevil.py:308 mpdevil.py:813 mpdevil.py:1402 mpdevil.py:1954
msgid "Length"
msgstr ""
#: mpdevil.py:345 mpdevil.py:886 mpdevil.py:1796
#: mpdevil.py:348 mpdevil.py:968 mpdevil.py:1994
msgid "Unknown Title"
msgstr ""
#: mpdevil.py:349 mpdevil.py:894 mpdevil.py:1804
#: mpdevil.py:352 mpdevil.py:980 mpdevil.py:2002
msgid "Unknown Artist"
msgstr ""
#: mpdevil.py:390
#: mpdevil.py:393
msgid "all genres"
msgstr ""
#: mpdevil.py:452
#: mpdevil.py:461
msgid "Album Artist"
msgstr ""
#: mpdevil.py:533 mpdevil.py:789
#: mpdevil.py:553 mpdevil.py:899
#, python-format
msgid "%(total_tracks)i titles (%(total_length)s)"
msgstr ""
#: mpdevil.py:898 mpdevil.py:1808
#: mpdevil.py:801 mpdevil.py:1402
msgid "Disc"
msgstr ""
#: mpdevil.py:810 mpdevil.py:1402 mpdevil.py:1948
msgid "Album"
msgstr ""
#: mpdevil.py:816 mpdevil.py:1402
msgid "Year"
msgstr ""
#: mpdevil.py:819 mpdevil.py:1402
msgid "Genre"
msgstr ""
#: mpdevil.py:984 mpdevil.py:2006
msgid "Unknown Album"
msgstr ""
#: mpdevil.py:1036
#: mpdevil.py:1154
msgid "Select"
msgstr ""
#: mpdevil.py:1038
#: mpdevil.py:1156
msgid "Profile:"
msgstr ""
#: mpdevil.py:1040
#: mpdevil.py:1158
msgid "Name:"
msgstr ""
#: mpdevil.py:1042
#: mpdevil.py:1160
msgid "Host:"
msgstr ""
#: mpdevil.py:1044
#: mpdevil.py:1162
msgid "Port:"
msgstr ""
#: mpdevil.py:1046
#: mpdevil.py:1164
msgid "Password:"
msgstr ""
#: mpdevil.py:1048
#: mpdevil.py:1166
msgid "Music lib:"
msgstr ""
#: mpdevil.py:1134
#: mpdevil.py:1252
msgid "Choose directory"
msgstr ""
#: mpdevil.py:1174
#: mpdevil.py:1292
msgid "Main cover size:"
msgstr ""
#: mpdevil.py:1176
#: mpdevil.py:1294
msgid "Album-view cover size:"
msgstr ""
#: mpdevil.py:1182
#: mpdevil.py:1300
msgid "Button icon size (restart required):"
msgstr ""
#: mpdevil.py:1191
#: mpdevil.py:1334
msgid "Use alternative layout"
msgstr ""
#: mpdevil.py:1334
msgid "Show stop button"
msgstr ""
#: mpdevil.py:1194
#: mpdevil.py:1335
msgid "Show genre filter"
msgstr ""
#: mpdevil.py:1197
#: mpdevil.py:1335
msgid "Show initials in artist view"
msgstr ""
#: mpdevil.py:1336
msgid "Show tooltips in album view"
msgstr ""
#: mpdevil.py:1200
#: mpdevil.py:1337
msgid "Sort albums by year"
msgstr ""
#: mpdevil.py:1203
#: mpdevil.py:1337
msgid "Show all artists"
msgstr ""
#: mpdevil.py:1206
#: mpdevil.py:1338
msgid "Send notification on title change"
msgstr ""
#: mpdevil.py:1209
#: mpdevil.py:1338
msgid "Stop playback on quit"
msgstr ""
#: mpdevil.py:1212
#: mpdevil.py:1339
msgid "Play selected album after current title"
msgstr ""
#: mpdevil.py:1256 mpdevil.py:1948
#: mpdevil.py:1379
msgid "Drag & Drop to rearange"
msgstr ""
#: mpdevil.py:1398
msgid "Column"
msgstr ""
#: mpdevil.py:1432 mpdevil.py:2149
msgid "Settings"
msgstr ""
#: mpdevil.py:1269
#: mpdevil.py:1446
msgid "General"
msgstr ""
#: mpdevil.py:1270
#: mpdevil.py:1447
msgid "Profiles"
msgstr ""
#: mpdevil.py:1454
#: mpdevil.py:1448
msgid "Playlist"
msgstr ""
#: mpdevil.py:1642
msgid "Random mode"
msgstr ""
#: mpdevil.py:1457
#: mpdevil.py:1645
msgid "Repeat mode"
msgstr ""
#: mpdevil.py:1460
#: mpdevil.py:1648
msgid "Single mode"
msgstr ""
#: mpdevil.py:1463
#: mpdevil.py:1651
msgid "Consume mode"
msgstr ""
#: mpdevil.py:1553
#: mpdevil.py:1741
msgid "Click to show additional information"
msgstr ""
#: mpdevil.py:1578
#: mpdevil.py:1767
msgid "MPD-Tag"
msgstr ""
#: mpdevil.py:1582 mpdevil.py:1685
#: mpdevil.py:1771 mpdevil.py:1875
msgid "Value"
msgstr ""
#: mpdevil.py:1604
#: mpdevil.py:1793
#, python-format
msgid ""
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s "
"channels, %(file_type)s"
msgstr ""
#: mpdevil.py:1664
#: mpdevil.py:1853
msgid "Stats"
msgstr ""
#: mpdevil.py:1682
#: mpdevil.py:1872
msgid "Tag"
msgstr ""
#: mpdevil.py:1702
#: mpdevil.py:1892
msgid "Search"
msgstr ""
#: mpdevil.py:1753
msgid "Album"
msgstr ""
#: mpdevil.py:1815
#: mpdevil.py:2013
#, python-format
msgid "Hits: %i"
msgstr ""
#: mpdevil.py:1819
#: mpdevil.py:2017
msgid "Lyrics"
msgstr ""
#: mpdevil.py:1852
#: mpdevil.py:2051
msgid "searching..."
msgstr ""
#: mpdevil.py:1856
#: mpdevil.py:2055
msgid "not found"
msgstr ""
#: mpdevil.py:1931
#: mpdevil.py:2132
msgid "Select profile"
msgstr ""
#: mpdevil.py:1936
#: mpdevil.py:2137
msgid "Return to album of current title"
msgstr ""
#: mpdevil.py:1939
#: mpdevil.py:2140
msgid "Title search"
msgstr ""
#: mpdevil.py:1942
#: mpdevil.py:2143
msgid "Show lyrics"
msgstr ""
#: mpdevil.py:1947
msgid "Save window size"
#: mpdevil.py:2148
msgid "Save window layout"
msgstr ""
#: mpdevil.py:1949
#: mpdevil.py:2150
msgid "Update database"
msgstr ""
#: mpdevil.py:1950
#: mpdevil.py:2151
msgid "Server stats"
msgstr ""
#: mpdevil.py:1951
#: mpdevil.py:2152
msgid "About"
msgstr ""
#: mpdevil.py:1952
#: mpdevil.py:2153
msgid "Quit"
msgstr ""
#: mpdevil.py:1958
#: mpdevil.py:2159
msgid "Main menu"
msgstr ""
#: mpdevil.py:2126
#: mpdevil.py:2342
msgid "A small MPD client written in python"
msgstr ""

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 KiB