use getInfo() to get track-number and track-count when using hardware-decoder instead...
[enigma2-plugins.git] / merlinmusicplayer / src / plugin.py
1 #
2 #  Merlin Music Player E2
3 #
4 #  $Id$
5 #
6 #  Coded by Dr.Best (c) 2010
7 #  Support: www.dreambox-tools.info
8 #
9 #  This plugin is licensed under the Creative Commons 
10 #  Attribution-NonCommercial-ShareAlike 3.0 Unported 
11 #  License. To view a copy of this license, visit
12 #  http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative
13 #  Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
14 #
15 #  Alternatively, this plugin may be distributed and executed on hardware which
16 #  is licensed by Dream Multimedia GmbH.
17
18 #  This plugin is NOT free software. It is open source, you are allowed to
19 #  modify it (if you keep the license), but it may not be commercially 
20 #  distributed other than under the conditions noted above.
21 #
22
23 from Plugins.Plugin import PluginDescriptor
24 from Screens.Screen import Screen
25 from Components.ActionMap import ActionMap, NumberActionMap
26 from Components.Label import Label
27 from enigma import RT_VALIGN_CENTER, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_HALIGN_CENTER, gFont, eListbox,ePoint, eListboxPythonMultiContent
28 # merlin mp3 player
29 import merlinmp3player
30 ENIGMA_MERLINPLAYER_ID = 0x1014
31 from Components.FileList import FileList
32 from enigma import eServiceReference, eTimer
33 from os import path as os_path, mkdir as os_mkdir, listdir as os_listdir, walk as os_walk, access as os_access, W_OK as os_W_OK
34 from Components.ProgressBar import ProgressBar
35 from twisted.internet import reactor, defer
36 from twisted.web import client
37 from twisted.web.client import HTTPClientFactory, downloadPage
38 from enigma import getDesktop
39 from Screens.MessageBox import MessageBox
40 from Components.GUIComponent import GUIComponent
41 from enigma import ePicLoad
42 from xml.etree.cElementTree import fromstring as cet_fromstring
43 from urllib import quote
44 from Components.ScrollLabel import ScrollLabel
45 from Components.AVSwitch import AVSwitch
46 from Tools.Directories import fileExists, resolveFilename, SCOPE_CURRENT_SKIN
47 from Tools.LoadPixmap import LoadPixmap
48 from Components.Pixmap import Pixmap, MultiPixmap
49 from Components.ServicePosition import ServicePositionGauge
50 from Screens.InfoBarGenerics import  InfoBarSeek, InfoBarNotifications
51 from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
52 from enigma import iPlayableService, iServiceInformation
53 from Components.Sources.StaticText import StaticText
54 from Screens.ChoiceBox import ChoiceBox
55 from Screens.VirtualKeyBoard import VirtualKeyBoard
56 from Tools.BoundFunction import boundFunction
57 from sqlite3 import dbapi2 as sqlite
58 from mutagen.flac import FLAC
59 from mutagen.mp3 import MP3
60 from mutagen.id3 import ID3, USLT
61 from mutagen.easyid3 import EasyID3
62 from mutagen.easymp4 import EasyMP4
63 from mutagen.oggvorbis import OggVorbis
64 from datetime import timedelta as datetime_timedelta
65 from time import time
66 from random import shuffle, randrange
67 from Components.config import config, ConfigSubsection, ConfigDirectory, ConfigYesNo, ConfigInteger, getConfigListEntry, configfile
68 from Components.ConfigList import ConfigListScreen
69 from Tools.HardwareInfo import HardwareInfo
70
71 from Components.SystemInfo import SystemInfo
72 from enigma import eServiceCenter, getBestPlayableServiceReference
73 from Components.VideoWindow import VideoWindow
74 from ServiceReference import ServiceReference
75 from Screens.EpgSelection import EPGSelection
76 from Screens.EventView import  EventViewEPGSelect
77 from enigma import ePoint, eEPGCache
78 from Screens.InfoBarGenerics import NumberZap
79
80
81 START_MERLIN_PLAYER_SCREEN_TIMER_VALUE = 7000
82
83 config.plugins.merlinmusicplayer = ConfigSubsection()
84 config.plugins.merlinmusicplayer.hardwaredecoder = ConfigYesNo(default = True)
85 config.plugins.merlinmusicplayer.startlastsonglist = ConfigYesNo(default = True)
86 config.plugins.merlinmusicplayer.lastsonglistindex = ConfigInteger(-1)
87 config.plugins.merlinmusicplayer.databasepath = ConfigDirectory(default = "/hdd/")
88 config.plugins.merlinmusicplayer.usegoogleimage = ConfigYesNo(default = True)
89 config.plugins.merlinmusicplayer.googleimagepath = ConfigDirectory(default = "/hdd/")
90 config.plugins.merlinmusicplayer.usescreensaver = ConfigYesNo(default = True)
91 config.plugins.merlinmusicplayer.screensaverwait = ConfigInteger(1,limits = (1, 60))
92 config.plugins.merlinmusicplayer.idreamextendedpluginlist = ConfigYesNo(default = True)
93 config.plugins.merlinmusicplayer.merlinmusicplayerextendedpluginlist = ConfigYesNo(default = True)
94 config.plugins.merlinmusicplayer.defaultfilebrowserpath = ConfigDirectory(default = "/hdd/")
95 config.plugins.merlinmusicplayer.rememberlastfilebrowserpath = ConfigYesNo(default = True)
96 config.plugins.merlinmusicplayer.idreammainmenu = ConfigYesNo(default = False)
97 config.plugins.merlinmusicplayer.merlinmusicplayermainmenu = ConfigYesNo(default = False)
98
99 from enigma import ePythonMessagePump
100 from threading import Thread, Lock
101 from timer import TimerEntry
102
103 class ThreadQueue:
104         def __init__(self):
105                 self.__list = [ ]
106                 self.__lock = Lock()
107
108         def push(self, val):
109                 lock = self.__lock
110                 lock.acquire()
111                 self.__list.append(val)
112                 lock.release()
113
114         def pop(self):
115                 lock = self.__lock
116                 lock.acquire()
117                 ret = self.__list.pop()
118                 lock.release()
119                 return ret
120
121 THREAD_WORKING = 1
122 THREAD_FINISHED = 2
123
124 class PathToDatabase(Thread):
125         def __init__(self):
126                 Thread.__init__(self)
127                 self.__running = False
128                 self.__cancel = False
129                 self.__path = None
130                 self.__messages = ThreadQueue()
131                 self.__messagePump = ePythonMessagePump()
132
133         def __getMessagePump(self):
134                 return self.__messagePump
135
136         def __getMessageQueue(self):
137                 return self.__messages
138
139         def __getRunning(self):
140                 return self.__running
141
142         def Cancel(self):
143                 self.__cancel = True
144
145         MessagePump = property(__getMessagePump)
146         Message = property(__getMessageQueue)
147         isRunning = property(__getRunning)
148
149         def Start(self, path):
150                 if not self.__running:
151                         self.__path = path
152                         self.start()
153
154         def run(self):
155                 mp = self.__messagePump
156                 self.__running = True
157                 self.__cancel = False
158                 if self.__path:
159                         connection = OpenDatabase()
160                         if connection is not None:
161                                 connection.text_factory = str
162                                 cursor = connection.cursor()
163                                 counter = 0
164                                 checkTime = 0
165                                 for root, subFolders, files in os_walk(self.__path):
166                                         if self.__cancel:
167                                                 break
168                                         for filename in files:
169                                                 if self.__cancel:
170                                                         break
171                                                 cursor.execute('SELECT song_id FROM Songs WHERE filename = "%s";' % os_path.join(root,filename))
172                                                 row = cursor.fetchone()
173                                                 if row is None:
174                                                         audio, isAudio, title, genre,artist,album,tracknr,track,date,length,bitrate = getID3Tags(root,filename)
175                                                         if  audio:      
176                                                                 # 1. Artist
177                                                                 artistID = -1
178                                                                 cursor.execute('SELECT artist_id FROM Artists WHERE artist = "%s";' % (artist.replace('"','""')))
179
180                                                                 row = cursor.fetchone()
181                                                                 if row is None:
182                                                                                 cursor.execute('INSERT INTO Artists (artist) VALUES("%s");' % (artist.replace('"','""')))
183                                                                                 artistID = cursor.lastrowid
184                                                                 else:
185                                                                                 artistID = row[0]
186                                                                 # 2. Album
187                                                                 albumID = -1
188                                                                 cursor.execute('SELECT album_id FROM Album WHERE album_text = "%s";' % (album.replace('"','""')))
189                                                                 row = cursor.fetchone()
190                                                                 if row is None:
191                                                                                 cursor.execute('INSERT INTO Album (album_text) VALUES("%s");' % (album.replace('"','""')))
192                                                                                 albumID = cursor.lastrowid
193                                                                 else:
194                                                                                 albumID = row[0]
195
196                                                                 # 3. Genre
197                                                                 genreID = -1            
198                                                                 cursor.execute('SELECT genre_id FROM Genre WHERE genre_text = "%s";' % (genre.replace('"','""')))
199                                                                 row = cursor.fetchone()
200                                                                 if row is None:
201                                                                                 cursor.execute('INSERT INTO Genre (genre_text) VALUES("%s");' % (genre.replace('"','""')))
202                                                                                 genreID = cursor.lastrowid
203                                                                 else:
204                                                                                 genreID = row[0]
205                                                         
206                                                                 # 4. Songs
207                                                                 try:
208                                                                         cursor.execute("INSERT INTO Songs (filename,title,artist_id,album_id,genre_id,tracknumber, bitrate, length, track, date) VALUES(?,?,?,?,?,?,?,?,?,?);" , (os_path.join(root,filename),title,artistID,albumID,genreID, tracknr, bitrate, length, track, date))
209                                                                         self.__messages.push((THREAD_WORKING, _("%s\n added to database") % os_path.join(root,filename)))
210                                                                         mp.send(0)
211                                                                         counter +=1
212                                                                 except sqlite.IntegrityError:
213                                                                         self.__messages.push((THREAD_WORKING, _("%s\n already exists in database!") % os_path.join(root,filename)))
214                                                                         mp.send(0)
215                                                                 audio = None
216                                                 elif time() - checkTime >= 0.1: # update interval for gui
217                                                         self.__messages.push((THREAD_WORKING, _("%s\n already exists in database!") % os_path.join(root,filename)))
218                                                         mp.send(0)
219                                                         checkTime = time()
220                                                 
221                                 if not self.__cancel:
222                                         connection.commit()
223                                 cursor.close()  
224                                 connection.close()
225                                 if self.__cancel:
226                                         self.__messages.push((THREAD_FINISHED, _("Process aborted.\n 0 files added to database!\nPress OK to close.") ))
227                                 else:   
228                                         self.__messages.push((THREAD_FINISHED, _("%d files added to database!\nPress OK to close." % counter)))
229                         else:
230                                 self.__messages.push((THREAD_FINISHED, _("Error!\nCan not open database!\nCheck if save folder is correct and writeable!\nPress OK to close.") ))
231                         mp.send(0)
232                         self.__running = False
233                         Thread.__init__(self)
234
235 pathToDatabase = PathToDatabase()
236
237
238 class iDreamAddToDatabase(Screen):
239         skin = """<screen name="iDreamAddToDatabase" position="center,center" size="560,320" title="Add music files to iDream database">
240                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
241                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
242                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
243                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
244                         <widget name="output" position="10,10" size="540,300" valign="center" halign="center" font="Regular;22" />
245                         <widget render="Label" source="key_red" position="0,0" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
246                         <widget render="Label" source="key_green" position="140,0" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
247                 </screen>"""
248         def __init__(self, session, initDir):
249                 Screen.__init__(self, session)
250                 self["actions"] = ActionMap(["WizardActions", "ColorActions"],
251                 {
252                         "back": self.cancel,
253                         "green": self.green,
254                         "red": self.cancel,
255                         "ok": self.green,
256                         
257                 }, -1)
258                 self["key_red"] = StaticText(_("Cancel"))
259                 self["key_green"] = StaticText("Close")
260                 self["output"] = Label()
261                 self.onClose.append(self.__onClose)
262                 pathToDatabase.MessagePump.recv_msg.get().append(self.gotThreadMsg)     
263                 if not pathToDatabase.isRunning and initDir:
264                         pathToDatabase.Start(initDir)
265
266         def gotThreadMsg(self, msg):
267                 msg = pathToDatabase.Message.pop()
268                 self["output"].setText(msg[1])
269                 if msg[0] == THREAD_FINISHED:
270                         self["key_red"].setText("")
271         
272         def green(self):
273                 self.close()
274         
275         def cancel(self):
276                 if pathToDatabase.isRunning:
277                         pathToDatabase.Cancel()
278
279         def __onClose(self):
280                 pathToDatabase.MessagePump.recv_msg.get().remove(self.gotThreadMsg)     
281                 
282
283 class myHTTPClientFactory(HTTPClientFactory):
284         def __init__(self, url, method='GET', postdata=None, headers=None,
285         agent="SHOUTcast", timeout=0, cookies=None,
286         followRedirect=1, lastModified=None, etag=None):
287                 HTTPClientFactory.__init__(self, url, method=method, postdata=postdata,
288                 headers=headers, agent=agent, timeout=timeout, cookies=cookies,followRedirect=followRedirect)
289
290 def sendUrlCommand(url, contextFactory=None, timeout=60, *args, **kwargs):
291         scheme, host, port, path = client._parse(url)
292         factory = myHTTPClientFactory(url, *args, **kwargs)
293         reactor.connectTCP(host, port, factory, timeout=timeout)
294         return factory.deferred
295
296
297 class MethodArguments:
298         def __init__(self, method = None, arguments = None):
299                 self.method = method
300                 self.arguments = arguments
301
302 class CacheList:
303         def __init__(self, cache = True, index = 0, listview = [], headertext = "", methodarguments = None):
304                 self.cache = cache
305                 self.index = index
306                 self.listview = listview
307                 self.headertext = headertext
308                 self.methodarguments = methodarguments
309
310 class Item:
311         def __init__(self, text = "", mode = 0, id = -1, navigator = False, artistID = 0, albumID = 0, title = "", artist = "", filename = "", bitrate = None, length = "", genre = "", track = "", date = "", album = "", playlistID = 0,  genreID = 0, songID = 0, join = True, PTS = None):
312                 self.text = text
313                 self.mode = mode
314                 self.navigator = navigator
315                 self.artistID = artistID
316                 self.albumID = albumID
317                 self.title = title
318                 self.artist = artist
319                 self.filename = filename
320                 if bitrate is not None:
321                         if join:
322                                 self.bitrate = "%d Kbps" % bitrate
323                         else:
324                                 self.bitrate = bitrate
325                 else:
326                         self.bitrate = ""
327                 self.length = length
328                 self.genre = genre
329                 if track is not None:
330                         self.track = "Track %s" % track
331                 else:
332                         self.track = ""
333                 if date is not None:
334                         if join:
335                                 self.date = " (%s)" % date
336                         else:
337                                 self.date = date
338                 else:
339                         self.date = ""
340                 self.album = album
341                 self.playlistID = playlistID
342                 self.genreID = genreID
343                 self.songID = songID
344                 self.PTS = PTS
345
346
347 def OpenDatabase():
348                 connectstring = os_path.join(config.plugins.merlinmusicplayer.databasepath.value ,"iDream.db")
349                 db_exists = False
350                 if os_path.exists(connectstring):
351                         db_exists = True
352                 try:
353                         connection = sqlite.connect(connectstring)
354                         if not os_access(connectstring, os_W_OK):
355                                 print "[MerlinMusicPlayer] Error: database file needs to be writable, can not open %s for writing..." % connectstring
356                                 connection.close()
357                                 return None
358                 except:
359                         print "[MerlinMusicPlayer] unable to open database file: %s" % connectstring
360                         return None
361                 if not db_exists :
362                                 connection.execute('CREATE TABLE IF NOT EXISTS Songs (song_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, filename TEXT NOT NULL UNIQUE, title TEXT, artist_id INTEGER, album_id INTEGER, genre_id INTEGER, tracknumber INTEGER, bitrate INTEGER, length TEXT, track TEXT, date TEXT, lyrics TEXT);')
363                                 connection.execute('CREATE TABLE IF NOT EXISTS Artists (artist_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, artist TEXT NOT NULL UNIQUE);')
364                                 connection.execute('CREATE TABLE IF NOT EXISTS Album (album_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, album_text TEXT NOT NULL UNIQUE);')
365                                 connection.execute('CREATE TABLE IF NOT EXISTS Genre (genre_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, genre_text TEXT NOT NULL UNIQUE);')
366                                 connection.execute('CREATE TABLE IF NOT EXISTS Playlists (playlist_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, playlist_text TEXT NOT NULL);')
367                                 connection.execute('CREATE TABLE IF NOT EXISTS Playlist_Songs (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, playlist_id INTEGER NOT NULL, song_id INTEGER NOT NULL);')
368                                 connection.execute('CREATE TABLE IF NOT EXISTS CurrentSongList (ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, song_id INTEGER, filename TEXT NOT NULL, title TEXT, artist TEXT, album TEXT, genre TEXT, bitrate TEXT, length TEXT, track TEXT, date TEXT, PTS INTEGER);')
369                 return connection
370
371 def getEncodedString(value):
372         returnValue = ""
373         try:
374                 returnValue = value.encode("utf-8", 'ignore')
375         except UnicodeDecodeError:
376                 try:
377                         returnValue = value.encode("iso8859-1", 'ignore')
378                 except UnicodeDecodeError:
379                         try:
380                                 returnValue = value.decode("cp1252").encode("utf-8")
381                         except UnicodeDecodeError:
382                                 returnValue = "n/a"
383         return returnValue
384
385 def getID3Tags(root,filename):
386         audio = None
387         isFlac = False
388         isAudio = True
389         title = ""
390         genre = ""
391         artist = ""
392         album = ""
393         tracknr = -1
394         track = None
395         date = None
396         length = ""
397         bitrate = None
398         if filename.lower().endswith(".mp3"):
399                 try: audio = MP3(os_path.join(root,filename), ID3 = EasyID3)
400                 except: audio = None
401         elif filename.lower().endswith(".flac"):
402                 try: 
403                         audio = FLAC(os_path.join(root,filename))
404                         isFlac = True
405                 except: audio = None
406         elif filename.lower().endswith(".m4a"):
407                 try: audio = EasyMP4(os_path.join(root,filename))
408                 except: audio = None
409         elif filename.lower().endswith(".ogg"):
410                 try: audio = OggVorbis(os_path.join(root,filename))
411                 except: audio = None
412         else:
413                 isAudio = False
414         if audio:
415                 title = getEncodedString(audio.get('title', [filename])[0])
416                 try:
417                         # list index out of range workaround
418                         genre = getEncodedString(audio.get('genre', ['n/a'])[0])
419                 except:
420                         genre = "n/a"
421                 artist = getEncodedString(audio.get('artist', ['n/a'])[0])
422                 album = getEncodedString(audio.get('album', ['n/a'])[0])
423                 try:
424                         tracknr = int(audio.get('tracknumber', ['-1'])[0].split("/")[0])
425                 except:
426                         tracknr = -1
427                 track = getEncodedString(audio.get('tracknumber', ['n/a'])[0])
428                 date = getEncodedString(audio.get('date', ['n/a'])[0])
429                 try:
430                         length = str(datetime_timedelta(seconds=int(audio.info.length))).encode("utf-8", 'ignore')
431                 except:
432                         length = -1
433                 if not isFlac:
434                         bitrate = audio.info.bitrate / 1000
435                 else:
436                         bitrate = None
437         else:
438                 if isAudio:
439                         title = os_path.splitext(os_path.basename(filename))[0]
440                         genre = "n/a"
441                         artist = "n/a"
442                         album = "n/a"
443                         tracknr = -1
444                         track = None
445                         date = None
446                         length = ""
447                         bitrate = None
448
449         return audio, isAudio, title, genre ,artist, album, tracknr, track, date, length, bitrate
450         
451 class MerlinMusicPlayerScreenSaver(Screen):
452
453         sz_w = getDesktop(0).size().width()
454         if sz_w == 1280:
455                 skin = """
456                         <screen name="MerlinMusicPlayerScreenSaver" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="MerlinMusicPlayerScreenSaver">
457                         <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="200,77" size="238,238" transparent="1" alphatest="blend" />
458                         <widget name="display" position="200,315" size="1280,24" zPosition="1" transparent="1" font="Regular;20" foregroundColor="#fcc000" />
459                         </screen>"""
460         elif sz_w == 1024:
461                 skin = """
462                         <screen name="MerlinMusicPlayerScreenSaver" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="MerlinMusicPlayerScreenSaver">
463                         <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="200,77" size="238,238" transparent="1" alphatest="blend" />
464                         <widget name="display" position="200,315" size="1024,24" zPosition="1" transparent="1" font="Regular;20" foregroundColor="#fcc000" />
465                         </screen>"""
466
467         else:
468                 skin = """
469                         <screen name="MerlinMusicPlayerScreenSaver" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="MerlinMusicPlayerScreenSaver">
470                         <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="200,77" size="238,238" transparent="1" alphatest="blend" />
471                         <widget name="display" position="200,315" size="720,24" zPosition="1" transparent="1" font="Regular;20" foregroundColor="#fcc000" />
472                         </screen>"""
473                 
474         
475         def __init__(self, session):
476                 self.session = session
477                 Screen.__init__(self, session)
478                 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "ColorActions", "EventViewActions"],
479                 {
480                         "back": self.close,
481                         "right": self.close,
482                         "left": self.close,
483                         "up": self.close,
484                         "down": self.close,
485                         "ok": self.close,
486                         "pageUp": self.close,
487                         "pageDown": self.close,
488                         "yellow": self.close,
489                         "blue": self.close,
490                         "red": self.close,
491                         "green": self.close,
492                         "right": self.close,
493                         "left": self.close,
494                         "prevBouquet": self.close,
495                         "nextBouquet": self.close,
496                         "info": self.close,
497
498                 }, -1)
499                 self["coverArt"] = MerlinMediaPixmap()
500                 self.coverMoveTimer = eTimer()
501                 self.coverMoveTimer.timeout.get().append(self.moveCoverArt)
502                 self.coverMoveTimer.start(1)
503                 self["display"] = Label()
504                 self.onClose.append(self.__onClose)
505                 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
506         
507         def sleepTimerEntryOnStateChange(self, timer):
508                 if timer.state == TimerEntry.StateEnded:
509                         self.close()
510
511         def __onClose(self):
512                 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
513
514         def updateDisplayText(self, text):
515                 self["display"].setText(text)
516
517         def updateLCD(self, text, line):
518                 self.summaries.setText(text,line)
519
520         def updateCover(self, filename = None, modus = 0):
521                 print "[MerlinMusicPlayerScreenSaver] updating coverart with filename = %s and modus = %d" % (filename, modus)
522                 if modus == 0:
523                         if filename:
524                                 self["coverArt"].showCoverFromFile(filename)
525                         else:
526                                 self["coverArt"].showDefaultCover()
527                 elif modus == 1:
528                         self["coverArt"].showDefaultCover()
529                 elif modus == 2:
530                         self["coverArt"].embeddedCoverArt()
531                 elif modus == 3:
532                         self["coverArt"].updateCoverArt(filename)
533                 elif modus == 4:
534                         self["coverArt"].showCoverFromFile(filename)
535
536         def moveCoverArt(self):
537                 x = randrange(getDesktop(0).size().width()-238)
538                 y = randrange(getDesktop(0).size().height()-238-28)
539                 self["coverArt"].move(ePoint(x,y))
540                 self["display"].move(ePoint(x,y+240))
541                 self.coverMoveTimer.start(15000)
542
543         def createSummary(self):
544                 return MerlinMusicPlayerLCDScreen
545
546
547 class MerlinMusicPlayerTV(MerlinMusicPlayerScreenSaver):
548
549         w = getDesktop(0).size().width()
550         h = getDesktop(0).size().height()
551         if w == 1280:
552                 cy = 606
553         else:
554                 cy = 462
555         dx = 135
556         cx = 66
557         dy = cy + 20
558         dw = w - dx - cx
559
560         skin = """
561                 <screen backgroundColor="transparent" flags="wfNoBorder" position="0,0" size="%d,%d" title="MerlinMusicPlayerTV">
562                         <widget backgroundColor="transparent" name="video" position="0,0" size="%d,%d" zPosition="1"/>
563                         <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="%d,%d" size="64,64" transparent="1" alphatest="blend" zPosition="2" />
564                         <widget name="display" position="%d,%d" size="%d,24" zPosition="2" backgroundColor="#33000000" font="Regular;20" foregroundColor="#fcc000" />           
565                 </screen>""" % (w,h,w,h,cx,cy,dx,dy,dw)
566
567
568         def __init__(self, session, currentService, servicelist):
569                 self.session = session
570                 Screen.__init__(self, session)
571                 self.onClose.append(self.__onClose)
572                 self["actions"] = ActionMap(["OkCancelActions", "DirectionActions", "ChannelSelectBaseActions", "ChannelSelectEPGActions"], 
573                 {
574                         "cancel": self.close,
575                         "ok": self.showHide,
576                         "right": self.nextService,
577                         "left": self.prevService,
578                         "nextBouquet": self.nextBouquet,
579                         "prevBouquet": self.prevBouquet,
580                         "showEPGList": self.openEventView,
581
582                 }, -1)
583                 self["actions2"] = NumberActionMap(["NumberActions"],
584                 {
585                         "1": self.keyNumberGlobal,
586                         "2": self.keyNumberGlobal,
587                         "3": self.keyNumberGlobal,
588                         "4": self.keyNumberGlobal,
589                         "5": self.keyNumberGlobal,
590                         "6": self.keyNumberGlobal,
591                         "7": self.keyNumberGlobal,
592                         "8": self.keyNumberGlobal,
593                         "9": self.keyNumberGlobal,
594                 }, -1)
595                 self.epgcache = eEPGCache.getInstance()
596                 self.servicelist = servicelist
597                 self["coverArt"] = MerlinMediaPixmap()
598                 self["display"] = Label()
599                 self["video"] = VideoWindow(fb_width = getDesktop(0).size().width(), fb_height = getDesktop(0).size().height())
600                 if self.servicelist is None:
601                         self.playService(currentService)
602                 else:
603                         current = ServiceReference(self.servicelist.getCurrentSelection())
604                         self.playService(current.ref)
605
606                 self.showHideTimer = eTimer()
607                 self.showHideTimer.timeout.get().append(self.showHideTimerTimeout)
608                 self.idx = config.usage.infobar_timeout.index
609                 if self.idx:
610                         self.showHideTimer.start(self.idx * 1000)
611                 self.displayShown = True
612                 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
613         
614         def sleepTimerEntryOnStateChange(self, timer):
615                 if timer.state == TimerEntry.StateEnded:
616                         self.close()
617
618         def showHide(self):
619                 if self.displayShown:
620                         if self.showHideTimer.isActive():
621                                 self.showHideTimer.stop()
622                         self["coverArt"].hide()
623                         self["display"].hide()
624                 else:
625                         self["coverArt"].show()
626                         self["display"].show()
627                         if self.idx:
628                                 self.showHideTimer.start(self.idx * 1000)
629                 self.displayShown = not self.displayShown
630
631         def showHideTimerTimeout(self):
632                 self.showHide()
633
634         def updateDisplayText(self, text):
635                 if self.showHideTimer.isActive():
636                         self.showHideTimer.stop()
637                 self["display"].setText(text)
638                 self.displayShown = False
639                 self.showHide()
640
641 # Source Code taken from Virtual(Pip)Zap :-)
642
643         # switch with numbers
644         def keyNumberGlobal(self, number):
645                 self.session.openWithCallback(self.numberEntered, NumberZap, number)
646
647         def numberEntered(self, retval):
648                 if retval > 0:
649                         self.zapToNumber(retval)
650
651         def searchNumberHelper(self, serviceHandler, num, bouquet):
652                 servicelist = serviceHandler.list(bouquet)
653                 if not servicelist is None:
654                         while num:
655                                 serviceIterator = servicelist.getNext()
656                                 if not serviceIterator.valid(): #check end of list
657                                         break
658                                 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
659                                 if playable:
660                                         num -= 1;
661                         if not num: #found service with searched number ?
662                                 return serviceIterator, 0
663                 return None, num
664
665         def zapToNumber(self, number):
666                 bouquet = self.servicelist.bouquet_root
667                 service = None
668                 serviceHandler = eServiceCenter.getInstance()
669                 bouquetlist = serviceHandler.list(bouquet)
670                 if not bouquetlist is None:
671                         while number:
672                                 bouquet = bouquetlist.getNext()
673                                 if not bouquet.valid(): #check end of list
674                                         break
675                                 if bouquet.flags & eServiceReference.isDirectory:
676                                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
677                 if not service is None:
678                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
679                                 self.servicelist.clearPath()
680                                 if self.servicelist.bouquet_root != bouquet:
681                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
682                                 self.servicelist.enterPath(bouquet)
683                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
684                 # update infos, no matter if service is none or not
685                 current = ServiceReference(self.servicelist.getCurrentSelection())
686                 self.playService(current.ref)
687
688         def nextService(self):
689                 if self.servicelist is not None:
690                         # get next service
691                         if self.servicelist.inBouquet():
692                                 prev = self.servicelist.getCurrentSelection()
693                                 if prev:
694                                         prev = prev.toString()
695                                         while True:
696                                                 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
697                                                         self.servicelist.nextBouquet()
698                                                 else:
699                                                         self.servicelist.moveDown()
700                                                 cur = self.servicelist.getCurrentSelection()
701                                                 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
702                                                         break
703                         else:
704                                 self.servicelist.moveDown()
705                         if self.isPlayable():
706                                 current = ServiceReference(self.servicelist.getCurrentSelection())
707                                 self.playService(current.ref)
708                         else:
709                                 self.nextService()
710
711         def prevService(self):
712                 if self.servicelist is not None:
713                         # get previous service
714                         if self.servicelist.inBouquet():
715                                 prev = self.servicelist.getCurrentSelection()
716                                 if prev:
717                                         prev = prev.toString()
718                                         while True:
719                                                 if config.usage.quickzap_bouquet_change.value:
720                                                         if self.servicelist.atBegin():
721                                                                 self.servicelist.prevBouquet()
722                                                 self.servicelist.moveUp()
723                                                 cur = self.servicelist.getCurrentSelection()
724                                                 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
725                                                         break
726                         else:
727                                 self.servicelist.moveUp()
728                         if self.isPlayable():
729                                 current = ServiceReference(self.servicelist.getCurrentSelection())
730                                 self.playService(current.ref)
731                         else:
732                                 self.prevService()
733
734         def isPlayable(self):
735                 # check if service is playable
736                 current = ServiceReference(self.servicelist.getCurrentSelection())
737                 return not (current.ref.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
738
739
740         def nextBouquet(self):
741                 if self.servicelist is not None:
742                         # next bouquet with first service
743                         if config.usage.multibouquet.value:
744                                 self.servicelist.nextBouquet()
745                                 current = ServiceReference(self.servicelist.getCurrentSelection())
746                                 self.playService(current.ref)
747
748         def prevBouquet(self):
749                 if self.servicelist is not None:
750                         # previous bouquet with first service
751                         if config.usage.multibouquet.value:
752                                 self.servicelist.prevBouquet()
753                                 current = ServiceReference(self.servicelist.getCurrentSelection())
754                                 self.playService(current.ref)
755
756         def openSingleServiceEPG(self):
757                 # show EPGList
758                 current = ServiceReference(self.servicelist.getCurrentSelection())
759                 self.session.open(EPGSelection, current.ref)
760
761         def openEventView(self):
762                 # show EPG Event
763                 epglist = [ ]
764                 self.epglist = epglist
765                 service = ServiceReference(self.servicelist.getCurrentSelection())
766                 ref = service.ref
767                 evt = self.epgcache.lookupEventTime(ref, -1)
768                 if evt:
769                         epglist.append(evt)
770                 evt = self.epgcache.lookupEventTime(ref, -1, 1)
771                 if evt:
772                         epglist.append(evt)
773                 if epglist:
774                         self.session.open(EventViewEPGSelect, epglist[0], service, self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
775
776         def eventViewCallback(self, setEvent, setService, val):
777                 epglist = self.epglist
778                 if len(epglist) > 1:
779                         tmp = epglist[0]
780                         epglist[0] = epglist[1]
781                         epglist[1] = tmp
782                         setEvent(epglist[0])
783
784         def openMultiServiceEPG(self):
785                 # not supported
786                 pass
787
788         def openSimilarList(self, eventid, refstr):
789                 self.session.open(EPGSelection, refstr, None, eventid)
790
791         def playService(self, service):
792                 if service and (service.flags & eServiceReference.isGroup):
793                         ref = getBestPlayableServiceReference(service, eServiceReference())
794                 else:
795                         ref = service
796                 self.pipservice = eServiceCenter.getInstance().play(ref)
797                 if self.pipservice and not self.pipservice.setTarget(1):
798                         self.pipservice.start()
799
800         def __onClose(self):
801                 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
802                 self.pipservice = None
803                 if self.showHideTimer.isActive():
804                         self.showHideTimer.stop()
805
806
807
808 class MerlinMusicPlayerScreen(Screen, InfoBarBase, InfoBarSeek, InfoBarNotifications):
809         
810         sz_w = getDesktop(0).size().width()
811         if sz_w == 1280:
812                 skin = """
813                         <screen name="MerlinMusicPlayerScreen" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
814                         <eLabel backgroundColor="#999999" position="178,112" size="924,2" zPosition="1"/>
815                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="178,104" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
816                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="852,104" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
817                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmp3pHD.png" position="128,72" size="1024,576"/>
818                         <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="328,149" size="238,238" transparent="1" alphatest="blend" />
819                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr.png" position="688,232" size="150,20" transparent="1" zPosition="1"/>
820                         <widget name="title" position="362,415" size="600,28" zPosition="1" transparent="1" font="Regular;24" foregroundColor="#fcc000" />
821                         <widget name="album" position="362,462" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
822                         <widget name="artist" position="362,492" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
823                         <widget name="genre" position="362,522" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
824                         <widget name="nextTitle" position="362,562" size="600,22" zPosition="1" transparent="1" font="Regular;16" foregroundColor="#f0f0f0" />
825                         <widget name="PositionGauge" position="664,264" size="198,14" pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" seek_pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" transparent="1"/>
826                         <widget source="session.CurrentService" render="Label" position="873,267" size="116,18" zPosition="1" font="Regular;18" halign="left" foregroundColor="#999999" transparent="1" >
827                                         <convert type="ServicePosition">Length,ShowHours</convert>
828                         </widget>
829                         <widget source="session.CurrentService" render="Label" position="684,292" size="198,20" zPosition="1" font="Regular;20" halign="left" foregroundColor="#fcc000" transparent="1" >
830                                         <convert type="ServicePosition">Position,ShowHours</convert>
831                         </widget>
832                         <widget name="shuffle" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_shuf.png" position="598,275" size="53,34" transparent="1" alphatest="on"/>
833                         <widget name="repeat" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_rep.png" position="598,239" size="53,34" transparent="1" alphatest="on"/>
834                         <widget name="dvrStatus" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pl.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pau.png" position="683,227" size="160,39" transparent="1" alphatest="on"/>
835                         </screen>"""
836         elif sz_w == 1024:
837                 skin = """
838                         <screen name="MerlinMusicPlayerScreen" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
839                         <eLabel backgroundColor="#999999" position="50,40" size="924,2" zPosition="1"/>
840                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,32" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
841                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="724,32" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
842                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmp3pHD.png" position="0,0" size="1024,576"/>
843                         <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="200,77" size="238,238" transparent="1" alphatest="blend" />
844                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr.png" position="560,160" size="150,20" transparent="1" zPosition="1"/>
845                         <widget name="title" position="234,343" size="600,28" zPosition="1" transparent="1" font="Regular;24" foregroundColor="#fcc000" />
846                         <widget name="album" position="234,390" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
847                         <widget name="artist" position="234,420" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
848                         <widget name="genre" position="234,450" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
849                         <widget name="nextTitle" position="234,490" size="600,22" zPosition="1" transparent="1" font="Regular;16" foregroundColor="#f0f0f0" />
850                         <widget name="PositionGauge" position="536,197" size="198,14" pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" seek_pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" transparent="1"/>
851                         <widget source="session.CurrentService" render="Label" position="745,195" size="116,18" zPosition="1" font="Regular;18" halign="left" foregroundColor="#999999" transparent="1" >
852                                         <convert type="ServicePosition">Length,ShowHours</convert>
853                         </widget>
854                         <widget source="session.CurrentService" render="Label" position="556,220" size="198,20" zPosition="1" font="Regular;20" halign="left" foregroundColor="#fcc000" transparent="1" >
855                                         <convert type="ServicePosition">Position,ShowHours</convert>
856                         </widget>
857                         <widget name="shuffle" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_shuf.png" position="470,203" size="53,34" transparent="1" alphatest="on"/>
858                         <widget name="repeat" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_rep.png" position="470,167" size="53,34" transparent="1" alphatest="on"/>
859                         <widget name="dvrStatus" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pl.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pau.png" position="555,155" size="160,39" transparent="1" alphatest="on"/>
860                         </screen>"""
861         else:
862                 skin = """
863                         <screen name="MerlinMusicPlayerScreen" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
864                         <eLabel backgroundColor="#999999" position="50,50" size="620,2" zPosition="1"/>
865                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,40" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
866                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="420,40" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
867                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmp3p.png" position="120,350" size="33,162"/>
868                         <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="106,130" size="180,180" transparent="1" alphatest="blend" />
869                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr.png" position="410,160" size="150,20" transparent="1" zPosition="1"/>
870                         <widget name="title" position="160,345" size="550,28" zPosition="1" transparent="1" font="Regular;24" foregroundColor="#fcc000" />
871                         <widget name="album" position="160,392" size="550,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
872                         <widget name="artist" position="160,422" size="550,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
873                         <widget name="genre" position="160,455" size="550,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
874                         <widget name="nextTitle" position="160,492" size="550,22" zPosition="1" transparent="1" font="Regular;16" foregroundColor="#f0f0f0" />
875                         <widget name="PositionGauge" position="386,197" size="198,14" pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" seek_pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" transparent="1"/>
876                         <widget source="session.CurrentService" render="Label" position="595,193" size="116,18" zPosition="1" font="Regular;18" halign="left" foregroundColor="#999999" transparent="1" >
877                                         <convert type="ServicePosition">Length,ShowHours</convert>
878                         </widget>
879                         <widget source="session.CurrentService" render="Label" position="406,220" size="198,20" zPosition="1" font="Regular;20" halign="left" foregroundColor="#fcc000" transparent="1" >
880                                         <convert type="ServicePosition">Position,ShowHours</convert>
881                         </widget>
882                         <widget name="shuffle" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_shuf.png" position="320,203" size="53,34" transparent="1" alphatest="on"/>
883                         <widget name="repeat" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_rep.png" position="320,167" size="53,34" transparent="1" alphatest="on"/>
884                         <widget name="dvrStatus" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pl.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pau.png" position="405,155" size="160,39" transparent="1" alphatest="on"/>
885                         </screen>"""
886                 
887         def __init__(self, session, songlist, index, idreammode, currentservice, servicelist):
888                 self.session = session
889                 Screen.__init__(self, session)
890                 InfoBarNotifications.__init__(self)
891                 InfoBarBase.__init__(self)
892                 self["actions"] = ActionMap(["WizardActions", "MediaPlayerActions", "EPGSelectActions", "MediaPlayerSeekActions", "ColorActions"],
893                 {
894                         "back": self.closePlayer,
895                         "pause": self.pauseEntry,
896                         "stop": self.stopEntry,
897                         "right": self.playNext,
898                         "left": self.playPrevious,
899                         "up": self.showPlaylist,
900                         "down" : self.showPlaylist,
901                         "prevBouquet": self.shuffleList,
902                         "nextBouquet": self.repeatSong,
903                         "info" : self.showLyrics,
904                         "yellow": self.pauseEntry,
905                         "green": self.play,
906                         "input_date_time": self.config,
907                         "ok": self.showTV,
908                 }, -1)
909
910                 self.onClose.append(self.__onClose)
911                 self.session.nav.stopService()
912                 self["PositionGauge"] = ServicePositionGauge(self.session.nav)
913                 self["coverArt"] = MerlinMediaPixmap()
914                 self["repeat"] = MultiPixmap()
915                 self["shuffle"] = MultiPixmap()
916                 self["dvrStatus"] = MultiPixmap()
917                 self["title"] = Label()
918                 self["album"] = Label()
919                 self["artist"] = Label()
920                 self["genre"] = Label()
921                 self["track"] = Label()
922                 self["nextTitle"] = Label()
923                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
924                         {
925                                 iPlayableService.evUpdatedInfo: self.__evUpdatedInfo,
926                                 iPlayableService.evUser+10: self.__evAudioDecodeError,
927                                 iPlayableService.evUser+12: self.__evPluginError,
928                                 iPlayableService.evUser+13: self.embeddedCoverArt,
929                                 iPlayableService.evStart: self.__serviceStarted,
930                         })
931
932                 InfoBarSeek.__init__(self, actionmap = "MediaPlayerSeekActions")
933                 self.songList = songlist
934                 self.origSongList = songlist[:]
935                 self.currentIndex = index
936                 self.shuffle = False
937                 self.repeat = False
938                 self.currentFilename = ""
939                 self.currentGoogleCoverFile = ""
940                 self.googleDownloadDir = os_path.join(config.plugins.merlinmusicplayer.googleimagepath.value, "downloaded_covers/" )
941                 if not os_path.exists(self.googleDownloadDir):
942                         try:
943                                 os_mkdir(self.googleDownloadDir)
944                         except:
945                                 self.googleDownloadDir = "/tmp/"
946                                 
947                 self.init = 0
948                 self.onShown.append(self.__onShown)
949                 # for lcd
950                 self.currentTitle = ""
951                 self.nextTitle = ""
952                 self.screenSaverTimer = eTimer()
953                 self.screenSaverTimer.timeout.get().append(self.screenSaverTimerTimeout)
954                 self.screenSaverScreen = None
955
956                 self.iDreamMode = idreammode
957                 self.currentService = currentservice
958                 self.serviceList = servicelist
959
960                 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
961         
962         def sleepTimerEntryOnStateChange(self, timer):
963                 if timer.state == TimerEntry.StateEnded:
964                         self.closePlayer()
965
966         def embeddedCoverArt(self):             
967                 self["coverArt"].embeddedCoverArt()
968                 if self.screenSaverScreen:
969                         self.screenSaverScreen.updateCover(modus = 2)
970
971         def screenSaverTimerTimeout(self):
972                 if config.plugins.merlinmusicplayer.usescreensaver.value:
973                         if self.screenSaverTimer.isActive():
974                                 self.screenSaverTimer.stop()
975                         if not self.screenSaverScreen:
976                                 self.screenSaverScreen = self.session.instantiateDialog(MerlinMusicPlayerScreenSaver)
977                                 self.session.execDialog(self.screenSaverScreen)
978                                 self.screenSaverScreen.updateLCD(self.currentTitle,1)
979                                 self.screenSaverScreen.updateLCD(self.nextTitle,4)
980                                 album = self["album"].getText()
981                                 if album:
982                                         text = "%s - %s" % (self["title"].getText(), album)
983                                 else:
984                                         text = self["title"].getText()
985                                 self.screenSaverScreen.updateDisplayText(text)
986                                 self.screenSaverScreen.updateCover(self["coverArt"].coverArtFileName, modus = 0)
987
988         def resetScreenSaverTimer(self):
989                 if config.plugins.merlinmusicplayer.usescreensaver.value and config.plugins.merlinmusicplayer.screensaverwait.value != 0:
990                         if self.screenSaverTimer.isActive():
991                                 self.screenSaverTimer.stop()
992                         self.screenSaverTimer.start(config.plugins.merlinmusicplayer.screensaverwait.value * 60000)
993
994         def __onShown(self):
995                 if self.init == 0:
996                         self.init = 1
997                         self["coverArt"].onShow()
998                         self.playSong(self.songList[self.currentIndex][0].filename)
999                 else:
1000                         self.summaries.setText(self.currentTitle,1)
1001                         self.summaries.setText(self.nextTitle,4)
1002                         if self.screenSaverScreen:
1003                                 self.screenSaverScreen.doClose()
1004                                 self.screenSaverScreen = None
1005                 self.resetScreenSaverTimer()
1006                 
1007         def __onClose(self):
1008                 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
1009                 del self["coverArt"].picload
1010                 self.seek = None
1011
1012         def config(self):
1013                 if self.screenSaverTimer.isActive():
1014                         self.screenSaverTimer.stop()
1015                 self.session.openWithCallback(self.setupFinished, MerlinMusicPlayerSetup, False)
1016
1017         def showTV(self):
1018                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1019                         if self.screenSaverTimer.isActive():
1020                                 self.screenSaverTimer.stop()
1021                         if self.screenSaverScreen:
1022                                 self.screenSaverScreen.doClose()
1023                                 self.screenSaverScreen = None
1024                         self.screenSaverScreen = self.session.instantiateDialog(MerlinMusicPlayerTV, self.currentService, self.serviceList)
1025                         self.session.execDialog(self.screenSaverScreen)
1026                         self.screenSaverScreen.updateLCD(self.currentTitle,1)
1027                         self.screenSaverScreen.updateLCD(self.nextTitle,4)
1028                         album = self["album"].getText()
1029                         if album:
1030                                 text = "%s - %s" % (self["title"].getText(), album)
1031                         else:
1032                                 text = self["title"].getText()
1033                         self.screenSaverScreen.updateDisplayText(text)
1034                         self.screenSaverScreen.updateCover(self["coverArt"].coverArtFileName, modus = 0)
1035         
1036         def setupFinished(self, result):
1037                 if result:
1038                         self.googleDownloadDir = os_path.join(config.plugins.merlinmusicplayer.googleimagepath.value, "downloaded_covers/" )
1039                         if not os_path.exists(self.googleDownloadDir):
1040                                 try:
1041                                         os_mkdir(self.googleDownloadDir)
1042                                 except:
1043                                         self.googleDownloadDir = "/tmp/"
1044                 self.resetScreenSaverTimer()
1045
1046         def closePlayer(self):  
1047                 if config.plugins.merlinmusicplayer.startlastsonglist.value:
1048                         config.plugins.merlinmusicplayer.lastsonglistindex.value = self.currentIndex
1049                         config.plugins.merlinmusicplayer.lastsonglistindex.save()
1050                         connection = OpenDatabase()
1051                         if connection is not None:
1052                                 connection.text_factory = str
1053                                 cursor = connection.cursor()
1054                                 cursor.execute("Delete from CurrentSongList;")
1055                                 for song in self.origSongList:
1056                                         cursor.execute("INSERT INTO CurrentSongList (song_id, filename,title,artist,album,genre, bitrate, length, track, date, PTS) VALUES(?,?,?,?,?,?,?,?,?,?,?);" , (song[0].songID, song[0].filename,song[0].title,song[0].artist,song[0].album,song[0].genre, song[0].bitrate, song[0].length, song[0].track, song[0].date, song[0].PTS))
1057                                 connection.commit()
1058                                 cursor.close()
1059                                 connection.close()
1060                 if self.screenSaverTimer.isActive():
1061                         self.screenSaverTimer.stop()
1062                 self.close()
1063
1064         def playSong(self, filename):
1065                 self.session.nav.stopService()
1066                 self.seek = None
1067                 self.currentFilename = filename
1068                 if not config.plugins.merlinmusicplayer.hardwaredecoder.value and self.currentFilename.lower().endswith(".mp3") and self.songList[self.currentIndex][0].PTS is None:
1069                         sref = eServiceReference(ENIGMA_MERLINPLAYER_ID, 0, self.currentFilename) # play mp3 file with merlinmp3player lib
1070                         self.session.nav.playService(sref)
1071                         if self.iDreamMode:
1072                                 self.updateMusicInformation( self.songList[self.currentIndex][0].artist, self.songList[self.currentIndex][0].title, 
1073                                         self.songList[self.currentIndex][0].album, self.songList[self.currentIndex][0].genre, self.songList[self.currentIndex][0].date, self.songList[self.currentIndex][0].track.replace("Track",""), clear = True )
1074                         else:
1075                                 path,filename = os_path.split(self.currentFilename)
1076                                 audio, isAudio, title, genre,artist,album,tracknr,track,date,length,bitrate = getID3Tags(path,filename)
1077                                 if audio:
1078                                         if date:
1079                                                 year = "(%s)" % str(date)
1080                                         else:
1081                                                 year = ""
1082                                         self.updateMusicInformation( artist, title, album, genre, year, track, clear = True )
1083                                 else:
1084                                         self.updateMusicInformation( title = title, clear = True)
1085                                 audio = None
1086                 else:
1087                         sref = eServiceReference(4097, 0, self.currentFilename)
1088                         self.session.nav.playService(sref)
1089                         if self.songList[self.currentIndex][0].PTS is not None:
1090                                 service = self.session.nav.getCurrentService()
1091                                 if service:
1092                                         self.seek = service.seek()
1093                                 self.updateMusicInformationCUE()
1094                                 self.ptsTimer = eTimer()
1095                                 self.ptsTimer.callback.append(self.ptsTimerCallback)
1096                                 self.ptsTimer.start(1000)
1097                 self["nextTitle"].setText(self.getNextTitle())
1098
1099         def ptsTimerCallback(self):
1100                 if self.seek:
1101                         pts = self.seek.getPlayPosition()
1102                         index = 0
1103                         currentIndex = 0
1104                         for songs in self.songList:
1105                                 if pts[1] > songs[0].PTS:
1106                                         currentIndex = index
1107                                 else:
1108                                         break
1109                                 index +=1
1110                         if currentIndex != self.currentIndex:
1111                                 self.currentIndex = currentIndex
1112                                 self.updateMusicInformationCUE()
1113                 self.ptsTimer.start(1000)
1114
1115         def updateMusicInformationCUE(self):
1116                 self.updateSingleMusicInformation("artist", self.songList[self.currentIndex][0].artist, True)
1117                 self.updateSingleMusicInformation("title", self.songList[self.currentIndex][0].title, True)
1118                 self.updateSingleMusicInformation("album", self.songList[self.currentIndex][0].album, True)
1119                 self.updateSingleMusicInformation("track", self.songList[self.currentIndex][0].track.replace("Track",""), True)
1120                 self.summaries.setText(self.songList[self.currentIndex][0].title,1)
1121                 if self.screenSaverScreen:
1122                         self.screenSaverScreen.updateLCD(self.songList[self.currentIndex][0].title,1)
1123                         if self.songList[self.currentIndex][0].album:
1124                                 self.screenSaverScreen.updateDisplayText("%s - %s" % (self.songList[self.currentIndex][0].title,self.songList[self.currentIndex][0].album))
1125                         else:
1126                                 self.screenSaverScreen.updateDisplayText(self.songList[self.currentIndex][0].title)
1127                 self.updateCover(self.songList[self.currentIndex][0].artist, self.songList[self.currentIndex][0].album)
1128                 self.currentTitle = self.songList[self.currentIndex][0].title
1129                 self["nextTitle"].setText(self.getNextTitle())
1130
1131         def __serviceStarted(self):
1132                 self["dvrStatus"].setPixmapNum(0)
1133
1134         def __evUpdatedInfo(self):
1135                 currPlay = self.session.nav.getCurrentService()
1136                 if currPlay is not None:
1137                         sTitle = currPlay.info().getInfoString(iServiceInformation.sTagTitle)
1138                         sAlbum = currPlay.info().getInfoString(iServiceInformation.sTagAlbum)
1139                         sArtist = currPlay.info().getInfoString(iServiceInformation.sTagArtist)
1140                         sGenre = currPlay.info().getInfoString(iServiceInformation.sTagGenre)
1141                         sYear = currPlay.info().getInfoString(iServiceInformation.sTagDate)
1142                         sTrackNumber = currPlay.info().getInfo(iServiceInformation.sTagTrackNumber)
1143                         sTrackCount = currPlay.info().getInfo(iServiceInformation.sTagTrackCount)
1144                         track = ""
1145                         if sTrackNumber and sTrackCount:
1146                                 track = "%s/%s" % (sTrackNumber,sTrackCount)
1147                         elif sTrackNumber:
1148                                 track = sTrackNumber
1149                         if sYear:
1150                                 sYear = "(%s)" % sYear
1151                         if not sTitle:
1152                                 sTitle = os_path.splitext(os_path.basename(self.currentFilename))[0]
1153                         
1154                         if self.songList[self.currentIndex][0].PTS is None:
1155                                 self.updateMusicInformation( sArtist, sTitle, sAlbum, sGenre, sYear, track, clear = True )
1156                         else:
1157                                 self.updateSingleMusicInformation("genre", sGenre, True)
1158                 else:
1159                         self.updateMusicInformation()
1160
1161         def updateMusicInformation(self, artist = "", title = "", album = "", genre = "", year = "", track = "", clear = False):
1162                 if year and album:
1163                         album = "%s %s" % (album, year)
1164                 self.updateSingleMusicInformation("artist", artist, clear)
1165                 self.updateSingleMusicInformation("title", title, clear)
1166                 self.updateSingleMusicInformation("album", album, clear)
1167                 self.updateSingleMusicInformation("genre", genre, clear)
1168                 self.updateSingleMusicInformation("track", track, clear)
1169
1170                 self.currentTitle = title
1171                 if not self.iDreamMode and self.songList[self.currentIndex][0].PTS is None:
1172                         # for lyrics
1173                         self.songList[self.currentIndex][0].title = title
1174                         self.songList[self.currentIndex][0].artist = artist
1175                 self.summaries.setText(title,1)
1176                 if self.screenSaverScreen:
1177                         self.screenSaverScreen.updateLCD(title,1)
1178                         if album:
1179                                 self.screenSaverScreen.updateDisplayText("%s - %s" % (title,album))
1180                         else:
1181                                 self.screenSaverScreen.updateDisplayText(title)
1182                 self.updateCover(artist, album)
1183
1184         def updateCover(self, artist, album):
1185                 hasCover = False
1186                 audio = None
1187                 audiotype = 0
1188                 if self.currentFilename.lower().endswith(".mp3"):
1189                         try: 
1190                                 audio = ID3(self.currentFilename)
1191                                 audiotype = 1
1192                         except: audio = None
1193                 elif self.currentFilename.lower().endswith(".flac"):
1194                         try: 
1195                                 audio = FLAC(self.currentFilename)
1196                                 audiotype = 2
1197                         except: audio = None
1198                 elif self.currentFilename.lower().endswith(".m4a"):
1199                         try: 
1200                                 audio = MP4(self.currentFilename)
1201                                 audiotype = 3
1202                         except: audio = None
1203                 elif self.currentFilename.lower().endswith(".ogg"):
1204                         try:
1205                                 audio = OggVorbis(self.currentFilename)
1206                                 audiotype = 4
1207                         except: audio = None
1208                 if audio:
1209                         if audiotype == 1:
1210                                 apicframes = audio.getall("APIC")
1211                                 if len(apicframes) >= 1:
1212                                         hasCover = True
1213                                         if not config.plugins.merlinmusicplayer.hardwaredecoder.value:
1214                                                 coverArtFile = file("/tmp/.id3coverart", 'wb')
1215                                                 coverArtFile.write(apicframes[0].data)
1216                                                 coverArtFile.close()
1217                                                 self["coverArt"].embeddedCoverArt()
1218                                                 if self.screenSaverScreen:
1219                                                         self.screenSaverScreen.updateCover(modus = 2)
1220                         elif audiotype == 2:
1221                                 if len(audio.pictures) >= 1:
1222                                         hasCover = True
1223                         elif audiotype == 3:
1224                                 if 'covr' in audio.tags:
1225                                         hasCover = True
1226                         elif audiotype == 4:
1227                                 if 'METADATA_BLOCK_PICTURE' in audio.tags:
1228                                         hasCover = True
1229                         audio = None
1230                 if not hasCover:
1231                         if not self["coverArt"].updateCoverArt(self.currentFilename):
1232                                 if config.plugins.merlinmusicplayer.usegoogleimage.value:
1233                                         self.getGoogleCover(artist, album)
1234                                 else:
1235                                         self["coverArt"].showDefaultCover()
1236                                         if self.screenSaverScreen:
1237                                                 self.screenSaverScreen.updateCover(modus = 1)
1238                         else:
1239                                 if self.screenSaverScreen:
1240                                         self.screenSaverScreen.updateCover(filename = self.currentFilename, modus = 3)
1241                                 self.currentGoogleCoverFile = ""
1242                 else:
1243                         self.currentGoogleCoverFile = ""
1244
1245         def updateSingleMusicInformation(self, name, info, clear):
1246                 if info != "" or clear:
1247                         if self[name].getText() != info:
1248                                 self[name].setText(info)
1249
1250         def getGoogleCover(self, artist,album):
1251                 if artist != "" and album != "":
1252                         url = "http://images.google.de/images?q=%s+%s&btnG=Bilder-Suche" % (quote(album),quote(artist))
1253                         sendUrlCommand(url, None,10).addCallback(self.googleImageCallback).addErrback(self.coverDownloadFailed)
1254                 else:
1255                         self["coverArt"].showDefaultCover()
1256
1257         def googleImageCallback(self, result):
1258                 foundPos = result.find("imgres?imgurl=")
1259                 foundPos2 = result.find("&amp;imgrefurl=")
1260                 if foundPos != -1 and foundPos2 != -1:
1261                         url = result[foundPos+14:foundPos2]
1262                         parts = url.split("/")
1263                         filename = parts[-1]
1264                         if filename != self.currentGoogleCoverFile:
1265                                 self.currentGoogleCoverFile = filename
1266                                 filename = self.googleDownloadDir + parts[-1]
1267                                 if os_path.exists(filename):
1268                                         print "[MerlinMusicPlayer] using cover from %s " % filename
1269                                         self["coverArt"].showCoverFromFile(filename)
1270                                         if self.screenSaverScreen:
1271                                                 self.screenSaverScreen.updateCover(filename = filename, modus = 4)
1272                                 else:
1273                                         print "[MerlinMusicPlayer] downloading cover from %s " % url
1274                                         downloadPage(url , self.googleDownloadDir + parts[-1]).addCallback(boundFunction(self.coverDownloadFinished, filename)).addErrback(self.coverDownloadFailed)
1275
1276         def coverDownloadFailed(self,result):
1277                 print "[MerlinMusicPlayer] cover download failed: %s " % result
1278                 self["coverArt"].showDefaultCover()
1279                 if self.screenSaverScreen:
1280                         self.screenSaverScreen.updateCover(modus = 1)
1281
1282         def coverDownloadFinished(self,filename, result):
1283                 print "[MerlinMusicPlayer] cover download finished"
1284                 self["coverArt"].showCoverFromFile(filename)
1285                 if self.screenSaverScreen:
1286                         self.screenSaverScreen.updateCover(filename = filename, modus = 4)
1287
1288         def __evAudioDecodeError(self):
1289                 currPlay = self.session.nav.getCurrentService()
1290                 sAudioType = currPlay.info().getInfoString(iServiceInformation.sUser+10)
1291                 print "[MerlinMusicPlayer] audio-codec %s can't be decoded by hardware" % (sAudioType)
1292                 self.session.open(MessageBox, _("This Dreambox can't decode %s streams!") % sAudioType, type = MessageBox.TYPE_INFO,timeout = 20 )
1293
1294         def __evPluginError(self):
1295                 currPlay = self.session.nav.getCurrentService()
1296                 message = currPlay.info().getInfoString(iServiceInformation.sUser+12)
1297                 print "[MerlinMusicPlayer]" , message
1298                 self.session.open(MessageBox, message, type = MessageBox.TYPE_INFO,timeout = 20 )
1299
1300         def doEofInternal(self, playing):
1301                 if playing:
1302                         self.playNext()
1303
1304         def checkSkipShowHideLock(self):
1305                 self.updatedSeekState()
1306
1307         def updatedSeekState(self):
1308                 if self.seekstate == self.SEEK_STATE_PAUSE:
1309                         self["dvrStatus"].setPixmapNum(1)
1310                 elif self.seekstate == self.SEEK_STATE_PLAY:
1311                         self["dvrStatus"].setPixmapNum(0)
1312
1313         def pauseEntry(self):
1314                 self.pauseService()
1315                 self.resetScreenSaverTimer()
1316
1317         def play(self):
1318                 #play the current song from beginning again
1319                 if self.songList[self.currentIndex][0].PTS is None:
1320                         self.playSong(self.songList[self.currentIndex][0].filename)
1321                 else:
1322                         if self.seek:
1323                                 self.seek.seekTo(self.songList[self.currentIndex][0].PTS)
1324                                 self.updatedSeekState()
1325                 self.resetScreenSaverTimer()            
1326
1327         def unPauseService(self):
1328                 self.setSeekState(self.SEEK_STATE_PLAY)
1329
1330         def stopEntry(self):
1331                 self.seek = None
1332                 self.session.nav.stopService()
1333                 self.origSongList = []
1334                 self.songList = []
1335                 if config.plugins.merlinmusicplayer.startlastsonglist.value:
1336                         config.plugins.merlinmusicplayer.lastsonglistindex.value = -1
1337                         config.plugins.merlinmusicplayer.lastsonglistindex.save()
1338                         connection = OpenDatabase()
1339                         if connection is not None:
1340                                 connection.text_factory = str
1341                                 cursor = connection.cursor()
1342                                 cursor.execute("Delete from CurrentSongList;")
1343                                 connection.commit()
1344                                 cursor.close()
1345                                 connection.close()
1346                 self.resetScreenSaverTimer()
1347                 self.close()
1348
1349         def playNext(self):
1350                 if not self.repeat:
1351                         if self.currentIndex +1 > len(self.songList) -1:
1352                                 self.currentIndex = 0
1353                         else:
1354                                 self.currentIndex += 1
1355                 if self.songList[self.currentIndex][0].PTS is None:
1356                         self.playSong(self.songList[self.currentIndex][0].filename)
1357                 else:
1358                         self.playCUETrack()
1359                 if not self.screenSaverScreen:
1360                         self.resetScreenSaverTimer()
1361
1362         def playPrevious(self):
1363                 if not self.repeat:
1364                         if self.currentIndex - 1 < 0:
1365                                 self.currentIndex = len(self.songList) - 1
1366                         else:
1367                                 self.currentIndex -= 1
1368
1369                 if self.songList[self.currentIndex][0].PTS is None:
1370                         self.playSong(self.songList[self.currentIndex][0].filename)
1371                 else:
1372                         self.playCUETrack()
1373                 self.resetScreenSaverTimer()
1374
1375         def getNextTitle(self):
1376                 if self.repeat:
1377                         index = self.currentIndex
1378                 else:
1379                         if self.currentIndex + 1 > len(self.songList) -1:
1380                                 index = 0
1381                         else:
1382                                 index = self.currentIndex + 1
1383                 if self.iDreamMode or self.songList[index][0].PTS is not None:
1384                         text = "%s - %s" % (self.songList[index][0].title, self.songList[index][0].artist)
1385                 else:
1386                         if self.songList[index][0].filename.lower().startswith("http://"):
1387                                 text = self.songList[index][0].filename
1388                         else:
1389                                 path,filename = os_path.split(self.songList[index][0].filename)
1390                                 audio, isAudio, title, genre,artist,album,tracknr,track,date,length,bitrate = getID3Tags(path,filename)
1391                                 if audio:
1392                                         if artist:
1393                                                 text = "%s - %s" % (title, artist)
1394                                         else:
1395                                                 text = title
1396                                 else:
1397                                         text = title
1398                                 audio = None
1399                 self.nextTitle = text
1400                 self.summaries.setText(text,4)
1401                 if self.screenSaverScreen:
1402                         self.screenSaverScreen.updateLCD(text,4)
1403                 return str(text)
1404
1405         def shuffleList(self):
1406                 if self.songList[self.currentIndex][0].PTS is None: # not implemented for cue files yet
1407                         self.shuffle = not self.shuffle
1408                         if self.shuffle:
1409                                 self["shuffle"].setPixmapNum(1)
1410                                 shuffle(self.songList)
1411                         else:
1412                                 self.songList = self.origSongList[:]
1413                                 self["shuffle"].setPixmapNum(0)
1414                         index = 0
1415                         for x in self.songList:
1416                                 if x[0].filename == self.currentFilename:
1417                                         self.currentIndex = index
1418                                         break
1419                                 index += 1
1420                         self["nextTitle"].setText(self.getNextTitle())
1421                 else:
1422                         self.session.open(MessageBox, _("Shuffle is not available yet with cue-files!"), type = MessageBox.TYPE_INFO,timeout = 20 )
1423                 self.resetScreenSaverTimer()
1424
1425         def repeatSong(self):
1426                 if self.songList[self.currentIndex][0].PTS is None: # not implemented for cue files yet
1427                         self.repeat = not self.repeat
1428                         if self.repeat:
1429                                 self["repeat"].setPixmapNum(1)
1430                         else:
1431                                 self["repeat"].setPixmapNum(0)
1432                         self["nextTitle"].setText(self.getNextTitle())
1433                 else:
1434                         self.session.open(MessageBox, _("Repeat is not available yet with cue-files!"), type = MessageBox.TYPE_INFO,timeout = 20 )
1435                 self.resetScreenSaverTimer()
1436
1437         def showPlaylist(self):
1438                 if self.screenSaverTimer.isActive():
1439                         self.screenSaverTimer.stop()
1440                 self.session.openWithCallback(self.showPlaylistCallback, MerlinMusicPlayerSongList, self.songList, self.currentIndex, self.iDreamMode)
1441
1442         def showPlaylistCallback(self, index):
1443                 if index != -1:
1444                         self.currentIndex = index
1445
1446                         if self.songList[self.currentIndex][0].PTS is None:
1447                                 self.playSong(self.songList[self.currentIndex][0].filename)
1448                         else:
1449                                 self.playCUETrack()             
1450
1451                 self.resetScreenSaverTimer()
1452
1453         def playCUETrack(self):
1454                 if self.ptsTimer.isActive():
1455                         self.ptsTimer.stop()
1456                 if self.seek:
1457                         self.seek.seekTo(self.songList[self.currentIndex][0].PTS)
1458                         self.updatedSeekState()
1459                         self.updateMusicInformationCUE()
1460                         self.ptsTimer.start(1000)
1461
1462         def showLyrics(self):
1463                 if self.screenSaverTimer.isActive():
1464                         self.screenSaverTimer.stop()
1465                 self.session.openWithCallback(self.resetScreenSaverTimer, MerlinMusicPlayerLyrics, self.songList[self.currentIndex][0])
1466
1467         def createSummary(self):
1468                 return MerlinMusicPlayerLCDScreen
1469
1470 class MerlinMusicPlayerLyrics(Screen):
1471
1472         sz_w = getDesktop(0).size().width()
1473         if sz_w == 1280:
1474                 skin = """
1475                         <screen name="MerlinMusicPlayerLyrics" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="Merlin Music Player Lyrics">
1476                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1477                         <eLabel backgroundColor="#999999" position="178,112" size="924,2" zPosition="1"/>
1478                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="178,104" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1479                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="852,104" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1480                         <widget name="headertext" position="178,145" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1481                         <widget name="resulttext" position="178,172" zPosition="1" size="900,20" font="Regular;16" transparent="1"   backgroundColor="#00000000"/>
1482                         <widget name="lyric_text" position="178,222" zPosition="2" size="940,350" font="Regular;18" transparent="0"  backgroundColor="#00000000"/>
1483                         </screen>"""
1484         elif sz_w == 1024:
1485                 skin = """
1486                         <screen name="MerlinMusicPlayerLyrics" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="Merlin Music Player Lyrics">
1487                         <eLabel backgroundColor="#999999" position="50,40" size="924,2" zPosition="1"/>
1488                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,32" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1489                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="724,32" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1490                         <widget name="headertext" position="50,73" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1491                         <widget name="resulttext" position="50,100" zPosition="1" size="900,20" font="Regular;16" transparent="1"   backgroundColor="#00000000"/>
1492                         <widget name="lyric_text" position="50,150" zPosition="2" size="940,350" font="Regular;18" transparent="0"  backgroundColor="#00000000"/>
1493                         </screen>"""
1494         else:
1495                 skin = """
1496                         <screen name="MerlinMusicPlayerLyrics" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="Merlin Music Player Lyrics">
1497                         <eLabel backgroundColor="#999999" position="50,50" size="620,2" zPosition="1"/>
1498                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,40" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1499                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="420,40" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1500                         <widget name="headertext" position="50,73" zPosition="1" size="620,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1501                         <widget name="resulttext" position="50,100" zPosition="1" size="620,20" font="Regular;16" transparent="1"   backgroundColor="#00000000"/>
1502                         <widget name="lyric_text" position="50,150" zPosition="2" size="620,350" font="Regular;18" transparent="0"  backgroundColor="#00000000"/>
1503                         </screen>"""
1504                 
1505         
1506         def __init__(self, session, currentsong):
1507                 self.session = session
1508                 Screen.__init__(self, session)
1509                 self["headertext"] = Label(_("Merlin Music Player Lyrics"))
1510                 # leoslyrics does not work anymore
1511 #               self["resulttext"] = Label(_("Getting lyrics from api.leoslyrics.com..."))
1512                 self["resulttext"] = Label()
1513                 self["actions"] = ActionMap(["WizardActions", "DirectionActions"],
1514                 {
1515                         "back": self.close,
1516                         "upUp": self.pageUp,
1517                         "leftUp": self.pageUp,
1518                         "downUp": self.pageDown,
1519                         "rightUp": self.pageDown,
1520                 }, -1)
1521                 self["lyric_text"] = ScrollLabel()
1522                 self.currentSong = currentsong
1523                 self.onLayoutFinish.append(self.startRun)
1524
1525         def startRun(self):
1526                 # get lyric-text from id3 tag
1527                 try:
1528                         audio = ID3(self.currentSong.filename)
1529                 except:
1530                         audio = None
1531                 text = getEncodedString(self.getLyricsFromID3Tag(audio)).replace("\r\n","\n")
1532                 text = text.replace("\r","\n")
1533                 self["lyric_text"].setText(text)
1534   
1535         def getLyricsFromID3Tag(self,tag):
1536                 if tag:
1537                         for frame in tag.values():
1538                                 if frame.FrameID == "USLT":
1539                                         return frame.text
1540                 url = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=%s&song=%s" % (quote(self.currentSong.artist), quote(self.currentSong.title))
1541                 sendUrlCommand(url, None,10).addCallback(self.gotLyrics).addErrback(self.urlError)
1542                 return "No lyrics found in id3-tag, trying api.chartlyrics.com..."
1543         
1544         def urlError(self, error = None):
1545                 if error is not None:
1546                         self["resulttext"].setText(str(error.getErrorMessage()))
1547                         self["lyric_text"].setText("")
1548
1549         def gotLyrics(self, xmlstring):
1550                 root = cet_fromstring(xmlstring)
1551                 lyrictext = ""
1552                 lyrictext = root.findtext("{http://api.chartlyrics.com/}Lyric").encode("utf-8", 'ignore')
1553                 self["lyric_text"].setText(lyrictext)
1554                 title = root.findtext("{http://api.chartlyrics.com/}LyricSong").encode("utf-8", 'ignore')
1555                 artist = root.findtext("{http://api.chartlyrics.com/}LyricArtist").encode("utf-8", 'ignore')
1556                 result = _("Response -> lyrics for: %s (%s)") % (title,artist)
1557                 self["resulttext"].setText(result)
1558                 if not lyrictext:
1559                         self["resulttext"].setText(_("No lyrics found"))
1560                         self["lyric_text"].setText("")
1561
1562         def pageUp(self):
1563                 self["lyric_text"].pageUp()
1564
1565         def pageDown(self):
1566                 self["lyric_text"].pageDown()   
1567
1568 class MerlinMusicPlayerSongList(Screen):
1569         
1570         sz_w = getDesktop(0).size().width()
1571         if sz_w == 1280:
1572                 skin = """
1573                         <screen name="MerlinMusicPlayerSongList" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1574                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1575                         <eLabel backgroundColor="#999999" position="178,112" size="924,2" zPosition="1"/>
1576                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="178,104" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1577                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="852,104" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1578                         <widget name="headertext" position="178,145" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1579                         <widget name="list" position="178,182" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1580                         </screen>"""
1581         elif sz_w == 1024:
1582                 skin = """
1583                         <screen name="MerlinMusicPlayerSongList" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1584                         <eLabel backgroundColor="#999999" position="50,40" size="924,2" zPosition="1"/>
1585                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,32" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1586                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="724,32" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1587                         <widget name="headertext" position="50,73" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1588                         <widget name="list" position="50,110" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1589                         </screen>"""
1590         else:
1591                 skin = """
1592                         <screen name="MerlinMusicPlayerSongList" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1593                         <eLabel backgroundColor="#999999" position="50,50" size="620,2" zPosition="1"/>
1594                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,40" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1595                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="420,40" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1596                         <widget name="headertext" position="50,73" zPosition="1" size="620,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1597                         <widget name="list" position="50,110" zPosition="2" size="620,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1598                         </screen>"""
1599                 
1600         
1601         def __init__(self, session, songlist, index, idreammode):
1602                 self.session = session
1603                 Screen.__init__(self, session)
1604                 self["headertext"] = Label(_("Merlin Music Player Songlist"))
1605                 self["list"] = iDreamList()
1606                 self["list"].connectSelChanged(self.lcdUpdate)
1607                 self["actions"] = ActionMap(["WizardActions"],
1608                 {
1609                         "ok": self.ok,
1610                         "back": self.closing,
1611                 }, -1)
1612                 self.songList = songlist
1613                 self.index = index
1614                 self.iDreamMode = idreammode
1615                 self.onLayoutFinish.append(self.startRun)
1616                 self.onShown.append(self.lcdUpdate)
1617
1618         def startRun(self):
1619                 if self.iDreamMode:
1620                         self["list"].setMode(10) # songlist
1621                 self["list"].setList(self.songList)
1622                 self["list"].moveToIndex(self.index)
1623
1624         def ok(self):
1625                 self.close(self["list"].getCurrentIndex())
1626
1627         def closing(self):
1628                 self.close(-1)
1629
1630         def lcdUpdate(self):
1631                 try:
1632                         index = self["list"].getCurrentIndex()
1633                         songlist = self["list"].getList()
1634                         mode =  self.iDreamMode or songlist[index][0].PTS
1635                         if mode:
1636                                 self.summaries.setText(songlist[index][0].title,1)
1637                         else:
1638                                 self.summaries.setText(songlist[index][0].text,1)
1639                         count = self["list"].getItemCount()
1640                         # voheriges
1641                         index -= 1
1642                         if index < 0:
1643                                 index = count
1644                         if mode:
1645                                 self.summaries.setText(songlist[index][0].title,3)
1646                         else:
1647                                 self.summaries.setText(songlist[index][0].text,3)
1648                         # naechstes
1649                         index = self["list"].getCurrentIndex() + 1
1650                         if index > count:
1651                                 index = 0
1652                         if mode:
1653                                 self.summaries.setText(songlist[index][0].title,4)
1654                         else:
1655                                 self.summaries.setText(songlist[index][0].text,4)
1656                 except: pass
1657
1658         def createSummary(self):
1659                 return MerlinMusicPlayerLCDScreenText
1660
1661 class iDreamMerlin(Screen):
1662         
1663
1664         sz_w = getDesktop(0).size().width()
1665         if sz_w == 1280:
1666                 skin = """
1667                         <screen name="iDreamMerlin" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1668                                 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1669                                 <ePixmap position="178,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1670                                 <ePixmap position="328,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1671                                 <ePixmap position="478,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1672                                 <ePixmap position="628,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1673                                 <widget render="Label" source="key_red" position="178,102" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1674                                 <widget render="Label" source="key_green" position="328,102" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1675                                 <widget render="Label" source="key_yellow" position="478,102" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1676                                 <widget render="Label" source="key_blue" position="628,102" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1677                                 <widget name="headertext" position="178,149" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1678                                 <widget name="list" position="178,182" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1679                         </screen>"""
1680         elif sz_w == 1024:
1681                 skin = """
1682                         <screen name="iDreamMerlin" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1683                                 <ePixmap position="50,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1684                                 <ePixmap position="200,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1685                                 <ePixmap position="350,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1686                                 <ePixmap position="500,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1687                                 <widget render="Label" source="key_red" position="50,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1688                                 <widget render="Label" source="key_green" position="200,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1689                                 <widget render="Label" source="key_yellow" position="350,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1690                                 <widget render="Label" source="key_blue" position="500,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1691                                 <widget name="headertext" position="50,77" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1692                                 <widget name="list" position="50,110" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1693                         </screen>"""
1694         else:
1695                 skin = """
1696                         <screen name="iDreamMerlin" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1697                                 <ePixmap position="50,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1698                                 <ePixmap position="200,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1699                                 <ePixmap position="350,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1700                                 <ePixmap position="500,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1701                                 <widget render="Label" source="key_red" position="50,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1702                                 <widget render="Label" source="key_green" position="200,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1703                                 <widget render="Label" source="key_yellow" position="350,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1704                                 <widget render="Label" source="key_blue" position="500,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1705                                 <widget name="headertext" position="50,77" zPosition="1" size="620,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1706                                 <widget name="list" position="50,110" zPosition="2" size="620,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1707                         </screen>"""
1708                 
1709         
1710         def __init__(self, session, servicelist):
1711                 self.session = session
1712                 Screen.__init__(self, session)
1713                 self["list"] = iDreamList()
1714                 self["list"].connectSelChanged(self.lcdUpdate)
1715
1716
1717                 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "ColorActions", "EPGSelectActions"],
1718                 {
1719                         "ok": self.ok,
1720                         "back": self.closing,
1721                         "red": self.red_pressed,
1722                         "green": self.green_pressed,
1723                         "yellow": self.yellow_pressed,
1724                         "blue": self.blue_pressed,
1725                         "input_date_time": self.menu_pressed,
1726                         "info" : self.info_pressed,
1727                 }, -1)
1728
1729                 self["actions2"] = NumberActionMap(["InputActions"],
1730                 {
1731                         "0": self.keyNumber_pressed,
1732                 }, -1)
1733
1734                 self.onLayoutFinish.append(self.startRun)
1735                 self.onShown.append(self.lcdUpdate)
1736                 self.onClose.append(self.__onClose)
1737                 
1738                 self.serviceList = servicelist
1739                 self.currentService = self.session.nav.getCurrentlyPlayingServiceReference()
1740                 self.session.nav.stopService()
1741                 
1742                 self.mode = 0
1743                 self.mainMenuList = []
1744                 self.cacheList = []
1745                 self.LastMethod = None
1746                 self.player = None
1747                 
1748                 self["key_red"] = StaticText("")
1749                 self["key_green"] = StaticText("")
1750                 self["key_yellow"] = StaticText("")
1751                 self["key_blue"] = StaticText("")
1752                 self["headertext"] = Label(_("iDream Main Menu"))
1753
1754                 self.startMerlinPlayerScreenTimer = eTimer()
1755                 self.startMerlinPlayerScreenTimer.timeout.get().append(self.info_pressed)
1756
1757                 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
1758         
1759         def sleepTimerEntryOnStateChange(self, timer):
1760                 if timer.state == TimerEntry.StateEnded:
1761                         self.close()
1762
1763         def getPlayList(self):
1764                 connection = OpenDatabase()
1765                 if connection is not None:
1766                         connection.text_factory = str
1767                         cursor = connection.cursor()
1768                         playList = []
1769                         cursor.execute("select playlist_id,playlist_text from playlists order by playlist_text;")
1770                         for row in cursor:
1771                                 playList.append((row[1], row[0]))
1772                         cursor.close()  
1773                         connection.close()
1774                         return playList
1775                 else:
1776                         return None
1777
1778         def sqlCommand(self, sqlSatement):
1779                 connection = OpenDatabase()
1780                 if connection is not None:
1781                         cursor = connection.cursor()
1782                         cursor.execute(sqlSatement)
1783                         cursor.close()
1784                         connection.commit()
1785                         connection.close()
1786
1787         def clearCache(self):
1788                 for items in self.cacheList:
1789                         items.cache = False
1790                         items.listview = []
1791                         items.headertext = ""
1792
1793         def getCurrentSelection(self):
1794                 sel = None
1795                 try: sel = self["list"].l.getCurrentSelection()[0]
1796                 except: pass
1797                 return sel
1798
1799         def addListToPlaylistConfirmed(self, methodName, answer):
1800                 if answer:
1801                         playList = self.getPlayList()
1802                         if len(playList):
1803                                 self.session.openWithCallback(methodName, ChoiceBox,list = playList)
1804                         else:
1805                                 self.session.openWithCallback(self.createPlaylistConfirmed, MessageBox, _("There are no playlists defined.\nDo you want to create a new playlist?"))
1806
1807         def menu_pressed(self):
1808                 self.startMerlinPlayerScreenTimer.stop()
1809                 options = [(_("Configuration"), self.config),(_("Search in iDream database"), self.searchInIDreamDatabase),]
1810                 options.extend(((_("Scan path for music files and add them to database"), self.scanDir),))
1811                 if self.mode != 1:
1812                         options.extend(((_("Create new playlist"), self.createPlaylist),))
1813                 if self["list"].getDisplaySongMode():
1814                         if self.mode == 2:
1815                                 options.extend(((_("Delete song from current playlist"), self.deleteSongFromPlaylist),))
1816                         else:
1817                                 options.extend(((_("Add selected song to a playlist"), self.addSongToPlaylist),))
1818                                 if self.mode == 18:
1819                                         options.extend(((_("Add all songs from selected album to a playlist"), self.addAlbumToPlaylist),))
1820                                 elif self.mode == 19:
1821                                         options.extend(((_("Add all songs from selected artist to a playlist"), self.addArtistToPlaylist),))
1822                                 options.extend(((_("Delete song from database"), self.deleteSongFromDatabase),))
1823                         options.extend(((_("Clear current songlist and play selected entry"), self.stopPlayingAndAppendFileToSongList),))
1824                         options.extend(((_("Append file to current playing songlist"), self.appendFileToSongList),))
1825                         if self.player is not None and self.player.songList:
1826                                 options.extend(((_("Insert file to current playing songlist and play next"), self.insertFileToSongList),))
1827                 else:
1828                         if self.mode == 1:
1829                                 options.extend(((_("Delete selected playlist"), self.deletePlaylist),))
1830                         elif self.mode == 4:
1831                                 options.extend(((_("Add all songs from selected artist to a playlist"), self.addArtistToPlaylist),))
1832                         elif self.mode == 5 or self.mode == 7:
1833                                 options.extend(((_("Add all songs from selected album to a playlist"), self.addAlbumToPlaylist),))
1834                         elif self.mode == 13:
1835                                 options.extend(((_("Add all songs from selected genre to a playlist"), self.addGenreToPlaylist),))
1836                 self.session.openWithCallback(self.menuCallback, ChoiceBox,list = options)
1837
1838         def menuCallback(self, ret):
1839                 ret and ret[1]()
1840
1841         def scanDir(self):
1842                 SelectPath
1843                 self.session.openWithCallback(self.pathSelected,SelectPath,"/media/")
1844
1845         def pathSelected(self, res):
1846                 if res is not None:
1847                         self.session.openWithCallback(self.filesAdded, iDreamAddToDatabase,res)
1848
1849         def filesAdded(self):
1850                 if pathToDatabase.isRunning:
1851                         self.close()
1852                 else:
1853                         self.red_pressed()
1854                 
1855
1856
1857
1858         def addGenreToPlaylist(self):
1859                 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed,self.addGenreToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that genre to a playlist?"))
1860
1861         def addGenreToPlaylistConfirmedCallback(self, ret):
1862                 if ret:
1863                         sel = self.getCurrentSelection()
1864                         if sel:
1865                                 self.sqlCommand("INSERT INTO Playlist_Songs (playlist_id,song_id) select %d, song_id from songs where genre_id=%d order by album_id,tracknumber,title,filename;" % (ret[1],sel.genreID))
1866                                 self.clearCache()
1867
1868         def addArtistToPlaylist(self):
1869                 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed, self.addArtistToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that artist to a playlist?"))
1870
1871         def addArtistToPlaylistConfirmedCallback(self, ret):
1872                 if ret:
1873                         sel = self.getCurrentSelection()
1874                         if sel:
1875                                 self.sqlCommand("INSERT INTO Playlist_Songs (playlist_id,song_id) select %d, song_id from songs where artist_id=%d order by album_id,tracknumber,title,filename;" % (ret[1],sel.artistID))
1876                                 self.clearCache()
1877
1878         def addAlbumToPlaylist(self):
1879                 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed, self.addAlbumToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that album to a playlist?"))
1880
1881         def addAlbumToPlaylistConfirmedCallback(self, ret):
1882                 if ret:
1883                         sel = self.getCurrentSelection()
1884                         if sel:
1885                                 self.sqlCommand("INSERT INTO Playlist_Songs (playlist_id,song_id) select %d, song_id from songs where album_id=%d order by tracknumber,title,filename;" % (ret[1],sel.albumID))
1886                                 self.clearCache()
1887
1888         def deletePlaylist(self):
1889                 self.session.openWithCallback(self.deletePlaylistConfirmed, MessageBox, _("Do you really want to delete the current playlist?"))
1890
1891         def deletePlaylistConfirmed(self, answer):
1892                 if answer:
1893                         sel = self.getCurrentSelection()
1894                         if sel:
1895                                 self.sqlCommand("delete from playlist_songs where playlist_id = %d" % (sel.playlistID))
1896                                 self.sqlCommand("delete from playlists where playlist_id = %d" % (sel.playlistID))
1897                                 self["list"].removeItem(self["list"].getCurrentIndex())
1898                                 self.clearCache()
1899
1900
1901         def deleteSongFromPlaylist(self):
1902                 self.session.openWithCallback(self.deleteSongFromPlaylistConfirmed, MessageBox, _("Do you really want to delete that song the current playlist?"))
1903
1904         def deleteSongFromPlaylistConfirmed(self, answer):
1905                 if answer:
1906                         sel = self.getCurrentSelection()
1907                         if sel:
1908                                 self.sqlCommand("delete from playlist_songs where song_id = %d" % (sel.songID))
1909                                 self["list"].removeItem(self["list"].getCurrentIndex())
1910                                 self.clearCache()
1911
1912         def deleteSongFromDatabase(self):
1913                 self.session.openWithCallback(self.deleteSongFromDatabaseConfirmed, MessageBox, _("Do you really want to delete that song from the database?"))
1914
1915         def deleteSongFromDatabaseConfirmed(self, answer):
1916                 if answer:
1917                         sel = self.getCurrentSelection()
1918                         if sel:
1919                                 self.sqlCommand("delete from playlist_songs where song_id = %d" % (sel.songID))
1920                                 self.sqlCommand("delete from songs where song_id = %d" % (sel.songID))
1921                                 self["list"].removeItem(self["list"].getCurrentIndex())
1922                                 self.clearCache()
1923                         
1924         def addSongToPlaylist(self):
1925                 playList = self.getPlayList()
1926                 if len(playList):
1927                         self.session.openWithCallback(self.addSongToPlaylistCallback, ChoiceBox,list = playList)
1928                 else:
1929                         self.session.openWithCallback(self.createPlaylistConfirmed, MessageBox, _("There are no playlists defined.\nDo you want to create a new playlist?"))
1930
1931         def createPlaylistConfirmed(self, val):
1932                 if val:
1933                         self.createPlaylist()
1934
1935         def addSongToPlaylistCallback(self,ret):
1936                 if ret:
1937                         sel = self.getCurrentSelection()
1938                         if sel:
1939                                 self.sqlCommand("INSERT INTO Playlist_Songs (playlist_id,song_id) VALUES(%d,%d);" % (ret[1],sel.songID))
1940                                 self.clearCache()
1941
1942         def createPlaylist(self):
1943                 self.session.openWithCallback(self.createPlaylistFinished, VirtualKeyBoard, title = _("Enter name for playlist"))
1944
1945         def createPlaylistFinished(self, text = None):
1946                 if text:
1947                         self.sqlCommand('INSERT INTO Playlists (playlist_text) VALUES("%s");' % (text))
1948                         self.clearCache()
1949                         self.menu_pressed()
1950
1951         def searchInIDreamDatabase(self):
1952                 options = [(_("search for title"), 1),
1953                         (_("search for artist"), 2),
1954                         (_("search for album"), 3),
1955                         (_("search in all of them"), 4),]
1956                 self.session.openWithCallback(self.enterSearchText, ChoiceBox,list = options)
1957
1958         def enterSearchText(self, ret):
1959                 if ret:
1960                         self.session.openWithCallback(boundFunction(self.enterSearchTextFinished,ret[1]), VirtualKeyBoard, title = _("Enter search-text"))
1961
1962         def enterSearchTextFinished(self, searchType, searchText = None):
1963                 if searchText:
1964                         search = "%" + searchText + "%"
1965                         if searchType == 1:
1966                                 sql_where = "where title like '%s'" % search
1967                                 text = _('Search results for "%s" in all titles') % searchText
1968                         elif searchType == 2:
1969                                 sql_where = "where artists.artist like '%s'" % search
1970                                 text = _('Search results for "%s" in all artists') % searchText
1971                         elif searchType == 3:
1972                                 sql_where = "where album_text like '%s'" % search
1973                                 text = _('Search results for "%s" in all albums') % searchText
1974                         else:
1975                                 sql_where = "where (title like '%s' or artists.artist like '%s' or album_text like '%s')"  % (search,search,search)
1976                                 text = _('Search results for "%s" in title, artist or album') % searchText
1977                         self.setButtons(red = True, yellow = True, blue = True)
1978                         oldmode = self.mode
1979                         self.mode = 20
1980                         self["list"].setMode(self.mode)
1981                         self.buildSearchSongList(sql_where, text, oldmode, True)
1982
1983
1984         def keyNumber_pressed(self, number):
1985                 if number == 0 and self.mode != 0:
1986                         self["list"].moveToIndex(0)
1987                         self.ok()
1988
1989         def ok(self):
1990                 sel = self.getCurrentSelection()
1991                 if sel is None:
1992                         return
1993                 if sel.mode == 99:
1994                         self.green_pressed()
1995                 else:
1996                         self.mode = sel.mode
1997                         self["list"].setMode(self.mode)
1998                         if sel.navigator and len(self.cacheList) > 0:
1999                                 cache = self.cacheList.pop()
2000                         else:
2001                                 cache = CacheList(cache = False, index = -1)
2002                         if sel.navigator: 
2003                                 self["headertext"].setText(cache.headertext)
2004                                 if cache.cache:
2005                                         self["list"].setList(cache.listview)
2006                                         self.LastMethod = MethodArguments(method = cache.methodarguments.method, arguments = cache.methodarguments.arguments)
2007                                 else:
2008                                         cache.methodarguments.method(**cache.methodarguments.arguments)
2009                                 self["list"].moveToIndex(cache.index)
2010                         if self.mode == 0:
2011                                 self.setButtons()
2012                                 if not sel.navigator:
2013                                         self.buildMainMenuList()
2014                         elif self.mode == 1:
2015                                 self.setButtons(red = True)
2016                                 if not sel.navigator:
2017                                         self.buildPlaylistList(addToCache = True)
2018                         elif self.mode == 2:
2019                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2020                                 if not sel.navigator:
2021                                         self.buildPlaylistSongList(playlistID = sel.playlistID, addToCache = True)
2022                         elif self.mode == 4:
2023                                 self.setButtons(red = True)
2024                                 if not sel.navigator:
2025                                         self.buildArtistList(addToCache = True)
2026                         elif self.mode == 5:
2027                                 self.setButtons(red = True)
2028                                 if not sel.navigator:
2029                                         self.buildArtistAlbumList(sel.artistID, addToCache = True)
2030                         elif self.mode == 6:
2031                                 self.setButtons(red = True, green = True, yellow = True)
2032                                 if not sel.navigator:
2033                                         self.buildAlbumSongList(albumID = sel.albumID, mode = 5, addToCache = True)
2034                         elif self.mode == 7:
2035                                 self.setButtons(red = True)
2036                                 if not sel.navigator:
2037                                         self.buildAlbumList(addToCache = True)
2038                         elif self.mode == 8:
2039                                 self.setButtons(red = True, green = True, yellow = True)
2040                                 if not sel.navigator:
2041                                         self.buildAlbumSongList(albumID = sel.albumID, mode = 7, addToCache = True)
2042                         elif self.mode == 10:
2043                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2044                                 if not sel.navigator:
2045                                         self.buildSongList(addToCache = True)
2046                         elif self.mode == 13:
2047                                 self.setButtons(red = True)
2048                                 if not sel.navigator:
2049                                         self.buildGenreList(addToCache = True)
2050                         elif self.mode == 14:
2051                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2052                                 if not sel.navigator:
2053                                         self.buildGenreSongList(genreID = sel.genreID, addToCache = True)
2054                         elif self.mode == 18 or self.mode == 19:
2055                                 if self.mode == 18:
2056                                         self.setButtons(red = True, green = True, yellow = True)
2057                                 if self.mode == 19:
2058                                         self.setButtons(red = True, green = True, blue = True)
2059                                 if not sel.navigator:
2060                                         self.red_pressed() # back to main menu --> normally that can not be happened
2061                         elif self.mode == 20:
2062                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2063                                 if not sel.navigator:
2064                                         self.red_pressed() # back to main menu --> normally that can not be happened
2065
2066         def buildPlaylistList(self, addToCache):
2067                 if addToCache:
2068                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2069                 arguments = {}
2070                 arguments["addToCache"] = False
2071                 self.LastMethod = MethodArguments(method = self.buildPlaylistList, arguments = arguments)
2072                 self["headertext"].setText(_("Playlists"))
2073                 connection = OpenDatabase()
2074                 if connection is not None:
2075                         connection.text_factory = str
2076                         cursor = connection.cursor()
2077                         playlistList = []
2078                         playlistList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2079                         cursor.execute("select playlists.playlist_id, playlist_text, count(Playlist_Songs.playlist_id) from playlists left outer join Playlist_Songs on playlists.playlist_id = Playlist_Songs.playlist_id group by playlists.playlist_id order by playlists.playlist_text;")
2080                         for row in cursor:
2081                                 playlistList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 2, playlistID = row[0]),))
2082                         cursor.close() 
2083                         connection.close()
2084                         self["list"].setList(playlistList)
2085                         if len(playlistList) > 1:
2086                                 self["list"].moveToIndex(1)
2087
2088         def buildPlaylistSongList(self, playlistID, addToCache):
2089                 if addToCache:
2090                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2091                 arguments = {}
2092                 arguments["playlistID"] = playlistID
2093                 arguments["addToCache"] = False
2094                 self.LastMethod = MethodArguments(method = self.buildPlaylistSongList, arguments = arguments)
2095                 connection = OpenDatabase()
2096                 if connection is not None:
2097                         connection.text_factory = str
2098                         cursor = connection.cursor()
2099                         playlistSongList = []
2100                         playlistSongList.append((Item(text = _("[back]"), mode = 1, navigator = True),))
2101                         cursor.execute("select songs.song_id, title, artists.artist, filename, songs.artist_id, bitrate, length, genre_text, track, date, album_text, songs.Album_id from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id inner join playlist_songs on songs.song_id = playlist_songs.song_id where playlist_songs.playlist_id =  %d order by playlist_songs.id;" % (playlistID))
2102                         for row in cursor:
2103                                 playlistSongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], artistID = row[4], bitrate = row[5], length = row[6], genre = row[7], track = row[8], date = row[9], album = row[10], albumID = row[11], playlistID = playlistID),))
2104                         cursor.execute("SELECT playlist_text from playlists where playlist_id = %d;" % playlistID)
2105                         row = cursor.fetchone()
2106                         self["headertext"].setText(_("Playlist (%s) -> Song List") % row[0])
2107                         cursor.close() 
2108                         connection.close()
2109                         self["list"].setList(playlistSongList)
2110                         if len(playlistSongList) > 1:
2111                                 self["list"].moveToIndex(1)
2112                                 
2113         def buildGenreList(self, addToCache):
2114                 if addToCache:
2115                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2116                 arguments = {}
2117                 arguments["addToCache"] = False
2118                 self.LastMethod = MethodArguments(method = self.buildGenreList, arguments = arguments)
2119                 self["headertext"].setText(_("Genre List"))
2120                 connection = OpenDatabase()
2121                 if connection is not None:
2122                         connection.text_factory = str
2123                         cursor = connection.cursor()
2124                         genreList = []
2125                         genreList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2126                         cursor.execute("select Genre.genre_id,Genre.Genre_text, count(*) from songs inner join Genre on songs.genre_id = Genre.Genre_id group by songs.Genre_id order by Genre.Genre_text;")
2127                         for row in cursor:
2128                                 genreList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 14, genreID = row[0]),))
2129                         cursor.close() 
2130                         connection.close()
2131                         self["list"].setList(genreList)
2132                         if len(genreList) > 1:
2133                                 self["list"].moveToIndex(1)
2134
2135         def buildGenreSongList(self, genreID, addToCache):
2136                 if addToCache:
2137                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2138                 arguments = {}
2139                 arguments["genreID"] = genreID
2140                 arguments["addToCache"] = False
2141                 self.LastMethod = MethodArguments(method = self.buildGenreSongList, arguments = arguments)
2142                 connection = OpenDatabase()
2143                 if connection is not None:
2144                         connection.text_factory = str
2145                         cursor = connection.cursor()
2146                         genreSongList = []
2147                         genreSongList.append((Item(text = _("[back]"), mode = 13, navigator = True),))
2148                         cursor.execute("select song_id, title, artists.artist, filename, songs.artist_id, bitrate, length, genre_text, track, date, album_text, songs.Album_id from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id where songs.genre_id = %d order by title, filename;" % (genreID))
2149                         for row in cursor:
2150                                 genreSongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], artistID = row[4], bitrate = row[5], length = row[6], genre = row[7], track = row[8], date = row[9], album = row[10], albumID = row[11], genreID = genreID),))
2151                         cursor.execute("SELECT genre_text from genre where genre_ID = %d;" % genreID)
2152                         row = cursor.fetchone()
2153                         self["headertext"].setText(_("Genre (%s) -> Song List") % row[0])
2154                         cursor.close() 
2155                         connection.close()
2156                         self["list"].setList(genreSongList)
2157                         if len(genreSongList) > 1:
2158                                 self["list"].moveToIndex(1)
2159
2160         def setButtons(self, red = False, green = False, yellow = False, blue = False):
2161                 if red:
2162                         self["key_red"].setText("Main Menu")
2163                 else:
2164                         self["key_red"].setText("")
2165                 if green:
2166                         self["key_green"].setText("Play")
2167                 else:
2168                         self["key_green"].setText("")
2169                 if yellow:
2170                         self["key_yellow"].setText("All Artists")
2171                 else:           
2172                         self["key_yellow"].setText("")
2173                 if blue:
2174                         self["key_blue"].setText("Show Album")
2175                 else:
2176                         self["key_blue"].setText("")
2177
2178         def info_pressed(self):
2179                 self.startMerlinPlayerScreenTimer.stop()
2180                 if self.player is not None:
2181                         if self.player.songList:
2182                                 self.session.execDialog(self.player)
2183
2184         def green_pressed(self):
2185                 try:
2186                         sel = self["list"].l.getCurrentSelection()[0]
2187                 except: 
2188                         sel = None
2189                 if sel is None:
2190                         return
2191                 if sel.songID != 0:
2192                         if self.player is not None:
2193                                 self.player.doClose()
2194                                 self.player = None
2195                         self.startMerlinPlayerScreenTimer.stop()
2196                         self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,self["list"].getList()[1:], self["list"].getCurrentIndex() -1, True, self.currentService, self.serviceList)
2197                         self.session.execDialog(self.player)
2198
2199         def red_pressed(self):
2200                 self.cacheList = []
2201                 self.setButtons()
2202                 self.mode = 0
2203                 self["list"].setMode(self.mode)
2204                 self.buildMainMenuList()
2205         
2206         def yellow_pressed(self):
2207                 try:
2208                         sel = self["list"].l.getCurrentSelection()[0]
2209                 except: 
2210                         return
2211                 if sel.artistID != 0:
2212                         oldmode = self.mode
2213                         self.mode = 19
2214                         self.setButtons(red = True, green = True, blue = True)
2215                         self["list"].setMode(self.mode)
2216                         self.buildArtistSongList(artistID = sel.artistID, mode = oldmode, addToCache = True)
2217                 
2218         def blue_pressed(self):
2219                 try:
2220                         sel = self["list"].l.getCurrentSelection()[0]
2221                 except: 
2222                         return
2223                 if sel.albumID != 0:
2224                         self.setButtons(red = True, green = True, yellow = True)
2225                         oldmode = self.mode
2226                         self.mode = 18
2227                         self["list"].setMode(self.mode)
2228                         self.buildAlbumSongList(albumID = sel.albumID, mode = oldmode, addToCache = True)
2229         
2230         def buildSongList(self, addToCache):
2231                 if addToCache:
2232                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2233                 arguments = {}
2234                 arguments["addToCache"] = False
2235                 self.LastMethod = MethodArguments(method = self.buildSongList,  arguments = arguments)
2236                 self["headertext"].setText(_("All Songs"))
2237                 connection = OpenDatabase()
2238                 if connection is not None:
2239                         connection.text_factory = str
2240                         cursor = connection.cursor()
2241                         SongList = []
2242                         SongList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2243                         cursor.execute("select song_id, title, artists.artist, filename, songs.artist_id, bitrate, length, genre_text, track, date, album_text, songs.Album_id from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id order by title, filename;")
2244                         for row in cursor:
2245                                 SongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], artistID = row[4], bitrate = row[5], length = row[6], genre = row[7], track = row[8], date = row[9], album = row[10], albumID = row[11]),))
2246                         cursor.close() 
2247                         connection.close()
2248                         self["list"].setList(SongList)
2249                         if len(SongList) > 1:
2250                                 self["list"].moveToIndex(1)
2251
2252
2253         def buildSearchSongList(self, sql_where, headerText, mode, addToCache):
2254                 if addToCache:
2255                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2256                 arguments = {}
2257                 arguments["sql_where"] = sql_where
2258                 arguments["headerText"] = headerText
2259                 arguments["mode"] = mode
2260                 arguments["addToCache"] = False
2261                 self.LastMethod = MethodArguments(method = self.buildSearchSongList, arguments = arguments)
2262                 self["headertext"].setText(headerText)
2263                 connection = OpenDatabase()
2264                 if connection is not None:
2265                         connection.text_factory = str
2266                         cursor = connection.cursor()
2267                         SongList = []
2268                         SongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2269                         cursor.execute("select song_id, title, artists.artist, filename, songs.artist_id, bitrate, length, genre_text, track, date, album_text, songs.Album_id from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id %s order by title, filename;" % sql_where)
2270                         for row in cursor:
2271                                 SongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], artistID = row[4], bitrate = row[5], length = row[6], genre = row[7], track = row[8], date = row[9], album = row[10], albumID = row[11]),))
2272                         cursor.close() 
2273                         connection.close()
2274                         self["list"].setList(SongList)
2275                         if len(SongList) > 1:
2276                                 self["list"].moveToIndex(1)
2277
2278         
2279         def buildArtistSongList(self, artistID, mode, addToCache):
2280                 if addToCache:
2281                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2282                 arguments = {}
2283                 arguments["artistID"] = artistID
2284                 arguments["mode"] = mode
2285                 arguments["addToCache"] = False
2286                 self.LastMethod = MethodArguments(method = self.buildArtistSongList, arguments = arguments)
2287                 connection = OpenDatabase()
2288                 if connection is not None:
2289                         connection.text_factory = str
2290                         cursor = connection.cursor()
2291                         artistSongList = []
2292                         artistSongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2293                         cursor.execute("select song_id, title, artists.artist, filename, bitrate, length, genre_text, track, date, album_text, songs.Album_id from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id where songs.artist_id = %d order by Album.album_text, tracknumber, filename;" % (artistID))
2294                         for row in cursor:
2295                                 artistSongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], bitrate = row[4], length = row[5], genre = row[6], track = row[7], date = row[8], album = row[9], albumID = row[10], artistID = artistID),))
2296                         cursor.execute("SELECT artist from artists where artist_ID = %d;" % artistID)
2297                         row = cursor.fetchone()
2298                         self["headertext"].setText(_("Artist (%s) -> Song List") % row[0])
2299                         cursor.close() 
2300                         connection.close()
2301                         self["list"].setList(artistSongList)
2302                         if len(artistSongList) > 1:
2303                                 self["list"].moveToIndex(1)
2304                         
2305         def buildAlbumSongList(self, albumID, mode, addToCache):
2306                 if addToCache:
2307                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2308                 arguments = {}
2309                 arguments["albumID"] = albumID
2310                 arguments["mode"] = mode
2311                 arguments["addToCache"] = False
2312                 self.LastMethod = MethodArguments(method = self.buildAlbumSongList, arguments = arguments)
2313                 connection = OpenDatabase()
2314                 if connection is not None:
2315                         connection.text_factory = str
2316                         cursor = connection.cursor()
2317                         albumSongList = []
2318                         albumSongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2319                         cursor.execute("select song_id, title, artists.artist, filename, songs.artist_id, bitrate, length, genre_text, track, date, album_text from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id where songs.album_id = %d order by tracknumber, filename;" % (albumID))
2320                         for row in cursor:
2321                                 albumSongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], artistID = row[4], bitrate = row[5], length = row[6], genre = row[7], track = row[8], date = row[9], album = row[10], albumID = albumID),))
2322                         cursor.execute("SELECT album_text from album where album_ID = %d;" % albumID)
2323                         row = cursor.fetchone()
2324                         self["headertext"].setText(_("Album (%s) -> Song List") % row[0])
2325                         cursor.close() 
2326                         connection.close()
2327                         self["list"].setList(albumSongList)
2328                         if len(albumSongList) > 1:
2329                                 self["list"].moveToIndex(1)
2330                 
2331         def buildMainMenuList(self, addToCache = True):
2332                 arguments = {}
2333                 arguments["addToCache"] = True
2334                 self.LastMethod = MethodArguments(method = self.buildMainMenuList, arguments = arguments)
2335                 self["headertext"].setText(_("iDream Main Menu"))
2336                 mainMenuList = []
2337                 connection = OpenDatabase()
2338                 if connection is not None:
2339                         connection.text_factory = str
2340                         cursor = connection.cursor()
2341                         # 1. Playlists
2342                         cursor.execute("SELECT COUNT (*) FROM playlists;")
2343                         row = cursor.fetchone()
2344                         mainMenuList.append((Item(text = _("Playlists (%d)") % row[0], mode = 1),))
2345                         # 2. Artists
2346                         cursor.execute("SELECT COUNT (*) FROM artists;")
2347                         row = cursor.fetchone()
2348                         mainMenuList.append((Item(text = _("Artists (%d)") % row[0], mode = 4),))
2349                         # 3. Albums
2350                         cursor.execute("SELECT COUNT (DISTINCT album_text) FROM album;")
2351                         row = cursor.fetchone()
2352                         mainMenuList.append((Item(text = _("Albums (%d)") % row[0], mode = 7),))
2353                         # 4. Songs
2354                         cursor.execute("SELECT COUNT (*) FROM songs;")
2355                         row = cursor.fetchone()
2356                         mainMenuList.append((Item(text = _("Songs (%d)") % row[0], mode = 10),))
2357                         # 5. Genres
2358                         cursor.execute("SELECT COUNT (*) FROM genre;")
2359                         row = cursor.fetchone()
2360                         mainMenuList.append((Item(text = _("Genres (%d)") % row[0], mode = 13),))
2361                         cursor.close()  
2362                         connection.close()
2363                         self["list"].setList(mainMenuList)
2364                         self["list"].moveToIndex(0)
2365                 
2366         def buildArtistList(self, addToCache):
2367                 if addToCache:
2368                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2369                 arguments = {}
2370                 arguments["addToCache"] = False
2371                 self.LastMethod = MethodArguments(method = self.buildArtistList, arguments = arguments)
2372                 self["headertext"].setText(_("Artists List"))
2373                 connection = OpenDatabase()
2374                 if connection is not None:
2375                         connection.text_factory = str
2376                         cursor = connection.cursor()
2377                         artistList = []
2378                         artistList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2379                         cursor.execute("SELECT artists.artist_id,artists.artist, count (distinct album.album_text) FROM songs INNER JOIN artists ON songs.artist_id = artists.artist_id inner join album on songs.album_id =  album.album_id GROUP BY songs.artist_id ORDER BY artists.artist;")
2380                         for row in cursor:
2381                                 artistList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 5, artistID = row[0]),))
2382                         cursor.close() 
2383                         connection.close()
2384                         self["list"].setList(artistList)
2385                 
2386         def buildArtistAlbumList(self, ArtistID, addToCache):
2387                 if addToCache:
2388                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2389                 arguments = {}
2390                 arguments["ArtistID"] = ArtistID
2391                 arguments["addToCache"] = False
2392                 self.LastMethod = MethodArguments(method = self.buildArtistAlbumList, arguments = arguments)
2393                 connection = OpenDatabase()
2394                 if connection is not None:
2395                         connection.text_factory = str
2396                         cursor = connection.cursor()
2397                         albumArtistList = []
2398                         albumArtistList.append((Item(text = _("[back]"), mode = 4, navigator = True),))
2399                         cursor.execute("select Album.Album_id,Album.Album_text from songs inner join Album on songs.Album_id = Album.Album_id where songs.artist_id = %d group by songs.Album_id order by Album.Album_text;" % ArtistID)
2400                         for row in cursor:
2401                                 cursor2 = connection.cursor()
2402                                 cursor2.execute("select count(song_id) from songs where album_id = %d;" % row[0])
2403                                 row2 = cursor2.fetchone()
2404                                 albumArtistList.append((Item(text = "%s (%d)" % (row[1], row2[0]), mode = 6, albumID = row[0], artistID = ArtistID),))
2405                                 cursor2.close()
2406                         cursor.execute("SELECT artist from artists where artist_ID = %d;" % ArtistID)
2407                         row = cursor.fetchone()
2408                         self["headertext"].setText(_("Artist (%s) -> Album List") % row[0])
2409                         cursor.close() 
2410                         connection.close()
2411                         self["list"].setList(albumArtistList)
2412                         if len(albumArtistList) > 1:
2413                                 self["list"].moveToIndex(1)
2414                         
2415         def buildAlbumList(self, addToCache):
2416                 if addToCache:
2417                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2418                 arguments = {}
2419                 arguments["addToCache"] = False
2420                 self.LastMethod = MethodArguments(method = self.buildAlbumList, arguments = arguments)
2421                 self["headertext"].setText(_("Albums List"))
2422                 connection = OpenDatabase()
2423                 if connection is not None:
2424                         connection.text_factory = str
2425                         cursor = connection.cursor()
2426                         albumList = []
2427                         albumList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2428                         cursor.execute("select Album.Album_id,Album.Album_text, count(*) from songs inner join Album on songs.Album_id = Album.Album_id group by songs.Album_id order by Album.Album_text;")
2429                         for row in cursor:
2430                                 albumList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 8, albumID = row[0]),))
2431                         cursor.close() 
2432                         connection.close()
2433                         self["list"].setList(albumList)
2434                         if len(albumList) > 1:
2435                                 self["list"].moveToIndex(1)
2436                         
2437         def startRun(self):
2438                 if pathToDatabase.isRunning:
2439                         self.showScanner = eTimer()
2440                         self.showScanner.callback.append(self.showScannerCallback)
2441                         self.showScanner.start(0,1)
2442                 else:
2443                         if config.plugins.merlinmusicplayer.startlastsonglist.value:
2444                                 self.startPlayerTimer = eTimer()
2445                                 self.startPlayerTimer.callback.append(self.startPlayerTimerCallback)
2446                                 self.startPlayerTimer.start(0,1)
2447                         self.mode = 0
2448                         self["list"].setMode(self.mode)
2449                         self.buildMainMenuList()
2450
2451         def showScannerCallback(self):
2452                 self.session.openWithCallback(self.filesAdded, iDreamAddToDatabase,None)
2453
2454
2455         def startPlayerTimerCallback(self):
2456                 connection = OpenDatabase()
2457                 if connection is not None:
2458                         connection.text_factory = str
2459                         cursor = connection.cursor()
2460                         iDreamMode = False
2461                         SongList = []
2462                         cursor.execute("select song_id, filename, title, artist, album, genre, bitrate, length,  track, date, PTS from CurrentSongList;")
2463                         for row in cursor:
2464                                 SongList.append((Item(songID = row[0], text = os_path.basename(row[1]), filename = row[1], title = row[2], artist = row[3], album = row[4], genre = row[5],  bitrate = row[6], length = row[7], track = row[8], date = row[9], PTS = row[10], join = False),))
2465                                 if row[0] != 0:
2466                                         iDreamMode = True
2467                         cursor.close() 
2468                         connection.close()
2469                         if self.player is not None:
2470                                 self.player.doClose()
2471                                 self.player = None
2472                         self.startMerlinPlayerScreenTimer.stop()
2473                         count = len(SongList)
2474                         if count:
2475                                 # just to be sure, check the index , it's critical
2476                                 index = config.plugins.merlinmusicplayer.lastsonglistindex.value
2477                                 if index >= count:
2478                                         index = 0
2479                                 self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,SongList, index, iDreamMode, self.currentService, self.serviceList)
2480                                 self.session.execDialog(self.player)
2481
2482         def config(self):
2483                 self.startMerlinPlayerScreenTimer.stop()
2484                 self.session.openWithCallback(self.setupFinished, MerlinMusicPlayerSetup, True)
2485
2486         def setupFinished(self, result):
2487                 if result:
2488                         self.red_pressed()
2489
2490
2491         def stopPlayingAndAppendFileToSongList(self):
2492                 self.startMerlinPlayerScreenTimer.stop()
2493                 if self.player is not None:
2494                         self.player.doClose()
2495                         self.player = None
2496                 self.appendFileToSongList()     
2497                 self.startMerlinPlayerScreenTimer.start(START_MERLIN_PLAYER_SCREEN_TIMER_VALUE)
2498
2499         def appendFileToSongList(self):
2500                 SongList = []
2501                 playerAvailable =  self.player is not None and self.player.songList
2502                 sel = self.getCurrentSelection()
2503                 if sel:
2504                         if playerAvailable:
2505                                 self.player.songList.append((sel,))
2506                                 self.player.origSongList.append((sel,))
2507                         else:
2508                                 SongList.append((sel,))
2509                         if not playerAvailable:
2510                                 if self.player is not None:
2511                                         self.player.doClose()
2512                                         self.player = None
2513                                 self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,SongList, 0, True, self.currentService, self.serviceList)
2514                                 self.player.playSong(self.player.songList[self.player.currentIndex][0].filename)
2515                                 self.player["coverArt"].onShow()
2516                                 self.player.init = 1
2517                         else:
2518                                 self.player["nextTitle"].setText(self.player.getNextTitle())
2519                                 self.session.open(MessageBox, _("%s\nappended to songlist")%sel.title, type = MessageBox.TYPE_INFO,timeout = 3 )
2520
2521         def insertFileToSongList(self):
2522                 sel = self.getCurrentSelection()
2523                 if sel:
2524                         if self.player is not None and self.player.songList:
2525                                 index = self.player.currentIndex
2526                                 self.player.songList.insert(index+1,(sel,))
2527                                 self.player.origSongList.insert(index+1,(sel,))
2528                                 self.player["nextTitle"].setText(self.player.getNextTitle())
2529                                 self.session.open(MessageBox, _("%s\ninserted and will be played as next song")%sel.title, type = MessageBox.TYPE_INFO,timeout = 3 )
2530                         else:
2531                                 self.appendFileToSongList()
2532
2533         def Error(self, error = None):
2534                 if error is not None:
2535                         self["list"].hide()
2536                         self["statustext"].setText(str(error.getErrorMessage()))
2537                         
2538         def closing(self):
2539                 self.close()
2540                 
2541         def __onClose(self):
2542                 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
2543                 self.startMerlinPlayerScreenTimer.stop()
2544                 if self.player is not None:
2545                         self.player.closePlayer()
2546                         self.player.doClose()
2547                         self.player = None
2548                 if self.serviceList is None:
2549                         self.session.nav.playService(self.currentService)
2550                 else: