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, InfoBarBase
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, "skin_default/no_coverArt.png")
43 return Pixmap.applySkin(self, desktop)
45 class MediaPlayer(Screen, InfoBarBase, 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 InfoBarBase.__init__(self)
55 HelpableScreen.__init__(self)
57 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
58 self.session.nav.stopService()
60 self.playlistparsers = {}
61 self.addPlaylistParser(PlaylistIOM3U, "m3u")
62 self.addPlaylistParser(PlaylistIOPLS, "pls")
63 self.addPlaylistParser(PlaylistIOInternal, "e2pls")
65 # 'None' is magic to start at the list of mountpoints
66 self.filelist = FileList(None, matchingPattern = "(?i)^.*\.(mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob)", useServiceRef = True, additionalExtensions = "4098:m3u 4098:e2pls 4098:pls")
67 self["filelist"] = self.filelist
69 self.playlist = MyPlayList()
70 #self.playlist = PlayList()
71 self.is_closing = False
73 self["playlist"] = self.playlist
75 self["PositionGauge"] = ServicePositionGauge(self.session.nav)
77 self["currenttext"] = Label("")
79 self["artisttext"] = Label(_("Artist:"))
80 self["artist"] = Label("")
81 self["titletext"] = Label(_("Title:"))
82 self["title"] = Label("")
83 self["albumtext"] = Label(_("Album:"))
84 self["album"] = Label("")
85 self["yeartext"] = Label(_("Year:"))
86 self["year"] = Label("")
87 self["genretext"] = Label(_("Genre:"))
88 self["genre"] = Label("")
89 self["coverArt"] = MediaPixmap()
91 self.seek_target = None
93 class MoviePlayerActionMap(NumberActionMap):
94 def __init__(self, player, contexts = [ ], actions = { }, prio=0):
95 NumberActionMap.__init__(self, contexts, actions, prio)
98 def action(self, contexts, action):
100 return NumberActionMap.action(self, contexts, action)
103 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
105 "ok": (self.ok, _("add file to playlist")),
106 "cancel": (self.exit, _("exit mediaplayer")),
109 self["MediaPlayerActions"] = HelpableActionMap(self, "MediaPlayerActions",
111 "play": (self.xplayEntry, _("play entry")),
112 "pause": (self.pauseEntry, _("pause")),
113 "stop": (self.stopEntry, _("stop entry")),
114 "previous": (self.previousMarkOrEntry, _("play from previous mark or playlist entry")),
115 "next": (self.nextMarkOrEntry, _("play from next mark or playlist entry")),
116 "menu": (self.showMenu, _("menu")),
117 "skipListbegin": (self.skip_listbegin, _("jump to listbegin")),
118 "skipListend": (self.skip_listend, _("jump to listend")),
119 "prevBouquet": (self.switchToPlayList, _("switch to playlist")),
120 "nextBouquet": (self.switchToFileList, _("switch to filelist")),
121 "delete": (self.deletePlaylistEntry, _("delete playlist entry")),
122 "shift_stop": (self.clear_playlist, _("clear playlist")),
123 "shift_record": (self.playlist.PlayListShuffle, _("shuffle playlist")),
126 self["InfobarEPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
128 "showEventInfo": (self.showEventInformation, _("show event details")),
131 self["actions"] = MoviePlayerActionMap(self, ["DirectionActions"],
133 "right": self.rightDown,
134 "rightRepeated": self.doNothing,
135 "rightUp": self.rightUp,
136 "left": self.leftDown,
137 "leftRepeated": self.doNothing,
138 "leftUp": self.leftUp,
141 "upRepeated": self.up,
142 "upUp": self.doNothing,
144 "downRepeated": self.down,
145 "downUp": self.doNothing,
148 InfoBarSeek.__init__(self, actionmap = "MediaPlayerSeekActions")
150 self.onClose.append(self.delMPTimer)
151 self.onClose.append(self.__onClose)
153 self.righttimer = False
154 self.rightKeyTimer = eTimer()
155 self.rightKeyTimer.callback.append(self.rightTimerFire)
157 self.lefttimer = False
158 self.leftKeyTimer = eTimer()
159 self.leftKeyTimer.callback.append(self.leftTimerFire)
161 self.currList = "filelist"
163 self.coverArtFileName = ""
165 self.playlistIOInternal = PlaylistIOInternal()
166 list = self.playlistIOInternal.open(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
169 self.playlist.addFile(x.ref)
170 self.playlist.updateList()
175 def createSummary(self):
176 return MediaPlayerLCDScreen
179 self.session.openWithCallback(self.exitCB, MessageBox, _("Do you really want to exit?"), timeout=5)
181 def exitCB(self, answer):
183 self.playlistIOInternal.clear()
184 for x in self.playlist.list:
185 self.playlistIOInternal.addService(ServiceReference(x[0]))
186 self.playlistIOInternal.save(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
189 def checkSkipShowHideLock(self):
190 self.updatedSeekState()
192 def doEofInternal(self, playing):
199 self.session.nav.playService(self.oldService)
201 def delMPTimer(self):
202 del self.rightKeyTimer
203 del self.leftKeyTimer
205 def readTitleInformation(self):
206 currPlay = self.session.nav.getCurrentService()
207 if currPlay is not None:
208 stitle = currPlay.info().getInfoString(iServiceInformation.sTitle)
210 stitle = currPlay.info().getName().split('/')[-1]
212 self.updateMusicInformation( artist = currPlay.info().getInfoString(iServiceInformation.sArtist),
214 album = currPlay.info().getInfoString(iServiceInformation.sAlbum),
215 genre = currPlay.info().getInfoString(iServiceInformation.sGenre),
218 self.updateMusicInformation()
220 def updateMusicInformation(self, artist = "", title = "", album = "", year = "", genre = "", clear = False):
221 self.updateSingleMusicInformation("artist", artist, clear)
222 self.updateSingleMusicInformation("title", title, clear)
223 self.updateSingleMusicInformation("album", album, clear)
224 self.updateSingleMusicInformation("year", year, clear)
225 self.updateSingleMusicInformation("genre", genre, clear)
227 def updateSingleMusicInformation(self, name, info, clear):
228 if info != "" or clear:
229 if self[name].getText() != info:
230 self[name].setText(info)
232 def updateCoverArtPixmap(self, path):
233 while not path.endswith("/"):
235 pngname = path + "folder.png"
237 if not os_path.exists(pngname):
238 pngname = self["coverArt"].default_pixmap
239 if self.coverArtFileName != pngname:
240 self.coverArtFileName = pngname
241 self["coverArt"].instance.setPixmapFromFile(self.coverArtFileName)
244 self.lefttimer = True
245 self.leftKeyTimer.start(1000)
248 self.righttimer = True
249 self.rightKeyTimer.start(1000)
253 self.leftKeyTimer.stop()
254 self.lefttimer = False
255 self[self.currList].pageUp()
256 self.updateCurrentInfo()
260 self.rightKeyTimer.stop()
261 self.righttimer = False
262 self[self.currList].pageDown()
263 self.updateCurrentInfo()
265 def leftTimerFire(self):
266 self.leftKeyTimer.stop()
267 self.lefttimer = False
268 self.switchToFileList()
270 def rightTimerFire(self):
271 self.rightKeyTimer.stop()
272 self.righttimer = False
273 self.switchToPlayList()
275 def switchToFileList(self):
276 self.currList = "filelist"
277 self.filelist.selectionEnabled(1)
278 self.playlist.selectionEnabled(0)
279 self.updateCurrentInfo()
281 def switchToPlayList(self):
282 if len(self.playlist) != 0:
283 self.currList = "playlist"
284 self.filelist.selectionEnabled(0)
285 self.playlist.selectionEnabled(1)
286 self.updateCurrentInfo()
289 self[self.currList].up()
290 self.updateCurrentInfo()
293 self[self.currList].down()
294 self.updateCurrentInfo()
296 def showAfterSeek(self):
299 def showAfterCuesheetOperation(self):
302 def hideAfterResume(self):
305 # FIXME: maybe this code can be optimized
306 def updateCurrentInfo(self):
308 if self.currList == "filelist":
309 idx = self.filelist.getSelectionIndex()
310 r = self.filelist.list[idx]
317 self.summaries.setText(text,1)
320 if idx < len(self.filelist.list):
321 r = self.filelist.list[idx]
325 self.summaries.setText(text,3)
327 self.summaries.setText(" ",3)
330 if idx < len(self.filelist.list):
331 r = self.filelist.list[idx]
335 self.summaries.setText(text,4)
337 self.summaries.setText(" ",4)
340 if not self.filelist.canDescent():
341 r = self.filelist.getServiceRef()
345 self["currenttext"].setText(os_path.basename(text))
347 if self.currList == "playlist":
348 t = self.playlist.getSelection()
351 #display current selected entry on LCD
353 text = text.split('/')[-1]
354 self.summaries.setText(text,1)
355 self["currenttext"].setText(text)
356 idx = self.playlist.getSelectionIndex()
358 if idx < len(self.playlist):
359 currref = self.playlist.getServiceRefList()[idx]
360 text = currref.getPath()
361 text = text.split('/')[-1]
362 self.summaries.setText(text,3)
364 self.summaries.setText(" ",3)
367 if idx < len(self.playlist):
368 currref = self.playlist.getServiceRefList()[idx]
369 text = currref.getPath()
370 text = text.split('/')[-1]
371 self.summaries.setText(text,4)
373 self.summaries.setText(" ",4)
376 if self.currList == "filelist":
377 if self.filelist.canDescent():
378 self.filelist.descent()
379 self.updateCurrentInfo()
383 if self.currList == "playlist":
384 selection = self["playlist"].getSelection()
385 self.changeEntry(self.playlist.getSelectionIndex())
389 if self.currList == "filelist":
390 if self.filelist.canDescent():
391 menu.append((_("add directory to playlist"), "copydir"))
393 menu.append((_("add files to playlist"), "copyfiles"))
394 menu.append((_("switch to playlist"), "playlist"))
396 menu.append((_("switch to filelist"), "filelist"))
398 menu.append((_("shuffle playlist"), "shuffle"))
400 menu.append((_("delete"), "delete"))
401 menu.append((_("clear playlist"), "clear"))
402 menu.append((_("hide player"), "hide"));
403 menu.append((_("save playlist"), "saveplaylist"));
404 menu.append((_("load playlist"), "loadplaylist"));
405 menu.append((_("delete saved playlist"), "deleteplaylist"));
406 self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu)
408 def menuCallback(self, choice):
412 if choice[1] == "copydir":
413 self.copyDirectory(self.filelist.getSelection()[0])
414 elif choice[1] == "copyfiles":
416 self.playlist.clear()
417 self.copyDirectory(os_path.dirname(self.filelist.getSelection()[0].getPath()) + "/", recursive = False)
418 self.playServiceRefEntry(self.filelist.getServiceRef())
419 elif choice[1] == "playlist":
420 self.switchToPlayList()
421 elif choice[1] == "filelist":
422 self.switchToFileList()
423 elif choice[1] == "delete":
424 if self.playlist.getSelectionIndex() == self.playlist.getCurrentIndex():
427 elif choice[1] == "clear":
429 self.playlist.clear()
430 self.switchToFileList()
431 elif choice[1] == "hide":
433 elif choice[1] == "saveplaylist":
435 elif choice[1] == "loadplaylist":
437 elif choice[1] == "deleteplaylist":
438 self.delete_saved_playlist()
439 elif choice[1] == "shuffle":
440 self.playlist.PlayListShuffle()
443 def showEventInformation(self):
444 from Screens.EventView import EventViewSimple
445 from ServiceReference import ServiceReference
446 evt = self[self.currList].getCurrentEvent()
448 self.session.open(EventViewSimple, evt, ServiceReference(self.getCurrent()))
450 # also works on filelist (?)
451 def getCurrent(self):
452 return self["playlist"].getCurrent()
454 def deletePlaylistEntry(self):
455 if self.currList == "playlist":
456 if self.playlist.getSelectionIndex() == self.playlist.getCurrentIndex():
460 def skip_listbegin(self):
461 if self.currList == "filelist":
462 self.filelist.moveToIndex(0)
464 self.playlist.moveToIndex(0)
465 self.updateCurrentInfo()
467 def skip_listend(self):
468 if self.currList == "filelist":
469 idx = len(self.filelist.list)
470 self.filelist.moveToIndex(idx - 1)
472 self.playlist.moveToIndex(len(self.playlist)-1)
473 self.updateCurrentInfo()
475 def save_playlist(self):
476 self.session.openWithCallback(self.save_playlist2,InputBox, title=_("Please enter filename (empty = use current date)"),windowTitle = _("Save Playlist"))
478 def save_playlist2(self, name):
482 name = strftime("%y%m%d_%H%M%S")
484 self.playlistIOInternal.clear()
485 for x in self.playlist.list:
486 self.playlistIOInternal.addService(ServiceReference(x[0]))
487 self.playlistIOInternal.save(resolveFilename(SCOPE_PLAYLIST) + name)
489 def load_playlist(self):
491 playlistdir = resolveFilename(SCOPE_PLAYLIST)
493 for i in os_listdir(playlistdir):
494 listpath.append((i,playlistdir + i))
496 print "Error while scanning subdirs ",e
497 self.session.openWithCallback(self.PlaylistSelected, ChoiceBox, title=_("Please select a playlist..."), list = listpath)
499 def PlaylistSelected(self,path):
501 self.clear_playlist()
502 self.playlistIOInternal = PlaylistIOInternal()
503 list = self.playlistIOInternal.open(path[1])
506 self.playlist.addFile(x.ref)
507 self.playlist.updateList()
509 def delete_saved_playlist(self):
511 playlistdir = resolveFilename(SCOPE_PLAYLIST)
513 for i in os_listdir(playlistdir):
514 listpath.append((i,playlistdir + i))
516 print "Error while scanning subdirs ",e
517 self.session.openWithCallback(self.DeletePlaylistSelected, ChoiceBox, title=_("Please select a playlist to delete..."), list = listpath)
519 def DeletePlaylistSelected(self,path):
521 self.delname = path[1]
522 self.session.openWithCallback(self.deleteConfirmed, MessageBox, _("Do you really want to delete %s?") % (path[1]))
524 def deleteConfirmed(self, confirmed):
526 os_remove(self.delname)
528 def clear_playlist(self):
530 self.playlist.clear()
531 self.switchToFileList()
533 def copyDirectory(self, directory, recursive = True):
534 print "copyDirectory", directory
535 filelist = FileList(directory, useServiceRef = True, isTop = True)
537 for x in filelist.getFileList():
538 if x[0][1] == True: #isDir
540 self.copyDirectory(x[0][0])
542 self.playlist.addFile(x[0][0])
543 self.playlist.updateList()
546 if self.filelist.getServiceRef().type == 4098: # playlist
547 ServiceRef = self.filelist.getServiceRef()
548 extension = ServiceRef.getPath()[ServiceRef.getPath().rfind('.') + 1:]
549 print "extension:", extension
550 if self.playlistparsers.has_key(extension):
551 playlist = self.playlistparsers[extension]()
552 list = playlist.open(ServiceRef.getPath())
554 self.playlist.addFile(x.ref)
556 self.playlist.addFile(self.filelist.getServiceRef())
557 self.playlist.updateList()
558 if len(self.playlist) == 1:
561 def addPlaylistParser(self, parser, extension):
562 self.playlistparsers[extension] = parser
565 next = self.playlist.getCurrentIndex() + 1
566 if next < len(self.playlist):
567 self.changeEntry(next)
569 def nextMarkOrEntry(self):
570 if not self.jumpPreviousNextMark(lambda x: x):
571 next = self.playlist.getCurrentIndex() + 1
572 if next < len(self.playlist):
573 self.changeEntry(next)
577 def previousMarkOrEntry(self):
578 if not self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True):
579 next = self.playlist.getCurrentIndex() - 1
581 self.changeEntry(next)
583 def deleteEntry(self):
584 self.playlist.deleteFile(self.playlist.getSelectionIndex())
585 self.playlist.updateList()
586 if len(self.playlist) == 0:
587 self.switchToFileList()
589 def changeEntry(self, index):
590 self.playlist.setCurrentPlaying(index)
593 def playServiceRefEntry(self, serviceref):
594 serviceRefList = self.playlist.getServiceRefList()
595 for count in range(len(serviceRefList)):
596 if serviceRefList[count] == serviceref:
597 self.changeEntry(count)
600 def xplayEntry(self):
601 if self.currList == "playlist":
605 self.playlist.clear()
606 sel = self.filelist.getSelection()
608 if sel[1]: # can descent
609 # add directory to playlist
610 self.copyDirectory(sel[0])
612 # add files to playlist
613 self.copyDirectory(os_path.dirname(sel[0].getPath()) + "/", recursive = False)
614 if len(self.playlist) > 0:
618 if len(self.playlist.getServiceRefList()):
619 needsInfoUpdate = False
620 currref = self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()]
621 if self.session.nav.getCurrentlyPlayingServiceReference() is None or currref != self.session.nav.getCurrentlyPlayingServiceReference():
622 self.session.nav.playService(self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()])
623 info = eServiceCenter.getInstance().info(currref)
624 description = info and info.getInfoString(currref, iServiceInformation.sDescription) or ""
625 self["title"].setText(description)
626 # display just playing musik on LCD
627 idx = self.playlist.getCurrentIndex()
628 currref = self.playlist.getServiceRefList()[idx]
629 text = currref.getPath()
630 text = text.split('/')[-1]
632 ext = text[-3:].lower()
634 # FIXME: the information if the service contains video (and we should hide our window) should com from the service instead
635 if ext not in ["mp3", "wav", "ogg"]:
638 needsInfoUpdate = True
639 self.summaries.setText(text,1)
641 # get the next two entries
643 if idx < len(self.playlist):
644 currref = self.playlist.getServiceRefList()[idx]
645 text = currref.getPath()
646 text = text.split('/')[-1]
647 self.summaries.setText(text,3)
649 self.summaries.setText(" ",3)
652 if idx < len(self.playlist):
653 currref = self.playlist.getServiceRefList()[idx]
654 text = currref.getPath()
655 text = text.split('/')[-1]
656 self.summaries.setText(text,4)
658 self.summaries.setText(" ",4)
660 idx = self.playlist.getCurrentIndex()
661 currref = self.playlist.getServiceRefList()[idx]
662 text = currref.getPath()
663 ext = text[-3:].lower()
664 if ext not in ["mp3", "wav", "ogg"]:
667 needsInfoUpdate = True
669 self.unPauseService()
670 if needsInfoUpdate == True:
671 self.updateCoverArtPixmap(currref.getPath())
673 pngname = self["coverArt"].default_pixmap
674 self.coverArtFileName = pngname
675 self["coverArt"].instance.setPixmapFromFile(self.coverArtFileName)
676 self.readTitleInformation()
678 def updatedSeekState(self):
679 if self.seekstate == self.SEEK_STATE_PAUSE:
680 self.playlist.pauseFile()
681 elif self.seekstate == self.SEEK_STATE_PLAY:
682 self.playlist.playFile()
683 elif self.isStateForward(self.seekstate):
684 self.playlist.forwardFile()
685 elif self.isStateBackward(self.seekstate):
686 self.playlist.rewindFile()
688 def pauseEntry(self):
693 self.playlist.stopFile()
694 self.session.nav.playService(None)
695 self.updateMusicInformation(clear=True)
698 def unPauseService(self):
699 self.setSeekState(self.SEEK_STATE_PLAY)
702 class MediaPlayerLCDScreen(Screen):
704 <screen position="0,0" size="132,64" title="LCD Text">
705 <widget name="text1" position="4,0" size="132,35" font="Regular;16"/>
706 <widget name="text3" position="4,36" size="132,14" font="Regular;10"/>
707 <widget name="text4" position="4,49" size="132,14" font="Regular;10"/>
710 def __init__(self, session, parent):
711 Screen.__init__(self, session)
712 self["text1"] = Label("Mediaplayer")
713 self["text3"] = Label("")
714 self["text4"] = Label("")
716 def setText(self, text, line):
717 print "lcd set text:", text, line
719 if text[-4:] == ".mp3":
722 text = text + textleer*10
724 self["text1"].setText(text)
726 self["text3"].setText(text)
728 self["text4"].setText(text)
730 def main(session, **kwargs):
731 session.open(MediaPlayer)
733 def menu(menuid, **kwargs):
734 if menuid == "mainmenu":
735 return [(_("Media player"), main, "media_player", 45)]
738 def filescan_open(list, session, **kwargs):
739 from enigma import eServiceReference
741 mp = session.open(MediaPlayer)
743 mp.switchToPlayList()
745 ref = eServiceReference(4097, 0, file.path)
746 mp.playlist.addFile(ref)
748 # TODO: rather play first than last file?
749 mp.playServiceRefEntry(ref)
750 mp.playlist.updateList()
752 def filescan(**kwargs):
753 from Components.Scanner import Scanner, ScanPath
755 Scanner(mimetypes = ["video/mpeg"],
758 ScanPath(path = "", with_subdirs = False),
761 description = "View Movies...",
762 openfnc = filescan_open,
764 Scanner(mimetypes = ["audio/mpeg", "audio/x-wav", "application/ogg"],
767 ScanPath(path = "", with_subdirs = False),
770 description = "Play Music...",
771 openfnc = filescan_open,
775 from Plugins.Plugin import PluginDescriptor
776 def Plugins(**kwargs):
778 PluginDescriptor(name = "MediaPlayer", description = "Play back media files", where = PluginDescriptor.WHERE_MENU, fnc = menu),
779 PluginDescriptor(name = "MediaPlayer", where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)