also scan for video-CD content in directory MPEGAV (capitalized)
[enigma2.git] / lib / python / Plugins / Extensions / MediaPlayer / plugin.py
index 03d7617..afb8772 100644 (file)
@@ -1,20 +1,20 @@
-from os import path as os_path, remove as os_remove, listdir as os_listdir
+from os import path as os_path, remove as os_remove, listdir as os_listdir, popen
 from time import strftime
-from enigma import eTimer, iPlayableService, eServiceCenter, iServiceInformation
+from enigma import iPlayableService, eTimer, eServiceCenter, iServiceInformation
 from Screens.Screen import Screen
 from Screens.MessageBox import MessageBox
 from Screens.InputBox import InputBox
 from Components.ActionMap import NumberActionMap, HelpableActionMap
 from Components.Label import Label
-from Components.Pixmap import Pixmap
+from Components.Pixmap import Pixmap,MultiPixmap
 from Components.Label import Label
 from Components.FileList import FileList
 from Components.MediaPlayer import PlayList
 from Tools.Directories import resolveFilename, SCOPE_CONFIG, SCOPE_PLAYLIST, SCOPE_SKIN_IMAGE
 from Components.ServicePosition import ServicePositionGauge
-from Components.ServiceEventTracker import ServiceEventTracker
+from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
 from Components.Playlist import PlaylistIOInternal, PlaylistIOM3U, PlaylistIOPLS
-from Screens.InfoBarGenerics import InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSupport, InfoBarNotifications
+from Screens.InfoBarGenerics import InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSupport, InfoBarNotifications, InfoBarSubtitleSupport
 from ServiceReference import ServiceReference
 from Screens.ChoiceBox import ChoiceBox
 from Screens.HelpMenu import HelpableScreen
@@ -31,7 +31,7 @@ class MyPlayList(PlayList):
                self.oldCurrPlaying = -1
 
 class MediaPixmap(Pixmap):
-       def applySkin(self, desktop):
+       def applySkin(self, desktop, screen):
                self.default_pixmap = None
                if self.skinAttributes is not None:
                        for (attrib, value) in self.skinAttributes:
@@ -39,10 +39,10 @@ class MediaPixmap(Pixmap):
                                        self.default_pixmap = value
                                        break
                if self.default_pixmap is None:
-                       self.default_pixmap = resolveFilename(SCOPE_SKIN_IMAGE, "no_coverArt.png")
-               return Pixmap.applySkin(self, desktop)
+                       self.default_pixmap = resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/no_coverArt.png")
+               return Pixmap.applySkin(self, desktop, screen)
 
-class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSupport, InfoBarNotifications, HelpableScreen):
+class MediaPlayer(Screen, InfoBarBase, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSupport, InfoBarNotifications, InfoBarSubtitleSupport, HelpableScreen):
        ALLOW_SUSPEND = True
        ENABLE_RESUME_SUPPORT = True
 
@@ -51,6 +51,8 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                InfoBarAudioSelection.__init__(self)
                InfoBarCueSheetSupport.__init__(self, actionmap = "MediaPlayerCueSheetActions")
                InfoBarNotifications.__init__(self)
+               InfoBarBase.__init__(self)
+               InfoBarSubtitleSupport.__init__(self)
                HelpableScreen.__init__(self)
                self.summary = None
                self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
@@ -62,7 +64,7 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                self.addPlaylistParser(PlaylistIOInternal, "e2pls")
 
                # 'None' is magic to start at the list of mountpoints
-               self.filelist = FileList(None, matchingPattern = "(?i)^.*\.(mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob)", useServiceRef = True)
+               self.filelist = FileList(None, matchingPattern = "(?i)^.*\.(mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob|avi|mkv|dat|flac)", useServiceRef = True, additionalExtensions = "4098:m3u 4098:e2pls 4098:pls")
                self["filelist"] = self.filelist
 
                self.playlist = MyPlayList()
@@ -86,7 +88,9 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                self["genretext"] = Label(_("Genre:"))
                self["genre"] = Label("")
                self["coverArt"] = MediaPixmap()
+               self["repeat"] = MultiPixmap()
 
+               self.repeat = False
                self.seek_target = None
 
                class MoviePlayerActionMap(NumberActionMap):
