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