You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
amarok/amarok/src/scripts/webcontrol/Playlist.py

310 lines
10 KiB

#!/usr/bin/env python
# -*- coding: Latin-1 -*-
#
# Author: Andr<64> Kelpe fs111 at web dot de
# : Jonas Drewsen kde at xspect dot dk
# : Peter Ndikuwera pndiku at gmail dot com
#
# License: GPL
#
import user
import os
from xml.dom import minidom
import time
import string
import Globals
# the current.xml file
PLAYLISTFILE = "%s/.trinity/share/apps/amarok/current.xml"%(user.home)
# the fields to be shown via http
FIELDS = ("TrackNo", "Title", "Artist", "Album", "Length", "Rating")
class Track(object):
"""Class that holds the information of one track in the current playlist"""
__Q_SLOTS__ = FIELDS
max_field_value_lengths = [(0,"")] * len(FIELDS)
def __init__(self, **kwargs):
for key,value in kwargs.iteritems():
setattr(self, key, value)
def toRow(self, style='', id=None, trackno=None, reqid=None, sesid = None):
"""Returns the a html-table-row with member values of this class"""
trno = str(trackno)
astart = "<a "
if id:
astart += "name='nowplaying' "
# append "#nowplaying" if we want to jump to current song.
astart += ("class='track' href='javascript:dolink(\"action=goto&value="
+ trno + "&"
+ reqid + "&"
+ sesid + "\");'>")
aend = "</a>"
tmp = [ '<td>' + astart + i + aend +'</td>' for i in [getattr(self,f) for f in self.__Q_SLOTS__ ] ]
index = 0
# for f in self.__Q_SLOTS__ :
# print string.strip(f)
for i in [getattr(self,f) for f in self.__Q_SLOTS__ ]:
if len(string.strip(i)) > Track.max_field_value_lengths[index][0]:
Track.max_field_value_lengths[index] = (len(string.strip(i)),i)
index += 1
tr_style = ''
tr_id = ''
if style:
tr_style='class="%s"'%(style)
if trackno == 0:
tr_id = 'id="trackone"'
if id:
tr_id = 'id="%s"'%(id)
return '<tr %s %s>%s</tr>'%(tr_style, tr_id, ''.join(tmp))
class Playlist:
"""The Playlist class represents the Playlist as one object. It holds all
needed information and does most of the work"""
def __init__(self):
"""The Constructor takes no arguments."""
self.tracks = []
self.encoding = "UTF-8"
#"ISO-8859-15"
self.fullPage = ""
self.templateFilename = "template.thtml"
self.templateLastChanged = -1
self.templateLastChanged = self._loadHtmlTemplate()
def _loadHtmlTemplate(self):
"""Loads the global string template variables that are used for
rendering the page. The template can be changed by setting
self.templateFilename to a template file."""
ep = Globals.EXEC_PATH
st = os.stat(ep + "/" + self.templateFilename)[8]
if self.templateLastChanged != st:
tmp = open(ep + "/" + self.templateFilename).read()
a = compile(tmp, "<string>", 'exec')
eval(a)
return st
def toHtml(self, status):
"""Returns a html representation of the whole Playlist"""
self.templateLastChanged = self._loadHtmlTemplate()
self.sync()
self.tracks = []
self.mtime = self._getMtime()
self._buildDoc()
self._setFullPage(status)
return self.fullPage
def _createButton(self, name, action, reqid, sesid):
"""Return a button to be used as an action"""
return ("<a href='javascript:dolink(\"action="
+ action + reqid + sesid + "\");'><img src='player_" + name + ".png'></a>")
def _createVolume(self, vol_val, reqid, sesid):
"""Return a HTML volume seletor."""
volume = '<table width="100%" class="volume">'
volume += "<tr>";
button = "<div style='width:6px; height:12px;'></div>";
for i in range(1,vol_val+1):
volume += ("<td class='volumeset'>" +
"<a href='javascript:dolink(\"action=setvolume&value="
+ str(i*10) + reqid + sesid + "\");'>" + button + "</a></td>")
for i in range(vol_val+1, 11):
volume += ("<td class='volumeunset'>" +
"<a href='javascript:dolink(\"action=setvolume&value="
+ str(i*10) + reqid + sesid + "\");'>" + button + "</a></td>")
volume += "</tr></table>"
return volume
def _createTimeStr(self, status):
"""Returns the string representation of the the remaining time of the current song."""
_gmtime = time.gmtime(status.timeLeft())
hours = status.timeLeft() / (60*60)
mins = (status.timeLeft() % (60*60)) / 60
secs = status.timeLeft() - 60 * mins - 60 * 60 * hours
hours_str = str(hours) + ":"
if hours < 10:
hours_str = "0" + hours_str
if hours == 0:
hours_str = ""
mins_str = str(mins) + ":"
if mins < 10:
mins_str = "0" + mins_str
secs_str = str(secs)
if secs < 10:
secs_str = "0" + secs_str
return hours_str + mins_str + secs_str
def _trackTimeStr(self, trackTime):
"""Returns the string representation of the track time of a song."""
_time = int(trackTime)
hours = _time / (60*60)
mins = (_time % (60*60)) / 60
secs = _time - 60 * mins - 60 * 60 * hours
hours_str = str(hours) + ":"
if hours < 10:
hours_str = "0" + hours_str
if hours == 0:
hours_str = ""
mins_str = str(mins) + ":"
if mins < 10:
mins_str = "0" + mins_str
secs_str = str(secs)
if secs < 10:
secs_str = "0" + secs_str
return hours_str + mins_str + secs_str
def _ratingStars(self, rating):
"""Returns a number of stars showing the rating of a song."""
_rating = int(rating)
_numBig = _rating / 2
_numSmall = _rating % 2
img_str=""
for i in range(1,_numBig+1):
img_str = img_str + "<img height='12' width='12' src='star.png'>"
if _numSmall> 0:
img_str = img_str + "<img height='12' width='12' src='smallstar.png'>"
return img_str
def _createActions(self, status):
"""Returns HTML for allactions that can be performed and for the time counter"""
if not status.controlsEnabled():
return ""
reqid = "&reqid=" + str(status.reqid)
sesid = "&sesid=" + str(status.sesid)
playpause = self._createButton("play", "play", reqid, sesid)
if status.isPlaying():
playpause = self._createButton("pause", "pause", reqid, sesid)
buttons = (playpause + "&nbsp;" +
self._createButton("stop", "stop", reqid, sesid) + "&nbsp;" +
self._createButton("start", "prev", reqid, sesid) + "&nbsp;" +
self._createButton("end", "next", reqid, sesid))
vol_val = int(float(status.getVolume()) / 10.0 + 0.5)
volume = self._createVolume(vol_val, reqid, sesid)
return actions % ( buttons, volume, self._createTimeStr(status) )
def _setFullPage(self, status):
"""Renders the fullpage into the varialbe self.fullPage"""
counter = ""
if status.isPlaying():
counter = "countdown(" + str(status.timeLeft()) + ");"
self.fullPage = code % ( counter,
self._createActions(status).encode(self.encoding,'replace'),
os.environ['LOGNAME'],
self._createTable(status).encode(self.encoding,'replace') )
def _getMtime(self):
"""gets the mtime from the current.xml file, to check if the current.xml
has been modified or not"""
return os.stat(PLAYLISTFILE)[-2]
def _buildDoc(self):
"""Build the DOM-doc and calls the _extractTracks-Method"""
self.doc = minidom.parse(PLAYLISTFILE)
self._extractTracks()
def _extractTracks(self):
"""extracts all "item"-elements from the doc and creates an Track-object
to store the associated information"""
append = self.tracks.append
i = 1
for item in self.doc.getElementsByTagName("item"):
curTrack = Track()
for elem in FIELDS:
try:
value = item.getElementsByTagName(elem)[0].firstChild.nodeValue
except:
value = ' '
if elem == "Title":
value = value.strip()
if elem == "Length":
value = self._trackTimeStr(value);
if elem == "Rating":
value = self._ratingStars(value);
if elem == "TrackNo":
value = str(i)
i = i + 1
setattr(curTrack, elem, value)
append(curTrack)
def _createTable(self, status):
"""Returns the HTML-Table"""
tbl = tblhead
rows = self._createRows(status)
thead = "<tr id='trackheader'>" + "".join(['<td>%s</td>'%(i) for i in FIELDS]) + "</tr>"
rowsstr = "".join(rows)
return tbl%("tracks", "<thead>" + thead + "</thead><tbody>" + rowsstr + "</tbody>")
def _createRows(self, status):
"""Returns the table rows"""
retval = []
i = 1
reqid = "&reqid=" + str(status.reqid)
sesid = "&sesid=" + str(status.sesid)
curindex = status.getActiveIndex()
id = None
for track in self.tracks:
style = 'tr_one'
if i %2 == 0:
style = 'tr_two'
if curindex == (i-1):
id = "nowplaying"
retval.append(track.toRow(style=style, id=id, trackno=(i-1), reqid=reqid, sesid=sesid))
i = i+1
return retval
def sync(self):
"""Saves the current Amarok-Playlist to the current.xml file. Calls
amarok via dcop"""
os.system("dcop amarok playlist saveCurrentPlaylist")