diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 5d3fea78..ade52190 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -114,6 +114,9 @@
/modules/programs/keychain.nix @marsam
+/modules/programs/kodi.nix @dwagenk
+/tests/modules/programs/kodi @dwagenk
+
/modules/programs/lazygit.nix @kalhauge
/modules/programs/less.nix @pamplemousse
diff --git a/modules/lib/maintainers.nix b/modules/lib/maintainers.nix
index 0eb34829..0a54d1e0 100644
--- a/modules/lib/maintainers.nix
+++ b/modules/lib/maintainers.nix
@@ -19,6 +19,12 @@
github = "CarlosLoboxyz";
githubId = 86011416;
};
+ dwagenk = {
+ email = "dwagenk@mailbox.org";
+ github = "dwagenk";
+ githubId = 32838899;
+ name = "Daniel Wagenknecht";
+ };
justinlovinger = {
name = "Justin Lovinger";
email = "git@justinlovinger.com";
diff --git a/modules/misc/news.nix b/modules/misc/news.nix
index 94c053b8..b5678546 100644
--- a/modules/misc/news.nix
+++ b/modules/misc/news.nix
@@ -2387,6 +2387,14 @@ in
A new module is available: 'programs.pandoc'.
'';
}
+
+ {
+ time = "2022-01-26T22:08:29+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'programs.kodi'.
+ '';
+ }
];
};
}
diff --git a/modules/modules.nix b/modules/modules.nix
index 42b3e902..844a62ad 100644
--- a/modules/modules.nix
+++ b/modules/modules.nix
@@ -86,6 +86,7 @@ let
./programs/kakoune.nix
./programs/keychain.nix
./programs/kitty.nix
+ ./programs/kodi.nix
./programs/lazygit.nix
./programs/less.nix
./programs/lesspipe.nix
diff --git a/modules/programs/kodi.nix b/modules/programs/kodi.nix
new file mode 100644
index 00000000..36169948
--- /dev/null
+++ b/modules/programs/kodi.nix
@@ -0,0 +1,255 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.programs.kodi;
+
+ stylesheetCommonHeader = ''
+
+
+
+
+ '';
+
+ stylesheetCommonFooter = "";
+
+ stylesheetNestedTags = ''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ '';
+
+ stylesheetTagsAsSettingWithId = ''
+
+
+
+
+
+
+
+
+ '';
+
+ stylesheetAdvancedSettingsRootTag = ''
+
+ Generated by Home Manager.
+
+
+
+
+ '';
+
+ stylesheetSourcesRootTag = ''
+
+ Generated by Home Manager.
+
+
+
+
+ '';
+
+ stylesheetAddonSettingsRootTag = ''
+
+ Generated by Home Manager.
+
+
+
+
+ '';
+
+ attrsetToXml = attrs: name: stylesheet:
+ pkgs.runCommand name {
+ # Package splicing for libxslt does not work correctly leading to errors
+ # when cross-compiling. Use the version from buildPackages explicitely to
+ # fix this.
+ nativeBuildInputs = [ pkgs.buildPackages.libxslt.bin ];
+ xml = builtins.toXML attrs;
+ passAsFile = [ "xml" ];
+ } ''
+ xsltproc ${stylesheet} - < "$xmlPath" > "$out"
+ '';
+
+ attrsetToAdvancedSettingsXml = attrs: name:
+ let
+ stylesheet = builtins.toFile "stylesheet.xsl" ''
+ ${stylesheetCommonHeader}
+ ${stylesheetAdvancedSettingsRootTag}
+ ${stylesheetNestedTags}
+ ${stylesheetCommonFooter}
+ '';
+ in attrsetToXml attrs name stylesheet;
+
+ attrsetToSourcesXml = attrs: name:
+ let
+ stylesheet = builtins.toFile "stylesheet.xsl" ''
+ ${stylesheetCommonHeader}
+ ${stylesheetSourcesRootTag}
+ ${stylesheetNestedTags}
+ ${stylesheetCommonFooter}
+ '';
+ in attrsetToXml attrs name stylesheet;
+
+ attrsetToAddonSettingsXml = attrs: name:
+ let
+ stylesheet = builtins.toFile "stylesheet.xsl" ''
+ ${stylesheetCommonHeader}
+ ${stylesheetAddonSettingsRootTag}
+ ${stylesheetTagsAsSettingWithId}
+ ${stylesheetCommonFooter}
+ '';
+ in attrsetToXml attrs name stylesheet;
+
+in {
+ meta.maintainers = [ hm.maintainers.dwagenk ];
+
+ options.programs.kodi = {
+ enable = mkEnableOption "Kodi";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.kodi;
+ defaultText = literalExpression "pkgs.kodi";
+ example = literalExpression ''
+ { pkgs.kodi.withPackages (exts: [ exts.pvr-iptvsimple ]) }
+ '';
+ description = ''
+ The kodi package to use.
+ Can be used to specify extensions.
+ '';
+ };
+
+ datadir = mkOption {
+ type = types.path;
+ default = "${config.home.homeDirectory}/.kodi";
+ defaultText =
+ literalExpression ''"''${config.home.homeDirectory}/.kodi"'';
+ example = literalExpression ''"''${config.xdg.dataHome}/kodi"'';
+ description = "Directory to store configuration and metadata.";
+ };
+
+ settings = mkOption {
+ type = with types;
+ let
+ valueType = either str (attrsOf valueType) // {
+ description = "attribute sets of strings";
+ };
+ in nullOr valueType;
+ default = null;
+ example = literalExpression ''
+ { videolibrary.showemptytvshows = "true"; }
+ '';
+ description = ''
+ Configuration to write to the advancedsettings.xml
+ file in kodis userdata directory. Settings specified here will be
+ immutable from inside kodi and be hidden from the GUI settings dialog.
+
+ See as
+ reference for how settings need to be specified.
+
+ The innermost attributes must be of type str.
+ '';
+ };
+
+ sources = mkOption {
+ type = with types;
+ let
+ valueType = oneOf [ str (attrsOf valueType) (listOf valueType) ] // {
+ description = "attribute sets or lists of strings";
+ };
+ in nullOr valueType;
+ default = null;
+ example = literalExpression ''
+ {
+ video = {
+ default = "movies";
+ source = [
+ { name = "videos"; path = "/path/to/videos"; allowsharing = "true"; }
+ { name = "movies"; path = "/path/to/movies"; allowsharing = "true"; }
+ ];
+ };
+ }
+ '';
+ description = ''
+ Contents to populate the file sources.xml in kodis
+ userdata directory.
+
+ See as
+ reference for how sources need to be specified.
+
+ Kodi will still show the dialogs to modify sources in the GUI and they
+ appear to be mutable. This however is not the case and the sources will
+ stay as specified via Home Manager.
+
+ The innermost attributes must be of type str.
+ '';
+ };
+
+ addonSettings = mkOption {
+ type = with types; nullOr (attrsOf (attrsOf str));
+ default = null;
+ example = literalExpression ''
+ { "service.xbmc.versioncheck".versioncheck_enable = "false"; }
+ '';
+ description = ''
+ Attribute set with the plugin namespace as toplevel key and the plugins
+ settings as lower level key/value pairs.
+
+ Kodi will still show the settings of plugins configured via this
+ mechanism in the GUI and they appear to be mutable. This however is
+ not the case and the settings will stay as specified via Home Manager.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable (mkMerge [
+ {
+ assertions = [
+ (lib.hm.assertions.assertPlatform "programs.kodi" pkgs
+ lib.platforms.linux)
+ ];
+
+ home.packages = [ cfg.package ];
+ home.sessionVariables = { KODI_DATA = cfg.datadir; };
+ }
+
+ (mkIf (cfg.settings != null) {
+ home.file."${cfg.datadir}/userdata/advancedsettings.xml".source =
+ attrsetToAdvancedSettingsXml cfg.settings "kodi-advancedsettings.xml";
+ })
+
+ (mkIf (cfg.sources != null) {
+ home.file."${cfg.datadir}/userdata/sources.xml".source =
+ attrsetToSourcesXml cfg.sources "kodi-sources.xml";
+ })
+
+ (mkIf (cfg.addonSettings != null) {
+ home.file = mapAttrs' (k: v:
+ attrsets.nameValuePair
+ ("${cfg.datadir}/userdata/addon_data/${k}/settings.xml") {
+ source = attrsetToAddonSettingsXml v "kodi-addon-${k}-settings.xml";
+ }) cfg.addonSettings;
+ })
+ ]);
+}
diff --git a/tests/default.nix b/tests/default.nix
index 6844f673..3a2eb4ff 100644
--- a/tests/default.nix
+++ b/tests/default.nix
@@ -123,6 +123,7 @@ import nmt {
./modules/programs/gnome-terminal
./modules/programs/hexchat
./modules/programs/i3status-rust
+ ./modules/programs/kodi
./modules/programs/mangohud
./modules/programs/ncmpcpp-linux
./modules/programs/neovim # Broken package dependency on Darwin.
diff --git a/tests/modules/programs/kodi/default.nix b/tests/modules/programs/kodi/default.nix
new file mode 100644
index 00000000..1902645c
--- /dev/null
+++ b/tests/modules/programs/kodi/default.nix
@@ -0,0 +1,5 @@
+{
+ kodi-example-addon-settings = ./example-addon-settings.nix;
+ kodi-example-settings = ./example-settings.nix;
+ kodi-example-sources = ./example-sources.nix;
+}
diff --git a/tests/modules/programs/kodi/example-addon-settings-expected.xml b/tests/modules/programs/kodi/example-addon-settings-expected.xml
new file mode 100644
index 00000000..21259b24
--- /dev/null
+++ b/tests/modules/programs/kodi/example-addon-settings-expected.xml
@@ -0,0 +1,4 @@
+
+
+ false
+
diff --git a/tests/modules/programs/kodi/example-addon-settings.nix b/tests/modules/programs/kodi/example-addon-settings.nix
new file mode 100644
index 00000000..c591d1ae
--- /dev/null
+++ b/tests/modules/programs/kodi/example-addon-settings.nix
@@ -0,0 +1,18 @@
+{ config, ... }:
+
+{
+ programs.kodi = {
+ enable = true;
+ package = config.lib.test.mkStubPackage { };
+
+ addonSettings = {
+ "service.xbmc.versioncheck".versioncheck_enable = "false";
+ };
+ };
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.kodi/userdata/addon_data/service.xbmc.versioncheck/settings.xml \
+ ${./example-addon-settings-expected.xml}
+ '';
+}
diff --git a/tests/modules/programs/kodi/example-settings-expected.xml b/tests/modules/programs/kodi/example-settings-expected.xml
new file mode 100644
index 00000000..2aca9763
--- /dev/null
+++ b/tests/modules/programs/kodi/example-settings-expected.xml
@@ -0,0 +1,6 @@
+
+
+
+ true
+
+
diff --git a/tests/modules/programs/kodi/example-settings.nix b/tests/modules/programs/kodi/example-settings.nix
new file mode 100644
index 00000000..bdb48268
--- /dev/null
+++ b/tests/modules/programs/kodi/example-settings.nix
@@ -0,0 +1,16 @@
+{ config, ... }:
+
+{
+ programs.kodi = {
+ enable = true;
+ package = config.lib.test.mkStubPackage { };
+
+ settings = { videolibrary.showemptytvshows = "true"; };
+ };
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.kodi/userdata/advancedsettings.xml \
+ ${./example-settings-expected.xml}
+ '';
+}
diff --git a/tests/modules/programs/kodi/example-sources-expected.xml b/tests/modules/programs/kodi/example-sources-expected.xml
new file mode 100644
index 00000000..736f8963
--- /dev/null
+++ b/tests/modules/programs/kodi/example-sources-expected.xml
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/tests/modules/programs/kodi/example-sources.nix b/tests/modules/programs/kodi/example-sources.nix
new file mode 100644
index 00000000..f49b5000
--- /dev/null
+++ b/tests/modules/programs/kodi/example-sources.nix
@@ -0,0 +1,33 @@
+{ config, ... }:
+
+{
+ programs.kodi = {
+ enable = true;
+ package = config.lib.test.mkStubPackage { };
+
+ sources = {
+ video = {
+ default = "movies";
+ source = [
+ {
+ name = "videos";
+ path = "/path/to/videos";
+ allowsharing = "true";
+ }
+ {
+ name = "movies";
+ path = "/path/to/movies";
+ allowsharing = "true";
+ }
+ ];
+ };
+ };
+
+ };
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.kodi/userdata/sources.xml \
+ ${./example-sources-expected.xml}
+ '';
+}