Solve GSOD when movieselection is parentally protected.
[openblackhole/openblackhole-enigma2.git] / lib / python / Screens / MovieSelection.py
1 from Screen import Screen
2 from Components.Button import Button
3 from Components.ActionMap import HelpableActionMap, ActionMap, NumberActionMap
4 from Components.ChoiceList import ChoiceList, ChoiceEntryComponent
5 from Components.MenuList import MenuList
6 from Components.MovieList import MovieList, resetMoviePlayState, AUDIO_EXTENSIONS, DVD_EXTENSIONS, IMAGE_EXTENSIONS
7 from Components.DiskInfo import DiskInfo
8 from Components.Pixmap import Pixmap, MultiPixmap
9 from Components.Label import Label
10 from Components.PluginComponent import plugins
11 from Components.config import config, ConfigSubsection, ConfigText, ConfigInteger, ConfigLocations, ConfigSet, ConfigYesNo, ConfigSelection, getConfigListEntry
12 from Components.ConfigList import ConfigListScreen
13 from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
14 from Components.Sources.ServiceEvent import ServiceEvent
15 from Components.Sources.StaticText import StaticText
16 import Components.Harddisk
17 from Components.UsageConfig import preferredTimerPath
18
19 from Plugins.Plugin import PluginDescriptor
20
21 from Screens.MessageBox import MessageBox
22 from Screens.ChoiceBox import ChoiceBox
23 from Screens.LocationBox import MovieLocationBox
24 from Screens.HelpMenu import HelpableScreen
25 from Screens.InputBox import PinInput
26 import Screens.InfoBar
27
28 from Tools import NumericalTextInput
29 from Tools.Directories import resolveFilename, SCOPE_HDD
30 from Tools.BoundFunction import boundFunction
31 import Tools.Trashcan
32 import NavigationInstance
33 import RecordTimer
34
35 from enigma import eServiceReference, eServiceCenter, eTimer, eSize, iPlayableService, iServiceInformation, getPrevAsciiCode, eRCInput
36 import os
37 import time
38 import cPickle as pickle
39
40 config.movielist = ConfigSubsection()
41 config.movielist.moviesort = ConfigInteger(default=MovieList.SORT_GROUPWISE)
42 config.movielist.listtype = ConfigInteger(default=MovieList.LISTTYPE_MINIMAL)
43 config.movielist.description = ConfigInteger(default=MovieList.SHOW_DESCRIPTION)
44 config.movielist.last_videodir = ConfigText(default=resolveFilename(SCOPE_HDD))
45 config.movielist.last_timer_videodir = ConfigText(default=resolveFilename(SCOPE_HDD))
46 config.movielist.videodirs = ConfigLocations(default=[resolveFilename(SCOPE_HDD)])
47 config.movielist.last_selected_tags = ConfigSet([], default=[])
48 config.movielist.play_audio_internal = ConfigYesNo(default=True)
49 config.movielist.settings_per_directory = ConfigYesNo(default=True)
50 config.movielist.root = ConfigSelection(default="/media", choices=["/","/media","/media/hdd","/media/hdd/movie","/media/usb","/media/usb/movie"])
51 config.movielist.hide_extensions = ConfigYesNo(default=False)
52 config.movielist.stop_service = ConfigYesNo(default=True)
53
54 userDefinedButtons = None
55 last_selected_dest = []
56 preferredTagEditor = None
57
58 # this kludge is needed because ConfigSelection only takes numbers
59 # and someone appears to be fascinated by 'enums'.
60 l_moviesort = [
61         (str(MovieList.SORT_GROUPWISE), _("default") , '02/01 & A-Z'),
62         (str(MovieList.SORT_RECORDED), _("by date"), '03/02/01'),
63         (str(MovieList.SORT_ALPHANUMERIC), _("alphabetic"), 'A-Z'),
64         (str(MovieList.SORT_ALPHANUMERIC_FLAT), _("flat alphabetic"), 'A-Z Flat'),
65         (str(MovieList.SHUFFLE), _("shuffle"), '?'),
66         (str(MovieList.SORT_RECORDED_REVERSE), _("reverse by date"), '01/02/03'),
67         (str(MovieList.SORT_ALPHANUMERIC_REVERSE), _("alphabetic reverse"), 'Z-A'),
68         (str(MovieList.SORT_ALPHANUMERIC_FLAT_REVERSE), _("flat alphabetic reverse"), 'Z-A Flat')]
69 l_listtype = [(str(MovieList.LISTTYPE_ORIGINAL), _("list style default")),
70         (str(MovieList.LISTTYPE_COMPACT_DESCRIPTION), _("list style compact with description")),
71         (str(MovieList.LISTTYPE_COMPACT), _("list style compact")),
72         (str(MovieList.LISTTYPE_MINIMAL), _("list style single line"))]
73
74 def defaultMoviePath():
75         result = config.usage.default_path.value
76         if not os.path.isdir(result):
77                 from Tools import Directories
78                 return Directories.defaultRecordingLocation()
79         return result
80
81 def setPreferredTagEditor(te):
82         global preferredTagEditor
83         if preferredTagEditor is None:
84                 preferredTagEditor = te
85                 print "Preferred tag editor changed to", preferredTagEditor
86         else:
87                 print "Preferred tag editor already set to", preferredTagEditor, "ignoring", te
88
89 def getPreferredTagEditor():
90         global preferredTagEditor
91         return preferredTagEditor
92
93 def isTrashFolder(ref):
94         if not config.usage.movielist_trashcan.value or not ref.flags & eServiceReference.mustDescent:
95                 return False
96         path = os.path.realpath(ref.getPath())
97         return path.endswith('.Trash') and path.startswith(Tools.Trashcan.getTrashFolder(path))
98
99 def isInTrashFolder(ref):
100         if not config.usage.movielist_trashcan.value or not ref.flags & eServiceReference.mustDescent:
101                 return False
102         path = os.path.realpath(ref.getPath())
103         return path.startswith(Tools.Trashcan.getTrashFolder(path))
104
105 def isSimpleFile(item):
106         if not item:
107                 return False
108         if not item[0] or not item[1]:
109                 return False
110         return (item[0].flags & eServiceReference.mustDescent) == 0
111
112 def isFolder(item):
113         if not item:
114                 return False
115         if not item[0] or not item[1]:
116                 return False
117         return (item[0].flags & eServiceReference.mustDescent) != 0
118
119
120 def canMove(item):
121         if not item:
122                 return False
123         if not item[0] or not item[1]:
124                 return False
125         if item[0].flags & eServiceReference.mustDescent:
126                 return not isTrashFolder(item[0])
127         return True
128
129 canDelete = canMove
130
131 def canCopy(item):
132         if not item:
133                 return False
134         if not item[0] or not item[1]:
135                 return False
136         if item[0].flags & eServiceReference.mustDescent:
137                 return False
138         return True
139
140 def createMoveList(serviceref, dest):
141         #normpath is to remove the trailing '/' from directories
142         src = isinstance(serviceref, str) and serviceref + ".ts" or os.path.normpath(serviceref.getPath())
143         srcPath, srcName = os.path.split(src)
144         if os.path.normpath(srcPath) == dest:
145                 # move file to itself is allowed, so we have to check it
146                 raise Exception, "Refusing to move to the same directory"
147         # Make a list of items to move
148         moveList = [(src, os.path.join(dest, srcName))]
149         if isinstance(serviceref, str) or not serviceref.flags & eServiceReference.mustDescent:
150                 # Real movie, add extra files...
151                 srcBase = os.path.splitext(src)[0]
152                 baseName = os.path.split(srcBase)[1]
153                 eitName =  srcBase + '.eit'
154                 if os.path.exists(eitName):
155                         moveList.append((eitName, os.path.join(dest, baseName+'.eit')))
156                 baseName = os.path.split(src)[1]
157                 for ext in ('.ap', '.cuts', '.meta', '.sc'):
158                         candidate = src + ext
159                         if os.path.exists(candidate):
160                                 moveList.append((candidate, os.path.join(dest, baseName+ext)))
161         return moveList
162
163 def moveServiceFiles(serviceref, dest, name=None, allowCopy=True):
164         moveList = createMoveList(serviceref, dest)
165         # Try to "atomically" move these files
166         movedList = []
167         try:
168                 try:
169                         for item in moveList:
170                                 os.rename(item[0], item[1])
171                                 movedList.append(item)
172                 except OSError, e:
173                         if e.errno == 18 and allowCopy:
174                                 print "[MovieSelection] cannot rename across devices, trying slow move"
175                                 import CopyFiles
176                                 # start with the smaller files, do the big one later.
177                                 moveList.reverse()
178                                 if name is None:
179                                         name = os.path.split(moveList[-1][0])[1]
180                                 CopyFiles.moveFiles(moveList, name)
181                                 print "[MovieSelection] Moving in background..."
182                         else:
183                                 raise
184         except Exception, e:
185                 print "[MovieSelection] Failed move:", e
186                 for item in movedList:
187                         try:
188                                 os.rename(item[1], item[0])
189                         except:
190                                 print "[MovieSelection] Failed to undo move:", item
191                 # rethrow exception
192                 raise
193
194 def copyServiceFiles(serviceref, dest, name=None):
195         # current should be 'ref' type, dest a simple path string
196         moveList = createMoveList(serviceref, dest)
197         # Try to "atomically" move these files
198         movedList = []
199         try:
200                 for item in moveList:
201                         os.link(item[0], item[1])
202                         movedList.append(item)
203                 # this worked, we're done
204                 return
205         except Exception, e:
206                 print "[MovieSelection] Failed copy using link:", e
207                 for item in movedList:
208                         try:
209                                 os.unlink(item[1])
210                         except:
211                                 print "[MovieSelection] Failed to undo copy:", item
212         #Link failed, really copy.
213         import CopyFiles
214         # start with the smaller files, do the big one later.
215         moveList.reverse()
216         if name is None:
217                 name = os.path.split(moveList[-1][0])[1]
218         CopyFiles.copyFiles(moveList, name)
219         print "[MovieSelection] Copying in background..."
220
221 # Appends possible destinations to the bookmarks object. Appends tuples
222 # in the form (description, path) to it.
223 def buildMovieLocationList(bookmarks):
224         inlist = []
225         for d in config.movielist.videodirs.value:
226                 d = os.path.normpath(d)
227                 bookmarks.append((d,d))
228                 inlist.append(d)
229         for p in Components.Harddisk.harddiskmanager.getMountedPartitions():
230                 d = os.path.normpath(p.mountpoint)
231                 if d in inlist:
232                         # improve shortcuts to mountpoints
233                         try:
234                                 bookmarks[bookmarks.index((d,d))] = (p.tabbedDescription(), d)
235                         except:
236                                 pass # When already listed as some "friendly" name
237                 else:
238                         bookmarks.append((p.tabbedDescription(), d))
239                 inlist.append(d)
240         for d in last_selected_dest:
241                 if d not in inlist:
242                         bookmarks.append((d,d))
243
244 class MovieBrowserConfiguration(ConfigListScreen,Screen):
245         skin = """
246 <screen position="center,center" size="560,400" title="Movie Browser Configuration" >
247         <ePixmap name="red"    position="0,0"   zPosition="2" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
248         <ePixmap name="green"  position="140,0" zPosition="2" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
249
250         <widget name="key_red" position="0,0" size="140,40" valign="center" halign="center" zPosition="4"  foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
251         <widget name="key_green" position="140,0" size="140,40" valign="center" halign="center" zPosition="4"  foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
252         <widget name="config" position="10,40" size="540,340" scrollbarMode="showOnDemand" />
253
254         <ePixmap alphatest="on" pixmap="skin_default/icons/clock.png" position="480,383" size="14,14" zPosition="3"/>
255         <widget font="Regular;18" halign="left" position="505,380" render="Label" size="55,20" source="global.CurrentTime" transparent="1" valign="center" zPosition="3">
256                 <convert type="ClockToText">Default</convert>
257         </widget>
258 </screen>"""
259
260         def __init__(self, session, args = 0):
261                 self.session = session
262                 self.setup_title = _("Movie list configuration")
263                 Screen.__init__(self, session)
264                 cfg = ConfigSubsection()
265                 self.cfg = cfg
266                 cfg.moviesort = ConfigSelection(default=str(config.movielist.moviesort.value), choices = l_moviesort)
267                 cfg.listtype = ConfigSelection(default=str(config.movielist.listtype.value), choices = l_listtype)
268                 cfg.description = ConfigYesNo(default=(config.movielist.description.value != MovieList.HIDE_DESCRIPTION))
269                 configList = [
270                         getConfigListEntry(_("Sort"), cfg.moviesort),
271                         getConfigListEntry(_("Show extended description"), cfg.description),
272                         getConfigListEntry(_("Type"), cfg.listtype),
273                         getConfigListEntry(_("Use individual settings for each directory"), config.movielist.settings_per_directory),
274                         getConfigListEntry(_("Behavior when a movie reaches the end"), config.usage.on_movie_eof),
275                         getConfigListEntry(_("Stop service on return to movie list"), config.movielist.stop_service),
276                         getConfigListEntry(_("Load length of movies in movie list"), config.usage.load_length_of_movies_in_moviellist),
277                         getConfigListEntry(_("Show status icons in movie list"), config.usage.show_icons_in_movielist),
278                         getConfigListEntry(_("Show icon for new/unseen items"), config.usage.movielist_unseen),
279                         getConfigListEntry(_("Play audio in background"), config.movielist.play_audio_internal),
280                         getConfigListEntry(_("Root directory"), config.movielist.root),
281                         getConfigListEntry(_("Hide known extensions"), config.movielist.hide_extensions),
282                         ]
283                 for btn in ('red', 'green', 'yellow', 'blue', 'TV', 'Radio', 'Text', 'F1', 'F2', 'F3'):
284                         configList.append(getConfigListEntry(_(btn), userDefinedButtons[btn]))
285                 ConfigListScreen.__init__(self, configList, session=session, on_change = self.changedEntry)
286                 self["key_red"] = Button(_("Cancel"))
287                 self["key_green"] = Button(_("Ok"))
288                 self["setupActions"] = ActionMap(["SetupActions", "ColorActions",  "MenuActions"],
289                 {
290                         "red": self.cancel,
291                         "green": self.save,
292                         "save": self.save,
293                         "cancel": self.cancel,
294                         "ok": self.save,
295                         "menu": self.cancel,
296                 }, -2)
297                 self.onChangedEntry = []
298
299         # for summary:
300         def changedEntry(self):
301                 for x in self.onChangedEntry:
302                         x()
303         def getCurrentEntry(self):
304                 return self["config"].getCurrent()[0]
305         def getCurrentValue(self):
306                 return str(self["config"].getCurrent()[1].getText())
307         def createSummary(self):
308                 from Screens.Setup import SetupSummary
309                 return SetupSummary
310
311         def save(self):
312                 self.saveAll()
313                 cfg = self.cfg
314                 config.movielist.moviesort.value = int(cfg.moviesort.value)
315                 config.movielist.listtype.value = int(cfg.listtype.value)
316                 if cfg.description.value:
317                         config.movielist.description.value = MovieList.SHOW_DESCRIPTION
318                 else:
319                         config.movielist.description.value = MovieList.HIDE_DESCRIPTION
320                 if not config.movielist.settings_per_directory.value:
321                         config.movielist.moviesort.save()
322                         config.movielist.listtype.save()
323                         config.movielist.description.save()
324                         config.usage.on_movie_eof.save()
325                 self.close(True)
326
327         def cancel(self):
328                 if self["config"].isChanged():
329                         self.session.openWithCallback(self.cancelCallback, MessageBox, _("Really close without saving settings?"))
330                 else:
331                         self.cancelCallback(True)
332
333         def cancelCallback(self, answer):
334                 if answer:
335                         for x in self["config"].list:
336                                 x[1].cancel()
337                         self.close(False)
338
339 class MovieContextMenuSummary(Screen):
340         def __init__(self, session, parent):
341                 Screen.__init__(self, session, parent = parent)
342                 self["selected"] = StaticText("")
343                 self.onShow.append(self.__onShow)
344                 self.onHide.append(self.__onHide)
345
346         def __onShow(self):
347                 self.parent["menu"].onSelectionChanged.append(self.selectionChanged)
348                 self.selectionChanged()
349
350         def __onHide(self):
351                 self.parent["menu"].onSelectionChanged.remove(self.selectionChanged)
352
353         def selectionChanged(self):
354                 item = self.parent["menu"].getCurrent()
355                 self["selected"].text = item[0][0]
356
357 from Screens.ParentalControlSetup import ProtectedScreen
358
359 class MovieContextMenu(Screen, ProtectedScreen):
360         # Contract: On OK returns a callable object (e.g. delete)
361         def __init__(self, session, csel, service):
362                 Screen.__init__(self, session)
363                 self.csel = csel
364                 ProtectedScreen.__init__(self)
365
366                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "NumberActions", "MenuActions"],
367                         {
368                                 "ok": self.okbuttonClick,
369                                 "cancel": self.cancelClick,
370                                 "yellow": self.do_showNetworkSetup,
371                                 "menu": self.do_configure,
372                                 "2": self.do_rename,
373                                 "5": self.do_copy,
374                                 "6": self.do_move,
375                                 "7": self.do_createdir,
376                                 "8": self.do_delete
377                         })
378
379                 def append_to_menu(menu, args, key=""):
380                         menu.append(ChoiceEntryComponent(key, args))
381
382                 menu = []
383                 if service:
384                         if (service.flags & eServiceReference.mustDescent):
385                                 if isTrashFolder(service):
386                                         append_to_menu(menu, (_("Permanently remove all deleted items"), csel.purgeAll), key="8")
387                                 else:
388                                         append_to_menu(menu, (_("Delete"), csel.do_delete), key="8")
389                                         append_to_menu(menu, (_("Move"), csel.do_move), key="6")
390                                         append_to_menu(menu, (_("Rename"), csel.do_rename), key="2")
391                         else:
392                                 append_to_menu(menu, (_("Delete"), csel.do_delete), key="8")
393                                 append_to_menu(menu, (_("Move"), csel.do_move), key="6")
394                                 append_to_menu(menu, (_("Copy"), csel.do_copy), key="5")
395                                 append_to_menu(menu, (_("Reset playback position"), csel.do_reset))
396                                 append_to_menu(menu, (_("Rename"), csel.do_rename), key="2")
397                                 append_to_menu(menu, (_("Start offline decode"), csel.do_decode))
398
399                                 # Plugins expect a valid selection, so only include them if we selected a non-dir
400                                 for p in plugins.getPlugins(PluginDescriptor.WHERE_MOVIELIST):
401                                         append_to_menu( menu, (p.description, boundFunction(p, session, service)), key="bullet")
402                 if csel.exist_bookmark():
403                         append_to_menu(menu, (_("Remove bookmark"), csel.do_addbookmark))
404                 else:
405                         append_to_menu(menu, (_("Add bookmark"), csel.do_addbookmark))
406                 append_to_menu(menu, (_("create directory"), csel.do_createdir), key="7")
407                 append_to_menu(menu, (_("Sort by") + "...", csel.selectSortby))
408                 append_to_menu(menu, (_("Network") + "...", csel.showNetworkSetup), key="yellow")
409                 append_to_menu(menu, (_("Settings") + "...", csel.configure), key="menu")
410                 self["menu"] = ChoiceList(menu)
411
412         def isProtected(self):
413                 return self.csel.protectContextMenu and config.ParentalControl.setuppinactive.value and config.ParentalControl.config_sections.context_menus.value
414
415         def pinEntered(self, answer):
416                 if answer:
417                         self.csel.protectContextMenu = False
418                 ProtectedScreen.pinEntered(self, answer)
419
420         def createSummary(self):
421                 return MovieContextMenuSummary
422
423         def okbuttonClick(self):
424                 self.close(self["menu"].getCurrent()[0][1])
425
426         def do_rename(self):
427                 self.close(self.csel.do_rename())
428         def do_copy(self):
429                 self.close(self.csel.do_copy())
430         def do_move(self):
431                 self.close(self.csel.do_move())
432         def do_createdir(self):
433                 self.close(self.csel.do_createdir())
434         def do_delete(self):
435                 self.close(self.csel.do_delete())
436         def do_configure(self):
437                 self.close(self.csel.configure())
438         def do_showNetworkSetup(self):
439                 self.close(self.csel.showNetworkSetup())
440
441         def cancelClick(self):
442                 self.close(None)
443
444 class SelectionEventInfo:
445         def __init__(self):
446                 self["Service"] = ServiceEvent()
447                 self.list.connectSelChanged(self.__selectionChanged)
448                 self.timer = eTimer()
449                 self.timer.callback.append(self.updateEventInfo)
450                 self.onShown.append(self.__selectionChanged)
451
452         def __selectionChanged(self):
453                 if self.execing and self.settings["description"] == MovieList.SHOW_DESCRIPTION:
454                         self.timer.start(100, True)
455
456         def updateEventInfo(self):
457                 serviceref = self.getCurrent()
458                 self["Service"].newService(serviceref)
459
460 class MovieSelectionSummary(Screen):
461         # Kludgy component to display current selection on LCD. Should use
462         # parent.Service as source for everything, but that seems to have a
463         # performance impact as the MovieSelection goes through hoops to prevent
464         # this when the info is not selected
465         def __init__(self, session, parent):
466                 Screen.__init__(self, session, parent = parent)
467                 self["name"] = StaticText("")
468                 self.onShow.append(self.__onShow)
469                 self.onHide.append(self.__onHide)
470
471         def __onShow(self):
472                 self.parent.list.connectSelChanged(self.selectionChanged)
473                 self.selectionChanged()
474
475         def __onHide(self):
476                 self.parent.list.disconnectSelChanged(self.selectionChanged)
477
478         def selectionChanged(self):
479                 item = self.parent.getCurrentSelection()
480                 if item and item[0]:
481                         data = item[3]
482                         if (data is not None) and (data != -1):
483                                 name = data.txt
484                         elif not item[1]:
485                                 # special case, one up
486                                 name = ".."
487                         else:
488                                 name = item[1].getName(item[0])
489                         if (item[0].flags & eServiceReference.mustDescent):
490                                 if len(name) > 12:
491                                         name = os.path.split(os.path.normpath(name))[1]
492                                 name = "> " + name
493                         self["name"].text = name
494                 else:
495                         self["name"].text = ""
496
497 class MovieSelection(Screen, HelpableScreen, SelectionEventInfo, InfoBarBase, ProtectedScreen):
498         # SUSPEND_PAUSES actually means "please call my pauseService()"
499         ALLOW_SUSPEND = Screen.SUSPEND_PAUSES
500
501         def __init__(self, session, selectedmovie = None, timeshiftEnabled = False):
502                 Screen.__init__(self, session)
503                 HelpableScreen.__init__(self)
504                 if not timeshiftEnabled:
505                         InfoBarBase.__init__(self) # For ServiceEventTracker
506                 ProtectedScreen.__init__(self)
507                 self.protectContextMenu = True
508
509                 self.initUserDefinedActions()
510                 self.tags = {}
511                 if selectedmovie:
512                         self.selected_tags = config.movielist.last_selected_tags.value
513                 else:
514                         self.selected_tags = None
515                 self.selected_tags_ele = None
516                 self.nextInBackground = None
517
518                 self.movemode = False
519                 self.bouquet_mark_edit = False
520
521                 self.feedbackTimer = None
522                 self.pathselectEnabled = False
523
524                 self.numericalTextInput = NumericalTextInput.NumericalTextInput(mapping=NumericalTextInput.MAP_SEARCH_UPCASE)
525                 self["chosenletter"] = Label("")
526                 self["chosenletter"].visible = False
527
528                 self["waitingtext"] = Label(_("Please wait... Loading list..."))
529
530                 # create optional description border and hide immediately
531                 self["DescriptionBorder"] = Pixmap()
532                 self["DescriptionBorder"].hide()
533
534                 if not os.path.isdir(config.movielist.last_videodir.value):
535                         config.movielist.last_videodir.value = defaultMoviePath()
536                         config.movielist.last_videodir.save()
537                 self.setCurrentRef(config.movielist.last_videodir.value)
538
539                 self.settings = {\
540                         "listtype": config.movielist.listtype.value,
541                         "moviesort": config.movielist.moviesort.value,
542                         "description": config.movielist.description.value,
543                         "movieoff": config.usage.on_movie_eof.value
544                 }
545                 self.movieOff = self.settings["movieoff"]
546
547                 self["list"] = MovieList(None, list_type=self.settings["listtype"], sort_type=self.settings["moviesort"], descr_state=self.settings["description"])
548
549                 self.loadLocalSettings()
550
551                 self.list = self["list"]
552                 self.selectedmovie = selectedmovie
553
554                 self.playGoTo = None #1 - preview next item / -1 - preview previous
555
556                 title = _("Movie selection")
557                 self.setTitle(title)
558
559                 # Need list for init
560                 SelectionEventInfo.__init__(self)
561
562                 self["key_red"] = Button("")
563                 self["key_green"] = Button("")
564                 self["key_yellow"] = Button("")
565                 self["key_blue"] = Button("")
566                 self._updateButtonTexts()
567
568                 self["movie_off"] = MultiPixmap()
569                 self["movie_off"].hide()
570
571                 self["movie_sort"] = MultiPixmap()
572                 self["movie_sort"].hide()
573
574                 self["freeDiskSpace"] = self.diskinfo = DiskInfo(config.movielist.last_videodir.value, DiskInfo.FREE, update=False)
575
576                 self["InfobarActions"] = HelpableActionMap(self, "InfobarActions",
577                         {
578                                 "showMovies": (self.doPathSelect, _("Select the movie path")),
579                                 "showRadio": (self.btn_radio, boundFunction(self.getinitUserDefinedActionsDescription, "btn_radio")),
580                                 "showTv": (self.btn_tv, boundFunction(self.getinitUserDefinedActionsDescription, "btn_tv")),
581                                 "showText": (self.btn_text, boundFunction(self.getinitUserDefinedActionsDescription, "btn_text")),
582                         })
583
584                 self["NumberActions"] =  NumberActionMap(["NumberActions", "InputAsciiActions"],
585                         {
586                                 "gotAsciiCode": self.keyAsciiCode,
587                                 "0": self.keyNumberGlobal,
588                                 "1": self.keyNumberGlobal,
589                                 "2": self.keyNumberGlobal,
590                                 "3": self.keyNumberGlobal,
591                                 "4": self.keyNumberGlobal,
592                                 "5": self.keyNumberGlobal,
593                                 "6": self.keyNumberGlobal,
594                                 "7": self.keyNumberGlobal,
595                                 "8": self.keyNumberGlobal,
596                                 "9": self.keyNumberGlobal
597                         })
598
599                 self["playbackActions"] = HelpableActionMap(self, "MoviePlayerActions",
600                         {
601                                 "leavePlayer": (self.playbackStop, _("Stop")),
602                                 "moveNext": (self.playNext, _("Play next")),
603                                 "movePrev": (self.playPrev, _("Play previous")),
604                                 "channelUp": (self.moveToFirstOrFirstFile, _("Go to first movie or top of list")),
605                                 "channelDown": (self.moveToLastOrFirstFile, _("Go to first movie or last item")),
606                         })
607                 self["MovieSelectionActions"] = HelpableActionMap(self, "MovieSelectionActions",
608                         {
609                                 "contextMenu": (self.doContext, _("Menu")),
610                                 "showEventInfo": (self.showEventInformation, _("Show event details")),
611                         })
612
613                 self["ColorActions"] = HelpableActionMap(self, "ColorActions",
614                         {
615                                 "red": (self.btn_red, boundFunction(self.getinitUserDefinedActionsDescription, "btn_red")),
616                                 "green": (self.btn_green, boundFunction(self.getinitUserDefinedActionsDescription, "btn_green")),
617                                 "yellow": (self.btn_yellow, boundFunction(self.getinitUserDefinedActionsDescription, "btn_yellow")),
618                                 "blue": (self.btn_blue, boundFunction(self.getinitUserDefinedActionsDescription, "btn_blue")),
619                         })
620                 self["FunctionKeyActions"] = HelpableActionMap(self, "FunctionKeyActions",
621                         {
622                                 "f1": (self.btn_F1, boundFunction(self.getinitUserDefinedActionsDescription, "btn_F1")),
623                                 "f2": (self.btn_F2, boundFunction(self.getinitUserDefinedActionsDescription, "btn_F2")),
624                                 "f3": (self.btn_F3, boundFunction(self.getinitUserDefinedActionsDescription, "btn_F3")),
625                         })
626                 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
627                         {
628                                 "cancel": (self.abort, _("Exit movie list")),
629                                 "ok": (self.itemSelected, _("Select movie")),
630                         })
631                 self["DirectionActions"] = HelpableActionMap(self, "DirectionActions",
632                         {
633                                 "up": (self.keyUp, _("Go up the list")),
634                                 "down": (self.keyDown, _("Go down the list"))
635                         }, prio = -2)
636
637                 tPreview = _("Preview")
638                 tFwd = _("skip forward") + " (" + tPreview +")"
639                 tBack= _("skip backward") + " (" + tPreview +")"
640                 sfwd = lambda: self.seekRelative(1, config.seek.selfdefined_46.value * 90000)
641                 ssfwd = lambda: self.seekRelative(1, config.seek.selfdefined_79.value * 90000)
642                 sback = lambda: self.seekRelative(-1, config.seek.selfdefined_46.value * 90000)
643                 ssback = lambda: self.seekRelative(-1, config.seek.selfdefined_79.value * 90000)
644                 self["SeekActions"] = HelpableActionMap(self, "InfobarSeekActions",
645                         {
646                                 "playpauseService": (self.preview, _("Preview")),
647                                 "seekFwd": (sfwd, tFwd),
648                                 "seekFwdManual": (ssfwd, tFwd),
649                                 "seekBack": (sback, tBack),
650                                 "seekBackManual": (ssback, tBack),
651                         }, prio=5)
652                 self.onShown.append(self.onFirstTimeShown)
653                 self.onLayoutFinish.append(self.saveListsize)
654                 self.list.connectSelChanged(self.updateButtons)
655                 self.onClose.append(self.__onClose)
656                 NavigationInstance.instance.RecordTimer.on_state_change.append(self.list.updateRecordings)
657                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
658                         {
659                                 #iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
660                                 iPlayableService.evStart: self.__serviceStarted,
661                                 iPlayableService.evEOF: self.__evEOF,
662                                 #iPlayableService.evSOF: self.__evSOF,
663                         })
664                 self.onExecBegin.append(self.asciiOn)
665
666         def isProtected(self):
667                 return config.ParentalControl.setuppinactive.value and config.ParentalControl.config_sections.movie_list.value
668
669         def asciiOn(self):
670                 rcinput = eRCInput.getInstance()
671                 rcinput.setKeyboardMode(rcinput.kmAscii)
672
673         def asciiOff(self):
674                 rcinput = eRCInput.getInstance()
675                 rcinput.setKeyboardMode(rcinput.kmNone)
676
677         def initUserDefinedActions(self):
678                 global userDefinedButtons, userDefinedActions, config
679                 if userDefinedButtons is None:
680                         userDefinedActions = {
681                                 'delete': _("Delete"),
682                                 'move': _("Move"),
683                                 'copy': _("Copy"),
684                                 'reset': _("Reset"),
685                                 'tags': _("Tags"),
686                                 'addbookmark': _("Add bookmark"),
687                                 'bookmarks': _("Location"),
688                                 'rename': _("Rename"),
689                                 'gohome': _("Home"),
690                                 'sort': _("Sort"),
691                                 'sortby': _("Sort by"),
692                                 'listtype': _("List type"),
693                                 'preview': _("Preview"),
694                                 'movieoff': _("On end of movie"),
695                                 'movieoff_menu': _("On end of movie (as menu)")
696                         }
697                         for p in plugins.getPlugins(PluginDescriptor.WHERE_MOVIELIST):
698                                 userDefinedActions['@' + p.name] = p.description
699                         locations = []
700                         buildMovieLocationList(locations)
701                         prefix = _("Goto") + ": "
702                         for d,p in locations:
703                                 if p and p.startswith('/'):
704                                         userDefinedActions[p] = prefix + d
705                         config.movielist.btn_red = ConfigSelection(default='delete', choices=userDefinedActions)
706                         config.movielist.btn_green = ConfigSelection(default='move', choices=userDefinedActions)
707                         config.movielist.btn_yellow = ConfigSelection(default='bookmarks', choices=userDefinedActions)
708                         config.movielist.btn_blue = ConfigSelection(default='sort', choices=userDefinedActions)
709                         config.movielist.btn_radio = ConfigSelection(default='tags', choices=userDefinedActions)
710                         config.movielist.btn_tv = ConfigSelection(default='gohome', choices=userDefinedActions)
711                         config.movielist.btn_text = ConfigSelection(default='movieoff', choices=userDefinedActions)
712                         config.movielist.btn_F1 = ConfigSelection(default='movieoff_menu', choices=userDefinedActions)
713                         config.movielist.btn_F2 = ConfigSelection(default='preview', choices=userDefinedActions)
714                         config.movielist.btn_F3 = ConfigSelection(default='/media', choices=userDefinedActions)
715                         userDefinedButtons ={
716                                 'red': config.movielist.btn_red,
717                                 'green': config.movielist.btn_green,
718                                 'yellow': config.movielist.btn_yellow,
719                                 'blue': config.movielist.btn_blue,
720                                 'Radio': config.movielist.btn_radio,
721                                 'TV': config.movielist.btn_tv,
722                                 'Text': config.movielist.btn_text,
723                                 'F1': config.movielist.btn_F1,
724                                 'F2': config.movielist.btn_F2,
725                                 'F3': config.movielist.btn_F3
726                         }
727
728         def getinitUserDefinedActionsDescription(self, key):
729                 return _(userDefinedActions.get(eval("config.movielist." + key + ".value"), _("Not Defined")))
730
731         def _callButton(self, name):
732                 if name.startswith('@'):
733                         item = self.getCurrentSelection()
734                         if isSimpleFile(item):
735                                 name = name[1:]
736                                 for p in plugins.getPlugins(PluginDescriptor.WHERE_MOVIELIST):
737                                         if name == p.name:
738                                                 p(self.session, item[0])
739                 elif name.startswith('/'):
740                         self.gotFilename(name)
741                 else:
742                         try:
743                                 a = getattr(self, 'do_' + name)
744                         except Exception:
745                                 # Undefined action
746                                 return
747                         a()
748
749         def btn_red(self):
750                 self._callButton(config.movielist.btn_red.value)
751         def btn_green(self):
752                 self._callButton(config.movielist.btn_green.value)
753         def btn_yellow(self):
754                 self._callButton(config.movielist.btn_yellow.value)
755         def btn_blue(self):
756                 self._callButton(config.movielist.btn_blue.value)
757         def btn_radio(self):
758                 self._callButton(config.movielist.btn_radio.value)
759         def btn_tv(self):
760                 self._callButton(config.movielist.btn_tv.value)
761         def btn_text(self):
762                 self._callButton(config.movielist.btn_text.value)
763         def btn_F1(self):
764                 self._callButton(config.movielist.btn_F1.value)
765         def btn_F2(self):
766                 self._callButton(config.movielist.btn_F2.value)
767         def btn_F3(self):
768                 self._callButton(config.movielist.btn_F3.value)
769
770         def keyUp(self):
771                 if self["list"].getCurrentIndex() < 1:
772                         self["list"].moveToLast()
773                 else:
774                         self["list"].moveUp()
775
776         def keyDown(self):
777                 if self["list"].getCurrentIndex() == len(self["list"]) - 1:
778                         self["list"].moveToFirst()
779                 else:
780                         self["list"].moveDown()
781
782         def moveToFirstOrFirstFile(self):
783                 if self.list.getCurrentIndex() <= self.list.firstFileEntry: #selection above or on first movie
784                         if self.list.getCurrentIndex() < 1:
785                                 self.list.moveToLast()
786                         else:
787                                 self.list.moveToFirst()
788                 else:
789                         self.list.moveToFirstMovie()
790
791         def moveToLastOrFirstFile(self):
792                 if self.list.getCurrentIndex() >= self.list.firstFileEntry or self.list.firstFileEntry == len(self.list): #selection below or on first movie or no files
793                         if self.list.getCurrentIndex() == len(self.list) - 1:
794                                 self.list.moveToFirst()
795                         else:
796                                 self.list.moveToLast()
797                 else:
798                         self.list.moveToFirstMovie()
799
800         def keyNumberGlobal(self, number):
801                 unichar = self.numericalTextInput.getKey(number)
802                 charstr = unichar.encode("utf-8")
803                 if len(charstr) == 1:
804                         self.list.moveToChar(charstr[0], self["chosenletter"])
805
806         def keyAsciiCode(self):
807                 unichar = unichr(getPrevAsciiCode())
808                 charstr = unichar.encode("utf-8")
809                 if len(charstr) == 1:
810                         self.list.moveToString(charstr[0], self["chosenletter"])
811
812         def isItemPlayable(self, index):
813                 item = self.list.getItem(index)
814                 if item:
815                         path = item.getPath()
816                         if not item.flags & eServiceReference.mustDescent:
817                                 ext = os.path.splitext(path)[1].lower()
818                                 if ext in IMAGE_EXTENSIONS:
819                                         return False
820                                 else:
821                                         return True
822                 return False
823
824         def goToPlayingService(self):
825                 service = self.session.nav.getCurrentlyPlayingServiceOrGroup()
826                 if service:
827                         path = service.getPath()
828                         if path:
829                                 path = os.path.split(os.path.normpath(path))[0]
830                                 if not path.endswith('/'):
831                                         path += '/'
832                                 self.gotFilename(path, selItem = service)
833                                 return True
834                 return False
835
836         def playNext(self):
837                 if self.list.playInBackground:
838                         if self.list.moveTo(self.list.playInBackground):
839                                 if self.isItemPlayable(self.list.getCurrentIndex() + 1):
840                                         self.list.moveDown()
841                                         self.callLater(self.preview)
842                         else:
843                                 self.playGoTo = 1
844                                 self.goToPlayingService()
845                 else:
846                         self.preview()
847
848         def playPrev(self):
849                 if self.list.playInBackground:
850                         if self.list.moveTo(self.list.playInBackground):
851                                 if self.isItemPlayable(self.list.getCurrentIndex() - 1):
852                                         self.list.moveUp()
853                                         self.callLater(self.preview)
854                         else:
855                                 self.playGoTo = -1
856                                 self.goToPlayingService()
857
858         def __onClose(self):
859                 try:
860                         NavigationInstance.instance.RecordTimer.on_state_change.remove(self.list.updateRecordings)
861                 except Exception, e:
862                         print "[ML] failed to unsubscribe:", e
863                         pass
864
865         def createSummary(self):
866                 return MovieSelectionSummary
867
868         def updateDescription(self):
869                 if self.settings["description"] == MovieList.SHOW_DESCRIPTION:
870                         self["DescriptionBorder"].show()
871                         self["list"].instance.resize(eSize(self.listWidth, self.listHeight-self["DescriptionBorder"].instance.size().height()))
872                 else:
873                         self["Service"].newService(None)
874                         self["DescriptionBorder"].hide()
875                         self["list"].instance.resize(eSize(self.listWidth, self.listHeight))
876
877         def pauseService(self):
878                 # Called when pressing Power button (go to standby)
879                 self.playbackStop()
880                 self.session.nav.stopService()
881
882         def unPauseService(self):
883                 # When returning from standby. It might have been a while, so
884                 # reload the list.
885                 self.reloadList()
886
887         def can_delete(self, item):
888                 if not item:
889                         return False
890                 return canDelete(item) or isTrashFolder(item[0])
891         def can_move(self, item):
892                 return canMove(item)
893         def can_default(self, item):
894                 # returns whether item is a regular file
895                 return isSimpleFile(item)
896         def can_sort(self, item):
897                 return True
898         def can_listtype(self, item):
899                 return True
900         def can_preview(self, item):
901                 return isSimpleFile(item)
902
903         def _updateButtonTexts(self):
904                 for k in ('red', 'green', 'yellow', 'blue'):
905                         btn = userDefinedButtons[k]
906                         self['key_' + k].setText(userDefinedActions[btn.value])
907
908         def updateButtons(self):
909                 item = self.getCurrentSelection()
910                 for name in ('red', 'green', 'yellow', 'blue'):
911                         action = userDefinedButtons[name].value
912                         if action.startswith('@'):
913                                 check = self.can_default
914                         elif action.startswith('/'):
915                                 check = self.can_gohome
916                         else:
917                                 try:
918                                         check = getattr(self, 'can_' + action)
919                                 except:
920                                         check = self.can_default
921                         gui = self["key_" + name]
922                         if check(item):
923                                 gui.show()
924                         else:
925                                 gui.hide()
926
927         def showEventInformation(self):
928                 from Screens.EventView import EventViewSimple
929                 from ServiceReference import ServiceReference
930                 evt = self["list"].getCurrentEvent()
931                 if evt:
932                         self.session.open(EventViewSimple, evt, ServiceReference(self.getCurrent()))
933
934         def saveListsize(self):
935                         listsize = self["list"].instance.size()
936                         self.listWidth = listsize.width()
937                         self.listHeight = listsize.height()
938                         self.updateDescription()
939
940         def onFirstTimeShown(self):
941                 self.onShown.remove(self.onFirstTimeShown) # Just once, not after returning etc.
942                 self.show()
943                 self.reloadList(self.selectedmovie, home=True)
944                 del self.selectedmovie
945
946         def getCurrent(self):
947                 # Returns selected serviceref (may be None)
948                 return self["list"].getCurrent()
949
950         def getCurrentSelection(self):
951                 # Returns None or (serviceref, info, begin, len)
952                 return self["list"].l.getCurrentSelection()
953
954         def playAsDVD(self, path):
955                 try:
956                         from Screens import DVD
957                         if path.endswith('VIDEO_TS/'):
958                                 # strip away VIDEO_TS/ part
959                                 path = os.path.split(path.rstrip('/'))[0]
960                         self.session.open(DVD.DVDPlayer, dvd_filelist=[path])
961                         return True
962                 except Exception, e:
963                         print "[ML] DVD Player not installed:", e
964
965         def __serviceStarted(self):
966                 if not self.list.playInBackground:
967                         return
968                 ref = self.session.nav.getCurrentService()
969                 cue = ref.cueSheet()
970                 if not cue:
971                         return
972                 # disable writing the stop position
973                 cue.setCutListEnable(2)
974                 # find "resume" position
975                 cuts = cue.getCutList()
976                 if not cuts:
977                         return
978                 for (pts, what) in cuts:
979                         if what == 3:
980                                 last = pts
981                                 break
982                 else:
983                         # no resume, jump to start of program (first marker)
984                         last = cuts[0][0]
985                 self.doSeekTo = last
986                 self.callLater(self.doSeek)
987
988         def doSeek(self, pts = None):
989                 if pts is None:
990                         pts = self.doSeekTo
991                 seekable = self.getSeek()
992                 if seekable is None:
993                         return
994                 seekable.seekTo(pts)
995
996         def getSeek(self):
997                 service = self.session.nav.getCurrentService()
998                 if service is None:
999                         return None
1000                 seek = service.seek()
1001                 if seek is None or not seek.isCurrentlySeekable():
1002                         return None
1003                 return seek
1004
1005         def callLater(self, function):
1006                 self.previewTimer = eTimer()
1007                 self.previewTimer.callback.append(function)
1008                 self.previewTimer.start(10, True)
1009
1010         def __evEOF(self):
1011                 playInBackground = self.list.playInBackground
1012                 if not playInBackground:
1013                         print "Not playing anything in background"
1014                         return
1015                 self.session.nav.stopService()
1016                 self.list.playInBackground = None
1017                 if config.movielist.play_audio_internal.value:
1018                         index = self.list.findService(playInBackground)
1019                         if index is None:
1020                                 return # Not found?
1021                         next = self.list.getItem(index + 1)
1022                         if not next:
1023                                 return
1024                         path = next.getPath()
1025                         ext = os.path.splitext(path)[1].lower()
1026                         print "Next up:", path
1027                         if ext in AUDIO_EXTENSIONS:
1028                                 self.nextInBackground = next
1029                                 self.callLater(self.preview)
1030                                 self["list"].moveToIndex(index+1)
1031
1032         def preview(self):
1033                 current = self.getCurrent()
1034                 if current is not None:
1035                         path = current.getPath()
1036                         if current.flags & eServiceReference.mustDescent:
1037                                 self.gotFilename(path)
1038                         else:
1039                                 Screens.InfoBar.InfoBar.instance.checkTimeshiftRunning(self.previewCheckTimeshiftCallback)
1040
1041         def startPreview(self):
1042                 if self.nextInBackground is not None:
1043                         current = self.nextInBackground
1044                         self.nextInBackground = None
1045                 else:
1046                         current = self.getCurrent()
1047                 playInBackground = self.list.playInBackground
1048                 if playInBackground:
1049                         self.list.playInBackground = None
1050                         self.session.nav.stopService()
1051                         if playInBackground != current:
1052                                 # come back to play the new one
1053                                 self.callLater(self.preview)
1054                 else:
1055                         self.list.playInBackground = current
1056                         self.session.nav.playService(current)
1057
1058         def previewCheckTimeshiftCallback(self, answer):
1059                 if answer:
1060                         self.startPreview()
1061
1062         def seekRelative(self, direction, amount):
1063                 if self.list.playInBackground:
1064                         seekable = self.getSeek()
1065                         if seekable is None:
1066                                 return
1067                         seekable.seekRelative(direction, amount)
1068
1069         def playbackStop(self):
1070                 if self.list.playInBackground:
1071                         self.list.playInBackground = None
1072                         self.session.nav.stopService()
1073
1074         def itemSelected(self, answer = True):
1075                 current = self.getCurrent()
1076                 if current is not None:
1077                         path = current.getPath()
1078                         if current.flags & eServiceReference.mustDescent:
1079                                 if path.endswith("VIDEO_TS/") or os.path.exists(os.path.join(path, 'VIDEO_TS.IFO')):
1080                                         #force a DVD extention
1081                                         Screens.InfoBar.InfoBar.instance.checkTimeshiftRunning(boundFunction(self.itemSelectedCheckTimeshiftCallback, ".iso", path))
1082                                         return
1083                                 self.gotFilename(path)
1084                         else:
1085                                 ext = os.path.splitext(path)[1].lower()
1086                                 if config.movielist.play_audio_internal.value and (ext in AUDIO_EXTENSIONS):
1087                                         self.preview()
1088                                         return
1089                                 if self.list.playInBackground:
1090                                         # Stop preview, come back later
1091                                         self.session.nav.stopService()
1092                                         self.list.playInBackground = None
1093                                         self.callLater(self.itemSelected)
1094                                         return
1095                                 if ext in IMAGE_EXTENSIONS:
1096                                         try:
1097                                                 from Plugins.Extensions.PicturePlayer import ui
1098                                                 # Build the list for the PicturePlayer UI
1099                                                 filelist = []
1100                                                 index = 0
1101                                                 for item in self.list.list:
1102                                                         p = item[0].getPath()
1103                                                         if p == path:
1104                                                                 index = len(filelist)
1105                                                         if os.path.splitext(p)[1].lower() in IMAGE_EXTENSIONS:
1106                                                                 filelist.append(((p,False), None))
1107                                                 self.session.open(ui.Pic_Full_View, filelist, index, path)
1108                                         except Exception, ex:
1109                                                 print "[ML] Cannot display", str(ex)
1110                                         return
1111                                 Screens.InfoBar.InfoBar.instance.checkTimeshiftRunning(boundFunction(self.itemSelectedCheckTimeshiftCallback, ext, path))
1112
1113         def itemSelectedCheckTimeshiftCallback(self, ext, path, answer):
1114                 if answer:
1115                         if ext in DVD_EXTENSIONS:
1116                                 if self.playAsDVD(path):
1117                                         return
1118                         self.movieSelected()
1119
1120         # Note: DVDBurn overrides this method, hence the itemSelected indirection.
1121         def movieSelected(self):
1122                 current = self.getCurrent()
1123                 if current is not None:
1124                         self.saveconfig()
1125                         self.close(current)
1126
1127         def doContext(self):
1128                 current = self.getCurrent()
1129                 if current is not None:
1130                         self.session.openWithCallback(self.doneContext, MovieContextMenu, self, current)
1131
1132         def doneContext(self, action):
1133                 if action is not None:
1134                         action()
1135
1136         def saveLocalSettings(self):
1137                 if config.movielist.settings_per_directory.value:
1138                         try:
1139                                 path = os.path.join(config.movielist.last_videodir.value, ".e2settings.pkl")
1140                                 pickle.dump(self.settings, open(path, "wb"))
1141                         except Exception, e:
1142                                 print "Failed to save settings to %s: %s" % (path, e)
1143                 # Also set config items, in case the user has a read-only disk
1144                 config.movielist.moviesort.value = self.settings["moviesort"]
1145                 config.movielist.listtype.value = self.settings["listtype"]
1146                 config.movielist.description.value = self.settings["description"]
1147                 config.usage.on_movie_eof.value = self.settings["movieoff"]
1148                 # save moviesort and movieeof values for using by hotkeys
1149                 config.movielist.moviesort.save()
1150                 config.usage.on_movie_eof.save()
1151
1152         def loadLocalSettings(self):
1153                 'Load settings, called when entering a directory'
1154                 if config.movielist.settings_per_directory.value:
1155                         try:
1156                                 path = os.path.join(config.movielist.last_videodir.value, ".e2settings.pkl")
1157                                 updates = pickle.load(open(path, "rb"))
1158                                 self.applyConfigSettings(updates)
1159                         except IOError, e:
1160                                 updates = {
1161                                         "listtype": config.movielist.listtype.default,
1162                                         "moviesort": config.movielist.moviesort.default,
1163                                         "description": config.movielist.description.default,
1164                                         "movieoff": config.usage.on_movie_eof.default
1165                                 }
1166                                 self.applyConfigSettings(updates)
1167                                 pass # ignore fail to open errors
1168                         except Exception, e:
1169                                 print "Failed to load settings from %s: %s" % (path, e)
1170                 else:
1171                         updates = {
1172                                 "listtype": config.movielist.listtype.value,
1173                                 "moviesort": config.movielist.moviesort.value,
1174                                 "description": config.movielist.description.value,
1175                                 "movieoff": config.usage.on_movie_eof.value
1176                                 }
1177                         self.applyConfigSettings(updates)
1178
1179         def applyConfigSettings(self, updates):
1180                 needUpdate = ("description" in updates) and (updates["description"] != self.settings["description"])
1181                 self.settings.update(updates)
1182                 if needUpdate:
1183                         self["list"].setDescriptionState(self.settings["description"])
1184                         self.updateDescription()
1185                 if self.settings["listtype"] != self["list"].list_type:
1186                         self["list"].setListType(self.settings["listtype"])
1187                         needUpdate = True
1188                 if self.settings["moviesort"] != self["list"].sort_type:
1189                         self["list"].setSortType(self.settings["moviesort"])
1190                         needUpdate = True
1191                 if self.settings["movieoff"] != self.movieOff:
1192                         self.movieOff = self.settings["movieoff"]
1193                         needUpdate = True
1194                 config.movielist.moviesort.value = self.settings["moviesort"]
1195                 config.movielist.listtype.value = self.settings["listtype"]
1196                 config.movielist.description.value = self.settings["description"]
1197                 config.usage.on_movie_eof.value = self.settings["movieoff"]
1198                 return needUpdate
1199
1200         def sortBy(self, newType):
1201                 self.settings["moviesort"] = newType
1202                 self.saveLocalSettings()
1203                 self.setSortType(newType)
1204                 self.reloadList()
1205
1206         def listType(self, newType):
1207                 self.settings["listtype"] = newType
1208                 self.saveLocalSettings()
1209                 self.setListType(newType)
1210                 self.reloadList()
1211
1212         def showDescription(self, newType):
1213                 self.settings["description"] = newType
1214                 self.saveLocalSettings()
1215                 self.setDescriptionState(newType)
1216                 self.updateDescription()
1217
1218         def abort(self):
1219                 global playlist
1220                 del playlist[:]
1221                 if self.list.playInBackground:
1222                         self.list.playInBackground = None
1223                         self.session.nav.stopService()
1224                         self.callLater(self.abort)
1225                         return
1226                 self.saveconfig()
1227                 self.close(None)
1228
1229         def saveconfig(self):
1230                 config.movielist.last_selected_tags.value = self.selected_tags
1231
1232         def configure(self):
1233                 self.session.openWithCallback(self.configureDone, MovieBrowserConfiguration)
1234
1235         def configureDone(self, result):
1236                 if result:
1237                         self.applyConfigSettings({\
1238                                 "listtype": config.movielist.listtype.value,
1239                                 "moviesort": config.movielist.moviesort.value,
1240                                 "description": config.movielist.description.value,
1241                                 "movieoff": config.usage.on_movie_eof.value})
1242                         self.saveLocalSettings()
1243                         self._updateButtonTexts()
1244                         self.reloadList()
1245
1246         def can_sortby(self, item):
1247                 return True
1248
1249         def do_sortby(self):
1250                 self.selectSortby()
1251
1252         def selectSortby(self):
1253                 menu = []
1254                 index = 0
1255                 used = 0
1256                 for x in l_moviesort:
1257                         if int(x[0]) == int(config.movielist.moviesort.value):
1258                                 used = index
1259                         menu.append((_(x[1]), x[0], "%d" % index))
1260                         index += 1
1261                 self.session.openWithCallback(self.sortbyMenuCallback, ChoiceBox, title=_("Sort list:"), list=menu, selection = used)
1262
1263         def sortbyMenuCallback(self, choice):
1264                 if choice is None:
1265                         return
1266                 self.sortBy(int(choice[1]))
1267                 self["movie_sort"].setPixmapNum(int(choice[1])-1)
1268
1269         def getTagDescription(self, tag):
1270                 # TODO: access the tag database
1271                 return tag
1272
1273         def updateTags(self):
1274                 # get a list of tags available in this list
1275                 self.tags = self["list"].tags
1276
1277         def setListType(self, type):
1278                 self["list"].setListType(type)
1279
1280         def setDescriptionState(self, val):
1281                 self["list"].setDescriptionState(val)
1282
1283         def setSortType(self, type):
1284                 self["list"].setSortType(type)
1285
1286         def setCurrentRef(self, path):
1287                 self.current_ref = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + path)
1288                 # Magic: this sets extra things to show
1289                 self.current_ref.setName('16384:jpg 16384:png 16384:gif 16384:bmp')
1290
1291         def reloadList(self, sel = None, home = False):
1292                 self.reload_sel = sel
1293                 self.reload_home = home
1294                 self["waitingtext"].visible = True
1295                 self.pathselectEnabled = False
1296                 self.callLater(self.reloadWithDelay)
1297
1298         def reloadWithDelay(self):
1299                 if not os.path.isdir(config.movielist.last_videodir.value):
1300                         path = defaultMoviePath()
1301                         config.movielist.last_videodir.value = path
1302                         config.movielist.last_videodir.save()
1303                         self.setCurrentRef(path)
1304                         self["freeDiskSpace"].path = path
1305                 if self.reload_sel is None:
1306                         self.reload_sel = self.getCurrent()
1307                 self["list"].reload(self.current_ref, self.selected_tags)
1308                 self.updateTags()
1309                 title = _("Recorded files...")
1310                 if config.usage.setup_level.index >= 2: # expert+
1311                         title += "  " + config.movielist.last_videodir.value
1312                 if self.selected_tags:
1313                         title += " - " + ','.join(self.selected_tags)
1314                 self.setTitle(title)
1315                 self.displayMovieOffStatus()
1316                 self.displaySortStatus()
1317                 if not (self.reload_sel and self["list"].moveTo(self.reload_sel)):
1318                         if self.reload_home:
1319                                 self["list"].moveToFirstMovie()
1320                 self["freeDiskSpace"].update()
1321                 self["waitingtext"].visible = False
1322                 self.createPlaylist()
1323                 if self.playGoTo:
1324                         if self.isItemPlayable(self.list.getCurrentIndex() + 1):
1325                                 if self.playGoTo > 0:
1326                                         self.list.moveDown()
1327                                 else:
1328                                         self.list.moveUp()
1329                                 self.playGoTo = None
1330                                 self.callLater(self.preview)
1331                 self.callLater(self.enablePathSelect)
1332
1333         def enablePathSelect(self):
1334                 self.pathselectEnabled = True
1335
1336         def doPathSelect(self):
1337                 if self.pathselectEnabled:
1338                         self.session.openWithCallback(
1339                                 self.gotFilename,
1340                                 MovieLocationBox,
1341                                 _("Please select the movie path..."),
1342                                 config.movielist.last_videodir.value
1343                         )
1344
1345         def gotFilename(self, res, selItem=None, doParentalControl=True):
1346                 def pinEntered(res, selItem, result):
1347                         if result:
1348                                 from Components.ParentalControl import parentalControl
1349                                 parentalControl.setSessionPinCached()
1350                                 self.gotFilename(res, selItem, False)
1351                 if not res:
1352                         return
1353                 # serviceref must end with /
1354                 if not res.endswith('/'):
1355                         res += '/'
1356                 currentDir = config.movielist.last_videodir.value
1357                 if res != currentDir:
1358                         if os.path.isdir(res):
1359                                 baseName = os.path.basename(res[:-1])
1360                                 if doParentalControl and config.ParentalControl.servicepin[0].value and baseName.startswith(".") and not baseName.startswith(".Trash"):
1361                                         self.session.openWithCallback(boundFunction(pinEntered, res, selItem), PinInput, pinList=[x.value for x in config.ParentalControl.servicepin], triesEntry=config.ParentalControl.retries.servicepin, title=_("Please enter the correct pin code"), windowTitle=_("Enter pin code"))
1362                                 else:
1363                                         config.movielist.last_videodir.value = res
1364                                         config.movielist.last_videodir.save()
1365                                         self.loadLocalSettings()
1366                                         self.setCurrentRef(res)
1367                                         self["freeDiskSpace"].path = res
1368                                         if selItem:
1369                                                 self.reloadList(home = True, sel = selItem)
1370                                         else:
1371                                                 self.reloadList(home = True, sel = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + currentDir))
1372                         else:
1373                                 self.session.open(
1374                                         MessageBox,
1375                                         _("Directory %s does not exist.") % (res),
1376                                         type = MessageBox.TYPE_ERROR,
1377                                         timeout = 5
1378                                         )
1379
1380         def showAll(self):
1381                 self.selected_tags_ele = None
1382                 self.selected_tags = None
1383                 self.reloadList(home = True)
1384
1385         def showTagsN(self, tagele):
1386                 if not self.tags:
1387                         self.showTagWarning()
1388                 elif not tagele or (self.selected_tags and tagele.value in self.selected_tags) or not tagele.value in self.tags:
1389                         self.showTagsMenu(tagele)
1390                 else:
1391                         self.selected_tags_ele = tagele
1392                         self.selected_tags = self.tags[tagele.value]
1393                         self.reloadList(home = True)
1394
1395         def showTagsFirst(self):
1396                 self.showTagsN(config.movielist.first_tags)
1397
1398         def showTagsSecond(self):
1399                 self.showTagsN(config.movielist.second_tags)
1400
1401         def can_tags(self, item):
1402                 return self.tags
1403         def do_tags(self):
1404                 self.showTagsN(None)
1405
1406         def tagChosen(self, tag):
1407                 if tag is not None:
1408                         if tag[1] is None: # all
1409                                 self.showAll()
1410                                 return
1411                         # TODO: Some error checking maybe, don't wanna crash on KeyError
1412                         self.selected_tags = self.tags[tag[0]]
1413                         if self.selected_tags_ele:
1414                                 self.selected_tags_ele.value = tag[0]
1415                                 self.selected_tags_ele.save()
1416                         self.reloadList(home = True)
1417
1418         def showTagsMenu(self, tagele):
1419                 self.selected_tags_ele = tagele
1420                 lst = [(_("show all tags"), None)] + [(tag, self.getTagDescription(tag)) for tag in sorted(self.tags)]
1421                 self.session.openWithCallback(self.tagChosen, ChoiceBox, title=_("Please select tag to filter..."), list = lst)
1422
1423         def showTagWarning(self):
1424                 self.session.open(MessageBox, _("No tags are set on these movies."), MessageBox.TYPE_ERROR)
1425
1426         def selectMovieLocation(self, title, callback):
1427                 bookmarks = [("("+_("Other")+"...)", None)]
1428                 buildMovieLocationList(bookmarks)
1429                 self.onMovieSelected = callback
1430                 self.movieSelectTitle = title
1431                 self.session.openWithCallback(self.gotMovieLocation, ChoiceBox, title=title, list = bookmarks)
1432
1433         def gotMovieLocation(self, choice):
1434                 if not choice:
1435                         # cancelled
1436                         self.onMovieSelected(None)
1437                         del self.onMovieSelected
1438                         return
1439                 if isinstance(choice, tuple):
1440                         if choice[1] is None:
1441                                 # Display full browser, which returns string
1442                                 self.session.openWithCallback(
1443                                         self.gotMovieLocation,
1444                                         MovieLocationBox,
1445                                         self.movieSelectTitle,
1446                                         config.movielist.last_videodir.value
1447                                 )
1448                                 return
1449                         choice = choice[1]
1450                 choice = os.path.normpath(choice)
1451                 self.rememberMovieLocation(choice)
1452                 self.onMovieSelected(choice)
1453                 del self.onMovieSelected
1454
1455         def rememberMovieLocation(self, where):
1456                 if where in last_selected_dest:
1457                         last_selected_dest.remove(where)
1458                 last_selected_dest.insert(0, where)
1459                 if len(last_selected_dest) > 5:
1460                         del last_selected_dest[-1]
1461
1462         def can_bookmarks(self, item):
1463                 return True
1464         def do_bookmarks(self):
1465                 self.selectMovieLocation(title=_("Please select the movie path..."), callback=self.gotFilename)
1466
1467         def can_addbookmark(self, item):
1468                 return True
1469         def exist_bookmark(self):
1470                 path = config.movielist.last_videodir.value
1471                 if path in config.movielist.videodirs.value:
1472                         return True
1473                 return False
1474         def do_addbookmark(self):
1475                 path = config.movielist.last_videodir.value
1476                 if path in config.movielist.videodirs.value:
1477                         if len(path) > 40:
1478                                 path = '...' + path[-40:]
1479                         self.session.openWithCallback(self.removeBookmark, MessageBox, _("Do you really want to remove your bookmark of %s?") % path)
1480                 else:
1481                         config.movielist.videodirs.value += [path]
1482                         config.movielist.videodirs.save()
1483         def removeBookmark(self, yes):
1484                 if not yes:
1485                         return
1486                 path = config.movielist.last_videodir.value
1487                 bookmarks = config.movielist.videodirs.value
1488                 bookmarks.remove(path)
1489                 config.movielist.videodirs.value = bookmarks
1490                 config.movielist.videodirs.save()
1491
1492         def can_createdir(self, item):
1493                 return True
1494         def do_createdir(self):
1495                 from Screens.VirtualKeyBoard import VirtualKeyBoard
1496                 self.session.openWithCallback(self.createDirCallback, VirtualKeyBoard,
1497                         title = _("Please enter name of the new directory"),
1498                         text = "")
1499         def createDirCallback(self, name):
1500                 if not name:
1501                         return
1502                 msg = None
1503                 try:
1504                         path = os.path.join(config.movielist.last_videodir.value, name)
1505                         os.mkdir(path)
1506                         if not path.endswith('/'):
1507                                 path += '/'
1508                         self.reloadList(sel = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + path))
1509                 except OSError, e:
1510                         print "Error %s:" % e.errno, e
1511                         if e.errno == 17:
1512                                 msg = _("The path %s already exists.") % name
1513                         else:
1514                                 msg = _("Error") + '\n' + str(e)
1515                 except Exception, e:
1516                         print "[ML] Unexpected error:", e
1517                         msg = _("Error") + '\n' + str(e)
1518                 if msg:
1519                         self.session.open(MessageBox, msg, type = MessageBox.TYPE_ERROR, timeout = 5)
1520
1521         def can_rename(self, item):
1522                 return canMove(item)
1523         def do_rename(self):
1524                 item = self.getCurrentSelection()
1525                 if not canMove(item):
1526                         return
1527                 if isFolder(item):
1528                         p = os.path.split(item[0].getPath())
1529                         if not p[1]:
1530                                 # if path ends in '/', p is blank.
1531                                 p = os.path.split(p[0])
1532                         name = p[1]
1533                 else:
1534                         info = item[1]
1535                         name = info.getName(item[0])
1536                 from Screens.VirtualKeyBoard import VirtualKeyBoard
1537                 self.session.openWithCallback(self.renameCallback, VirtualKeyBoard,
1538                         title = _("Rename"),
1539                         text = name)
1540
1541         def do_decode(self):
1542                 from ServiceReference import ServiceReference
1543                 item = self.getCurrentSelection()
1544                 info = item[1]
1545                 serviceref = ServiceReference(None, reftype = eServiceReference.idDVB, path = item[0].getPath())
1546                 name = info.getName(item[0]) + ' - decoded'
1547                 description = info.getInfoString(item[0], iServiceInformation.sDescription)
1548                 recording = RecordTimer.RecordTimerEntry(serviceref, int(time.time()), int(time.time()) + 3600, name, description, 0, dirname = preferredTimerPath())
1549                 recording.dontSave = True
1550                 recording.autoincrease = True
1551                 recording.setAutoincreaseEnd()
1552                 self.session.nav.RecordTimer.record(recording, ignoreTSC = True)
1553
1554         def renameCallback(self, name):
1555                 if not name:
1556                         return
1557                 name = name.strip()
1558                 item = self.getCurrentSelection()
1559                 if item and item[0]:
1560                         try:
1561                                 path = item[0].getPath().rstrip('/')
1562                                 meta = path + '.meta'
1563                                 if os.path.isfile(meta):
1564                                         metafile = open(meta, "r+")
1565                                         sid = metafile.readline()
1566                                         oldtitle = metafile.readline()
1567                                         rest = metafile.read()
1568                                         metafile.seek(0)
1569                                         metafile.write("%s%s\n%s" %(sid, name, rest))
1570                                         metafile.truncate()
1571                                         metafile.close()
1572                                         index = self.list.getCurrentIndex()
1573                                         info = self.list.list[index]
1574                                         if hasattr(info[3], 'txt'):
1575                                                 info[3].txt = name
1576                                         else:
1577                                                 self.list.invalidateCurrentItem()
1578                                         return
1579                                 pathname,filename = os.path.split(path)
1580                                 newpath = os.path.join(pathname, name)
1581                                 msg = None
1582                                 print "[ML] rename", path, "to", newpath
1583                                 os.rename(path, newpath)
1584                                 self.reloadList(sel = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + newpath))
1585                         except OSError, e:
1586                                 print "Error %s:" % e.errno, e
1587                                 if e.errno == 17:
1588                                         msg = _("The path %s already exists.") % name
1589                                 else:
1590                                         msg = _("Error") + '\n' + str(e)
1591                         except Exception, e:
1592                                 import traceback
1593                                 print "[ML] Unexpected error:", e
1594                                 traceback.print_exc()
1595                                 msg = _("Error") + '\n' + str(e)
1596                         if msg:
1597                                 self.session.open(MessageBox, msg, type = MessageBox.TYPE_ERROR, timeout = 5)
1598
1599         def do_reset(self):
1600                 current = self.getCurrent()
1601                 if current:
1602                         resetMoviePlayState(current.getPath() + ".cuts", current)
1603                         self["list"].invalidateCurrentItem() # trigger repaint
1604
1605         def do_move(self):
1606                 item = self.getCurrentSelection()
1607                 if canMove(item):
1608                         current = item[0]
1609                         info = item[1]
1610                         if info is None:
1611                                 # Special case
1612                                 return
1613                         name = info and info.getName(current) or _("this recording")
1614                         path = os.path.normpath(current.getPath())
1615                         # show a more limited list of destinations, no point
1616                         # in showing mountpoints.
1617                         title = _("Select destination for:") + " " + name
1618                         bookmarks = [("("+_("Other")+"...)", None)]
1619                         inlist = []
1620                         # Subdirs
1621                         try:
1622                                 base = os.path.split(path)[0]
1623                                 for fn in os.listdir(base):
1624                                         if not fn.startswith('.'): # Skip hidden things
1625                                                 d = os.path.join(base, fn)
1626                                                 if os.path.isdir(d) and (d not in inlist):
1627                                                         bookmarks.append((fn,d))
1628                                                         inlist.append(d)
1629                         except Exception, e :
1630                                 print "[MovieSelection]", e
1631                         # Last favourites
1632                         for d in last_selected_dest:
1633                                 if d not in inlist:
1634                                         bookmarks.append((d,d))
1635                         # Other favourites
1636                         for d in config.movielist.videodirs.value:
1637                                 d = os.path.normpath(d)
1638                                 bookmarks.append((d,d))
1639                                 inlist.append(d)
1640                         for p in Components.Harddisk.harddiskmanager.getMountedPartitions():
1641                                 d = os.path.normpath(p.mountpoint)
1642                                 if d not in inlist:
1643                                         bookmarks.append((p.description, d))
1644                                         inlist.append(d)
1645                         self.onMovieSelected = self.gotMoveMovieDest
1646                         self.movieSelectTitle = title
1647                         self.session.openWithCallback(self.gotMovieLocation, ChoiceBox, title=title, list=bookmarks)
1648
1649         def gotMoveMovieDest(self, choice):
1650                 if not choice:
1651                         return
1652                 dest = os.path.normpath(choice)
1653                 try:
1654                         item = self.getCurrentSelection()
1655                         current = item[0]
1656                         if item[1] is None:
1657                                 name = None
1658                         else:
1659                                 name = item[1].getName(current)
1660                         moveServiceFiles(current, dest, name)
1661                         self["list"].removeService(current)
1662                 except Exception, e:
1663                         self.session.open(MessageBox, str(e), MessageBox.TYPE_ERROR)
1664
1665         def can_copy(self, item):
1666                 return canCopy(item)
1667         def do_copy(self):
1668                 item = self.getCurrentSelection()
1669                 if canMove(item):
1670                         current = item[0]
1671                         info = item[1]
1672                         if info is None:
1673                                 # Special case
1674                                 return
1675                         name = info and info.getName(current) or _("this recording")
1676                         self.selectMovieLocation(title=_("Select copy destination for:") + " " + name, callback=self.gotCopyMovieDest)
1677
1678         def gotCopyMovieDest(self, choice):
1679                 if not choice:
1680                         return
1681                 dest = os.path.normpath(choice)
1682                 try:
1683                         item = self.getCurrentSelection()
1684                         current = item[0]
1685                         if item[1] is None:
1686                                 name = None
1687                         else:
1688                                 name = item[1].getName(current)
1689                         copyServiceFiles(current, dest, name)
1690                 except Exception, e:
1691                         self.session.open(MessageBox, str(e), MessageBox.TYPE_ERROR)
1692
1693         def stopTimer(self, timer):
1694                 if timer.isRunning():
1695                         if timer.repeated:
1696                                 timer.enable()
1697                                 timer.processRepeated(findRunningEvent = False)
1698                                 self.session.nav.RecordTimer.doActivate(timer)
1699                         else:
1700                                 timer.afterEvent = RecordTimer.AFTEREVENT.NONE
1701                                 NavigationInstance.instance.RecordTimer.removeEntry(timer)
1702
1703         def onTimerChoice(self, choice):
1704                 if isinstance(choice, tuple) and choice[1]:
1705                         choice, timer = choice[1]
1706                         if not choice:
1707                                 # cancel
1708                                 return
1709                         if "s" in choice:
1710                                 self.stopTimer(timer)
1711                         if "d" in choice:
1712                                 self.delete(True)
1713
1714         def do_delete(self):
1715                 self.delete()
1716
1717         def delete(self, *args):
1718                 if args and (not args[0]):
1719                         # cancelled by user (passing any arg means it's a dialog return)
1720                         return
1721                 item = self.getCurrentSelection()
1722                 if not canDelete(item):
1723                         if item and isTrashFolder(item[0]):
1724                                 # Red button to empty trashcan...
1725                                 self.purgeAll()
1726                         return
1727                 current = item[0]
1728                 info = item[1]
1729                 cur_path = os.path.realpath(current.getPath())
1730                 st = os.stat(cur_path)
1731                 name = info and info.getName(current) or _("this recording")
1732                 are_you_sure = _("Do you really want to delete %s?") % (name)
1733                 if current.flags & eServiceReference.mustDescent:
1734                         files = 0
1735                         subdirs = 0
1736                         if args:
1737                                 # already confirmed...
1738                                 # but not implemented yet...
1739                                 msg = ''
1740                                 if config.usage.movielist_trashcan.value:
1741                                         try:
1742                                                 # Move the files to the trash can in a way that their CTIME is
1743                                                 # set to "now". A simple move would not correctly update the
1744                                                 # ctime, and hence trigger a very early purge.
1745                                                 trash = Tools.Trashcan.createTrashFolder(cur_path)
1746                                                 trash = os.path.join(trash, os.path.split(cur_path)[1])
1747                                                 os.mkdir(trash)
1748                                                 for root, dirnames, filenames in os.walk(cur_path):
1749                                                         trashroot = os.path.join(trash, root[len(cur_path)+1:])
1750                                                         for fn in filenames:
1751                                                                 print "Move %s -> %s" % (os.path.join(root, fn), os.path.join(trashroot, fn))
1752                                                                 os.rename(os.path.join(root, fn), os.path.join(trashroot, fn))
1753                                                         for dn in dirnames:
1754                                                                 print "MkDir", os.path.join(trashroot, dn)
1755                                                                 os.mkdir(os.path.join(trashroot, dn))
1756                                                 # second pass to remove the empty directories
1757                                                 for root, dirnames, filenames in os.walk(cur_path, topdown=False):
1758                                                         for dn in dirnames:
1759                                                                 print "rmdir", os.path.join(trashroot, dn)
1760                                                                 os.rmdir(os.path.join(root, dn))
1761                                                 os.rmdir(cur_path)
1762                                                 self["list"].removeService(current)
1763                                                 self.showActionFeedback(_("Deleted") + " " + name)
1764                                                 # Files were moved to .Trash, ok.
1765                                                 return
1766                                         except OSError, e:
1767                                                 print "[MovieSelection] Cannot move to trash", e
1768                                                 if e.errno == 18:
1769                                                         # This occurs when moving across devices
1770                                                         msg = _("Cannot move files on a different disk or system to the trash can") + ". "
1771                                                 else:
1772                                                         msg = _("Cannot move to trash can") + ".\n" + str(e) + "\n"
1773                                         except Exception, e:
1774                                                 print "[MovieSelection] Weird error moving to trash", e
1775                                                 # Failed to create trash or move files.
1776                                                 msg = _("Cannot move to trash can") + "\n" + str(e) + "\n"
1777                                 msg += _("Sorry, deleting directories can (for now) only be done through the trash can.")
1778                                 self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR)
1779                                 return
1780                         for fn in os.listdir(cur_path):
1781                                 if (fn != '.') and (fn != '..'):
1782                                         ffn = os.path.join(cur_path, fn)
1783                                         if os.path.isdir(ffn):
1784                                                 subdirs += 1
1785                                         else:
1786                                                 files += 1
1787                         if files or subdirs:
1788                                 msg = _("Directory contains %s and %s.") % (ngettext("%d file", "%d files", files) % files, ngettext("%d subdirectory", "%d subdirectories", subdirs) % subdirs) + '\n' + are_you_sure
1789                                 if isInTrashFolder(current):
1790                                         # Red button to empty trashcan item or subdir
1791                                         msg = _("Deleted items") + "\n" + msg
1792                                         callback = self.purgeConfirmed
1793                                 else:
1794                                         callback = self.delete
1795                                 self.session.openWithCallback(callback, MessageBox, msg)
1796                                 return
1797                         else:
1798                                 try:
1799                                         os.rmdir(cur_path)
1800                                 except Exception, e:
1801                                         print "[MovieSelection] Failed delete", e
1802                                         self.session.open(MessageBox, _("Delete failed!") + "\n" + str(e), MessageBox.TYPE_ERROR)
1803                                 else:
1804                                         self["list"].removeService(current)
1805                                         self.showActionFeedback(_("Deleted") + " " + name)
1806                 else:
1807                         if not args:
1808                                 rec_filename = os.path.split(current.getPath())[1]
1809                                 if rec_filename.endswith(".ts"): rec_filename = rec_filename[:-3]
1810                                 for timer in NavigationInstance.instance.RecordTimer.timer_list:
1811                                         if timer.isRunning() and not timer.justplay and rec_filename in timer.Filename:
1812                                                 choices = [
1813                                                         (_("Cancel"), None),
1814                                                         (_("Stop recording"), ("s", timer)),
1815                                                         (_("Stop recording and delete"), ("sd", timer))]
1816                                                 self.session.openWithCallback(self.onTimerChoice, ChoiceBox, title=_("Recording in progress") + ":\n%s" % name, list=choices)
1817                                                 return
1818                                 if time.time() - st.st_mtime < 5:
1819                                         if not args:
1820                                                 self.session.openWithCallback(self.delete, MessageBox, _("File appears to be busy.\n") + are_you_sure)
1821                                                 return
1822                         if config.usage.movielist_trashcan.value:
1823                                 try:
1824                                         trash = Tools.Trashcan.createTrashFolder(cur_path)
1825                                         # Also check whether we're INSIDE the trash, then it's a purge.
1826                                         if cur_path.startswith(trash):
1827                                                 msg = _("Deleted items") + "\n"
1828                                         else:
1829                                                 moveServiceFiles(current, trash, name, allowCopy=False)
1830                                                 self["list"].removeService(current)
1831                                                 # Files were moved to .Trash, ok.
1832                                                 from Screens.InfoBarGenerics import delResumePoint
1833                                                 delResumePoint(current)
1834                                                 self.showActionFeedback(_("Deleted") + " " + name)
1835                                                 return
1836                                 except OSError, e:
1837                                         print "[MovieSelection] Cannot move to trash", e
1838                                         if e.errno == 18:
1839                                                 # This occurs when moving across devices
1840                                                 msg = _("Cannot move files on a different disk or system to the trash can") + ". "
1841                                         else:
1842                                                 msg = _("Cannot move to trash can") + ".\n" + str(e) + "\n"
1843                                 except Exception, e:
1844                                         print "[MovieSelection] Weird error moving to trash", e
1845                                         # Failed to create trash or move files.
1846                                         msg = _("Cannot move to trash can") + "\n" + str(e) + "\n"
1847                         else:
1848                                 msg = ''
1849                         self.session.openWithCallback(self.deleteConfirmed, MessageBox, msg + are_you_sure)
1850
1851         def deleteConfirmed(self, confirmed):
1852                 if not confirmed:
1853                         return
1854                 item = self.getCurrentSelection()
1855                 if item is None:
1856                         return # huh?
1857                 current = item[0]
1858                 info = item[1]
1859                 name = info and info.getName(current) or _("this recording")
1860                 serviceHandler = eServiceCenter.getInstance()
1861                 offline = serviceHandler.offlineOperations(current)
1862                 try:
1863                         if offline is None:
1864                                 from enigma import eBackgroundFileEraser
1865                                 eBackgroundFileEraser.getInstance().erase(os.path.realpath(current.getPath()))
1866                         else:
1867                                 if offline.deleteFromDisk(0):
1868                                         raise Exception, "Offline delete failed"
1869                         self["list"].removeService(current)
1870                         from Screens.InfoBarGenerics import delResumePoint
1871                         delResumePoint(current)
1872                         self.showActionFeedback(_("Deleted") + " " + name)
1873                 except Exception, ex:
1874                         self.session.open(MessageBox, _("Delete failed!") + "\n" + name + "\n" + str(ex), MessageBox.TYPE_ERROR)
1875
1876
1877         def purgeAll(self):
1878                 recordings = self.session.nav.getRecordings()
1879                 next_rec_time = -1
1880                 msg = _("Permanently delete all recordings in the trash can?")
1881                 if not recordings:
1882                         next_rec_time = self.session.nav.RecordTimer.getNextRecordingTime()
1883                 if recordings or (next_rec_time > 0 and (next_rec_time - time.time()) < 120):
1884                         msg += "\n" + _("Recording(s) are in progress or coming up in few seconds!")
1885                 self.session.openWithCallback(self.purgeConfirmed, MessageBox, msg)
1886
1887         def purgeConfirmed(self, confirmed):
1888                 if not confirmed:
1889                         return
1890                 item = self.getCurrentSelection()
1891                 current = item[0]
1892                 cur_path = os.path.realpath(current.getPath())
1893                 Tools.Trashcan.cleanAll(cur_path)
1894
1895         def showNetworkSetup(self):
1896                 import NetworkSetup
1897                 self.session.open(NetworkSetup.NetworkAdapterSelection)
1898
1899         def showActionFeedback(self, text):
1900                 if self.feedbackTimer is None:
1901                         self.feedbackTimer = eTimer()
1902                         self.feedbackTimer.callback.append(self.hideActionFeedback)
1903                 else:
1904                         self.feedbackTimer.stop()
1905                 self.feedbackTimer.start(3000, 1)
1906                 self.diskinfo.setText(text)
1907
1908         def hideActionFeedback(self):
1909                 print "[ML] hide feedback"
1910                 self.diskinfo.update()
1911
1912         def can_gohome(self, item):
1913                 return True
1914
1915         def do_gohome(self):
1916                 self.gotFilename(defaultMoviePath())
1917
1918         def do_sort(self):
1919                 index = 0
1920                 for index, item in enumerate(l_moviesort):
1921                         if int(item[0]) == int(config.movielist.moviesort.value):
1922                                 break
1923                 if index >= len(l_moviesort) - 1:
1924                         index = 0
1925                 else:
1926                         index += 1
1927                 #descriptions in native languages too long...
1928                 sorttext = l_moviesort[index][2]
1929                 if config.movielist.btn_red.value == "sort": self['key_red'].setText(sorttext)
1930                 if config.movielist.btn_green.value == "sort": self['key_green'].setText(sorttext)
1931                 if config.movielist.btn_yellow.value == "sort": self['key_yellow'].setText(sorttext)
1932                 if config.movielist.btn_blue.value == "sort": self['key_blue'].setText(sorttext)
1933                 self.sorttimer = eTimer()
1934                 self.sorttimer.callback.append(self._updateButtonTexts)
1935                 self.sorttimer.start(1500, True) #time for displaying sorting type just applied
1936                 self.sortBy(int(l_moviesort[index][0]))
1937                 self["movie_sort"].setPixmapNum(int(l_moviesort[index][0])-1)
1938
1939         def do_listtype(self):
1940                 index = 0
1941                 for index, item in enumerate(l_listtype):
1942                         if int(item[0]) == int(config.movielist.listtype.value):
1943                                 break
1944                 if index >= len(l_listtype) - 1:
1945                         index = 0
1946                 else:
1947                         index += 1
1948                 self.listType(int(l_listtype[index][0]))
1949
1950         def do_preview(self):
1951                 self.preview()
1952
1953         def displaySortStatus(self):
1954                 self["movie_sort"].setPixmapNum(int(config.movielist.moviesort.value)-1)
1955                 self["movie_sort"].show()
1956
1957         def can_movieoff(self, item):
1958                 return True
1959
1960         def do_movieoff(self):
1961                 self.setNextMovieOffStatus()
1962                 self.displayMovieOffStatus()
1963
1964         def displayMovieOffStatus(self):
1965                 self["movie_off"].setPixmapNum(config.usage.on_movie_eof.getIndex())
1966                 self["movie_off"].show()
1967
1968         def setNextMovieOffStatus(self):
1969                 config.usage.on_movie_eof.selectNext()
1970                 self.settings["movieoff"] = config.usage.on_movie_eof.value
1971                 self.saveLocalSettings()
1972
1973         def can_movieoff_menu(self, item):
1974                 return True
1975
1976         def do_movieoff_menu(self):
1977                 current_movie_eof = config.usage.on_movie_eof.value
1978                 menu = []
1979                 for x in config.usage.on_movie_eof.choices:
1980                         config.usage.on_movie_eof.value = x
1981                         menu.append((config.usage.on_movie_eof.getText(), x))
1982                 config.usage.on_movie_eof.value = current_movie_eof
1983                 used = config.usage.on_movie_eof.getIndex()
1984                 self.session.openWithCallback(self.movieoffMenuCallback, ChoiceBox, title = _("On end of movie"), list = menu, selection = used)
1985
1986         def movieoffMenuCallback(self, choice):
1987                 if choice is None:
1988                         return
1989                 self.settings["movieoff"] = choice[1]
1990                 self.saveLocalSettings()
1991                 self.displayMovieOffStatus()
1992
1993         def createPlaylist(self):
1994                 global playlist
1995                 items = playlist
1996                 del items[:]
1997                 for index, item in enumerate(self["list"]):
1998                         if item:
1999                                 item = item[0]
2000                                 path = item.getPath()
2001                                 if not item.flags & eServiceReference.mustDescent:
2002                                         ext = os.path.splitext(path)[1].lower()
2003                                         if ext in IMAGE_EXTENSIONS:
2004                                                 continue
2005                                         else:
2006                                                 items.append(item)
2007
2008 playlist = []