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
19 from Plugins.Plugin import PluginDescriptor
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
28 from Tools import NumericalTextInput
29 from Tools.Directories import resolveFilename, SCOPE_HDD
30 from Tools.BoundFunction import boundFunction
32 import NavigationInstance
35 from enigma import eServiceReference, eServiceCenter, eTimer, eSize, iPlayableService, iServiceInformation, getPrevAsciiCode, eRCInput
38 import cPickle as pickle
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)
54 userDefinedButtons = None
55 last_selected_dest = []
56 preferredTagEditor = None
58 # this kludge is needed because ConfigSelection only takes numbers
59 # and someone appears to be fascinated by 'enums'.
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"))]
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()
81 def setPreferredTagEditor(te):
82 global preferredTagEditor
83 if preferredTagEditor is None:
84 preferredTagEditor = te
85 print "Preferred tag editor changed to", preferredTagEditor
87 print "Preferred tag editor already set to", preferredTagEditor, "ignoring", te
89 def getPreferredTagEditor():
90 global preferredTagEditor
91 return preferredTagEditor
93 def isTrashFolder(ref):
94 if not config.usage.movielist_trashcan.value or not ref.flags & eServiceReference.mustDescent:
96 path = os.path.realpath(ref.getPath())
97 return path.endswith('.Trash') and path.startswith(Tools.Trashcan.getTrashFolder(path))
99 def isInTrashFolder(ref):
100 if not config.usage.movielist_trashcan.value or not ref.flags & eServiceReference.mustDescent:
102 path = os.path.realpath(ref.getPath())
103 return path.startswith(Tools.Trashcan.getTrashFolder(path))
105 def isSimpleFile(item):
108 if not item[0] or not item[1]:
110 return (item[0].flags & eServiceReference.mustDescent) == 0
115 if not item[0] or not item[1]:
117 return (item[0].flags & eServiceReference.mustDescent) != 0
123 if not item[0] or not item[1]:
125 if item[0].flags & eServiceReference.mustDescent:
126 return not isTrashFolder(item[0])
134 if not item[0] or not item[1]:
136 if item[0].flags & eServiceReference.mustDescent:
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)))
163 def moveServiceFiles(serviceref, dest, name=None, allowCopy=True):
164 moveList = createMoveList(serviceref, dest)
165 # Try to "atomically" move these files
169 for item in moveList:
170 os.rename(item[0], item[1])
171 movedList.append(item)
173 if e.errno == 18 and allowCopy:
174 print "[MovieSelection] cannot rename across devices, trying slow move"
176 # start with the smaller files, do the big one later.
179 name = os.path.split(moveList[-1][0])[1]
180 CopyFiles.moveFiles(moveList, name)
181 print "[MovieSelection] Moving in background..."
185 print "[MovieSelection] Failed move:", e
186 for item in movedList:
188 os.rename(item[1], item[0])
190 print "[MovieSelection] Failed to undo move:", item
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
200 for item in moveList:
201 os.link(item[0], item[1])
202 movedList.append(item)
203 # this worked, we're done
206 print "[MovieSelection] Failed copy using link:", e
207 for item in movedList:
211 print "[MovieSelection] Failed to undo copy:", item
212 #Link failed, really copy.
214 # start with the smaller files, do the big one later.
217 name = os.path.split(moveList[-1][0])[1]
218 CopyFiles.copyFiles(moveList, name)
219 print "[MovieSelection] Copying in background..."
221 # Appends possible destinations to the bookmarks object. Appends tuples
222 # in the form (description, path) to it.
223 def buildMovieLocationList(bookmarks):
225 for d in config.movielist.videodirs.value:
226 d = os.path.normpath(d)
227 bookmarks.append((d,d))
229 for p in Components.Harddisk.harddiskmanager.getMountedPartitions():
230 d = os.path.normpath(p.mountpoint)
232 # improve shortcuts to mountpoints
234 bookmarks[bookmarks.index((d,d))] = (p.tabbedDescription(), d)
236 pass # When already listed as some "friendly" name
238 bookmarks.append((p.tabbedDescription(), d))
240 for d in last_selected_dest:
242 bookmarks.append((d,d))
244 class MovieBrowserConfiguration(ConfigListScreen,Screen):
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" />
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" />
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>
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()
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))
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),
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"],
293 "cancel": self.cancel,
297 self.onChangedEntry = []
300 def changedEntry(self):
301 for x in self.onChangedEntry:
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
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
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()
328 if self["config"].isChanged():
329 self.session.openWithCallback(self.cancelCallback, MessageBox, _("Really close without saving settings?"))
331 self.cancelCallback(True)
333 def cancelCallback(self, answer):
335 for x in self["config"].list:
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)
347 self.parent["menu"].onSelectionChanged.append(self.selectionChanged)
348 self.selectionChanged()
351 self.parent["menu"].onSelectionChanged.remove(self.selectionChanged)
353 def selectionChanged(self):
354 item = self.parent["menu"].getCurrent()
355 self["selected"].text = item[0][0]
357 from Screens.ParentalControlSetup import ProtectedScreen
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)
364 ProtectedScreen.__init__(self)
366 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "NumberActions", "MenuActions"],
368 "ok": self.okbuttonClick,
369 "cancel": self.cancelClick,
370 "yellow": self.do_showNetworkSetup,
371 "menu": self.do_configure,
375 "7": self.do_createdir,
379 def append_to_menu(menu, args, key=""):
380 menu.append(ChoiceEntryComponent(key, args))
384 if (service.flags & eServiceReference.mustDescent):
385 if isTrashFolder(service):
386 append_to_menu(menu, (_("Permanently remove all deleted items"), csel.purgeAll), key="8")
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")
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))
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))
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)
412 def isProtected(self):
413 return self.csel.protectContextMenu and config.ParentalControl.setuppinactive.value and config.ParentalControl.config_sections.context_menus.value
415 def pinEntered(self, answer):
417 self.csel.protectContextMenu = False
418 ProtectedScreen.pinEntered(self, answer)
420 def createSummary(self):
421 return MovieContextMenuSummary
423 def okbuttonClick(self):
424 self.close(self["menu"].getCurrent()[0][1])
427 self.close(self.csel.do_rename())
429 self.close(self.csel.do_copy())
431 self.close(self.csel.do_move())
432 def do_createdir(self):
433 self.close(self.csel.do_createdir())
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())
441 def cancelClick(self):
444 class SelectionEventInfo:
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)
452 def __selectionChanged(self):
453 if self.execing and self.settings["description"] == MovieList.SHOW_DESCRIPTION:
454 self.timer.start(100, True)
456 def updateEventInfo(self):
457 serviceref = self.getCurrent()
458 self["Service"].newService(serviceref)
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)
472 self.parent.list.connectSelChanged(self.selectionChanged)
473 self.selectionChanged()
476 self.parent.list.disconnectSelChanged(self.selectionChanged)
478 def selectionChanged(self):
479 item = self.parent.getCurrentSelection()
482 if (data is not None) and (data != -1):
485 # special case, one up
488 name = item[1].getName(item[0])
489 if (item[0].flags & eServiceReference.mustDescent):
491 name = os.path.split(os.path.normpath(name))[1]
493 self["name"].text = name
495 self["name"].text = ""
497 class MovieSelection(Screen, HelpableScreen, SelectionEventInfo, InfoBarBase, ProtectedScreen):
498 # SUSPEND_PAUSES actually means "please call my pauseService()"
499 ALLOW_SUSPEND = Screen.SUSPEND_PAUSES
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
509 self.initUserDefinedActions()
512 self.selected_tags = config.movielist.last_selected_tags.value
514 self.selected_tags = None
515 self.selected_tags_ele = None
516 self.nextInBackground = None
518 self.movemode = False
519 self.bouquet_mark_edit = False
521 self.feedbackTimer = None
522 self.pathselectEnabled = False
524 self.numericalTextInput = NumericalTextInput.NumericalTextInput(mapping=NumericalTextInput.MAP_SEARCH_UPCASE)
525 self["chosenletter"] = Label("")
526 self["chosenletter"].visible = False
528 self["waitingtext"] = Label(_("Please wait... Loading list..."))
530 # create optional description border and hide immediately
531 self["DescriptionBorder"] = Pixmap()
532 self["DescriptionBorder"].hide()
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)
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
545 self.movieOff = self.settings["movieoff"]
547 self["list"] = MovieList(None, list_type=self.settings["listtype"], sort_type=self.settings["moviesort"], descr_state=self.settings["description"])
549 self.loadLocalSettings()
551 self.list = self["list"]
552 self.selectedmovie = selectedmovie
554 self.playGoTo = None #1 - preview next item / -1 - preview previous
556 title = _("Movie selection")
560 SelectionEventInfo.__init__(self)
562 self["key_red"] = Button("")
563 self["key_green"] = Button("")
564 self["key_yellow"] = Button("")
565 self["key_blue"] = Button("")
566 self._updateButtonTexts()
568 self["movie_off"] = MultiPixmap()
569 self["movie_off"].hide()
571 self["movie_sort"] = MultiPixmap()
572 self["movie_sort"].hide()
574 self["freeDiskSpace"] = self.diskinfo = DiskInfo(config.movielist.last_videodir.value, DiskInfo.FREE, update=False)
576 self["InfobarActions"] = HelpableActionMap(self, "InfobarActions",
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")),
584 self["NumberActions"] = NumberActionMap(["NumberActions", "InputAsciiActions"],
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
599 self["playbackActions"] = HelpableActionMap(self, "MoviePlayerActions",
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")),
607 self["MovieSelectionActions"] = HelpableActionMap(self, "MovieSelectionActions",
609 "contextMenu": (self.doContext, _("Menu")),
610 "showEventInfo": (self.showEventInformation, _("Show event details")),
613 self["ColorActions"] = HelpableActionMap(self, "ColorActions",
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")),
620 self["FunctionKeyActions"] = HelpableActionMap(self, "FunctionKeyActions",
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")),
626 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
628 "cancel": (self.abort, _("Exit movie list")),
629 "ok": (self.itemSelected, _("Select movie")),
631 self["DirectionActions"] = HelpableActionMap(self, "DirectionActions",
633 "up": (self.keyUp, _("Go up the list")),
634 "down": (self.keyDown, _("Go down the list"))
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",
646 "playpauseService": (self.preview, _("Preview")),
647 "seekFwd": (sfwd, tFwd),
648 "seekFwdManual": (ssfwd, tFwd),
649 "seekBack": (sback, tBack),
650 "seekBackManual": (ssback, tBack),
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=
659 #iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
660 iPlayableService.evStart: self.__serviceStarted,
661 iPlayableService.evEOF: self.__evEOF,
662 #iPlayableService.evSOF: self.__evSOF,
664 self.onExecBegin.append(self.asciiOn)
666 def isProtected(self):
667 return config.ParentalControl.setuppinactive.value and config.ParentalControl.config_sections.movie_list.value
670 rcinput = eRCInput.getInstance()
671 rcinput.setKeyboardMode(rcinput.kmAscii)
674 rcinput = eRCInput.getInstance()
675 rcinput.setKeyboardMode(rcinput.kmNone)
677 def initUserDefinedActions(self):
678 global userDefinedButtons, userDefinedActions, config
679 if userDefinedButtons is None:
680 userDefinedActions = {
681 'delete': _("Delete"),
686 'addbookmark': _("Add bookmark"),
687 'bookmarks': _("Location"),
688 'rename': _("Rename"),
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)")
697 for p in plugins.getPlugins(PluginDescriptor.WHERE_MOVIELIST):
698 userDefinedActions['@' + p.name] = p.description
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
728 def getinitUserDefinedActionsDescription(self, key):
729 return _(userDefinedActions.get(eval("config.movielist." + key + ".value"), _("Not Defined")))
731 def _callButton(self, name):
732 if name.startswith('@'):
733 item = self.getCurrentSelection()
734 if isSimpleFile(item):
736 for p in plugins.getPlugins(PluginDescriptor.WHERE_MOVIELIST):
738 p(self.session, item[0])
739 elif name.startswith('/'):
740 self.gotFilename(name)
743 a = getattr(self, 'do_' + name)
750 self._callButton(config.movielist.btn_red.value)
752 self._callButton(config.movielist.btn_green.value)
753 def btn_yellow(self):
754 self._callButton(config.movielist.btn_yellow.value)
756 self._callButton(config.movielist.btn_blue.value)
758 self._callButton(config.movielist.btn_radio.value)
760 self._callButton(config.movielist.btn_tv.value)
762 self._callButton(config.movielist.btn_text.value)
764 self._callButton(config.movielist.btn_F1.value)
766 self._callButton(config.movielist.btn_F2.value)
768 self._callButton(config.movielist.btn_F3.value)
771 if self["list"].getCurrentIndex() < 1:
772 self["list"].moveToLast()
774 self["list"].moveUp()
777 if self["list"].getCurrentIndex() == len(self["list"]) - 1:
778 self["list"].moveToFirst()
780 self["list"].moveDown()
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()
787 self.list.moveToFirst()
789 self.list.moveToFirstMovie()
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()
796 self.list.moveToLast()
798 self.list.moveToFirstMovie()
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"])
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"])
812 def isItemPlayable(self, index):
813 item = self.list.getItem(index)
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:
824 def goToPlayingService(self):
825 service = self.session.nav.getCurrentlyPlayingServiceOrGroup()
827 path = service.getPath()
829 path = os.path.split(os.path.normpath(path))[0]
830 if not path.endswith('/'):
832 self.gotFilename(path, selItem = service)
837 if self.list.playInBackground:
838 if self.list.moveTo(self.list.playInBackground):
839 if self.isItemPlayable(self.list.getCurrentIndex() + 1):
841 self.callLater(self.preview)
844 self.goToPlayingService()
849 if self.list.playInBackground:
850 if self.list.moveTo(self.list.playInBackground):
851 if self.isItemPlayable(self.list.getCurrentIndex() - 1):
853 self.callLater(self.preview)
856 self.goToPlayingService()
860 NavigationInstance.instance.RecordTimer.on_state_change.remove(self.list.updateRecordings)
862 print "[ML] failed to unsubscribe:", e
865 def createSummary(self):
866 return MovieSelectionSummary
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()))
873 self["Service"].newService(None)
874 self["DescriptionBorder"].hide()
875 self["list"].instance.resize(eSize(self.listWidth, self.listHeight))
877 def pauseService(self):
878 # Called when pressing Power button (go to standby)
880 self.session.nav.stopService()
882 def unPauseService(self):
883 # When returning from standby. It might have been a while, so
887 def can_delete(self, item):
890 return canDelete(item) or isTrashFolder(item[0])
891 def can_move(self, 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):
898 def can_listtype(self, item):
900 def can_preview(self, item):
901 return isSimpleFile(item)
903 def _updateButtonTexts(self):
904 for k in ('red', 'green', 'yellow', 'blue'):
905 btn = userDefinedButtons[k]
906 self['key_' + k].setText(userDefinedActions[btn.value])
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
918 check = getattr(self, 'can_' + action)
920 check = self.can_default
921 gui = self["key_" + name]
927 def showEventInformation(self):
928 from Screens.EventView import EventViewSimple
929 from ServiceReference import ServiceReference
930 evt = self["list"].getCurrentEvent()
932 self.session.open(EventViewSimple, evt, ServiceReference(self.getCurrent()))
934 def saveListsize(self):
935 listsize = self["list"].instance.size()
936 self.listWidth = listsize.width()
937 self.listHeight = listsize.height()
938 self.updateDescription()
940 def onFirstTimeShown(self):
941 self.onShown.remove(self.onFirstTimeShown) # Just once, not after returning etc.
943 self.reloadList(self.selectedmovie, home=True)
944 del self.selectedmovie
946 def getCurrent(self):
947 # Returns selected serviceref (may be None)
948 return self["list"].getCurrent()
950 def getCurrentSelection(self):
951 # Returns None or (serviceref, info, begin, len)
952 return self["list"].l.getCurrentSelection()
954 def playAsDVD(self, path):
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])
963 print "[ML] DVD Player not installed:", e
965 def __serviceStarted(self):
966 if not self.list.playInBackground:
968 ref = self.session.nav.getCurrentService()
972 # disable writing the stop position
973 cue.setCutListEnable(2)
974 # find "resume" position
975 cuts = cue.getCutList()
978 for (pts, what) in cuts:
983 # no resume, jump to start of program (first marker)
986 self.callLater(self.doSeek)
988 def doSeek(self, pts = None):
991 seekable = self.getSeek()
997 service = self.session.nav.getCurrentService()
1000 seek = service.seek()
1001 if seek is None or not seek.isCurrentlySeekable():
1005 def callLater(self, function):
1006 self.previewTimer = eTimer()
1007 self.previewTimer.callback.append(function)
1008 self.previewTimer.start(10, True)
1011 playInBackground = self.list.playInBackground
1012 if not playInBackground:
1013 print "Not playing anything in background"
1015 self.session.nav.stopService()
1016 self.list.playInBackground = None
1017 if config.movielist.play_audio_internal.value:
1018 index = self.list.findService(playInBackground)
1021 next = self.list.getItem(index + 1)
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)
1033 current = self.getCurrent()
1034 if current is not None:
1035 path = current.getPath()
1036 if current.flags & eServiceReference.mustDescent:
1037 self.gotFilename(path)
1039 Screens.InfoBar.InfoBar.instance.checkTimeshiftRunning(self.previewCheckTimeshiftCallback)
1041 def startPreview(self):
1042 if self.nextInBackground is not None:
1043 current = self.nextInBackground
1044 self.nextInBackground = None
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)
1055 self.list.playInBackground = current
1056 self.session.nav.playService(current)
1058 def previewCheckTimeshiftCallback(self, answer):
1062 def seekRelative(self, direction, amount):
1063 if self.list.playInBackground:
1064 seekable = self.getSeek()
1065 if seekable is None:
1067 seekable.seekRelative(direction, amount)
1069 def playbackStop(self):
1070 if self.list.playInBackground:
1071 self.list.playInBackground = None
1072 self.session.nav.stopService()
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))
1083 self.gotFilename(path)
1085 ext = os.path.splitext(path)[1].lower()
1086 if config.movielist.play_audio_internal.value and (ext in AUDIO_EXTENSIONS):
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)
1095 if ext in IMAGE_EXTENSIONS:
1097 from Plugins.Extensions.PicturePlayer import ui
1098 # Build the list for the PicturePlayer UI
1101 for item in self.list.list:
1102 p = item[0].getPath()
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)
1111 Screens.InfoBar.InfoBar.instance.checkTimeshiftRunning(boundFunction(self.itemSelectedCheckTimeshiftCallback, ext, path))
1113 def itemSelectedCheckTimeshiftCallback(self, ext, path, answer):
1115 if ext in DVD_EXTENSIONS:
1116 if self.playAsDVD(path):
1118 self.movieSelected()
1120 # Note: DVDBurn overrides this method, hence the itemSelected indirection.
1121 def movieSelected(self):
1122 current = self.getCurrent()
1123 if current is not None:
1127 def doContext(self):
1128 current = self.getCurrent()
1129 if current is not None:
1130 self.session.openWithCallback(self.doneContext, MovieContextMenu, self, current)
1132 def doneContext(self, action):
1133 if action is not None:
1136 def saveLocalSettings(self):
1137 if config.movielist.settings_per_directory.value:
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()
1152 def loadLocalSettings(self):
1153 'Load settings, called when entering a directory'
1154 if config.movielist.settings_per_directory.value:
1156 path = os.path.join(config.movielist.last_videodir.value, ".e2settings.pkl")
1157 updates = pickle.load(open(path, "rb"))
1158 self.applyConfigSettings(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
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)
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
1177 self.applyConfigSettings(updates)
1179 def applyConfigSettings(self, updates):
1180 needUpdate = ("description" in updates) and (updates["description"] != self.settings["description"])
1181 self.settings.update(updates)
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"])
1188 if self.settings["moviesort"] != self["list"].sort_type:
1189 self["list"].setSortType(self.settings["moviesort"])
1191 if self.settings["movieoff"] != self.movieOff:
1192 self.movieOff = self.settings["movieoff"]
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"]
1200 def sortBy(self, newType):
1201 self.settings["moviesort"] = newType
1202 self.saveLocalSettings()
1203 self.setSortType(newType)
1206 def listType(self, newType):
1207 self.settings["listtype"] = newType
1208 self.saveLocalSettings()
1209 self.setListType(newType)
1212 def showDescription(self, newType):
1213 self.settings["description"] = newType
1214 self.saveLocalSettings()
1215 self.setDescriptionState(newType)
1216 self.updateDescription()
1221 if self.list.playInBackground:
1222 self.list.playInBackground = None
1223 self.session.nav.stopService()
1224 self.callLater(self.abort)
1229 def saveconfig(self):
1230 config.movielist.last_selected_tags.value = self.selected_tags
1232 def configure(self):
1233 self.session.openWithCallback(self.configureDone, MovieBrowserConfiguration)
1235 def configureDone(self, 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()
1246 def can_sortby(self, item):
1249 def do_sortby(self):
1252 def selectSortby(self):
1256 for x in l_moviesort:
1257 if int(x[0]) == int(config.movielist.moviesort.value):
1259 menu.append((_(x[1]), x[0], "%d" % index))
1261 self.session.openWithCallback(self.sortbyMenuCallback, ChoiceBox, title=_("Sort list:"), list=menu, selection = used)
1263 def sortbyMenuCallback(self, choice):
1266 self.sortBy(int(choice[1]))
1267 self["movie_sort"].setPixmapNum(int(choice[1])-1)
1269 def getTagDescription(self, tag):
1270 # TODO: access the tag database
1273 def updateTags(self):
1274 # get a list of tags available in this list
1275 self.tags = self["list"].tags
1277 def setListType(self, type):
1278 self["list"].setListType(type)
1280 def setDescriptionState(self, val):
1281 self["list"].setDescriptionState(val)
1283 def setSortType(self, type):
1284 self["list"].setSortType(type)
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')
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)
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)
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()
1324 if self.isItemPlayable(self.list.getCurrentIndex() + 1):
1325 if self.playGoTo > 0:
1326 self.list.moveDown()
1329 self.playGoTo = None
1330 self.callLater(self.preview)
1331 self.callLater(self.enablePathSelect)
1333 def enablePathSelect(self):
1334 self.pathselectEnabled = True
1336 def doPathSelect(self):
1337 if self.pathselectEnabled:
1338 self.session.openWithCallback(
1341 _("Please select the movie path..."),
1342 config.movielist.last_videodir.value
1345 def gotFilename(self, res, selItem=None, doParentalControl=True):
1348 # serviceref must end with /
1349 if not res.endswith('/'):
1351 currentDir = config.movielist.last_videodir.value
1352 if res != currentDir:
1353 if os.path.isdir(res):
1354 baseName = os.path.basename(res[:-1])
1355 if doParentalControl and config.ParentalControl.servicepin[0].value and baseName.startswith(".") and not baseName.startswith(".Trash"):
1356 self.session.openWithCallback(boundFunction(self.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"))
1358 config.movielist.last_videodir.value = res
1359 config.movielist.last_videodir.save()
1360 self.loadLocalSettings()
1361 self.setCurrentRef(res)
1362 self["freeDiskSpace"].path = res
1364 self.reloadList(home = True, sel = selItem)
1366 self.reloadList(home = True, sel = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + currentDir))
1370 _("Directory %s does not exist.") % (res),
1371 type = MessageBox.TYPE_ERROR,
1375 def pinEntered(self, res, selItem, result):
1377 from Components.ParentalControl import parentalControl
1378 parentalControl.setSessionPinCached()
1379 self.gotFilename(res, selItem, False)
1382 self.selected_tags_ele = None
1383 self.selected_tags = None
1384 self.reloadList(home = True)
1386 def showTagsN(self, tagele):
1388 self.showTagWarning()
1389 elif not tagele or (self.selected_tags and tagele.value in self.selected_tags) or not tagele.value in self.tags:
1390 self.showTagsMenu(tagele)
1392 self.selected_tags_ele = tagele
1393 self.selected_tags = self.tags[tagele.value]
1394 self.reloadList(home = True)
1396 def showTagsFirst(self):
1397 self.showTagsN(config.movielist.first_tags)
1399 def showTagsSecond(self):
1400 self.showTagsN(config.movielist.second_tags)
1402 def can_tags(self, item):
1405 self.showTagsN(None)
1407 def tagChosen(self, tag):
1409 if tag[1] is None: # all
1412 # TODO: Some error checking maybe, don't wanna crash on KeyError
1413 self.selected_tags = self.tags[tag[0]]
1414 if self.selected_tags_ele:
1415 self.selected_tags_ele.value = tag[0]
1416 self.selected_tags_ele.save()
1417 self.reloadList(home = True)
1419 def showTagsMenu(self, tagele):
1420 self.selected_tags_ele = tagele
1421 lst = [(_("show all tags"), None)] + [(tag, self.getTagDescription(tag)) for tag in sorted(self.tags)]
1422 self.session.openWithCallback(self.tagChosen, ChoiceBox, title=_("Please select tag to filter..."), list = lst)
1424 def showTagWarning(self):
1425 self.session.open(MessageBox, _("No tags are set on these movies."), MessageBox.TYPE_ERROR)
1427 def selectMovieLocation(self, title, callback):
1428 bookmarks = [("("+_("Other")+"...)", None)]
1429 buildMovieLocationList(bookmarks)
1430 self.onMovieSelected = callback
1431 self.movieSelectTitle = title
1432 self.session.openWithCallback(self.gotMovieLocation, ChoiceBox, title=title, list = bookmarks)
1434 def gotMovieLocation(self, choice):
1437 self.onMovieSelected(None)
1438 del self.onMovieSelected
1440 if isinstance(choice, tuple):
1441 if choice[1] is None:
1442 # Display full browser, which returns string
1443 self.session.openWithCallback(
1444 self.gotMovieLocation,
1446 self.movieSelectTitle,
1447 config.movielist.last_videodir.value
1451 choice = os.path.normpath(choice)
1452 self.rememberMovieLocation(choice)
1453 self.onMovieSelected(choice)
1454 del self.onMovieSelected
1456 def rememberMovieLocation(self, where):
1457 if where in last_selected_dest:
1458 last_selected_dest.remove(where)
1459 last_selected_dest.insert(0, where)
1460 if len(last_selected_dest) > 5:
1461 del last_selected_dest[-1]
1463 def can_bookmarks(self, item):
1465 def do_bookmarks(self):
1466 self.selectMovieLocation(title=_("Please select the movie path..."), callback=self.gotFilename)
1468 def can_addbookmark(self, item):
1470 def exist_bookmark(self):
1471 path = config.movielist.last_videodir.value
1472 if path in config.movielist.videodirs.value:
1475 def do_addbookmark(self):
1476 path = config.movielist.last_videodir.value
1477 if path in config.movielist.videodirs.value:
1479 path = '...' + path[-40:]
1480 self.session.openWithCallback(self.removeBookmark, MessageBox, _("Do you really want to remove your bookmark of %s?") % path)
1482 config.movielist.videodirs.value += [path]
1483 config.movielist.videodirs.save()
1484 def removeBookmark(self, yes):
1487 path = config.movielist.last_videodir.value
1488 bookmarks = config.movielist.videodirs.value
1489 bookmarks.remove(path)
1490 config.movielist.videodirs.value = bookmarks
1491 config.movielist.videodirs.save()
1493 def can_createdir(self, item):
1495 def do_createdir(self):
1496 from Screens.VirtualKeyBoard import VirtualKeyBoard
1497 self.session.openWithCallback(self.createDirCallback, VirtualKeyBoard,
1498 title = _("Please enter name of the new directory"),
1500 def createDirCallback(self, name):
1505 path = os.path.join(config.movielist.last_videodir.value, name)
1507 if not path.endswith('/'):
1509 self.reloadList(sel = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + path))
1511 print "Error %s:" % e.errno, e
1513 msg = _("The path %s already exists.") % name
1515 msg = _("Error") + '\n' + str(e)
1516 except Exception, e:
1517 print "[ML] Unexpected error:", e
1518 msg = _("Error") + '\n' + str(e)
1520 self.session.open(MessageBox, msg, type = MessageBox.TYPE_ERROR, timeout = 5)
1522 def can_rename(self, item):
1523 return canMove(item)
1524 def do_rename(self):
1525 item = self.getCurrentSelection()
1526 if not canMove(item):
1529 p = os.path.split(item[0].getPath())
1531 # if path ends in '/', p is blank.
1532 p = os.path.split(p[0])
1536 name = info.getName(item[0])
1537 from Screens.VirtualKeyBoard import VirtualKeyBoard
1538 self.session.openWithCallback(self.renameCallback, VirtualKeyBoard,
1539 title = _("Rename"),
1542 def do_decode(self):
1543 from ServiceReference import ServiceReference
1544 item = self.getCurrentSelection()
1546 serviceref = ServiceReference(None, reftype = eServiceReference.idDVB, path = item[0].getPath())
1547 name = info.getName(item[0]) + ' - decoded'
1548 description = info.getInfoString(item[0], iServiceInformation.sDescription)
1549 recording = RecordTimer.RecordTimerEntry(serviceref, int(time.time()), int(time.time()) + 3600, name, description, 0, dirname = preferredTimerPath())
1550 recording.dontSave = True
1551 recording.autoincrease = True
1552 recording.setAutoincreaseEnd()
1553 self.session.nav.RecordTimer.record(recording, ignoreTSC = True)
1555 def renameCallback(self, name):
1559 item = self.getCurrentSelection()
1560 if item and item[0]:
1562 path = item[0].getPath().rstrip('/')
1563 meta = path + '.meta'
1564 if os.path.isfile(meta):
1565 metafile = open(meta, "r+")
1566 sid = metafile.readline()
1567 oldtitle = metafile.readline()
1568 rest = metafile.read()
1570 metafile.write("%s%s\n%s" %(sid, name, rest))
1573 index = self.list.getCurrentIndex()
1574 info = self.list.list[index]
1575 if hasattr(info[3], 'txt'):
1578 self.list.invalidateCurrentItem()
1580 pathname,filename = os.path.split(path)
1581 newpath = os.path.join(pathname, name)
1583 print "[ML] rename", path, "to", newpath
1584 os.rename(path, newpath)
1585 self.reloadList(sel = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + newpath))
1587 print "Error %s:" % e.errno, e
1589 msg = _("The path %s already exists.") % name
1591 msg = _("Error") + '\n' + str(e)
1592 except Exception, e:
1594 print "[ML] Unexpected error:", e
1595 traceback.print_exc()
1596 msg = _("Error") + '\n' + str(e)
1598 self.session.open(MessageBox, msg, type = MessageBox.TYPE_ERROR, timeout = 5)
1601 current = self.getCurrent()
1603 resetMoviePlayState(current.getPath() + ".cuts", current)
1604 self["list"].invalidateCurrentItem() # trigger repaint
1607 item = self.getCurrentSelection()
1614 name = info and info.getName(current) or _("this recording")
1615 path = os.path.normpath(current.getPath())
1616 # show a more limited list of destinations, no point
1617 # in showing mountpoints.
1618 title = _("Select destination for:") + " " + name
1619 bookmarks = [("("+_("Other")+"...)", None)]
1623 base = os.path.split(path)[0]
1624 for fn in os.listdir(base):
1625 if not fn.startswith('.'): # Skip hidden things
1626 d = os.path.join(base, fn)
1627 if os.path.isdir(d) and (d not in inlist):
1628 bookmarks.append((fn,d))
1630 except Exception, e :
1631 print "[MovieSelection]", e
1633 for d in last_selected_dest:
1635 bookmarks.append((d,d))
1637 for d in config.movielist.videodirs.value:
1638 d = os.path.normpath(d)
1639 bookmarks.append((d,d))
1641 for p in Components.Harddisk.harddiskmanager.getMountedPartitions():
1642 d = os.path.normpath(p.mountpoint)
1644 bookmarks.append((p.description, d))
1646 self.onMovieSelected = self.gotMoveMovieDest
1647 self.movieSelectTitle = title
1648 self.session.openWithCallback(self.gotMovieLocation, ChoiceBox, title=title, list=bookmarks)
1650 def gotMoveMovieDest(self, choice):
1653 dest = os.path.normpath(choice)
1655 item = self.getCurrentSelection()
1660 name = item[1].getName(current)
1661 moveServiceFiles(current, dest, name)
1662 self["list"].removeService(current)
1663 except Exception, e:
1664 self.session.open(MessageBox, str(e), MessageBox.TYPE_ERROR)
1666 def can_copy(self, item):
1667 return canCopy(item)
1669 item = self.getCurrentSelection()
1676 name = info and info.getName(current) or _("this recording")
1677 self.selectMovieLocation(title=_("Select copy destination for:") + " " + name, callback=self.gotCopyMovieDest)
1679 def gotCopyMovieDest(self, choice):
1682 dest = os.path.normpath(choice)
1684 item = self.getCurrentSelection()
1689 name = item[1].getName(current)
1690 copyServiceFiles(current, dest, name)
1691 except Exception, e:
1692 self.session.open(MessageBox, str(e), MessageBox.TYPE_ERROR)
1694 def stopTimer(self, timer):
1695 if timer.isRunning():
1698 timer.processRepeated(findRunningEvent = False)
1699 self.session.nav.RecordTimer.doActivate(timer)
1701 timer.afterEvent = RecordTimer.AFTEREVENT.NONE
1702 NavigationInstance.instance.RecordTimer.removeEntry(timer)
1704 def onTimerChoice(self, choice):
1705 if isinstance(choice, tuple) and choice[1]:
1706 choice, timer = choice[1]
1711 self.stopTimer(timer)
1715 def do_delete(self):
1718 def delete(self, *args):
1719 if args and (not args[0]):
1720 # cancelled by user (passing any arg means it's a dialog return)
1722 item = self.getCurrentSelection()
1723 if not canDelete(item):
1724 if item and isTrashFolder(item[0]):
1725 # Red button to empty trashcan...
1730 cur_path = os.path.realpath(current.getPath())
1731 st = os.stat(cur_path)
1732 name = info and info.getName(current) or _("this recording")
1733 are_you_sure = _("Do you really want to delete %s?") % (name)
1734 if current.flags & eServiceReference.mustDescent:
1738 # already confirmed...
1739 # but not implemented yet...
1741 if config.usage.movielist_trashcan.value:
1743 # Move the files to the trash can in a way that their CTIME is
1744 # set to "now". A simple move would not correctly update the
1745 # ctime, and hence trigger a very early purge.
1746 trash = Tools.Trashcan.createTrashFolder(cur_path)
1747 trash = os.path.join(trash, os.path.split(cur_path)[1])
1749 for root, dirnames, filenames in os.walk(cur_path):
1750 trashroot = os.path.join(trash, root[len(cur_path)+1:])
1751 for fn in filenames:
1752 print "Move %s -> %s" % (os.path.join(root, fn), os.path.join(trashroot, fn))
1753 os.rename(os.path.join(root, fn), os.path.join(trashroot, fn))
1755 print "MkDir", os.path.join(trashroot, dn)
1756 os.mkdir(os.path.join(trashroot, dn))
1757 # second pass to remove the empty directories
1758 for root, dirnames, filenames in os.walk(cur_path, topdown=False):
1760 print "rmdir", os.path.join(trashroot, dn)
1761 os.rmdir(os.path.join(root, dn))
1763 self["list"].removeService(current)
1764 self.showActionFeedback(_("Deleted") + " " + name)
1765 # Files were moved to .Trash, ok.
1768 print "[MovieSelection] Cannot move to trash", e
1770 # This occurs when moving across devices
1771 msg = _("Cannot move files on a different disk or system to the trash can") + ". "
1773 msg = _("Cannot move to trash can") + ".\n" + str(e) + "\n"
1774 except Exception, e:
1775 print "[MovieSelection] Weird error moving to trash", e
1776 # Failed to create trash or move files.
1777 msg = _("Cannot move to trash can") + "\n" + str(e) + "\n"
1778 msg += _("Sorry, deleting directories can (for now) only be done through the trash can.")
1779 self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR)
1781 for fn in os.listdir(cur_path):
1782 if (fn != '.') and (fn != '..'):
1783 ffn = os.path.join(cur_path, fn)
1784 if os.path.isdir(ffn):
1788 if files or subdirs:
1789 msg = _("Directory contains %s and %s.") % (ngettext("%d file", "%d files", files) % files, ngettext("%d subdirectory", "%d subdirectories", subdirs) % subdirs) + '\n' + are_you_sure
1790 if isInTrashFolder(current):
1791 # Red button to empty trashcan item or subdir
1792 msg = _("Deleted items") + "\n" + msg
1793 callback = self.purgeConfirmed
1795 callback = self.delete
1796 self.session.openWithCallback(callback, MessageBox, msg)
1801 except Exception, e:
1802 print "[MovieSelection] Failed delete", e
1803 self.session.open(MessageBox, _("Delete failed!") + "\n" + str(e), MessageBox.TYPE_ERROR)
1805 self["list"].removeService(current)
1806 self.showActionFeedback(_("Deleted") + " " + name)
1809 rec_filename = os.path.split(current.getPath())[1]
1810 if rec_filename.endswith(".ts"): rec_filename = rec_filename[:-3]
1811 for timer in NavigationInstance.instance.RecordTimer.timer_list:
1812 if timer.isRunning() and not timer.justplay and rec_filename in timer.Filename:
1814 (_("Cancel"), None),
1815 (_("Stop recording"), ("s", timer)),
1816 (_("Stop recording and delete"), ("sd", timer))]
1817 self.session.openWithCallback(self.onTimerChoice, ChoiceBox, title=_("Recording in progress") + ":\n%s" % name, list=choices)
1819 if time.time() - st.st_mtime < 5:
1821 self.session.openWithCallback(self.delete, MessageBox, _("File appears to be busy.\n") + are_you_sure)
1823 if config.usage.movielist_trashcan.value:
1825 trash = Tools.Trashcan.createTrashFolder(cur_path)
1826 # Also check whether we're INSIDE the trash, then it's a purge.
1827 if cur_path.startswith(trash):
1828 msg = _("Deleted items") + "\n"
1830 moveServiceFiles(current, trash, name, allowCopy=False)
1831 self["list"].removeService(current)
1832 # Files were moved to .Trash, ok.
1833 from Screens.InfoBarGenerics import delResumePoint
1834 delResumePoint(current)
1835 self.showActionFeedback(_("Deleted") + " " + name)
1838 print "[MovieSelection] Cannot move to trash", e
1840 # This occurs when moving across devices
1841 msg = _("Cannot move files on a different disk or system to the trash can") + ". "
1843 msg = _("Cannot move to trash can") + ".\n" + str(e) + "\n"
1844 except Exception, e:
1845 print "[MovieSelection] Weird error moving to trash", e
1846 # Failed to create trash or move files.
1847 msg = _("Cannot move to trash can") + "\n" + str(e) + "\n"
1850 self.session.openWithCallback(self.deleteConfirmed, MessageBox, msg + are_you_sure)
1852 def deleteConfirmed(self, confirmed):
1855 item = self.getCurrentSelection()
1860 name = info and info.getName(current) or _("this recording")
1861 serviceHandler = eServiceCenter.getInstance()
1862 offline = serviceHandler.offlineOperations(current)
1865 from enigma import eBackgroundFileEraser
1866 eBackgroundFileEraser.getInstance().erase(os.path.realpath(current.getPath()))
1868 if offline.deleteFromDisk(0):
1869 raise Exception, "Offline delete failed"
1870 self["list"].removeService(current)
1871 from Screens.InfoBarGenerics import delResumePoint
1872 delResumePoint(current)
1873 self.showActionFeedback(_("Deleted") + " " + name)
1874 except Exception, ex:
1875 self.session.open(MessageBox, _("Delete failed!") + "\n" + name + "\n" + str(ex), MessageBox.TYPE_ERROR)
1879 recordings = self.session.nav.getRecordings()
1881 msg = _("Permanently delete all recordings in the trash can?")
1883 next_rec_time = self.session.nav.RecordTimer.getNextRecordingTime()
1884 if recordings or (next_rec_time > 0 and (next_rec_time - time.time()) < 120):
1885 msg += "\n" + _("Recording(s) are in progress or coming up in few seconds!")
1886 self.session.openWithCallback(self.purgeConfirmed, MessageBox, msg)
1888 def purgeConfirmed(self, confirmed):
1891 item = self.getCurrentSelection()
1893 cur_path = os.path.realpath(current.getPath())
1894 Tools.Trashcan.cleanAll(cur_path)
1896 def showNetworkSetup(self):
1898 self.session.open(NetworkSetup.NetworkAdapterSelection)
1900 def showActionFeedback(self, text):
1901 if self.feedbackTimer is None:
1902 self.feedbackTimer = eTimer()
1903 self.feedbackTimer.callback.append(self.hideActionFeedback)
1905 self.feedbackTimer.stop()
1906 self.feedbackTimer.start(3000, 1)
1907 self.diskinfo.setText(text)
1909 def hideActionFeedback(self):
1910 print "[ML] hide feedback"
1911 self.diskinfo.update()
1913 def can_gohome(self, item):
1916 def do_gohome(self):
1917 self.gotFilename(defaultMoviePath())
1921 for index, item in enumerate(l_moviesort):
1922 if int(item[0]) == int(config.movielist.moviesort.value):
1924 if index >= len(l_moviesort) - 1:
1928 #descriptions in native languages too long...
1929 sorttext = l_moviesort[index][2]
1930 if config.movielist.btn_red.value == "sort": self['key_red'].setText(sorttext)
1931 if config.movielist.btn_green.value == "sort": self['key_green'].setText(sorttext)
1932 if config.movielist.btn_yellow.value == "sort": self['key_yellow'].setText(sorttext)
1933 if config.movielist.btn_blue.value == "sort": self['key_blue'].setText(sorttext)
1934 self.sorttimer = eTimer()
1935 self.sorttimer.callback.append(self._updateButtonTexts)
1936 self.sorttimer.start(1500, True) #time for displaying sorting type just applied
1937 self.sortBy(int(l_moviesort[index][0]))
1938 self["movie_sort"].setPixmapNum(int(l_moviesort[index][0])-1)
1940 def do_listtype(self):
1942 for index, item in enumerate(l_listtype):
1943 if int(item[0]) == int(config.movielist.listtype.value):
1945 if index >= len(l_listtype) - 1:
1949 self.listType(int(l_listtype[index][0]))
1951 def do_preview(self):
1954 def displaySortStatus(self):
1955 self["movie_sort"].setPixmapNum(int(config.movielist.moviesort.value)-1)
1956 self["movie_sort"].show()
1958 def can_movieoff(self, item):
1961 def do_movieoff(self):
1962 self.setNextMovieOffStatus()
1963 self.displayMovieOffStatus()
1965 def displayMovieOffStatus(self):
1966 self["movie_off"].setPixmapNum(config.usage.on_movie_eof.getIndex())
1967 self["movie_off"].show()
1969 def setNextMovieOffStatus(self):
1970 config.usage.on_movie_eof.selectNext()
1971 self.settings["movieoff"] = config.usage.on_movie_eof.value
1972 self.saveLocalSettings()
1974 def can_movieoff_menu(self, item):
1977 def do_movieoff_menu(self):
1978 current_movie_eof = config.usage.on_movie_eof.value
1980 for x in config.usage.on_movie_eof.choices:
1981 config.usage.on_movie_eof.value = x
1982 menu.append((config.usage.on_movie_eof.getText(), x))
1983 config.usage.on_movie_eof.value = current_movie_eof
1984 used = config.usage.on_movie_eof.getIndex()
1985 self.session.openWithCallback(self.movieoffMenuCallback, ChoiceBox, title = _("On end of movie"), list = menu, selection = used)
1987 def movieoffMenuCallback(self, choice):
1990 self.settings["movieoff"] = choice[1]
1991 self.saveLocalSettings()
1992 self.displayMovieOffStatus()
1994 def createPlaylist(self):
1998 for index, item in enumerate(self["list"]):
2001 path = item.getPath()
2002 if not item.flags & eServiceReference.mustDescent:
2003 ext = os.path.splitext(path)[1].lower()
2004 if ext in IMAGE_EXTENSIONS: