You are not logged in.
NOTE - A much more prolific HowTo that includes Deadbeef can be seen here
I have worked with several music apps and getting conky to work... When my good friend Ivanovnegro asked me to create a conky for Deadbeef with cover art, it was a challenge that I could not pass up
Here is the results and how to create a Deadbeef Conky with cover art
You will need Deadbeef with the plugin Album Artwork setup and functioning.. and you must do this in Deadbeef to have images even show up
(From Deadbeef Help)
ALBUM ART DISPLAY
to display album artwork, you need to follow the steps below
1. add new column, select Album Art type
2. right click on the playlist column headers, and in context menu select "group by" submenu. click "Artist/Date/Album"
You will also need the DeaDBeeF-MPRIS-plugin installed. Get it here
Now the files
.conkyrcdb Save to your /home directory and make sure it is executable
(start your conky with conky -c ~/.conkyrcdb -q -d &)
# .conkyrcdb - Edited from various examples across the 'net
# Used by VastOne on #! Wheezy
use_xft yes
xftfont Sans:size=15
alignment top_right
xftalpha 0.9
own_window yes
own_window_argb_visual yes
own_window_type desktop
own_window_transparent yes
own_window_class Conky
own_window_hints undecorated,below,sticky,skip_taskbar,skip_pager
double_buffer yes
draw_shades no
draw_outline no
draw_borders no
stippled_borders 10
border_outer_margin 40
border_width 1
default_shade_color grey
default_outline_color black
default_color white
use_spacer yes
no_buffers yes
uppercase no
imlib_cache_size 0
text_buffer_size 2048
# default_bar_size 230 9
total_run_times 0
update_interval 1
cpu_avg_samples 1
net_avg_samples 1
override_utf8_locale no
minimum_size 335 5
maximum_width 335
#minimum_size 335
gap_x 20
gap_y 40
# colors
color1 B9CDD4
color2 white
color3 white
color4 BAF58B
TEXT
${execp ~/conkyDeadbeef.py --template=~/conkyDeadbeef.template}Next is the two files called in the .conkyrcdb Copy both of these to your /home directory and make sure they are executable
conkyDeadbeef.py
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
###############################################################################
# conkydeadbeef.py is a simple python script to gather
# details from deadbeef for use in conky.
#
# Author: VastOne
# Created: 01/28/2011
from datetime import datetime
from optparse import OptionParser
import sys
import traceback
import codecs
import os
import htmllib
import re
from htmlentitydefs import name2codepoint
import shutil
import urllib
try:
import dbus
DBUS_AVAIL = True
except ImportError:
# Dummy D-Bus library
class _Connection:
get_object = lambda *a: object()
class _Interface:
__init__ = lambda *a: None
ListNames = lambda *a: []
class Dummy: pass
dbus = Dummy()
dbus.Interface = _Interface
dbus.service = Dummy()
dbus.service.method = lambda *a: lambda f: f
dbus.service.Object = object
dbus.SessionBus = _Connection
DBUS_AVAIL = False
class CommandLineParser:
parser = None
def __init__(self):
self.parser = OptionParser()
self.parser.add_option("-t", "--template", dest="template", type="string", metavar="FILE", help=u"define a template file to generate output in one call. A displayable item in the file is in the form [--datatype=TI]. The following are possible options within each item: --datatype,--ratingchar. Note that the short forms of the options are not currently supported! None of these options are applicable at command line when using templates.")
self.parser.add_option("-d", "--datatype", dest="datatype", default="TI", type="string", metavar="DATATYPE", help=u"[default: %default] The data type options are: ST (status), CA (coverart), TI (title), AL (album), AR (artist), GE (genre), YR (year), TN (track number), FN (file name), BR (bitrate k/s), LE (length), PP (current position in percent), PT (current position in time), VO (volume), RT (rating). Not applicable at command line when using templates.")
self.parser.add_option("-c", "--coverartpath", dest="coverartpath", default="/tmp/cover", type="string", metavar="PATH", help=u"[default: %default] The file where coverart gets copied to if found when using the --datatype=CA option. Note that if set to an empty string i.e. \"\" the original file path is provided for the coverart path.")
self.parser.add_option("-r", "--ratingchar", dest="ratingchar", default="*", type="string", metavar="CHAR", help=u"[default: %default] The output character for the ratings scale. Command line option overridden if used in templates.")
self.parser.add_option("-s", "--statustext", dest="statustext", default="Playing,Paused,Stopped", type="string", metavar="TEXT", help=u"[default: %default] The text must be comma delimited in the form 'A,B,C'. Command line option overridden if used in templates.")
self.parser.add_option("-n", "--nounknownoutput", dest="nounknownoutput", default=False, action="store_true", help=u"Turn off unknown output such as \"Unknown\" for title and \"0:00\" for length. Command line option overridden if used in templates.")
self.parser.add_option("-S", "--secondsoutput", dest="secondsoutput", default=False, action="store_true", help=u"Force all position and length output to be in seconds only.")
self.parser.add_option("-m", "--maxlength", dest="maxlength", default="0", type="int", metavar="LENGTH", help=u"[default: %default] Define the maximum length of any datatypes output, if truncated the output ends in \"...\"")
self.parser.add_option("-v", "--verbose", dest="verbose", default=False, action="store_true", help=u"Request verbose output, not a good idea when running through conky!")
self.parser.add_option("-V", "--version", dest="version", default=False, action="store_true", help=u"Displays the version of the script.")
self.parser.add_option("--errorlogfile", dest="errorlogfile", type="string", metavar="FILE", help=u"If a filepath is set, the script appends errors to the filepath.")
self.parser.add_option("--infologfile", dest="infologfile", type="string", metavar="FILE", help=u"If a filepath is set, the script appends info to the filepath.")
def parse_args(self):
(options, args) = self.parser.parse_args()
return (options, args)
def print_help(self):
return self.parser.print_help()
class MusicData:
def __init__(self,status,coverart,title,album,length,artist,tracknumber,genre,year,filename,bitrate,current_position_percent,current_position,rating,volume):
self.status = status
self.coverart = coverart
self.title = title
self.album = album
self.length = length
self.artist = artist
self.tracknumber = tracknumber
self.genre = genre
self.year = year
self.filename = filename
self.bitrate = bitrate
self.current_position_percent = current_position_percent
self.current_position = current_position
self.rating = rating
self.volume = volume
class DeadbeefInfo:
error = u""
musicData = None
def __init__(self, options):
self.options = options
def testDBus(self, bus, interface):
obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
dbus_iface = dbus.Interface(obj, 'org.freedesktop.DBus')
avail = dbus_iface.ListNames()
return interface in avail
def getOutputData(self, datatype, ratingchar, statustext, nounknownoutput, maxlength):
output = u""
if nounknownoutput == True:
unknown_time = ""
unknown_number = ""
unknown_string = ""
else:
unknown_time = "0:00"
unknown_number = "0"
unknown_string = "Unknown"
try:
bus = dbus.SessionBus()
if self.musicData == None:
if self.testDBus(bus, 'org.mpris.deadbeef'):
self.logInfo("Calling dbus interface for music data")
try:
self.logInfo("Setting up dbus interface")
# setup dbus hooks
remote_player = bus.get_object('org.mpris.deadbeef', '/Player')
iface_player = dbus.Interface(remote_player, 'org.freedesktop.MediaPlayer')
self.logInfo("Calling dbus interface for music data")
status = self.getStatusText(iface_player.GetStatus()[0], statustext)
# try to get all the normal stuff...the props return an empty string if nothing is available
props = iface_player.GetMetadata()
# grab the data into variables
if "location" in props:
location = props["location"]
else:
location = None
# handle a file or stream differently for filename
if location.find("file://") != -1:
filename = location[location.rfind("/")+1:]
elif len(location) > 0:
filename = location
else:
filename = ""
if "title" in props:
title = props["title"]
else:
title = None
if "album" in props:
album = props["album"]
else:
album = None
if "artist" in props:
artist = props["artist"]
else:
artist = None
if "year" in props:
year = str(props["year"])
else:
year = None
if "tracknumber" in props:
tracknumber = str(props["tracknumber"])
else:
tracknumber = None
if "audio-bitrate" in props:
bitrate = str(props["audio-bitrate"])
else:
bitrate = None
if year == "0": year = None
if tracknumber == "0": tracknumber = None
if bitrate == "0": bitrate = None
# TODO: get album art working for internet based (if feasible)...
# get coverart url or file link
if "arturl" in props:
coverart = os.path.expanduser(urllib.unquote(str(props["arturl"])).replace("file://",""))
#coverart = os.path.expanduser(str(props["arturl"]).replace(" ", "%20"))
if coverart.find("http://") != -1:
coverart = None
else:
coverart = None
# common details
if "genre" in props:
genre = props["genre"]
else:
genre = None
if "rating" in props:
rating = props["rating"]
else:
rating = None
if "time" in props:
length_seconds = props["time"]
else:
length_seconds = 0
current_seconds = int(iface_player.PositionGet() / 1000)
if length_seconds > 0:
current_position_percent = str(int((float(current_seconds) / float(length_seconds))*100))
else:
length_seconds = 0
current_position_percent = "0"
if self.options.secondsoutput == True:
length = str(length_seconds)
current_position = str(current_seconds)
else:
length = str(length_seconds/60).rjust(1,"0")+":"+str(length_seconds%60).rjust(2,"0")
current_position = str(int(current_seconds/60)).rjust(1,"0")+":"+str(int(current_seconds%60)).rjust(2,"0")
volume = str(iface_player.VolumeGet())
self.musicData = MusicData(status,coverart,title,album,length,artist,tracknumber,genre,year,filename,bitrate,current_position_percent,current_position,rating,volume)
except Exception, e:
self.logError("Issue calling the dbus service:"+e.__str__())
if self.musicData != None:
self.logInfo("Preparing output for datatype:"+datatype)
if datatype == "ST": #status
if self.musicData.status == None or len(self.musicData.status) == 0:
output = None
else:
output = self.musicData.status
elif datatype == "CA": #coverart
if self.musicData.coverart == None or len(self.musicData.coverart) == 0:
output = None
else:
self.logInfo("Copying coverart from %s to %s"%(self.musicData.coverart, self.options.coverartpath))
shutil.copy(self.musicData.coverart, self.options.coverartpath)
self.musicData.coverart = self.options.coverartpath
output = self.musicData.coverart
elif datatype == "TI": #title
if self.musicData.title == None or len(self.musicData.title) == 0:
output = None
else:
output = self.musicData.title
elif datatype == "AL": #album
if self.musicData.album == None or len(self.musicData.album) == 0:
output = None
else:
output = self.musicData.album
elif datatype == "AR": #artist
if self.musicData.artist == None or len(self.musicData.artist) == 0:
output = None
else:
output = self.musicData.artist
elif datatype == "TN": #tracknumber
if self.musicData.tracknumber == None or len(self.musicData.tracknumber) == 0:
output = None
else:
output = self.musicData.tracknumber
elif datatype == "GE": #genre
if self.musicData.title == genre or len(self.musicData.genre) == 0:
output = None
else:
output = self.musicData.genre
elif datatype == "YR": #year
if self.musicData.year == None or len(self.musicData.year) == 0:
output = None
else:
output = self.musicData.year
elif datatype == "FN": #filename
if self.musicData.filename == None or len(self.musicData.filename) == 0:
output = None
else:
output = self.musicData.filename
elif datatype == "BR": #bitrate
if self.musicData.bitrate == None or len(self.musicData.bitrate) == 0:
output = None
else:
output = self.musicData.bitrate
elif datatype == "LE": # length
if self.musicData.length == None or len(self.musicData.length) == 0:
output = None
else:
output = self.musicData.length
elif datatype == "PP": #current position in percent
if self.musicData.current_position_percent == None or len(self.musicData.current_position_percent) == 0:
output = None
else:
output = self.musicData.current_position_percent
elif datatype == "PT": #current position in time
if self.musicData.current_position == None or len(self.musicData.current_position) == 0:
output = None
else:
output = self.musicData.current_position
elif datatype == "VO": #volume
if self.musicData.volume == None or len(self.musicData.volume) == 0:
output = None
else:
output = self.musicData.volume
elif datatype == "RT": #rating
if self.musicData.rating == None or self.isNumeric(self.musicData.rating) == False:
output = None
else:
rating = int(self.musicData.rating)
if rating > 0:
output = u"".ljust(rating,ratingchar)
elif rating == 0:
output = u""
else:
output = None
else:
self.logError("Unknown datatype provided: " + datatype)
return u""
if output == None or self.musicData == None:
if datatype in ["LE","PT"]:
if self.options.secondsoutput == True:
output = unknown_number
else:
output = unknown_time
elif datatype in ["PP","VO","YR","TN"]:
output = unknown_number
elif datatype == "CA":
output = ""
else:
output = unknown_string
if maxlength > 0 and len(output) > maxlength:
output = output[:maxlength-3]+"..."
return output
except SystemExit:
self.logError("System Exit!")
return u""
except Exception, e:
traceback.print_exc()
self.logError("Unknown error when calling getOutputData:" + e.__str__())
return u""
def getStatusText(self, status, statustext):
if status != None:
statustextparts = statustext.split(",")
if status == 0:
return statustextparts[0]
elif status == 1:
return statustextparts[1]
elif status == 2:
return statustextparts[2]
else:
return status
def getTemplateItemOutput(self, template_text):
# keys to template data
DATATYPE_KEY = "datatype"
RATINGCHAR_KEY = "ratingchar"
STATUSTEXT_KEY = "statustext"
NOUNKNOWNOUTPUT_KEY = "nounknownoutput"
MAXLENGTH_KEY = "maxlength"
datatype = None
ratingchar = self.options.ratingchar #default to command line option
statustext = self.options.statustext #default to command line option
nounknownoutput = self.options.nounknownoutput #default to command line option
maxlength = self.options.maxlength #default to command line option
for option in template_text.split('--'):
if len(option) == 0 or option.isspace():
continue
# not using split here...it can't assign both key and value in one call, this should be faster
x = option.find('=')
if (x != -1):
key = option[:x].strip()
value = option[x + 1:].strip()
if value == "":
value = None
else:
key = option.strip()
value = None
try:
if key == DATATYPE_KEY:
datatype = value
elif key == RATINGCHAR_KEY:
ratingchar = value
elif key == STATUSTEXT_KEY:
statustext = value
elif key == NOUNKNOWNOUTPUT_KEY:
nounknownoutput = True
elif key == MAXLENGTH_KEY:
maxlength = int(value)
else:
self.logError("Unknown template option: " + option)
except (TypeError, ValueError):
self.logError("Cannot convert option argument to number: " + option)
return u""
if datatype != None:
return self.getOutputData(datatype, ratingchar, statustext, nounknownoutput, maxlength)
else:
self.logError("Template item does not have datatype defined")
return u""
def getOutputFromTemplate(self, template):
output = u""
end = False
a = 0
# a and b are indexes in the template string
# moving from left to right the string is processed
# b is index of the opening bracket and a of the closing bracket
# everything between b and a is a template that needs to be parsed
while not end:
b = template.find('[', a)
if b == -1:
b = len(template)
end = True
# if there is something between a and b, append it straight to output
if b > a:
output += template[a : b]
# check for the escape char (if we are not at the end)
if template[b - 1] == '\\' and not end:
# if its there, replace it by the bracket
output = output[:-1] + '['
# skip the bracket in the input string and continue from the beginning
a = b + 1
continue
if end:
break
a = template.find(']', b)
if a == -1:
self.logError("Missing terminal bracket (]) for a template item")
return u""
# if there is some template text...
if a > b + 1:
output += self.getTemplateItemOutput(template[b + 1 : a])
a = a + 1
return output
def writeOutput(self):
if self.options.template != None:
#load the file
try:
fileinput = codecs.open(os.path.expanduser(self.options.template), encoding='utf-8')
template = fileinput.read()
fileinput.close()
except Exception, e:
self.logError("Error loading template file: " + e.__str__())
else:
output = self.getOutputFromTemplate(template)
else:
output = self.getOutputData(self.options.datatype, self.options.ratingchar, self.options.statustext, self.options.nounknownoutput, self.options.maxlength)
print output.encode("utf-8",'replace')
def isNumeric(self,value):
try:
temp = int(value)
return True
except:
return False
def logInfo(self, text):
if self.options.verbose == True:
print >> sys.stdout, "INFO: " + text
if self.options.infologfile != None:
datetimestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
fileoutput = open(self.options.infologfile, "ab")
fileoutput.write(datetimestamp+" INFO: "+text+"\n")
fileoutput.close()
def logError(self, text):
print >> sys.stderr, "ERROR: " + text
if self.options.errorlogfile != None:
datetimestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
fileoutput = open(self.options.errorlogfile, "ab")
fileoutput.write(datetimestamp+" ERROR: "+text+"\n")
fileoutput.close()
def unEscape(self,text):
parser = htmllib.HTMLParser(None)
parser.save_bgn()
parser.feed(text)
return parser.save_end()
def htmlentitydecode(self,s):
return re.sub('&(%s);' % '|'.join(name2codepoint),
lambda m: unichr(name2codepoint[m.group(1)]), s)
def main():
parser = CommandLineParser()
(options, args) = parser.parse_args()
if options.version == True:
print >> sys.stdout,"conkydeadbeef v.2.00"
else:
if options.verbose == True:
print >> sys.stdout, "*** INITIAL OPTIONS:"
print >> sys.stdout, " datatype:", options.datatype
print >> sys.stdout, " template:", options.template
print >> sys.stdout, " ratingchar:", options.ratingchar
print >> sys.stdout, " nounknownoutput:", options.nounknownoutput
print >> sys.stdout, " secondsoutput:", options.secondsoutput
print >> sys.stdout, " maxlength:", options.maxlength
print >> sys.stdout, " verbose:", options.verbose
print >> sys.stdout, " errorlogfile:",options.errorlogfile
print >> sys.stdout, " infologfile:",options.infologfile
deadbeefinfo = DeadbeefInfo(options)
deadbeefinfo.writeOutput()
if __name__ == '__main__':
main()
sys.exit()conkyDeadbeef.template
Note - this is the file you would edit to make changes to the appearance of the conky
${color #7CB2E5}${#FFFFFF}[--datatype=TI --maxlength=32]
${color #7CB2E5}${#FFFFFF}[--datatype=AR --maxlength=32]
${color #7CB2E5}${#FFFFFF}[--datatype=AL --maxlength=30]
${image ~/images/dbeef.png -p 3,445 -s 36x36}
${voffset 318}${#FFFFFF}[--datatype=PT] ${color #7CB2E5}${execibar 1 ~/conkyDeadbeef.py --datatype=PP} ${#FFFFFF}[--datatype=LE]
${voffset 7}${#FFFFFF}[--datatype=ST] ${color #7CB2E5} Volume ${#FFFFFF}[--datatype=VO] ${color 7CB2E5}BitRate ${#FFFFFF}[--datatype=BR]
${execi 20 ~/db-cover.sh}${image /tmp/deadbeef-coverart.png -p 3,84 -s 335x335}db-cover.sh Copy this to your /home dir and make sure it is executable
#! /bin/bash
# An album art script for Deadbeef
ARTCACHE=~/.cache/deadbeef/covers
ARTIST="`deadbeef --nowplaying "%a"`"
ALBUM="`deadbeef --nowplaying "%b"`"
CURCOVER="$ARTCACHE/$ARTIST/$ALBUM.jpg"
TMPDIR="/tmp"
COVER="$TMPDIR/deadbeef-coverart.png"
cp "$CURCOVER" "$COVER"
fiHere is a link to the dbeef.png file. Save it as dbeef.png in your ~/images directory
Thats it...
Here is what the results should look like.. and remember to change it in the template to suit your tastes...
http://www.zimagez.com/zimage/screensho … 0631pm.php
Edit - I need to give props to Kaivalagi as the mastermind behind so many python Conky scripts.. It is his work that the conkyDeadbeef.py is based on
Last edited by VastOne (2011-12-23 21:29:39)
VSIDO
If you build it, they will come...
Words That Build Or Destroy
Offline
^^ Awesome!!!!!!!
Now found at Conky PitStop complete with the archive of all files needed.
Last edited by Sector11 (2011-09-09 01:53:32)
#! Etiquette | Conky PitStop | VSIDO | Interactive LUA
Weather v9000 | Teo x4 Sites | Arclance | Finnish
Offline
^^ Awesome!!!!!!!
Thank you... You had recommended this one a long time ago but Deadbeef does not handle some of the MPRIS functions needed for cover art..
I created an end around using what Deadbeef does allow for...
I will post this same thing on CPS if you think it is of value... or you can just link it back to here if you want
Last edited by VastOne (2011-09-04 23:53:09)
VSIDO
If you build it, they will come...
Words That Build Or Destroy
Offline
Good work VastOne! Still figuring out how to make it to work here, had some starter problems, you know yet. 
Online
Good work VastOne! Still figuring out how to make it to work here, had some starter problems, you know yet.
You need more plum schnapps!
#! Etiquette | Conky PitStop | VSIDO | Interactive LUA
Weather v9000 | Teo x4 Sites | Arclance | Finnish
Offline
Sector11 wrote:^^ Awesome!!!!!!!
Thank you... You had recommended this one a long time ago but Deadbeef does not handle some of the MPRIS functions needed for cover art..
I created an end around using what Deadbeef does allow for...
I will post this same thing on CPS if you think it is of value... or you can just link it back to here if you want
Sounds like a plan. I should install deadbeef, as opposed to livepork, and give it a try.
Pass the plum juice Ivan!
#! Etiquette | Conky PitStop | VSIDO | Interactive LUA
Weather v9000 | Teo x4 Sites | Arclance | Finnish
Offline
^ On its way. 
Online
^ On its way.
Black Ops Ivan sent me a pint of it and I had the most creative day yesterday solving both Deadbeef and Pogo Conky layouts...
... But man the hangover/headache today...

VSIDO
If you build it, they will come...
Words That Build Or Destroy
Offline
ivanovnegro wrote:^ On its way.
Black Ops Ivan sent me a pint of it and I had the most creative day yesterday solving both Deadbeef and Pogo Conky layouts...
... But man the hangover/headache today...
![]()
I actually found a cure - well was told of a cure and it worked - I refused to tell a buddy that saw me DRUNK on Friday night and no hangover on Saturday morning, for a few weeks in a row - until he tried it first!
So we went out Saturday night had a few past a few to many and back at the barracks I gave him the cure.
early Sunday morning he's in my room shaking me awake, "What did you give me last night?"
"Oh, hi John," opening the top drawer in my locker and moving my socks aside, "two of these."
"NO WAY!"
"Yup, now you understand why I needed you to try it first?"
"OH YEA!!!!!!"
Suppose you want to know huh!
CHECK for PM's - vor = for
Last edited by Sector11 (2011-09-05 16:25:09)
#! Etiquette | Conky PitStop | VSIDO | Interactive LUA
Weather v9000 | Teo x4 Sites | Arclance | Finnish
Offline
Hey, no hanover here, good training. 
Online
deleted
Last edited by Sector11 (2011-09-09 01:52:49)
#! Etiquette | Conky PitStop | VSIDO | Interactive LUA
Weather v9000 | Teo x4 Sites | Arclance | Finnish
Offline
Doesn't work anymore because of mpris plugin incompatibility with new deadbeef 0.5.5. Is there any other way?
Offline
^ I will take a look at it. Is the comparability issue going to be fixed by the plugin dev, meaning is it just a matter of time?
VSIDO
If you build it, they will come...
Words That Build Or Destroy
Offline
The bug report was posted months ago by the author of deadbeef and has not been commented on by the plugin dev.
Maybe someone should email the plugin dev to see if it is ever going to be fixed.
I know that the plugin works if you can get deadbeef to start but it tends to crash 9/10 times when you start it for me.
You can get most of the data except Volume and Playing/Paused/Stopped by using "deadbeef --nowplaying".
--nowplaying FMT Print formatted track name to stdout
FMT %-syntax: [a]rtist, [t]itle, al[b]um,
[l]ength, track[n]umber, [y]ear, [c]omment,
copy[r]ight, [e]lapsed
e.g.: --nowplaying "%a - %t" should print "artist - title"
for more info, see http://sourceforge.net/apps/mediawiki/deadbeef/index.php?title=Title_FormattingLast edited by arclance (2012-08-28 13:37:23)
Offline
Copyright © 2012 CrunchBang Linux.
Proudly powered by Debian. Hosted by Linode.
Debian is a registered trademark of Software in the Public Interest, Inc.