2 # Merlin Music Player E2
6 # Coded by Dr.Best (c) 2010
7 # Support: www.dreambox-tools.info
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.
15 # Alternatively, this plugin may be distributed and executed on hardware which
16 # is licensed by Dream Multimedia GmbH.
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.
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
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
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
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
81 START_MERLIN_PLAYER_SCREEN_TIMER_VALUE = 7000
83 config.plugins.merlinmusicplayer = ConfigSubsection()
84 config.plugins.merlinmusicplayer.hardwaredecoder = ConfigYesNo(default = True)
85 config.plugins.merlinmusicplayer.startlastsonglist = ConfigYesNo(default = True)
86 config.plugins.merlinmusicplayer.lastsonglistindex = ConfigInteger(-1)
87 config.plugins.merlinmusicplayer.databasepath = ConfigDirectory(default = "/hdd/")
88 config.plugins.merlinmusicplayer.usegoogleimage = ConfigYesNo(default = True)
89 config.plugins.merlinmusicplayer.googleimagepath = ConfigDirectory(default = "/hdd/")
90 config.plugins.merlinmusicplayer.usescreensaver = ConfigYesNo(default = True)
91 config.plugins.merlinmusicplayer.screensaverwait = ConfigInteger(1,limits = (1, 60))
92 config.plugins.merlinmusicplayer.idreamextendedpluginlist = ConfigYesNo(default = True)
93 config.plugins.merlinmusicplayer.merlinmusicplayerextendedpluginlist = ConfigYesNo(default = True)
94 config.plugins.merlinmusicplayer.defaultfilebrowserpath = ConfigDirectory(default = "/hdd/")
95 config.plugins.merlinmusicplayer.rememberlastfilebrowserpath = ConfigYesNo(default = True)
96 config.plugins.merlinmusicplayer.idreammainmenu = ConfigYesNo(default = False)
97 config.plugins.merlinmusicplayer.merlinmusicplayermainmenu = ConfigYesNo(default = False)
99 from enigma import ePythonMessagePump
100 from threading import Thread, Lock
101 from timer import TimerEntry
111 self.__list.append(val)
117 ret = self.__list.pop()
124 class PathToDatabase(Thread):
126 Thread.__init__(self)
127 self.__running = False
128 self.__cancel = False
130 self.__messages = ThreadQueue()
131 self.__messagePump = ePythonMessagePump()
133 def __getMessagePump(self):
134 return self.__messagePump
136 def __getMessageQueue(self):
137 return self.__messages
139 def __getRunning(self):
140 return self.__running
145 MessagePump = property(__getMessagePump)
146 Message = property(__getMessageQueue)
147 isRunning = property(__getRunning)
149 def Start(self, path):
150 if not self.__running:
155 mp = self.__messagePump
156 self.__running = True
157 self.__cancel = False
159 connection = OpenDatabase()
160 if connection is not None:
161 connection.text_factory = str
162 cursor = connection.cursor()
165 for root, subFolders, files in os_walk(self.__path):
168 for filename in files:
171 cursor.execute('SELECT song_id FROM Songs WHERE filename = "%s";' % os_path.join(root,filename))
172 row = cursor.fetchone()
174 audio, isAudio, title, genre,artist,album,tracknr,track,date,length,bitrate = getID3Tags(root,filename)
178 cursor.execute('SELECT artist_id FROM Artists WHERE artist = "%s";' % (artist.replace('"','""')))
180 row = cursor.fetchone()
182 cursor.execute('INSERT INTO Artists (artist) VALUES("%s");' % (artist.replace('"','""')))
183 artistID = cursor.lastrowid
188 cursor.execute('SELECT album_id FROM Album WHERE album_text = "%s";' % (album.replace('"','""')))
189 row = cursor.fetchone()
191 cursor.execute('INSERT INTO Album (album_text) VALUES("%s");' % (album.replace('"','""')))
192 albumID = cursor.lastrowid
198 cursor.execute('SELECT genre_id FROM Genre WHERE genre_text = "%s";' % (genre.replace('"','""')))
199 row = cursor.fetchone()
201 cursor.execute('INSERT INTO Genre (genre_text) VALUES("%s");' % (genre.replace('"','""')))
202 genreID = cursor.lastrowid
208 cursor.execute("INSERT INTO Songs (filename,title,artist_id,album_id,genre_id,tracknumber, bitrate, length, track, date) VALUES(?,?,?,?,?,?,?,?,?,?);" , (os_path.join(root,filename),title,artistID,albumID,genreID, tracknr, bitrate, length, track, date))
209 self.__messages.push((THREAD_WORKING, _("%s\n added to database") % os_path.join(root,filename)))
212 except sqlite.IntegrityError:
213 self.__messages.push((THREAD_WORKING, _("%s\n already exists in database!") % os_path.join(root,filename)))
216 elif time() - checkTime >= 0.1: # update interval for gui
217 self.__messages.push((THREAD_WORKING, _("%s\n already exists in database!") % os_path.join(root,filename)))
221 if not self.__cancel:
226 self.__messages.push((THREAD_FINISHED, _("Process aborted.\n 0 files added to database!\nPress OK to close.") ))
228 self.__messages.push((THREAD_FINISHED, _("%d files added to database!\nPress OK to close." % counter)))
230 self.__messages.push((THREAD_FINISHED, _("Error!\nCan not open database!\nCheck if save folder is correct and writeable!\nPress OK to close.") ))
232 self.__running = False
233 Thread.__init__(self)
235 pathToDatabase = PathToDatabase()
238 class iDreamAddToDatabase(Screen):
239 skin = """<screen name="iDreamAddToDatabase" position="center,center" size="560,320" title="Add music files to iDream database">
240 <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
241 <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
242 <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
243 <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
244 <widget name="output" position="10,10" size="540,300" valign="center" halign="center" font="Regular;22" />
245 <widget render="Label" source="key_red" position="0,0" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
246 <widget render="Label" source="key_green" position="140,0" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
248 def __init__(self, session, initDir):
249 Screen.__init__(self, session)
250 self["actions"] = ActionMap(["WizardActions", "ColorActions"],
258 self["key_red"] = StaticText(_("Cancel"))
259 self["key_green"] = StaticText("Close")
260 self["output"] = Label()
261 self.onClose.append(self.__onClose)
262 pathToDatabase.MessagePump.recv_msg.get().append(self.gotThreadMsg)
263 if not pathToDatabase.isRunning and initDir:
264 pathToDatabase.Start(initDir)
266 def gotThreadMsg(self, msg):
267 msg = pathToDatabase.Message.pop()
268 self["output"].setText(msg[1])
269 if msg[0] == THREAD_FINISHED:
270 self["key_red"].setText("")
276 if pathToDatabase.isRunning:
277 pathToDatabase.Cancel()
280 pathToDatabase.MessagePump.recv_msg.get().remove(self.gotThreadMsg)
283 class myHTTPClientFactory(HTTPClientFactory):
284 def __init__(self, url, method='GET', postdata=None, headers=None,
285 agent="SHOUTcast", timeout=0, cookies=None,
286 followRedirect=1, lastModified=None, etag=None):
287 HTTPClientFactory.__init__(self, url, method=method, postdata=postdata,
288 headers=headers, agent=agent, timeout=timeout, cookies=cookies,followRedirect=followRedirect)
290 def sendUrlCommand(url, contextFactory=None, timeout=60, *args, **kwargs):
291 scheme, host, port, path = client._parse(url)
292 factory = myHTTPClientFactory(url, *args, **kwargs)
293 reactor.connectTCP(host, port, factory, timeout=timeout)
294 return factory.deferred
297 class MethodArguments:
298 def __init__(self, method = None, arguments = None):
300 self.arguments = arguments
303 def __init__(self, cache = True, index = 0, listview = [], headertext = "", methodarguments = None):
306 self.listview = listview
307 self.headertext = headertext
308 self.methodarguments = methodarguments
311 def __init__(self, text = "", mode = 0, id = -1, navigator = False, artistID = 0, albumID = 0, title = "", artist = "", filename = "", bitrate = None, length = "", genre = "", track = "", date = "", album = "", playlistID = 0, genreID = 0, songID = 0, join = True, PTS = None):
314 self.navigator = navigator
315 self.artistID = artistID
316 self.albumID = albumID
319 self.filename = filename
320 if bitrate is not None:
322 self.bitrate = "%d Kbps" % bitrate
324 self.bitrate = bitrate
329 if track is not None:
330 self.track = "Track %s" % track
335 self.date = " (%s)" % date
341 self.playlistID = playlistID
342 self.genreID = genreID
348 connectstring = os_path.join(config.plugins.merlinmusicplayer.databasepath.value ,"iDream.db")
350 if os_path.exists(connectstring):
353 connection = sqlite.connect(connectstring)
354 if not os_access(connectstring, os_W_OK):
355 print "[MerlinMusicPlayer] Error: database file needs to be writable, can not open %s for writing..." % connectstring
359 print "[MerlinMusicPlayer] unable to open database file: %s" % connectstring
362 connection.execute('CREATE TABLE IF NOT EXISTS Songs (song_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, filename TEXT NOT NULL UNIQUE, title TEXT, artist_id INTEGER, album_id INTEGER, genre_id INTEGER, tracknumber INTEGER, bitrate INTEGER, length TEXT, track TEXT, date TEXT, lyrics TEXT);')
363 connection.execute('CREATE TABLE IF NOT EXISTS Artists (artist_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, artist TEXT NOT NULL UNIQUE);')
364 connection.execute('CREATE TABLE IF NOT EXISTS Album (album_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, album_text TEXT NOT NULL UNIQUE);')
365 connection.execute('CREATE TABLE IF NOT EXISTS Genre (genre_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, genre_text TEXT NOT NULL UNIQUE);')
366 connection.execute('CREATE TABLE IF NOT EXISTS Playlists (playlist_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, playlist_text TEXT NOT NULL);')
367 connection.execute('CREATE TABLE IF NOT EXISTS Playlist_Songs (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, playlist_id INTEGER NOT NULL, song_id INTEGER NOT NULL);')
368 connection.execute('CREATE TABLE IF NOT EXISTS CurrentSongList (ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, song_id INTEGER, filename TEXT NOT NULL, title TEXT, artist TEXT, album TEXT, genre TEXT, bitrate TEXT, length TEXT, track TEXT, date TEXT, PTS INTEGER);')
371 def getEncodedString(value):
374 returnValue = value.encode("utf-8", 'ignore')
375 except UnicodeDecodeError:
377 returnValue = value.encode("iso8859-1", 'ignore')
378 except UnicodeDecodeError:
380 returnValue = value.decode("cp1252").encode("utf-8")
381 except UnicodeDecodeError:
385 def getID3Tags(root,filename):
398 if filename.lower().endswith(".mp3"):
399 try: audio = MP3(os_path.join(root,filename), ID3 = EasyID3)
401 elif filename.lower().endswith(".flac"):
403 audio = FLAC(os_path.join(root,filename))
406 elif filename.lower().endswith(".m4a"):
407 try: audio = EasyMP4(os_path.join(root,filename))
409 elif filename.lower().endswith(".ogg"):
410 try: audio = OggVorbis(os_path.join(root,filename))
415 title = getEncodedString(audio.get('title', [filename])[0])
417 # list index out of range workaround
418 genre = getEncodedString(audio.get('genre', ['n/a'])[0])
421 artist = getEncodedString(audio.get('artist', ['n/a'])[0])
422 album = getEncodedString(audio.get('album', ['n/a'])[0])
424 tracknr = int(audio.get('tracknumber', ['-1'])[0].split("/")[0])
427 track = getEncodedString(audio.get('tracknumber', ['n/a'])[0])
428 date = getEncodedString(audio.get('date', ['n/a'])[0])
430 length = str(datetime_timedelta(seconds=int(audio.info.length))).encode("utf-8", 'ignore')
434 bitrate = audio.info.bitrate / 1000
439 title = os_path.splitext(os_path.basename(filename))[0]
449 return audio, isAudio, title, genre ,artist, album, tracknr, track, date, length, bitrate
451 class MerlinMusicPlayerScreenSaver(Screen):
453 sz_w = getDesktop(0).size().width()
456 <screen name="MerlinMusicPlayerScreenSaver" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="MerlinMusicPlayerScreenSaver">
457 <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="200,77" size="238,238" transparent="1" alphatest="blend" />
458 <widget name="display" position="200,315" size="1280,24" zPosition="1" transparent="1" font="Regular;20" foregroundColor="#fcc000" />
462 <screen name="MerlinMusicPlayerScreenSaver" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="MerlinMusicPlayerScreenSaver">
463 <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="200,77" size="238,238" transparent="1" alphatest="blend" />
464 <widget name="display" position="200,315" size="1024,24" zPosition="1" transparent="1" font="Regular;20" foregroundColor="#fcc000" />
469 <screen name="MerlinMusicPlayerScreenSaver" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="MerlinMusicPlayerScreenSaver">
470 <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="200,77" size="238,238" transparent="1" alphatest="blend" />
471 <widget name="display" position="200,315" size="720,24" zPosition="1" transparent="1" font="Regular;20" foregroundColor="#fcc000" />
475 def __init__(self, session):
476 self.session = session
477 Screen.__init__(self, session)
478 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "ColorActions", "EventViewActions"],
486 "pageUp": self.close,
487 "pageDown": self.close,
488 "yellow": self.close,
494 "prevBouquet": self.close,
495 "nextBouquet": self.close,
499 self["coverArt"] = MerlinMediaPixmap()
500 self.coverMoveTimer = eTimer()
501 self.coverMoveTimer.timeout.get().append(self.moveCoverArt)
502 self.coverMoveTimer.start(1)
503 self["display"] = Label()
504 self.onClose.append(self.__onClose)
505 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
507 def sleepTimerEntryOnStateChange(self, timer):
508 if timer.state == TimerEntry.StateEnded:
512 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
514 def updateDisplayText(self, text):
515 self["display"].setText(text)
517 def updateLCD(self, text, line):
518 self.summaries.setText(text,line)
520 def updateCover(self, filename = None, modus = 0):
521 print "[MerlinMusicPlayerScreenSaver] updating coverart with filename = %s and modus = %d" % (filename, modus)
524 self["coverArt"].showCoverFromFile(filename)
526 self["coverArt"].showDefaultCover()
528 self["coverArt"].showDefaultCover()
530 self["coverArt"].embeddedCoverArt()
532 self["coverArt"].updateCoverArt(filename)
534 self["coverArt"].showCoverFromFile(filename)
536 def moveCoverArt(self):
537 x = randrange(getDesktop(0).size().width()-238)
538 y = randrange(getDesktop(0).size().height()-238-28)
539 self["coverArt"].move(ePoint(x,y))
540 self["display"].move(ePoint(x,y+240))
541 self.coverMoveTimer.start(15000)
543 def createSummary(self):
544 return MerlinMusicPlayerLCDScreen
547 class MerlinMusicPlayerTV(MerlinMusicPlayerScreenSaver):
549 w = getDesktop(0).size().width()
550 h = getDesktop(0).size().height()
561 <screen backgroundColor="transparent" flags="wfNoBorder" position="0,0" size="%d,%d" title="MerlinMusicPlayerTV">
562 <widget backgroundColor="transparent" name="video" position="0,0" size="%d,%d" zPosition="1"/>
563 <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="%d,%d" size="64,64" transparent="1" alphatest="blend" zPosition="2" />
564 <widget name="display" position="%d,%d" size="%d,24" zPosition="2" backgroundColor="#33000000" font="Regular;20" foregroundColor="#fcc000" />
565 </screen>""" % (w,h,w,h,cx,cy,dx,dy,dw)
568 def __init__(self, session, currentService, servicelist):
569 self.session = session
570 Screen.__init__(self, session)
571 self.onClose.append(self.__onClose)
572 self["actions"] = ActionMap(["OkCancelActions", "DirectionActions", "ChannelSelectBaseActions", "ChannelSelectEPGActions"],
574 "cancel": self.close,
576 "right": self.nextService,
577 "left": self.prevService,
578 "nextBouquet": self.nextBouquet,
579 "prevBouquet": self.prevBouquet,
580 "showEPGList": self.openEventView,
583 self["actions2"] = NumberActionMap(["NumberActions"],
585 "1": self.keyNumberGlobal,
586 "2": self.keyNumberGlobal,
587 "3": self.keyNumberGlobal,
588 "4": self.keyNumberGlobal,
589 "5": self.keyNumberGlobal,
590 "6": self.keyNumberGlobal,
591 "7": self.keyNumberGlobal,
592 "8": self.keyNumberGlobal,
593 "9": self.keyNumberGlobal,
595 self.epgcache = eEPGCache.getInstance()
596 self.servicelist = servicelist
597 self["coverArt"] = MerlinMediaPixmap()
598 self["display"] = Label()
599 self["video"] = VideoWindow(fb_width = getDesktop(0).size().width(), fb_height = getDesktop(0).size().height())
600 if self.servicelist is None:
601 self.playService(currentService)
603 current = ServiceReference(self.servicelist.getCurrentSelection())
604 self.playService(current.ref)
606 self.showHideTimer = eTimer()
607 self.showHideTimer.timeout.get().append(self.showHideTimerTimeout)
608 self.idx = config.usage.infobar_timeout.index
610 self.showHideTimer.start(self.idx * 1000)
611 self.displayShown = True
612 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
614 def sleepTimerEntryOnStateChange(self, timer):
615 if timer.state == TimerEntry.StateEnded:
619 if self.displayShown:
620 if self.showHideTimer.isActive():
621 self.showHideTimer.stop()
622 self["coverArt"].hide()
623 self["display"].hide()
625 self["coverArt"].show()
626 self["display"].show()
628 self.showHideTimer.start(self.idx * 1000)
629 self.displayShown = not self.displayShown
631 def showHideTimerTimeout(self):
634 def updateDisplayText(self, text):
635 if self.showHideTimer.isActive():
636 self.showHideTimer.stop()
637 self["display"].setText(text)
638 self.displayShown = False
641 # Source Code taken from Virtual(Pip)Zap :-)
643 # switch with numbers
644 def keyNumberGlobal(self, number):
645 self.session.openWithCallback(self.numberEntered, NumberZap, number)
647 def numberEntered(self, retval):
649 self.zapToNumber(retval)
651 def searchNumberHelper(self, serviceHandler, num, bouquet):
652 servicelist = serviceHandler.list(bouquet)
653 if not servicelist is None:
655 serviceIterator = servicelist.getNext()
656 if not serviceIterator.valid(): #check end of list
658 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
661 if not num: #found service with searched number ?
662 return serviceIterator, 0
665 def zapToNumber(self, number):
666 bouquet = self.servicelist.bouquet_root
668 serviceHandler = eServiceCenter.getInstance()
669 bouquetlist = serviceHandler.list(bouquet)
670 if not bouquetlist is None:
672 bouquet = bouquetlist.getNext()
673 if not bouquet.valid(): #check end of list
675 if bouquet.flags & eServiceReference.isDirectory:
676 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
677 if not service is None:
678 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
679 self.servicelist.clearPath()
680 if self.servicelist.bouquet_root != bouquet:
681 self.servicelist.enterPath(self.servicelist.bouquet_root)
682 self.servicelist.enterPath(bouquet)
683 self.servicelist.setCurrentSelection(service) #select the service in servicelist
684 # update infos, no matter if service is none or not
685 current = ServiceReference(self.servicelist.getCurrentSelection())
686 self.playService(current.ref)
688 def nextService(self):
689 if self.servicelist is not None:
691 if self.servicelist.inBouquet():
692 prev = self.servicelist.getCurrentSelection()
694 prev = prev.toString()
696 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
697 self.servicelist.nextBouquet()
699 self.servicelist.moveDown()
700 cur = self.servicelist.getCurrentSelection()
701 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
704 self.servicelist.moveDown()
705 if self.isPlayable():
706 current = ServiceReference(self.servicelist.getCurrentSelection())
707 self.playService(current.ref)
711 def prevService(self):
712 if self.servicelist is not None:
713 # get previous service
714 if self.servicelist.inBouquet():
715 prev = self.servicelist.getCurrentSelection()
717 prev = prev.toString()
719 if config.usage.quickzap_bouquet_change.value:
720 if self.servicelist.atBegin():
721 self.servicelist.prevBouquet()
722 self.servicelist.moveUp()
723 cur = self.servicelist.getCurrentSelection()
724 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
727 self.servicelist.moveUp()
728 if self.isPlayable():
729 current = ServiceReference(self.servicelist.getCurrentSelection())
730 self.playService(current.ref)
734 def isPlayable(self):
735 # check if service is playable
736 current = ServiceReference(self.servicelist.getCurrentSelection())
737 return not (current.ref.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
740 def nextBouquet(self):
741 if self.servicelist is not None:
742 # next bouquet with first service
743 if config.usage.multibouquet.value:
744 self.servicelist.nextBouquet()
745 current = ServiceReference(self.servicelist.getCurrentSelection())
746 self.playService(current.ref)
748 def prevBouquet(self):
749 if self.servicelist is not None:
750 # previous bouquet with first service
751 if config.usage.multibouquet.value:
752 self.servicelist.prevBouquet()
753 current = ServiceReference(self.servicelist.getCurrentSelection())
754 self.playService(current.ref)
756 def openSingleServiceEPG(self):
758 current = ServiceReference(self.servicelist.getCurrentSelection())
759 self.session.open(EPGSelection, current.ref)
761 def openEventView(self):
764 self.epglist = epglist
765 service = ServiceReference(self.servicelist.getCurrentSelection())
767 evt = self.epgcache.lookupEventTime(ref, -1)
770 evt = self.epgcache.lookupEventTime(ref, -1, 1)
774 self.session.open(EventViewEPGSelect, epglist[0], service, self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
776 def eventViewCallback(self, setEvent, setService, val):
777 epglist = self.epglist
780 epglist[0] = epglist[1]
784 def openMultiServiceEPG(self):
788 def openSimilarList(self, eventid, refstr):
789 self.session.open(EPGSelection, refstr, None, eventid)
791 def playService(self, service):
792 if service and (service.flags & eServiceReference.isGroup):
793 ref = getBestPlayableServiceReference(service, eServiceReference())
796 self.pipservice = eServiceCenter.getInstance().play(ref)
797 if self.pipservice and not self.pipservice.setTarget(1):
798 self.pipservice.start()
801 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
802 self.pipservice = None
803 if self.showHideTimer.isActive():
804 self.showHideTimer.stop()
808 class MerlinMusicPlayerScreen(Screen, InfoBarBase, InfoBarSeek, InfoBarNotifications):
810 sz_w = getDesktop(0).size().width()
813 <screen name="MerlinMusicPlayerScreen" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
814 <eLabel backgroundColor="#999999" position="178,112" size="924,2" zPosition="1"/>
815 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="178,104" size="250,20" text="MERLIN MUSIC PLAYER" valign="center" zPosition="2"/>
816 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="852,104" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
817 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmp3pHD.png" position="128,72" size="1024,576"/>
818 <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="328,149" size="238,238" transparent="1" alphatest="blend" />
819 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr.png" position="688,232" size="150,20" transparent="1" zPosition="1"/>
820 <widget name="title" position="362,415" size="600,28" zPosition="1" transparent="1" font="Regular;24" foregroundColor="#fcc000" />
821 <widget name="album" position="362,462" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
822 <widget name="artist" position="362,492" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
823 <widget name="genre" position="362,522" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
824 <widget name="nextTitle" position="362,562" size="600,22" zPosition="1" transparent="1" font="Regular;16" foregroundColor="#f0f0f0" />
825 <widget name="PositionGauge" position="664,264" size="198,14" pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" seek_pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" transparent="1"/>
826 <widget source="session.CurrentService" render="Label" position="873,267" size="116,18" zPosition="1" font="Regular;18" halign="left" foregroundColor="#999999" transparent="1" >
827 <convert type="ServicePosition">Length,ShowHours</convert>
829 <widget source="session.CurrentService" render="Label" position="684,292" size="198,20" zPosition="1" font="Regular;20" halign="left" foregroundColor="#fcc000" transparent="1" >
830 <convert type="ServicePosition">Position,ShowHours</convert>
832 <widget name="shuffle" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_shuf.png" position="598,275" size="53,34" transparent="1" alphatest="on"/>
833 <widget name="repeat" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_rep.png" position="598,239" size="53,34" transparent="1" alphatest="on"/>
834 <widget name="dvrStatus" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pl.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pau.png" position="683,227" size="160,39" transparent="1" alphatest="on"/>
838 <screen name="MerlinMusicPlayerScreen" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
839 <eLabel backgroundColor="#999999" position="50,40" size="924,2" zPosition="1"/>
840 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,32" size="250,20" text="MERLIN MUSIC PLAYER" valign="center" zPosition="2"/>
841 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="724,32" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
842 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmp3pHD.png" position="0,0" size="1024,576"/>
843 <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="200,77" size="238,238" transparent="1" alphatest="blend" />
844 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr.png" position="560,160" size="150,20" transparent="1" zPosition="1"/>
845 <widget name="title" position="234,343" size="600,28" zPosition="1" transparent="1" font="Regular;24" foregroundColor="#fcc000" />
846 <widget name="album" position="234,390" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
847 <widget name="artist" position="234,420" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
848 <widget name="genre" position="234,450" size="600,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
849 <widget name="nextTitle" position="234,490" size="600,22" zPosition="1" transparent="1" font="Regular;16" foregroundColor="#f0f0f0" />
850 <widget name="PositionGauge" position="536,197" size="198,14" pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" seek_pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" transparent="1"/>
851 <widget source="session.CurrentService" render="Label" position="745,195" size="116,18" zPosition="1" font="Regular;18" halign="left" foregroundColor="#999999" transparent="1" >
852 <convert type="ServicePosition">Length,ShowHours</convert>
854 <widget source="session.CurrentService" render="Label" position="556,220" size="198,20" zPosition="1" font="Regular;20" halign="left" foregroundColor="#fcc000" transparent="1" >
855 <convert type="ServicePosition">Position,ShowHours</convert>
857 <widget name="shuffle" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_shuf.png" position="470,203" size="53,34" transparent="1" alphatest="on"/>
858 <widget name="repeat" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_rep.png" position="470,167" size="53,34" transparent="1" alphatest="on"/>
859 <widget name="dvrStatus" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pl.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pau.png" position="555,155" size="160,39" transparent="1" alphatest="on"/>
863 <screen name="MerlinMusicPlayerScreen" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
864 <eLabel backgroundColor="#999999" position="50,50" size="620,2" zPosition="1"/>
865 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,40" size="250,20" text="MERLIN MUSIC PLAYER" valign="center" zPosition="2"/>
866 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="420,40" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
867 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmp3p.png" position="120,350" size="33,162"/>
868 <widget name="coverArt" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/no_coverArt.png" position="106,130" size="180,180" transparent="1" alphatest="blend" />
869 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr.png" position="410,160" size="150,20" transparent="1" zPosition="1"/>
870 <widget name="title" position="160,345" size="550,28" zPosition="1" transparent="1" font="Regular;24" foregroundColor="#fcc000" />
871 <widget name="album" position="160,392" size="550,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
872 <widget name="artist" position="160,422" size="550,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
873 <widget name="genre" position="160,455" size="550,22" zPosition="1" transparent="1" font="Regular;18" foregroundColor="#999999" />
874 <widget name="nextTitle" position="160,492" size="550,22" zPosition="1" transparent="1" font="Regular;16" foregroundColor="#f0f0f0" />
875 <widget name="PositionGauge" position="386,197" size="198,14" pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" seek_pointer="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/progressbar.png:198,0" transparent="1"/>
876 <widget source="session.CurrentService" render="Label" position="595,193" size="116,18" zPosition="1" font="Regular;18" halign="left" foregroundColor="#999999" transparent="1" >
877 <convert type="ServicePosition">Length,ShowHours</convert>
879 <widget source="session.CurrentService" render="Label" position="406,220" size="198,20" zPosition="1" font="Regular;20" halign="left" foregroundColor="#fcc000" transparent="1" >
880 <convert type="ServicePosition">Position,ShowHours</convert>
882 <widget name="shuffle" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_shuf.png" position="320,203" size="53,34" transparent="1" alphatest="on"/>
883 <widget name="repeat" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/placeholder1.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_rep.png" position="320,167" size="53,34" transparent="1" alphatest="on"/>
884 <widget name="dvrStatus" pixmaps="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pl.png,/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/dvr_pau.png" position="405,155" size="160,39" transparent="1" alphatest="on"/>
887 def __init__(self, session, songlist, index, idreammode, currentservice, servicelist):
888 self.session = session
889 Screen.__init__(self, session)
890 InfoBarNotifications.__init__(self)
891 InfoBarBase.__init__(self)
892 self["actions"] = ActionMap(["WizardActions", "MediaPlayerActions", "EPGSelectActions", "MediaPlayerSeekActions", "ColorActions"],
894 "back": self.closePlayer,
895 "pause": self.pauseEntry,
896 "stop": self.stopEntry,
897 "right": self.playNext,
898 "left": self.playPrevious,
899 "up": self.showPlaylist,
900 "down" : self.showPlaylist,
901 "prevBouquet": self.shuffleList,
902 "nextBouquet": self.repeatSong,
903 "info" : self.showLyrics,
904 "yellow": self.pauseEntry,
906 "input_date_time": self.config,
910 self.onClose.append(self.__onClose)
911 self.session.nav.stopService()
912 self["PositionGauge"] = ServicePositionGauge(self.session.nav)
913 self["coverArt"] = MerlinMediaPixmap()
914 self["repeat"] = MultiPixmap()
915 self["shuffle"] = MultiPixmap()
916 self["dvrStatus"] = MultiPixmap()
917 self["title"] = Label()
918 self["album"] = Label()
919 self["artist"] = Label()
920 self["genre"] = Label()
921 self["nextTitle"] = Label()
922 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
924 iPlayableService.evUpdatedInfo: self.__evUpdatedInfo,
925 iPlayableService.evUser+10: self.__evAudioDecodeError,
926 iPlayableService.evUser+12: self.__evPluginError,
927 iPlayableService.evUser+13: self.embeddedCoverArt,
928 iPlayableService.evStart: self.__serviceStarted,
931 InfoBarSeek.__init__(self, actionmap = "MediaPlayerSeekActions")
932 self.songList = songlist
933 self.origSongList = songlist[:]
934 self.currentIndex = index
937 self.currentFilename = ""
938 self.currentGoogleCoverFile = ""
939 self.googleDownloadDir = os_path.join(config.plugins.merlinmusicplayer.googleimagepath.value, "downloaded_covers/" )
940 if not os_path.exists(self.googleDownloadDir):
942 os_mkdir(self.googleDownloadDir)
944 self.googleDownloadDir = "/tmp/"
947 self.onShown.append(self.__onShown)
949 self.currentTitle = ""
951 self.screenSaverTimer = eTimer()
952 self.screenSaverTimer.timeout.get().append(self.screenSaverTimerTimeout)
953 self.screenSaverScreen = None
955 self.iDreamMode = idreammode
956 self.currentService = currentservice
957 self.serviceList = servicelist
959 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
961 def sleepTimerEntryOnStateChange(self, timer):
962 if timer.state == TimerEntry.StateEnded:
965 def embeddedCoverArt(self):
966 self["coverArt"].embeddedCoverArt()
967 if self.screenSaverScreen:
968 self.screenSaverScreen.updateCover(modus = 2)
970 def screenSaverTimerTimeout(self):
971 if config.plugins.merlinmusicplayer.usescreensaver.value:
972 if self.screenSaverTimer.isActive():
973 self.screenSaverTimer.stop()
974 if not self.screenSaverScreen:
975 self.screenSaverScreen = self.session.instantiateDialog(MerlinMusicPlayerScreenSaver)
976 self.session.execDialog(self.screenSaverScreen)
977 self.screenSaverScreen.updateLCD(self.currentTitle,1)
978 self.screenSaverScreen.updateLCD(self.nextTitle,4)
979 album = self["album"].getText()
981 text = "%s - %s" % (self["title"].getText(), album)
983 text = self["title"].getText()
984 self.screenSaverScreen.updateDisplayText(text)
985 self.screenSaverScreen.updateCover(self["coverArt"].coverArtFileName, modus = 0)
987 def resetScreenSaverTimer(self):
988 if config.plugins.merlinmusicplayer.usescreensaver.value and config.plugins.merlinmusicplayer.screensaverwait.value != 0:
989 if self.screenSaverTimer.isActive():
990 self.screenSaverTimer.stop()
991 self.screenSaverTimer.start(config.plugins.merlinmusicplayer.screensaverwait.value * 60000)
996 self["coverArt"].onShow()
997 self.playSong(self.songList[self.currentIndex][0].filename)
999 self.summaries.setText(self.currentTitle,1)
1000 self.summaries.setText(self.nextTitle,4)
1001 if self.screenSaverScreen:
1002 self.screenSaverScreen.doClose()
1003 self.screenSaverScreen = None
1004 self.resetScreenSaverTimer()
1006 def __onClose(self):
1007 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
1008 del self["coverArt"].picload
1012 if self.screenSaverTimer.isActive():
1013 self.screenSaverTimer.stop()
1014 self.session.openWithCallback(self.setupFinished, MerlinMusicPlayerSetup, False)
1017 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1018 if self.screenSaverTimer.isActive():
1019 self.screenSaverTimer.stop()
1020 if self.screenSaverScreen:
1021 self.screenSaverScreen.doClose()
1022 self.screenSaverScreen = None
1023 self.screenSaverScreen = self.session.instantiateDialog(MerlinMusicPlayerTV, self.currentService, self.serviceList)
1024 self.session.execDialog(self.screenSaverScreen)
1025 self.screenSaverScreen.updateLCD(self.currentTitle,1)
1026 self.screenSaverScreen.updateLCD(self.nextTitle,4)
1027 album = self["album"].getText()
1029 text = "%s - %s" % (self["title"].getText(), album)
1031 text = self["title"].getText()
1032 self.screenSaverScreen.updateDisplayText(text)
1033 self.screenSaverScreen.updateCover(self["coverArt"].coverArtFileName, modus = 0)
1035 def setupFinished(self, result):
1037 self.googleDownloadDir = os_path.join(config.plugins.merlinmusicplayer.googleimagepath.value, "downloaded_covers/" )
1038 if not os_path.exists(self.googleDownloadDir):
1040 os_mkdir(self.googleDownloadDir)
1042 self.googleDownloadDir = "/tmp/"
1043 self.resetScreenSaverTimer()
1045 def closePlayer(self):
1046 if config.plugins.merlinmusicplayer.startlastsonglist.value:
1047 config.plugins.merlinmusicplayer.lastsonglistindex.value = self.currentIndex
1048 config.plugins.merlinmusicplayer.lastsonglistindex.save()
1049 connection = OpenDatabase()
1050 if connection is not None:
1051 connection.text_factory = str
1052 cursor = connection.cursor()
1053 cursor.execute("Delete from CurrentSongList;")
1054 for song in self.origSongList:
1055 cursor.execute("INSERT INTO CurrentSongList (song_id, filename,title,artist,album,genre, bitrate, length, track, date, PTS) VALUES(?,?,?,?,?,?,?,?,?,?,?);" , (song[0].songID, song[0].filename,song[0].title,song[0].artist,song[0].album,song[0].genre, song[0].bitrate, song[0].length, song[0].track, song[0].date, song[0].PTS))
1059 if self.screenSaverTimer.isActive():
1060 self.screenSaverTimer.stop()
1063 def playSong(self, filename):
1064 self.session.nav.stopService()
1066 self.currentFilename = filename
1067 if not config.plugins.merlinmusicplayer.hardwaredecoder.value and self.currentFilename.lower().endswith(".mp3") and self.songList[self.currentIndex][0].PTS is None:
1068 sref = eServiceReference(ENIGMA_MERLINPLAYER_ID, 0, self.currentFilename) # play mp3 file with merlinmp3player lib
1069 self.session.nav.playService(sref)
1071 self.updateMusicInformation( self.songList[self.currentIndex][0].artist, self.songList[self.currentIndex][0].title,
1072 self.songList[self.currentIndex][0].album, self.songList[self.currentIndex][0].genre, self.songList[self.currentIndex][0].date, clear = True )
1074 path,filename = os_path.split(self.currentFilename)
1075 audio, isAudio, title, genre,artist,album,tracknr,track,date,length,bitrate = getID3Tags(path,filename)
1078 year = "(%s)" % str(date)
1081 self.updateMusicInformation( artist, title, album, genre, year, clear = True )
1083 self.updateMusicInformation( title = title, clear = True)
1086 sref = eServiceReference(4097, 0, self.currentFilename)
1087 self.session.nav.playService(sref)
1088 if self.songList[self.currentIndex][0].PTS is not None:
1089 service = self.session.nav.getCurrentService()
1091 self.seek = service.seek()
1092 self.updateMusicInformationCUE()
1093 self.ptsTimer = eTimer()
1094 self.ptsTimer.callback.append(self.ptsTimerCallback)
1095 self.ptsTimer.start(1000)
1096 self["nextTitle"].setText(self.getNextTitle())
1098 def ptsTimerCallback(self):
1100 pts = self.seek.getPlayPosition()
1103 for songs in self.songList:
1104 if pts[1] > songs[0].PTS:
1105 currentIndex = index
1109 if currentIndex != self.currentIndex:
1110 self.currentIndex = currentIndex
1111 self.updateMusicInformationCUE()
1112 self.ptsTimer.start(1000)
1114 def updateMusicInformationCUE(self):
1115 self.updateSingleMusicInformation("artist", self.songList[self.currentIndex][0].artist, True)
1116 self.updateSingleMusicInformation("title", self.songList[self.currentIndex][0].title, True)
1117 self.updateSingleMusicInformation("album", self.songList[self.currentIndex][0].album, True)
1118 self.summaries.setText(self.songList[self.currentIndex][0].title,1)
1119 if self.screenSaverScreen:
1120 self.screenSaverScreen.updateLCD(self.songList[self.currentIndex][0].title,1)
1121 if self.songList[self.currentIndex][0].album:
1122 self.screenSaverScreen.updateDisplayText("%s - %s" % (self.songList[self.currentIndex][0].title,self.songList[self.currentIndex][0].album))
1124 self.screenSaverScreen.updateDisplayText(self.songList[self.currentIndex][0].title)
1125 self.updateCover(self.songList[self.currentIndex][0].artist, self.songList[self.currentIndex][0].album)
1126 self.currentTitle = self.songList[self.currentIndex][0].title
1127 self["nextTitle"].setText(self.getNextTitle())
1129 def __serviceStarted(self):
1130 self["dvrStatus"].setPixmapNum(0)
1132 def __evUpdatedInfo(self):
1133 currPlay = self.session.nav.getCurrentService()
1134 if currPlay is not None:
1135 sTitle = currPlay.info().getInfoString(iServiceInformation.sTagTitle)
1136 sAlbum = currPlay.info().getInfoString(iServiceInformation.sTagAlbum)
1137 sArtist = currPlay.info().getInfoString(iServiceInformation.sTagArtist)
1138 sGenre = currPlay.info().getInfoString(iServiceInformation.sTagGenre)
1139 sYear = currPlay.info().getInfoString(iServiceInformation.sTagDate)
1141 sYear = "(%s)" % sYear
1143 sTitle = os_path.splitext(os_path.basename(self.currentFilename))[0]
1145 if self.songList[self.currentIndex][0].PTS is None:
1146 self.updateMusicInformation( sArtist, sTitle, sAlbum, sGenre, sYear, clear = True )
1148 self.updateSingleMusicInformation("genre", sGenre, True)
1150 self.updateMusicInformation()
1152 def updateMusicInformation(self, artist = "", title = "", album = "", genre = "", year = "", clear = False):
1154 album = "%s %s" % (album, year)
1155 self.updateSingleMusicInformation("artist", artist, clear)
1156 self.updateSingleMusicInformation("title", title, clear)
1157 self.updateSingleMusicInformation("album", album, clear)
1158 self.updateSingleMusicInformation("genre", genre, clear)
1159 self.currentTitle = title
1160 if not self.iDreamMode and self.songList[self.currentIndex][0].PTS is None:
1162 self.songList[self.currentIndex][0].title = title
1163 self.songList[self.currentIndex][0].artist = artist
1164 self.summaries.setText(title,1)
1165 if self.screenSaverScreen:
1166 self.screenSaverScreen.updateLCD(title,1)
1168 self.screenSaverScreen.updateDisplayText("%s - %s" % (title,album))
1170 self.screenSaverScreen.updateDisplayText(title)
1171 self.updateCover(artist, album)
1173 def updateCover(self, artist, album):
1177 if self.currentFilename.lower().endswith(".mp3"):
1179 audio = ID3(self.currentFilename)
1181 except: audio = None
1182 elif self.currentFilename.lower().endswith(".flac"):
1184 audio = FLAC(self.currentFilename)
1186 except: audio = None
1187 elif self.currentFilename.lower().endswith(".m4a"):
1189 audio = MP4(self.currentFilename)
1191 except: audio = None
1192 elif self.currentFilename.lower().endswith(".ogg"):
1194 audio = OggVorbis(self.currentFilename)
1196 except: audio = None
1199 apicframes = audio.getall("APIC")
1200 if len(apicframes) >= 1:
1202 if not config.plugins.merlinmusicplayer.hardwaredecoder.value:
1203 coverArtFile = file("/tmp/.id3coverart", 'wb')
1204 coverArtFile.write(apicframes[0].data)
1205 coverArtFile.close()
1206 self["coverArt"].embeddedCoverArt()
1207 if self.screenSaverScreen:
1208 self.screenSaverScreen.updateCover(modus = 2)
1209 elif audiotype == 2:
1210 if len(audio.pictures) >= 1:
1212 elif audiotype == 3:
1213 if 'covr' in audio.tags:
1215 elif audiotype == 4:
1216 if 'METADATA_BLOCK_PICTURE' in audio.tags:
1220 if not self["coverArt"].updateCoverArt(self.currentFilename):
1221 if config.plugins.merlinmusicplayer.usegoogleimage.value:
1222 self.getGoogleCover(artist, album)
1224 self["coverArt"].showDefaultCover()
1225 if self.screenSaverScreen:
1226 self.screenSaverScreen.updateCover(modus = 1)
1228 if self.screenSaverScreen:
1229 self.screenSaverScreen.updateCover(filename = self.currentFilename, modus = 3)
1230 self.currentGoogleCoverFile = ""
1232 self.currentGoogleCoverFile = ""
1234 def updateSingleMusicInformation(self, name, info, clear):
1235 if info != "" or clear:
1236 if self[name].getText() != info:
1237 self[name].setText(info)
1239 def getGoogleCover(self, artist,album):
1240 if artist != "" and album != "":
1241 url = "http://images.google.de/images?q=%s+%s&btnG=Bilder-Suche" % (quote(album),quote(artist))
1242 sendUrlCommand(url, None,10).addCallback(self.googleImageCallback).addErrback(self.coverDownloadFailed)
1244 self["coverArt"].showDefaultCover()
1246 def googleImageCallback(self, result):
1247 foundPos = result.find("imgres?imgurl=")
1248 foundPos2 = result.find("&imgrefurl=")
1249 if foundPos != -1 and foundPos2 != -1:
1250 url = result[foundPos+14:foundPos2]
1251 parts = url.split("/")
1252 filename = parts[-1]
1253 if filename != self.currentGoogleCoverFile:
1254 self.currentGoogleCoverFile = filename
1255 filename = self.googleDownloadDir + parts[-1]
1256 if os_path.exists(filename):
1257 print "[MerlinMusicPlayer] using cover from %s " % filename
1258 self["coverArt"].showCoverFromFile(filename)
1259 if self.screenSaverScreen:
1260 self.screenSaverScreen.updateCover(filename = filename, modus = 4)
1262 print "[MerlinMusicPlayer] downloading cover from %s " % url
1263 downloadPage(url , self.googleDownloadDir + parts[-1]).addCallback(boundFunction(self.coverDownloadFinished, filename)).addErrback(self.coverDownloadFailed)
1265 def coverDownloadFailed(self,result):
1266 print "[MerlinMusicPlayer] cover download failed: %s " % result
1267 self["coverArt"].showDefaultCover()
1268 if self.screenSaverScreen:
1269 self.screenSaverScreen.updateCover(modus = 1)
1271 def coverDownloadFinished(self,filename, result):
1272 print "[MerlinMusicPlayer] cover download finished"
1273 self["coverArt"].showCoverFromFile(filename)
1274 if self.screenSaverScreen:
1275 self.screenSaverScreen.updateCover(filename = filename, modus = 4)
1277 def __evAudioDecodeError(self):
1278 currPlay = self.session.nav.getCurrentService()
1279 sAudioType = currPlay.info().getInfoString(iServiceInformation.sUser+10)
1280 print "[MerlinMusicPlayer] audio-codec %s can't be decoded by hardware" % (sAudioType)
1281 self.session.open(MessageBox, _("This Dreambox can't decode %s streams!") % sAudioType, type = MessageBox.TYPE_INFO,timeout = 20 )
1283 def __evPluginError(self):
1284 currPlay = self.session.nav.getCurrentService()
1285 message = currPlay.info().getInfoString(iServiceInformation.sUser+12)
1286 print "[MerlinMusicPlayer]" , message
1287 self.session.open(MessageBox, message, type = MessageBox.TYPE_INFO,timeout = 20 )
1289 def doEofInternal(self, playing):
1293 def checkSkipShowHideLock(self):
1294 self.updatedSeekState()
1296 def updatedSeekState(self):
1297 if self.seekstate == self.SEEK_STATE_PAUSE:
1298 self["dvrStatus"].setPixmapNum(1)
1299 elif self.seekstate == self.SEEK_STATE_PLAY:
1300 self["dvrStatus"].setPixmapNum(0)
1302 def pauseEntry(self):
1304 self.resetScreenSaverTimer()
1307 #play the current song from beginning again
1308 if self.songList[self.currentIndex][0].PTS is None:
1309 self.playSong(self.songList[self.currentIndex][0].filename)
1312 self.seek.seekTo(self.songList[self.currentIndex][0].PTS)
1313 self.updatedSeekState()
1314 self.resetScreenSaverTimer()
1316 def unPauseService(self):
1317 self.setSeekState(self.SEEK_STATE_PLAY)
1319 def stopEntry(self):
1321 self.session.nav.stopService()
1322 self.origSongList = []
1324 if config.plugins.merlinmusicplayer.startlastsonglist.value:
1325 config.plugins.merlinmusicplayer.lastsonglistindex.value = -1
1326 config.plugins.merlinmusicplayer.lastsonglistindex.save()
1327 connection = OpenDatabase()
1328 if connection is not None:
1329 connection.text_factory = str
1330 cursor = connection.cursor()
1331 cursor.execute("Delete from CurrentSongList;")
1335 self.resetScreenSaverTimer()
1340 if self.currentIndex +1 > len(self.songList) -1:
1341 self.currentIndex = 0
1343 self.currentIndex += 1
1344 if self.songList[self.currentIndex][0].PTS is None:
1345 self.playSong(self.songList[self.currentIndex][0].filename)
1348 if not self.screenSaverScreen:
1349 self.resetScreenSaverTimer()
1351 def playPrevious(self):
1353 if self.currentIndex - 1 < 0:
1354 self.currentIndex = len(self.songList) - 1
1356 self.currentIndex -= 1
1358 if self.songList[self.currentIndex][0].PTS is None:
1359 self.playSong(self.songList[self.currentIndex][0].filename)
1362 self.resetScreenSaverTimer()
1364 def getNextTitle(self):
1366 index = self.currentIndex
1368 if self.currentIndex + 1 > len(self.songList) -1:
1371 index = self.currentIndex + 1
1372 if self.iDreamMode or self.songList[index][0].PTS is not None:
1373 text = "%s - %s" % (self.songList[index][0].title, self.songList[index][0].artist)
1375 if self.songList[index][0].filename.lower().startswith("http://"):
1376 text = self.songList[index][0].filename
1378 path,filename = os_path.split(self.songList[index][0].filename)
1379 audio, isAudio, title, genre,artist,album,tracknr,track,date,length,bitrate = getID3Tags(path,filename)
1382 text = "%s - %s" % (title, artist)
1388 self.nextTitle = text
1389 self.summaries.setText(text,4)
1390 if self.screenSaverScreen:
1391 self.screenSaverScreen.updateLCD(text,4)
1394 def shuffleList(self):
1395 if self.songList[self.currentIndex][0].PTS is None: # not implemented for cue files yet
1396 self.shuffle = not self.shuffle
1398 self["shuffle"].setPixmapNum(1)
1399 shuffle(self.songList)
1401 self.songList = self.origSongList[:]
1402 self["shuffle"].setPixmapNum(0)
1404 for x in self.songList:
1405 if x[0].filename == self.currentFilename:
1406 self.currentIndex = index
1409 self["nextTitle"].setText(self.getNextTitle())
1411 self.session.open(MessageBox, _("Shuffle is not available yet with cue-files!"), type = MessageBox.TYPE_INFO,timeout = 20 )
1412 self.resetScreenSaverTimer()
1414 def repeatSong(self):
1415 if self.songList[self.currentIndex][0].PTS is None: # not implemented for cue files yet
1416 self.repeat = not self.repeat
1418 self["repeat"].setPixmapNum(1)
1420 self["repeat"].setPixmapNum(0)
1421 self["nextTitle"].setText(self.getNextTitle())
1423 self.session.open(MessageBox, _("Repeat is not available yet with cue-files!"), type = MessageBox.TYPE_INFO,timeout = 20 )
1424 self.resetScreenSaverTimer()
1426 def showPlaylist(self):
1427 if self.screenSaverTimer.isActive():
1428 self.screenSaverTimer.stop()
1429 self.session.openWithCallback(self.showPlaylistCallback, MerlinMusicPlayerSongList, self.songList, self.currentIndex, self.iDreamMode)
1431 def showPlaylistCallback(self, index):
1433 self.currentIndex = index
1435 if self.songList[self.currentIndex][0].PTS is None:
1436 self.playSong(self.songList[self.currentIndex][0].filename)
1440 self.resetScreenSaverTimer()
1442 def playCUETrack(self):
1443 if self.ptsTimer.isActive():
1444 self.ptsTimer.stop()
1446 self.seek.seekTo(self.songList[self.currentIndex][0].PTS)
1447 self.updatedSeekState()
1448 self.updateMusicInformationCUE()
1449 self.ptsTimer.start(1000)
1451 def showLyrics(self):
1452 if self.screenSaverTimer.isActive():
1453 self.screenSaverTimer.stop()
1454 self.session.openWithCallback(self.resetScreenSaverTimer, MerlinMusicPlayerLyrics, self.songList[self.currentIndex][0])
1456 def createSummary(self):
1457 return MerlinMusicPlayerLCDScreen
1459 class MerlinMusicPlayerLyrics(Screen):
1461 sz_w = getDesktop(0).size().width()
1464 <screen name="MerlinMusicPlayerLyrics" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="Merlin Music Player Lyrics">
1465 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1466 <eLabel backgroundColor="#999999" position="178,112" size="924,2" zPosition="1"/>
1467 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="178,104" size="250,20" text="MERLIN MUSIC PLAYER" valign="center" zPosition="2"/>
1468 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="852,104" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1469 <widget name="headertext" position="178,145" zPosition="1" size="900,23" font="Regular;20" transparent="1" foregroundColor="#fcc000" backgroundColor="#00000000"/>
1470 <widget name="resulttext" position="178,172" zPosition="1" size="900,20" font="Regular;16" transparent="1" backgroundColor="#00000000"/>
1471 <widget name="lyric_text" position="178,222" zPosition="2" size="940,350" font="Regular;18" transparent="0" backgroundColor="#00000000"/>
1475 <screen name="MerlinMusicPlayerLyrics" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="Merlin Music Player Lyrics">
1476 <eLabel backgroundColor="#999999" position="50,40" size="924,2" zPosition="1"/>
1477 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,32" size="250,20" text="MERLIN MUSIC PLAYER" valign="center" zPosition="2"/>
1478 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="724,32" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1479 <widget name="headertext" position="50,73" zPosition="1" size="900,23" font="Regular;20" transparent="1" foregroundColor="#fcc000" backgroundColor="#00000000"/>
1480 <widget name="resulttext" position="50,100" zPosition="1" size="900,20" font="Regular;16" transparent="1" backgroundColor="#00000000"/>
1481 <widget name="lyric_text" position="50,150" zPosition="2" size="940,350" font="Regular;18" transparent="0" backgroundColor="#00000000"/>
1485 <screen name="MerlinMusicPlayerLyrics" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="Merlin Music Player Lyrics">
1486 <eLabel backgroundColor="#999999" position="50,50" size="620,2" zPosition="1"/>
1487 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,40" size="250,20" text="MERLIN MUSIC PLAYER" valign="center" zPosition="2"/>
1488 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="420,40" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1489 <widget name="headertext" position="50,73" zPosition="1" size="620,23" font="Regular;20" transparent="1" foregroundColor="#fcc000" backgroundColor="#00000000"/>
1490 <widget name="resulttext" position="50,100" zPosition="1" size="620,20" font="Regular;16" transparent="1" backgroundColor="#00000000"/>
1491 <widget name="lyric_text" position="50,150" zPosition="2" size="620,350" font="Regular;18" transparent="0" backgroundColor="#00000000"/>
1495 def __init__(self, session, currentsong):
1496 self.session = session
1497 Screen.__init__(self, session)
1498 self["headertext"] = Label(_("Merlin Music Player Lyrics"))
1499 # leoslyrics does not work anymore
1500 # self["resulttext"] = Label(_("Getting lyrics from api.leoslyrics.com..."))
1501 self["resulttext"] = Label()
1502 self["actions"] = ActionMap(["WizardActions", "DirectionActions"],
1505 "upUp": self.pageUp,
1506 "leftUp": self.pageUp,
1507 "downUp": self.pageDown,
1508 "rightUp": self.pageDown,
1510 self["lyric_text"] = ScrollLabel()
1511 self.currentSong = currentsong
1512 self.onLayoutFinish.append(self.startRun)
1515 # get lyric-text from id3 tag
1517 audio = ID3(self.currentSong.filename)
1520 text = getEncodedString(self.getLyricsFromID3Tag(audio)).replace("\r\n","\n")
1521 text = text.replace("\r","\n")
1522 self["lyric_text"].setText(text)
1524 def getLyricsFromID3Tag(self,tag):
1526 for frame in tag.values():
1527 if frame.FrameID == "USLT":
1529 url = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=%s&song=%s" % (quote(self.currentSong.artist), quote(self.currentSong.title))
1530 sendUrlCommand(url, None,10).addCallback(self.gotLyrics).addErrback(self.urlError)
1531 return "No lyrics found in id3-tag, trying api.chartlyrics.com..."
1533 def urlError(self, error = None):
1534 if error is not None:
1535 self["resulttext"].setText(str(error.getErrorMessage()))
1536 self["lyric_text"].setText("")
1538 def gotLyrics(self, xmlstring):
1539 root = cet_fromstring(xmlstring)
1541 lyrictext = root.findtext("{http://api.chartlyrics.com/}Lyric").encode("utf-8", 'ignore')
1542 self["lyric_text"].setText(lyrictext)
1543 title = root.findtext("{http://api.chartlyrics.com/}LyricSong").encode("utf-8", 'ignore')
1544 artist = root.findtext("{http://api.chartlyrics.com/}LyricArtist").encode("utf-8", 'ignore')
1545 result = _("Response -> lyrics for: %s (%s)") % (title,artist)
1546 self["resulttext"].setText(result)
1548 self["resulttext"].setText(_("No lyrics found"))
1549 self["lyric_text"].setText("")
1552 self["lyric_text"].pageUp()
1555 self["lyric_text"].pageDown()
1557 class MerlinMusicPlayerSongList(Screen):
1559 sz_w = getDesktop(0).size().width()
1562 <screen name="MerlinMusicPlayerSongList" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1563 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1564 <eLabel backgroundColor="#999999" position="178,112" size="924,2" zPosition="1"/>
1565 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="178,104" size="250,20" text="MERLIN MUSIC PLAYER" valign="center" zPosition="2"/>
1566 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="852,104" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1567 <widget name="headertext" position="178,145" zPosition="1" size="900,23" font="Regular;20" transparent="1" foregroundColor="#fcc000" backgroundColor="#00000000"/>
1568 <widget name="list" position="178,182" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0" backgroundColor="#00000000"/>
1572 <screen name="MerlinMusicPlayerSongList" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1573 <eLabel backgroundColor="#999999" position="50,40" size="924,2" zPosition="1"/>
1574 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,32" size="250,20" text="MERLIN MUSIC PLAYER" valign="center" zPosition="2"/>
1575 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="724,32" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1576 <widget name="headertext" position="50,73" zPosition="1" size="900,23" font="Regular;20" transparent="1" foregroundColor="#fcc000" backgroundColor="#00000000"/>
1577 <widget name="list" position="50,110" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0" backgroundColor="#00000000"/>
1581 <screen name="MerlinMusicPlayerSongList" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="Songlist">
1582 <eLabel backgroundColor="#999999" position="50,50" size="620,2" zPosition="1"/>
1583 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="50,40" size="250,20" text="MERLIN MUSIC PLAYER" valign="center" zPosition="2"/>
1584 <eLabel backgroundColor="#999999" font="Regular;16" foregroundColor="#0f0f0f" halign="center" position="420,40" size="250,20" text="WWW.DREAMBOX-TOOLS.INFO" valign="center" zPosition="2"/>
1585 <widget name="headertext" position="50,73" zPosition="1" size="620,23" font="Regular;20" transparent="1" foregroundColor="#fcc000" backgroundColor="#00000000"/>
1586 <widget name="list" position="50,110" zPosition="2" size="620,350" scrollbarMode="showOnDemand" transparent="0" backgroundColor="#00000000"/>
1590 def __init__(self, session, songlist, index, idreammode):
1591 self.session = session
1592 Screen.__init__(self, session)
1593 self["headertext"] = Label(_("Merlin Music Player Songlist"))
1594 self["list"] = iDreamList()
1595 self["list"].connectSelChanged(self.lcdUpdate)
1596 self["actions"] = ActionMap(["WizardActions"],
1599 "back": self.closing,
1601 self.songList = songlist
1603 self.iDreamMode = idreammode
1604 self.onLayoutFinish.append(self.startRun)
1605 self.onShown.append(self.lcdUpdate)
1609 self["list"].setMode(10) # songlist
1610 self["list"].setList(self.songList)
1611 self["list"].moveToIndex(self.index)
1614 self.close(self["list"].getCurrentIndex())
1619 def lcdUpdate(self):
1621 index = self["list"].getCurrentIndex()
1622 songlist = self["list"].getList()
1623 mode = self.iDreamMode or songlist[index][0].PTS
1625 self.summaries.setText(songlist[index][0].title,1)
1627 self.summaries.setText(songlist[index][0].text,1)
1628 count = self["list"].getItemCount()
1634 self.summaries.setText(songlist[index][0].title,3)
1636 self.summaries.setText(songlist[index][0].text,3)
1638 index = self["list"].getCurrentIndex() + 1
1642 self.summaries.setText(songlist[index][0].title,4)
1644 self.summaries.setText(songlist[index][0].text,4)
1647 def createSummary(self):
1648 return MerlinMusicPlayerLCDScreenText
1650 class iDreamMerlin(Screen):
1653 sz_w = getDesktop(0).size().width()
1656 <screen name="iDreamMerlin" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1657 <ePixmap alphatest="on" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/MerlinMusicPlayer/images/mmpborderHD.png" position="128,72" size="1024,576"/>
1658 <ePixmap position="178,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1659 <ePixmap position="328,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1660 <ePixmap position="478,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1661 <ePixmap position="628,102" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1662 <widget render="Label" source="key_red" position="178,102" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1663 <widget render="Label" source="key_green" position="328,102" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1664 <widget render="Label" source="key_yellow" position="478,102" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1665 <widget render="Label" source="key_blue" position="628,102" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1666 <widget name="headertext" position="178,149" zPosition="1" size="900,23" font="Regular;20" transparent="1" foregroundColor="#fcc000" backgroundColor="#00000000"/>
1667 <widget name="list" position="178,182" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0" backgroundColor="#00000000"/>
1671 <screen name="iDreamMerlin" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1672 <ePixmap position="50,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1673 <ePixmap position="200,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1674 <ePixmap position="350,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1675 <ePixmap position="500,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1676 <widget render="Label" source="key_red" position="50,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1677 <widget render="Label" source="key_green" position="200,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1678 <widget render="Label" source="key_yellow" position="350,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1679 <widget render="Label" source="key_blue" position="500,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1680 <widget name="headertext" position="50,77" zPosition="1" size="900,23" font="Regular;20" transparent="1" foregroundColor="#fcc000" backgroundColor="#00000000"/>
1681 <widget name="list" position="50,110" zPosition="2" size="940,350" scrollbarMode="showOnDemand" transparent="0" backgroundColor="#00000000"/>
1685 <screen name="iDreamMerlin" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="iDream">
1686 <ePixmap position="50,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1687 <ePixmap position="200,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1688 <ePixmap position="350,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1689 <ePixmap position="500,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1690 <widget render="Label" source="key_red" position="50,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1691 <widget render="Label" source="key_green" position="200,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1692 <widget render="Label" source="key_yellow" position="350,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1693 <widget render="Label" source="key_blue" position="500,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1694 <widget name="headertext" position="50,77" zPosition="1" size="620,23" font="Regular;20" transparent="1" foregroundColor="#fcc000" backgroundColor="#00000000"/>
1695 <widget name="list" position="50,110" zPosition="2" size="620,350" scrollbarMode="showOnDemand" transparent="0" backgroundColor="#00000000"/>
1699 def __init__(self, session, servicelist):
1700 self.session = session
1701 Screen.__init__(self, session)
1702 self["list"] = iDreamList()
1703 self["list"].connectSelChanged(self.lcdUpdate)
1706 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "ColorActions", "EPGSelectActions"],
1709 "back": self.closing,
1710 "red": self.red_pressed,
1711 "green": self.green_pressed,
1712 "yellow": self.yellow_pressed,
1713 "blue": self.blue_pressed,
1714 "input_date_time": self.menu_pressed,
1715 "info" : self.info_pressed,
1718 self["actions2"] = NumberActionMap(["InputActions"],
1720 "0": self.keyNumber_pressed,
1723 self.onLayoutFinish.append(self.startRun)
1724 self.onShown.append(self.lcdUpdate)
1725 self.onClose.append(self.__onClose)
1727 self.serviceList = servicelist
1728 self.currentService = self.session.nav.getCurrentlyPlayingServiceReference()
1729 self.session.nav.stopService()
1732 self.mainMenuList = []
1734 self.LastMethod = None
1737 self["key_red"] = StaticText("")
1738 self["key_green"] = StaticText("")
1739 self["key_yellow"] = StaticText("")
1740 self["key_blue"] = StaticText("")
1741 self["headertext"] = Label(_("iDream Main Menu"))
1743 self.startMerlinPlayerScreenTimer = eTimer()
1744 self.startMerlinPlayerScreenTimer.timeout.get().append(self.info_pressed)
1746 self.session.nav.SleepTimer.on_state_change.append(self.sleepTimerEntryOnStateChange)
1748 def sleepTimerEntryOnStateChange(self, timer):
1749 if timer.state == TimerEntry.StateEnded:
1752 def getPlayList(self):
1753 connection = OpenDatabase()
1754 if connection is not None:
1755 connection.text_factory = str
1756 cursor = connection.cursor()
1758 cursor.execute("select playlist_id,playlist_text from playlists order by playlist_text;")
1760 playList.append((row[1], row[0]))
1767 def sqlCommand(self, sqlSatement):
1768 connection = OpenDatabase()
1769 if connection is not None:
1770 cursor = connection.cursor()
1771 cursor.execute(sqlSatement)
1776 def clearCache(self):
1777 for items in self.cacheList:
1780 items.headertext = ""
1782 def getCurrentSelection(self):
1784 try: sel = self["list"].l.getCurrentSelection()[0]
1788 def addListToPlaylistConfirmed(self, methodName, answer):
1790 playList = self.getPlayList()
1792 self.session.openWithCallback(methodName, ChoiceBox,list = playList)
1794 self.session.openWithCallback(self.createPlaylistConfirmed, MessageBox, _("There are no playlists defined.\nDo you want to create a new playlist?"))
1796 def menu_pressed(self):
1797 self.startMerlinPlayerScreenTimer.stop()
1798 options = [(_("Configuration"), self.config),(_("Search in iDream database"), self.searchInIDreamDatabase),]
1799 options.extend(((_("Scan path for music files and add them to database"), self.scanDir),))
1801 options.extend(((_("Create new playlist"), self.createPlaylist),))
1802 if self["list"].getDisplaySongMode():
1804 options.extend(((_("Delete song from current playlist"), self.deleteSongFromPlaylist),))
1806 options.extend(((_("Add selected song to a playlist"), self.addSongToPlaylist),))
1808 options.extend(((_("Add all songs from selected album to a playlist"), self.addAlbumToPlaylist),))
1809 elif self.mode == 19:
1810 options.extend(((_("Add all songs from selected artist to a playlist"), self.addArtistToPlaylist),))
1811 options.extend(((_("Delete song from database"), self.deleteSongFromDatabase),))
1812 options.extend(((_("Clear current songlist and play selected entry"), self.stopPlayingAndAppendFileToSongList),))
1813 options.extend(((_("Append file to current playing songlist"), self.appendFileToSongList),))
1814 if self.player is not None and self.player.songList:
1815 options.extend(((_("Insert file to current playing songlist and play next"), self.insertFileToSongList),))
1818 options.extend(((_("Delete selected playlist"), self.deletePlaylist),))
1819 elif self.mode == 4:
1820 options.extend(((_("Add all songs from selected artist to a playlist"), self.addArtistToPlaylist),))
1821 elif self.mode == 5 or self.mode == 7:
1822 options.extend(((_("Add all songs from selected album to a playlist"), self.addAlbumToPlaylist),))
1823 elif self.mode == 13:
1824 options.extend(((_("Add all songs from selected genre to a playlist"), self.addGenreToPlaylist),))
1825 self.session.openWithCallback(self.menuCallback, ChoiceBox,list = options)
1827 def menuCallback(self, ret):
1832 self.session.openWithCallback(self.pathSelected,SelectPath,"/media/")
1834 def pathSelected(self, res):
1836 self.session.openWithCallback(self.filesAdded, iDreamAddToDatabase,res)
1838 def filesAdded(self):
1839 if pathToDatabase.isRunning:
1847 def addGenreToPlaylist(self):
1848 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed,self.addGenreToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that genre to a playlist?"))
1850 def addGenreToPlaylistConfirmedCallback(self, ret):
1852 sel = self.getCurrentSelection()
1854 self.sqlCommand("INSERT INTO Playlist_Songs (playlist_id,song_id) select %d, song_id from songs where genre_id=%d order by album_id,tracknumber,title,filename;" % (ret[1],sel.genreID))
1857 def addArtistToPlaylist(self):
1858 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed, self.addArtistToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that artist to a playlist?"))
1860 def addArtistToPlaylistConfirmedCallback(self, ret):
1862 sel = self.getCurrentSelection()
1864 self.sqlCommand("INSERT INTO Playlist_Songs (playlist_id,song_id) select %d, song_id from songs where artist_id=%d order by album_id,tracknumber,title,filename;" % (ret[1],sel.artistID))
1867 def addAlbumToPlaylist(self):
1868 self.session.openWithCallback(boundFunction(self.addListToPlaylistConfirmed, self.addAlbumToPlaylistConfirmedCallback), MessageBox, _("Do you really want to add all songs from that album to a playlist?"))
1870 def addAlbumToPlaylistConfirmedCallback(self, ret):
1872 sel = self.getCurrentSelection()
1874 self.sqlCommand("INSERT INTO Playlist_Songs (playlist_id,song_id) select %d, song_id from songs where album_id=%d order by tracknumber,title,filename;" % (ret[1],sel.albumID))
1877 def deletePlaylist(self):
1878 self.session.openWithCallback(self.deletePlaylistConfirmed, MessageBox, _("Do you really want to delete the current playlist?"))
1880 def deletePlaylistConfirmed(self, answer):
1882 sel = self.getCurrentSelection()
1884 self.sqlCommand("delete from playlist_songs where playlist_id = %d" % (sel.playlistID))
1885 self.sqlCommand("delete from playlists where playlist_id = %d" % (sel.playlistID))
1886 self["list"].removeItem(self["list"].getCurrentIndex())
1890 def deleteSongFromPlaylist(self):
1891 self.session.openWithCallback(self.deleteSongFromPlaylistConfirmed, MessageBox, _("Do you really want to delete that song the current playlist?"))
1893 def deleteSongFromPlaylistConfirmed(self, answer):
1895 sel = self.getCurrentSelection()
1897 self.sqlCommand("delete from playlist_songs where song_id = %d" % (sel.songID))
1898 self["list"].removeItem(self["list"].getCurrentIndex())
1901 def deleteSongFromDatabase(self):
1902 self.session.openWithCallback(self.deleteSongFromDatabaseConfirmed, MessageBox, _("Do you really want to delete that song from the database?"))
1904 def deleteSongFromDatabaseConfirmed(self, answer):
1906 sel = self.getCurrentSelection()
1908 self.sqlCommand("delete from playlist_songs where song_id = %d" % (sel.songID))
1909 self.sqlCommand("delete from songs where song_id = %d" % (sel.songID))
1910 self["list"].removeItem(self["list"].getCurrentIndex())
1913 def addSongToPlaylist(self):
1914 playList = self.getPlayList()
1916 self.session.openWithCallback(self.addSongToPlaylistCallback, ChoiceBox,list = playList)
1918 self.session.openWithCallback(self.createPlaylistConfirmed, MessageBox, _("There are no playlists defined.\nDo you want to create a new playlist?"))
1920 def createPlaylistConfirmed(self, val):
1922 self.createPlaylist()
1924 def addSongToPlaylistCallback(self,ret):
1926 sel = self.getCurrentSelection()
1928 self.sqlCommand("INSERT INTO Playlist_Songs (playlist_id,song_id) VALUES(%d,%d);" % (ret[1],sel.songID))
1931 def createPlaylist(self):
1932 self.session.openWithCallback(self.createPlaylistFinished, VirtualKeyBoard, title = _("Enter name for playlist"))
1934 def createPlaylistFinished(self, text = None):
1936 self.sqlCommand('INSERT INTO Playlists (playlist_text) VALUES("%s");' % (text))
1940 def searchInIDreamDatabase(self):
1941 options = [(_("search for title"), 1),
1942 (_("search for artist"), 2),
1943 (_("search for album"), 3),
1944 (_("search in all of them"), 4),]
1945 self.session.openWithCallback(self.enterSearchText, ChoiceBox,list = options)
1947 def enterSearchText(self, ret):
1949 self.session.openWithCallback(boundFunction(self.enterSearchTextFinished,ret[1]), VirtualKeyBoard, title = _("Enter search-text"))
1951 def enterSearchTextFinished(self, searchType, searchText = None):
1953 search = "%" + searchText + "%"
1955 sql_where = "where title like '%s'" % search
1956 text = _('Search results for "%s" in all titles') % searchText
1957 elif searchType == 2:
1958 sql_where = "where artists.artist like '%s'" % search
1959 text = _('Search results for "%s" in all artists') % searchText
1960 elif searchType == 3:
1961 sql_where = "where album_text like '%s'" % search
1962 text = _('Search results for "%s" in all albums') % searchText
1964 sql_where = "where (title like '%s' or artists.artist like '%s' or album_text like '%s')" % (search,search,search)
1965 text = _('Search results for "%s" in title, artist or album') % searchText
1966 self.setButtons(red = True, yellow = True, blue = True)
1969 self["list"].setMode(self.mode)
1970 self.buildSearchSongList(sql_where, text, oldmode, True)
1973 def keyNumber_pressed(self, number):
1974 if number == 0 and self.mode != 0:
1975 self["list"].moveToIndex(0)
1979 sel = self.getCurrentSelection()
1983 self.green_pressed()
1985 self.mode = sel.mode
1986 self["list"].setMode(self.mode)
1987 if sel.navigator and len(self.cacheList) > 0:
1988 cache = self.cacheList.pop()
1990 cache = CacheList(cache = False, index = -1)
1992 self["headertext"].setText(cache.headertext)
1994 self["list"].setList(cache.listview)
1995 self.LastMethod = MethodArguments(method = cache.methodarguments.method, arguments = cache.methodarguments.arguments)
1997 cache.methodarguments.method(**cache.methodarguments.arguments)
1998 self["list"].moveToIndex(cache.index)
2001 if not sel.navigator:
2002 self.buildMainMenuList()
2003 elif self.mode == 1:
2004 self.setButtons(red = True)
2005 if not sel.navigator:
2006 self.buildPlaylistList(addToCache = True)
2007 elif self.mode == 2:
2008 self.setButtons(red = True, green = True, yellow = True, blue = True)
2009 if not sel.navigator:
2010 self.buildPlaylistSongList(playlistID = sel.playlistID, addToCache = True)
2011 elif self.mode == 4:
2012 self.setButtons(red = True)
2013 if not sel.navigator:
2014 self.buildArtistList(addToCache = True)
2015 elif self.mode == 5:
2016 self.setButtons(red = True)
2017 if not sel.navigator:
2018 self.buildArtistAlbumList(sel.artistID, addToCache = True)
2019 elif self.mode == 6:
2020 self.setButtons(red = True, green = True, yellow = True)
2021 if not sel.navigator:
2022 self.buildAlbumSongList(albumID = sel.albumID, mode = 5, addToCache = True)
2023 elif self.mode == 7:
2024 self.setButtons(red = True)
2025 if not sel.navigator:
2026 self.buildAlbumList(addToCache = True)
2027 elif self.mode == 8:
2028 self.setButtons(red = True, green = True, yellow = True)
2029 if not sel.navigator:
2030 self.buildAlbumSongList(albumID = sel.albumID, mode = 7, addToCache = True)
2031 elif self.mode == 10:
2032 self.setButtons(red = True, green = True, yellow = True, blue = True)
2033 if not sel.navigator:
2034 self.buildSongList(addToCache = True)
2035 elif self.mode == 13:
2036 self.setButtons(red = True)
2037 if not sel.navigator:
2038 self.buildGenreList(addToCache = True)
2039 elif self.mode == 14:
2040 self.setButtons(red = True, green = True, yellow = True, blue = True)
2041 if not sel.navigator:
2042 self.buildGenreSongList(genreID = sel.genreID, addToCache = True)
2043 elif self.mode == 18 or self.mode == 19:
2045 self.setButtons(red = True, green = True, yellow = True)
2047 self.setButtons(red = True, green = True, blue = True)
2048 if not sel.navigator:
2049 self.red_pressed() # back to main menu --> normally that can not be happened
2050 elif self.mode == 20:
2051 self.setButtons(red = True, green = True, yellow = True, blue = True)
2052 if not sel.navigator:
2053 self.red_pressed() # back to main menu --> normally that can not be happened
2055 def buildPlaylistList(self, addToCache):
2057 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2059 arguments["addToCache"] = False
2060 self.LastMethod = MethodArguments(method = self.buildPlaylistList, arguments = arguments)
2061 self["headertext"].setText(_("Playlists"))
2062 connection = OpenDatabase()
2063 if connection is not None:
2064 connection.text_factory = str
2065 cursor = connection.cursor()
2067 playlistList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2068 cursor.execute("select playlists.playlist_id, playlist_text, count(Playlist_Songs.playlist_id) from playlists left outer join Playlist_Songs on playlists.playlist_id = Playlist_Songs.playlist_id group by playlists.playlist_id order by playlists.playlist_text;")
2070 playlistList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 2, playlistID = row[0]),))
2073 self["list"].setList(playlistList)
2074 if len(playlistList) > 1:
2075 self["list"].moveToIndex(1)
2077 def buildPlaylistSongList(self, playlistID, addToCache):
2079 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2081 arguments["playlistID"] = playlistID
2082 arguments["addToCache"] = False
2083 self.LastMethod = MethodArguments(method = self.buildPlaylistSongList, arguments = arguments)
2084 connection = OpenDatabase()
2085 if connection is not None:
2086 connection.text_factory = str
2087 cursor = connection.cursor()
2088 playlistSongList = []
2089 playlistSongList.append((Item(text = _("[back]"), mode = 1, navigator = True),))
2090 cursor.execute("select songs.song_id, title, artists.artist, filename, songs.artist_id, bitrate, length, genre_text, track, date, album_text, songs.Album_id from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id inner join playlist_songs on songs.song_id = playlist_songs.song_id where playlist_songs.playlist_id = %d order by playlist_songs.id;" % (playlistID))
2092 playlistSongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], artistID = row[4], bitrate = row[5], length = row[6], genre = row[7], track = row[8], date = row[9], album = row[10], albumID = row[11], playlistID = playlistID),))
2093 cursor.execute("SELECT playlist_text from playlists where playlist_id = %d;" % playlistID)
2094 row = cursor.fetchone()
2095 self["headertext"].setText(_("Playlist (%s) -> Song List") % row[0])
2098 self["list"].setList(playlistSongList)
2099 if len(playlistSongList) > 1:
2100 self["list"].moveToIndex(1)
2102 def buildGenreList(self, addToCache):
2104 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2106 arguments["addToCache"] = False
2107 self.LastMethod = MethodArguments(method = self.buildGenreList, arguments = arguments)
2108 self["headertext"].setText(_("Genre List"))
2109 connection = OpenDatabase()
2110 if connection is not None:
2111 connection.text_factory = str
2112 cursor = connection.cursor()
2114 genreList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2115 cursor.execute("select Genre.genre_id,Genre.Genre_text, count(*) from songs inner join Genre on songs.genre_id = Genre.Genre_id group by songs.Genre_id order by Genre.Genre_text;")
2117 genreList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 14, genreID = row[0]),))
2120 self["list"].setList(genreList)
2121 if len(genreList) > 1:
2122 self["list"].moveToIndex(1)
2124 def buildGenreSongList(self, genreID, addToCache):
2126 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2128 arguments["genreID"] = genreID
2129 arguments["addToCache"] = False
2130 self.LastMethod = MethodArguments(method = self.buildGenreSongList, arguments = arguments)
2131 connection = OpenDatabase()
2132 if connection is not None:
2133 connection.text_factory = str
2134 cursor = connection.cursor()
2136 genreSongList.append((Item(text = _("[back]"), mode = 13, navigator = True),))
2137 cursor.execute("select song_id, title, artists.artist, filename, songs.artist_id, bitrate, length, genre_text, track, date, album_text, songs.Album_id from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id where songs.genre_id = %d order by title, filename;" % (genreID))
2139 genreSongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], artistID = row[4], bitrate = row[5], length = row[6], genre = row[7], track = row[8], date = row[9], album = row[10], albumID = row[11], genreID = genreID),))
2140 cursor.execute("SELECT genre_text from genre where genre_ID = %d;" % genreID)
2141 row = cursor.fetchone()
2142 self["headertext"].setText(_("Genre (%s) -> Song List") % row[0])
2145 self["list"].setList(genreSongList)
2146 if len(genreSongList) > 1:
2147 self["list"].moveToIndex(1)
2149 def setButtons(self, red = False, green = False, yellow = False, blue = False):
2151 self["key_red"].setText("Main Menu")
2153 self["key_red"].setText("")
2155 self["key_green"].setText("Play")
2157 self["key_green"].setText("")
2159 self["key_yellow"].setText("All Artists")
2161 self["key_yellow"].setText("")
2163 self["key_blue"].setText("Show Album")
2165 self["key_blue"].setText("")
2167 def info_pressed(self):
2168 self.startMerlinPlayerScreenTimer.stop()
2169 if self.player is not None:
2170 if self.player.songList:
2171 self.session.execDialog(self.player)
2173 def green_pressed(self):
2175 sel = self["list"].l.getCurrentSelection()[0]
2181 if self.player is not None:
2182 self.player.doClose()
2184 self.startMerlinPlayerScreenTimer.stop()
2185 self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,self["list"].getList()[1:], self["list"].getCurrentIndex() -1, True, self.currentService, self.serviceList)
2186 self.session.execDialog(self.player)
2188 def red_pressed(self):
2192 self["list"].setMode(self.mode)
2193 self.buildMainMenuList()
2195 def yellow_pressed(self):
2197 sel = self["list"].l.getCurrentSelection()[0]
2200 if sel.artistID != 0:
2203 self.setButtons(red = True, green = True, blue = True)
2204 self["list"].setMode(self.mode)
2205 self.buildArtistSongList(artistID = sel.artistID, mode = oldmode, addToCache = True)
2207 def blue_pressed(self):
2209 sel = self["list"].l.getCurrentSelection()[0]
2212 if sel.albumID != 0:
2213 self.setButtons(red = True, green = True, yellow = True)
2216 self["list"].setMode(self.mode)
2217 self.buildAlbumSongList(albumID = sel.albumID, mode = oldmode, addToCache = True)
2219 def buildSongList(self, addToCache):
2221 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2223 arguments["addToCache"] = False
2224 self.LastMethod = MethodArguments(method = self.buildSongList, arguments = arguments)
2225 self["headertext"].setText(_("All Songs"))
2226 connection = OpenDatabase()
2227 if connection is not None:
2228 connection.text_factory = str
2229 cursor = connection.cursor()
2231 SongList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2232 cursor.execute("select song_id, title, artists.artist, filename, songs.artist_id, bitrate, length, genre_text, track, date, album_text, songs.Album_id from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id order by title, filename;")
2234 SongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], artistID = row[4], bitrate = row[5], length = row[6], genre = row[7], track = row[8], date = row[9], album = row[10], albumID = row[11]),))
2237 self["list"].setList(SongList)
2238 if len(SongList) > 1:
2239 self["list"].moveToIndex(1)
2242 def buildSearchSongList(self, sql_where, headerText, mode, addToCache):
2244 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2246 arguments["sql_where"] = sql_where
2247 arguments["headerText"] = headerText
2248 arguments["mode"] = mode
2249 arguments["addToCache"] = False
2250 self.LastMethod = MethodArguments(method = self.buildSearchSongList, arguments = arguments)
2251 self["headertext"].setText(headerText)
2252 connection = OpenDatabase()
2253 if connection is not None:
2254 connection.text_factory = str
2255 cursor = connection.cursor()
2257 SongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2258 cursor.execute("select song_id, title, artists.artist, filename, songs.artist_id, bitrate, length, genre_text, track, date, album_text, songs.Album_id from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id %s order by title, filename;" % sql_where)
2260 SongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], artistID = row[4], bitrate = row[5], length = row[6], genre = row[7], track = row[8], date = row[9], album = row[10], albumID = row[11]),))
2263 self["list"].setList(SongList)
2264 if len(SongList) > 1:
2265 self["list"].moveToIndex(1)
2268 def buildArtistSongList(self, artistID, mode, addToCache):
2270 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2272 arguments["artistID"] = artistID
2273 arguments["mode"] = mode
2274 arguments["addToCache"] = False
2275 self.LastMethod = MethodArguments(method = self.buildArtistSongList, arguments = arguments)
2276 connection = OpenDatabase()
2277 if connection is not None:
2278 connection.text_factory = str
2279 cursor = connection.cursor()
2281 artistSongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2282 cursor.execute("select song_id, title, artists.artist, filename, bitrate, length, genre_text, track, date, album_text, songs.Album_id from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id where songs.artist_id = %d order by Album.album_text, tracknumber, filename;" % (artistID))
2284 artistSongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], bitrate = row[4], length = row[5], genre = row[6], track = row[7], date = row[8], album = row[9], albumID = row[10], artistID = artistID),))
2285 cursor.execute("SELECT artist from artists where artist_ID = %d;" % artistID)
2286 row = cursor.fetchone()
2287 self["headertext"].setText(_("Artist (%s) -> Song List") % row[0])
2290 self["list"].setList(artistSongList)
2291 if len(artistSongList) > 1:
2292 self["list"].moveToIndex(1)
2294 def buildAlbumSongList(self, albumID, mode, addToCache):
2296 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2298 arguments["albumID"] = albumID
2299 arguments["mode"] = mode
2300 arguments["addToCache"] = False
2301 self.LastMethod = MethodArguments(method = self.buildAlbumSongList, arguments = arguments)
2302 connection = OpenDatabase()
2303 if connection is not None:
2304 connection.text_factory = str
2305 cursor = connection.cursor()
2307 albumSongList.append((Item(text = _("[back]"), mode = mode, navigator = True),))
2308 cursor.execute("select song_id, title, artists.artist, filename, songs.artist_id, bitrate, length, genre_text, track, date, album_text from songs inner join artists on songs.artist_id = artists.artist_id inner join Album on songs.Album_id = Album.Album_id inner join genre on songs.genre_id = genre.genre_id where songs.album_id = %d order by tracknumber, filename;" % (albumID))
2310 albumSongList.append((Item(mode = 99, songID = row[0], title = row[1], artist = row[2], filename = row[3], artistID = row[4], bitrate = row[5], length = row[6], genre = row[7], track = row[8], date = row[9], album = row[10], albumID = albumID),))
2311 cursor.execute("SELECT album_text from album where album_ID = %d;" % albumID)
2312 row = cursor.fetchone()
2313 self["headertext"].setText(_("Album (%s) -> Song List") % row[0])
2316 self["list"].setList(albumSongList)
2317 if len(albumSongList) > 1:
2318 self["list"].moveToIndex(1)
2320 def buildMainMenuList(self, addToCache = True):
2322 arguments["addToCache"] = True
2323 self.LastMethod = MethodArguments(method = self.buildMainMenuList, arguments = arguments)
2324 self["headertext"].setText(_("iDream Main Menu"))
2326 connection = OpenDatabase()
2327 if connection is not None:
2328 connection.text_factory = str
2329 cursor = connection.cursor()
2331 cursor.execute("SELECT COUNT (*) FROM playlists;")
2332 row = cursor.fetchone()
2333 mainMenuList.append((Item(text = _("Playlists (%d)") % row[0], mode = 1),))
2335 cursor.execute("SELECT COUNT (*) FROM artists;")
2336 row = cursor.fetchone()
2337 mainMenuList.append((Item(text = _("Artists (%d)") % row[0], mode = 4),))
2339 cursor.execute("SELECT COUNT (DISTINCT album_text) FROM album;")
2340 row = cursor.fetchone()
2341 mainMenuList.append((Item(text = _("Albums (%d)") % row[0], mode = 7),))
2343 cursor.execute("SELECT COUNT (*) FROM songs;")
2344 row = cursor.fetchone()
2345 mainMenuList.append((Item(text = _("Songs (%d)") % row[0], mode = 10),))
2347 cursor.execute("SELECT COUNT (*) FROM genre;")
2348 row = cursor.fetchone()
2349 mainMenuList.append((Item(text = _("Genres (%d)") % row[0], mode = 13),))
2352 self["list"].setList(mainMenuList)
2353 self["list"].moveToIndex(0)
2355 def buildArtistList(self, addToCache):
2357 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2359 arguments["addToCache"] = False
2360 self.LastMethod = MethodArguments(method = self.buildArtistList, arguments = arguments)
2361 self["headertext"].setText(_("Artists List"))
2362 connection = OpenDatabase()
2363 if connection is not None:
2364 connection.text_factory = str
2365 cursor = connection.cursor()
2367 artistList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2368 cursor.execute("SELECT artists.artist_id,artists.artist, count (distinct album.album_text) FROM songs INNER JOIN artists ON songs.artist_id = artists.artist_id inner join album on songs.album_id = album.album_id GROUP BY songs.artist_id ORDER BY artists.artist;")
2370 artistList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 5, artistID = row[0]),))
2373 self["list"].setList(artistList)
2375 def buildArtistAlbumList(self, ArtistID, addToCache):
2377 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2379 arguments["ArtistID"] = ArtistID
2380 arguments["addToCache"] = False
2381 self.LastMethod = MethodArguments(method = self.buildArtistAlbumList, arguments = arguments)
2382 connection = OpenDatabase()
2383 if connection is not None:
2384 connection.text_factory = str
2385 cursor = connection.cursor()
2386 albumArtistList = []
2387 albumArtistList.append((Item(text = _("[back]"), mode = 4, navigator = True),))
2388 cursor.execute("select Album.Album_id,Album.Album_text from songs inner join Album on songs.Album_id = Album.Album_id where songs.artist_id = %d group by songs.Album_id order by Album.Album_text;" % ArtistID)
2390 cursor2 = connection.cursor()
2391 cursor2.execute("select count(song_id) from songs where album_id = %d;" % row[0])
2392 row2 = cursor2.fetchone()
2393 albumArtistList.append((Item(text = "%s (%d)" % (row[1], row2[0]), mode = 6, albumID = row[0], artistID = ArtistID),))
2395 cursor.execute("SELECT artist from artists where artist_ID = %d;" % ArtistID)
2396 row = cursor.fetchone()
2397 self["headertext"].setText(_("Artist (%s) -> Album List") % row[0])
2400 self["list"].setList(albumArtistList)
2401 if len(albumArtistList) > 1:
2402 self["list"].moveToIndex(1)
2404 def buildAlbumList(self, addToCache):
2406 self.cacheList.append(CacheList(index = self["list"].getCurrentIndex(), listview = self["list"].getList(), headertext = self["headertext"].getText(), methodarguments = self.LastMethod))
2408 arguments["addToCache"] = False
2409 self.LastMethod = MethodArguments(method = self.buildAlbumList, arguments = arguments)
2410 self["headertext"].setText(_("Albums List"))
2411 connection = OpenDatabase()
2412 if connection is not None:
2413 connection.text_factory = str
2414 cursor = connection.cursor()
2416 albumList.append((Item(text = _("[back]"), mode = 0, navigator = True),))
2417 cursor.execute("select Album.Album_id,Album.Album_text, count(*) from songs inner join Album on songs.Album_id = Album.Album_id group by songs.Album_id order by Album.Album_text;")
2419 albumList.append((Item(text = "%s (%d)" % (row[1], row[2]), mode = 8, albumID = row[0]),))
2422 self["list"].setList(albumList)
2423 if len(albumList) > 1:
2424 self["list"].moveToIndex(1)
2427 if pathToDatabase.isRunning:
2428 self.showScanner = eTimer()
2429 self.showScanner.callback.append(self.showScannerCallback)
2430 self.showScanner.start(0,1)
2432 if config.plugins.merlinmusicplayer.startlastsonglist.value:
2433 self.startPlayerTimer = eTimer()
2434 self.startPlayerTimer.callback.append(self.startPlayerTimerCallback)
2435 self.startPlayerTimer.start(0,1)
2437 self["list"].setMode(self.mode)
2438 self.buildMainMenuList()
2440 def showScannerCallback(self):
2441 self.session.openWithCallback(self.filesAdded, iDreamAddToDatabase,None)
2444 def startPlayerTimerCallback(self):
2445 connection = OpenDatabase()
2446 if connection is not None:
2447 connection.text_factory = str
2448 cursor = connection.cursor()
2451 cursor.execute("select song_id, filename, title, artist, album, genre, bitrate, length, track, date, PTS from CurrentSongList;")
2453 SongList.append((Item(songID = row[0], text = os_path.basename(row[1]), filename = row[1], title = row[2], artist = row[3], album = row[4], genre = row[5], bitrate = row[6], length = row[7], track = row[8], date = row[9], PTS = row[10], join = False),))
2458 if self.player is not None:
2459 self.player.doClose()
2461 self.startMerlinPlayerScreenTimer.stop()
2462 count = len(SongList)
2464 # just to be sure, check the index , it's critical
2465 index = config.plugins.merlinmusicplayer.lastsonglistindex.value
2468 self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,SongList, index, iDreamMode, self.currentService, self.serviceList)
2469 self.session.execDialog(self.player)
2472 self.startMerlinPlayerScreenTimer.stop()
2473 self.session.openWithCallback(self.setupFinished, MerlinMusicPlayerSetup, True)
2475 def setupFinished(self, result):
2480 def stopPlayingAndAppendFileToSongList(self):
2481 self.startMerlinPlayerScreenTimer.stop()
2482 if self.player is not None:
2483 self.player.doClose()
2485 self.appendFileToSongList()
2486 self.startMerlinPlayerScreenTimer.start(START_MERLIN_PLAYER_SCREEN_TIMER_VALUE)
2488 def appendFileToSongList(self):
2490 playerAvailable = self.player is not None and self.player.songList
2491 sel = self.getCurrentSelection()
2494 self.player.songList.append((sel,))
2495 self.player.origSongList.append((sel,))
2497 SongList.append((sel,))
2498 if not playerAvailable:
2499 if self.player is not None:
2500 self.player.doClose()
2502 self.player = self.session.instantiateDialog(MerlinMusicPlayerScreen,SongList, 0, True, self.currentService, self.serviceList)
2503 self.player.playSong(self.player.songList[self.player.currentIndex][0].filename)
2504 self.player["coverArt"].onShow()
2505 self.player.init = 1
2507 self.player["nextTitle"].setText(self.player.getNextTitle())
2508 self.session.open(MessageBox, _("%s\nappended to songlist")%sel.title, type = MessageBox.TYPE_INFO,timeout = 3 )
2510 def insertFileToSongList(self):
2511 sel = self.getCurrentSelection()
2513 if self.player is not None and self.player.songList:
2514 index = self.player.currentIndex
2515 self.player.songList.insert(index+1,(sel,))
2516 self.player.origSongList.insert(index+1,(sel,))
2517 self.player["nextTitle"].setText(self.player.getNextTitle())
2518 self.session.open(MessageBox, _("%s\ninserted and will be played as next song")%sel.title, type = MessageBox.TYPE_INFO,timeout = 3 )
2520 self.appendFileToSongList()
2522 def Error(self, error = None):
2523 if error is not None:
2525 self["statustext"].setText(str(error.getErrorMessage()))
2530 def __onClose(self):
2531 self.session.nav.SleepTimer.on_state_change.remove(self.sleepTimerEntryOnStateChange)
2532 self.startMerlinPlayerScreenTimer.stop()
2533 if self.player is not None:
2534 self.player.closePlayer()
2535 self.player.doClose()
2537 if self.serviceList is None:
2538 self.session.nav.playService(self.currentService)
2540 current = ServiceReference(self.serviceList.getCurrentSelection())
2541 self.session.nav.playService(current.ref)
2544 def lcdUpdate(self):
2545 self.startMerlinPlayerScreenTimer.start(START_MERLIN_PLAYER_SCREEN_TIMER_VALUE)
2547 count = self["list"].getItemCount()
2548 index = self["list"].getCurrentIndex()
2549 iDreamList = self["list"].getList()
2550 self.summaries.setText(iDreamList[index][0].title or iDreamList[index][0].text,1)