-from httpclient import getPage
+from httpclient import getPage,unquote_plus
\r
from md5 import md5 # to encode password\r
from string import split, rstrip
class LastFMHandler:\r
def __init__(self):\r
pass\r
- def onConnectSuccessful(self,reason):\r
- pass\r
+ def onPlaylistLoaded(self,reason):
+ pass
+ def onConnectSuccessful(self,reason):
+ pass
def onConnectFailed(self,reason):\r
pass\r
def onCommandFailed(self,reason):\r
pass\r
def onTrackLoved(self,reason):\r
pass\r
- def onTrackBaned(self,reason):\r
+ def onTrackBanned(self,reason):\r
pass\r
def onGlobalTagsLoaded(self,tags):\r
pass\r
metadata = {}\r
info={}\r
cache_toptags= "/tmp/toptags"\r
+ playlist = None
\r
def __init__(self):\r
LastFMHandler.__init__(self)\r
self.subscriber = self.info["subscriber"]\r
self.framehack = self.info["base_path"]\r
self.state = True\r
- self.onConnectSuccessful("loggedin")\r
+ self.onConnectSuccessful("loggedin")
+ \r
else:\r
self.onConnectFailed("login failed")\r
\r
vars = split(str, "\n")\r
for v in vars:\r
x = split(rstrip(v), "=", 1)\r
- if len(x) == 2:\r
- res[x[0]] = x[1].encode("utf-8")\r
+ if len(x) == 2:
+ try:\r
+ res[x[0]] = x[1].encode("utf-8")
+ except UnicodeDecodeError:
+ res[x[0]] = "unicodeproblem"\r
elif x != [""]:\r
print "(urk?", x, ")"\r
return res\r
+
+ def loadPlaylist(self):
+ print "LOADING PLAYLIST"
+ if self.state is not True:
+ self.onCommandFailed("not logged in")
+ else:
+ getPage(self.info["base_url"],80
+ ,self.info["base_path"] + "/xspf.php?sk=" + self.info["session"]+"&discovery=0&desktop=1.3.1.1"
+ ,callback=self.loadPlaylistCB,errorback=self.onCommandFailed)
+
+ def loadPlaylistCB(self,xmlsource):
+ self.playlist = LastFMPlaylist(xmlsource)
+ self.onPlaylistLoaded("playlist loaded")
\r
def getPersonalURL(self,username,level=50):\r
return "lastfm://user/%s/recommended/32"%username\r
return "lastfm://group/%s"%self.metadata['artist'].replace(" ","%20")\r
else:\r
return "lastfm://group/%s"%artist.replace(" ","%20")\r
- \r
+ """\r
def getMetadata(self):\r
if self.state is not True:\r
self.onCommandFailed("not logged in")\r
lastfm_event_register.onMetadataChanged(self.metadata)\r
else:\r
self.onCommandFailed("Error while parsing Metadata")\r
-\r
+ """\r
def command(self, cmd,callback):\r
# commands = skip, love, ban, rtp, nortp\r
if self.state is not True:\r
return self.command("love",self.onTrackLovedCB)\r
\r
def ban(self):\r
- return self.command("ban",self.onTrackBanedCB)\r
+ return self.command("ban",self.onTrackBannedCB)\r
\r
- def skip(self):\r
+ def skip(self):
+ """unneeded"""\r
return self.command("skip",self.onTrackSkipedCB)\r
\r
def hexify(self,s):\r
else:\r
self.onCommandFailed("Server returned "+res["response"])\r
\r
+############
+class LastFMPlaylist:
+ """
+ this is the new way last.fm handles streams with metadata
+ """
+ DEFAULT_NAMESPACES = (None,)
+ DUBLIN_CORE = ('http://purl.org/dc/elements/1.1/',) #why do i need this?
+
+ name = "N/A"
+ creator = "N/A"
+ tracks = []
+ length = 0
+
+ def __init__(self,xmlsource):
+ self.xmldoc = parseString(xmlsource)
+ self.name = unquote_plus(self._get_txt( self.xmldoc, "title", "no playlistname" ))
+ self.creator =self._get_txt( self.xmldoc, "creator", "no playlistcreator" )
+ self.parseTracks()
+
+ def getTracks(self):
+ return self.tracks
+
+ def getTrack(self,tracknumber):
+ return self.tracks[tracknumber]
+
+ def parseTracks(self):
+ try:
+ self.tracks = []
+ for node in self._getElementsByTagName(self.xmldoc, 'track'):
+ nodex={}
+ nodex['station'] = self.name
+ nodex['location'] = self._get_txt( node, "location", "no location" )
+ nodex['title'] = self._get_txt( node, "title", "no title" )
+ nodex['id'] = self._get_txt( node, "id", "no id" )
+ nodex['album'] = self._get_txt( node, "album", "no album" )
+ nodex['creator'] = self._get_txt( node, "creator", "no creator" )
+ nodex['duration'] = int(self._get_txt( node, "duration", "0" ))
+ nodex['image'] = self._get_txt( node, "image", "no image" )
+ self.tracks.append(nodex)
+ self.length = len(self.tracks)
+ return True
+ except:
+ return False
+
+ def _getElementsByTagName( self, node, tagName, possibleNamespaces=DEFAULT_NAMESPACES ):
+ for namespace in possibleNamespaces:
+ children = node.getElementsByTagNameNS(namespace, tagName)
+ if len(children): return children
+ return []
+
+ def _node_data( self, node, tagName, possibleNamespaces=DEFAULT_NAMESPACES):
+ children = self._getElementsByTagName(node, tagName, possibleNamespaces)
+ node = len(children) and children[0] or None
+ return node and "".join([child.data.encode("utf-8") for child in node.childNodes]) or None
+
+ def _get_txt( self, node, tagName, default_txt="" ):
+ return self._node_data( node, tagName ) or self._node_data( node, tagName, self.DUBLIN_CORE ) or default_txt
from enigma import eServiceReference
from os import system
-
+from math import ceil
+import time
class StreamPlayer:
+ STATE_PLAYINGSTARTED = 0
+ STATE_STOP = 1
+ STATE_PLAYLISTENDS = 2
is_playing = False
+ trackstarttime = 0
+ currentplaylistitemnumber = 0
+ playlist = None
def __init__(self,session, args = 0):
print " init StreamPlayer"
self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
self.session.nav.event.append(self.__event)
self.onStateChanged = []
-
- def stateChanged(self):
+
+ def setPlaylist(self,playlist):
+ if self.playlist is not None:
+ self.currentplaylistitemnumber = 0
+ self.playlist = playlist
+
+ def stateChanged(self,reason):
for i in self.onStateChanged:
- i()
+ i(reason)
def __event(self, ev):
print "EVENT ==>",ev
- if ev ==6:
+ if ev == 6:
self.stop("got EVENT 6, GST stopped")
+ if ev == 4:
+ self.trackstarttime = time.time()
- def play(self,stream):
- print " start streaming %s" %stream
- if self.is_playing is True:
- self.stop()
- self.play(stream)
- else:
- if stream.startswith("/") is not True:
- print "playing remote stream",stream
- self.session.nav.stopService()
-# sref = eServiceReference(4097,0,stream )
-# sref = eServiceReference("4097:0:0:0:0:0:0:0:0:0:%s"%stream.replace(":",":"))
-# self.session.nav.playService(sref)
- self.targetfile = "/tmp/lastfm.mp3"
- system("mknod %s p" %self.targetfile)
- system("wget %s -O- > %s&" %(stream,self.targetfile))
- self.session.nav.playService(eServiceReference("4097:0:0:0:0:0:0:0:0:0:%s"%self.targetfile))
+ def getRemaining(self):
+ remaining = int((self.playlist.getTrack(self.currentplaylistitemnumber)["duration"]/1000) - (time.time() - self.trackstarttime))
+ minutes = int(remaining/60)
+ seconds = int(remaining-(minutes*60))
+ def shiftchars(integer,char):
+ if integer in range(0,10):
+ return char+str(integer)
else:
- print "playing local stream",stream
- esref = eServiceReference("4097:0:0:0:0:0:0:0:0:0:%s"%stream)
- self.session.nav.playService(esref)
- self.is_playing = True
- self.stateChanged()
+ return str(integer)
+ return "-%s:%s"%(shiftchars(minutes," "), shiftchars(seconds,"0"))
+
+ def play(self,tracknumber=False):
+ self.session.nav.stopService()
- def stop(self,text=""):
- if self.is_playing is True:
- print " stop streaming",text
+ if tracknumber is False:
+ self.currentplaylistitemnumber = 0
+ else:
+ self.currentplaylistitemnumber = tracknumber
+
+ track = self.playlist.getTrack(self.currentplaylistitemnumber)
+
+ if track['location'] != "no location":
+ print "playing item "+str(self.currentplaylistitemnumber) +"/"+str(self.playlist.length)+" with url ",track['location']
+ self.session.nav.stopService()
+ self.targetfile = "/tmp/lastfm.mp3"
+ system("mknod %s p" %self.targetfile)
+ system("wget %s -O- > %s&" %(track['location'],self.targetfile))
+ self.session.nav.playService(eServiceReference("4097:0:0:0:0:0:0:0:0:0:%s"%self.targetfile))
+ self.is_playing = True
+
+ def skip(self):
+ self.stop()
+
+ def stop(self,text="",force=False):
+ if force is False and self.playlist.length > 0 and (self.playlist.length-1) > self.currentplaylistitemnumber:
+ self.play(tracknumber=self.currentplaylistitemnumber+1)
+ self.stateChanged(self.STATE_PLAYINGSTARTED)
+ elif self.is_playing is True and force is True:
self.session.nav.stopService()
system("killall -9 wget")
system("rm %s" %self.targetfile)
self.session.nav.playService(self.oldService)
- self.is_playing = False
- self.stateChanged()
+ self.is_playing = False
+ self.stateChanged(self.STATE_STOP)
+
+ else:
+ self.stateChanged(self.STATE_PLAYLISTENDS)
+
def exit(self):
self.stop()
-
\ No newline at end of file
+
+ def getMetadata(self,key):
+ try:
+ track = self.playlist.getTrack(self.currentplaylistitemnumber)
+ return track[key]
+ except:
+ return "N/A"
\ No newline at end of file
config.plugins.LastFM.password = ConfigText("passwd",fixed_size=False)
config.plugins.LastFM.timeoutstatustext = ConfigInteger(3,limits = (0, 10))
config.plugins.LastFM.timeouttabselect = ConfigInteger(2,limits = (0, 10))
-config.plugins.LastFM.metadatarefreshinterval = ConfigInteger(5,limits = (0, 100))
+config.plugins.LastFM.metadatarefreshinterval = ConfigInteger(1,limits = (0, 100))
config.plugins.LastFM.recommendedlevel = ConfigInteger(3,limits = (0, 100))
config.plugins.LastFM.sendSubmissions = ConfigYesNo(default = False)
<widget name="album" position="0,40" size="70,30" valign=\"center\" halign=\"left\" zPosition=\"2\" foregroundColor=\"white\" font=\"Regular;18\" />
<widget name="track" position="0,80" size="70,30" valign=\"center\" halign=\"left\" zPosition=\"2\" foregroundColor=\"white\" font=\"Regular;18\" />
- <widget name="info_artist" position="70,0" size="344,30" valign=\"center\" halign=\"left\" zPosition=\"2\" foregroundColor=\"white\" font=\"Regular;18\" />
+ <widget name="info_artist" position="70,0" size="284,30" valign=\"center\" halign=\"left\" zPosition=\"2\" foregroundColor=\"white\" font=\"Regular;18\" />
+ <widget name="duration" position="354,0" size="60,30" valign=\"center\" halign=\"right\" zPosition=\"2\" foregroundColor=\"white\" font=\"Regular;18\" />
<widget name="info_album" position="70,40" size="344,30" valign=\"center\" halign=\"left\" zPosition=\"2\" foregroundColor=\"white\" font=\"Regular;18\" />
<widget name="info_track" position="70,80" size="344,30" valign=\"center\" halign=\"left\" zPosition=\"2\" foregroundColor=\"white\" font=\"Regular;18\" />
<widget name="info_cover" position="414,0" size="116,116" />
- <widget name="tablist" position="0,120" size="150,260" scrollbarMode="showOnDemand" />
+ <widget name="tablist" position="0,120" size="150,260" scrollbarMode="showOnDemand" backgroundColor="#55cccccc"/>
<widget name="streamlist" position="150,120" size="380,260" scrollbarMode="showOnDemand" />
<widget name="button_red" position="10,400" size="60,30" backgroundColor=\"red\" valign=\"center\" halign=\"center\" zPosition=\"2\" foregroundColor=\"white\" font=\"Regular;18\" />
LastFM.__init__(self)
self.session = session
self.streamplayer = StreamPlayer(session)
- self.streamplayer.onStateChanged.append(self.updateGUI)
+ self.streamplayer.onStateChanged.append(self.onStreamplayerStateChanged)
self.imageconverter = ImageConverter(116,116,self.setCoverArt)
Screen.__init__(self, session)
self.tablist.onSelectionChanged.append(self.action_TabChanged)
self["artist"] = Label(_("Artist")+":")
+ self["duration"] = Label("-00:00")
self["album"] = Label(_("Album")+":")
self["track"] = Label(_("Track")+":")
"ok": self.action_ok,
"back": self.action_exit,
"red": self.action_startstop,
- "green": self.skip,
+ "green": self.skipTrack,
"yellow": self.love,
- "blue": self.ban ,
+ "blue": self.banTrack ,
"historyNext": self.action_nextTab,
"historyBack": self.action_prevTab,
def initLastFM(self):
self.setInfoLabel("loggin into last.fm")
self.connect(config.plugins.LastFM.username.value,config.plugins.LastFM.password.value)
-
+
+ def onStreamplayerStateChanged(self,reason):
+ if reason is self.streamplayer.STATE_PLAYLISTENDS:
+ self.loadPlaylist()
+ else:
+ pass
def onConnectSuccessful(self,text):
self.setInfoLabel("login successful")
def onTrackLoved(self,reason):
self.setInfoLabel("Track loved")
- def onTrackBaned(self,reason):
+ def onTrackBanned(self,reason):
self.setInfoLabel("Track baned")
+
def onCommandFailed(self,reason):
self.setInfoLabel(reason)
self.buildMenuList(user)
def onStationChanged(self,reason):
- self.setInfoLabel(reason)
+ self.setInfoLabel(reason)
+ self.loadPlaylist()
def onMetadataLoaded(self,metadata):
self.updateGUI()
self.guiupdatetimer.start(config.plugins.LastFM.metadatarefreshinterval.value*1000)
+
+ def onPlaylistLoaded(self,reason):
+ self.streamplayer.setPlaylist(self.playlist)
+ self.streamplayer.play()
+ def skipTrack(self):
+ self.streamplayer.skip()
+ self.updateGUI()
+
+ def banTrack(self):
+ self.ban()
+ self.streamplayer.skip()
+ self.updateGUI()
+
def action_TabChanged(self):
self.tabchangetimer.stop()
self.tabchangetimer.start(config.plugins.LastFM.timeouttabselect.value*1000)
def guiupdatetimerFired(self):
- if self.streamplayer.is_playing:
- self.getMetadata()
+ self.updateGUI()
self.guiupdatetimer.start(config.plugins.LastFM.metadatarefreshinterval.value*1000)
def tabchangedtimerFired(self):
def startScreensaver(self):
self.screensavertimer.stop()
- self.session.openWithCallback(self.updateGUI, LastFMSaveScreen,self.metadata)
+ self.session.openWithCallback(self.updateGUI, LastFMSaveScreen,self.streamplayer)
def action_nextTab(self):
self.tablist.down()
def action_exit(self):
self.screensavertimer.stop()
self.guiupdatetimer.stop()
- self.streamplayer.stop()
+ self.streamplayer.stop(force=True)
self.streamplayer.onStateChanged=[]
self.close()
elif len(x) >1:
self.changeStation(x[1])
self.resetScreensaverTimer()
-
+
def action_startstop(self):
self.resetScreensaverTimer()
if self.streamplayer.is_playing:
- self.streamplayer.stop()
- self.metadata = {}
+ self.streamplayer.stop(force=True)
self.setInfoLabel("stream stopped")
-
else:
self.setInfoLabel("starting stream",timeout=True)
- if self.info.has_key("stream_url"):
- self.streamplayer.play(self.info["stream_url"])
- self.guiupdatetimer.start(config.plugins.LastFM.metadatarefreshinterval.value*1000)
+ self.loadPlaylist()
+ self.updateGUI() #forcing guiupdate, so we dont wait till guiupdatetimer fired
+ self.guiupdatetimer.start(config.plugins.LastFM.metadatarefreshinterval.value*1000)
def setInfoLabel(self,text,timeout=True):
self.infolabelcleartimer.stop()
self["infolabel"].setText("")
def updateGUI(self):
- print "updateGUI"
+ if self.streamplayer.is_playing is True:
+ self["duration"].setText(self.streamplayer.getRemaining())
+ else:
+ self["duration"].setText("00:00")
+
+
if self.streamplayer.is_playing is True:
self["button_red"].setText(_("stop"))
else:
if self.streamplayer.is_playing is not True or self.shown is not True:
return None
- if self.metadata.has_key("station"):
- self.setTitle("Last.FM: "+self.metadata["station"])
+ if self.streamplayer.is_playing is True:
+ self.setTitle("Last.FM: "+self.streamplayer.getMetadata("station"))
else:
self.setTitle("Last.FM")
- if self.metadata.has_key("artist"):
- self["info_artist"].setText(self.metadata["artist"])
+ if self.streamplayer.is_playing is True:
+ self["info_artist"].setText(self.streamplayer.getMetadata("creator"))
else:
self["info_artist"].setText("N/A")
- if self.metadata.has_key("album"):
- self["info_album"].setText(self.metadata["album"])
+ if self.streamplayer.is_playing is True:
+ self["info_album"].setText(self.streamplayer.getMetadata("album"))
else:
self["info_album"].setText("N/A")
- if self.metadata.has_key("track"):
- self["info_track"].setText(self.metadata["track"])
+ if self.streamplayer.is_playing is True:
+ self["info_track"].setText(self.streamplayer.getMetadata("title"))
else:
self["info_track"].setText("N/A")
- if self.metadata.has_key("albumcover_large") and config.plugins.LastFM.showcoverart.value:
- self.imageconverter.convert(self.metadata["albumcover_large"])
- elif self.metadata.has_key("albumcover_medium") and config.plugins.LastFM.showcoverart.value:
- self.imageconverter.convert(self.metadata["albumcover_medium"])
- elif self.metadata.has_key("albumcover_small") and config.plugins.LastFM.showcoverart.value:
- self.imageconverter.convert(self.metadata["albumcover_small"],self.setCoverArt)
+ if self.streamplayer.getMetadata("image").startswith("http") and config.plugins.LastFM.showcoverart.value:
+ self.imageconverter.convert(self.streamplayer.getMetadata("image"))
else:
self.setCoverArt()
x["stationurl"] = self.getLovedURL(config.plugins.LastFM.username.value)
tags.append(x)
- if self.metadata.has_key("artist"):
+ creator = self.streamplayer.getMetadata("creator")
+ if creator != "no creator" and creator != "N/A":
x= {}
- x["_display"] = "similar Tracks of current Artist"
- x["stationurl"] = self.getSimilarArtistsURL()
+ x["_display"] = "Tracks similar to "+self.streamplayer.getMetadata("creator")
+ x["stationurl"] = self.getSimilarArtistsURL(artist=creator)
tags.append(x)
x= {}
- x["_display"] = "Tracks liked by Fans of current Track"
- x["stationurl"] = self.getArtistsLikedByFans()
+ x["_display"] = "Tracks liked by Fans of "+self.streamplayer.getMetadata("creator")
+ x["stationurl"] = self.getArtistsLikedByFans(artist=creator)
tags.append(x)
x= {}
- x["_display"] = "Group of Artist of current Track"
- x["stationurl"] = self.getArtistGroup()
+ x["_display"] = "Group of "+self.streamplayer.getMetadata("creator")
+ x["stationurl"] = self.getArtistGroup(artist=creator)
tags.append(x)
self.buildMenuList(tags)
</screen>"""
noCoverArtPNG = "/usr/share/enigma2/no_coverArt.png"
coverartsize= [200,200]
- def __init__(self,session,initialMetadata):
+ lastcreator=""
+ def __init__(self,session,streamplayer):
self.skin = """<screen position="0,0" size="720,576" flags="wfNoBorder" title="LastFMSaveScreen" >
<widget name="cover" position="50,50" size="%i,%i" />
</screen>"""%(self.coverartsize[0],self.coverartsize[1])
Screen.__init__(self,session)
self.imageconverter = ImageConverter(self.coverartsize[0],self.coverartsize[1],self.setCoverArt)
self.session = session
- self.initialMetadata = initialMetadata
+ self.streamplayer = streamplayer
self["cover"] = MovingPixmap()
self["actions"] = ActionMap(["InfobarChannelSelection","WizardActions", "DirectionActions","MenuActions","ShortcutActions","GlobalActions","HelpActions"],
{
}, -1)
self.onLayoutFinish.append(self.update)
- self.onLayoutFinish.append(self.registerToMetadataUpdates)
-
+ self.updatetimer = eTimer()
+ self.updatetimer.timeout.get().append(self.update)
+ self.updatetimer.start(1000)
+
if config.plugins.LastFM.sreensaver.coverartanimation.value:
self.startmovingtimer = eTimer()
self.startmovingtimer.timeout.get().append(self.movePixmap)
pass
def action_exit(self):
- lastfm_event_register.removeOnMetadataChanged(self.update)
self.close()
def setCoverArt(self,pixmap=None):
if pixmap is None:
self["cover"].instance.setPixmapFromFile(self.noCoverArtPNG)
else:
- self["cover"].instance.setPixmap(pixmap.__deref__())
-
- def registerToMetadataUpdates(self):
- lastfm_event_register.addOnMetadataChanged(self.update)#added here, to make shure that is called after onLayoutFinished
-
+ self["cover"].instance.setPixmap(pixmap.__deref__())
- def update(self,metadata=None):
-
- if metadata is None:
- metadata = self.initialMetadata
-
- if config.plugins.LastFM.sreensaver.showcoverart.value is not True:
- pass#do nothing
- elif metadata.has_key("albumcover_large") and config.plugins.LastFM.showcoverart.value:
- self.imageconverter.convert(metadata["albumcover_large"])
- elif metadata.has_key("albumcover_medium") and config.plugins.LastFM.showcoverart.value:
- self.imageconverter.convert(metadata["albumcover_medium"])
- elif metadata.has_key("albumcover_small") and config.plugins.LastFM.showcoverart.value:
- self.imageconverter.convert(metadata["albumcover_small"],self.setCoverArt)
+ def update(self):
+ if self.streamplayer.getMetadata("creator") == self.lastcreator:
+ pass
else:
- self.setCoverArt()
-
+ self.lastcreator = self.streamplayer.getMetadata("creator")
+ if config.plugins.LastFM.sreensaver.showcoverart.value is not True:
+ pass#do nothing
+ elif self.streamplayer.getMetadata("image").startswith("http") and config.plugins.LastFM.showcoverart.value:
+ self.imageconverter.convert(self.streamplayer.getMetadata("image"))
+ else:
+ self.setCoverArt()
+ self.updatetimer.start(1000)
+
def movePixmap(self):
self.startmovingtimer.stop()
newX = randrange(720-self.coverartsize[0]-1)