mirror of
https://github.com/SoongNoonien/mpdevil.git
synced 2023-08-10 21:12:44 +03:00
Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7037628af9 | ||
![]() |
26ee51fb18 | ||
![]() |
9625bd9cf3 | ||
![]() |
75d8ff21c6 | ||
![]() |
36b023f04c | ||
![]() |
177bd27aa5 | ||
![]() |
e07cce7ea6 | ||
![]() |
0332fe75b7 | ||
![]() |
e62f4824c2 | ||
![]() |
b9b1ba989a | ||
![]() |
f1831a5569 | ||
![]() |
b904907f6f | ||
![]() |
54673a9840 | ||
![]() |
89ffc03cb2 | ||
![]() |
d04c84e5d0 | ||
![]() |
6990d59f72 | ||
![]() |
421f685b58 | ||
![]() |
6238df4d21 | ||
![]() |
b8d1f9aafc | ||
![]() |
3414212173 | ||
![]() |
ab7c9c6bd6 | ||
![]() |
4c7f953c98 | ||
![]() |
8ba986bb4d | ||
![]() |
0230544df4 | ||
![]() |
0b08bd80bf | ||
![]() |
b786b55644 | ||
![]() |
6f608d923b |
33
README.md
Normal file
33
README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
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 especially on the AlbumArtist tag.
|
||||
|
||||

