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