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