|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
1. playing songs without doubleclicking
|
||||
2. displaying covers
|
||||
3. fetching lyrics form the web (based on PyLyrics 1.1.0)
|
||||
4. searching songs in your music library
|
||||
5. removing single tracks form playlist by hovering and pressing del
|
||||
6. appending albums by middleclick
|
||||
7. query albums by rightclick
|
||||
8. sending notifications on title change
|
||||
9. managing multiple mpd servers
|
||||
|
||||
TODO
|
||||
----
|
||||
1. MPRIS interface
|
||||
2. connecting to mpd servers with password
|
||||
|
||||
Building and installation
|
||||
-------------------------
|
||||
|
||||
To build from source, use::
|
||||
|
||||
./autogen.sh
|
||||
make
|
||||
make install
|
||||
|
30
README.rst
30
README.rst
@@ -1,30 +0,0 @@
|
||||
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.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
-playing songs without doubleclicking
|
||||
|
||||
-displaying covers
|
||||
|
||||
-fetching lyrics form the web (based on PyLyrics 1.1.0)
|
||||
|
||||
-searching songs in your music library
|
||||
|
||||
-removing single tracks form playlist by hovering and pressing del
|
||||
|
||||
-sending notifications on title change
|
||||
|
||||
-managing multiple mpd servers
|
||||
|
||||
Building and installation
|
||||
-------------------------
|
||||
|
||||
To build from source, use::
|
||||
|
||||
./autogen.sh
|
||||
make
|
||||
make install
|
||||
|
404
bin/mpdevil.py
404
bin/mpdevil.py
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# mpdevil - MPD Client.
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
import gi #python-gobject dev-python/pygobject:3[${PYTHON_USEDEP}]
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gio, Gdk, GdkPixbuf, GObject, GLib
|
||||
from gi.repository import Gtk, Gio, Gdk, GdkPixbuf, Pango, GObject, GLib
|
||||
from mpd import MPDClient
|
||||
import requests #dev-python/requests
|
||||
from bs4 import BeautifulSoup, Comment #, NavigableString #dev-python/beautifulsoup
|
||||
@@ -79,8 +79,16 @@ class Cover(object):
|
||||
return GdkPixbuf.Pixbuf.new_from_file_at_size(self.path, size, size)
|
||||
|
||||
class Client(MPDClient):
|
||||
def __init__(self):
|
||||
def __init__(self, settings):
|
||||
MPDClient.__init__(self)
|
||||
self.settings = settings
|
||||
|
||||
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])
|
||||
except:
|
||||
pass
|
||||
|
||||
def connected(self):
|
||||
try:
|
||||
@@ -89,6 +97,136 @@ class Client(MPDClient):
|
||||
except:
|
||||
return False
|
||||
|
||||
class Settings(Gio.Settings):
|
||||
BASE_KEY = "org.mpdevil"
|
||||
def __init__(self):
|
||||
super().__init__(schema=self.BASE_KEY)
|
||||
|
||||
def array_append(self, vtype, key, value): #append to Gio.Settings (self.settings) array
|
||||
array=self.get_value(key).unpack()
|
||||
array.append(value)
|
||||
self.set_value(key, GLib.Variant(vtype, array))
|
||||
|
||||
def array_delete(self, vtype, key, pos): #delete entry of Gio.Settings (self.settings) array
|
||||
array=self.get_value(key).unpack()
|
||||
array.pop(pos)
|
||||
self.set_value(key, GLib.Variant(vtype, array))
|
||||
|
||||
def array_modify(self, vtype, key, pos, value): #modify entry of Gio.Settings (self.settings) array
|
||||
array=self.get_value(key).unpack()
|
||||
array[pos]=value
|
||||
self.set_value(key, GLib.Variant(vtype, array))
|
||||
|
||||
def get_gtk_icon_size(self, key):
|
||||
icon_size=self.get_int(key)
|
||||
if icon_size == 16:
|
||||
return Gtk.IconSize.BUTTON
|
||||
elif icon_size == 32:
|
||||
return Gtk.IconSize.DND
|
||||
elif icon_size == 48:
|
||||
return Gtk.IconSize.DIALOG
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
class AlbumDialog(Gtk.Dialog):
|
||||
def __init__(self, parent, client, album, artist, year):
|
||||
Gtk.Dialog.__init__(self, title=(artist+" - "+album+" ("+year+")"), transient_for=parent)
|
||||
self.add_buttons(Gtk.STOCK_ADD, Gtk.ResponseType.ACCEPT, Gtk.STOCK_MEDIA_PLAY, Gtk.ResponseType.YES, Gtk.STOCK_OPEN, Gtk.ResponseType.OK, Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE)
|
||||
self.set_default_size(800, 600)
|
||||
|
||||
#adding vars
|
||||
self.client=client
|
||||
|
||||
#scroll
|
||||
scroll=Gtk.ScrolledWindow()
|
||||
scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
|
||||
#Store
|
||||
#(track, title, artist, duration, file)
|
||||
self.store = Gtk.ListStore(str, str, str, str, str)
|
||||
|
||||
#TreeView
|
||||
self.treeview = Gtk.TreeView(model=self.store)
|
||||
self.treeview.set_search_column(-1)
|
||||
self.treeview.columns_autosize()
|
||||
|
||||
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", False)
|
||||
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", False)
|
||||
self.treeview.append_column(self.column_artist)
|
||||
|
||||
self.column_time = Gtk.TreeViewColumn(_("Length"), renderer_text, text=3)
|
||||
self.column_time.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
self.column_time.set_property("resizable", False)
|
||||
self.treeview.append_column(self.column_time)
|
||||
|
||||
self.populate_treeview(album, artist, year)
|
||||
|
||||
#connect
|
||||
self.title_activated=self.treeview.connect("row-activated", self.on_row_activated)
|
||||
|
||||
#packing
|
||||
scroll.add(self.treeview)
|
||||
self.vbox.pack_start(scroll, True, True, 0) #vbox default widget of dialogs
|
||||
self.vbox.set_spacing(6)
|
||||
self.show_all()
|
||||
|
||||
#selection workaround
|
||||
self.selection.unselect_all()
|
||||
self.title_change=self.selection.connect("changed", self.on_selection_change)
|
||||
|
||||
def on_row_activated(self, widget, path, view_column):
|
||||
treeiter=self.store.get_iter(path)
|
||||
selected_title=self.store.get_value(treeiter, 4)
|
||||
self.client.clear()
|
||||
self.client.add(selected_title)
|
||||
self.client.play()
|
||||
|
||||
def on_selection_change(self, widget):
|
||||
treeiter=widget.get_selected()[1]
|
||||
if not treeiter == None:
|
||||
selected_title=self.store.get_value(treeiter, 4)
|
||||
self.client.add(selected_title)
|
||||
|
||||
def populate_treeview(self, album, artist, year):
|
||||
songs=self.client.find("album", album, "date", year, "albumartist", artist)
|
||||
if not songs == []:
|
||||
for song in songs:
|
||||
try:
|
||||
title=song["title"]
|
||||
except:
|
||||
title=_("Unknown Title")
|
||||
try:
|
||||
artist=song["artist"]
|
||||
except:
|
||||
artist=_("Unknown Artist")
|
||||
try:
|
||||
track=song["track"].zfill(2)
|
||||
except:
|
||||
track="00"
|
||||
try:
|
||||
dura=float(song["duration"])
|
||||
except:
|
||||
dura=0.0
|
||||
duration=str(datetime.timedelta(seconds=int(dura)))
|
||||
self.store.append([track, title, artist, duration, song["file"]] )
|
||||
|
||||
class ArtistView(Gtk.ScrolledWindow):
|
||||
def __init__(self, client):
|
||||
Gtk.ScrolledWindow.__init__(self)
|
||||
@@ -113,7 +251,7 @@ class ArtistView(Gtk.ScrolledWindow):
|
||||
|
||||
#Old Name Column
|
||||
renderer_text = Gtk.CellRendererText()
|
||||
self.column_name = Gtk.TreeViewColumn(_("Artist"), renderer_text, text=0)
|
||||
self.column_name = Gtk.TreeViewColumn(_("Album Artist"), renderer_text, text=0)
|
||||
self.column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
self.column_name.set_property("resizable", True)
|
||||
self.column_name.set_sort_column_id(0)
|
||||
@@ -161,28 +299,19 @@ class AlbumView(Gtk.ScrolledWindow):
|
||||
|
||||
self.add(self.iconview)
|
||||
|
||||
def gen_title_list(self, album, artist, year): #needed for tooltips
|
||||
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)
|
||||
title_list=""
|
||||
for song in songs:
|
||||
try:
|
||||
title=song["title"]
|
||||
dura=float(song["duration"])
|
||||
except:
|
||||
title=_("Unknown Title")
|
||||
try:
|
||||
track=song["track"].zfill(2)
|
||||
except:
|
||||
track="00"
|
||||
length=length+float(song["duration"])
|
||||
duration=str(datetime.timedelta(seconds=int(float(song["duration"]))))
|
||||
title_list=title_list+"\n"+(track+" - "+title+" ("+duration+")")
|
||||
if not year == "":
|
||||
year=" ("+year+")"
|
||||
dura=0.0
|
||||
length=length+dura
|
||||
length_human_readable=str(datetime.timedelta(seconds=int(length)))
|
||||
title_list=(_("%(album)s%(year)s (tracks: %(total_tracks)i) (%(total_length)s):") % {"album": album, "year": year, "total_tracks": len(songs), "total_length": length_human_readable})+title_list
|
||||
return title_list.replace("&", "") #& must not be in tooltips
|
||||
tooltip=(_("%(total_tracks)i titles (%(total_length)s)") % {"total_tracks": len(songs), "total_length": length_human_readable})
|
||||
return tooltip
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -202,9 +331,9 @@ 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_title_list(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"]])
|
||||
else:
|
||||
self.store.append([img, album["album"]+" ("+album["year"]+")", self.gen_title_list(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"]])
|
||||
while Gtk.events_pending():
|
||||
Gtk.main_iteration_do(True)
|
||||
|
||||
@@ -227,7 +356,6 @@ class TrackView(Gtk.Box):
|
||||
#TreeView
|
||||
self.treeview = Gtk.TreeView(model=self.store)
|
||||
self.treeview.set_search_column(-1)
|
||||
self.treeview.set_tooltip_column(5)
|
||||
self.treeview.columns_autosize()
|
||||
|
||||
#selection
|
||||
@@ -269,6 +397,16 @@ class TrackView(Gtk.Box):
|
||||
#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.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)
|
||||
@@ -283,7 +421,7 @@ class TrackView(Gtk.Box):
|
||||
#packing
|
||||
self.pack_start(self.cover, False, False, 0)
|
||||
self.pack_start(scroll, True, True, 0)
|
||||
self.pack_end(audio, False, False, 0)
|
||||
self.pack_end(status_bar, False, False, 0)
|
||||
|
||||
def update_cover(self):
|
||||
try:
|
||||
@@ -302,14 +440,11 @@ class TrackView(Gtk.Box):
|
||||
for song in songs:
|
||||
self.client.add(song["file"])
|
||||
else:
|
||||
if self.settings.get_boolean("add-album") and not force:
|
||||
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()
|
||||
try:
|
||||
self.client.moveid(status["songid"], 0) #bad song index possible
|
||||
self.client.moveid(status["songid"], 0)
|
||||
self.song_to_delete=self.client.playlistinfo()[0]["file"]
|
||||
except:
|
||||
pass
|
||||
self.selection.handler_unblock(self.title_change)
|
||||
try:
|
||||
self.client.delete((1,)) # delete all songs, but the first. #bad song index possible
|
||||
@@ -330,15 +465,17 @@ class TrackView(Gtk.Box):
|
||||
self.client.clear()
|
||||
for song in songs:
|
||||
self.client.add(song["file"])
|
||||
self.client.play(0)
|
||||
self.client.play()
|
||||
|
||||
def refresh(self):
|
||||
self.selection.handler_block(self.title_change)
|
||||
if self.client.connected():
|
||||
if self.client.playlist() != self.playlist:
|
||||
self.playlist_info.set_text("")
|
||||
self.store.clear()
|
||||
songs=self.client.playlistinfo()
|
||||
if not songs == []:
|
||||
whole_length=float(0)
|
||||
for song in songs:
|
||||
try:
|
||||
title=song["title"]
|
||||
@@ -356,8 +493,15 @@ class TrackView(Gtk.Box):
|
||||
album=song["album"]
|
||||
except:
|
||||
album=_("Unknown Album")
|
||||
duration=str(datetime.timedelta(seconds=int(float(song["duration"]))))
|
||||
try:
|
||||
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.playlist=self.client.playlist()
|
||||
else:
|
||||
if not self.song_to_delete == "":
|
||||
@@ -373,8 +517,9 @@ class TrackView(Gtk.Box):
|
||||
path = Gtk.TreePath(int(song))
|
||||
self.selection.select_path(path)
|
||||
except:
|
||||
self.selection.select_path(Gtk.TreePath(0))
|
||||
self.selection.unselect_all()
|
||||
else:
|
||||
self.playlist_info.set_text("")
|
||||
self.store.clear()
|
||||
self.playlist=[]
|
||||
self.selection.handler_unblock(self.title_change)
|
||||
@@ -417,12 +562,13 @@ class TrackView(Gtk.Box):
|
||||
self.client.play(selected_title)
|
||||
|
||||
class Browser(Gtk.Box):
|
||||
def __init__(self, client, settings):
|
||||
def __init__(self, client, settings, window):
|
||||
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=3)
|
||||
|
||||
#adding vars
|
||||
self.client=client
|
||||
self.settings=settings
|
||||
self.window=window
|
||||
|
||||
#widgets
|
||||
self.artist_list=ArtistView(self.client)
|
||||
@@ -502,14 +648,26 @@ class Browser(Gtk.Box):
|
||||
|
||||
def on_album_view_button_press_event(self, widget, event):
|
||||
path = widget.get_path_at_pos(int(event.x), int(event.y))
|
||||
if event.button == 3:
|
||||
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()
|
||||
@@ -603,21 +761,6 @@ class ProfileSettings(Gtk.Grid):
|
||||
self.attach_next_to(self.port_entry, port_label, Gtk.PositionType.RIGHT, 1, 1)
|
||||
self.attach_next_to(self.path_select_button, path_label, Gtk.PositionType.RIGHT, 1, 1)
|
||||
|
||||
def settings_array_append(self, vtype, key, value): #append to Gio.Settings (self.settings) array
|
||||
array=self.settings.get_value(key).unpack()
|
||||
array.append(value)
|
||||
self.settings.set_value(key, GLib.Variant(vtype, array))
|
||||
|
||||
def settings_array_delete(self, vtype, key, pos): #delete entry of Gio.Settings (self.settings) array
|
||||
array=self.settings.get_value(key).unpack()
|
||||
array.pop(pos)
|
||||
self.settings.set_value(key, GLib.Variant(vtype, array))
|
||||
|
||||
def settings_array_modify(self, vtype, key, pos, value): #modify entry of Gio.Settings (self.settings) array
|
||||
array=self.settings.get_value(key).unpack()
|
||||
array[pos]=value
|
||||
self.settings.set_value(key, GLib.Variant(vtype, array))
|
||||
|
||||
def profiles_combo_reload(self, *args):
|
||||
self.profiles_combo.handler_block(self.profiles_combo_changed)
|
||||
self.profile_entry.handler_block(self.profile_entry_changed)
|
||||
@@ -635,33 +778,33 @@ class ProfileSettings(Gtk.Grid):
|
||||
|
||||
def on_add_button_clicked(self, *args):
|
||||
pos=self.profiles_combo.get_active()
|
||||
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', "paths", "")
|
||||
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', "paths", "")
|
||||
self.profiles_combo_reload()
|
||||
self.profiles_combo.set_active(pos)
|
||||
|
||||
def on_delete_button_clicked(self, *args):
|
||||
pos=self.profiles_combo.get_active()
|
||||
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', "paths", pos)
|
||||
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', "paths", pos)
|
||||
self.profiles_combo_reload()
|
||||
self.profiles_combo.set_active(0)
|
||||
|
||||
def on_profile_entry_changed(self, *args):
|
||||
pos=self.profiles_combo.get_active()
|
||||
self.settings_array_modify('as', "profiles", pos, self.profile_entry.get_text())
|
||||
self.settings.array_modify('as', "profiles", pos, self.profile_entry.get_text())
|
||||
self.profiles_combo_reload()
|
||||
self.profiles_combo.set_active(pos)
|
||||
|
||||
def on_host_entry_changed(self, *args):
|
||||
self.settings_array_modify('as', "hosts", self.profiles_combo.get_active(), self.host_entry.get_text())
|
||||
self.settings.array_modify('as', "hosts", self.profiles_combo.get_active(), self.host_entry.get_text())
|
||||
|
||||
def on_port_entry_changed(self, *args):
|
||||
self.settings_array_modify('ai', "ports", self.profiles_combo.get_active(), self.port_entry.get_int())
|
||||
self.settings.array_modify('ai', "ports", self.profiles_combo.get_active(), self.port_entry.get_int())
|
||||
|
||||
def on_path_select_button_clicked(self, widget, parent):
|
||||
dialog = Gtk.FileChooserDialog(title=_("Choose directory"), transient_for=parent, action=Gtk.FileChooserAction.SELECT_FOLDER)
|
||||
@@ -671,7 +814,7 @@ class ProfileSettings(Gtk.Grid):
|
||||
dialog.set_current_folder(self.settings.get_value("paths")[self.profiles_combo.get_active()])
|
||||
response = dialog.run()
|
||||
if response == Gtk.ResponseType.OK:
|
||||
self.settings_array_modify('as', "paths", self.profiles_combo.get_active(), dialog.get_filename())
|
||||
self.settings.array_modify('as', "paths", self.profiles_combo.get_active(), dialog.get_filename())
|
||||
dialog.destroy()
|
||||
|
||||
def on_profiles_changed(self, *args):
|
||||
@@ -708,10 +851,19 @@ class GeneralSettings(Gtk.Grid):
|
||||
track_cover_size=IntEntry(self.settings.get_int("track-cover"), 100, 1200)
|
||||
album_cover_size=IntEntry(self.settings.get_int("album-cover"), 50, 600)
|
||||
|
||||
icon_size_label=Gtk.Label(label=_("Button icon size (restart required):"))
|
||||
icon_size_label.set_xalign(1)
|
||||
icon_size_combo=Gtk.ComboBoxText()
|
||||
icon_size_combo.set_entry_text_column(0)
|
||||
sizes=[16, 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")))
|
||||
|
||||
show_stop=Gtk.CheckButton(label=_("Show stop button"))
|
||||
show_stop.set_active(self.settings.get_boolean("show-stop"))
|
||||
|
||||
show_album_view_tooltips=Gtk.CheckButton(label=_("Show title list as tooltip in album view"))
|
||||
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"))
|
||||
|
||||
send_notify=Gtk.CheckButton(label=_("Send notification on title change"))
|
||||
@@ -726,6 +878,7 @@ class GeneralSettings(Gtk.Grid):
|
||||
#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_album_view_tooltips.connect("toggled", self.on_toggled, "show-album-view-tooltips")
|
||||
send_notify.connect("toggled", self.on_toggled, "send-notify")
|
||||
@@ -735,9 +888,11 @@ class GeneralSettings(Gtk.Grid):
|
||||
#packing
|
||||
self.add(track_cover_label)
|
||||
self.attach_next_to(album_cover_label, track_cover_label, Gtk.PositionType.BOTTOM, 1, 1)
|
||||
self.attach_next_to(icon_size_label, album_cover_label, Gtk.PositionType.BOTTOM, 1, 1)
|
||||
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(show_stop, album_cover_label, Gtk.PositionType.BOTTOM, 2, 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_album_view_tooltips, show_stop, Gtk.PositionType.BOTTOM, 2, 1)
|
||||
self.attach_next_to(send_notify, show_album_view_tooltips, Gtk.PositionType.BOTTOM, 2, 1)
|
||||
self.attach_next_to(add_album, send_notify, Gtk.PositionType.BOTTOM, 2, 1)
|
||||
@@ -749,6 +904,10 @@ class GeneralSettings(Gtk.Grid):
|
||||
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)
|
||||
|
||||
class SettingsDialog(Gtk.Dialog):
|
||||
def __init__(self, parent, settings):
|
||||
Gtk.Dialog.__init__(self, title=_("Settings"), transient_for=parent)
|
||||
@@ -767,6 +926,7 @@ class SettingsDialog(Gtk.Dialog):
|
||||
tabs.append_page(general, Gtk.Label(label=_("General")))
|
||||
tabs.append_page(profiles, Gtk.Label(label=_("Profiles")))
|
||||
self.vbox.pack_start(tabs, True, True, 0) #vbox default widget of dialogs
|
||||
self.vbox.set_spacing(6)
|
||||
|
||||
self.show_all()
|
||||
|
||||
@@ -777,12 +937,13 @@ class ClientControl(Gtk.ButtonBox):
|
||||
#adding vars
|
||||
self.client=client
|
||||
self.settings=settings
|
||||
self.icon_size=self.settings.get_gtk_icon_size("icon-size")
|
||||
|
||||
#widgets
|
||||
self.play_button = Gtk.Button(image=Gtk.Image.new_from_icon_name("media-playback-start", Gtk.IconSize.DND))
|
||||
self.stop_button = Gtk.Button(image=Gtk.Image.new_from_icon_name("media-playback-stop", Gtk.IconSize.DND))
|
||||
self.prev_button = Gtk.Button(image=Gtk.Image.new_from_icon_name("media-skip-backward", Gtk.IconSize.DND))
|
||||
self.next_button = Gtk.Button(image=Gtk.Image.new_from_icon_name("media-skip-forward", Gtk.IconSize.DND))
|
||||
self.play_button = Gtk.Button(image=Gtk.Image.new_from_icon_name("media-playback-start-symbolic", self.icon_size))
|
||||
self.stop_button = Gtk.Button(image=Gtk.Image.new_from_icon_name("media-playback-stop-symbolic", self.icon_size))
|
||||
self.prev_button = Gtk.Button(image=Gtk.Image.new_from_icon_name("media-skip-backward-symbolic", self.icon_size))
|
||||
self.next_button = Gtk.Button(image=Gtk.Image.new_from_icon_name("media-skip-forward-symbolic", self.icon_size))
|
||||
|
||||
#connect
|
||||
self.play_button.connect("clicked", self.on_play_clicked)
|
||||
@@ -805,15 +966,15 @@ class ClientControl(Gtk.ButtonBox):
|
||||
if self.client.connected():
|
||||
status=self.client.status()
|
||||
if status["state"] == "play":
|
||||
self.play_button.set_image(Gtk.Image.new_from_icon_name("media-playback-pause", Gtk.IconSize.DND))
|
||||
self.play_button.set_image(Gtk.Image.new_from_icon_name("media-playback-pause-symbolic", self.icon_size))
|
||||
self.prev_button.set_sensitive(True)
|
||||
self.next_button.set_sensitive(True)
|
||||
elif status["state"] == "pause":
|
||||
self.play_button.set_image(Gtk.Image.new_from_icon_name("media-playback-start", Gtk.IconSize.DND))
|
||||
self.play_button.set_image(Gtk.Image.new_from_icon_name("media-playback-start-symbolic", self.icon_size))
|
||||
self.prev_button.set_sensitive(True)
|
||||
self.next_button.set_sensitive(True)
|
||||
else:
|
||||
self.play_button.set_image(Gtk.Image.new_from_icon_name("media-playback-start", Gtk.IconSize.DND))
|
||||
self.play_button.set_image(Gtk.Image.new_from_icon_name("media-playback-start-symbolic", self.icon_size))
|
||||
self.prev_button.set_sensitive(False)
|
||||
self.next_button.set_sensitive(False)
|
||||
return True
|
||||
@@ -830,7 +991,7 @@ class ClientControl(Gtk.ButtonBox):
|
||||
self.client.play(status["song"])
|
||||
except:
|
||||
try:
|
||||
self.client.play(0) #bad song index possible
|
||||
self.client.play()
|
||||
except:
|
||||
pass
|
||||
self.update()
|
||||
@@ -865,7 +1026,9 @@ class SeekBar(Gtk.Box):
|
||||
|
||||
#widgets
|
||||
self.elapsed=Gtk.Label()
|
||||
self.elapsed.set_width_chars(7)
|
||||
self.rest=Gtk.Label()
|
||||
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)
|
||||
|
||||
@@ -882,13 +1045,10 @@ class SeekBar(Gtk.Box):
|
||||
|
||||
def seek(self, range, scroll, value):
|
||||
status=self.client.status()
|
||||
try:
|
||||
duration=float(status["duration"])
|
||||
factor=(value/100)
|
||||
pos=(duration*factor)
|
||||
self.client.seekcur(pos)
|
||||
except:
|
||||
pass
|
||||
|
||||
def update(self):
|
||||
try:
|
||||
@@ -908,22 +1068,25 @@ class SeekBar(Gtk.Box):
|
||||
return True
|
||||
|
||||
class PlaybackOptions(Gtk.Box):
|
||||
def __init__(self, client):
|
||||
def __init__(self, client, settings):
|
||||
Gtk.Box.__init__(self)
|
||||
|
||||
#adding vars
|
||||
self.client=client
|
||||
self.settings=settings
|
||||
self.icon_size=self.settings.get_gtk_icon_size("icon-size")
|
||||
|
||||
#widgets
|
||||
self.random=Gtk.ToggleButton(image=Gtk.Image.new_from_icon_name("media-playlist-shuffle-symbolic", Gtk.IconSize.DND))
|
||||
self.random=Gtk.ToggleButton(image=Gtk.Image.new_from_icon_name("media-playlist-shuffle-symbolic", self.icon_size))
|
||||
self.random.set_tooltip_text(_("Random mode"))
|
||||
self.repeat=Gtk.ToggleButton(image=Gtk.Image.new_from_icon_name("media-playlist-repeat-symbolic", Gtk.IconSize.DND))
|
||||
self.repeat=Gtk.ToggleButton(image=Gtk.Image.new_from_icon_name("media-playlist-repeat-symbolic", self.icon_size))
|
||||
self.repeat.set_tooltip_text(_("Repeat mode"))
|
||||
self.single=Gtk.ToggleButton(image=Gtk.Image.new_from_icon_name("zoom-original-symbolic", Gtk.IconSize.DND))
|
||||
self.single=Gtk.ToggleButton(image=Gtk.Image.new_from_icon_name("zoom-original-symbolic", self.icon_size))
|
||||
self.single.set_tooltip_text(_("Single mode"))
|
||||
self.consume=Gtk.ToggleButton(image=Gtk.Image.new_from_icon_name("edit-cut-symbolic", Gtk.IconSize.DND))
|
||||
self.consume=Gtk.ToggleButton(image=Gtk.Image.new_from_icon_name("edit-cut-symbolic", self.icon_size))
|
||||
self.consume.set_tooltip_text(_("Consume mode"))
|
||||
self.volume=Gtk.VolumeButton()
|
||||
self.volume.set_property("size", self.icon_size)
|
||||
|
||||
#connect
|
||||
self.random_toggled=self.random.connect("toggled", self.set_random)
|
||||
@@ -1024,6 +1187,7 @@ class AudioType(Gtk.EventBox):
|
||||
#widgets
|
||||
self.label=Gtk.Label()
|
||||
self.label.set_xalign(1)
|
||||
self.label.set_ellipsize(Pango.EllipsizeMode.END)
|
||||
self.popover=Gtk.Popover()
|
||||
|
||||
#Store
|
||||
@@ -1098,16 +1262,18 @@ class ProfileSelect(Gtk.ComboBoxText):
|
||||
self.client=client
|
||||
self.settings=settings
|
||||
|
||||
#connect
|
||||
self.changed=self.connect("changed", self.on_changed)
|
||||
|
||||
self.reload()
|
||||
self.set_active(self.settings.get_int("active-profile"))
|
||||
|
||||
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::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()
|
||||
@@ -1121,11 +1287,8 @@ class ProfileSelect(Gtk.ComboBoxText):
|
||||
def on_changed(self, *args):
|
||||
active=self.get_active()
|
||||
self.settings.set_int("active-profile", active)
|
||||
try:
|
||||
self.client.disconnect()
|
||||
self.client.connect(self.settings.get_value("hosts")[active], self.settings.get_value("ports")[active])
|
||||
except:
|
||||
pass
|
||||
self.client.try_connect_default()
|
||||
|
||||
class ServerStats(Gtk.Dialog):
|
||||
def __init__(self, parent, client):
|
||||
@@ -1168,7 +1331,7 @@ class ServerStats(Gtk.Dialog):
|
||||
class Search(Gtk.Dialog):
|
||||
def __init__(self, parent, client):
|
||||
Gtk.Dialog.__init__(self, title=_("Search"), transient_for=parent)
|
||||
self.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
|
||||
self.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE)
|
||||
self.set_default_size(800, 600)
|
||||
|
||||
#adding vars
|
||||
@@ -1180,7 +1343,6 @@ class Search(Gtk.Dialog):
|
||||
|
||||
#search entry
|
||||
self.search_entry=Gtk.SearchEntry()
|
||||
self.search_entry.connect("search-changed", self.on_search_changed)
|
||||
|
||||
#label
|
||||
self.label=Gtk.Label()
|
||||
@@ -1193,7 +1355,6 @@ class Search(Gtk.Dialog):
|
||||
#TreeView
|
||||
self.treeview = Gtk.TreeView(model=self.store)
|
||||
self.treeview.set_search_column(-1)
|
||||
self.treeview.set_tooltip_column(5)
|
||||
self.treeview.columns_autosize()
|
||||
|
||||
self.selection = self.treeview.get_selection()
|
||||
@@ -1205,31 +1366,37 @@ class Search(Gtk.Dialog):
|
||||
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.column_track.set_sort_column_id(0)
|
||||
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", False)
|
||||
self.column_title.set_sort_column_id(1)
|
||||
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", False)
|
||||
self.column_artist.set_sort_column_id(2)
|
||||
self.treeview.append_column(self.column_artist)
|
||||
|
||||
self.column_album = Gtk.TreeViewColumn(_("Album"), renderer_text, text=3)
|
||||
self.column_album.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
self.column_album.set_property("resizable", False)
|
||||
self.column_album.set_sort_column_id(3)
|
||||
self.treeview.append_column(self.column_album)
|
||||
|
||||
self.column_time = Gtk.TreeViewColumn(_("Length"), renderer_text, text=4)
|
||||
self.column_time.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
self.column_time.set_property("resizable", False)
|
||||
self.column_time.set_sort_column_id(4)
|
||||
self.treeview.append_column(self.column_time)
|
||||
|
||||
#connect
|
||||
self.title_activated=self.treeview.connect("row-activated", self.on_row_activated)
|
||||
self.title_change=self.selection.connect("changed", self.on_selection_change)
|
||||
self.search_entry.connect("search-changed", self.on_search_changed)
|
||||
|
||||
#packing
|
||||
scroll.add(self.treeview)
|
||||
@@ -1243,7 +1410,7 @@ class Search(Gtk.Dialog):
|
||||
selected_title=self.store.get_value(treeiter, 5)
|
||||
self.client.clear()
|
||||
self.client.add(selected_title)
|
||||
self.client.play(0)
|
||||
self.client.play()
|
||||
|
||||
def on_selection_change(self, widget):
|
||||
treeiter=widget.get_selected()[1]
|
||||
@@ -1253,7 +1420,7 @@ class Search(Gtk.Dialog):
|
||||
|
||||
def on_search_changed(self, widget):
|
||||
self.store.clear()
|
||||
for song in self.client.search("title", self.search_entry.get_text()):
|
||||
for song in self.client.search("any", self.search_entry.get_text()):
|
||||
try:
|
||||
title=song["title"]
|
||||
except:
|
||||
@@ -1270,7 +1437,11 @@ class Search(Gtk.Dialog):
|
||||
album=song["album"]
|
||||
except:
|
||||
album=_("Unknown Album")
|
||||
duration=str(datetime.timedelta(seconds=int(float(song["duration"]))))
|
||||
try:
|
||||
dura=float(song["duration"])
|
||||
except:
|
||||
dura=0.0
|
||||
duration=str(datetime.timedelta(seconds=int(dura)))
|
||||
self.store.append([track, title, artist, album, duration, song["file"].replace("&", "")] )
|
||||
self.label.set_text(_("Hits: %i") % (len(self.store)))
|
||||
|
||||
@@ -1281,15 +1452,11 @@ class LyricsWindow(Gtk.Window): #Lyrics view with own client because MPDClient i
|
||||
self.set_default_size(450, 800)
|
||||
|
||||
#adding vars
|
||||
self.client=Client()
|
||||
self.settings=settings
|
||||
self.client=Client(self.settings)
|
||||
|
||||
#connect client
|
||||
active=self.settings.get_int("active-profile")
|
||||
try:
|
||||
self.client.connect(self.settings.get_value("hosts")[active], self.settings.get_value("ports")[active])
|
||||
except:
|
||||
pass
|
||||
self.client.try_connect_default()
|
||||
self.current_song={}
|
||||
|
||||
#widgets
|
||||
@@ -1316,7 +1483,7 @@ class LyricsWindow(Gtk.Window): #Lyrics view with own client because MPDClient i
|
||||
|
||||
def update_loop():
|
||||
while not self.stop:
|
||||
try:
|
||||
if self.client.connected():
|
||||
cs=self.client.currentsong()
|
||||
cs.pop("pos") #avoid unnecessary reloads caused by position change of current title
|
||||
if cs != self.current_song:
|
||||
@@ -1327,7 +1494,7 @@ class LyricsWindow(Gtk.Window): #Lyrics view with own client because MPDClient i
|
||||
text=_("not found")
|
||||
GLib.idle_add(update_label, text)
|
||||
self.current_song=cs
|
||||
except:
|
||||
else:
|
||||
self.current_song={}
|
||||
GLib.idle_add(update_label, _("not connected"))
|
||||
time.sleep(1)
|
||||
@@ -1371,12 +1538,8 @@ class LyricsWindow(Gtk.Window): #Lyrics view with own client because MPDClient i
|
||||
return output.encode('utf-8')
|
||||
|
||||
def on_settings_changed(self, *args):
|
||||
active=self.settings.get_int("active-profile")
|
||||
try:
|
||||
self.client.disconnect()
|
||||
self.client.connect(self.settings.get_value("hosts")[active], self.settings.get_value("ports")[active])
|
||||
except:
|
||||
pass
|
||||
self.client.try_connect_default()
|
||||
|
||||
class MainWindow(Gtk.ApplicationWindow):
|
||||
def __init__(self, app, client, settings):
|
||||
@@ -1388,6 +1551,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
#adding vars
|
||||
self.client=client
|
||||
self.songid_playing=None
|
||||
self.icon_size=self.settings.get_gtk_icon_size("icon-size")
|
||||
|
||||
#actions
|
||||
save_action = Gio.SimpleAction.new("save", None)
|
||||
@@ -1407,18 +1571,18 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
self.add_action(update_action)
|
||||
|
||||
#widgets
|
||||
self.browser=Browser(self.client, self.settings)
|
||||
self.browser=Browser(self.client, self.settings, self)
|
||||
self.profiles=ProfileSelect(self.client, self.settings)
|
||||
self.profiles.set_tooltip_text(_("Select profile"))
|
||||
self.control=ClientControl(self.client, self.settings)
|
||||
self.progress=SeekBar(self.client)
|
||||
self.go_home_button=Gtk.Button(image=Gtk.Image.new_from_icon_name("go-home", Gtk.IconSize.DND))
|
||||
self.go_home_button=Gtk.Button(image=Gtk.Image.new_from_icon_name("go-home-symbolic", self.icon_size))
|
||||
self.go_home_button.set_tooltip_text(_("Return to album of current title"))
|
||||
self.search_button=Gtk.Button(image=Gtk.Image.new_from_icon_name("system-search", Gtk.IconSize.DND))
|
||||
self.search_button=Gtk.Button(image=Gtk.Image.new_from_icon_name("system-search-symbolic", self.icon_size))
|
||||
self.search_button.set_tooltip_text(_("Title search"))
|
||||
self.lyrics_button=Gtk.ToggleButton(image=Gtk.Image.new_from_icon_name("media-view-subtitles", Gtk.IconSize.DND))
|
||||
self.lyrics_button=Gtk.ToggleButton(image=Gtk.Image.new_from_icon_name("media-view-subtitles-symbolic", self.icon_size))
|
||||
self.lyrics_button.set_tooltip_text(_("Show lyrics"))
|
||||
self.play_opts=PlaybackOptions(self.client)
|
||||
self.play_opts=PlaybackOptions(self.client, self.settings)
|
||||
|
||||
#info bar
|
||||
self.info_bar=Gtk.InfoBar.new()
|
||||
@@ -1471,6 +1635,11 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
|
||||
self.add(self.vbox)
|
||||
|
||||
#connect client
|
||||
self.client.try_connect_default()
|
||||
|
||||
self.show_all()
|
||||
|
||||
def update(self, app): #update title and send notify
|
||||
if self.client.connected():
|
||||
self.info_bar.set_revealed(False)
|
||||
@@ -1509,11 +1678,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
|
||||
def on_info_bar_response(self, info_bar, response_id):
|
||||
if response_id == Gtk.ResponseType.OK:
|
||||
active=self.settings.get_int("active-profile")
|
||||
try:
|
||||
self.client.connect(self.settings.get_value("hosts")[active], self.settings.get_value("ports")[active])
|
||||
except:
|
||||
pass
|
||||
self.client.try_connect_default()
|
||||
info_bar.set_revealed(False)
|
||||
|
||||
def on_search_clicked(self, widget):
|
||||
@@ -1558,17 +1723,17 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
self.client.update()
|
||||
|
||||
class mpdevil(Gtk.Application):
|
||||
BASE_KEY = "org.mpdevil"
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, application_id="org.mpdevil", flags=Gio.ApplicationFlags.FLAGS_NONE, **kwargs)
|
||||
self.client=Client()
|
||||
self.settings = Gio.Settings.new(self.BASE_KEY)
|
||||
self.settings = Settings()
|
||||
self.client=Client(self.settings)
|
||||
self.window=None
|
||||
|
||||
def do_activate(self):
|
||||
if not self.window: #allow just one instance
|
||||
self.window = MainWindow(self, self.client, self.settings)
|
||||
self.window.connect("delete-event", self.on_delete_event)
|
||||
self.window.show_all()
|
||||
self.window.present()
|
||||
|
||||
def do_startup(self):
|
||||
Gtk.Application.do_startup(self)
|
||||
@@ -1592,6 +1757,7 @@ class mpdevil(Gtk.Application):
|
||||
dialog.set_version(VERSION)
|
||||
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_logo_icon_name(PACKAGE)
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
|
18
configure.ac
18
configure.ac
@@ -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.3])
|
||||
AC_INIT([mpdevil], [0.4.2])
|
||||
AC_CONFIG_SRCDIR([bin/mpdevil.py])
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
@@ -50,7 +50,7 @@ if $PYTHON -c "$prog" 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD; then
|
||||
AC_MSG_RESULT(found)
|
||||
else
|
||||
AC_MSG_RESULT(not found)
|
||||
AC_MSG_ERROR(MPDClient not found)
|
||||
AC_MSG_ERROR(python module mpd not found)
|
||||
fi
|
||||
|
||||
dnl Check for beautifulsoup
|
||||
@@ -62,7 +62,19 @@ if $PYTHON -c "$prog" 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD; then
|
||||
AC_MSG_RESULT(found)
|
||||
else
|
||||
AC_MSG_RESULT(not found)
|
||||
AC_MSG_ERROR(beautifulsoup not found)
|
||||
AC_MSG_ERROR(python module bs4 not found)
|
||||
fi
|
||||
|
||||
dnl Check for requests
|
||||
AC_MSG_CHECKING(for requests installed)
|
||||
prog="
|
||||
import requests
|
||||
"
|
||||
if $PYTHON -c "$prog" 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD; then
|
||||
AC_MSG_RESULT(found)
|
||||
else
|
||||
AC_MSG_RESULT(not found)
|
||||
AC_MSG_ERROR(python module requests not found)
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
|
@@ -31,6 +31,11 @@
|
||||
<summary>Size of main cover</summary>
|
||||
<description></description>
|
||||
</key>
|
||||
<key type="i" name="icon-size">
|
||||
<default>16</default>
|
||||
<summary>Size of button icons in control bar</summary>
|
||||
<description></description>
|
||||
</key>
|
||||
<key type="b" name="show-stop">
|
||||
<default>false</default>
|
||||
<summary>Show stop button</summary>
|
||||
|
144
po/de.po
144
po/de.po
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-01-06 00:53+0100\n"
|
||||
"PO-Revision-Date: 2020-01-06 00:55+0100\n"
|
||||
"POT-Creation-Date: 2020-01-28 19:56+0100\n"
|
||||
"PO-Revision-Date: 2020-01-28 19:58+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: de\n"
|
||||
@@ -18,136 +18,144 @@ msgstr ""
|
||||
"X-Generator: Poedit 2.2.4\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: mpdevil.py:116 mpdevil.py:250 mpdevil.py:1215
|
||||
msgid "Artist"
|
||||
msgstr "Künstler"
|
||||
|
||||
#: mpdevil.py:173 mpdevil.py:346 mpdevil.py:1260
|
||||
msgid "Unknown Title"
|
||||
msgstr "Unbekannter Titel"
|
||||
|
||||
#: mpdevil.py:184
|
||||
#, python-format
|
||||
msgid "%(album)s%(year)s (tracks: %(total_tracks)i) (%(total_length)s):"
|
||||
msgstr "%(album)s%(year)s (Titel: %(total_tracks)i) (%(total_length)s):"
|
||||
|
||||
#: mpdevil.py:240 mpdevil.py:1205
|
||||
#: mpdevil.py:159 mpdevil.py:368 mpdevil.py:1366
|
||||
msgid "No"
|
||||
msgstr "Nr."
|
||||
|
||||
#: mpdevil.py:245 mpdevil.py:1210
|
||||
#: mpdevil.py:164 mpdevil.py:373 mpdevil.py:1372
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: mpdevil.py:255 mpdevil.py:1225
|
||||
#: mpdevil.py:169 mpdevil.py:378 mpdevil.py:1378
|
||||
msgid "Artist"
|
||||
msgstr "Interpret"
|
||||
|
||||
#: mpdevil.py:174 mpdevil.py:383 mpdevil.py:1390
|
||||
msgid "Length"
|
||||
msgstr "Länge"
|
||||
|
||||
#: mpdevil.py:354 mpdevil.py:1268
|
||||
#: mpdevil.py:214 mpdevil.py:483 mpdevil.py:1427
|
||||
msgid "Unknown Title"
|
||||
msgstr "Unbekannter Titel"
|
||||
|
||||
#: mpdevil.py:218 mpdevil.py:491 mpdevil.py:1435
|
||||
msgid "Unknown Artist"
|
||||
msgstr "Unbekannter Künstler"
|
||||
|
||||
#: mpdevil.py:358 mpdevil.py:1272
|
||||
#: mpdevil.py:254
|
||||
msgid "Album Artist"
|
||||
msgstr "Albuminterpret"
|
||||
|
||||
#: mpdevil.py:313 mpdevil.py:504
|
||||
#, python-format
|
||||
msgid "%(total_tracks)i titles (%(total_length)s)"
|
||||
msgstr "%(total_tracks)i Titel (%(total_length)s)"
|
||||
|
||||
#: mpdevil.py:495 mpdevil.py:1439
|
||||
msgid "Unknown Album"
|
||||
msgstr "Unbekanntes Album"
|
||||
|
||||
#: mpdevil.py:567
|
||||
#: mpdevil.py:725
|
||||
msgid "Select"
|
||||
msgstr "Auswählen"
|
||||
|
||||
#: mpdevil.py:569
|
||||
#: mpdevil.py:727
|
||||
msgid "Profile:"
|
||||
msgstr "Profil:"
|
||||
|
||||
#: mpdevil.py:571
|
||||
#: mpdevil.py:729
|
||||
msgid "Name:"
|
||||
msgstr "Name:"
|
||||
|
||||
#: mpdevil.py:573
|
||||
#: mpdevil.py:731
|
||||
msgid "Host:"
|
||||
msgstr "Host:"
|
||||
|
||||
#: mpdevil.py:575
|
||||
#: mpdevil.py:733
|
||||
msgid "Port:"
|
||||
msgstr "Port:"
|
||||
|
||||
#: mpdevil.py:577
|
||||
#: mpdevil.py:735
|
||||
msgid "Music lib:"
|
||||
msgstr "Musikverzeichnis:"
|
||||
|
||||
#: mpdevil.py:667
|
||||
#: mpdevil.py:810
|
||||
msgid "Choose directory"
|
||||
msgstr "Verzeichnis Wählen"
|
||||
|
||||
#: mpdevil.py:703
|
||||
#: mpdevil.py:846
|
||||
msgid "Main cover size:"
|
||||
msgstr "Größe des Haupt-Covers:"
|
||||
|
||||
#: mpdevil.py:705
|
||||
#: mpdevil.py:848
|
||||
msgid "Album-view cover size:"
|
||||
msgstr "Covergröße in Albumansicht:"
|
||||
|
||||
#: mpdevil.py:711
|
||||
#: mpdevil.py:854
|
||||
msgid "Button icon size (restart required):"
|
||||
msgstr "Symbolgröße der Knöpfe (Neustart erforderlich):"
|
||||
|
||||
#: mpdevil.py:863
|
||||
msgid "Show stop button"
|
||||
msgstr "Zeige Stopp-Knopf"
|
||||
|
||||
#: mpdevil.py:714
|
||||
msgid "Show title list as tooltip in album view"
|
||||
msgstr "Zeige Titellisten als Tooltips in Albumansicht"
|
||||
#: mpdevil.py:866
|
||||
msgid "Show tooltips in album view"
|
||||
msgstr "Zeige Tooltips in Albumansicht"
|
||||
|
||||
#: mpdevil.py:717
|
||||
#: mpdevil.py:869
|
||||
msgid "Send notification on title change"
|
||||
msgstr "Sende Benachrichtigung bei Titelwechsel"
|
||||
|
||||
#: mpdevil.py:720
|
||||
#: mpdevil.py:872
|
||||
msgid "Stop playback on quit"
|
||||
msgstr "Wiedergabe beim Beenden stoppen"
|
||||
|
||||
#: mpdevil.py:723
|
||||
#: mpdevil.py:875
|
||||
msgid "Play selected album after current title"
|
||||
msgstr "Ausgewähltes Album hinter aktuellem Titel einreihen"
|
||||
|
||||
#: mpdevil.py:754 mpdevil.py:1432
|
||||
#: mpdevil.py:913 mpdevil.py:1596
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#: mpdevil.py:767
|
||||
#: mpdevil.py:926
|
||||
msgid "General"
|
||||
msgstr "Allgemein"
|
||||
|
||||
#: mpdevil.py:768
|
||||
#: mpdevil.py:927
|
||||
msgid "Profiles"
|
||||
msgstr "Profile"
|
||||
|
||||
#: mpdevil.py:919
|
||||
#: mpdevil.py:1081
|
||||
msgid "Random mode"
|
||||
msgstr "Zufallsmodus"
|
||||
|
||||
#: mpdevil.py:921
|
||||
#: mpdevil.py:1083
|
||||
msgid "Repeat mode"
|
||||
msgstr "Dauerschleife"
|
||||
|
||||
#: mpdevil.py:923
|
||||
#: mpdevil.py:1085
|
||||
msgid "Single mode"
|
||||
msgstr "Einzelstückmodus"
|
||||
|
||||
#: mpdevil.py:925
|
||||
#: mpdevil.py:1087
|
||||
msgid "Consume mode"
|
||||
msgstr "Playliste verbrauchen"
|
||||
|
||||
#: mpdevil.py:1019
|
||||
#: mpdevil.py:1182
|
||||
msgid "Right click to show additional information"
|
||||
msgstr "Rechtsclick für weitere Informationen"
|
||||
|
||||
#: mpdevil.py:1042
|
||||
#: mpdevil.py:1206
|
||||
msgid "MPD-Tag"
|
||||
msgstr "MPD-Tag"
|
||||
|
||||
#: mpdevil.py:1045 mpdevil.py:1153
|
||||
#: mpdevil.py:1209 mpdevil.py:1316
|
||||
msgid "Value"
|
||||
msgstr "Wert"
|
||||
|
||||
#: mpdevil.py:1066
|
||||
#: mpdevil.py:1230
|
||||
#, python-format
|
||||
msgid ""
|
||||
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s "
|
||||
@@ -156,88 +164,88 @@ msgstr ""
|
||||
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s "
|
||||
"Kanäle, %(file_type)s"
|
||||
|
||||
#: mpdevil.py:1132
|
||||
#: mpdevil.py:1295
|
||||
msgid "Stats"
|
||||
msgstr "Statistik"
|
||||
|
||||
#: mpdevil.py:1150
|
||||
#: mpdevil.py:1313
|
||||
msgid "Tag"
|
||||
msgstr "Tag"
|
||||
|
||||
#: mpdevil.py:1170
|
||||
#: mpdevil.py:1333
|
||||
msgid "Search"
|
||||
msgstr "Suche"
|
||||
|
||||
#: mpdevil.py:1220
|
||||
#: mpdevil.py:1384
|
||||
msgid "Album"
|
||||
msgstr "Album"
|
||||
|
||||
#: mpdevil.py:1275
|
||||
#: mpdevil.py:1446
|
||||
#, python-format
|
||||
msgid "Hits: %i"
|
||||
msgstr "Treffer: %i"
|
||||
|
||||
#: mpdevil.py:1279
|
||||
#: mpdevil.py:1450
|
||||
msgid "Lyrics"
|
||||
msgstr "Liedtext"
|
||||
|
||||
#: mpdevil.py:1323
|
||||
#: mpdevil.py:1490
|
||||
msgid "searching..."
|
||||
msgstr "suche..."
|
||||
|
||||
#: mpdevil.py:1327
|
||||
#: mpdevil.py:1494
|
||||
msgid "not found"
|
||||
msgstr "nicht gefunden"
|
||||
|
||||
#: mpdevil.py:1332
|
||||
#: mpdevil.py:1499
|
||||
msgid "not connected"
|
||||
msgstr "nicht verbunden"
|
||||
|
||||
#: mpdevil.py:1412
|
||||
#: mpdevil.py:1576
|
||||
msgid "Select profile"
|
||||
msgstr "Profil auswählen"
|
||||
|
||||
#: mpdevil.py:1416
|
||||
#: mpdevil.py:1580
|
||||
msgid "Return to album of current title"
|
||||
msgstr "Zu Album des aktuellen Titels zurückkehren"
|
||||
|
||||
#: mpdevil.py:1418
|
||||
#: mpdevil.py:1582
|
||||
msgid "Title search"
|
||||
msgstr "Titelsuche"
|
||||
|
||||
#: mpdevil.py:1420
|
||||
#: mpdevil.py:1584
|
||||
msgid "Show lyrics"
|
||||
msgstr "Zeige Liedtext"
|
||||
|
||||
#: mpdevil.py:1427
|
||||
#: mpdevil.py:1591
|
||||
msgid "Not connected to MPD-server. Reconnect?"
|
||||
msgstr "Nicht mit MPD-Server verbunden. Verbindung wiederherstellen?"
|
||||
|
||||
#: mpdevil.py:1431
|
||||
#: mpdevil.py:1595
|
||||
msgid "Save window size"
|
||||
msgstr "Fenstergröße speichern"
|
||||
|
||||
#: mpdevil.py:1433
|
||||
#: mpdevil.py:1597
|
||||
msgid "Update database"
|
||||
msgstr "Datenbank aktualisieren"
|
||||
|
||||
#: mpdevil.py:1434
|
||||
#: mpdevil.py:1598
|
||||
msgid "Server stats"
|
||||
msgstr "Serverstatistik"
|
||||
|
||||
#: mpdevil.py:1435
|
||||
#: mpdevil.py:1599
|
||||
msgid "About"
|
||||
msgstr "Über"
|
||||
|
||||
#: mpdevil.py:1436
|
||||
#: mpdevil.py:1600
|
||||
msgid "Quit"
|
||||
msgstr "Beenden"
|
||||
|
||||
#: mpdevil.py:1441
|
||||
#: mpdevil.py:1605
|
||||
msgid "Main menu"
|
||||
msgstr "Hauptmenu"
|
||||
|
||||
#: mpdevil.py:1593
|
||||
#: mpdevil.py:1758
|
||||
msgid "A small MPD client written in python"
|
||||
msgstr ""
|
||||
|
||||
|
140
po/mpdevil.pot
140
po/mpdevil.pot
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-01-06 00:53+0100\n"
|
||||
"POT-Creation-Date: 2020-01-28 19:56+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,223 +17,231 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: mpdevil.py:116 mpdevil.py:250 mpdevil.py:1215
|
||||
msgid "Artist"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:173 mpdevil.py:346 mpdevil.py:1260
|
||||
msgid "Unknown Title"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:184
|
||||
#, python-format
|
||||
msgid "%(album)s%(year)s (tracks: %(total_tracks)i) (%(total_length)s):"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:240 mpdevil.py:1205
|
||||
#: mpdevil.py:159 mpdevil.py:368 mpdevil.py:1366
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:245 mpdevil.py:1210
|
||||
#: mpdevil.py:164 mpdevil.py:373 mpdevil.py:1372
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:255 mpdevil.py:1225
|
||||
#: mpdevil.py:169 mpdevil.py:378 mpdevil.py:1378
|
||||
msgid "Artist"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:174 mpdevil.py:383 mpdevil.py:1390
|
||||
msgid "Length"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:354 mpdevil.py:1268
|
||||
#: mpdevil.py:214 mpdevil.py:483 mpdevil.py:1427
|
||||
msgid "Unknown Title"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:218 mpdevil.py:491 mpdevil.py:1435
|
||||
msgid "Unknown Artist"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:358 mpdevil.py:1272
|
||||
#: mpdevil.py:254
|
||||
msgid "Album Artist"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:313 mpdevil.py:504
|
||||
#, python-format
|
||||
msgid "%(total_tracks)i titles (%(total_length)s)"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:495 mpdevil.py:1439
|
||||
msgid "Unknown Album"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:567
|
||||
#: mpdevil.py:725
|
||||
msgid "Select"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:569
|
||||
#: mpdevil.py:727
|
||||
msgid "Profile:"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:571
|
||||
#: mpdevil.py:729
|
||||
msgid "Name:"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:573
|
||||
#: mpdevil.py:731
|
||||
msgid "Host:"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:575
|
||||
#: mpdevil.py:733
|
||||
msgid "Port:"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:577
|
||||
#: mpdevil.py:735
|
||||
msgid "Music lib:"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:667
|
||||
#: mpdevil.py:810
|
||||
msgid "Choose directory"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:703
|
||||
#: mpdevil.py:846
|
||||
msgid "Main cover size:"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:705
|
||||
#: mpdevil.py:848
|
||||
msgid "Album-view cover size:"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:711
|
||||
#: mpdevil.py:854
|
||||
msgid "Button icon size (restart required):"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:863
|
||||
msgid "Show stop button"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:714
|
||||
msgid "Show title list as tooltip in album view"
|
||||
#: mpdevil.py:866
|
||||
msgid "Show tooltips in album view"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:717
|
||||
#: mpdevil.py:869
|
||||
msgid "Send notification on title change"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:720
|
||||
#: mpdevil.py:872
|
||||
msgid "Stop playback on quit"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:723
|
||||
#: mpdevil.py:875
|
||||
msgid "Play selected album after current title"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:754 mpdevil.py:1432
|
||||
#: mpdevil.py:913 mpdevil.py:1596
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:767
|
||||
#: mpdevil.py:926
|
||||
msgid "General"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:768
|
||||
#: mpdevil.py:927
|
||||
msgid "Profiles"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:919
|
||||
#: mpdevil.py:1081
|
||||
msgid "Random mode"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:921
|
||||
#: mpdevil.py:1083
|
||||
msgid "Repeat mode"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:923
|
||||
#: mpdevil.py:1085
|
||||
msgid "Single mode"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:925
|
||||
#: mpdevil.py:1087
|
||||
msgid "Consume mode"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1019
|
||||
#: mpdevil.py:1182
|
||||
msgid "Right click to show additional information"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1042
|
||||
#: mpdevil.py:1206
|
||||
msgid "MPD-Tag"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1045 mpdevil.py:1153
|
||||
#: mpdevil.py:1209 mpdevil.py:1316
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1066
|
||||
#: mpdevil.py:1230
|
||||
#, python-format
|
||||
msgid ""
|
||||
"%(bitrate)s kb/s, %(frequency)s kHz, %(resolution)s bit, %(channels)s "
|
||||
"channels, %(file_type)s"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1132
|
||||
#: mpdevil.py:1295
|
||||
msgid "Stats"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1150
|
||||
#: mpdevil.py:1313
|
||||
msgid "Tag"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1170
|
||||
#: mpdevil.py:1333
|
||||
msgid "Search"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1220
|
||||
#: mpdevil.py:1384
|
||||
msgid "Album"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1275
|
||||
#: mpdevil.py:1446
|
||||
#, python-format
|
||||
msgid "Hits: %i"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1279
|
||||
#: mpdevil.py:1450
|
||||
msgid "Lyrics"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1323
|
||||
#: mpdevil.py:1490
|
||||
msgid "searching..."
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1327
|
||||
#: mpdevil.py:1494
|
||||
msgid "not found"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1332
|
||||
#: mpdevil.py:1499
|
||||
msgid "not connected"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1412
|
||||
#: mpdevil.py:1576
|
||||
msgid "Select profile"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1416
|
||||
#: mpdevil.py:1580
|
||||
msgid "Return to album of current title"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1418
|
||||
#: mpdevil.py:1582
|
||||
msgid "Title search"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1420
|
||||
#: mpdevil.py:1584
|
||||
msgid "Show lyrics"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1427
|
||||
#: mpdevil.py:1591
|
||||
msgid "Not connected to MPD-server. Reconnect?"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1431
|
||||
#: mpdevil.py:1595
|
||||
msgid "Save window size"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1433
|
||||
#: mpdevil.py:1597
|
||||
msgid "Update database"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1434
|
||||
#: mpdevil.py:1598
|
||||
msgid "Server stats"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1435
|
||||
#: mpdevil.py:1599
|
||||
msgid "About"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1436
|
||||
#: mpdevil.py:1600
|
||||
msgid "Quit"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1441
|
||||
#: mpdevil.py:1605
|
||||
msgid "Main menu"
|
||||
msgstr ""
|
||||
|
||||
#: mpdevil.py:1593
|
||||
#: mpdevil.py:1758
|
||||
msgid "A small MPD client written in python"
|
||||
msgstr ""
|
||||
|
BIN
screenshots/mainwindow.png
Normal file
BIN
screenshots/mainwindow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1005 KiB |
Reference in New Issue
Block a user