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