# Copyright (C) 2007-2009 daelstorm. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
import gtk, gobject
#import os, commands, sys, re, thread, threading
import os, sys, re, thread, threading
import copy
import sys
if sys.platform == "win32":
HAS_WIN32GUI = False
try:
from win32gui import *
HAS_WIN32GUI = True
except:
pass
from pynicotine.utils import _, executeCommand
from pynicotine.logfacility import log
class NowPlaying:
def __init__(self, frame, fake=False):
"""Create NowPlayer interface
If faked=True it will only create a partial instance, enough to debug NP-code with from the command line"""
# Only configure things here that are required for a faked instance
self.frame = frame
try:
import dbus
import dbus.glib
self.bus = dbus.SessionBus()
except Exception, e:
self.bus = None
if fake:
return
# All things that aren't needed for a faked instance
self.accel_group = gtk.AccelGroup()
self.wTree = gtk.glade.XML(os.path.join(os.path.dirname(os.path.realpath(__file__)), "nowplaying.glade" ), None, 'nicotine' )
widgets = self.wTree.get_widget_prefix("")
for i in widgets:
name = gtk.glade.get_widget_name(i)
self.__dict__[name] = i
self.wTree.signal_autoconnect(self)
self.NowPlaying.set_icon(self.frame.images["n"])
self.NowPlaying.set_position(gtk.WIN_POS_NONE)
#self.NowPlaying.set_modal(True)
self.NowPlaying.set_transient_for(self.frame.MainWindow)
self.NowPlaying.add_accel_group(self.accel_group)
self.NowPlaying.connect("destroy", self.quit)
self.NowPlaying.connect("destroy-event", self.quit)
self.NowPlaying.connect("delete-event", self.quit)
self.NowPlaying.set_resizable(False)
self.defaultlist = [ "$n", "$a - $t", "[$a] $t", "Now $s: [$a] $t", "Now $s: $n", "$a - $b - $t", "$a - $b - $t ($l/$rKBps) from $y $c" ]
self.title_clear()
self.player_replacers = []
self.NPFormat_List = gtk.ListStore(gobject.TYPE_STRING)
self.NPFormat.set_model(self.NPFormat_List)
self.NPFormat.set_text_column(0)
self.NPFormat.child.connect("activate", self.OnAddFormat)
self.NPFormat.child.connect("changed", self.OnModifyFormat)
self.OnNPPlayer(None)
#self.NowPlaying.add(self.vbox1)
# Set the active radio button
config = self.frame.np.config.sections
self.SetPlayer(config["players"]["npplayer"])
if config["players"]["npformat"] != "":
self.NPFormat.child.set_text(config["players"]["npformat"])
if config["players"]["npformatlist"] != []:
if config["players"]["npformat"] == "":
self.NPFormat.child.set_text(str(config["players"]["npformatlist"][0]))
for item in config["players"]["npformatlist"]:
self.NPFormat_List.append([item])
if config["players"]["npformat"] == "":
self.NPFormat.child.set_text(str(self.defaultlist[0]))
self.NPCommand.set_text(config["players"]["npothercommand"])
for item in self.defaultlist:
self.NPFormat_List.append([str(item)])
def SetTextBG(self, widget, bgcolor="", fgcolor=""):
if bgcolor == "":
colour = None
else:
colour = gtk.gdk.color_parse(bgcolor)
widget.modify_base(gtk.STATE_NORMAL, colour)
widget.modify_bg(gtk.STATE_NORMAL, colour)
if type(widget) in (gtk.Entry, gtk.SpinButton):
if fgcolor == "":
colour = None
else:
colour = gtk.gdk.color_parse(fgcolor)
widget.modify_text(gtk.STATE_NORMAL, colour)
widget.modify_fg(gtk.STATE_NORMAL, colour)
def title_clear(self):
self.Example.set_text("")
self.title = { "title": "", "artist": "", "comment": "", "year": "", "album": "", "track":"", "length": "", "nowplaying": "", "status": "", "bitrate": "", "filename": ""}
def SetPlayer(self, player):
if player == "infopipe":
self.NP_infopipe.set_active(1)
elif player == "amarok":
self.NP_amarok.set_active(1)
elif player == "amarok2":
self.NP_amarok2.set_active(1)
elif player == "audacious":
self.NP_audacious.set_active(1)
elif player == "mpd":
self.NP_mpd.set_active(1)
elif player == "banshee":
self.NP_banshee.set_active(1)
#elif player == "mp3blaster":
# self.NP_mp3blaster.set_active(1)
elif player == "rhythmbox":
self.NP_rhythmbox.set_active(1)
elif player == "bmpx":
self.NP_bmpx.set_active(1)
elif player == "exaile":
self.NP_exaile.set_active(1)
elif player == "lastfm":
self.NP_lastfm.set_active(1)
elif player == "foobar":
self.NP_foobar.set_active(1)
elif player == "mpris":
self.NP_mpris.set_active(1)
elif player == "other":
self.NP_other.set_active(1)
self.player_replacers = ["$n"]
else:
self.NP_other.set_active(1)
def OnModifyFormat(self, widget):
text = self.NPFormat.child.get_text().strip()
replacers = []
for replacer in ["$n", "$t", "$l", "$a", "$b", "$c", "$k", "$y", "$r", "$f", "$s", "$p"]:
if replacer in text:
replacers.append(replacer)
for replacer in replacers:
if replacer not in self.player_replacers:
self.frame.SetTextBG(self.NPFormat.child, "red", "white")
return
self.frame.SetTextBG(self.NPFormat.child, "", "")
def OnAddFormat(self, widget):
text = self.NPFormat.child.get_text().strip()
if text.isspace() or text == "":
return
items = self.frame.np.config.sections["players"]["npformatlist"]
if text in self.defaultlist:
return
if text in items:
items.remove(text)
items.insert(0, text)
self.frame.np.config.sections["players"]["npformat"] = text
del items[15:]
self.frame.np.config.writeConfiguration()
# Repopulate the combo list
self.NPFormat.get_model().clear()
templist = []
for i in items:
if i not in templist:
templist.append(i)
templist += self.defaultlist
for i in templist:
self.NPFormat.append_text(i)
def OnNPPlayer(self, widget):
isset = False
if self.NP_infopipe.get_active():
self.player_replacers = ["$n", "$l", "$b", "$c", "$k", "$r", "$f", "$s"]
isset = True
elif self.NP_mpd.get_active():
self.player_replacers = ["$n", "$t", "$a", "$b", "$f", "$k"]
isset = True
elif self.NP_banshee.get_active():
self.player_replacers = ["$n", "$t", "$l", "$a", "$b", "$k", "$y", "$r", "$f", "$s"]
isset = True
elif self.NP_amarok.get_active():
self.player_replacers = ["$n", "$t", "$l", "$a", "$b", "$c", "$k", "$y", "$r", "$f", "$s"]
isset = True
elif self.NP_amarok2.get_active():
self.player_replacers = ["$n", "$t", "$l", "$a", "$b", "$c", "$k", "$y", "$r", "$f", "$s"]
isset = True
elif self.NP_audacious.get_active():
self.player_replacers = ["$n", "$t", "$l", "$a", "$b", "$c", "$k", "$y", "$r", "$f", "$s"]
isset = True
elif self.NP_rhythmbox.get_active():
self.player_replacers = ["$n", "$t", "$l", "$a", "$b", "$c", "$k", "$y", "$r", "$f", "$s"]
isset = True
elif self.NP_bmpx.get_active():
self.player_replacers = ["$n", "$t", "$l", "$a", "$b", "$k", "$y", "$r", "$f"]
isset = True
elif self.NP_exaile.get_active():
self.player_replacers = ["$t", "$l", "$a", "$b"]
isset = True
elif self.NP_lastfm.get_active():
self.player_replacers = ["$n", "$s", "$t", "$a"]
self.player_input.set_text(_("Username:"))
isset = True
elif self.NP_foobar.get_active():
self.player_replacers = ["$n"]
isset = True
elif self.NP_mpris.get_active():
self.player_replacers = ['$p', '$a', '$b', '$t', '$c', '$r', '$k', '$l']
self.player_input.set_text(_("Client name (empty = auto):"))
isset = True
elif self.NP_other.get_active():
self.player_replacers = ["$n"]
self.player_input.set_text(_("Command:"))
isset = True
self.NPCommand.set_sensitive(
self.NP_lastfm.get_active() or
self.NP_other.get_active() or
self.NP_mpris.get_active()
)
legend = ""
for item in self.player_replacers:
legend += item + "\t"
if item == "$t":
legend += _("Title")
elif item == "$n":
legend += _("Now Playing (typically \"%(artist)s - %(title)s\")") % {'artist':_("Artist"), 'title':_("Title")}
elif item == "$l":
legend += _("Length")
elif item == "$r":
legend += _("Bitrate")
elif item == "$c":
legend += _("Comment")
elif item == "$a":
legend += _("Artist")
elif item == "$b":
legend += _("Album")
elif item == "$k":
legend += _("Track Number")
elif item == "$y":
legend += _("Year")
elif item == "$f":
legend += _("Filename (URI)")
elif item == "$s":
legend += _("Status")
elif item == "$p":
legend += _("Program")
legend += "\n"
self.Legend.set_text(legend)
if not isset:
self.Legend.set_text("")
self.OnModifyFormat(self.NPFormat.child)
def OnNPCancel(self, widget):
self.quit(None)
def quit(self, widget, s=None):
self.NowPlaying.hide()
return True
def OnNPTest(self, widget):
self.DisplayNowPlaying(None, 1)
def DisplayNowPlaying(self, widget, test=0, callback=None):
if (self.NP_rhythmbox.get_active() or
self.NP_bmpx.get_active() or
self.NP_mpris.get_active()):
# dbus (no threads, please)
self.GetNP(None, test, callback)
else:
# thread (command execution)
thread.start_new_thread(self.GetNP, (None, test, callback))
def GetNP(self, widget, test=None, callback=None):
self.title_clear()
try:
if self.NP_infopipe.get_active():
result = self.xmms()
elif self.NP_amarok.get_active():
result = self.amarok()
elif self.NP_amarok2.get_active():
result = self.amarok2()
elif self.NP_audacious.get_active():
result = self.audacious()
elif self.NP_mpd.get_active():
result = self.mpd()
elif self.NP_banshee.get_active():
result = self.banshee()
elif self.NP_rhythmbox.get_active():
result = self.rhythmbox()
elif self.NP_bmpx.get_active():
result = self.bmpx()
elif self.NP_exaile.get_active():
result = self.exaile()
elif self.NP_lastfm.get_active():
result = self.lastfm()
elif self.NP_foobar.get_active():
result = self.foobar()
elif self.NP_other.get_active():
result = self.other()
elif self.NP_mpris.get_active():
result = self.mpris()
except RuntimeError:
log.addwarning("ERROR: Could not execute now playing code. Are you sure you picked the right player?")
result = None
if not result:
return None
# Since we need unicode instead of bytes we'll try to force such a
# conversion. Individual player commands should have done this already
# - this is a failsafe.
oldtitle = copy.copy(self.title)
self.title_clear()
for key, value in oldtitle.iteritems():
try:
self.title[key] = unicode(value, "UTF-8", "replace")
except TypeError:
self.title[key] = value # already unicode
title = self.NPFormat.child.get_text()
title = title.replace("%", "%%") # Escaping user supplied % symbols
#print "Title1: " + title
title = title.replace("$t", "%(title)s")
title = title.replace("$a", "%(artist)s")
title = title.replace("$b", "%(album)s")
title = title.replace("$c", "%(comment)s")
title = title.replace("$n", "%(nowplaying)s")
title = title.replace("$k", "%(track)s")
title = title.replace("$l", "%(length)s")
title = title.replace("$y", "%(year)s")
title = title.replace("$r", "%(bitrate)s")
title = title.replace("$s", "%(status)s")
title = title.replace("$f", "%(filename)s")
title = title.replace("$p", "%(program)s")
#print "Title2: " + title
title = title % self.title
title = ' '.join([x for x in title.replace('\r', '\n').split('\n') if x])
if test:
self.Example.set_text(title)
return None
if title:
if callback:
callback(title)
return title
return None
def OnNPSave(self, widget):
if self.NP_infopipe.get_active():
player = "infopipe"
elif self.NP_amarok.get_active():
player = "amarok"
elif self.NP_amarok2.get_active():
player = "amarok2"
elif self.NP_audacious.get_active():
player = "audacious"
elif self.NP_mpd.get_active():
player = "mpd"
elif self.NP_banshee.get_active():
player = "banshee"
#elif self.NP_mp3blaster.get_active():
# player = "mp3blaster"
elif self.NP_rhythmbox.get_active():
player = "rhythmbox"
elif self.NP_bmpx.get_active():
player = "bmpx"
elif self.NP_exaile.get_active():
player = "exaile"
elif self.NP_lastfm.get_active():
player = "lastfm"
elif self.NP_foobar.get_active():
player = "foobar"
elif self.NP_mpris.get_active():
player = "mpris"
elif self.NP_other.get_active():
player = "other"
self.frame.np.config.sections["players"]["npplayer"] = player
self.frame.np.config.sections["players"]["npothercommand"] = self.NPCommand.get_text()
self.frame.np.config.sections["players"]["npformat"] = self.NPFormat.child.get_text()
self.frame.np.config.writeConfiguration()
self.quit(None)
def get_custom_widget(self, id, string1, string2, int1, int2):
w = gtk.Label(_("(custom widget: %s)") % id)
return w
def bmpx(self):
if self.bus is None:
self.frame.logMessage(_("ERROR: DBus not available:")+" "+"BMPx"+ " "+ _("cannot be contacted"))
return
try:
bmp_object = self.bus.get_object('org.beepmediaplayer.bmp', '/Core')
bmp_iface = self.bus.Interface(bmp_object, 'org.beepmediaplayer.bmp')
except Exception, error:
self.frame.logMessage(_("ERROR while accessing the %(program)s DBus interface: %(error)s") % {"program": "BMPx", "error": error})
return
try:
if bmp_iface.GetCurrentSource() == -1:
return None
metadata = bmp_iface.GetMetadataFromSource(bmp_iface.GetCurrentSource())
self.title["bitrate"] = str(metadata["bitrate"])
self.title["track"] = str(metadata["tracknumber"])
self.title["title"] = metadata["title"]
self.title["artist"] = metadata["artist"]
self.title["length"] = self.get_length_time(metadata["time"])
self.title["nowplaying"] = metadata["artist"] + " - " +metadata["title"]
self.title["year"] = str(metadata["date"])
self.title["album"] = metadata["album"]
self.title["filename"] = metadata["location"]
return True
except Exception, error:
self.frame.logMessage(_("ERROR while reading data from the %(program)s DBus interface: %(error)s") % {"program": "BMPx", "error": error})
return None
def mpd(self):
format = self.NPFormat.child.get_text()
if "$a" in format:
output = self.mpd_command("%artist%")
if output:
self.title["artist"] = output
if "$t" in format:
output = self.mpd_command("%title%")
if output:
self.title["title"] = output
if "$n" in format:
output = self.mpd_command("%artist% - %title%")
if output:
self.title["nowplaying"] = output
if "$f" in format:
output = self.mpd_command("%file%")
if output:
self.title["filename"] = output
if "$b" in format:
output = self.mpd_command("%album%")
if output:
self.title["album"] = output
return True
def mpd_command(self, command):
#output = commands.getoutput("mpc --format %s" % command).split('\n')[0]
output = executeCommand("mpc --format $", command, returnoutput=True).split('\n')[0]
if output == '' or output.startswith("MPD_HOST") or output.startswith("volume: "):
return None
return output
def banshee(self):
slist = self.NPFormat.child.get_text()
if "$n" in slist:
commandlist = ["--query-artist", "--query-title"]
else:
commandlist = []
if "$t" in slist:
commandlist.append("--query-title")
if "$a" in slist:
commandlist.append("--query-artist")
if "$b" in slist:
commandlist.append("--query-album")
if "$k" in slist:
commandlist.append("--query-track-count")
if "$l" in slist:
commandlist.append("--query-duration")
if "$y" in slist:
commandlist.append("--query-year")
if "$r" in slist:
commandlist.append("--query-bit-rate")
if "$f" in slist:
commandlist.append("--query-uri")
if not commandlist:
return False
output = self.banshee_command(commandlist)
#print "'"+output+"'"
matches = {}
[ matches.__setitem__(i[0].split(':')[0], filter(len, i[1:])[0]) for i in re.findall(r"(?m)^(title: (?P
.*?)|artist: (?P.*?)|album: (?P.*?)|track-number: (?P