Merge remote-tracking branch 'remotes/origin/master' into webif_js_rewrite
[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                 text = getEncodedString(self.getLyricsFromID3Tag(audio)).replace("\r\n","\n")
1521                 text = text.replace("\r","\n")
1522                 self["lyric_text"].setText(text)
1523   
1524         def getLyricsFromID3Tag(self,tag):
1525                 if tag:
1526                         for frame in tag.values():
1527                                 if frame.FrameID == "USLT":
1528                                         return frame.text
1529                 url = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=%s&song=%s" % (quote(self.currentSong.artist), quote(self.currentSong.title))
1530                 sendUrlCommand(url, None,10).addCallback(self.gotLyrics).addErrback(self.urlError)
1531                 return "No lyrics found in id3-tag, trying api.chartlyrics.com..."
1532         
1533         def urlError(self, error = None):
1534                 if error is not None:
1535                         self["resulttext"].setText(str(error.getErrorMessage()))
1536                         self["lyric_text"].setText("")
1537
1538         def gotLyrics(self, xmlstring):
1539                 root = cet_fromstring(xmlstring)
1540                 lyrictext = ""
1541                 lyrictext = root.findtext("{http://api.chartlyrics.com/}Lyric").encode("utf-8", 'ignore')
1542                 self["lyric_text"].setText(lyrictext)
1543                 title = root.findtext("{http://api.chartlyrics.com/}LyricSong").encode("utf-8", 'ignore')
1544                 artist = root.findtext("{http://api.chartlyrics.com/}LyricArtist").encode("utf-8", 'ignore')
1545                 result = _("Response -> lyrics for: %s (%s)") % (title,artist)
1546                 self["resulttext"].setText(result)
1547                 if not lyrictext:
1548                         self["resulttext"].setText(_("No lyrics found"))
1549                         self["lyric_text"].setText("")
1550
1551         def pageUp(self):
1552                 self["lyric_text"].pageUp()
1553
1554         def pageDown(self):
1555                 self["lyric_text"].pageDown()   
1556
1557 class MerlinMusicPlayerSongList(Screen):
1558         
1559         sz_w = getDesktop(0).size().width()
1560         if sz_w == 1280:
1561                 skin = """
1562                         <screen name="MerlinMusicPlayerSongList" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1563                         <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1564                         <eLabel backgroundColor="#999999" position="178,112" size="924,2" zPosition="1"/>
1565                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="178,104" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1566                         <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"/>
1567                         <widget name="headertext" position="178,145" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1568                         <widget name="list" position="178,182" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1569                         </screen>"""
1570         elif sz_w == 1024:
1571                 skin = """
1572                         <screen name="MerlinMusicPlayerSongList" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1573                         <eLabel backgroundColor="#999999" position="50,40" size="924,2" zPosition="1"/>
1574                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,32" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1575                         <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"/>
1576                         <widget name="headertext" position="50,73" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1577                         <widget name="list" position="50,110" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1578                         </screen>"""
1579         else:
1580                 skin = """
1581                         <screen name="MerlinMusicPlayerSongList" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1582                         <eLabel backgroundColor="#999999" position="50,50" size="620,2" zPosition="1"/>
1583                         <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,40" size="250,20" text="MERLIN  MUSIC  PLAYER" valign="center" zPosition="2"/>
1584                         <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"/>
1585                         <widget name="headertext" position="50,73" zPosition="1" size="620,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1586                         <widget name="list" position="50,110" zPosition="2" size="620,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1587                         </screen>"""
1588                 
1589         
1590         def __init__(self, session, songlist, index, idreammode):
1591                 self.session = session
1592                 Screen.__init__(self, session)
1593                 self["headertext"] = Label(_("Merlin Music Player Songlist"))
1594                 self["list"] = iDreamList()
1595                 self["list"].connectSelChanged(self.lcdUpdate)
1596                 self["actions"] = ActionMap(["WizardActions"],
1597                 {
1598                         "ok": self.ok,
1599                         "back": self.closing,
1600                 }, -1)
1601                 self.songList = songlist
1602                 self.index = index
1603                 self.iDreamMode = idreammode
1604                 self.onLayoutFinish.append(self.startRun)
1605                 self.onShown.append(self.lcdUpdate)
1606
1607         def startRun(self):
1608                 if self.iDreamMode:
1609                         self["list"].setMode(10) # songlist
1610                 self["list"].setList(self.songList)
1611                 self["list"].moveToIndex(self.index)
1612
1613         def ok(self):
1614                 self.close(self["list"].getCurrentIndex())
1615
1616         def closing(self):
1617                 self.close(-1)
1618
1619         def lcdUpdate(self):
1620                 try:
1621                         index = self["list"].getCurrentIndex()
1622                         songlist = self["list"].getList()
1623                         mode =  self.iDreamMode or songlist[index][0].PTS
1624                         if mode:
1625                                 self.summaries.setText(songlist[index][0].title,1)
1626                         else:
1627                                 self.summaries.setText(songlist[index][0].text,1)
1628                         count = self["list"].getItemCount()
1629                         # voheriges
1630                         index -= 1
1631                         if index < 0:
1632                                 index = count
1633                         if mode:
1634                                 self.summaries.setText(songlist[index][0].title,3)
1635                         else:
1636                                 self.summaries.setText(songlist[index][0].text,3)
1637                         # naechstes
1638                         index = self["list"].getCurrentIndex() + 1
1639                         if index > count:
1640                                 index = 0
1641                         if mode:
1642                                 self.summaries.setText(songlist[index][0].title,4)
1643                         else:
1644                                 self.summaries.setText(songlist[index][0].text,4)
1645                 except: pass
1646
1647         def createSummary(self):
1648                 return MerlinMusicPlayerLCDScreenText
1649
1650 class iDreamMerlin(Screen):
1651         
1652
1653         sz_w = getDesktop(0).size().width()
1654         if sz_w == 1280:
1655                 skin = """
1656                         <screen name="iDreamMerlin" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1657                                 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1658                                 <ePixmap position="178,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1659                                 <ePixmap position="328,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1660                                 <ePixmap position="478,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1661                                 <ePixmap position="628,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1662                                 <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" />
1663                                 <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" />
1664                                 <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" />
1665                                 <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" />
1666                                 <widget name="headertext" position="178,149" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1667                                 <widget name="list" position="178,182" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1668                         </screen>"""
1669         elif sz_w == 1024:
1670                 skin = """
1671                         <screen name="iDreamMerlin" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1672                                 <ePixmap position="50,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1673                                 <ePixmap position="200,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1674                                 <ePixmap position="350,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1675                                 <ePixmap position="500,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1676                                 <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" />
1677                                 <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" />
1678                                 <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" />
1679                                 <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" />
1680                                 <widget name="headertext" position="50,77" zPosition="1" size="900,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1681                                 <widget name="list" position="50,110" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1682                         </screen>"""
1683         else:
1684                 skin = """
1685                         <screen name="iDreamMerlin" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1686                                 <ePixmap position="50,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1687                                 <ePixmap position="200,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1688                                 <ePixmap position="350,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1689                                 <ePixmap position="500,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1690                                 <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" />
1691                                 <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" />
1692                                 <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" />
1693                                 <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" />
1694                                 <widget name="headertext" position="50,77" zPosition="1" size="620,23" font="Regular;20" transparent="1"  foregroundColor="#fcc000" backgroundColor="#00000000"/>
1695                                 <widget name="list" position="50,110" zPosition="2" size="620,350" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
1696                         </screen>"""
1697                 
1698         
1699         def __init__(self, session, servicelist):
1700                 self.session = session
1701                 Screen.__init__(self, session)
1702                 self["list"] = iDreamList()
1703                 self["list"].connectSelChanged(self.lcdUpdate)
1704
1705
1706                 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "ColorActions", "EPGSelectActions"],
1707                 {
1708                         "ok": self.ok,
1709                         "back": self.closing,
1710                         "red": self.red_pressed,
1711                         "green": self.green_pressed,
1712                         "yellow": self.yellow_pressed,
1713                         "blue": self.blue_pressed,
1714                         "input_date_time": self.menu_pressed,
1715                         "info" : self.info_pressed,
1716                 }, -1)
1717
1718                 self["actions2"] = NumberActionMap(["InputActions"],
1719                 {
1720                         "0": self.keyNumber_pressed,
1721                 }, -1)
1722
1723                 self.onLayoutFinish.append(self.startRun)
1724                 self.onShown.append(self.lcdUpdate)
1725                 self.onClose.append(self.__onClose)
1726                 
1727                 self.serviceList = servicelist
1728                 self.currentService = self.session.nav.getCurrentlyPlayingServiceReference()
1729                 self.session.nav.stopService()
1730                 
1731                 self.mode = 0
1732                 self.mainMenuList = []
1733                 self.cacheList = []
1734                 self.LastMethod = None
1735                 self.player = None
1736                 
1737                 self["key_red"] = StaticText("")
1738                 self["key_green"] = StaticText("")
1739                 self["key_yellow"] = StaticText("")
1740                 self["key_blue"] = StaticText("")
1741                 self["headertext"] = Label(_("iDream Main Menu"))
1742
1743                 self.startMerlinPlayerScreenTimer = eTimer()
1744                 self.startMerlinPlayerScreenTimer.timeout.get().append(self.info_pressed)
1745
1746                 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
1747         
1748         def sleepTimerEntryOnStateChange(self, timer):
1749                 if timer.state == TimerEntry.StateEnded:
1750                         self.close()
1751
1752         def getPlayList(self):
1753                 connection = OpenDatabase()
1754                 if connection is not None:
1755                         connection.text_factory = str
1756                         cursor = connection.cursor()
1757                         playList = []
1758                         cursor.execute("select playlist_id,playlist_text from playlists order by playlist_text;")
1759                         for row in cursor:
1760                                 playList.append((row[1], row[0]))
1761                         cursor.close()  
1762                         connection.close()
1763                         return playList
1764                 else:
1765                         return None
1766
1767         def sqlCommand(self, sqlSatement):
1768                 connection = OpenDatabase()
1769                 if connection is not None:
1770                         cursor = connection.cursor()
1771                         cursor.execute(sqlSatement)
1772                         cursor.close()
1773                         connection.commit()
1774                         connection.close()
1775
1776         def clearCache(self):
1777                 for items in self.cacheList:
1778                         items.cache = False
1779                         items.listview = []
1780                         items.headertext = ""
1781
1782         def getCurrentSelection(self):
1783                 sel = None
1784                 try: sel = self["list"].l.getCurrentSelection()[0]
1785                 except: pass
1786                 return sel
1787
1788         def addListToPlaylistConfirmed(self, methodName, answer):
1789                 if answer:
1790                         playList = self.getPlayList()
1791                         if len(playList):
1792                                 self.session.openWithCallback(methodName, ChoiceBox,list = playList)
1793                         else:
1794                                 self.session.openWithCallback(self.createPlaylistConfirmed, MessageBox, _("There are no playlists defined.\nDo you want to create a new playlist?"))
1795
1796         def menu_pressed(self):
1797                 self.startMerlinPlayerScreenTimer.stop()
1798                 options = [(_("Configuration"), self.config),(_("Search in iDream database"), self.searchInIDreamDatabase),]
1799                 options.extend(((_("Scan path for music files and add them to database"), self.scanDir),))
1800                 if self.mode != 1:
1801                         options.extend(((_("Create new playlist"), self.createPlaylist),))
1802                 if self["list"].getDisplaySongMode():
1803                         if self.mode == 2:
1804                                 options.extend(((_("Delete song from current playlist"), self.deleteSongFromPlaylist),))
1805                         else:
1806                                 options.extend(((_("Add selected song to a playlist"), self.addSongToPlaylist),))
1807                                 if self.mode == 18:
1808                                         options.extend(((_("Add all songs from selected album to a playlist"), self.addAlbumToPlaylist),))
1809                                 elif self.mode == 19:
1810                                         options.extend(((_("Add all songs from selected artist to a playlist"), self.addArtistToPlaylist),))
1811                                 options.extend(((_("Delete song from database"), self.deleteSongFromDatabase),))
1812                         options.extend(((_("Clear current songlist and play selected entry"), self.stopPlayingAndAppendFileToSongList),))
1813                         options.extend(((_("Append file to current playing songlist"), self.appendFileToSongList),))
1814                         if self.player is not None and self.player.songList:
1815                                 options.extend(((_("Insert file to current playing songlist and play next"), self.insertFileToSongList),))
1816                 else:
1817                         if self.mode == 1:
1818                                 options.extend(((_("Delete selected playlist"), self.deletePlaylist),))
1819                         elif self.mode == 4:
1820                                 options.extend(((_("Add all songs from selected artist to a playlist"), self.addArtistToPlaylist),))
1821                         elif self.mode == 5 or self.mode == 7:
1822                                 options.extend(((_("Add all songs from selected album to a playlist"), self.addAlbumToPlaylist),))
1823                         elif self.mode == 13:
1824                                 options.extend(((_("Add all songs from selected genre to a playlist"), self.addGenreToPlaylist),))
1825                 self.session.openWithCallback(self.menuCallback, ChoiceBox,list = options)
1826
1827         def menuCallback(self, ret):
1828                 ret and ret[1]()
1829
1830         def scanDir(self):
1831                 SelectPath
1832                 self.session.openWithCallback(self.pathSelected,SelectPath,"/media/")
1833
1834         def pathSelected(self, res):
1835                 if res is not None:
1836                         self.session.openWithCallback(self.filesAdded, iDreamAddToDatabase,res)
1837
1838         def filesAdded(self):
1839                 if pathToDatabase.isRunning:
1840                         self.close()
1841                 else:
1842                         self.red_pressed()
1843                 
1844
1845
1846
1847         def addGenreToPlaylist(self):
1848                 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed,self.addGenreToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that genre to a playlist?"))
1849
1850         def addGenreToPlaylistConfirmedCallback(self, ret):
1851                 if ret:
1852                         sel = self.getCurrentSelection()
1853                         if sel:
1854                                 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))
1855                                 self.clearCache()
1856
1857         def addArtistToPlaylist(self):
1858                 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed, self.addArtistToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that artist to a playlist?"))
1859
1860         def addArtistToPlaylistConfirmedCallback(self, ret):
1861                 if ret:
1862                         sel = self.getCurrentSelection()
1863                         if sel:
1864                                 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))
1865                                 self.clearCache()
1866
1867         def addAlbumToPlaylist(self):
1868                 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed, self.addAlbumToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that album to a playlist?"))
1869
1870         def addAlbumToPlaylistConfirmedCallback(self, ret):
1871                 if ret:
1872                         sel = self.getCurrentSelection()
1873                         if sel:
1874                                 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))
1875                                 self.clearCache()
1876
1877         def deletePlaylist(self):
1878                 self.session.openWithCallback(self.deletePlaylistConfirmed, MessageBox, _("Do you really want to delete the current playlist?"))
1879
1880         def deletePlaylistConfirmed(self, answer):
1881                 if answer:
1882                         sel = self.getCurrentSelection()
1883                         if sel:
1884                                 self.sqlCommand("delete from playlist_songs where playlist_id = %d" % (sel.playlistID))
1885                                 self.sqlCommand("delete from playlists where playlist_id = %d" % (sel.playlistID))
1886                                 self["list"].removeItem(self["list"].getCurrentIndex())
1887                                 self.clearCache()
1888
1889
1890         def deleteSongFromPlaylist(self):
1891                 self.session.openWithCallback(self.deleteSongFromPlaylistConfirmed, MessageBox, _("Do you really want to delete that song the current playlist?"))
1892
1893         def deleteSongFromPlaylistConfirmed(self, answer):
1894                 if answer:
1895                         sel = self.getCurrentSelection()
1896                         if sel:
1897                                 self.sqlCommand("delete from playlist_songs where song_id = %d" % (sel.songID))
1898                                 self["list"].removeItem(self["list"].getCurrentIndex())
1899                                 self.clearCache()
1900
1901         def deleteSongFromDatabase(self):
1902                 self.session.openWithCallback(self.deleteSongFromDatabaseConfirmed, MessageBox, _("Do you really want to delete that song from the database?"))
1903
1904         def deleteSongFromDatabaseConfirmed(self, answer):
1905                 if answer:
1906                         sel = self.getCurrentSelection()
1907                         if sel:
1908                                 self.sqlCommand("delete from playlist_songs where song_id = %d" % (sel.songID))
1909                                 self.sqlCommand("delete from songs where song_id = %d" % (sel.songID))
1910                                 self["list"].removeItem(self["list"].getCurrentIndex())
1911                                 self.clearCache()
1912                         
1913         def addSongToPlaylist(self):
1914                 playList = self.getPlayList()
1915                 if len(playList):
1916                         self.session.openWithCallback(self.addSongToPlaylistCallback, ChoiceBox,list = playList)
1917                 else:
1918                         self.session.openWithCallback(self.createPlaylistConfirmed, MessageBox, _("There are no playlists defined.\nDo you want to create a new playlist?"))
1919
1920         def createPlaylistConfirmed(self, val):
1921                 if val:
1922                         self.createPlaylist()
1923
1924         def addSongToPlaylistCallback(self,ret):
1925                 if ret:
1926                         sel = self.getCurrentSelection()
1927                         if sel:
1928                                 self.sqlCommand("INSERT INTO Playlist_Songs (playlist_id,song_id) VALUES(%d,%d);" % (ret[1],sel.songID))
1929                                 self.clearCache()
1930
1931         def createPlaylist(self):
1932                 self.session.openWithCallback(self.createPlaylistFinished, VirtualKeyBoard, title = _("Enter name for playlist"))
1933
1934         def createPlaylistFinished(self, text = None):
1935                 if text:
1936                         self.sqlCommand('INSERT INTO Playlists (playlist_text) VALUES("%s");' % (text))
1937                         self.clearCache()
1938                         self.menu_pressed()
1939
1940         def searchInIDreamDatabase(self):
1941                 options = [(_("search for title"), 1),
1942                         (_("search for artist"), 2),
1943                         (_("search for album"), 3),
1944                         (_("search in all of them"), 4),]
1945                 self.session.openWithCallback(self.enterSearchText, ChoiceBox,list = options)
1946
1947         def enterSearchText(self, ret):
1948                 if ret:
1949                         self.session.openWithCallback(boundFunction(self.enterSearchTextFinished,ret[1]), VirtualKeyBoard, title = _("Enter search-text"))
1950
1951         def enterSearchTextFinished(self, searchType, searchText = None):
1952                 if searchText:
1953                         search = "%" + searchText + "%"
1954                         if searchType == 1:
1955                                 sql_where = "where title like '%s'" % search
1956                                 text = _('Search results for "%s" in all titles') % searchText
1957                         elif searchType == 2:
1958                                 sql_where = "where artists.artist like '%s'" % search
1959                                 text = _('Search results for "%s" in all artists') % searchText
1960                         elif searchType == 3:
1961                                 sql_where = "where album_text like '%s'" % search
1962                                 text = _('Search results for "%s" in all albums') % searchText
1963                         else:
1964                                 sql_where = "where (title like '%s' or artists.artist like '%s' or album_text like '%s')"  % (search,search,search)
1965                                 text = _('Search results for "%s" in title, artist or album') % searchText
1966                         self.setButtons(red = True, yellow = True, blue = True)
1967                         oldmode = self.mode
1968                         self.mode = 20
1969                         self["list"].setMode(self.mode)
1970                         self.buildSearchSongList(sql_where, text, oldmode, True)
1971
1972
1973         def keyNumber_pressed(self, number):
1974                 if number == 0 and self.mode != 0:
1975                         self["list"].moveToIndex(0)
1976                         self.ok()
1977
1978         def ok(self):
1979                 sel = self.getCurrentSelection()
1980                 if sel is None:
1981                         return
1982                 if sel.mode == 99:
1983                         self.green_pressed()
1984                 else:
1985                         self.mode = sel.mode
1986                         self["list"].setMode(self.mode)
1987                         if sel.navigator and len(self.cacheList) > 0:
1988                                 cache = self.cacheList.pop()
1989                         else:
1990                                 cache = CacheList(cache = False, index = -1)
1991                         if sel.navigator: 
1992                                 self["headertext"].setText(cache.headertext)
1993                                 if cache.cache:
1994                                         self["list"].setList(cache.listview)
1995                                         self.LastMethod = MethodArguments(method = cache.methodarguments.method, arguments = cache.methodarguments.arguments)
1996                                 else:
1997                                         cache.methodarguments.method(**cache.methodarguments.arguments)
1998                                 self["list"].moveToIndex(cache.index)
1999                         if self.mode == 0:
2000                                 self.setButtons()
2001                                 if not sel.navigator:
2002                                         self.buildMainMenuList()
2003                         elif self.mode == 1:
2004                                 self.setButtons(red = True)
2005                                 if not sel.navigator:
2006                                         self.buildPlaylistList(addToCache = True)
2007                         elif self.mode == 2:
2008                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2009                                 if not sel.navigator:
2010                                         self.buildPlaylistSongList(playlistID = sel.playlistID, addToCache = True)
2011                         elif self.mode == 4:
2012                                 self.setButtons(red = True)
2013                                 if not sel.navigator:
2014                                         self.buildArtistList(addToCache = True)
2015                         elif self.mode == 5:
2016                                 self.setButtons(red = True)
2017                                 if not sel.navigator:
2018                                         self.buildArtistAlbumList(sel.artistID, addToCache = True)
2019                         elif self.mode == 6:
2020                                 self.setButtons(red = True, green = True, yellow = True)
2021                                 if not sel.navigator:
2022                                         self.buildAlbumSongList(albumID = sel.albumID, mode = 5, addToCache = True)
2023                         elif self.mode == 7:
2024                                 self.setButtons(red = True)
2025                                 if not sel.navigator:
2026                                         self.buildAlbumList(addToCache = True)
2027                         elif self.mode == 8:
2028                                 self.setButtons(red = True, green = True, yellow = True)
2029                                 if not sel.navigator:
2030                                         self.buildAlbumSongList(albumID = sel.albumID, mode = 7, addToCache = True)
2031                         elif self.mode == 10:
2032                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2033                                 if not sel.navigator:
2034                                         self.buildSongList(addToCache = True)
2035                         elif self.mode == 13:
2036                                 self.setButtons(red = True)
2037                                 if not sel.navigator:
2038                                         self.buildGenreList(addToCache = True)
2039                         elif self.mode == 14:
2040                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2041                                 if not sel.navigator:
2042                                         self.buildGenreSongList(genreID = sel.genreID, addToCache = True)
2043                         elif self.mode == 18 or self.mode == 19:
2044                                 if self.mode == 18:
2045                                         self.setButtons(red = True, green = True, yellow = True)
2046                                 if self.mode == 19:
2047                                         self.setButtons(red = True, green = True, blue = True)
2048                                 if not sel.navigator:
2049                                         self.red_pressed() # back to main menu --> normally that can not be happened
2050                         elif self.mode == 20:
2051                                 self.setButtons(red = True, green = True, yellow = True, blue = True)
2052                                 if not sel.navigator:
2053                                         self.red_pressed() # back to main menu --> normally that can not be happened
2054
2055         def buildPlaylistList(self, addToCache):
2056                 if addToCache:
2057                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2058                 arguments = {}
2059                 arguments["addToCache"] = False
2060                 self.LastMethod = MethodArguments(method = self.buildPlaylistList, arguments = arguments)
2061                 self["headertext"].setText(_("Playlists"))
2062                 connection = OpenDatabase()
2063                 if connection is not None:
2064                         connection.text_factory = str
2065                         cursor = connection.cursor()
2066                         playlistList = []
2067                         playlistList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2068                         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;")
2069                         for row in cursor:
2070                                 playlistList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 2, playlistID = row[0]),))
2071                         cursor.close() 
2072                         connection.close()
2073                         self["list"].setList(playlistList)
2074                         if len(playlistList) > 1:
2075                                 self["list"].moveToIndex(1)
2076
2077         def buildPlaylistSongList(self, playlistID, addToCache):
2078                 if addToCache:
2079                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2080                 arguments = {}
2081                 arguments["playlistID"] = playlistID
2082                 arguments["addToCache"] = False
2083                 self.LastMethod = MethodArguments(method = self.buildPlaylistSongList, arguments = arguments)
2084                 connection = OpenDatabase()
2085                 if connection is not None:
2086                         connection.text_factory = str
2087                         cursor = connection.cursor()
2088                         playlistSongList = []
2089                         playlistSongList.append((Item(text = _("[back]"), mode = 1, navigator = True),))
2090                         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))
2091                         for row in cursor:
2092                                 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),))
2093                         cursor.execute("SELECT playlist_text from playlists where playlist_id = %d;" % playlistID)
2094                         row = cursor.fetchone()
2095                         self["headertext"].setText(_("Playlist (%s) -> Song List") % row[0])
2096                         cursor.close() 
2097                         connection.close()
2098                         self["list"].setList(playlistSongList)
2099                         if len(playlistSongList) > 1:
2100                                 self["list"].moveToIndex(1)
2101                                 
2102         def buildGenreList(self, addToCache):
2103                 if addToCache:
2104                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2105                 arguments = {}
2106                 arguments["addToCache"] = False
2107                 self.LastMethod = MethodArguments(method = self.buildGenreList, arguments = arguments)
2108                 self["headertext"].setText(_("Genre List"))
2109                 connection = OpenDatabase()
2110                 if connection is not None:
2111                         connection.text_factory = str
2112                         cursor = connection.cursor()
2113                         genreList = []
2114                         genreList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2115                         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;")
2116                         for row in cursor:
2117                                 genreList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 14, genreID = row[0]),))
2118                         cursor.close() 
2119                         connection.close()
2120                         self["list"].setList(genreList)
2121                         if len(genreList) > 1:
2122                                 self["list"].moveToIndex(1)
2123
2124         def buildGenreSongList(self, genreID, addToCache):
2125                 if addToCache:
2126                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2127                 arguments = {}
2128                 arguments["genreID"] = genreID
2129                 arguments["addToCache"] = False
2130                 self.LastMethod = MethodArguments(method = self.buildGenreSongList, arguments = arguments)
2131                 connection = OpenDatabase()
2132                 if connection is not None:
2133                         connection.text_factory = str
2134                         cursor = connection.cursor()
2135                         genreSongList = []
2136                         genreSongList.append((Item(text = _("[back]"), mode = 13, navigator = True),))
2137                         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))
2138                         for row in cursor:
2139                                 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),))
2140                         cursor.execute("SELECT genre_text from genre where genre_ID = %d;" % genreID)
2141                         row = cursor.fetchone()
2142                         self["headertext"].setText(_("Genre (%s) -> Song List") % row[0])
2143                         cursor.close() 
2144                         connection.close()
2145                         self["list"].setList(genreSongList)
2146                         if len(genreSongList) > 1:
2147                                 self["list"].moveToIndex(1)
2148
2149         def setButtons(self, red = False, green = False, yellow = False, blue = False):
2150                 if red:
2151                         self["key_red"].setText("Main Menu")
2152                 else:
2153                         self["key_red"].setText("")
2154                 if green:
2155                         self["key_green"].setText("Play")
2156                 else:
2157                         self["key_green"].setText("")
2158                 if yellow:
2159                         self["key_yellow"].setText("All Artists")
2160                 else:           
2161                         self["key_yellow"].setText("")
2162                 if blue:
2163                         self["key_blue"].setText("Show Album")
2164                 else:
2165                         self["key_blue"].setText("")
2166
2167         def info_pressed(self):
2168                 self.startMerlinPlayerScreenTimer.stop()
2169                 if self.player is not None:
2170                         if self.player.songList:
2171                                 self.session.execDialog(self.player)
2172
2173         def green_pressed(self):
2174                 try:
2175                         sel = self["list"].l.getCurrentSelection()[0]
2176                 except: 
2177                         sel = None
2178                 if sel is None:
2179                         return
2180                 if sel.songID != 0:
2181                         if self.player is not None:
2182                                 self.player.doClose()
2183                                 self.player = None
2184                         self.startMerlinPlayerScreenTimer.stop()
2185                         self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,self["list"].getList()[1:], self["list"].getCurrentIndex() -1, True, self.currentService, self.serviceList)
2186                         self.session.execDialog(self.player)
2187
2188         def red_pressed(self):
2189                 self.cacheList = []
2190                 self.setButtons()
2191                 self.mode = 0
2192                 self["list"].setMode(self.mode)
2193                 self.buildMainMenuList()
2194         
2195         def yellow_pressed(self):
2196                 try:
2197                         sel = self["list"].l.getCurrentSelection()[0]
2198                 except: 
2199                         return
2200                 if sel.artistID != 0:
2201                         oldmode = self.mode
2202                         self.mode = 19
2203                         self.setButtons(red = True, green = True, blue = True)
2204                         self["list"].setMode(self.mode)
2205                         self.buildArtistSongList(artistID = sel.artistID, mode = oldmode, addToCache = True)
2206                 
2207         def blue_pressed(self):
2208                 try:
2209                         sel = self["list"].l.getCurrentSelection()[0]
2210                 except: 
2211                         return
2212                 if sel.albumID != 0:
2213                         self.setButtons(red = True, green = True, yellow = True)
2214                         oldmode = self.mode
2215                         self.mode = 18
2216                         self["list"].setMode(self.mode)
2217                         self.buildAlbumSongList(albumID = sel.albumID, mode = oldmode, addToCache = True)
2218         
2219         def buildSongList(self, addToCache):
2220                 if addToCache:
2221                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2222                 arguments = {}
2223                 arguments["addToCache"] = False
2224                 self.LastMethod = MethodArguments(method = self.buildSongList,  arguments = arguments)
2225                 self["headertext"].setText(_("All Songs"))
2226                 connection = OpenDatabase()
2227                 if connection is not None:
2228                         connection.text_factory = str
2229                         cursor = connection.cursor()
2230                         SongList = []
2231                         SongList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2232                         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;")
2233                         for row in cursor:
2234                                 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]),))
2235                         cursor.close() 
2236                         connection.close()
2237                         self["list"].setList(SongList)
2238                         if len(SongList) > 1:
2239                                 self["list"].moveToIndex(1)
2240
2241
2242         def buildSearchSongList(self, sql_where, headerText, mode, addToCache):
2243                 if addToCache:
2244                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2245                 arguments = {}
2246                 arguments["sql_where"] = sql_where
2247                 arguments["headerText"] = headerText
2248                 arguments["mode"] = mode
2249                 arguments["addToCache"] = False
2250                 self.LastMethod = MethodArguments(method = self.buildSearchSongList, arguments = arguments)
2251                 self["headertext"].setText(headerText)
2252                 connection = OpenDatabase()
2253                 if connection is not None:
2254                         connection.text_factory = str
2255                         cursor = connection.cursor()
2256                         SongList = []
2257                         SongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2258                         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)
2259                         for row in cursor:
2260                                 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]),))
2261                         cursor.close() 
2262                         connection.close()
2263                         self["list"].setList(SongList)
2264                         if len(SongList) > 1:
2265                                 self["list"].moveToIndex(1)
2266
2267         
2268         def buildArtistSongList(self, artistID, mode, addToCache):
2269                 if addToCache:
2270                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2271                 arguments = {}
2272                 arguments["artistID"] = artistID
2273                 arguments["mode"] = mode
2274                 arguments["addToCache"] = False
2275                 self.LastMethod = MethodArguments(method = self.buildArtistSongList, arguments = arguments)
2276                 connection = OpenDatabase()
2277                 if connection is not None:
2278                         connection.text_factory = str
2279                         cursor = connection.cursor()
2280                         artistSongList = []
2281                         artistSongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2282                         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))
2283                         for row in cursor:
2284                                 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),))
2285                         cursor.execute("SELECT artist from artists where artist_ID = %d;" % artistID)
2286                         row = cursor.fetchone()
2287                         self["headertext"].setText(_("Artist (%s) -> Song List") % row[0])
2288                         cursor.close() 
2289                         connection.close()
2290                         self["list"].setList(artistSongList)
2291                         if len(artistSongList) > 1:
2292                                 self["list"].moveToIndex(1)
2293                         
2294         def buildAlbumSongList(self, albumID, mode, addToCache):
2295                 if addToCache:
2296                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2297                 arguments = {}
2298                 arguments["albumID"] = albumID
2299                 arguments["mode"] = mode
2300                 arguments["addToCache"] = False
2301                 self.LastMethod = MethodArguments(method = self.buildAlbumSongList, arguments = arguments)
2302                 connection = OpenDatabase()
2303                 if connection is not None:
2304                         connection.text_factory = str
2305                         cursor = connection.cursor()
2306                         albumSongList = []
2307                         albumSongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2308                         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))
2309                         for row in cursor:
2310                                 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),))
2311                         cursor.execute("SELECT album_text from album where album_ID = %d;" % albumID)
2312                         row = cursor.fetchone()
2313                         self["headertext"].setText(_("Album (%s) -> Song List") % row[0])
2314                         cursor.close() 
2315                         connection.close()
2316                         self["list"].setList(albumSongList)
2317                         if len(albumSongList) > 1:
2318                                 self["list"].moveToIndex(1)
2319                 
2320         def buildMainMenuList(self, addToCache = True):
2321                 arguments = {}
2322                 arguments["addToCache"] = True
2323                 self.LastMethod = MethodArguments(method = self.buildMainMenuList, arguments = arguments)
2324                 self["headertext"].setText(_("iDream Main Menu"))
2325                 mainMenuList = []
2326                 connection = OpenDatabase()
2327                 if connection is not None:
2328                         connection.text_factory = str
2329                         cursor = connection.cursor()
2330                         # 1. Playlists
2331                         cursor.execute("SELECT COUNT (*) FROM playlists;")
2332                         row = cursor.fetchone()
2333                         mainMenuList.append((Item(text = _("Playlists (%d)") % row[0], mode = 1),))
2334                         # 2. Artists
2335                         cursor.execute("SELECT COUNT (*) FROM artists;")
2336                         row = cursor.fetchone()
2337                         mainMenuList.append((Item(text = _("Artists (%d)") % row[0], mode = 4),))
2338                         # 3. Albums
2339                         cursor.execute("SELECT COUNT (DISTINCT album_text) FROM album;")
2340                         row = cursor.fetchone()
2341                         mainMenuList.append((Item(text = _("Albums (%d)") % row[0], mode = 7),))
2342                         # 4. Songs
2343                         cursor.execute("SELECT COUNT (*) FROM songs;")
2344                         row = cursor.fetchone()
2345                         mainMenuList.append((Item(text = _("Songs (%d)") % row[0], mode = 10),))
2346                         # 5. Genres
2347                         cursor.execute("SELECT COUNT (*) FROM genre;")
2348                         row = cursor.fetchone()
2349                         mainMenuList.append((Item(text = _("Genres (%d)") % row[0], mode = 13),))
2350                         cursor.close()  
2351                         connection.close()
2352                         self["list"].setList(mainMenuList)
2353                         self["list"].moveToIndex(0)
2354                 
2355         def buildArtistList(self, addToCache):
2356                 if addToCache:
2357                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2358                 arguments = {}
2359                 arguments["addToCache"] = False
2360                 self.LastMethod = MethodArguments(method = self.buildArtistList, arguments = arguments)
2361                 self["headertext"].setText(_("Artists List"))
2362                 connection = OpenDatabase()
2363                 if connection is not None:
2364                         connection.text_factory = str
2365                         cursor = connection.cursor()
2366                         artistList = []
2367                         artistList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2368                         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;")
2369                         for row in cursor:
2370                                 artistList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 5, artistID = row[0]),))
2371                         cursor.close() 
2372                         connection.close()
2373                         self["list"].setList(artistList)
2374                 
2375         def buildArtistAlbumList(self, ArtistID, addToCache):
2376                 if addToCache:
2377                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2378                 arguments = {}
2379                 arguments["ArtistID"] = ArtistID
2380                 arguments["addToCache"] = False
2381                 self.LastMethod = MethodArguments(method = self.buildArtistAlbumList, arguments = arguments)
2382                 connection = OpenDatabase()
2383                 if connection is not None:
2384                         connection.text_factory = str
2385                         cursor = connection.cursor()
2386                         albumArtistList = []
2387                         albumArtistList.append((Item(text = _("[back]"), mode = 4, navigator = True),))
2388                         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)
2389                         for row in cursor:
2390                                 cursor2 = connection.cursor()
2391                                 cursor2.execute("select count(song_id) from songs where album_id = %d;" % row[0])
2392                                 row2 = cursor2.fetchone()
2393                                 albumArtistList.append((Item(text = "%s (%d)" % (row[1], row2[0]), mode = 6, albumID = row[0], artistID = ArtistID),))
2394                                 cursor2.close()
2395                         cursor.execute("SELECT artist from artists where artist_ID = %d;" % ArtistID)
2396                         row = cursor.fetchone()
2397                         self["headertext"].setText(_("Artist (%s) -> Album List") % row[0])
2398                         cursor.close() 
2399                         connection.close()
2400                         self["list"].setList(albumArtistList)
2401                         if len(albumArtistList) > 1:
2402                                 self["list"].moveToIndex(1)
2403                         
2404         def buildAlbumList(self, addToCache):
2405                 if addToCache:
2406                         self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2407                 arguments = {}
2408                 arguments["addToCache"] = False
2409                 self.LastMethod = MethodArguments(method = self.buildAlbumList, arguments = arguments)
2410                 self["headertext"].setText(_("Albums List"))
2411                 connection = OpenDatabase()
2412                 if connection is not None:
2413                         connection.text_factory = str
2414                         cursor = connection.cursor()
2415                         albumList = []
2416                         albumList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2417                         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;")
2418                         for row in cursor:
2419                                 albumList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 8, albumID = row[0]),))
2420                         cursor.close() 
2421                         connection.close()
2422                         self["list"].setList(albumList)
2423                         if len(albumList) > 1:
2424                                 self["list"].moveToIndex(1)
2425                         
2426         def startRun(self):
2427                 if pathToDatabase.isRunning:
2428                         self.showScanner = eTimer()
2429                         self.showScanner.callback.append(self.showScannerCallback)
2430                         self.showScanner.start(0,1)
2431                 else:
2432                         if config.plugins.merlinmusicplayer.startlastsonglist.value:
2433                                 self.startPlayerTimer = eTimer()
2434                                 self.startPlayerTimer.callback.append(self.startPlayerTimerCallback)
2435                                 self.startPlayerTimer.start(0,1)
2436                         self.mode = 0
2437                         self["list"].setMode(self.mode)
2438                         self.buildMainMenuList()
2439
2440         def showScannerCallback(self):
2441                 self.session.openWithCallback(self.filesAdded, iDreamAddToDatabase,None)
2442
2443
2444         def startPlayerTimerCallback(self):
2445                 connection = OpenDatabase()
2446                 if connection is not None:
2447                         connection.text_factory = str
2448                         cursor = connection.cursor()
2449                         iDreamMode = False
2450                         SongList = []
2451                         cursor.execute("select song_id, filename, title, artist, album, genre, bitrate, length,  track, date, PTS from CurrentSongList;")
2452                         for row in cursor:
2453                                 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),))
2454                                 if row[0] != 0:
2455                                         iDreamMode = True
2456                         cursor.close() 
2457                         connection.close()
2458                         if self.player is not None:
2459                                 self.player.doClose()
2460                                 self.player = None
2461                         self.startMerlinPlayerScreenTimer.stop()
2462                         count = len(SongList)
2463                         if count:
2464                                 # just to be sure, check the index , it's critical
2465                                 index = config.plugins.merlinmusicplayer.lastsonglistindex.value
2466                                 if index >= count:
2467                                         index = 0
2468                                 self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,SongList, index, iDreamMode, self.currentService, self.serviceList)
2469                                 self.session.execDialog(self.player)
2470
2471         def config(self):
2472                 self.startMerlinPlayerScreenTimer.stop()
2473                 self.session.openWithCallback(self.setupFinished, MerlinMusicPlayerSetup, True)
2474
2475         def setupFinished(self, result):
2476                 if result:
2477                         self.red_pressed()
2478
2479
2480         def stopPlayingAndAppendFileToSongList(self):
2481                 self.startMerlinPlayerScreenTimer.stop()
2482                 if self.player is not None:
2483                         self.player.doClose()
2484                         self.player = None
2485                 self.appendFileToSongList()     
2486                 self.startMerlinPlayerScreenTimer.start(START_MERLIN_PLAYER_SCREEN_TIMER_VALUE)
2487
2488         def appendFileToSongList(self):
2489                 SongList = []
2490                 playerAvailable =  self.player is not None and self.player.songList
2491                 sel = self.getCurrentSelection()
2492                 if sel:
2493                         if playerAvailable:
2494                                 self.player.songList.append((sel,))
2495                                 self.player.origSongList.append((sel,))
2496                         else:
2497                                 SongList.append((sel,))
2498                         if not playerAvailable:
2499                                 if self.player is not None:
2500                                         self.player.doClose()
2501                                         self.player = None
2502                                 self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,SongList, 0, True, self.currentService, self.serviceList)
2503                                 self.player.playSong(self.player.songList[self.player.currentIndex][0].filename)
2504                                 self.player["coverArt"].onShow()
2505                                 self.player.init = 1
2506                         else:
2507                                 self.player["nextTitle"].setText(self.player.getNextTitle())
2508                                 self.session.open(MessageBox, _("%s\nappended to songlist")%sel.title, type = MessageBox.TYPE_INFO,timeout = 3 )
2509
2510         def insertFileToSongList(self):
2511                 sel = self.getCurrentSelection()
2512                 if sel:
2513                         if self.player is not None and self.player.songList:
2514                                 index = self.player.currentIndex
2515                                 self.player.songList.insert(index+1,(sel,))
2516                                 self.player.origSongList.insert(index+1,(sel,))
2517                                 self.player["nextTitle"].setText(self.player.getNextTitle())
2518                                 self.session.open(MessageBox, _("%s\ninserted and will be played as next song")%sel.title, type = MessageBox.TYPE_INFO,timeout = 3 )
2519                         else:
2520                                 self.appendFileToSongList()
2521
2522         def Error(self, error = None):
2523                 if error is not None:
2524                         self["list"].hide()
2525                         self["statustext"].setText(str(error.getErrorMessage()))
2526                         
2527         def closing(self):
2528                 self.close()
2529                 
2530         def __onClose(self):
2531                 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
2532                 self.startMerlinPlayerScreenTimer.stop()
2533                 if self.player is not None:
2534                         self.player.closePlayer()
2535                         self.player.doClose()
2536                         self.player = None
2537                 if self.serviceList is None:
2538                         self.session.nav.playService(self.currentService)
2539                 else:
2540                         current = ServiceReference(self.serviceList.getCurrentSelection())
2541                         self.session.nav.playService(current.ref)
2542                 
2543
2544         def lcdUpdate(self):
2545                 self.startMerlinPlayerScreenTimer.start(START_MERLIN_PLAYER_SCREEN_TIMER_VALUE)
2546                 try:
2547                         count = self["list"].getItemCount()
2548                         index = self["list"].getCurrentIndex()
2549                         iDreamList = self["list"].getList()
2550                         self.summaries.setText(iDreamList[index][0].title or iDreamList[index][0].text,1)
2551                         # voheriges