# Jellyfin NativeShell Specification ## NativeShell API * `NativeShell` * `getPlugins()` (**required**) - Return an array of require.js module names of plugins. (Can be used for alternate players and maybe other functionality?) > This **must** return an Array or the webapp will crash! * `openUrl(url, target)` (**required**) - Provide a method of opening external urls. Alternative to `target="_blank"` in browsers. * `enableFullscreen()` (**required**) - Enable fullscreen mode for video playback. * `disableFullscreen()` (**required**) - Disable fullscreen mode when video playback stops. * `findServers(number)` - Return a `Promise` which resolves with a list of discovered servers. Each server object should have the following structure: ```json { "Id": "SERVER_ID", "Address": "http://127.0.0.1:8096", "EndpointAddress": "http://example.com:8096", // This seems to only be used for servers that are not local? Maybe a remnant from Emby Connect? "Name": "My Jellyfin Server" } ``` * `downloadFile(url)` (**required**) - Download a file to the local filesystem. * `updateMediaSession(mediaInfo)` (**required**) - Update the state of the media player. The provided media info is in the following format: ```json { "action": eventName, "isLocalPlayer": isLocalPlayer, "itemId": itemId, "title": title, "artist": artist, "album": album, "duration": duration, "position": currentTime, "imageUrl": imageUrl, "canSeek": canSeek, "isPaused": isPaused } ``` * `hideMediaSession()` (**required**) - Hide media information/controls when media playback is complete. * `AppHost` (**required**) * `getDeviceProfile(profileBuilder)` - Return a `Promise` which resolves with **TODO** * `getSyncProfile(profileBuilder, appSettings)` - Return a `Promise` which resolves with **TODO** * `exit()` - Exit the Jellyfin app * `supports(command)` - Return a boolean value indicating if a command is supported in the app. (See list of Supported Commands) * `getDefaultLayout()` - Return the default UI layout. Valid values are `"mobile"`, `"desktop"`, or `"tv"`. * `init()` - Return a `Promise` which resolves with an app info object. The app info object should have the following structure: ```json { "deviceId": "DEVICE_ID", "deviceName": "My Phone", "appName": "Jellyfin for Platform", "appVersion": "1.0.0" } ``` * `deviceName()` - Return the device's name. * `deviceId()` - Return the device's unique ID. * `appName()` - Return the name of the app. * `appVersion()` - Return the version of the app. * `FileSystem` (optional) * `fileExists(path)` - Return a `Promise` which resolves to a boolean value indicating if a file exists at the given path. * `directoryExists(path)` - Return a `Promise` which resolves to a boolean value indicating if a directory exists at the given path. ### Supported Commands These are commands that web checks if a client can support via `AppHost.supports(command)` * `castmenuhashchange` * `chromecast` * `displaylanguage` * `displaymode` * `exit` * `externallinks` * `externalplayerintent` * `filedownload` * `fileinput` * `fullscreenchange` * `htmlvideoautoplay` * `imageanalysis` * `multiserver` * `physicalvolumecontrol` * `remoteaudio` * `remotecontrol` * `remotevideo` * `runatstartup` * `screensaver` * `sharing` * `skins` * `soundeffects` * `subtitleappearancesettings` * `subtitleburnsettings` * `sync` * `targetblank` ### Plugin API **TODO** ### Player Supported Features > Is this part of the NativeShell Plugins maybe? * `PictureInPicture` * `SetBrightness` * `SetAspectRatio` ## References ``` src/scripts/site.js 546: if (window.NativeShell) { 547: list = list.concat(window.NativeShell.getPlugins()); src/components/apphost.js 46: if (window.NativeShell) { 47: profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); 146: if (window.NativeShell) { 147: profile = window.NativeShell.AppHost.getSyncProfile(profileBuilder, appSettings); 328: if (window.NativeShell) { 329: window.NativeShell.AppHost.exit(); 341: if (window.NativeShell) { 342: return window.NativeShell.AppHost.supports(command); 351: if (window.NativeShell) { 352: return window.NativeShell.AppHost.getDefaultLayout(); 359: if (window.NativeShell) { 360: return window.NativeShell.AppHost.init(); 369: return window.NativeShell ? window.NativeShell.AppHost.deviceName() : deviceName; 372: return window.NativeShell ? window.NativeShell.AppHost.deviceId() : deviceId; 375: return window.NativeShell ? window.NativeShell.AppHost.appName() : appName; 378: return window.NativeShell ? window.NativeShell.AppHost.appVersion() : appVersion; src/components/shell.js 6: if (window.NativeShell) { 7: window.NativeShell.openUrl(url, target); 20: if (window.NativeShell) { 21: window.NativeShell.enableFullscreen(); 25: if (window.NativeShell) { 26: window.NativeShell.disableFullscreen(); src/libraries/apiclient/connectionmanager.js 306: if (window.NativeShell && typeof window.NativeShell.findServers === 'function') { 307: window.NativeShell.findServers(1e3).then(onFinish, function () { src/components/filedownloader.js 7: if (window.NativeShell) { 9: window.NativeShell.downloadFile(item.url); src/components/filesystem.js 6: if (window.NativeShell && window.NativeShell.FileSystem) { 7: return window.NativeShell.FileSystem.fileExists(path); 12: if (window.NativeShell && window.NativeShell.FileSystem) { 13: return window.NativeShell.FileSystem.directoryExists(path); src/components/playback/mediasession.js 5: if (!navigator.mediaSession && !window.NativeShell) { 184: window.NativeShell.updateMediaSession({ 251: window.NativeShell.hideMediaSession(); ```