@@ -110,8 +114,8 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                                "play": (self.xplayEntry, _("play entry")),
                                "pause": (self.pauseEntry, _("pause")),
                                "stop": (self.stopEntry, _("stop entry")),
-                               "previous": (self.previousEntry, _("play previous playlist entry")),
-                               "next": (self.nextEntry, _("play next playlist entry")),
+                               "previous": (self.previousMarkOrEntry, _("play from previous mark or playlist entry")),
+                               "next": (self.nextMarkOrEntry, _("play from next mark or playlist entry")),
                                "menu": (self.showMenu, _("menu")),
                                "skipListbegin": (self.skip_listbegin, _("jump to listbegin")),
                                "skipListend": (self.skip_listend, _("jump to listend")),
@@ -120,6 +124,7 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                                "delete": (self.deletePlaylistEntry, _("delete playlist entry")),
                                "shift_stop": (self.clear_playlist, _("clear playlist")),
                                "shift_record": (self.playlist.PlayListShuffle, _("shuffle playlist")),
+                               "subtitles": (self.subtitleSelection, _("Subtitle selection")),
                        }, -2)
 
                self["InfobarEPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
@@ -146,14 +151,6 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
 
                InfoBarSeek.__init__(self, actionmap = "MediaPlayerSeekActions")
 
-               self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
-                       {
-                               #iPlayableService.evStart: self.__serviceStarted,
-                               #iPlayableService.evSeekableStatusChanged: InfoBarSeek.__seekableStatusChanged,
-
-                               iPlayableService.evEOF: self.__evEOF,
-                       })
-
                self.onClose.append(self.delMPTimer)
                self.onClose.append(self.__onClose)
 
@@ -168,6 +165,9 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                self.currList = "filelist"
 
                self.coverArtFileName = ""
+               self.isAudioCD = False
+               self.AudioCD_albuminfo = {}
+               self.savePlaylistOnExit = True
 
                self.playlistIOInternal = PlaylistIOInternal()
                list = self.playlistIOInternal.open(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
@@ -176,6 +176,13 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                                self.playlist.addFile(x.ref)
                        self.playlist.updateList()
 
+               self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
+                       {
+                               iPlayableService.evUpdatedInfo: self.__evUpdatedInfo,
+                               iPlayableService.evUser+11: self.__evDecodeError,
+                               iPlayableService.evUser+12: self.__evPluginError
+                       })
+
        def doNothing(self):
                pass
 
@@ -190,18 +197,42 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                        self.playlistIOInternal.clear()
                        for x in self.playlist.list:
                                self.playlistIOInternal.addService(ServiceReference(x[0]))
