# 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();
```