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