new: close shoutcast/merlinmusicplayer when sleeptimer was set
[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["nextTitle"] = Label()
922                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
923                         {
924                                 iPlayableService.evUpdatedInfo: self.__evUpdatedInfo,
925                                 iPlayableService.evUser+10: self.__evAudioDecodeError,
926                                 iPlayableService.evUser+12: self.__evPluginError,
927                                 iPlayableService.evUser+13: self.embeddedCoverArt,
928                                 iPlayableService.evStart: self.__serviceStarted,
929                         })
930
931                 InfoBarSeek.__init__(self, actionmap = "MediaPlayerSeekActions")
932                 self.songList = songlist
933                 self.origSongList = songlist[:]
934                 self.currentIndex = index
935                 self.shuffle = False
936                 self.repeat = False
937                 self.currentFilename = ""
938                 self.currentGoogleCoverFile = ""
939                 self.googleDownloadDir = os_path.join(config.plugins.merlinmusicplayer.googleimagepath.value, "downloaded_covers/" )
940                 if not os_path.exists(self.googleDownloadDir):
941                         try:
942                                 os_mkdir(self.googleDownloadDir)
943                         except:
944                                 self.googleDownloadDir = "/tmp/"
945                                 
946                 self.init = 0
947                 self.onShown.append(self.__onShown)
948                 # for lcd
949                 self.currentTitle = ""
950                 self.nextTitle = ""
951                 self.screenSaverTimer = eTimer()
952                 self.screenSaverTimer.timeout.get().append(self.screenSaverTimerTimeout)
953                 self.screenSaverScreen = None
954
955                 self.iDreamMode = idreammode
956                 self.currentService = currentservice
957                 self.serviceList = servicelist
958
959                 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
960         
961         def sleepTimerEntryOnStateChange(self, timer):
962                 if timer.state == TimerEntry.StateEnded:
963                         self.closePlayer()
964
965         def embeddedCoverArt(self):             
966                 self["coverArt"].embeddedCoverArt()
967                 if self.screenSaverScreen:
968                         self.screenSaverScreen.updateCover(modus = 2)
969
970         def screenSaverTimerTimeout(self):
971                 if config.plugins.merlinmusicplayer.usescreensaver.value:
972                         if self.screenSaverTimer.isActive():
973                                 self.screenSaverTimer.stop()
974                         if not self.screenSaverScreen:
975                                 self.screenSaverScreen = self.session.instantiateDialog(MerlinMusicPlayerScreenSaver)
976                                 self.session.execDialog(self.screenSaverScreen)
977                                 self.screenSaverScreen.updateLCD(self.currentTitle,1)
978                                 self.screenSaverScreen.updateLCD(self.nextTitle,4)
979                                 album = self["album"].getText()
980                                 if album:
981                                         text = "%s - %s" % (self["title"].getText(), album)
982                                 else:
983                                         text = self["title"].getText()
984                                 self.screenSaverScreen.updateDisplayText(text)
985                                 self.screenSaverScreen.updateCover(self["coverArt"].coverArtFileName, modus = 0)
986
987         def resetScreenSaverTimer(self):
988                 if config.plugins.merlinmusicplayer.usescreensaver.value and config.plugins.merlinmusicplayer.screensaverwait.value != 0:
989                         if self.screenSaverTimer.isActive():
990                                 self.screenSaverTimer.stop()
991                         self.screenSaverTimer.start(config.plugins.merlinmusicplayer.screensaverwait.value * 60000)
992
993         def __onShown(self):
994                 if self.init == 0:
995                         self.init = 1
996                         self["coverArt"].onShow()
997                         self.playSong(self.songList[self.currentIndex][0].filename)
998                 else:
999                         self.summaries.setText(self.currentTitle,1)
1000                         self.summaries.setText(self.nextTitle,4)
1001                         if self.screenSaverScreen:
1002                                 self.screenSaverScreen.doClose()
1003                                 self.screenSaverScreen = None
1004                 self.resetScreenSaverTimer()
1005                 
1006         def __onClose(self):
1007                 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
1008                 del self["coverArt"].picload
1009                 self.seek = None
1010
1011         def config(self):
1012                 if self.screenSaverTimer.isActive():
1013                         self.screenSaverTimer.stop()
1014                 self.session.openWithCallback(self.setupFinished, MerlinMusicPlayerSetup, False)
1015
1016         def showTV(self):
1017                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1018                         if self.screenSaverTimer.isActive():
1019                                 self.screenSaverTimer.stop()
1020                         if self.screenSaverScreen:
1021                                 self.screenSaverScreen.doClose()
1022                                 self.screenSaverScreen = None
1023                         self.screenSaverScreen = self.session.instantiateDialog(MerlinMusicPlayerTV, self.currentService, self.serviceList)
1024                         self.session.execDialog(self.screenSaverScreen)
1025                         self.screenSaverScreen.updateLCD(self.currentTitle,1)
1026                         self.screenSaverScreen.updateLCD(self.nextTitle,4)
1027                         album = self["album"].getText()
1028                         if album:
1029                                 text = "%s - %s" % (self["title"].getText(), album)
1030                         else:
1031                                 text = self["title"].getText()
1032                         self.screenSaverScreen.updateDisplayText(text)
1033                         self.screenSaverScreen.updateCover(self["coverArt"].coverArtFileName, modus = 0)
1034         
1035         def setupFinished(self, result):
1036                 if result:
1037                         self.googleDownloadDir = os_path.join(config.plugins.merlinmusicplayer.googleimagepath.value, "downloaded_covers/" )
1038                         if not os_path.exists(self.googleDownloadDir):
1039                                 try:
1040                                         os_mkdir(self.googleDownloadDir)
1041                                 except:
1042                                         self.googleDownloadDir = "/tmp/"
1043                 self.resetScreenSaverTimer()
1044
1045         def closePlayer(self):  
1046                 if config.plugins.merlinmusicplayer.startlastsonglist.value:
1047                         config.plugins.merlinmusicplayer.lastsonglistindex.value = self.currentIndex
1048                         config.plugins.merlinmusicplayer.lastsonglistindex.save()
1049                         connection = OpenDatabase()
1050                         if connection is not None:
1051                                 connection.text_factory = str
1052                                 cursor = connection.cursor()
1053                                 cursor.execute("Delete from CurrentSongList;")
1054                                 for song in self.origSongList:
1055                                         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))
1056                                 connection.commit()
1057                                 cursor.close()
1058                                 connection.close()
1059                 if self.screenSaverTimer.isActive():
1060                         self.screenSaverTimer.stop()
1061                 self.close()
1062
1063         def playSong(self, filename):
1064                 self.session.nav.stopService()
1065                 self.seek = None
1066                 self.currentFilename = filename
1067                 if not config.plugins.merlinmusicplayer.hardwaredecoder.value and self.currentFilename.lower().endswith(".mp3") and self.songList[self.currentIndex][0].PTS is None:
1068                         sref = eServiceReference(ENIGMA_MERLINPLAYER_ID, 0, self.currentFilename) # play mp3 file with merlinmp3player lib
1069                         self.session.nav.playService(sref)
1070                         if self.iDreamMode:
1071                                 self.updateMusicInformation( self.songList[self.currentIndex][0].artist, self.songList[self.currentIndex][0].title, 
1072                                         self.songList[self.currentIndex][0].album, self.songList[self.currentIndex][0].genre, self.songList[self.currentIndex][0].date, clear = True )
1073                         else:
1074                                 path,filename = os_path.split(self.currentFilename)
1075                                 audio, isAudio, title, genre,artist,album,tracknr,track,date,length,bitrate = getID3Tags(path,filename)
1076                                 if audio:
1077                                         if date:
1078                                                 year = "(%s)" % str(date)
1079                                         else:
1080                                                 year = ""
1081                                         self.updateMusicInformation( artist, title, album, genre, year, clear = True )
1082                                 else:
1083                                         self.updateMusicInformation( title = title, clear = True)
1084                                 audio = None
1085                 else:
1086                         sref = eServiceReference(4097, 0, self.currentFilename)
1087                         self.session.nav.playService(sref)
1088                         if self.songList[self.currentIndex][0].PTS is not None:
1089                                 service = self.session.nav.getCurrentService()
1090                                 if service:
1091                                         self.seek = service.seek()
1092                                 self.updateMusicInformationCUE()
1093                                 self.ptsTimer = eTimer()
1094                                 self.ptsTimer.callback.append(self.ptsTimerCallback)
1095                                 self.ptsTimer.start(1000)
1096                 self["nextTitle"].setText(self.getNextTitle())
1097
1098         def ptsTimerCallback(self):
1099                 if self.seek:
1100                         pts = self.seek.getPlayPosition()
1101                         index = 0
1102                         currentIndex = 0
1103                         for songs in self.songList:
1104                                 if pts[1] > songs[0].PTS:
1105                                         currentIndex = index
1106                                 else:
1107                                         break
1108                                 index +=1
1109                         if currentIndex != self.currentIndex:
1110                                 self.currentIndex = currentIndex
1111                                 self.updateMusicInformationCUE()
1112                 self.ptsTimer.start(1000)
1113
1114         def updateMusicInformationCUE(self):
1115                 self.updateSingleMusicInformation("artist", self.songList[self.currentIndex][0].artist, True)
1116                 self.updateSingleMusicInformation("title", self.songList[self.currentIndex][0].title, True)
1117                 self.updateSingleMusicInformation("album", self.songList[self.currentIndex][0].album, True)
1118                 self.summaries.setText(self.songList[self.currentIndex][0].title,1)
1119                 if self.screenSaverScreen:
1120                         self.screenSaverScreen.updateLCD(self.songList[self.currentIndex][0].title,1)
1121                         if self.songList[self.currentIndex][0].album:
1122                                 self.screenSaverScreen.updateDisplayText("%s - %s" % (self.songList[self.currentIndex][0].title,self.songList[self.currentIndex][0].album))
1123                         else:
1124                                 self.screenSaverScreen.updateDisplayText(self.songList[self.currentIndex][0].title)
1125                 self.updateCover(self.songList[self.currentIndex][0].artist, self.songList[self.currentIndex][0].album)
1126                 self.currentTitle = self.songList[self.currentIndex][0].title
1127                 self["nextTitle"].setText(self.getNextTitle())
1128
1129         def __serviceStarted(self):
1130                 self["dvrStatus"].setPixmapNum(0)
1131
1132         def __evUpdatedInfo(self):
1133                 currPlay = self.session.nav.getCurrentService()
1134                 if currPlay is not None:
1135                         sTitle = currPlay.info().getInfoString(iServiceInformation.sTagTitle)
1136                         sAlbum = currPlay.info().getInfoString(iServiceInformation.sTagAlbum)
1137                         sArtist = currPlay.info().getInfoString(iServiceInformation.sTagArtist)
1138                         sGenre = currPlay.info().getInfoString(iServiceInformation.sTagGenre)
1139                         sYear = currPlay.info().getInfoString(iServiceInformation.sTagDate)
1140                         if sYear:
1141                                 sYear = "(%s)" % sYear
1142                         if not sTitle:
1143                                 sTitle = os_path.splitext(os_path.basename(self.currentFilename))[0]
1144                         
1145                         if self.songList[self.currentIndex][0].PTS is None:
1146                                 self.updateMusicInformation( sArtist, sTitle, sAlbum, sGenre, sYear, clear = True )
1147                         else:
1148                                 self.updateSingleMusicInformation("genre", sGenre, True)
1149                 else:
1150                         self.updateMusicInformation()
1151
1152         def updateMusicInformation(self, artist = "", title = "", album = "", genre = "", year = "", clear = False):
1153                 if year and album:
1154                         album = "%s %s" % (album, year)
1155                 self.updateSingleMusicInformation("artist", artist, clear)
1156                 self.updateSingleMusicInformation("title", title, clear)
1157                 self.updateSingleMusicInformation("album", album, clear)
1158                 self.updateSingleMusicInformation("genre", genre, clear)
1159                 self.currentTitle = title
1160                 if not self.iDreamMode and self.songList[self.currentIndex][0].PTS is None:
1161                         # for lyrics
1162                         self.songList[self.currentIndex][0].title = title
1163                         self.songList[self.currentIndex][0].artist = artist
1164                 self.summaries.setText(title,1)
1165                 if self.screenSaverScreen:
1166                         self.screenSaverScreen.updateLCD(title,1)
1167                         if album:
1168                                 self.screenSaverScreen.updateDisplayText("%s - %s" % (title,album))
1169                         else:
1170                                 self.screenSaverScreen.updateDisplayText(title)
1171                 self.updateCover(artist, album)
1172
1173         def updateCover(self, artist, album):
1174                 hasCover = False
1175                 audio = None
1176                 audiotype = 0
1177                 if self.currentFilename.lower().endswith(".mp3"):
1178                         try: 
1179                                 audio = ID3(self.currentFilename)
1180                                 audiotype = 1
1181                         except: audio = None
1182                 elif self.currentFilename.lower().endswith(".flac"):
1183                         try: 
1184                                 audio = FLAC(self.currentFilename)
1185                                 audiotype = 2
1186                         except: audio = None
1187                 elif self.currentFilename.lower().endswith(".m4a"):
1188                         try: 
1189                                 audio = MP4(self.currentFilename)
1190                                 audiotype = 3
1191                         except: audio = None
1192                 elif self.currentFilename.lower().endswith(".ogg"):
1193                         try:
1194                                 audio = OggVorbis(self.currentFilename)
1195                                 audiotype = 4
1196                         except: audio = None
1197                 if audio:
1198                         if audiotype == 1:
1199                                 apicframes = audio.getall("APIC")
1200                                 if len(apicframes) >= 1:
1201                                         hasCover = True
1202                                         if not config.plugins.merlinmusicplayer.hardwaredecoder.value:
1203                                                 coverArtFile = file("/tmp/.id3coverart", 'wb')
1204                                                 coverArtFile.write(apicframes[0].data)
1205                                                 coverArtFile.close()
1206                                                 self["coverArt"].embeddedCoverArt()
1207                                                 if self.screenSaverScreen:
1208                                                         self.screenSaverScreen.updateCover(modus = 2)
1209                         elif audiotype == 2:
1210                                 if len(audio.pictures) >= 1:
1211                                         hasCover = True
1212                         elif audiotype == 3:
1213                                 if 'covr' in audio.tags:
1214                                         hasCover = True
1215                         elif audiotype == 4:
1216                                 if 'METADATA_BLOCK_PICTURE' in audio.tags:
1217                                         hasCover = True
1218                         audio = None
1219                 if not hasCover:
1220                         if not self["coverArt"].updateCoverArt(self.currentFilename):
1221                                 if config.plugins.merlinmusicplayer.usegoogleimage.value:
1222                                         self.getGoogleCover(artist, album)
1223                                 else:
1224                                         self["coverArt"].showDefaultCover()
1225                                         if self.screenSaverScreen:
1226                                                 self.screenSaverScreen.updateCover(modus = 1)
1227                         else:
1228                                 if self.screenSaverScreen:
1229                                         self.screenSaverScreen.updateCover(filename = self.currentFilename, modus = 3)
1230                                 self.currentGoogleCoverFile = ""
1231                 else:
1232                         self.currentGoogleCoverFile = ""
1233
1234         def updateSingleMusicInformation(self, name, info, clear):
1235                 if info != "" or clear:
1236                         if self[name].getText() != info:
1237                                 self[name].setText(info)
1238
1239         def getGoogleCover(self, artist,album):
1240                 if artist != "" and album != "":
1241                         url = "http://images.google.de/images?q=%s+%s&btnG=Bilder-Suche" % (quote(album),quote(artist))
1242                         sendUrlCommand(url, None,10).addCallback(self.googleImageCallback).addErrback(self.coverDownloadFailed)
1243                 else:
1244                         self["coverArt"].showDefaultCover()
1245
1246         def googleImageCallback(self, result):
1247                 foundPos = result.find("imgres?imgurl=")
1248                 foundPos2 = result.find("&amp;imgrefurl=")
1249                 if foundPos != -1 and foundPos2 != -1:
1250                         url = result[foundPos+14:foundPos2]
1251                         parts = url.split("/")
1252                         filename = parts[-1]
1253                         if filename != self.currentGoogleCoverFile:
1254                                 self.currentGoogleCoverFile = filename
1255                                 filename = self.googleDownloadDir + parts[-1]
1256                                 if os_path.exists(filename):
1257                                         print "[MerlinMusicPlayer] using cover from %s " % filename
1258                                         self["coverArt"].showCoverFromFile(filename)
1259                                         if self.screenSaverScreen:
1260                                                 self.screenSaverScreen.updateCover(filename = filename, modus = 4)
1261                                 else:
1262                                         print "[MerlinMusicPlayer] downloading cover from %s " % url
1263                                         downloadPage(url , self.googleDownloadDir + parts[-1]).addCallback(boundFunction(self.coverDownloadFinished, filename)).addErrback(self.coverDownloadFailed)
1264
1265         def coverDownloadFailed(self,result):
1266                 print "[MerlinMusicPlayer] cover download failed: %s " % result
1267                 self["coverArt"].showDefaultCover()
1268                 if self.screenSaverScreen:
1269                         self.screenSaverScreen.updateCover(modus = 1)
1270
1271         def coverDownloadFinished(self,filename, result):
1272                 print "[MerlinMusicPlayer] cover download finished"
1273                 self["coverArt"].showCoverFromFile(filename)
1274                 if self.screenSaverScreen:
1275                         self.screenSaverScreen.updateCover(filename = filename, modus = 4)
1276
1277         def __evAudioDecodeError(self):
1278                 currPlay = self.session.nav.getCurrentService()
1279                 sAudioType = currPlay.info().getInfoString(iServiceInformation.sUser+10)
1280                 print "[MerlinMusicPlayer] audio-codec %s can't be decoded by hardware" % (sAudioType)
1281                 self.session.open(MessageBox, _("This Dreambox can't decode %s streams!") % sAudioType, type = MessageBox.TYPE_INFO,timeout = 20 )
1282
1283         def __evPluginError(self):
1284                 currPlay = self.session.nav.getCurrentService()
1285                 message = currPlay.info().getInfoString(iServiceInformation.sUser+12)
1286                 print "[MerlinMusicPlayer]" , message
1287                 self.session.open(MessageBox, message, type = MessageBox.TYPE_INFO,timeout = 20 )
1288
1289         def doEofInternal(self, playing):
1290                 if playing:
1291                         self.playNext()
1292
1293         def checkSkipShowHideLock(self):
1294                 self.updatedSeekState()
1295
1296         def updatedSeekState(self):
1297                 if self.seekstate == self.SEEK_STATE_PAUSE:
1298                         self["dvrStatus"].setPixmapNum(1)
1299                 elif self.seekstate == self.SEEK_STATE_PLAY:
1300                         self["dvrStatus"].setPixmapNum(0)
1301
1302         def pauseEntry(self):
1303                 self.pauseService()
1304                 self.resetScreenSaverTimer()
1305
1306         def play(self):
1307                 #play the current song from beginning again
1308                 if self.songList[self.currentIndex][0].PTS is None:
1309                         self.playSong(self.songList[self.currentIndex][0].filename)
1310                 else:
1311                         if self.seek:
1312                                 self.seek.seekTo(self.songList[self.currentIndex][0].PTS)
1313                                 self.updatedSeekState()
1314                 self.resetScreenSaverTimer()            
1315
1316         def unPauseService(self):
1317                 self.setSeekState(self.SEEK_STATE_PLAY)
1318
1319         def stopEntry(self):
1320                 self.seek = None
1321                 self.session.nav.stopService()
1322                 self.origSongList = []
1323                 self.songList = []
1324                 if config.plugins.merlinmusicplayer.startlastsonglist.value:
1325                         config.plugins.merlinmusicplayer.lastsonglistindex.value = -1
1326                         config.plugins.merlinmusicplayer.lastsonglistindex.save()
1327                         connection = OpenDatabase()
1328                         if connection is not None:
1329                                 connection.text_factory = str
1330                                 cursor = connection.cursor()
1331                                 cursor.execute("Delete from CurrentSongList;")
1332                                 connection.commit()
1333                                 cursor.close()
1334                                 connection.close()
1335                 self.resetScreenSaverTimer()
1336                 self.close()
1337
1338         def playNext(self):
1339                 if not self.repeat:
1340                         if self.currentIndex +1 > len(self.songList) -1:
1341                                 self.currentIndex = 0
1342                         else:
1343                                 self.currentIndex += 1
1344                 if self.songList[self.currentIndex][0].PTS is None:
1345                         self.playSong(self.songList[self.currentIndex][0].filename)
1346                 else:
1347                         self.playCUETrack()
1348                 if not self.screenSaverScreen:
1349                         self.resetScreenSaverTimer()
1350
1351         def playPrevious(self):
1352                 if not self.repeat:
1353                         if self.currentIndex - 1 < 0:
1354                                 self.currentIndex = len(self.songList) - 1
1355                         else:
1356                                 self.currentIndex -= 1
1357
1358                 if self.songList[self.currentIndex][0].PTS is None:
1359                         self.playSong(self.songList[self.currentIndex][0].filename)
1360                 else:
1361                         self.playCUETrack()
1362                 self.resetScreenSaverTimer()
1363
1364         def getNextTitle(self):
1365                 if self.repeat:
1366                         index = self.currentIndex
1367                 else:
1368                         if self.currentIndex + 1 > len(self.songList) -1:
1369                                 index = 0
1370                         else:
1371                                 index = self.currentIndex + 1
1372                 if self.iDreamMode or self.songList[index][0].PTS is not None:
1373                         text = "%s - %s" % (self.songList[index][0].title, self.songList[index][0].artist)
1374                 else:
1375                         if self.songList[index][0].filename.lower().startswith("http://"):
1376                                 text = self.songList[index][0].filename
1377                         else:
1378                                 path,filename = os_path.split(self.songList[index][0].filename)
1379                                 audio, isAudio, title, genre,artist,album,tracknr,track,date,length,bitrate = getID3Tags(path,filename)
1380                                 if audio:
1381                                         if artist:
1382                                                 text = "%s - %s" % (title, artist)
1383                                         else:
1384                                                 text = title
1385                                 else:
1386                                         text = title
1387                                 audio = None
1388                 self.nextTitle = text
1389                 self.summaries.setText(text,4)
1390                 if self.screenSaverScreen:
1391                         self.screenSaverScreen.updateLCD(text,4)
1392                 return str(text)
1393
1394         def shuffleList(self):
1395                 if self.songList[self.currentIndex][0].PTS is None: # not implemented for cue files yet
1396                         self.shuffle = not self.shuffle
1397                         if self.shuffle:
1398                                 self["shuffle"].setPixmapNum(1)
1399                                 shuffle(self.songList)
1400                         else:
1401                                 self.songList = self.origSongList[:]
1402                                 self["shuffle"].setPixmapNum(0)
1403                         index = 0
1404                         for x in self.songList:
1405                                 if x[0].filename == self.currentFilename:
1406                                         self.currentIndex = index
1407                                         break
1408                                 index += 1
1409                         self["nextTitle"].setText(self.getNextTitle())
1410                 else:
1411                         self.session.open(MessageBox, _("Shuffle is not available yet with cue-files!"), type = MessageBox.TYPE_INFO,timeout = 20 )
1412                 self.resetScreenSaverTimer()
1413
1414         def repeatSong(self):
1415                 if self.songList[self.currentIndex][0].PTS is None: # not implemented for cue files yet
1416                         self.repeat = not self.repeat
1417                         if self.repeat:
1418                                 self["repeat"].setPixmapNum(1)
1419                         else:
1420                                 self["repeat"].setPixmapNum(0)
1421                         self["nextTitle"].setText(self.getNextTitle())
1422                 else:
1423                         self.session.open(MessageBox, _("Repeat is not available yet with cue-files!"), type = MessageBox.TYPE_INFO,timeout = 20 )
1424                 self.resetScreenSaverTimer()
1425
1426         def showPlaylist(self):
1427                 if self.screenSaverTimer.isActive():
1428                         self.screenSaverTimer.stop()
1429                 self.session.openWithCallback(self.showPlaylistCallback, MerlinMusicPlayerSongList, self.songList, self.currentIndex, self.iDreamMode)
1430
1431         def showPlaylistCallback(self, index):
1432                 if index != -1:
1433                         self.currentIndex = index
1434
1435                         if self.songList[self.currentIndex][0].PTS is None:
1436                                 self.playSong(self.songList[self.currentIndex][0].filename)
1437                         else:
1438                                 self.playCUETrack()             
1439
1440                 self.resetScreenSaverTimer()
1441
1442         def playCUETrack(self):
1443                 if self.ptsTimer.isActive():
1444                         self.ptsTimer.stop()
1445                 if self.seek:
1446                         self.seek.seekTo(self.songList[self.currentIndex][0].PTS)
1447                         self.updatedSeekState()
1448                         self.updateMusicInformationCUE()
1449                         self.ptsTimer.start(1000)
1450
1451         def showLyrics(self):
1452                 if self.screenSaverTimer.isActive():
1453                         self.screenSaverTimer.stop()
1454                 self.session.openWithCallback(self.resetScreenSaverTimer, MerlinMusicPlayerLyrics, self.songList[self.currentIndex][0])
1455
1456         def createSummary(self):
1457                 return MerlinMusicPlayerLCDScreen
1458
1459 class MerlinMusicPlayerLyrics(Screen):
1460
1461         sz_w = getDesktop(0).size().width()
1462         if sz_w == 1280:
1463                 skin = """
1464                         <screen name="MerlinMusicPlayerLyrics" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="Merlin Music Player Lyrics">
1465                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1466                         <eLabel backgroundColor="#999999" position="178,112" size="924,2" zPosition="1"/>
1467                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="178,104" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1468                         <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"/>
1469                         <widget name="headertext" position="178,145" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1470                         <widget name="resulttext" position="178,172" zPosition="1" size="900,20" font="Regular;16" transparent="1"   backgroundColor="#00000000"/>
1471                         <widget name="lyric_text" position="178,222" zPosition="2" size="940,350" font="Regular;18" transparent="0"  backgroundColor="#00000000"/>
1472                         </screen>"""
1473         elif sz_w == 1024:
1474                 skin = """
1475                         <screen name="MerlinMusicPlayerLyrics" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="Merlin Music Player Lyrics">
1476                         <eLabel backgroundColor="#999999" position="50,40" size="924,2" zPosition="1"/>
1477                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,32" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1478                         <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"/>
1479                         <widget name="headertext" position="50,73" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1480                         <widget name="resulttext" position="50,100" zPosition="1" size="900,20" font="Regular;16" transparent="1"   backgroundColor="#00000000"/>
1481                         <widget name="lyric_text" position="50,150" zPosition="2" size="940,350" font="Regular;18" transparent="0"  backgroundColor="#00000000"/>
1482                         </screen>"""
1483         else:
1484                 skin = """
1485                         <screen name="MerlinMusicPlayerLyrics" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="Merlin Music Player Lyrics">
1486                         <eLabel backgroundColor="#999999" position="50,50" size="620,2" zPosition="1"/>
1487                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,40" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1488                         <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"/>
1489                         <widget name="headertext" position="50,73" zPosition="1" size="620,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1490                         <widget name="resulttext" position="50,100" zPosition="1" size="620,20" font="Regular;16" transparent="1"   backgroundColor="#00000000"/>
1491                         <widget name="lyric_text" position="50,150" zPosition="2" size="620,350" font="Regular;18" transparent="0"  backgroundColor="#00000000"/>
1492                         </screen>"""
1493                 
1494         
1495         def __init__(self, session, currentsong):
1496                 self.session = session
1497                 Screen.__init__(self, session)
1498                 self["headertext"] = Label(_("Merlin Music Player Lyrics"))
1499                 # leoslyrics does not work anymore
1500 #               self["resulttext"] = Label(_("Getting lyrics from api.leoslyrics.com..."))
1501                 self["resulttext"] = Label()
1502                 self["actions"] = ActionMap(["WizardActions", "DirectionActions"],
1503                 {
1504                         "back": self.close,
1505                         "upUp": self.pageUp,
1506                         "leftUp": self.pageUp,
1507                         "downUp": self.pageDown,
1508                         "rightUp": self.pageDown,
1509                 }, -1)
1510                 self["lyric_text"] = ScrollLabel()
1511                 self.currentSong = currentsong
1512                 self.onLayoutFinish.append(self.startRun)
1513
1514         def startRun(self):
1515                 # get lyric-text from id3 tag
1516                 try:
1517                         audio = ID3(self.currentSong.filename)
1518                 except:
1519                         audio = None
1520                 if audio:
1521                         text = getEncodedString(self.getLyricsFromID3Tag(audio)).replace("\r\n","\n")
1522                         text = text.replace("\r","\n")
1523                         self["lyric_text"].setText(text)
1524                 else:
1525                         self["lyric_text"].setText("No lyrics found")
1526   
1527         def getLyricsFromID3Tag(self,tag):
1528                 for frame in tag.values():
1529                         if frame.FrameID == "USLT":
1530                                 return frame.text
1531                 url = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=%s&song=%s" % (quote(self.currentSong.artist), quote(self.currentSong.title))
1532                 sendUrlCommand(url, None,10).addCallback(self.gotLyrics).addErrback(self.urlError)
1533                 return "No lyrics found in id3-tag, trying api.chartlyrics.com..."
1534
1535         
1536         def urlError(self, error = None):
1537                 if error is not None:
1538                         self["resulttext"].setText(str(error.getErrorMessage()))
1539                         self["lyric_text"].setText("")
1540
1541         def gotLyrics(self, xmlstring):
1542                 root = cet_fromstring(xmlstring)
1543                 lyrictext = ""
1544                 lyrictext = root.findtext("{http://api.chartlyrics.com/}Lyric").encode("utf-8", 'ignore')
1545                 self["lyric_text"].setText(lyrictext)
1546                 title = root.findtext("{http://api.chartlyrics.com/}LyricSong").encode("utf-8", 'ignore')
1547                 artist = root.findtext("{http://api.chartlyrics.com/}LyricArtist").encode("utf-8", 'ignore')
1548                 result = _("Response -> lyrics for: %s (%s)") % (title,artist)
1549                 self["resulttext"].setText(result)
1550                 if not lyrictext:
1551                         self["resulttext"].setText(_("No lyrics found"))
1552                         self["lyric_text"].setText("")
1553
1554         def pageUp(self):
1555                 self["lyric_text"].pageUp()
1556
1557         def pageDown(self):
1558                 self["lyric_text"].pageDown()   
1559
1560 class MerlinMusicPlayerSongList(Screen):
1561         
1562         sz_w = getDesktop(0).size().width()
1563         if sz_w == 1280:
1564                 skin = """
1565                         <screen name="MerlinMusicPlayerSongList" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1566                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1567                         <eLabel backgroundColor="#999999" position="178,112" size="924,2" zPosition="1"/>
1568                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="178,104" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1569                         <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"/>
1570                         <widget name="headertext" position="178,145" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1571                         <widget name="list" position="178,182" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1572                         </screen>"""
1573         elif sz_w == 1024:
1574                 skin = """
1575                         <screen name="MerlinMusicPlayerSongList" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1576                         <eLabel backgroundColor="#999999" position="50,40" size="924,2" zPosition="1"/>
1577                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,32" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1578                         <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"/>
1579                         <widget name="headertext" position="50,73" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1580                         <widget name="list" position="50,110" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1581                         </screen>"""
1582         else:
1583                 skin = """
1584                         <screen name="MerlinMusicPlayerSongList" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1585                         <eLabel backgroundColor="#999999" position="50,50" size="620,2" zPosition="1"/>
1586                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,40" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1587                         <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"/>
1588                         <widget name="headertext" position="50,73" zPosition="1" size="620,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1589                         <widget name="list" position="50,110" zPosition="2" size="620,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1590                         </screen>"""
1591                 
1592         
1593         def __init__(self, session, songlist, index, idreammode):
1594                 self.session = session
1595                 Screen.__init__(self, session)
1596                 self["headertext"] = Label(_("Merlin Music Player Songlist"))
1597                 self["list"] = iDreamList()
1598                 self["list"].connectSelChanged(self.lcdUpdate)
1599                 self["actions"] = ActionMap(["WizardActions"],
1600                 {
1601                         "ok": self.ok,
1602                         "back": self.closing,
1603                 }, -1)
1604                 self.songList = songlist
1605                 self.index = index
1606                 self.iDreamMode = idreammode
1607                 self.onLayoutFinish.append(self.startRun)
1608                 self.onShown.append(self.lcdUpdate)
1609
1610         def startRun(self):
1611                 if self.iDreamMode:
1612                         self["list"].setMode(10) # songlist
1613                 self["list"].setList(self.songList)
1614                 self["list"].moveToIndex(self.index)
1615
1616         def ok(self):
1617                 self.close(self["list"].getCurrentIndex())
1618
1619         def closing(self):
1620                 self.close(-1)
1621
1622         def lcdUpdate(self):
1623                 try:
1624                         index = self["list"].getCurrentIndex()
1625                         songlist = self["list"].getList()
1626                         mode =  self.iDreamMode or songlist[index][0].PTS
1627                         if mode:
1628                                 self.summaries.setText(songlist[index][0].title,1)
1629                         else:
1630                                 self.summaries.setText(songlist[index][0].text,1)
1631                         count = self["list"].getItemCount()
1632                         # voheriges
1633                         index -= 1
1634                         if index < 0:
1635                                 index = count
1636                         if mode:
1637                                 self.summaries.setText(songlist[index][0].title,3)
1638                         else:
1639                                 self.summaries.setText(songlist[index][0].text,3)
1640                         # naechstes
1641                         index = self["list"].getCurrentIndex() + 1
1642                         if index > count:
1643                                 index = 0
1644                         if mode:
1645                                 self.summaries.setText(songlist[index][0].title,4)
1646                         else:
1647                                 self.summaries.setText(songlist[index][0].text,4)
1648                 except: pass
1649
1650         def createSummary(self):
1651                 return MerlinMusicPlayerLCDScreenText
1652
1653 class iDreamMerlin(Screen):
1654         
1655
1656         sz_w = getDesktop(0).size().width()
1657         if sz_w == 1280:
1658                 skin = """
1659                         <screen name="iDreamMerlin" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1660                                 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1661                                 <ePixmap position="178,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1662                                 <ePixmap position="328,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1663                                 <ePixmap position="478,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1664                                 <ePixmap position="628,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1665                                 <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" />
1666                                 <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" />
1667                                 <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" />
1668                                 <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" />
1669                                 <widget name="headertext" position="178,149" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1670                                 <widget name="list" position="178,182" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1671                         </screen>"""
1672         elif sz_w == 1024:
1673                 skin = """
1674                         <screen name="iDreamMerlin" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1675                                 <ePixmap position="50,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1676                                 <ePixmap position="200,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1677                                 <ePixmap position="350,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1678                                 <ePixmap position="500,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1679                                 <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" />
1680                                 <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" />
1681                                 <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" />
1682                                 <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" />
1683                                 <widget name="headertext" position="50,77" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1684                                 <widget name="list" position="50,110" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1685                         </screen>"""
1686         else:
1687                 skin = """
1688                         <screen name="iDreamMerlin" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1689                                 <ePixmap position="50,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1690                                 <ePixmap position="200,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1691                                 <ePixmap position="350,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1692                                 <ePixmap position="500,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1693                                 <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" />
1694                                 <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" />
1695                                 <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" />
1696                                 <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" />
1697                                 <widget name="headertext" position="50,77" zPosition="1" size="620,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1698                                 <widget name="list" position="50,110" zPosition="2" size="620,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1699                         </screen>"""
1700                 
1701         
1702         def __init__(self, session, servicelist):
1703                 self.session = session
1704                 Screen.__init__(self, session)
1705                 self["list"] = iDreamList()
1706                 self["list"].connectSelChanged(self.lcdUpdate)
1707
1708
1709                 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "ColorActions", "EPGSelectActions"],
1710                 {
1711                         "ok": self.ok,
1712                         "back": self.closing,
1713                         "red": self.red_pressed,
1714                         "green": self.green_pressed,
1715                         "yellow": self.yellow_pressed,
1716                         "blue": self.blue_pressed,
1717                         "input_date_time": self.menu_pressed,
1718                         "info" : self.info_pressed,
1719                 }, -1)
1720
1721                 self["actions2"] = NumberActionMap(["InputActions"],
1722                 {
1723                         "0": self.keyNumber_pressed,
1724                 }, -1)
1725
1726                 self.onLayoutFinish.append(self.startRun)
1727                 self.onShown.append(self.lcdUpdate)
1728                 self.onClose.append(self.__onClose)
1729                 
1730                 self.serviceList = servicelist
1731                 self.currentService = self.session.nav.getCurrentlyPlayingServiceReference()
1732                 self.session.nav.stopService()
1733                 
1734                 self.mode = 0
1735                 self.mainMenuList = []
1736                 self.cacheList = []
1737                 self.LastMethod = None
1738                 self.player = None
1739                 
1740                 self["key_red"] = StaticText("")
1741                 self["key_green"] = StaticText("")
1742                 self["key_yellow"] = StaticText("")
1743                 self["key_blue"] = StaticText("")
1744                 self["headertext"] = Label(_("iDream Main Menu"))
1745
1746                 self.startMerlinPlayerScreenTimer = eTimer()
1747                 self.startMerlinPlayerScreenTimer.timeout.get().append(self.info_pressed)
1748
1749                 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
1750         
1751         def sleepTimerEntryOnStateChange(self, timer):
1752                 if timer.state == TimerEntry.StateEnded:
1753                         self.close()
1754
1755         def getPlayList(self):
1756                 connection = OpenDatabase()
1757                 if connection is not None:
1758                         connection.text_factory = str
1759                         cursor = connection.cursor()
1760                         playList = []
1761                         cursor.execute("select playlist_id,playlist_text from playlists order by playlist_text;")
1762                         for row in cursor:
1763                                 playList.append((row[1], row[0]))
1764                         cursor.close()  
1765                         connection.close()
1766                         return playList
1767                 else:
1768                         return None
1769
1770         def sqlCommand(self, sqlSatement):
1771                 connection = OpenDatabase()
1772                 if connection is not None:
1773                         cursor = connection.cursor()
1774                         cursor.execute(sqlSatement)
1775                         cursor.close()
1776                         connection.commit()
1777                         connection.close()
1778
1779         def clearCache(self):
1780                 for items in self.cacheList:
1781                         items.cache = False
1782                         items.listview = []
1783                         items.headertext = ""
1784
1785         def getCurrentSelection(self):
1786                 sel = None
1787                 try: sel = self["list"].l.getCurrentSelection()[0]
1788                 except: pass
1789                 return sel
1790
1791         def addListToPlaylistConfirmed(self, methodName, answer):
1792                 if answer:
1793                         playList = self.getPlayList()
1794                         if len(playList):
1795                                 self.session.openWithCallback(methodName, ChoiceBox,list = playList)
1796                         else:
1797                                 self.session.openWithCallback(self.createPlaylistConfirmed, MessageBox, _("There are no playlists defined.\nDo you want to create a new playlist?"))
1798
1799         def menu_pressed(self):
1800                 self.startMerlinPlayerScreenTimer.stop()
1801                 options = [(_("Configuration"), self.config),(_("Search in iDream database"), self.searchInIDreamDatabase),]
1802                 options.extend(((_("Scan path for music files and add them to database"), self.scanDir),))
1803                 if self.mode != 1:
1804                         options.extend(((_("Create new playlist"), self.createPlaylist),))
1805                 if self["list"].getDisplaySongMode():
1806                         if self.mode == 2:
1807                                 options.extend(((_("Delete song from current playlist"), self.deleteSongFromPlaylist),))
1808                         else:
1809                                 options.extend(((_("Add selected song to a playlist"), self.addSongToPlaylist),))
1810                                 if self.mode == 18:
1811                                         options.extend(((_("Add all songs from selected album to a playlist"), self.addAlbumToPlaylist),))
1812                                 elif self.mode == 19:
1813                                         options.extend(((_("Add all songs from selected artist to a playlist"), self.addArtistToPlaylist),))
1814                                 options.extend(((_("Delete song from database"), self.deleteSongFromDatabase),))
1815                         options.extend(((_("Clear current songlist and play selected entry"), self.stopPlayingAndAppendFileToSongList),))
1816                         options.extend(((_("Append file to current playing songlist"), self.appendFileToSongList),))
1817                         if self.player is not None and self.player.songList:
1818                                 options.extend(((_("Insert file to current playing songlist and play next"), self.insertFileToSongList),))
1819                 else:
1820                         if self.mode == 1:
1821                                 options.extend(((_("Delete selected playlist"), self.deletePlaylist),))
1822                         elif self.mode == 4:
1823                                 options.extend(((_("Add all songs from selected artist to a playlist"), self.addArtistToPlaylist),))
1824                         elif self.mode == 5 or self.mode == 7:
1825                                 options.extend(((_("Add all songs from selected album to a playlist"), self.addAlbumToPlaylist),))
1826                         elif self.mode == 13:
1827                                 options.extend(((_("Add all songs from selected genre to a playlist"), self.addGenreToPlaylist),))
1828                 self.session.openWithCallback(self.menuCallback, ChoiceBox,list = options)
1829
1830         def menuCallback(self, ret):
1831                 ret and ret[1]()
1832
1833         def scanDir(self):
1834                 SelectPath
1835                 self.session.openWithCallback(self.pathSelected,SelectPath,"/media/")
1836
1837         def pathSelected(self, res):
1838                 if res is not None:
1839                         self.session.openWithCallback(self.filesAdded, iDreamAddToDatabase,res)
1840
1841         def filesAdded(self):
1842                 if pathToDatabase.isRunning:
1843                         self.close()
1844                 else:
1845                         self.red_pressed()
1846                 
1847
1848
1849
1850         def addGenreToPlaylist(self):
1851                 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed,self.addGenreToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that genre to a playlist?"))
1852
1853         def addGenreToPlaylistConfirmedCallback(self, ret):
1854                 if ret:
1855                         sel = self.getCurrentSelection()
1856                         if sel:
1857                                 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))
1858                                 self.clearCache()
1859
1860         def addArtistToPlaylist(self):
1861                 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed, self.addArtistToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that artist to a playlist?"))
1862
1863         def addArtistToPlaylistConfirmedCallback(self, ret):
1864                 if ret:
1865                         sel = self.getCurrentSelection()
1866                         if sel:
1867                                 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))
1868                                 self.clearCache()
1869
1870         def addAlbumToPlaylist(self):
1871                 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed, self.addAlbumToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that album to a playlist?"))
1872
1873         def addAlbumToPlaylistConfirmedCallback(self, ret):
1874                 if ret:
1875                         sel = self.getCurrentSelection()
1876                         if sel:
1877                                 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))
1878                                 self.clearCache()
1879
1880         def deletePlaylist(self):
1881                 self.session.openWithCallback(self.deletePlaylistConfirmed, MessageBox, _("Do you really want to delete the current playlist?"))
1882
1883         def deletePlaylistConfirmed(self, answer):
1884                 if answer:
1885                         sel = self.getCurrentSelection()
1886                         if sel:
1887                                 self.sqlCommand("delete from playlist_songs where playlist_id = %d" % (sel.playlistID))
1888                                 self.sqlCommand("delete from playlists where playlist_id = %d" % (sel.playlistID))
1889                                 self["list"].removeItem(self["list"].getCurrentIndex())
1890                                 self.clearCache()
1891
1892
1893         def deleteSongFromPlaylist(self):
1894                 self.session.openWithCallback(self.deleteSongFromPlaylistConfirmed, MessageBox, _("Do you really want to delete that song the current playlist?"))
1895
1896         def deleteSongFromPlaylistConfirmed(self, answer):
1897                 if answer:
1898                         sel = self.getCurrentSelection()
1899                         if sel:
1900                                 self.sqlCommand("delete from playlist_songs where song_id = %d" % (sel.songID))
1901                                 self["list"].removeItem(self["list"].getCurrentIndex())
1902                                 self.clearCache()
1903
1904         def deleteSongFromDatabase(self):
1905                 self.session.openWithCallback(self.deleteSongFromDatabaseConfirmed, MessageBox, _("Do you really want to delete that song from the database?"))
1906
1907         def deleteSongFromDatabaseConfirmed(self, answer):
1908                 if answer:
1909                         sel = self.getCurrentSelection()
1910                         if sel:
1911                                 self.sqlCommand("delete from playlist_songs where song_id = %d" % (sel.songID))
1912                                 self.sqlCommand("delete from songs where song_id = %d" % (sel.songID))
1913                                 self["list"].removeItem(self["list"].getCurrentIndex())
1914                                 self.clearCache()
1915                         
1916         def addSongToPlaylist(self):
1917                 playList = self.getPlayList()
1918                 if len(playList):
1919                         self.session.openWithCallback(self.addSongToPlaylistCallback, ChoiceBox,list = playList)
1920                 else:
1921                         self.session.openWithCallback(self.createPlaylistConfirmed, MessageBox, _("There are no playlists defined.\nDo you want to create a new playlist?"))
1922
1923         def createPlaylistConfirmed(self, val):
1924                 if val:
1925                         self.createPlaylist()
1926
1927         def addSongToPlaylistCallback(self,ret):
1928                 if ret:
1929                         sel = self.getCurrentSelection()
1930                         if sel:
1931                                 self.sqlCommand("INSERT INTO Playlist_Songs (playlist_id,song_id) VALUES(%d,%d);" % (ret[1],sel.songID))
1932                                 self.clearCache()
1933
1934         def createPlaylist(self):
1935                 self.session.openWithCallback(self.createPlaylistFinished, VirtualKeyBoard, title = _("Enter name for playlist"))
1936
1937         def createPlaylistFinished(self, text = None):
1938                 if text:
1939                         self.sqlCommand('INSERT INTO Playlists (playlist_text) VALUES("%s");' % (text))
1940                         self.clearCache()
1941                         self.menu_pressed()
1942
1943         def searchInIDreamDatabase(self):
1944                 options = [(_("search for title"), 1),
1945                         (_("search for artist"), 2),
1946                         (_("search for album"), 3),
1947                         (_("search in all of them"), 4),]
1948                 self.session.openWithCallback(self.enterSearchText, ChoiceBox,list = options)
1949
1950         def enterSearchText(self, ret):
1951                 if ret:
1952                         self.session.openWithCallback(boundFunction(self.enterSearchTextFinished,ret[1]), VirtualKeyBoard, title = _("Enter search-text"))
1953
1954         def enterSearchTextFinished(self, searchType, searchText = None):
1955                 if searchText:
1956                         search = "%" + searchText + "%"
1957                         if searchType == 1:
1958                                 sql_where = "where title like '%s'" % search
1959                                 text = _('Search results for "%s" in all titles') % searchText
1960                         elif searchType == 2:
1961                                 sql_where = "where artists.artist like '%s'" % search
1962                                 text = _('Search results for "%s" in all artists') % searchText
1963                         elif searchType == 3:
1964                                 sql_where = "where album_text like '%s'" % search
1965                                 text = _('Search results for "%s" in all albums') % searchText
1966                         else:
1967                                 sql_where = "where (title like '%s' or artists.artist like '%s' or album_text like '%s')"  % (search,search,search)
1968                                 text = _('Search results for "%s" in title, artist or album') % searchText
1969                         self.setButtons(red = True, yellow = True, blue = True)
1970                         oldmode = self.mode
1971                         self.mode = 20
1972                         self["list"].setMode(self.mode)
1973                         self.buildSearchSongList(sql_where, text, oldmode, True)
1974
1975
1976         def keyNumber_pressed(self, number):
1977                 if number == 0 and self.mode != 0:
1978                         self["list"].moveToIndex(0)
1979                         self.ok()
1980
1981         def ok(self):
1982                 sel = self.getCurrentSelection()
1983                 if sel is None:
1984                         return
1985                 if sel.mode == 99:
1986                         self.green_pressed()
1987                 else:
1988                         self.mode = sel.mode
1989                         self["list"].setMode(self.mode)
1990                         if sel.navigator and len(self.cacheList) > 0:
1991                                 cache = self.cacheList.pop()
1992                         else:
1993                                 cache = CacheList(cache = False, index = -1)
1994                         if sel.navigator: 
1995                                 self["headertext"].setText(cache.headertext)
1996                                 if cache.cache:
1997                                         self["list"].setList(cache.listview)
1998                                         self.LastMethod = MethodArguments(method = cache.methodarguments.method, arguments = cache.methodarguments.arguments)
1999                                 else:
2000                                         cache.methodarguments.method(**cache.methodarguments.arguments)
2001                                 self["list"].moveToIndex(cache.index)
2002                         if self.mode == 0:
2003                                 self.setButtons()
2004                                 if not sel.navigator:
2005                                         self.buildMainMenuList()
2006                         elif self.mode == 1:
2007                                 self.setButtons(red = True)
2008                                 if not sel.navigator:
2009                                         self.buildPlaylistList(addToCache = True)
2010                         elif self.mode == 2:
2011                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2012                                 if not sel.navigator:
2013                                         self.buildPlaylistSongList(playlistID = sel.playlistID, addToCache = True)
2014                         elif self.mode == 4:
2015                                 self.setButtons(red = True)
2016                                 if not sel.navigator:
2017                                         self.buildArtistList(addToCache = True)
2018                         elif self.mode == 5:
2019                                 self.setButtons(red = True)
2020                                 if not sel.navigator:
2021                                         self.buildArtistAlbumList(sel.artistID, addToCache = True)
2022                         elif self.mode == 6:
2023                                 self.setButtons(red = True, green = True, yellow = True)
2024                                 if not sel.navigator:
2025                                         self.buildAlbumSongList(albumID = sel.albumID, mode = 5, addToCache = True)
2026                         elif self.mode == 7:
2027                                 self.setButtons(red = True)
2028                                 if not sel.navigator:
2029                                         self.buildAlbumList(addToCache = True)
2030                         elif self.mode == 8:
2031                                 self.setButtons(red = True, green = True, yellow = True)
2032                                 if not sel.navigator:
2033                                         self.buildAlbumSongList(albumID = sel.albumID, mode = 7, addToCache = True)
2034                         elif self.mode == 10:
2035                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2036                                 if not sel.navigator:
2037                                         self.buildSongList(addToCache = True)
2038                         elif self.mode == 13:
2039                                 self.setButtons(red = True)
2040                                 if not sel.navigator:
2041                                         self.buildGenreList(addToCache = True)
2042                         elif self.mode == 14:
2043                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2044                                 if not sel.navigator:
2045                                         self.buildGenreSongList(genreID = sel.genreID, addToCache = True)
2046                         elif self.mode == 18 or self.mode == 19:
2047                                 if self.mode == 18:
2048                                         self.setButtons(red = True, green = True, yellow = True)
2049                                 if self.mode == 19:
2050                                         self.setButtons(red = True, green = True, blue = True)
2051                                 if not sel.navigator:
2052                                         self.red_pressed() # back to main menu --> normally that can not be happened
2053                         elif self.mode == 20:
2054                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2055                                 if not sel.navigator:
2056                                         self.red_pressed() # back to main menu --> normally that can not be happened
2057
2058         def buildPlaylistList(self, addToCache):
2059                 if addToCache:
2060                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2061                 arguments = {}
2062                 arguments["addToCache"] = False
2063                 self.LastMethod = MethodArguments(method = self.buildPlaylistList, arguments = arguments)
2064                 self["headertext"].setText(_("Playlists"))
2065                 connection = OpenDatabase()
2066                 if connection is not None:
2067                         connection.text_factory = str
2068                         cursor = connection.cursor()
2069                         playlistList = []
2070                         playlistList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2071                         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;")
2072                         for row in cursor:
2073                                 playlistList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 2, playlistID = row[0]),))
2074                         cursor.close() 
2075                         connection.close()
2076                         self["list"].setList(playlistList)
2077                         if len(playlistList) > 1:
2078                                 self["list"].moveToIndex(1)
2079
2080         def buildPlaylistSongList(self, playlistID, addToCache):
2081                 if addToCache:
2082                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2083                 arguments = {}
2084                 arguments["playlistID"] = playlistID
2085                 arguments["addToCache"] = False
2086                 self.LastMethod = MethodArguments(method = self.buildPlaylistSongList, arguments = arguments)
2087                 connection = OpenDatabase()
2088                 if connection is not None:
2089                         connection.text_factory = str
2090                         cursor = connection.cursor()
2091                         playlistSongList = []
2092                         playlistSongList.append((Item(text = _("[back]"), mode = 1, navigator = True),))
2093                         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))
2094                         for row in cursor:
2095                                 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),))
2096                         cursor.execute("SELECT playlist_text from playlists where playlist_id = %d;" % playlistID)
2097                         row = cursor.fetchone()
2098                         self["headertext"].setText(_("Playlist (%s) -> Song List") % row[0])
2099                         cursor.close() 
2100                         connection.close()
2101                         self["list"].setList(playlistSongList)
2102                         if len(playlistSongList) > 1:
2103                                 self["list"].moveToIndex(1)
2104                                 
2105         def buildGenreList(self, addToCache):
2106                 if addToCache:
2107                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2108                 arguments = {}
2109                 arguments["addToCache"] = False
2110                 self.LastMethod = MethodArguments(method = self.buildGenreList, arguments = arguments)
2111                 self["headertext"].setText(_("Genre List"))
2112                 connection = OpenDatabase()
2113                 if connection is not None:
2114                         connection.text_factory = str
2115                         cursor = connection.cursor()
2116                         genreList = []
2117                         genreList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2118                         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;")
2119                         for row in cursor:
2120                                 genreList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 14, genreID = row[0]),))
2121                         cursor.close() 
2122                         connection.close()
2123                         self["list"].setList(genreList)
2124                         if len(genreList) > 1:
2125                                 self["list"].moveToIndex(1)
2126
2127         def buildGenreSongList(self, genreID, addToCache):
2128                 if addToCache:
2129                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2130                 arguments = {}
2131                 arguments["genreID"] = genreID
2132                 arguments["addToCache"] = False
2133                 self.LastMethod = MethodArguments(method = self.buildGenreSongList, arguments = arguments)
2134                 connection = OpenDatabase()
2135                 if connection is not None:
2136                         connection.text_factory = str
2137                         cursor = connection.cursor()
2138                         genreSongList = []
2139                         genreSongList.append((Item(text = _("[back]"), mode = 13, navigator = True),))
2140                         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))
2141                         for row in cursor:
2142                                 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),))
2143                         cursor.execute("SELECT genre_text from genre where genre_ID = %d;" % genreID)
2144                         row = cursor.fetchone()
2145                         self["headertext"].setText(_("Genre (%s) -> Song List") % row[0])
2146                         cursor.close() 
2147                         connection.close()
2148                         self["list"].setList(genreSongList)
2149                         if len(genreSongList) > 1:
2150                                 self["list"].moveToIndex(1)
2151
2152         def setButtons(self, red = False, green = False, yellow = False, blue = False):
2153                 if red:
2154                         self["key_red"].setText("Main Menu")
2155                 else:
2156                         self["key_red"].setText("")
2157                 if green:
2158                         self["key_green"].setText("Play")
2159                 else:
2160                         self["key_green"].setText("")
2161                 if yellow:
2162                         self["key_yellow"].setText("All Artists")
2163                 else:           
2164                         self["key_yellow"].setText("")
2165                 if blue:
2166                         self["key_blue"].setText("Show Album")
2167                 else:
2168                         self["key_blue"].setText("")
2169
2170         def info_pressed(self):
2171                 self.startMerlinPlayerScreenTimer.stop()
2172                 if self.player is not None:
2173                         if self.player.songList:
2174                                 self.session.execDialog(self.player)
2175
2176         def green_pressed(self):
2177                 try:
2178                         sel = self["list"].l.getCurrentSelection()[0]
2179                 except: 
2180                         sel = None
2181                 if sel is None:
2182                         return
2183                 if sel.songID != 0:
2184                         if self.player is not None:
2185                                 self.player.doClose()
2186                                 self.player = None
2187                         self.startMerlinPlayerScreenTimer.stop()
2188                         self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,self["list"].getList()[1:], self["list"].getCurrentIndex() -1, True, self.currentService, self.serviceList)
2189                         self.session.execDialog(self.player)
2190
2191         def red_pressed(self):
2192                 self.cacheList = []
2193                 self.setButtons()
2194                 self.mode = 0
2195                 self["list"].setMode(self.mode)
2196                 self.buildMainMenuList()
2197         
2198         def yellow_pressed(self):
2199                 try:
2200                         sel = self["list"].l.getCurrentSelection()[0]
2201                 except: 
2202                         return
2203                 if sel.artistID != 0:
2204                         oldmode = self.mode
2205                         self.mode = 19
2206                         self.setButtons(red = True, green = True, blue = True)
2207                         self["list"].setMode(self.mode)
2208                         self.buildArtistSongList(artistID = sel.artistID, mode = oldmode, addToCache = True)
2209                 
2210         def blue_pressed(self):
2211                 try:
2212                         sel = self["list"].l.getCurrentSelection()[0]
2213                 except: 
2214                         return
2215                 if sel.albumID != 0:
2216                         self.setButtons(red = True, green = True, yellow = True)
2217                         oldmode = self.mode
2218                         self.mode = 18
2219                         self["list"].setMode(self.mode)
2220                         self.buildAlbumSongList(albumID = sel.albumID, mode = oldmode, addToCache = True)
2221         
2222         def buildSongList(self, addToCache):
2223                 if addToCache:
2224                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2225                 arguments = {}
2226                 arguments["addToCache"] = False
2227                 self.LastMethod = MethodArguments(method = self.buildSongList,  arguments = arguments)
2228                 self["headertext"].setText(_("All Songs"))
2229                 connection = OpenDatabase()
2230                 if connection is not None:
2231                         connection.text_factory = str
2232                         cursor = connection.cursor()
2233                         SongList = []
2234                         SongList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2235                         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;")
2236                         for row in cursor:
2237                                 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]),))
2238                         cursor.close() 
2239                         connection.close()
2240                         self["list"].setList(SongList)
2241                         if len(SongList) > 1:
2242                                 self["list"].moveToIndex(1)
2243
2244
2245         def buildSearchSongList(self, sql_where, headerText, mode, addToCache):
2246                 if addToCache:
2247                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2248                 arguments = {}
2249                 arguments["sql_where"] = sql_where
2250                 arguments["headerText"] = headerText
2251                 arguments["mode"] = mode
2252                 arguments["addToCache"] = False
2253                 self.LastMethod = MethodArguments(method = self.buildSearchSongList, arguments = arguments)
2254                 self["headertext"].setText(headerText)
2255                 connection = OpenDatabase()
2256                 if connection is not None:
2257                         connection.text_factory = str
2258                         cursor = connection.cursor()
2259                         SongList = []
2260                         SongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2261                         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)
2262                         for row in cursor:
2263                                 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]),))
2264                         cursor.close() 
2265                         connection.close()
2266                         self["list"].setList(SongList)
2267                         if len(SongList) > 1:
2268                                 self["list"].moveToIndex(1)
2269
2270         
2271         def buildArtistSongList(self, artistID, mode, addToCache):
2272                 if addToCache:
2273                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2274                 arguments = {}
2275                 arguments["artistID"] = artistID
2276                 arguments["mode"] = mode
2277                 arguments["addToCache"] = False
2278                 self.LastMethod = MethodArguments(method = self.buildArtistSongList, arguments = arguments)
2279                 connection = OpenDatabase()
2280                 if connection is not None:
2281                         connection.text_factory = str
2282                         cursor = connection.cursor()
2283                         artistSongList = []
2284                         artistSongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2285                         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))
2286                         for row in cursor:
2287                                 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),))
2288                         cursor.execute("SELECT artist from artists where artist_ID = %d;" % artistID)
2289                         row = cursor.fetchone()
2290                         self["headertext"].setText(_("Artist (%s) -> Song List") % row[0])
2291                         cursor.close() 
2292                         connection.close()
2293                         self["list"].setList(artistSongList)
2294                         if len(artistSongList) > 1:
2295                                 self["list"].moveToIndex(1)
2296                         
2297         def buildAlbumSongList(self, albumID, mode, addToCache):
2298                 if addToCache:
2299                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2300                 arguments = {}
2301                 arguments["albumID"] = albumID
2302                 arguments["mode"] = mode
2303                 arguments["addToCache"] = False
2304                 self.LastMethod = MethodArguments(method = self.buildAlbumSongList, arguments = arguments)
2305                 connection = OpenDatabase()
2306                 if connection is not None:
2307                         connection.text_factory = str
2308                         cursor = connection.cursor()
2309                         albumSongList = []
2310                         albumSongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2311                         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))
2312                         for row in cursor:
2313                                 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),))
2314                         cursor.execute("SELECT album_text from album where album_ID = %d;" % albumID)
2315                         row = cursor.fetchone()
2316                         self["headertext"].setText(_("Album (%s) -> Song List") % row[0])
2317                         cursor.close() 
2318                         connection.close()
2319                         self["list"].setList(albumSongList)
2320                         if len(albumSongList) > 1:
2321                                 self["list"].moveToIndex(1)
2322                 
2323         def buildMainMenuList(self, addToCache = True):
2324                 arguments = {}
2325                 arguments["addToCache"] = True
2326                 self.LastMethod = MethodArguments(method = self.buildMainMenuList, arguments = arguments)
2327                 self["headertext"].setText(_("iDream Main Menu"))
2328                 mainMenuList = []
2329                 connection = OpenDatabase()
2330                 if connection is not None:
2331                         connection.text_factory = str
2332                         cursor = connection.cursor()
2333                         # 1. Playlists
2334                         cursor.execute("SELECT COUNT (*) FROM playlists;")
2335                         row = cursor.fetchone()
2336                         mainMenuList.append((Item(text = _("Playlists (%d)") % row[0], mode = 1),))
2337                         # 2. Artists
2338                         cursor.execute("SELECT COUNT (*) FROM artists;")
2339                         row = cursor.fetchone()
2340                         mainMenuList.append((Item(text = _("Artists (%d)") % row[0], mode = 4),))
2341                         # 3. Albums
2342                         cursor.execute("SELECT COUNT (DISTINCT album_text) FROM album;")
2343                         row = cursor.fetchone()
2344                         mainMenuList.append((Item(text = _("Albums (%d)") % row[0], mode = 7),))
2345                         # 4. Songs
2346                         cursor.execute("SELECT COUNT (*) FROM songs;")
2347                         row = cursor.fetchone()
2348                         mainMenuList.append((Item(text = _("Songs (%d)") % row[0], mode = 10),))
2349                         # 5. Genres
2350                         cursor.execute("SELECT COUNT (*) FROM genre;")
2351                         row = cursor.fetchone()
2352                         mainMenuList.append((Item(text = _("Genres (%d)") % row[0], mode = 13),))
2353                         cursor.close()  
2354                         connection.close()
2355                         self["list"].setList(mainMenuList)
2356                         self["list"].moveToIndex(0)
2357                 
2358         def buildArtistList(self, addToCache):
2359                 if addToCache:
2360                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2361                 arguments = {}
2362                 arguments["addToCache"] = False
2363                 self.LastMethod = MethodArguments(method = self.buildArtistList, arguments = arguments)
2364                 self["headertext"].setText(_("Artists List"))
2365                 connection = OpenDatabase()
2366                 if connection is not None:
2367                         connection.text_factory = str
2368                         cursor = connection.cursor()
2369                         artistList = []
2370                         artistList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2371                         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;")
2372                         for row in cursor:
2373                                 artistList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 5, artistID = row[0]),))
2374                         cursor.close() 
2375                         connection.close()
2376                         self["list"].setList(artistList)
2377                 
2378         def buildArtistAlbumList(self, ArtistID, addToCache):
2379                 if addToCache:
2380                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2381                 arguments = {}
2382                 arguments["ArtistID"] = ArtistID
2383                 arguments["addToCache"] = False
2384                 self.LastMethod = MethodArguments(method = self.buildArtistAlbumList, arguments = arguments)
2385                 connection = OpenDatabase()
2386                 if connection is not None:
2387                         connection.text_factory = str
2388                         cursor = connection.cursor()
2389                         albumArtistList = []
2390                         albumArtistList.append((Item(text = _("[back]"), mode = 4, navigator = True),))
2391                         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)
2392                         for row in cursor:
2393                                 cursor2 = connection.cursor()
2394                                 cursor2.execute("select count(song_id) from songs where album_id = %d;" % row[0])
2395                                 row2 = cursor2.fetchone()
2396                                 albumArtistList.append((Item(text = "%s (%d)" % (row[1], row2[0]), mode = 6, albumID = row[0], artistID = ArtistID),))
2397                                 cursor2.close()
2398                         cursor.execute("SELECT artist from artists where artist_ID = %d;" % ArtistID)
2399                         row = cursor.fetchone()
2400                         self["headertext"].setText(_("Artist (%s) -> Album List") % row[0])
2401                         cursor.close() 
2402                         connection.close()
2403                         self["list"].setList(albumArtistList)
2404                         if len(albumArtistList) > 1:
2405                                 self["list"].moveToIndex(1)
2406                         
2407         def buildAlbumList(self, addToCache):
2408                 if addToCache:
2409                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2410                 arguments = {}
2411                 arguments["addToCache"] = False
2412                 self.LastMethod = MethodArguments(method = self.buildAlbumList, arguments = arguments)
2413                 self["headertext"].setText(_("Albums List"))
2414                 connection = OpenDatabase()
2415                 if connection is not None:
2416                         connection.text_factory = str
2417                         cursor = connection.cursor()
2418                         albumList = []
2419                         albumList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2420                         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;")
2421                         for row in cursor:
2422                                 albumList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 8, albumID = row[0]),))
2423                         cursor.close() 
2424                         connection.close()
2425                         self["list"].setList(albumList)
2426                         if len(albumList) > 1:
2427                                 self["list"].moveToIndex(1)
2428                         
2429         def startRun(self):
2430                 if pathToDatabase.isRunning:
2431                         self.showScanner = eTimer()
2432                         self.showScanner.callback.append(self.showScannerCallback)
2433                         self.showScanner.start(0,1)
2434                 else:
2435                         if config.plugins.merlinmusicplayer.startlastsonglist.value:
2436                                 self.startPlayerTimer = eTimer()
2437                                 self.startPlayerTimer.callback.append(self.startPlayerTimerCallback)
2438                                 self.startPlayerTimer.start(0,1)
2439                         self.mode = 0
2440                         self["list"].setMode(self.mode)
2441                         self.buildMainMenuList()
2442
2443         def showScannerCallback(self):
2444                 self.session.openWithCallback(self.filesAdded, iDreamAddToDatabase,None)
2445
2446
2447         def startPlayerTimerCallback(self):
2448                 connection = OpenDatabase()
2449                 if connection is not None:
2450                         connection.text_factory = str
2451                         cursor = connection.cursor()
2452                         iDreamMode = False
2453                         SongList = []
2454                         cursor.execute("select song_id, filename, title, artist, album, genre, bitrate, length,  track, date, PTS from CurrentSongList;")
2455                         for row in cursor:
2456                                 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),))
2457                                 if row[0] != 0:
2458                                         iDreamMode = True
2459                         cursor.close() 
2460                         connection.close()
2461                         if self.player is not None:
2462                                 self.player.doClose()
2463                                 self.player = None
2464                         self.startMerlinPlayerScreenTimer.stop()
2465                         count = len(SongList)
2466                         if count:
2467                                 # just to be sure, check the index , it's critical
2468                                 index = config.plugins.merlinmusicplayer.lastsonglistindex.value
2469                                 if index >= count:
2470                                         index = 0
2471                                 self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,SongList, index, iDreamMode, self.currentService, self.serviceList)
2472                                 self.session.execDialog(self.player)
2473
2474         def config(self):
2475                 self.startMerlinPlayerScreenTimer.stop()
2476                 self.session.openWithCallback(self.setupFinished, MerlinMusicPlayerSetup, True)
2477
2478         def setupFinished(self, result):
2479                 if result:
2480                         self.red_pressed()
2481
2482
2483         def stopPlayingAndAppendFileToSongList(self):
2484                 self.startMerlinPlayerScreenTimer.stop()
2485                 if self.player is not None:
2486                         self.player.doClose()
2487                         self.player = None
2488                 self.appendFileToSongList()     
2489                 self.startMerlinPlayerScreenTimer.start(START_MERLIN_PLAYER_SCREEN_TIMER_VALUE)
2490
2491         def appendFileToSongList(self):
2492                 SongList = []
2493                 playerAvailable =  self.player is not None and self.player.songList
2494                 sel = self.getCurrentSelection()
2495                 if sel:
2496                         if playerAvailable:
2497                                 self.player.songList.append((sel,))
2498                                 self.player.origSongList.append((sel,))
2499                         else:
2500                                 SongList.append((sel,))
2501                         if not playerAvailable:
2502                                 if self.player is not None:
2503                                         self.player.doClose()
2504                                         self.player = None
2505                                 self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,SongList, 0, True, self.currentService, self.serviceList)
2506                                 self.player.playSong(self.player.songList[self.player.currentIndex][0].filename)
2507                                 self.player["coverArt"].onShow()
2508                                 self.player.init = 1
2509                         else:
2510                                 self.player["nextTitle"].setText(self.player.getNextTitle())
2511                                 self.session.open(MessageBox, _("%s\nappended to songlist")%sel.title, type = MessageBox.TYPE_INFO,timeout = 3 )
2512
2513         def insertFileToSongList(self):
2514                 sel = self.getCurrentSelection()
2515                 if sel:
2516                         if self.player is not None and self.player.songList:
2517                                 index = self.player.currentIndex
2518                                 self.player.songList.insert(index+1,(sel,))
2519                                 self.player.origSongList.insert(index+1,(sel,))
2520                                 self.player["nextTitle"].setText(self.player.getNextTitle())
2521                                 self.session.open(MessageBox, _("%s\ninserted and will be played as next song")%sel.title, type = MessageBox.TYPE_INFO,timeout = 3 )
2522                         else:
2523                                 self.appendFileToSongList()
2524
2525         def Error(self, error = None):
2526                 if error is not None:
2527                         self["list"].hide()
2528                         self["statustext"].setText(str(error.getErrorMessage()))
2529                         
2530         def closing(self):
2531                 self.close()
2532                 
2533         def __onClose(self):
2534                 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
2535                 self.startMerlinPlayerScreenTimer.stop()
2536                 if self.player is not None:
2537                         self.player.closePlayer()
2538                         self.player.doClose()
2539                         self.player = None
2540                 if self.serviceList is None:
2541                         self.session.nav.playService(self.currentService)
2542                 else:
2543                         current = ServiceReference(self.serviceList.getCurrentSelection())
2544                         self.session.nav.playService(current.ref)
2545                 
2546
2547         def lcdUpdate(self):
2548                 self.startMerlinPlayerScreenTimer.start(START_MERLIN_PLAYER_SCREEN_TIMER_VALUE)
2549                 try:
2550                         count = self["list"].getItemCount()
2551                         index = self["list"].getCurrentIndex()
2552                         iDreamList = self["list"].getLis