Repository navigation
Architecture
For developers. The user-facing behaviour is in the other pages; this one explains how the pieces fit together.
PVR key / plugin menu
|
MovieCockpit (MVC) ---- UI: list screen, menus, player, setup
|
+--> CacheCockpit (CAC) ---- FileManager: SQL cache, file operations
| | Recording: reacts to timers, covers, series folders
| +--> JobCockpit (JOC) ---- runs file operations as background jobs
|
+--> MountCockpit (MNC) ---- bookmarks, mount points, share availability, free space
|
+--> ComponentsCockpit (COC) ---- skin sources, converters, renderers
|
+--> CoverCockpit (COV) [optional] ---- cover download, via a plugin hook
TMDBCockpit, MediathekCockpit [optional] ---- movie info, Mediathek, via plugin hooks
PowerCockpit (POC) [optional] ---- shutdown tasks: trashcan purge, archive
The plugin identifies itself to the others with the id MVC (Version.ID). FileManager.getInstance("MVC")
returns the cache for it, MountCockpit.getInstance().getMountedBookmarks("MVC") the bookmarks. The same
infrastructure serves MediaCockpit with the id MDC.
| File | Role |
|---|---|
plugin.py |
Plugin descriptors. At session start it replaces InfoBar.showMovies with the function that opens the list (weight 1, so it runs after other movie list plugins), sets the EPG language and registers the bookmarks with MountCockpit |
MovieCockpit.py |
The list screen: loading, navigation, selection, delete / move / copy / archive, trashcan, series folders, player start |
MovieCockpitActions.py |
Key actions and the four-level colour-button matrix |
CockpitContextMenu.py |
The function menu and the plugins menu |
MovieList.py |
The list source: builds the rows (buildRow), sorting, list modes, selection |
Sorting.py |
Per-folder sort mode: read, write (cache and .sort file), toggle |
ServiceCenter.py |
A stand-in for eServiceCenter that answers service-info requests from the cache (title, times, description, size, cover) |
CutList.py |
Reading and writing the cut list (.cuts and the cache) |
RecordingInfo.py |
The technical info screen |
MovieInfoEPG.py |
The EPG info screen with the TMDB and Mediathek buttons |
SetupScreen.py, setup.xml, ConfigInit.py
|
Settings |
keymap.xml |
Maps remote keys to the action names in the CockpitActions context |
skin/default/*.ymlinc, skin/default/skin.xml
|
The skin (see Skinning) |
The player and many helpers are shared sources (CockpitPlayer, CockpitSeek, CockpitSmartSeek,
CockpitCueSheet, CutListUtils, FileUtils, FileManagerUtils, ServiceUtils, SkinUtils, ...). They live in the
Common repository and are copied into every Cockpit plugin when it is built, which is why they appear in the
plugin's folder on the box.
MovieCockpit.onDialogShow
FileManager.onDatabaseLoaded(loadList) -> called when the scan is finished
FileManager.onDatabaseChanged(loadList) -> called after every cache change
Loading spinner
loadList
MovieList.loadList(return_dir, return_path)
FileManager.getDirList / getFileList (SQL, no disk access)
sortList (per folder: date or alpha, up or down)
buildRow (date text, icon, progress, picon, colours)
Service source <- ServiceCenter (details pane, cover, front panel)
The list never reads the disks. The only disk access in the browsing path is the existence check when you press OK. Everything else is the SQL cache or the files that file jobs touch.
MovieList.buildRow appends nine display values to the 20 database columns (ROW_IDX_* in MovieList.py). The
category priority for colour and progress bar is: marked or locked, then running recording, then normal. Progress is
computed from the cut list's last position, or from now - event start for a running recording, or from the
job's progress for a download.
getSelectionList returns the marked entries, or the entry under the cursor. When exactly one entry is selected the
cursor is moved to the next one, so repeated use works through the list. return_dir and return_path are class
attributes on the screen, so they survive closing and reopening the plugin within one session.
MovieCockpit collects (op, path, target) tuples and calls FileManager.execFileOp. For each, CacheCockpit
- looks up the entry (and, for delete and same-disk move, every entry with the same relative path on other bookmarks),
- checks free space for copies and cross-disk moves (free space x 0.8 must cover the size),
- adds a job to JobCockpit; the job runs shell commands (
mv,cp,rm,mkdir, helper scripts for folders) on all files that share the base name, - calls back; MovieCockpit reloads the list after a 500 ms quiet period.
Entries with a running job are in FileManager.getLockList(); the list shows the operation and the selection colour.
Deleting moves to <bookmark>/.Trash/<relative folder>; .Trash is excluded from the normal queries and from
archiving. FileManager.purgeTrashcan(retention) deletes entries older than the retention; archive() queues moves
from the archive source to the target bookmark. Both finish with a trim job.
CacheCockpit's Recording class listens to RecordTimer.on_state_change. On a timer start it writes the
.xmeta file, adds the file to the cache and, if cover_auto_download is on, requests a cover through the
WHERE_COVER_DOWNLOAD plugin hook; on timer end it records the stop time, refreshes the entry and applies the
series-folder rule. If timer_autoclean is on, RecordTimer.cleanup() runs at start-up and two seconds after each
timer end (the timer only joins the processed list after the state callback has returned).
Hook (PluginDescriptor.where) |
Value | Used for |
|---|---|---|
WHERE_MOVIELIST |
standard | The long-MENU plugins menu |
WHERE_TMDB_MOVIELIST |
-97 | TMDB info (PluginUtils.WHERE_TMDB_MOVIELIST) |
WHERE_MEDIATHEK_SEARCH |
-96 | Mediathek search |
WHERE_COVER_DOWNLOAD |
-94 | Cover download |
getPlugin(where) returns the first plugin registered for a hook, or None; the buttons depend on the answer.
- Autotools project (
Makefile.amfiles); installed under.../Plugins/Extensions/MovieCockpit. -
CONTROL/controldefines the package and its dependencies;postrmremoves the plugin directory. - Translations:
po/MovieCockpit.potis the template,po/de.pothe German translation. Strings are wrapped in_()from the plugin's__init__; setup texts are insetup.xml. - Logging:
Debug.pysets up a logger with the prefixMVC:writing to stdout, so it ends up in the Enigma2 debug log. The level is the Log level setting. - Releases are listed in
.releases; the current one is 0.19.3.
- Every file starts with the licence header (GPLv3), and uses
logger.info("...")/logger.debug(...)at function entry. - Row field and database column indices are named constants (
FILE_IDX_*inFileManagerUtils.py); the skin uses the numbers, so keep both in sync. - Anything that can block (network shares, disk operations) runs in a thread or a job; the list code only talks to SQL.
MovieCockpit
Using it
- User Guide
- Keys and Buttons
- Playback
- Managing Recordings
- Covers and Metadata
- Storage and Network
- Settings
- Troubleshooting
Under the hood