1 from os import path as os_path, remove as os_remove, listdir as os_listdir
2 from time import strftime
3 from enigma import eTimer, iPlayableService, eServiceCenter, iServiceInformation
4 from Screens.Screen import Screen
5 from Screens.MessageBox import MessageBox
6 from Screens.InputBox import InputBox
7 from Components.ActionMap import NumberActionMap, HelpableActionMap
8 from Components.Label import Label
9 from Components.Pixmap import Pixmap
10 from Components.Label import Label
11 from Components.FileList import FileList
12 from Components.MediaPlayer import PlayList
13 from Tools.Directories import resolveFilename, SCOPE_CONFIG, SCOPE_PLAYLIST, SCOPE_SKIN_IMAGE
14 from Components.ServicePosition import ServicePositionGauge
15 from Components.ServiceEventTracker import ServiceEventTracker
16 from Components.Playlist import PlaylistIOInternal, PlaylistIOM3U, PlaylistIOPLS
17 from Screens.InfoBarGenerics import InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSupport, InfoBarNotifications
18 from ServiceReference import ServiceReference
19 from Screens.ChoiceBox import ChoiceBox
20 from Screens.HelpMenu import HelpableScreen
23 class MyPlayList(PlayList):
25 PlayList.__init__(self)
27 def PlayListShuffle(self):
28 random.shuffle(self.list)
29 self.l.setList(self.list)
31 self.oldCurrPlaying = -1
33 class MediaPixmap(Pixmap):
34 def applySkin(self, desktop):
35 self.default_pixmap = None
36 if self.skinAttributes is not None:
37 for (attrib, value) in self.skinAttributes:
38 if attrib == "pixmap":
39 self.default_pixmap = value
41 if self.default_pixmap is None:
42 self.default_pixmap = resolveFilename(SCOPE_SKIN_IMAGE, "no_coverArt.png")
43 return Pixmap.applySkin(self, desktop)
45 class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSupport, InfoBarNotifications, HelpableScreen):
47 ENABLE_RESUME_SUPPORT = True
49 def __init__(self, session, args = None):
50 Screen.__init__(self, session)
51 InfoBarAudioSelection.__init__(self)
52 InfoBarCueSheetSupport.__init__(self, actionmap = "MediaPlayerCueSheetActions")
53 InfoBarNotifications.__init__(self)
54 HelpableScreen.__init__(self)
56 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
57 self.session.nav.stopService()
59 self.playlistparsers = {}
60 self.addPlaylistParser(PlaylistIOM3U, "m3u")
61 self.addPlaylistParser(PlaylistIOPLS, "pls")
62 self.addPlaylistParser(PlaylistIOInternal, "e2pls")
64 # 'None' is magic to start at the list of mountpoints
65 self.filelist = FileList(None, matchingPattern = "(?i)^.*\.(mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob)", useServiceRef = True)
66 self["filelist"] = self.filelist
68 self.playlist = MyPlayList()
69 #self.playlist = PlayList()
70 self.is_closing = False
72 self["playlist"] = self.playlist
74 self["PositionGauge"] = ServicePositionGauge(self.session.nav)
76 self["currenttext"] = Label("")
78 self["artisttext"] = Label(_("Artist:"))
79 self["artist"] = Label("")
80 self["titletext"] = Label(_("Title:"))
81 self["title"] = Label("")
82 self["albumtext"] = Label(_("Album:"))
83 self["album"] = Label("")
84 self["yeartext"] = Label(_("Year:"))
85 self["year"] = Label("")
86 self["genretext"] = Label(_("Genre:"))
87 self["genre"] = Label("")
88 self["coverArt"] = MediaPixmap()
90 self.seek_target = None
92 class MoviePlayerActionMap(NumberActionMap):
93 def __init__(self, player, contexts = [ ], actions = { }, prio=0):
94 NumberActionMap.__init__(self, contexts, actions, prio)
97 def action(self, contexts, action):
99 return NumberActionMap.action(self, contexts, action)
102 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
104 "ok": (self.ok, _("add file to playlist")),
105 "cancel": (self.exit, _("exit mediaplayer")),
108 self["MediaPlayerActions"] = HelpableActionMap(self, "MediaPlayerActions",
110 "play": (self.playEntry, _("play entry")),
111 "pause": (self.pauseEntry, _("pause")),
112 "stop": (self.stopEntry, _("stop entry")),
113 "previous": (self.previousEntry, _("play previous playlist entry")),
114 "next": (self.nextEntry, _("play next playlist entry")),
115 "menu": (self.showMenu, _("menu")),
116 "skipListbegin": (self.skip_listbegin, _("jump to listbegin")),
117 "skipListend": (self.skip_listend, _("jump to listend")),
118 "prevBouquet": (self.switchToPlayList, _("switch to playlist")),
119 "nextBouquet": (self.switchToFileList, _("switch to filelist")),
120 "delete": (self.deletePlaylistEntry, _("delete playlist entry")),
121 "shift_stop": (self.clear_playlist, _("clear playlist")),
122 "shift_record": (self.playlist.PlayListShuffle, _("shuffle playlist")),
125 self["InfobarEPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
127 "showEventInfo": (self.showEventInformation, _("show event details")),
130 self["actions"] = MoviePlayerActionMap(self, ["DirectionActions"],
132 "right": self.rightDown,
133 "rightRepeated": self.doNothing,
134 "rightUp": self.rightUp,
135 "left": self.leftDown,
136 "leftRepeated": self.doNothing,
137 "leftUp": self.leftUp,
140 "upRepeated": self.up,
141 "upUp": self.doNothing,
143 "downRepeated": self.down,
144 "downUp": self.doNothing,
147 InfoBarSeek.__init__(self, actionmap = "MediaPlayerSeekActions")
149 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
151 #iPlayableService.evStart: self.__serviceStarted,
152 #iPlayableService.evSeekableStatusChanged: InfoBarSeek.__seekableStatusChanged,
154 iPlayableService.evEOF: self.__evEOF,
157 self.onClose.append(self.delMPTimer)
158 self.onClose.append(self.__onClose)
160 self.righttimer = False
161 self.rightKeyTimer = eTimer()
162 self.rightKeyTimer.timeout.get().append(self.rightTimerFire)
164 self.lefttimer = False
165 self.leftKeyTimer = eTimer()
166 self.leftKeyTimer.timeout.get().append(self.leftTimerFire)
168 self.currList = "filelist"
170 self.coverArtFileName = ""
172 self.playlistIOInternal = PlaylistIOInternal()
173 list = self.playlistIOInternal.open(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
176 self.playlist.addFile(x.ref)
177 self.playlist.updateList()
182 def createSummary(self):
183 return MediaPlayerLCDScreen
186 self.session.openWithCallback(self.exitCB, MessageBox, _("Do you really want to exit?"), timeout=5)
188 def exitCB(self, answer):
190 self.playlistIOInternal.clear()
191 for x in self.playlist.list:
192 self.playlistIOInternal.addService(ServiceReference(x[0]))
193 self.playlistIOInternal.save(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
196 def checkSkipShowHideLock(self):
197 self.updatedSeekState()
203 self.session.nav.playService(self.oldService)
205 def delMPTimer(self):
206 del self.rightKeyTimer
207 del self.leftKeyTimer
209 def readTitleInformation(self):
210 currPlay = self.session.nav.getCurrentService()
211 if currPlay is not None:
212 stitle = currPlay.info().getInfoString(iServiceInformation.sTitle)
214 stitle = currPlay.info().getName().split('/')[-1]
216 self.updateMusicInformation( artist = currPlay.info().getInfoString(iServiceInformation.sArtist),
218 album = currPlay.info().getInfoString(iServiceInformation.sAlbum),
219 genre = currPlay.info().getInfoString(iServiceInformation.sGenre),
222 self.updateMusicInformation()
224 def updateMusicInformation(self, artist = "", title = "", album = "", year = "", genre = "", clear = False):
225 self.updateSingleMusicInformation("artist", artist, clear)
226 self.updateSingleMusicInformation("title", title, clear)
227 self.updateSingleMusicInformation("album", album, clear)
228 self.updateSingleMusicInformation("year", year, clear)
229 self.updateSingleMusicInformation("genre", genre, clear)
231 def updateSingleMusicInformation(self, name, info, clear):
232 if info != "" or clear:
233 if self[name].getText() != info:
234 self[name].setText(info)
236 def updateCoverArtPixmap(self, path):
237 while not path.endswith("/"):
239 pngname = path + "folder.png"
241 if not os_path.exists(pngname):
242 pngname = self["coverArt"].default_pixmap
243 if self.coverArtFileName != pngname:
244 self.coverArtFileName = pngname
245 self["coverArt"].instance.setPixmapFromFile(self.coverArtFileName)
248 self.lefttimer = True
249 self.leftKeyTimer.start(1000)
252 self.righttimer = True
253 self.rightKeyTimer.start(1000)
257 self.leftKeyTimer.stop()
258 self.lefttimer = False
259 self[self.currList].pageUp()
260 self.updateCurrentInfo()
264 self.rightKeyTimer.stop()
265 self.righttimer = False
266 self[self.currList].pageDown()
267 self.updateCurrentInfo()
269 def leftTimerFire(self):
270 self.leftKeyTimer.stop()
271 self.lefttimer = False
272 self.switchToFileList()
274 def rightTimerFire(self):
275 self.rightKeyTimer.stop()
276 self.righttimer = False
277 self.switchToPlayList()
279 def switchToFileList(self):
280 self.currList = "filelist"
281 self.filelist.selectionEnabled(1)
282 self.playlist.selectionEnabled(0)
283 self.updateCurrentInfo()
285 def switchToPlayList(self):
286 if len(self.playlist) != 0:
287 self.currList = "playlist"
288 self.filelist.selectionEnabled(0)
289 self.playlist.selectionEnabled(1)
290 self.updateCurrentInfo()
293 self[self.currList].up()
294 self.updateCurrentInfo()
297 self[self.currList].down()
298 self.updateCurrentInfo()
300 def showAfterSeek(self):
303 def showAfterCuesheetOperation(self):
306 def hideAfterResume(self):
309 # FIXME: maybe this code can be optimized
310 def updateCurrentInfo(self):
312 if self.currList == "filelist":
313 idx = self.filelist.getSelectionIndex()
314 r = self.filelist.list[idx]
321 self.summaries.setText(text,1)
324 if idx < len(self.filelist.list):
325 r = self.filelist.list[idx]
329 self.summaries.setText(text,3)
331 self.summaries.setText(" ",3)
334 if idx < len(self.filelist.list):
335 r = self.filelist.list[idx]
339 self.summaries.setText(text,4)
341 self.summaries.setText(" ",4)
344 if not self.filelist.canDescent():
345 r = self.filelist.getServiceRef()
349 self["currenttext"].setText(os_path.basename(text))
351 if self.currList == "playlist":
352 t = self.playlist.getSelection()
355 #display current selected entry on LCD
357 text = text.split('/')[-1]
358 self.summaries.setText(text,1)
359 self["currenttext"].setText(text)
360 idx = self.playlist.getSelectionIndex()
362 if idx < len(self.playlist):
363 currref = self.playlist.getServiceRefList()[idx]
364 text = currref.getPath()
365 text = text.split('/')[-1]
366 self.summaries.setText(text,3)
368 self.summaries.setText(" ",3)
371 if idx < len(self.playlist):
372 currref = self.playlist.getServiceRefList()[idx]
373 text = currref.getPath()
374 text = text.split('/')[-1]
375 self.summaries.setText(text,4)
377 self.summaries.setText(" ",4)
380 if self.currList == "filelist":
381 if self.filelist.canDescent():
382 self.filelist.descent()
383 self.updateCurrentInfo()
387 if self.currList == "playlist":
388 selection = self["playlist"].getSelection()
389 self.changeEntry(self.playlist.getSelectionIndex())
393 if self.currList == "filelist":
394 if self.filelist.canDescent():
395 menu.append((_("add directory to playlist"), "copydir"))
397 menu.append((_("add files to playlist"), "copyfiles"))
398 menu.append((_("switch to playlist"), "playlist"))
400 menu.append((_("switch to filelist"), "filelist"))
402 menu.append((_("shuffle playlist"), "shuffle"))
404 menu.append((_("delete"), "delete"))
405 menu.append((_("clear playlist"), "clear"))
406 menu.append((_("hide player"), "hide"));
407 menu.append((_("save playlist"), "saveplaylist"));
408 menu.append((_("load playlist"), "loadplaylist"));
409 menu.append((_("delete saved playlist"), "deleteplaylist"));
410 self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu)
412 def menuCallback(self, choice):
416 if choice[1] == "copydir":
417 self.copyDirectory(self.filelist.getSelection()[0])
418 elif choice[1] == "copyfiles":
420 self.playlist.clear()
421 self.copyDirectory(os_path.dirname(self.filelist.getSelection()[0].getPath()) + "/", recursive = False)
422 self.playServiceRefEntry(self.filelist.getServiceRef())
423 elif choice[1] == "playlist":
424 self.switchToPlayList()
425 elif choice[1] == "filelist":
426 self.switchToFileList()
427 elif choice[1] == "delete":
428 if self.playlist.getSelectionIndex() == self.playlist.getCurrentIndex():
431 elif choice[1] == "clear":
433 self.playlist.clear()
434 self.switchToFileList()
435 elif choice[1] == "hide":
437 elif choice[1] == "saveplaylist":
439 elif choice[1] == "loadplaylist":
441 elif choice[1] == "deleteplaylist":
442 self.delete_saved_playlist()
443 elif choice[1] == "shuffle":
444 self.playlist.PlayListShuffle()
447 def showEventInformation(self):
448 from Screens.EventView import EventViewSimple
449 from ServiceReference import ServiceReference
450 evt = self[self.currList].getCurrentEvent()
452 self.session.open(EventViewSimple, evt, ServiceReference(self.getCurrent()))
454 # also works on filelist (?)
455 def getCurrent(self):
456 return self["playlist"].getCurrent()
458 def deletePlaylistEntry(self):
459 if self.currList == "playlist":
460 if self.playlist.getSelectionIndex() == self.playlist.getCurrentIndex():
464 def skip_listbegin(self):
465 if self.currList == "filelist":
466 self.filelist.moveToIndex(0)
468 self.playlist.moveToIndex(0)
469 self.updateCurrentInfo()
471 def skip_listend(self):
472 if self.currList == "filelist":
473 idx = len(self.filelist.list)
474 self.filelist.moveToIndex(idx - 1)
476 self.playlist.moveToIndex(len(self.playlist)-1)
477 self.updateCurrentInfo()
479 def save_playlist(self):
480 self.session.openWithCallback(self.save_playlist2,InputBox, title=_("Please enter filename (empty = use current date)"),windowTitle = _("Save Playlist"))
482 def save_playlist2(self, name):
486 name = strftime("%y%m%d_%H%M%S")
488 self.playlistIOInternal.clear()
489 for x in self.playlist.list:
490 self.playlistIOInternal.addService(ServiceReference(x[0]))
491 self.playlistIOInternal.save(resolveFilename(SCOPE_PLAYLIST) + name)
493 def load_playlist(self):
495 playlistdir = resolveFilename(SCOPE_PLAYLIST)
497 for i in os_listdir(playlistdir):
498 listpath.append((i,playlistdir + i))
500 print "Error while scanning subdirs ",e
501 self.session.openWithCallback(self.PlaylistSelected, ChoiceBox, title=_("Please select a playlist..."), list = listpath)
503 def PlaylistSelected(self,path):
505 self.clear_playlist()
506 self.playlistIOInternal = PlaylistIOInternal()
507 list = self.playlistIOInternal.open(path[1])
510 self.playlist.addFile(x.ref)
511 self.playlist.updateList()
513 def delete_saved_playlist(self):
515 playlistdir = resolveFilename(SCOPE_PLAYLIST)
517 for i in os_listdir(playlistdir):
518 listpath.append((i,playlistdir + i))
520 print "Error while scanning subdirs ",e
521 self.session.openWithCallback(self.DeletePlaylistSelected, ChoiceBox, title=_("Please select a playlist to delete..."), list = listpath)
523 def DeletePlaylistSelected(self,path):
525 self.delname = path[1]
526 self.session.openWithCallback(self.deleteConfirmed, MessageBox, _("Do you really want to delete %s?") % (path[1]))
528 def deleteConfirmed(self, confirmed):
530 os_remove(self.delname)
532 def clear_playlist(self):
534 self.playlist.clear()
535 self.switchToFileList()
537 def copyDirectory(self, directory, recursive = True):
538 print "copyDirectory", directory
539 filelist = FileList(directory, useServiceRef = True, isTop = True)
541 for x in filelist.getFileList():
542 if x[0][1] == True: #isDir
544 self.copyDirectory(x[0][0])
546 self.playlist.addFile(x[0][0])
547 self.playlist.updateList()
550 if self.filelist.getServiceRef().type == 4098: # playlist
551 ServiceRef = self.filelist.getServiceRef()
552 extension = ServiceRef.getPath()[ServiceRef.getPath().rfind('.') + 1:]
553 print "extension:", extension
554 if self.playlistparsers.has_key(extension):
555 playlist = self.playlistparsers[extension]()
556 list = playlist.open(ServiceRef.getPath())
558 self.playlist.addFile(x.ref)
560 self.playlist.addFile(self.filelist.getServiceRef())
561 self.playlist.updateList()
562 if len(self.playlist) == 1:
565 def addPlaylistParser(self, parser, extension):
566 self.playlistparsers[extension] = parser
569 next = self.playlist.getCurrentIndex() + 1
570 if next < len(self.playlist):
571 self.changeEntry(next)
573 def previousEntry(self):
574 next = self.playlist.getCurrentIndex() - 1
576 self.changeEntry(next)
578 def deleteEntry(self):
579 self.playlist.deleteFile(self.playlist.getSelectionIndex())
580 self.playlist.updateList()
581 if len(self.playlist) == 0:
582 self.switchToFileList()
584 def changeEntry(self, index):
585 self.playlist.setCurrentPlaying(index)
588 def playServiceRefEntry(self, serviceref):
589 serviceRefList = self.playlist.getServiceRefList()
590 for count in range(len(serviceRefList)):
591 if serviceRefList[count] == serviceref:
592 self.changeEntry(count)
596 if len(self.playlist.getServiceRefList()):
597 needsInfoUpdate = False
598 currref = self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()]
599 if self.session.nav.getCurrentlyPlayingServiceReference() is None or currref != self.session.nav.getCurrentlyPlayingServiceReference():
600 self.session.nav.playService(self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()])
601 info = eServiceCenter.getInstance().info(currref)
602 description = info and info.getInfoString(currref, iServiceInformation.sDescription) or ""
603 self["title"].setText(description)
604 # display just playing musik on LCD
605 idx = self.playlist.getCurrentIndex()
606 currref = self.playlist.getServiceRefList()[idx]
607 text = currref.getPath()
608 text = text.split('/')[-1]
610 ext = text[-3:].lower()
612 # FIXME: the information if the service contains video (and we should hide our window) should com from the service instead
613 if ext not in ["mp3", "wav", "ogg"]:
616 needsInfoUpdate = True
617 self.summaries.setText(text,1)
619 # get the next two entries
621 if idx < len(self.playlist):
622 currref = self.playlist.getServiceRefList()[idx]
623 text = currref.getPath()
624 text = text.split('/')[-1]
625 self.summaries.setText(text,3)
627 self.summaries.setText(" ",3)
630 if idx < len(self.playlist):
631 currref = self.playlist.getServiceRefList()[idx]
632 text = currref.getPath()
633 text = text.split('/')[-1]
634 self.summaries.setText(text,4)
636 self.summaries.setText(" ",4)
638 idx = self.playlist.getCurrentIndex()
639 currref = self.playlist.getServiceRefList()[idx]
640 text = currref.getPath()
641 ext = text[-3:].lower()
642 if ext not in ["mp3", "wav", "ogg"]:
645 needsInfoUpdate = True
647 self.unPauseService()
648 if needsInfoUpdate == True:
649 self.updateCoverArtPixmap(currref.getPath())
651 pngname = self["coverArt"].default_pixmap
652 self.coverArtFileName = pngname
653 self["coverArt"].instance.setPixmapFromFile(self.coverArtFileName)
654 self.readTitleInformation()
656 def updatedSeekState(self):
657 if self.seekstate == self.SEEK_STATE_PAUSE:
658 self.playlist.pauseFile()
659 elif self.seekstate == self.SEEK_STATE_PLAY:
660 self.playlist.playFile()
661 elif self.seekstate in ( self.SEEK_STATE_FF_2X,
662 self.SEEK_STATE_FF_4X,
663 self.SEEK_STATE_FF_8X,
664 self.SEEK_STATE_FF_16X,
665 self.SEEK_STATE_FF_32X,
666 self.SEEK_STATE_FF_48X,
667 self.SEEK_STATE_FF_64X,
668 self.SEEK_STATE_FF_128X):
669 self.playlist.forwardFile()
670 elif self.seekstate in ( self.SEEK_STATE_BACK_8X,
671 self.SEEK_STATE_BACK_16X,
672 self.SEEK_STATE_BACK_32X,
673 self.SEEK_STATE_BACK_48X,
674 self.SEEK_STATE_BACK_64X,
675 self.SEEK_STATE_BACK_128X):
676 self.playlist.rewindFile()
678 def pauseEntry(self):
683 self.playlist.stopFile()
684 self.session.nav.playService(None)
685 self.updateMusicInformation(clear=True)
688 def unPauseService(self):
689 self.setSeekState(self.SEEK_STATE_PLAY)
692 class MediaPlayerLCDScreen(Screen):
694 <screen position="0,0" size="132,64" title="LCD Text">
695 <widget name="text1" position="4,0" size="132,35" font="Regular;16"/>
696 <widget name="text3" position="4,36" size="132,14" font="Regular;10"/>
697 <widget name="text4" position="4,49" size="132,14" font="Regular;10"/>
700 def __init__(self, session, parent):
701 Screen.__init__(self, session)
702 self["text1"] = Label("Mediaplayer")
703 self["text3"] = Label("")
704 self["text4"] = Label("")
706 def setText(self, text, line):
707 print "lcd set text:", text, line
709 if text[-4:] == ".mp3":
712 text = text + textleer*10
714 self["text1"].setText(text)
716 self["text3"].setText(text)
718 self["text4"].setText(text)
720 def main(session, **kwargs):
721 session.open(MediaPlayer)
723 def menu(menuid, **kwargs):
724 if menuid == "mainmenu":
725 return [(_("Media player"), main, "media_player", 45)]
728 def filescan_open(list, session, **kwargs):
729 from enigma import eServiceReference
731 mp = session.open(MediaPlayer)
733 mp.switchToPlayList()
735 ref = eServiceReference(4097, 0, file.path)
736 mp.playlist.addFile(ref)
738 # TODO: rather play first than last file?
739 mp.playServiceRefEntry(ref)
740 mp.playlist.updateList()
742 def filescan(**kwargs):
743 from Components.Scanner import Scanner, ScanPath
745 Scanner(mimetypes = ["video/mpeg"],
748 ScanPath(path = "", with_subdirs = False),
751 description = "View Movies...",
752 openfnc = filescan_open,
754 Scanner(mimetypes = ["audio/mpeg", "audio/x-wav", "application/ogg"],
757 ScanPath(path = "", with_subdirs = False),
760 description = "Play Music...",
761 openfnc = filescan_open,
765 from Plugins.Plugin import PluginDescriptor
766 def Plugins(**kwargs):
768 PluginDescriptor(name = "MediaPlayer", description = "Play back media files", where = PluginDescriptor.WHERE_MENU, fnc = menu),
769 PluginDescriptor(name = "MediaPlayer", where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)