Merge branch '219_negative_ac3_pcm_delay'
[enigma2.git] / lib / python / Plugins / Extensions / MediaPlayer / plugin.py
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 iPlayableService, eTimer, eServiceCenter, iServiceInformation, ePicLoad
4 from ServiceReference import ServiceReference
5 from Screens.Screen import Screen
6 from Screens.HelpMenu import HelpableScreen
7 from Screens.MessageBox import MessageBox
8 from Screens.InputBox import InputBox
9 from Screens.ChoiceBox import ChoiceBox
10 from Screens.InfoBarGenerics import InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSupport, InfoBarNotifications, InfoBarSubtitleSupport
11 from Components.ActionMap import NumberActionMap, HelpableActionMap
12 from Components.Label import Label
13 from Components.Pixmap import Pixmap,MultiPixmap
14 from Components.FileList import FileList
15 from Components.MediaPlayer import PlayList
16 from Components.ServicePosition import ServicePositionGauge
17 from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
18 from Components.Playlist import PlaylistIOInternal, PlaylistIOM3U, PlaylistIOPLS
19 from Components.AVSwitch import AVSwitch
20 from Components.Harddisk import harddiskmanager
21 from Components.config import config
22 from Tools.Directories import fileExists, pathExists, resolveFilename, SCOPE_CONFIG, SCOPE_PLAYLIST, SCOPE_SKIN_IMAGE
23 from settings import MediaPlayerSettings
24 import random
25
26 class MyPlayList(PlayList):
27         def __init__(self):
28                 PlayList.__init__(self)
29
30         def PlayListShuffle(self):
31                 random.shuffle(self.list)
32                 self.l.setList(self.list)
33                 self.currPlaying = -1
34                 self.oldCurrPlaying = -1
35
36 class MediaPixmap(Pixmap):
37         def __init__(self):
38                 Pixmap.__init__(self)
39                 self.coverArtFileName = ""
40                 self.picload = ePicLoad()
41                 self.picload.PictureData.get().append(self.paintCoverArtPixmapCB)
42                 self.coverFileNames = ["folder.png", "folder.jpg"]
43
44         def applySkin(self, desktop, screen):
45                 from Tools.LoadPixmap import LoadPixmap
46                 noCoverFile = None
47                 if self.skinAttributes is not None:
48                         for (attrib, value) in self.skinAttributes:
49                                 if attrib == "pixmap":
50                                         noCoverFile = value
51                                         break
52                 if noCoverFile is None:
53                         noCoverFile = resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/no_coverArt.png")
54                 self.noCoverPixmap = LoadPixmap(noCoverFile)
55                 return Pixmap.applySkin(self, desktop, screen)
56
57         def onShow(self):
58                 Pixmap.onShow(self)
59                 sc = AVSwitch().getFramebufferScale()
60                 #0=Width 1=Height 2=Aspect 3=use_cache 4=resize_type 5=Background(#AARRGGBB)
61                 self.picload.setPara((self.instance.size().width(), self.instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
62
63         def paintCoverArtPixmapCB(self, picInfo=None):
64                 ptr = self.picload.getData()
65                 if ptr != None:
66                         self.instance.setPixmap(ptr.__deref__())
67
68         def updateCoverArt(self, path):
69                 while not path.endswith("/"):
70                         path = path[:-1]
71                 new_coverArtFileName = None
72                 for filename in self.coverFileNames:
73                         if fileExists(path + filename):
74                                 new_coverArtFileName = path + filename
75                 if self.coverArtFileName != new_coverArtFileName:
76                         self.coverArtFileName = new_coverArtFileName
77                         if new_coverArtFileName:
78                                 self.picload.startDecode(self.coverArtFileName)
79                         else:
80                                 self.showDefaultCover()
81
82         def showDefaultCover(self):
83                 self.instance.setPixmap(self.noCoverPixmap)
84
85         def embeddedCoverArt(self):
86                 print "[embeddedCoverArt] found"
87                 self.coverArtFileName = "/tmp/.id3coverart"
88                 self.picload.startDecode(self.coverArtFileName)
89
90 class MediaPlayer(Screen, InfoBarBase, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSupport, InfoBarNotifications, InfoBarSubtitleSupport, HelpableScreen):
91         ALLOW_SUSPEND = True
92         ENABLE_RESUME_SUPPORT = True
93
94         def __init__(self, session, args = None):
95                 Screen.__init__(self, session)
96                 InfoBarAudioSelection.__init__(self)
97                 InfoBarCueSheetSupport.__init__(self, actionmap = "MediaPlayerCueSheetActions")
98                 InfoBarNotifications.__init__(self)
99                 InfoBarBase.__init__(self)
100                 InfoBarSubtitleSupport.__init__(self)
101                 HelpableScreen.__init__(self)
102                 self.summary = None
103                 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
104                 self.session.nav.stopService()
105
106                 self.playlistparsers = {}
107                 self.addPlaylistParser(PlaylistIOM3U, "m3u")
108                 self.addPlaylistParser(PlaylistIOPLS, "pls")
109                 self.addPlaylistParser(PlaylistIOInternal, "e2pls")
110
111                 # 'None' is magic to start at the list of mountpoints
112                 defaultDir = config.mediaplayer.defaultDir.getValue()
113                 self.filelist = FileList(defaultDir, matchingPattern = "(?i)^.*\.(mp2|mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob|avi|divx|m4v|mkv|mp4|m4a|dat|flac|mov)", useServiceRef = True, additionalExtensions = "4098:m3u 4098:e2pls 4098:pls")
114                 self["filelist"] = self.filelist
115
116                 self.playlist = MyPlayList()
117                 self.is_closing = False
118                 self.delname = ""
119                 self["playlist"] = self.playlist
120
121                 self["PositionGauge"] = ServicePositionGauge(self.session.nav)
122
123                 self["currenttext"] = Label("")
124
125                 self["artisttext"] = Label(_("Artist")+':')
126                 self["artist"] = Label("")
127                 self["titletext"] = Label(_("Title")+':')
128                 self["title"] = Label("")
129                 self["albumtext"] = Label(_("Album")+':')
130                 self["album"] = Label("")
131                 self["yeartext"] = Label(_("Year")+':')
132                 self["year"] = Label("")
133                 self["genretext"] = Label(_("Genre")+':')
134                 self["genre"] = Label("")
135                 self["coverArt"] = MediaPixmap()
136                 self["repeat"] = MultiPixmap()
137
138                 self.seek_target = None
139
140                 from Plugins.SystemPlugins.Hotplug.plugin import hotplugNotifier
141                 hotplugNotifier.append(self.hotplugCB)
142
143                 class MoviePlayerActionMap(NumberActionMap):
144                         def __init__(self, player, contexts = [ ], actions = { }, prio=0):
145                                 NumberActionMap.__init__(self, contexts, actions, prio)
146                                 self.player = player
147
148                         def action(self, contexts, action):
149                                 self.player.show()
150                                 return NumberActionMap.action(self, contexts, action)
151
152                 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions", 
153                         {
154                                 "ok": (self.ok, _("add file to playlist")),
155                                 "cancel": (self.exit, _("exit mediaplayer")),
156                         }, -2)
157
158                 self["MediaPlayerActions"] = HelpableActionMap(self, "MediaPlayerActions", 
159                         {
160                                 "play": (self.xplayEntry, _("play entry")),
161                                 "pause": (self.pauseEntry, _("pause")),
162                                 "stop": (self.stopEntry, _("stop entry")),
163                                 "previous": (self.previousMarkOrEntry, _("play from previous mark or playlist entry")),
164                                 "next": (self.nextMarkOrEntry, _("play from next mark or playlist entry")),
165                                 "menu": (self.showMenu, _("menu")),
166                                 "skipListbegin": (self.skip_listbegin, _("jump to listbegin")),
167                                 "skipListend": (self.skip_listend, _("jump to listend")),
168                                 "prevBouquet": (self.switchToPlayList, _("switch to playlist")),
169                                 "nextBouquet": (self.switchToFileList, _("switch to filelist")),
170                                 "delete": (self.deletePlaylistEntry, _("delete playlist entry")),
171                                 "shift_stop": (self.clear_playlist, _("clear playlist")),
172                                 "shift_record": (self.playlist.PlayListShuffle, _("shuffle playlist")),
173                                 "subtitles": (self.subtitleSelection, _("Subtitle selection")),
174                         }, -2)
175
176                 self["InfobarEPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
177                         {
178                                 "showEventInfo": (self.showEventInformation, _("show event details")),
179                         })
180
181                 self["actions"] = MoviePlayerActionMap(self, ["DirectionActions"], 
182                 {
183                         "right": self.rightDown,
184                         "rightRepeated": self.doNothing,
185                         "rightUp": self.rightUp,
186                         "left": self.leftDown,
187                         "leftRepeated": self.doNothing,
188                         "leftUp": self.leftUp,
189
190                         "up": self.up,
191                         "upRepeated": self.up,
192                         "upUp": self.doNothing,
193                         "down": self.down,
194                         "downRepeated": self.down,
195                         "downUp": self.doNothing,
196                 }, -2)
197
198                 InfoBarSeek.__init__(self, actionmap = "MediaPlayerSeekActions")
199
200                 self.onClose.append(self.delMPTimer)
201                 self.onClose.append(self.__onClose)
202
203                 self.righttimer = False
204                 self.rightKeyTimer = eTimer()
205                 self.rightKeyTimer.callback.append(self.rightTimerFire)
206
207                 self.lefttimer = False
208                 self.leftKeyTimer = eTimer()
209                 self.leftKeyTimer.callback.append(self.leftTimerFire)
210
211                 self.currList = "filelist"
212                 self.isAudioCD = False
213                 self.AudioCD_albuminfo = {}
214                 self.cdAudioTrackFiles = []
215                 self.onShown.append(self.applySettings)
216
217                 self.playlistIOInternal = PlaylistIOInternal()
218                 list = self.playlistIOInternal.open(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
219                 if list:
220                         for x in list:
221                                 self.playlist.addFile(x.ref)
222                         self.playlist.updateList()
223
224                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
225                         {
226                                 iPlayableService.evUpdatedInfo: self.__evUpdatedInfo,
227                                 iPlayableService.evUser+10: self.__evAudioDecodeError,
228                                 iPlayableService.evUser+11: self.__evVideoDecodeError,
229                                 iPlayableService.evUser+12: self.__evPluginError,
230                                 iPlayableService.evUser+13: self["coverArt"].embeddedCoverArt
231                         })
232
233         def doNothing(self):
234                 pass
235
236         def createSummary(self):
237                 return MediaPlayerLCDScreen
238
239         def exit(self):
240                 self.playlistIOInternal.clear()
241                 for x in self.playlist.list:
242                         self.playlistIOInternal.addService(ServiceReference(x[0]))
243                 if self.savePlaylistOnExit:
244                         try:
245                                 self.playlistIOInternal.save(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
246                         except IOError:
247                                 print "couldn't save playlist.e2pls"
248                 if config.mediaplayer.saveDirOnExit.getValue():
249                         config.mediaplayer.defaultDir.setValue(self.filelist.getCurrentDirectory())
250                         config.mediaplayer.defaultDir.save()
251                 from Plugins.SystemPlugins.Hotplug.plugin import hotplugNotifier
252                 hotplugNotifier.remove(self.hotplugCB)
253                 del self["coverArt"].picload
254                 self.close()
255
256         def checkSkipShowHideLock(self):
257                 self.updatedSeekState()
258
259         def doEofInternal(self, playing):
260                 if playing:
261                         self.nextEntry()
262                 else:
263                         self.show()
264
265         def __onClose(self):
266                 self.session.nav.playService(self.oldService)
267
268         def __evUpdatedInfo(self):
269                 currPlay = self.session.nav.getCurrentService()
270                 sTagTrackNumber = currPlay.info().getInfo(iServiceInformation.sTagTrackNumber)
271                 sTagTrackCount = currPlay.info().getInfo(iServiceInformation.sTagTrackCount)
272                 sTagTitle = currPlay.info().getInfoString(iServiceInformation.sTagTitle)
273                 print "[__evUpdatedInfo] title %d of %d (%s)" % (sTagTrackNumber, sTagTrackCount, sTagTitle)
274                 self.readTitleInformation()
275
276         def __evAudioDecodeError(self):
277                 currPlay = self.session.nav.getCurrentService()
278                 sTagAudioCodec = currPlay.info().getInfoString(iServiceInformation.sTagAudioCodec)
279                 print "[__evAudioDecodeError] audio-codec %s can't be decoded by hardware" % (sTagAudioCodec)
280                 self.session.open(MessageBox, _("This Dreambox can't decode %s streams!") % sTagAudioCodec, type = MessageBox.TYPE_INFO,timeout = 20 )
281
282         def __evVideoDecodeError(self):
283                 currPlay = self.session.nav.getCurrentService()
284                 sTagVideoCodec = currPlay.info().getInfoString(iServiceInformation.sTagVideoCodec)
285                 print "[__evVideoDecodeError] video-codec %s can't be decoded by hardware" % (sTagVideoCodec)
286                 self.session.open(MessageBox, _("This Dreambox can't decode %s streams!") % sTagVideoCodec, type = MessageBox.TYPE_INFO,timeout = 20 )
287
288         def __evPluginError(self):
289                 currPlay = self.session.nav.getCurrentService()
290                 message = currPlay.info().getInfoString(iServiceInformation.sUser+12)
291                 print "[__evPluginError]" , message
292                 self.session.open(MessageBox, message, type = MessageBox.TYPE_INFO,timeout = 20 )
293
294         def delMPTimer(self):
295                 del self.rightKeyTimer
296                 del self.leftKeyTimer
297
298         def readTitleInformation(self):
299                 currPlay = self.session.nav.getCurrentService()
300                 if currPlay is not None:
301                         sTitle = currPlay.info().getInfoString(iServiceInformation.sTagTitle)
302                         sAlbum = currPlay.info().getInfoString(iServiceInformation.sTagAlbum)
303                         sGenre = currPlay.info().getInfoString(iServiceInformation.sTagGenre)
304                         sArtist = currPlay.info().getInfoString(iServiceInformation.sTagArtist)
305                         sYear = currPlay.info().getInfoString(iServiceInformation.sTagDate)
306
307                         if sTitle == "":
308                                 if not self.isAudioCD:
309                                         sTitle = currPlay.info().getName().split('/')[-1]
310                                 else:
311                                         sTitle = self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()].getName()
312
313                         if self.AudioCD_albuminfo:
314                                 if sAlbum == "" and "title" in self.AudioCD_albuminfo:
315                                         sAlbum = self.AudioCD_albuminfo["title"]
316                                 if sGenre == "" and "genre" in self.AudioCD_albuminfo:
317                                         sGenre = self.AudioCD_albuminfo["genre"]
318                                 if sArtist == "" and "artist" in self.AudioCD_albuminfo:
319                                         sArtist = self.AudioCD_albuminfo["artist"]
320                                 if "year" in self.AudioCD_albuminfo:
321                                         sYear = self.AudioCD_albuminfo["year"]
322
323                         self.updateMusicInformation( sArtist, sTitle, sAlbum, sYear, sGenre, clear = True )
324                 else:
325                         self.updateMusicInformation()
326
327         def updateMusicInformation(self, artist = "", title = "", album = "", year = "", genre = "", clear = False):
328                 self.updateSingleMusicInformation("artist", artist, clear)
329                 self.updateSingleMusicInformation("title", title, clear)
330                 self.updateSingleMusicInformation("album", album, clear)
331                 self.updateSingleMusicInformation("year", year, clear)
332                 self.updateSingleMusicInformation("genre", genre, clear)
333
334         def updateSingleMusicInformation(self, name, info, clear):
335                 if info != "" or clear:
336                         if self[name].getText() != info:
337                                 self[name].setText(info)
338
339         def leftDown(self):
340                 self.lefttimer = True
341                 self.leftKeyTimer.start(1000)
342
343         def rightDown(self):
344                 self.righttimer = True
345                 self.rightKeyTimer.start(1000)
346
347         def leftUp(self):
348                 if self.lefttimer:
349                         self.leftKeyTimer.stop()
350                         self.lefttimer = False
351                         self[self.currList].pageUp()
352                         self.updateCurrentInfo()
353
354         def rightUp(self):
355                 if self.righttimer:
356                         self.rightKeyTimer.stop()
357                         self.righttimer = False
358                         self[self.currList].pageDown()
359                         self.updateCurrentInfo()
360
361         def leftTimerFire(self):
362                 self.leftKeyTimer.stop()
363                 self.lefttimer = False
364                 self.switchToFileList()
365
366         def rightTimerFire(self):
367                 self.rightKeyTimer.stop()
368                 self.righttimer = False
369                 self.switchToPlayList()
370
371         def switchToFileList(self):
372                 self.currList = "filelist"
373                 self.filelist.selectionEnabled(1)
374                 self.playlist.selectionEnabled(0)
375                 self.updateCurrentInfo()
376
377         def switchToPlayList(self):
378                 if len(self.playlist) != 0:
379                         self.currList = "playlist"
380                         self.filelist.selectionEnabled(0)
381                         self.playlist.selectionEnabled(1)
382                         self.updateCurrentInfo()
383
384         def up(self):
385                 self[self.currList].up()
386                 self.updateCurrentInfo()
387
388         def down(self):
389                 self[self.currList].down()
390                 self.updateCurrentInfo()
391
392         def showAfterSeek(self):
393                 pass
394
395         def showAfterCuesheetOperation(self):
396                 self.show()
397
398         def hideAfterResume(self):
399                 self.hide()
400
401         def getIdentifier(self, ref):
402                 if self.isAudioCD:
403                         return ref.getName()
404                 else:
405                         text = ref.getPath()
406                         return text.split('/')[-1]
407
408         # FIXME: maybe this code can be optimized 
409         def updateCurrentInfo(self):
410                 text = ""
411                 if self.currList == "filelist":
412                         idx = self.filelist.getSelectionIndex()
413                         r = self.filelist.list[idx]
414                         text = r[1][7]
415                         if r[0][1] == True:
416                                 if len(text) < 2:
417                                         text += " "
418                                 if text[:2] != "..":
419                                         text = "/" + text
420                         self.summaries.setText(text,1)
421
422                         idx += 1
423                         if idx < len(self.filelist.list):
424                                 r = self.filelist.list[idx]
425                                 text = r[1][7]
426                                 if r[0][1] == True:
427                                         text = "/" + text
428                                 self.summaries.setText(text,3)
429                         else:
430                                 self.summaries.setText(" ",3)
431
432                         idx += 1
433                         if idx < len(self.filelist.list):
434                                 r = self.filelist.list[idx]
435                                 text = r[1][7]
436                                 if r[0][1] == True:
437                                         text = "/" + text
438                                 self.summaries.setText(text,4)
439                         else:
440                                 self.summaries.setText(" ",4)
441
442                         text = ""
443                         if not self.filelist.canDescent():
444                                 r = self.filelist.getServiceRef()
445                                 if r is None:
446                                         return
447                                 text = r.getPath()
448                                 self["currenttext"].setText(os_path.basename(text))
449
450                 if self.currList == "playlist":
451                         t = self.playlist.getSelection()
452                         if t is None:
453                                 return
454                         #display current selected entry on LCD
455                         text = self.getIdentifier(t)
456                         self.summaries.setText(text,1)
457                         self["currenttext"].setText(text)
458                         idx = self.playlist.getSelectionIndex()
459                         idx += 1
460                         if idx < len(self.playlist):
461                                 currref = self.playlist.getServiceRefList()[idx]
462                                 text = self.getIdentifier(currref)
463                                 self.summaries.setText(text,3)
464                         else:
465                                 self.summaries.setText(" ",3)
466
467                         idx += 1
468                         if idx < len(self.playlist):
469                                 currref = self.playlist.getServiceRefList()[idx]
470                                 text = self.getIdentifier(currref)
471                                 self.summaries.setText(text,4)
472                         else:
473                                 self.summaries.setText(" ",4)
474
475         def ok(self):
476                 if self.currList == "filelist":
477                         if self.filelist.canDescent():
478                                 self.filelist.descent()
479                                 self.updateCurrentInfo()
480                         else:
481                                 self.copyFile()
482
483                 if self.currList == "playlist":
484                         selection = self["playlist"].getSelection()
485                         self.changeEntry(self.playlist.getSelectionIndex())
486
487         def showMenu(self):
488                 menu = []
489                 if len(self.cdAudioTrackFiles):
490                         menu.insert(0,(_("Play Audio-CD..."), "audiocd"))
491                 if self.currList == "filelist":
492                         if self.filelist.canDescent():
493                                 menu.append((_("add directory to playlist"), "copydir"))
494                         else:
495                                 menu.append((_("add files to playlist"), "copyfiles"))
496                         menu.append((_("switch to playlist"), "playlist"))
497                         if config.usage.setup_level.index >= 1: # intermediate+
498                                 menu.append((_("delete file"), "deletefile"))
499                 else:
500                         menu.append((_("switch to filelist"), "filelist"))
501                         menu.append((_("clear playlist"), "clear"))
502                         menu.append((_("Delete entry"), "deleteentry"))
503                         if config.usage.setup_level.index >= 1: # intermediate+
504                                 menu.append((_("shuffle playlist"), "shuffle"))
505                 menu.append((_("hide player"), "hide"));
506                 menu.append((_("load playlist"), "loadplaylist"));
507                 if config.usage.setup_level.index >= 1: # intermediate+
508                         menu.append((_("save playlist"), "saveplaylist"));
509                         menu.append((_("delete saved playlist"), "deleteplaylist"));
510                         menu.append((_("Edit settings"), "settings"))
511                 self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu)
512
513         def menuCallback(self, choice):
514                 if choice is None:
515                         return
516
517                 if choice[1] == "copydir":
518                         self.copyDirectory(self.filelist.getSelection()[0])
519                 elif choice[1] == "copyfiles":
520                         self.stopEntry()
521                         self.playlist.clear()
522                         self.isAudioCD = False
523                         self.copyDirectory(os_path.dirname(self.filelist.getSelection()[0].getPath()) + "/", recursive = False)
524                         self.playServiceRefEntry(self.filelist.getServiceRef())
525                 elif choice[1] == "playlist":
526                         self.switchToPlayList()
527                 elif choice[1] == "filelist":
528                         self.switchToFileList()
529                 elif choice[1] == "deleteentry":
530                         if self.playlist.getSelectionIndex() == self.playlist.getCurrentIndex():
531                                 self.stopEntry()
532                         self.deleteEntry()
533                 elif choice[1] == "clear":
534                         self.clear_playlist()
535                 elif choice[1] == "hide":
536                         self.hide()
537                 elif choice[1] == "saveplaylist":
538                         self.save_playlist()
539                 elif choice[1] == "loadplaylist":
540                         self.load_playlist()
541                 elif choice[1] == "deleteplaylist":
542                         self.delete_saved_playlist()
543                 elif choice[1] == "shuffle":
544                         self.playlist.PlayListShuffle()
545                 elif choice[1] == "deletefile":
546                         self.deleteFile()
547                 elif choice[1] == "settings":
548                         self.session.openWithCallback(self.applySettings, MediaPlayerSettings, self)
549                 elif choice[1] == "audiocd":
550                         self.playAudioCD()
551
552         def playAudioCD(self):
553                 from enigma import eServiceReference
554                 from Plugins.Extensions.CDInfo.plugin import Query
555
556                 if len(self.cdAudioTrackFiles):
557                         self.playlist.clear()
558                         self.savePlaylistOnExit = False
559                         self.isAudioCD = True
560                         for file in self.cdAudioTrackFiles:
561                                 ref = eServiceReference(4097, 0, file)
562                                 self.playlist.addFile(ref)
563                         cdinfo = Query(self)
564                         cdinfo.scan()
565                         self.changeEntry(0)
566                         self.switchToPlayList()
567
568         def applySettings(self):
569                 self.savePlaylistOnExit = config.mediaplayer.savePlaylistOnExit.getValue()
570                 if config.mediaplayer.repeat.getValue() == True:
571                         self["repeat"].setPixmapNum(1)
572                 else:
573                         self["repeat"].setPixmapNum(0)
574
575         def showEventInformation(self):
576                 from Screens.EventView import EventViewSimple
577                 from ServiceReference import ServiceReference
578                 evt = self[self.currList].getCurrentEvent()
579                 if evt:
580                         self.session.open(EventViewSimple, evt, ServiceReference(self.getCurrent()))
581
582         # also works on filelist (?)
583         def getCurrent(self):
584                 return self["playlist"].getCurrent()
585
586         def deletePlaylistEntry(self):
587                 if self.currList == "playlist":
588                         if self.playlist.getSelectionIndex() == self.playlist.getCurrentIndex():
589                                 self.stopEntry()
590                         self.deleteEntry()
591
592         def skip_listbegin(self):
593                 if self.currList == "filelist":
594                         self.filelist.moveToIndex(0)
595                 else:
596                         self.playlist.moveToIndex(0)
597                 self.updateCurrentInfo()
598
599         def skip_listend(self):
600                 if self.currList == "filelist":
601                         idx = len(self.filelist.list)
602                         self.filelist.moveToIndex(idx - 1)
603                 else:
604                         self.playlist.moveToIndex(len(self.playlist)-1)
605                 self.updateCurrentInfo()
606
607         def save_playlist(self):
608                 self.session.openWithCallback(self.save_playlist2,InputBox, title=_("Please enter filename (empty = use current date)"),windowTitle = _("Save Playlist"))
609
610         def save_playlist2(self, name):
611                 if name is not None:
612                         name = name.strip()
613                         if name == "":
614                                 name = strftime("%y%m%d_%H%M%S")
615                         name += ".e2pls"
616                         self.playlistIOInternal.clear()
617                         for x in self.playlist.list:
618                                 self.playlistIOInternal.addService(ServiceReference(x[0]))
619                         self.playlistIOInternal.save(resolveFilename(SCOPE_PLAYLIST) + name)
620
621         def load_playlist(self):
622                 listpath = []
623                 playlistdir = resolveFilename(SCOPE_PLAYLIST)
624                 try:
625                         for i in os_listdir(playlistdir):
626                                 listpath.append((i,playlistdir + i))
627                 except IOError,e:
628                         print "Error while scanning subdirs ",e
629                 self.session.openWithCallback(self.PlaylistSelected, ChoiceBox, title=_("Please select a playlist..."), list = listpath)
630
631         def PlaylistSelected(self,path):
632                 if path is not None:
633                         self.clear_playlist()
634                         extension = path[0].rsplit('.',1)[-1]
635                         if self.playlistparsers.has_key(extension):
636                                 playlist = self.playlistparsers[extension]()
637                                 list = playlist.open(path[1])
638                                 for x in list:
639                                         self.playlist.addFile(x.ref)
640                         self.playlist.updateList()
641
642         def delete_saved_playlist(self):
643                 listpath = []
644                 playlistdir = resolveFilename(SCOPE_PLAYLIST)
645                 try:
646                         for i in os_listdir(playlistdir):
647                                 listpath.append((i,playlistdir + i))
648                 except IOError,e:
649                         print "Error while scanning subdirs ",e
650                 self.session.openWithCallback(self.DeletePlaylistSelected, ChoiceBox, title=_("Please select a playlist to delete..."), list = listpath)
651
652         def DeletePlaylistSelected(self,path):
653                 if path is not None:
654                         self.delname = path[1]
655                         self.session.openWithCallback(self.deleteConfirmed, MessageBox, _("Do you really want to delete %s?") % (path[1]))
656
657         def deleteConfirmed(self, confirmed):
658                 if confirmed:
659                         try:
660                                 os_remove(self.delname)
661                         except OSError,e:
662                                 print "delete failed:", e
663                                 self.session.open(MessageBox, _("Delete failed!"), MessageBox.TYPE_ERROR)
664
665         def clear_playlist(self):
666                 self.isAudioCD = False
667                 self.stopEntry()
668                 self.playlist.clear()
669                 self.switchToFileList()
670
671         def copyDirectory(self, directory, recursive = True):
672                 print "copyDirectory", directory
673                 if directory == '/':
674                         print "refusing to operate on /"
675                         return
676                 filelist = FileList(directory, useServiceRef = True, showMountpoints = False, isTop = True)
677
678                 for x in filelist.getFileList():
679                         if x[0][1] == True: #isDir
680                                 if recursive:
681                                         if x[0][0] != directory:
682                                                 self.copyDirectory(x[0][0])
683                         elif filelist.getServiceRef() and filelist.getServiceRef().type == 4097:
684                                 self.playlist.addFile(x[0][0])
685                 self.playlist.updateList()
686
687         def deleteFile(self):
688                 if self.currList == "filelist":
689                         self.service = self.filelist.getServiceRef()
690                 else:
691                         self.service = self.playlist.getSelection()
692                 if self.service is None:
693                         return
694                 if self.service.type != 4098 and self.session.nav.getCurrentlyPlayingServiceReference() is not None:
695                         if self.service == self.session.nav.getCurrentlyPlayingServiceReference():
696                                 self.stopEntry()
697
698                 serviceHandler = eServiceCenter.getInstance()
699                 offline = serviceHandler.offlineOperations(self.service)
700                 info = serviceHandler.info(self.service)
701                 name = info and info.getName(self.service)
702                 result = False
703                 if offline is not None:
704                         # simulate first
705                         if not offline.deleteFromDisk(1):
706                                 result = True
707                 if result == True:
708                         self.session.openWithCallback(self.deleteConfirmed_offline, MessageBox, _("Do you really want to delete %s?") % (name))
709                 else:
710                         self.session.openWithCallback(self.close, MessageBox, _("You cannot delete this!"), MessageBox.TYPE_ERROR)      
711
712         def deleteConfirmed_offline(self, confirmed):
713                 if confirmed:
714                         serviceHandler = eServiceCenter.getInstance()
715                         offline = serviceHandler.offlineOperations(self.service)
716                         result = False
717                         if offline is not None:
718                                 # really delete!
719                                 if not offline.deleteFromDisk(0):
720                                         result = True
721                         if result == False:
722                                 self.session.open(MessageBox, _("Delete failed!"), MessageBox.TYPE_ERROR)
723                         else:
724                                 self.removeListEntry()
725
726         def removeListEntry(self):
727                 currdir = self.filelist.getCurrentDirectory()
728                 self.filelist.changeDir(currdir)
729                 deleteend = False
730                 while not deleteend:
731                         index = 0
732                         deleteend = True
733                         if len(self.playlist) > 0:
734                                 for x in self.playlist.list:
735                                         if self.service == x[0]:
736                                                 self.playlist.deleteFile(index)
737                                                 deleteend = False
738                                                 break
739                                         index += 1
740                 self.playlist.updateList()
741                 if self.currList == "playlist":
742                         if len(self.playlist) == 0:
743                                 self.switchToFileList()
744
745         def copyFile(self):
746                 if self.filelist.getServiceRef().type == 4098: # playlist
747                         ServiceRef = self.filelist.getServiceRef()
748                         extension = ServiceRef.getPath()[ServiceRef.getPath().rfind('.') + 1:]
749                         if self.playlistparsers.has_key(extension):
750                                 playlist = self.playlistparsers[extension]()
751                                 list = playlist.open(ServiceRef.getPath())
752                                 for x in list:
753                                         self.playlist.addFile(x.ref)
754                         self.playlist.updateList()
755                 else:
756                         self.playlist.addFile(self.filelist.getServiceRef())
757                         self.playlist.updateList()
758                         if len(self.playlist) == 1:
759                                 self.changeEntry(0)
760
761         def addPlaylistParser(self, parser, extension):
762                 self.playlistparsers[extension] = parser
763
764         def nextEntry(self):
765                 next = self.playlist.getCurrentIndex() + 1
766                 if next < len(self.playlist):
767                         self.changeEntry(next)
768                 elif ( len(self.playlist) > 0 ) and ( config.mediaplayer.repeat.getValue() == True ):
769                         self.stopEntry()
770                         self.changeEntry(0)
771
772         def nextMarkOrEntry(self):
773                 if not self.jumpPreviousNextMark(lambda x: x):
774                         next = self.playlist.getCurrentIndex() + 1
775                         if next < len(self.playlist):
776                                 self.changeEntry(next)
777                         else:
778                                 self.doSeek(-1)
779
780         def previousMarkOrEntry(self):
781                 if not self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True):
782                         next = self.playlist.getCurrentIndex() - 1
783                         if next >= 0:
784                                 self.changeEntry(next)
785
786         def deleteEntry(self):
787                 self.playlist.deleteFile(self.playlist.getSelectionIndex())
788                 self.playlist.updateList()
789                 if len(self.playlist) == 0:
790                         self.switchToFileList()
791
792         def changeEntry(self, index):
793                 self.playlist.setCurrentPlaying(index)
794                 self.playEntry()
795
796         def playServiceRefEntry(self, serviceref):
797                 serviceRefList = self.playlist.getServiceRefList()
798                 for count in range(len(serviceRefList)):
799                         if serviceRefList[count] == serviceref:
800                                 self.changeEntry(count)
801                                 break
802                         
803         def xplayEntry(self):
804                 if self.currList == "playlist":
805                         self.playEntry()
806                 else:
807                         self.stopEntry()
808                         self.playlist.clear()
809                         self.isAudioCD = False
810                         sel = self.filelist.getSelection()
811                         if sel:
812                                 if sel[1]: # can descent
813                                         # add directory to playlist
814                                         self.copyDirectory(sel[0])
815                                 else:
816                                         # add files to playlist
817                                         self.copyDirectory(os_path.dirname(sel[0].getPath()) + "/", recursive = False)
818                         if len(self.playlist) > 0:
819                                 self.changeEntry(0)
820         
821         def playEntry(self):
822                 if len(self.playlist.getServiceRefList()):
823                         audio_extensions = (".mp2", ".mp3", ".wav", ".ogg", "flac", "m4a")
824                         needsInfoUpdate = False
825                         currref = self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()]
826                         if self.session.nav.getCurrentlyPlayingServiceReference() is None or currref != self.session.nav.getCurrentlyPlayingServiceReference():
827                                 self.session.nav.playService(self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()])
828                                 info = eServiceCenter.getInstance().info(currref)
829                                 description = info and info.getInfoString(currref, iServiceInformation.sDescription) or ""
830                                 self["title"].setText(description)
831                                 # display just playing musik on LCD
832                                 idx = self.playlist.getCurrentIndex()
833                                 currref = self.playlist.getServiceRefList()[idx]
834                                 text = self.getIdentifier(currref)
835                                 text = ">"+text
836                                 ext = text[-4:].lower()
837
838                                 # FIXME: the information if the service contains video (and we should hide our window) should com from the service instead 
839                                 if ext not in audio_extensions and not self.isAudioCD:
840                                         self.hide()
841                                 else:
842                                         needsInfoUpdate = True
843                                 self.summaries.setText(text,1)
844
845                                 # get the next two entries
846                                 idx += 1
847                                 if idx < len(self.playlist):
848                                         currref = self.playlist.getServiceRefList()[idx]
849                                         text = self.getIdentifier(currref)
850                                         self.summaries.setText(text,3)
851                                 else:
852                                         self.summaries.setText(" ",3)
853
854                                 idx += 1
855                                 if idx < len(self.playlist):
856                                         currref = self.playlist.getServiceRefList()[idx]
857                                         text = self.getIdentifier(currref)
858                                         self.summaries.setText(text,4)
859                                 else:
860                                         self.summaries.setText(" ",4)
861                         else:
862                                 idx = self.playlist.getCurrentIndex()
863                                 currref = self.playlist.getServiceRefList()[idx]
864                                 text = currref.getPath()
865                                 ext = text[-4:].lower()
866                                 if ext not in audio_extensions and not self.isAudioCD:
867                                         self.hide()
868                                 else:
869                                         needsInfoUpdate = True
870
871                         self.unPauseService()
872                         if needsInfoUpdate == True:
873                                 path = self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()].getPath()
874                                 self["coverArt"].updateCoverArt(path)
875                         else:
876                                 self["coverArt"].showDefaultCover()
877                         self.readTitleInformation()
878
879         def updatedSeekState(self):
880                 if self.seekstate == self.SEEK_STATE_PAUSE:
881                         self.playlist.pauseFile()
882                 elif self.seekstate == self.SEEK_STATE_PLAY:
883                         self.playlist.playFile()
884                 elif self.isStateForward(self.seekstate):
885                         self.playlist.forwardFile()
886                 elif self.isStateBackward(self.seekstate):
887                         self.playlist.rewindFile()
888
889         def pauseEntry(self):
890                 self.pauseService()
891                 if self.seekstate == self.SEEK_STATE_PAUSE:
892                         self.show()
893                 else:
894                         self.hide()
895
896         def stopEntry(self):
897                 self.playlist.stopFile()
898                 self.session.nav.playService(None)
899                 self.updateMusicInformation(clear=True)
900                 self.show()
901
902         def unPauseService(self):
903                 self.setSeekState(self.SEEK_STATE_PLAY)
904                 
905         def subtitleSelection(self):
906                 from Screens.Subtitles import Subtitles
907                 self.session.open(Subtitles, self)
908         
909         def hotplugCB(self, dev, media_state):
910                 if dev == harddiskmanager.getCD():
911                         if media_state == "1":
912                                 from Components.Scanner import scanDevice
913                                 devpath = harddiskmanager.getAutofsMountpoint(harddiskmanager.getCD())
914                                 self.cdAudioTrackFiles = []
915                                 res = scanDevice(devpath)
916                                 list = [ (r.description, r, res[r], self.session) for r in res ]
917                                 if list:
918                                         (desc, scanner, files, session) = list[0]
919                                         for file in files:
920                                                 if file.mimetype == "audio/x-cda":
921                                                         self.cdAudioTrackFiles.append(file.path)
922                         else:
923                                 self.cdAudioTrackFiles = []
924                                 if self.isAudioCD:
925                                         self.clear_playlist()
926
927 class MediaPlayerLCDScreen(Screen):
928         skin = """
929         <screen position="0,0" size="132,64" title="LCD Text">
930                 <widget name="text1" position="4,0" size="132,35" font="Regular;16"/>
931                 <widget name="text3" position="4,36" size="132,14" font="Regular;10"/>
932                 <widget name="text4" position="4,49" size="132,14" font="Regular;10"/>
933         </screen>"""
934
935         def __init__(self, session, parent):
936                 Screen.__init__(self, session)
937                 self["text1"] = Label("Mediaplayer")
938                 self["text3"] = Label("")
939                 self["text4"] = Label("")
940
941         def setText(self, text, line):
942                 if len(text) > 10:
943                         if text[-4:] == ".mp3":
944                                 text = text[:-4]
945                 textleer = "    "
946                 text = text + textleer*10
947                 if line == 1:
948                         self["text1"].setText(text)
949                 elif line == 3:
950                         self["text3"].setText(text)
951                 elif line == 4:
952                         self["text4"].setText(text)
953
954 def main(session, **kwargs):
955         session.open(MediaPlayer)
956
957 def menu(menuid, **kwargs):
958         if menuid == "mainmenu":
959                 return [(_("Media player"), main, "media_player", 45)]
960         return []
961
962 def filescan_open(list, session, **kwargs):
963         from enigma import eServiceReference
964
965         mp = session.open(MediaPlayer)
966         mp.playlist.clear()
967         mp.savePlaylistOnExit = False
968
969         for file in list:
970                 if file.mimetype == "video/MP2T":
971                         stype = 1
972                 else:
973                         stype = 4097
974                 ref = eServiceReference(stype, 0, file.path)
975                 mp.playlist.addFile(ref)
976
977         mp.changeEntry(0)
978         mp.switchToPlayList()
979
980 def audioCD_open(list, session, **kwargs):
981         from enigma import eServiceReference
982
983         mp = session.open(MediaPlayer)
984         mp.cdAudioTrackFiles = []
985         for file in list:
986                 mp.cdAudioTrackFiles.append(file.path)
987         mp.playAudioCD()
988
989 def filescan(**kwargs):
990         from Components.Scanner import Scanner, ScanPath
991         mediatypes = [
992                 Scanner(mimetypes = ["video/mpeg", "video/MP2T", "video/x-msvideo"],
993                         paths_to_scan =
994                                 [
995                                         ScanPath(path = "", with_subdirs = False),
996                                 ],
997                         name = "Movie",
998                         description = _("View Movies..."),
999                         openfnc = filescan_open,
1000                 ),
1001                 Scanner(mimetypes = ["video/x-vcd"],
1002                         paths_to_scan =
1003                                 [
1004                                         ScanPath(path = "mpegav", with_subdirs = False),
1005                                         ScanPath(path = "MPEGAV", with_subdirs = False),
1006                                 ],
1007                         name = "Video CD",
1008                         description = _("View Video CD..."),
1009                         openfnc = filescan_open,
1010                 ),
1011                 Scanner(mimetypes = ["audio/mpeg", "audio/x-wav", "application/ogg", "audio/x-flac"],
1012                         paths_to_scan =
1013                                 [
1014                                         ScanPath(path = "", with_subdirs = False),
1015                                 ],
1016                         name = "Music",
1017                         description = _("Play Music..."),
1018                         openfnc = filescan_open,
1019                 )]
1020         try:
1021                 from Plugins.Extensions.CDInfo.plugin import Query
1022                 mediatypes.append(
1023                 Scanner(mimetypes = ["audio/x-cda"],
1024                         paths_to_scan =
1025                                 [
1026                                         ScanPath(path = "", with_subdirs = False),
1027                                 ],
1028                         name = "Audio-CD",
1029                         description = _("Play Audio-CD..."),
1030                         openfnc = audioCD_open,
1031                 ))
1032                 return mediatypes
1033         except ImportError:
1034                 return mediatypes
1035
1036 from Plugins.Plugin import PluginDescriptor
1037 def Plugins(**kwargs):
1038         return [
1039                 PluginDescriptor(name = "MediaPlayer", description = "Play back media files", where = PluginDescriptor.WHERE_MENU, fnc = menu),
1040                 PluginDescriptor(name = "MediaPlayer", where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)
1041         ]