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