-                       self.playlistIOInternal.save(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
+                       if self.savePlaylistOnExit:
+                               self.playlistIOInternal.save(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
                        self.close()
 
        def checkSkipShowHideLock(self):
                self.updatedSeekState()
 
-       def __evEOF(self):
-               self.nextEntry()
+       def doEofInternal(self, playing):
+               if playing:
+                       self.nextEntry()
+               else:
+                       self.show()
 
        def __onClose(self):
                self.session.nav.playService(self.oldService)
 
+       def __evUpdatedInfo(self):
+               currPlay = self.session.nav.getCurrentService()
+               currenttitle = currPlay.info().getInfo(iServiceInformation.sCurrentTitle)
+               totaltitles = currPlay.info().getInfo(iServiceInformation.sTotalTitles)
+               sTitle = currPlay.info().getInfoString(iServiceInformation.sTitle)
+               print "[__evUpdatedInfo] title %d of %d (%s)" % (currenttitle, totaltitles, sTitle)
+               self.readTitleInformation()
+
+       def __evDecodeError(self):
+               currPlay = self.session.nav.getCurrentService()
+               sVideoType = currPlay.info().getInfoString(iServiceInformation.sVideoType)
+               print "[__evDecodeError] video-codec %s can't be decoded by hardware" % (sVideoType)
+               self.session.open(MessageBox, _("This Dreambox can't decode %s video streams!") % sVideoType, type = MessageBox.TYPE_INFO,timeout = 20 )
+
+       def __evPluginError(self):
+               currPlay = self.session.nav.getCurrentService()
+               message = currPlay.info().getInfoString(iServiceInformation.sUser+12)
+               print "[__evPluginError]" , message
+               self.session.open(MessageBox, ("GStreamer Error: missing %s") % message, type = MessageBox.TYPE_INFO,timeout = 20 )
+
        def delMPTimer(self):
                del self.rightKeyTimer
                del self.leftKeyTimer
@@ -209,15 +240,29 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
        def readTitleInformation(self):
                currPlay = self.session.nav.getCurrentService()
                if currPlay is not None:
-                       stitle = currPlay.info().getInfoString(iServiceInformation.sTitle)
-                       if stitle == "":
-                               stitle = currPlay.info().getName().split('/')[-1]
-
-                       self.updateMusicInformation( artist = currPlay.info().getInfoString(iServiceInformation.sArtist),
-                                                                                title = stitle,
-                                                                                album = currPlay.info().getInfoString(iServiceInformation.sAlbum),
-                                                                                genre = currPlay.info().getInfoString(iServiceInformation.sGenre),
-                                                                                clear = True)
+                       sTitle = currPlay.info().getInfoString(iServiceInformation.sTitle)
+                       sAlbum = currPlay.info().getInfoString(iServiceInformation.sAlbum)
+                       sGenre = currPlay.info().getInfoString(iServiceInformation.sGenre)
+                       sArtist = currPlay.info().getInfoString(iServiceInformation.sArtist)
+                       sYear = ""
+
+                       if sTitle == "":
+                               if not self.isAudioCD:
+                                       sTitle = currPlay.info().getName().split('/')[-1]
+                               else:
+                                       sTitle = self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()].getName()
+
+                       if self.AudioCD_albuminfo:
+                               if sAlbum == "" and "title" in self.AudioCD_albuminfo:
+                                       sAlbum = self.AudioCD_albuminfo["title"]
+                               if sGenre == "" and "genre" in self.AudioCD_albuminfo:
+                                       sGenre = self.AudioCD_albuminfo["genre"]
+                               if sArtist == "" and "artist" in self.AudioCD_albuminfo:
+                                       sArtist = self.AudioCD_albuminfo["artist"]
+                               if "year" in self.AudioCD_albuminfo:
+                                       sYear = self.AudioCD_albuminfo["year"]
+
+                       self.updateMusicInformation( sArtist, sTitle, sAlbum, sYear, sGenre, clear = True )
                else:
                        self.updateMusicInformation()
 
@@ -306,6 +351,13 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
        def hideAfterResume(self):
                self.hide()
 
+       def getIdentifier(self, ref):
+               if self.isAudioCD:
+                       return ref.getName()
+               else:
+                       text = ref.getPath()
+                       return text.split('/')[-1]
+
        # FIXME: maybe this code can be optimized 
        def updateCurrentInfo(self):
                text = ""
@@ -353,16 +405,14 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                        if t is None:
                                return
                        #display current selected entry on LCD
-                       text = t.getPath()
-                       text = text.split('/')[-1]
+                       text = self.getIdentifier(t)
                        self.summaries.setText(text,1)
                        self["currenttext"].setText(text)
                        idx = self.playlist.getSelectionIndex()
                        idx += 1
                        if idx < len(self.playlist):
                                currref = self.playlist.getServiceRefList()[idx]
-                               text = currref.getPath()
-                               text = text.split('/')[-1]
+                               text = self.getIdentifier(currref)
                                self.summaries.setText(text,3)
                        else:
                                self.summaries.setText(" ",3)
@@ -370,8 +420,7 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                        idx += 1
                        if idx < len(self.playlist):
                                currref = self.playlist.getServiceRefList()[idx]
-                               text = currref.getPath()
-                               text = text.split('/')[-1]
+                               text = self.getIdentifier(currref)
                                self.summaries.setText(text,4)
                        else:
                                self.summaries.setText(" ",4)
@@ -407,6 +456,7 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                menu.append((_("save playlist"), "saveplaylist"));
                menu.append((_("load playlist"), "loadplaylist"));
                menu.append((_("delete saved playlist"), "deleteplaylist"));
+               menu.append((_("repeat playlist"), "repeat"));
                self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu)
 
        def menuCallback(self, choice):
@@ -442,7 +492,13 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                        self.delete_saved_playlist()
                elif choice[1] == "shuffle":
                        self.playlist.PlayListShuffle()
-
+               elif choice[1] == "repeat":
+                       if self.repeat == True:
+                               self.repeat = False
+                               self["repeat"].setPixmapNum(0)
+                       else:
+                               self.repeat = True
+                               self["repeat"].setPixmapNum(1)
 
        def showEventInformation(self):
                from Screens.EventView import EventViewSimple
