diff --git a/README.md b/README.md index c93e7c5..7b1b4f5 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Install scripts are JSON configurations that automate app deployment on HexOS | [Immich](immich.json) | Self-hosted photo & video management | | [Jellyfin](jellyfin.json) | Free media server | | [Jellystat](jellystat.json) | Statistics App for Jellyfin | +| [Kodi](kodi.json) | Media player on the TV plugged into the server | | [Lidarr](lidarr.json) | Music collection manager | | [Linkwarden](linkwarden.json) | Webpage archiving utility | | [LubeLogger](lubelogger.json) | Vehicle maintenance tracker | diff --git a/_lib/contract.ts b/_lib/contract.ts index 7b0dbb8..d76ebe7 100644 --- a/_lib/contract.ts +++ b/_lib/contract.ts @@ -24,8 +24,8 @@ export const LOCATIONS = [ "VirtualDisks", ] as const; -/** APP_SPEC_REGEX. */ -export const SPEC_REGEX = /^(\d+)(MBRAM|GBRAM|MB|GB|CORE)$|^GPU$/; +/** APP_SPEC_REGEX. DISPLAY: the app draws on a TV or monitor plugged into the server. */ +export const SPEC_REGEX = /^(\d+)(MBRAM|GBRAM|MB|GB|CORE)$|^GPU$|^DISPLAY$/; /** AppPermission. */ export const PERMISSIONS = ["READ_WRITE_LOCATIONS"] as const; @@ -56,6 +56,12 @@ export const CALL_MACROS = [ "LOCATION", "APP_INSTALLED", "GPU_CONFIG", + // A display app's devices (its card's DRM and HDMI audio nodes, plus input), its card's slot, its + // TrueNAS GPU selection (an NVIDIA card by UUID), and the port it shows on ("HDMI-A:1"). + "DISPLAY_DEVICES", + "DISPLAY_GPU_PCI", + "DISPLAY_GPU_CONFIG", + "DISPLAY_PORT", ] as const; /** Macros substituted as bare tokens, with no argument list. */ diff --git a/kodi.json b/kodi.json new file mode 100644 index 0000000..afc32a1 --- /dev/null +++ b/kodi.json @@ -0,0 +1,118 @@ +{ + "version": 6, + "custom": true, + "metadata": { + "name": "Kodi", + "description": "Play your movies, shows and music on the TV plugged into this server. Kodi draws straight through the server's own graphics card, so no other computer or streaming box is needed. Your Movies, Shows and Music folders are in its library from the first start, and your Photos are ready to browse.", + "icon": "kodi/icon.svg", + "version": "21.2", + "categories": ["media"], + "keywords": ["kodi", "tv", "media player", "htpc", "hdmi"], + "screenshots": ["kodi/screenshot.jpg"] + }, + "script": { + "version": "1.0.0", + "changeLog": "Kodi on the TV plugged into the server, through its Intel, AMD or NVIDIA graphics card." + }, + "requirements": { + "locations": ["ApplicationsPerformance", "Media", "Photos", "Music", "Movies", "Shows", "Videos"], + "specifications": ["2CORE", "1GB", "DISPLAY"], + "permissions": ["READ_WRITE_LOCATIONS"], + "ports": [8080] + }, + "installation_questions": [ + { + "question": "Remote control password", + "description": "For Kodi's web remote and phone remotes such as Kore. The user name is kodi.", + "type": "password", + "key": "remote_password", + "required": true, + "default": "$RANDOM_STRING(16)" + } + ], + "ensure_directories_exists": [ + { "path": "$LOCATION(ApplicationsPerformance)" }, + { "path": "$LOCATION(Media)", "network_share": true }, + { "path": "$LOCATION(Photos)", "network_share": true }, + { "path": "$LOCATION(Music)", "network_share": true }, + { "path": "$LOCATION(Movies)", "network_share": true }, + { "path": "$LOCATION(Shows)", "network_share": true }, + { "path": "$LOCATION(Videos)", "network_share": true }, + { "path": "$LOCATION(ApplicationsPerformance)/kodi/config", "snapshot": { "id": "config" } } + ], + "app_values": { + "image": { "repository": "eshtek/kodi", "tag": "21.2-1", "pull_policy": "missing" }, + "host_network": true, + "restart_policy": "unless-stopped", + "devices": "$DISPLAY_DEVICES()", + "envs": [ + { "name": "HEXOS_DISPLAY_PCI", "value": "$DISPLAY_GPU_PCI()" }, + { "name": "HEXOS_DISPLAY_PORT", "value": "$DISPLAY_PORT()" }, + { "name": "KODI_WEB_PASSWORD", "value": "$QUESTION(remote_password)" }, + { "name": "KODI_WEB_PORT", "value": "8080" } + ], + "portals": [ + { + "name": "Remote", + "scheme": "http", + "use_node_ip": true, + "host": "", + "port": 8080, + "path": "/" + } + ], + "storage": [ + { + "type": "host_path", + "mount_path": "/config", + "host_path_config": { "path": "$LOCATION(ApplicationsPerformance)/kodi/config" } + }, + { + "type": "host_path", + "mount_path": "/run/udev", + "read_only": true, + "host_path_config": { "path": "/run/udev" } + }, + { + "type": "host_path", + "mount_path": "/media/media", + "read_only": true, + "host_path_config": { "path": "$LOCATION(Media)" } + }, + { + "type": "host_path", + "mount_path": "/media/movies", + "read_only": true, + "host_path_config": { "path": "$LOCATION(Movies)" } + }, + { + "type": "host_path", + "mount_path": "/media/shows", + "read_only": true, + "host_path_config": { "path": "$LOCATION(Shows)" } + }, + { + "type": "host_path", + "mount_path": "/media/videos", + "read_only": true, + "host_path_config": { "path": "$LOCATION(Videos)" } + }, + { + "type": "host_path", + "mount_path": "/media/music", + "read_only": true, + "host_path_config": { "path": "$LOCATION(Music)" } + }, + { + "type": "host_path", + "mount_path": "/media/photos", + "read_only": true, + "host_path_config": { "path": "$LOCATION(Photos)" } + } + ], + "resources": { + "limits": { "cpus": 2, "memory": 4096 }, + "gpus": "$DISPLAY_GPU_CONFIG()" + } + } +} diff --git a/kodi/icon.svg b/kodi/icon.svg new file mode 100644 index 0000000..515d587 --- /dev/null +++ b/kodi/icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/kodi/screenshot.jpg b/kodi/screenshot.jpg new file mode 100644 index 0000000..950795f Binary files /dev/null and b/kodi/screenshot.jpg differ