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