@@ -569,11 +625,23 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                next = self.playlist.getCurrentIndex() + 1
                if next < len(self.playlist):
                        self.changeEntry(next)
+               elif ( len(self.playlist) > 0 ) and ( self.repeat == True ):
+                       self.stopEntry()
+                       self.changeEntry(0)
 
-       def previousEntry(self):
-               next = self.playlist.getCurrentIndex() - 1
-               if next >= 0:
-                       self.changeEntry(next)
+       def nextMarkOrEntry(self):
+               if not self.jumpPreviousNextMark(lambda x: x):
+                       next = self.playlist.getCurrentIndex() + 1
+                       if next < len(self.playlist):
+                               self.changeEntry(next)
+                       else:
+                               self.doSeek(-1)
+
+       def previousMarkOrEntry(self):
+               if not self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True):
+                       next = self.playlist.getCurrentIndex() - 1
+                       if next >= 0:
+                               self.changeEntry(next)
 
        def deleteEntry(self):
                self.playlist.deleteFile(self.playlist.getSelectionIndex())
@@ -621,13 +689,12 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                                # display just playing musik on LCD
                                idx = self.playlist.getCurrentIndex()
                                currref = self.playlist.getServiceRefList()[idx]
-                               text = currref.getPath()
-                               text = text.split('/')[-1]
+                               text = self.getIdentifier(currref)
                                text = ">"+text
-                               ext = text[-3:].lower()
+                               ext = text[-4:].lower()
 
                                # FIXME: the information if the service contains video (and we should hide our window) should com from the service instead 
-                               if ext not in ["mp3", "wav", "ogg"]:
+                               if ext not in [".mp3", ".wav", ".ogg", "flac"] and not self.isAudioCD:
                                        self.hide()
                                else:
                                        needsInfoUpdate = True
@@ -637,8 +704,7 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                                idx += 1
                                if idx < len(self.playlist):
                                        currref = self.playlist.getServiceRefList()[idx]
-                                       text = currref.getPath()
-                                       text = text.split('/')[-1]
+                                       text = self.getIdentifier(currref)
                                        self.summaries.setText(text,3)
                                else:
                                        self.summaries.setText(" ",3)
@@ -646,8 +712,7 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                                idx += 1
                                if idx < len(self.playlist):
                                        currref = self.playlist.getServiceRefList()[idx]
-                                       text = currref.getPath()
-                                       text = text.split('/')[-1]
+                                       text = self.getIdentifier(currref)
                                        self.summaries.setText(text,4)
                                else:
                                        self.summaries.setText(" ",4)
@@ -655,8 +720,8 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                                idx = self.playlist.getCurrentIndex()
                                currref = self.playlist.getServiceRefList()[idx]
                                text = currref.getPath()
-                               ext = text[-3:].lower()
-                               if ext not in ["mp3", "wav", "ogg"]:
+                               ext = text[-4:].lower()
+                               if ext not in [".mp3", ".wav", ".ogg", "flac"] and not self.isAudioCD:
                                        self.hide()
                                else:
                                        needsInfoUpdate = True
@@ -675,26 +740,17 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                        self.playlist.pauseFile()
                elif self.seekstate == self.SEEK_STATE_PLAY:
                        self.playlist.playFile()
-               elif self.seekstate in ( self.SEEK_STATE_FF_2X,
-                                                                self.SEEK_STATE_FF_4X,
-                                                                self.SEEK_STATE_FF_8X,
-                                                                self.SEEK_STATE_FF_16X,
-                                                                self.SEEK_STATE_FF_32X,
-                                                                self.SEEK_STATE_FF_48X,
-                                                                self.SEEK_STATE_FF_64X,
-                                                                self.SEEK_STATE_FF_128X):
+               elif self.isStateForward(self.seekstate):
                        self.playlist.forwardFile()
-               elif self.seekstate in ( self.SEEK_STATE_BACK_8X,
-                                                                self.SEEK_STATE_BACK_16X,
-                                                                self.SEEK_STATE_BACK_32X,
-                                                                self.SEEK_STATE_BACK_48X,
-                                                                self.SEEK_STATE_BACK_64X,
-                                                                self.SEEK_STATE_BACK_128X):
+               elif self.isStateBackward(self.seekstate):
                        self.playlist.rewindFile()
 
        def pauseEntry(self):
                self.pauseService()
