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