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