-               self.show()
+               if self.seekstate == self.SEEK_STATE_PAUSE:
+                       self.show()
+               else:
+                       self.hide()
 
        def stopEntry(self):
                self.playlist.stopFile()
@@ -704,7 +760,10 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
 
        def unPauseService(self):
                self.setSeekState(self.SEEK_STATE_PLAY)
-
+               
+       def subtitleSelection(self):
+               from Screens.Subtitles import Subtitles
+               self.session.open(Subtitles)            
 
 class MediaPlayerLCDScreen(Screen):
        skin = """
@@ -735,7 +794,7 @@ class MediaPlayerLCDScreen(Screen):
                        self["text4"].setText(text)
 
 def main(session, **kwargs):
-        session.open(MediaPlayer)
+       session.open(MediaPlayer)
 
 def menu(menuid, **kwargs):
        if menuid == "mainmenu":
@@ -746,20 +805,43 @@ def filescan_open(list, session, **kwargs):
        from enigma import eServiceReference
 
        mp = session.open(MediaPlayer)
+       mp.playlist.clear()
+       mp.savePlaylistOnExit = False
+
+       for file in list:
+               if file.mimetype == "video/MP2T":
+                       stype = 1
+               else:
+                       stype = 4097
+               ref = eServiceReference(stype, 0, file.path)
+               mp.playlist.addFile(ref)
 
+       mp.changeEntry(0)
        mp.switchToPlayList()
+
+def audioCD_open(list, session, **kwargs):
+       from enigma import eServiceReference
+
+       mp = session.open(MediaPlayer)
+
+       mp.playlist.clear()
+       mp.savePlaylistOnExit = False
+       mp.isAudioCD = True
+
        for file in list:
                ref = eServiceReference(4097, 0, file.path)
                mp.playlist.addFile(ref)
+       from Plugins.Extensions.CDInfo.plugin import Query
+       cdinfo = Query(mp)
+       cdinfo.scan()
 
-       # TODO: rather play first than last file?
-       mp.playServiceRefEntry(ref)
-       mp.playlist.updateList()
+       mp.changeEntry(0)
+       mp.switchToPlayList()
 
 def filescan(**kwargs):
        from Components.Scanner import Scanner, ScanPath
-       return [
-               Scanner(mimetypes = ["video/mpeg"],
+       mediatypes = [
+               Scanner(mimetypes = ["video/mpeg", "video/MP2T", "video/x-msvideo"],
                        paths_to_scan =
                                [
                                        ScanPath(path = "", with_subdirs = False),
@@ -768,7 +850,17 @@ def filescan(**kwargs):
                        description = "View Movies...",
                        openfnc = filescan_open,
                ),
-               Scanner(mimetypes = ["audio/mpeg", "audio/x-wav", "application/ogg"],
+               Scanner(mimetypes = ["video/x-vcd"],
+                       paths_to_scan =
+                               [
+                                       ScanPath(path = "mpegav", with_subdirs = False),
+                                       ScanPath(path = "MPEGAV", with_subdirs = False),
+                               ],
+                       name = "Video CD",
+                       description = "View Video CD...",
+                       openfnc = filescan_open,
+               ),
+               Scanner(mimetypes = ["audio/mpeg", "audio/x-wav", "application/ogg", "audio/x-flac"],
                        paths_to_scan =
                                [
                                        ScanPath(path = "", with_subdirs = False),
@@ -776,8 +868,22 @@ def filescan(**kwargs):
                        name = "Music",
                        description = "Play Music...",
                        openfnc = filescan_open,
-               )
-       ]
+               )]
+       try:
+               from Plugins.Extensions.CDInfo.plugin import Query
+               mediatypes.append(
+               Scanner(mimetypes = ["audio/x-cda"],
+                       paths_to_scan =
+                               [
+                                       ScanPath(path = "", with_subdirs = False),
+                               ],
+                       name = "Audio-CD",
+                       description = "Play Audio-CD...",
+                       openfnc = audioCD_open,
+               ))
+               return mediatypes
+       except ImportError:
+               return mediatypes
 
 from Plugins.Plugin import PluginDescriptor
 def Plugins(**kwargs):