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