From d6949fe35c1b4c7a9e93d1430e5c1b1d5dd4d9c6 Mon Sep 17 00:00:00 2001 From: Chris Wells Date: Wed, 24 Jan 2024 14:56:56 -0600 Subject: Getting a whole kobo distribution together! --- .gitignore | 1 + README | 1 + config/default.nix | 41 ++++++++ flake.lock | 60 +++++++++++ flake.nix | 43 ++++++++ lib/default.nix | 14 +++ modules/default.nix | 9 ++ modules/installer.nix | 23 ++++ modules/kfmon.nix | 21 ++++ modules/kobo.nix | 61 +++++++++++ modules/koreader.nix | 21 ++++ modules/plato.nix | 257 +++++++++++++++++++++++++++++++++++++++++++++ modules/redux/builder.nix | 11 ++ modules/redux/file.nix | 44 ++++++++ modules/redux/kfmon.nix | 30 ++++++ modules/redux/koreader.nix | 42 ++++++++ modules/redux/nickle.nix | 41 ++++++++ modules/redux/plato.nix | 179 +++++++++++++++++++++++++++++++ overlays/default.nix | 7 ++ pkgs/install.nix | 110 +++++++++++++++++++ pkgs/plato.nix | 46 ++++++++ pkgs/source.nix | 21 ++++ 22 files changed, 1083 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100644 config/default.nix create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 lib/default.nix create mode 100644 modules/default.nix create mode 100644 modules/installer.nix create mode 100644 modules/kfmon.nix create mode 100644 modules/kobo.nix create mode 100644 modules/koreader.nix create mode 100644 modules/plato.nix create mode 100644 modules/redux/builder.nix create mode 100644 modules/redux/file.nix create mode 100644 modules/redux/kfmon.nix create mode 100644 modules/redux/koreader.nix create mode 100644 modules/redux/nickle.nix create mode 100644 modules/redux/plato.nix create mode 100644 overlays/default.nix create mode 100644 pkgs/install.nix create mode 100644 pkgs/plato.nix create mode 100644 pkgs/source.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b2be92b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +result diff --git a/README b/README new file mode 100644 index 0000000..b9a8b29 --- /dev/null +++ b/README @@ -0,0 +1 @@ +https://storage.gra.cloud.ovh.net/v1/AUTH_2ac4bfee353948ec8ea7fd1710574097/kfmon-pub/kfmon.html diff --git a/config/default.nix b/config/default.nix new file mode 100644 index 0000000..4c9688a --- /dev/null +++ b/config/default.nix @@ -0,0 +1,41 @@ +{ config, pkgs, lib, ... }: +with builtins // lib; +{ + kfmon.enable = true; + koreader.enable = true; + + plato = { + enable = true; + + fonts.packages = [ + pkgs.hack-font + ]; + + dictionaries.packages = with pkgs.dictdDBs; [ + wordnet + wiktionary + ]; + + settings = { + keyboard-layout = "English"; + frontlight = true; + wifi = false; + sleep-cover = true; + auto-share = true; + auto-suspend = 15.0; + auto-power-off = 4.0; + time-format = "%I:%M%P"; + intermissions = { + suspend = "cover:"; + power-off = "cover:"; + share = "logo:"; + }; + reader = { + font-size = 10.0; + }; + dictionary = { + font-size = 10.0; + }; + }; + }; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..0a95ed0 --- /dev/null +++ b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1705608299, + "narHash": "sha256-Y//xAKx/uzpKXx8qSQfiur7rB386QJ7TVD3f0ByqJyM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "68d14ba2a5c54ed931222079380f96de82489895", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..0a165b9 --- /dev/null +++ b/flake.nix @@ -0,0 +1,43 @@ +{ + description = "Modules and packages to flash KOBO eReader firmware"; + + inputs = { + nixpkgs.url = github:nixos/nixpkgs; + flake-utils.url = github:numtide/flake-utils; + }; + + outputs = inputs@{ self, nixpkgs, flake-utils, ... }: { + lib = import ./lib { inherit (nixpkgs) lib; }; + + overlays.default = import ./overlays; + + # koboConfigurations.default = self.lib.koboConfiguration { + # pkgs = import nixpkgs { + # system = "x86_64-linux"; + # overlays = builtins.attrValues self.overlays; + # }; + # modules = [ ./config ]; + # }; + + } // flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = builtins.attrValues self.overlays; + }; + myKobo = self.lib.koboConfiguration { + inherit pkgs; + modules = [ ./config ]; + }; + in { + packages = { + default = myKobo.config.kobo.package; + installer = myKobo.config.installer.package; + }; + + apps = { + type = "app"; + program = "${pkgs.lib.getExe myKobo.config.installer.package} ${myKobo.config.kobo.package}"; + }; + }); +} diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..fd028fb --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,14 @@ +{ lib, ... }: +rec { + koboConfiguration = { pkgs, modules ? [], specialArgs ? {}, ... }: lib.evalModules { + specialArgs = { inherit pkgs; } // specialArgs; + modules = [ + ../modules + ] ++ modules; + }; + + build = args: (koboConfiguration args).config.kobo.package; + + installer = args: (koboConfiguration args).config.installer.package; + +} diff --git a/modules/default.nix b/modules/default.nix new file mode 100644 index 0000000..8e04cce --- /dev/null +++ b/modules/default.nix @@ -0,0 +1,9 @@ +{ + imports = [ + ./installer.nix + ./kfmon.nix + ./kobo.nix + ./koreader.nix + ./plato.nix + ]; +} diff --git a/modules/installer.nix b/modules/installer.nix new file mode 100644 index 0000000..f5c93ba --- /dev/null +++ b/modules/installer.nix @@ -0,0 +1,23 @@ +{ config, pkgs, lib, ... }: +with builtins // lib; +let + cfg = config.installer; +in { + imports = [ + ./kobo.nix + ]; + + options.installer = { + package = mkPackageOption pkgs [ "kobo" "install" ] // { + apply = x: x.override { + koboLabel = cfg.koboLabel; + }; + }; + + koboLabel = mkOption { + type = types.str; + default = "KOBOeReader"; + description = "The label of the Kobo eReader mount target"; + }; + }; +} diff --git a/modules/kfmon.nix b/modules/kfmon.nix new file mode 100644 index 0000000..f47433e --- /dev/null +++ b/modules/kfmon.nix @@ -0,0 +1,21 @@ +{ config, pkgs, lib, ... }: +with builtins // lib; +let + cfg = config.kfmon; +in { + options.kfmon = { + enable = mkEnableOption "koreader"; + + generate = mkOption { + type = types.lines; + default = ""; + description = "Script used to generate the KFMon config"; + }; + + path = mkOption { + type = types.str; + default = ".adds/koreader"; + description = "Releative path to KFMon within src"; + }; + }; +} diff --git a/modules/kobo.nix b/modules/kobo.nix new file mode 100644 index 0000000..d46da7e --- /dev/null +++ b/modules/kobo.nix @@ -0,0 +1,61 @@ +{ config, pkgs, lib, ... }: +with builtins // lib; +let + cfg = config.kobo; +in { + imports = [ + ./kfmon.nix + ./koreader.nix + ./plato.nix + ]; + + options.kobo = { + package = mkOption { + type = types.package; + description = "The resulting kobo configuration"; + default = if cfg.generate == "" then cfg.src else pkgs.stdenvNoCC.mkDerivation { + inherit (cfg) src; + name = "kobo-config"; + + dontBuild = true; + dontFixup = true; + dontConfigure = true; + dontPatch = true; + + installPhase = '' + mkdir -p $out + cp -rv . $out + + ${cfg.generate} + ''; + }; + }; + + src = mkOption { + type = types.package; + description = "The base source files to use"; + default = with pkgs.kobo.src; + if config.plato.enable && config.koreader.enable then KOReader-Plato + else if config.plato.enable then Plato + else if config.koreader.enable then KOReader + else if config.kfmon.enable then KFMon + else error "At least one of plato, koreader or kfmon must be enabled in order to use a default package"; + }; + + generate = mkOption { + type = types.lines; + default = ""; + description = "Script used to generate the kobo config"; + }; + }; + + config = { + kobo = { + generate = mkBefore (concatStringsSep "\n" ( + optional config.kfmon.enable config.kfmon.generate ++ + optional config.plato.enable config.plato.generate ++ + optional config.koreader.enable config.koreader.generate + )); + }; + }; +} diff --git a/modules/koreader.nix b/modules/koreader.nix new file mode 100644 index 0000000..b4b6551 --- /dev/null +++ b/modules/koreader.nix @@ -0,0 +1,21 @@ +{ config, pkgs, lib, ... }: +with builtins // lib; +let + cfg = config.koreader; +in { + options.koreader = { + enable = mkEnableOption "koreader"; + + generate = mkOption { + type = types.lines; + default = ""; + description = "Script used to generate the KOReader config"; + }; + + path = mkOption { + type = types.str; + default = ".adds/koreader"; + description = "Releative path to KOReader within src"; + }; + }; +} diff --git a/modules/plato.nix b/modules/plato.nix new file mode 100644 index 0000000..afa7486 --- /dev/null +++ b/modules/plato.nix @@ -0,0 +1,257 @@ +{ config, pkgs, lib, ... }: +with builtins // lib; +let + cfg = config.plato; + toml = pkgs.formats.toml {}; + + bashList = concatMapStringsSep " " (x: ''"${toString x}"''); + +in { + options.plato = { + enable = mkEnableOption "plato"; + + # package = mkOption { + # type = types.package; + # readOnly = true; + # default = pkgs.runCommand "generate-plato" cfg.generate; + # }; + + # src = mkPackageOption pkgs [ "kobo" "plato" ] {}; + + generate = mkOption { + type = types.lines; + default = ""; + description = "Script used to generate the Plato config"; + }; + + path = mkOption { + type = types.str; + default = ".adds/plato"; + description = "Relative path to Plato within src"; + }; + + hyphenation = mkEnableOption "hyphenation" // { default = true; }; + + config = mkOption { + type = types.lines; + default = ""; + description = "Plato config.sh"; + }; + + settings = mkOption { + type = toml.type; + default = {}; + description = "Plato Settings.toml"; + }; + + dictionaries = { + packages = mkOption { + type = with types; listOf package; + default = []; + # default = with pkgs.dictdDBs; [ + # wordnet + # wiktionary + # ]; + description = "Dictionaries to install with plato"; + }; + + sporadic = mkOption { + type = with types; listOf str; + default = []; + description = "Extra paths to dictionary files"; + }; + + remove = mkOption { + type = with types; listOf str; + default = []; + description = "Remote these dictionaries from plato"; + }; + + generate = mkOption { + type = types.lines; + default = ""; + description = "Script used to setup dictionaries"; + }; + }; + + fonts = { + packages = mkOption { + type = with types; listOf package; + default = []; + description = "Fonts to install for plato"; + }; + + sporadic = mkOption { + type = with types; listOf str; + default = []; + description = "Extra paths to font files"; + }; + + remove = mkOption { + type = with types; listOf str; + default = []; + description = "Remove these fonts from plato"; + }; + + generate = mkOption { + type = types.lines; + default = ""; + description = "Script used to setup fonts"; + }; + }; + + css = { + epub = mkOption { + type = types.lines; + default = ""; + description = "epub css"; + }; + html = mkOption { + type = types.lines; + default = ""; + description = "html css"; + }; + dictionary = mkOption { + type = types.lines; + default = ""; + description = "dictionary css"; + }; + + generate = mkOption { + type = types.lines; + default = ""; + description = "Script used to setup css files"; + }; + }; + + wifi = { + up = { + pre = mkOption { + type = types.lines; + default = ""; + }; + post = mkOption { + type = types.lines; + default = ""; + }; + }; + down = { + pre = mkOption { + type = types.lines; + default = ""; + }; + post = mkOption { + type = types.lines; + default = ""; + }; + }; + + generate = mkOption { + type = types.lines; + default = ""; + description = "Script used to setup wifi scripts"; + }; + }; + + }; + + config.plato = { + config = optionalString (!cfg.hyphenation) '' + [ -d hyphenation-patterns ] && rm -rf hyphenation-patterns + ''; + + dictionaries.generate = mkBefore '' + mkdir -pv $out/${cfg.path}/dictionaries + + ${optionalString (cfg.dictionaries.packages != []) '' + echo "Adding dictionaries from packages..." + for d in ${bashList cfg.dictionaries.packages}; do + find $d/share/dictd/ -type f -regex '.*\.\(dict\(\.dz\)?\|index\)' -exec cp -v {} $out/${cfg.path}/dictionaries/ \; + done + ''} + + ${optionalString (cfg.dictionaries.sporadic != []) '' + echo "Adding sporadic dictionaries..." + for p in ${bashList cfg.dictionaries.sporadic}; do + cp -v "$p" $out/${cfg.path}/dictionaries/ + done + ''} + + ${optionalString (cfg.dictionaries.remove != []) '' + echo "Removing unwanted dictionaries..." + for p in ${bashList cfg.dictionaries.remove}; do + rm -f "$out/${cfg.path}/dictionaries/$p" + done + ''} + ''; + + fonts.generate = mkBefore '' + mkdir -pv $out/${cfg.path}/fonts + + ${optionalString (cfg.fonts.packages != []) '' + echo "Adding fonts from packages..." + for d in ${concatMapStringsSep " " toString cfg.fonts.packages}; do + find $d/share/fonts/truetype/ -type f -regex '.*\.\(ttf\|otf\)' -exec cp -v {} $out/${cfg.path}/fonts/ \; + done + ''} + + ${optionalString (cfg.fonts.sporadic != []) '' + echo "Adding sporadic fonts" + for p in ${concatMapStringsSep " " (x: ''"${x}"'') cfg.fonts.sporadic}; do + cp -v "$p" $out/${cfg.path}/fonts/ + done + ''} + + ${optionalString (cfg.fonts.remove != []) '' + echo "Removing unwanted fonts..." + for p in ${bashList cfg.fonts.remove}; do + rm -f "$out/${cfg.path}/fonts/$p" + done + ''} + ''; + + css.generate = mkBefore '' + ${optionalString (cfg.css.epub != "") '' + cp -v ${writeText "epub-user.css" cfg.css.epub} $out/${cfg.path}/css/epub-user.css + ''} + + ${optionalString (cfg.css.html != "") '' + cp -v ${pkgs.writeText "html-user.css" cfg.css.html} $out/${cfg.path}/css/html-user.css + ''} + + ${optionalString (cfg.css.dictionary != "") '' + cp -v ${pkgs.writeText "dictionary-user.css" cfg.css.dictionary} $out/${cfg.path}/css/dictionary-user.css + ''} + ''; + + wifi.generate = + let + mkWifi = t: p: optionalString (cfg.wifi.${t}.${p} != "") '' + cp -v ${pkgs.writeScript "wifi-${p}-${t}.sh" "#!/bin/sh\n${cfg.wifi.${t}.${p}}"} $out/${cfg.path}/scripts/wifi-${p}-${t}.sh + ''; + in mkBefore ( + concatMapStringsSep "\n" ({ t, p }: mkWifi t p) (cartesianProductOfSets { t = [ "up" "down" ]; p = [ "pre" "post" ]; }) + ); + + + generate = mkBefore '' + mkdir -p $out/${cfg.path} + + ${cfg.dictionaries.generate} + + ${cfg.fonts.generate} + + ${cfg.css.generate} + + ${cfg.wifi.generate} + + ${optionalString (cfg.settings != {}) '' + cp -v ${toml.generate "Settings.toml" cfg.settings} $out/${cfg.path}/Settings.toml + ''} + + ${optionalString (cfg.config != "") '' + cp -v ${pkgs.writeScript "config.sh" cfg.config} $out/${cfg.path}/config.sh + ''} + ''; + }; +} diff --git a/modules/redux/builder.nix b/modules/redux/builder.nix new file mode 100644 index 0000000..5f74a1e --- /dev/null +++ b/modules/redux/builder.nix @@ -0,0 +1,11 @@ +{ pkgs, lib, ... }: +with builtins // lib; +{ + copyFile = { source, directory, executable, target, ... }: '' + cp -${if directory then "r" else ""}v "${source}" "${target}" + + ${optionalString (isBool executable) '' + chmod ${if executable then "+" else "-"}x "${target}" + ''} + ''; +} diff --git a/modules/redux/file.nix b/modules/redux/file.nix new file mode 100644 index 0000000..e05318d --- /dev/null +++ b/modules/redux/file.nix @@ -0,0 +1,44 @@ +{ config, pkgs, lib, ... }: +with builtins // lib; +let + cfg = config.file; +in { + options.file = mkOption { + default = {}; + type = with types; lazyAttrsOf (submodule ({ name, config, ... }: { + options = { + enable = mkEnableOption name // { default = true; }; + target = mkOption { + type = types.str; + default = name; + }; + executable = mkOption { + type = with types; nullOr bool; + default = null; + }; + directory = mkOption { + type = types.bool; + default = false; + description = "Enable this if the source file is a directory"; + }; + source = mkOption { + type = types.path; + }; + text = mkOption { + type = with types; nullOr lines; + default = null; + }; + }; + + config = mkMerge [ + (mkIf (config.text != null) { + directory = false; + source = pkgs.writeText name config.text; + }) + (mkIf (config.text == "") { + enable = false; + }) + ]; + })); + }; +} diff --git a/modules/redux/kfmon.nix b/modules/redux/kfmon.nix new file mode 100644 index 0000000..772a10b --- /dev/null +++ b/modules/redux/kfmon.nix @@ -0,0 +1,30 @@ +{ config, pkgs, lib, options, ... }: +with builtins // lib; +let + cfg = config.kfmon; +in { + options.kfmon = { + enable = mkEnableOption "KFMon"; + + path = mkOption { + type = types.str; + default = ".adds/kfmon"; + description = "Relative path to kfmon within src"; + }; + + file = mkOption { + type = options.file.type; + default = {}; + description = "Files relative to ${cfg.path}"; + }; + }; + + config = mkIf cfg.enable { + file = mapAttrs' (n: v: { + name = "kfmon-${n}"; + value = v // { + target = "${cfg.path}/${v.target}"; + }; + }) cfg.file + }; +} diff --git a/modules/redux/koreader.nix b/modules/redux/koreader.nix new file mode 100644 index 0000000..1c885a8 --- /dev/null +++ b/modules/redux/koreader.nix @@ -0,0 +1,42 @@ +{ config, pkgs, lib, options, ... }: +with builtins // lib; +let + cfg = config.koreader; +in { + options.koreader = { + enable = mkEnableOption "KOReader"; + + path = mkOption { + type = types.str; + default = ".adds/koreader"; + description = "Relative path to KOReader within src"; + }; + + nickleEntry = mkEnableOption "Launch KOReader directly via NickleMenu"; + + file = mkOption { + type = options.file.type; + default = {}; + description = "Files relative to ${cfg.path}"; + }; + }; + + config = mkIf cfg.enable { + file = mapAttrs' (n: v: { + name = "koreader-${n}"; + value = v // { + target = "${cfg.path}/${v.target}"; + }; + }) cfg.file + + nickle.menuItems = mkIf cfg.nickleEntry { + "koreader" = [ + "main" + "KOReader" + "cmd_spawn" + "quiet" + "exec /mnt/onboard/${cfg.path}/koreader.sh" + ]; + }; + }; +} diff --git a/modules/redux/nickle.nix b/modules/redux/nickle.nix new file mode 100644 index 0000000..b08a6ec --- /dev/null +++ b/modules/redux/nickle.nix @@ -0,0 +1,41 @@ +{ config, pkgs, lib, options, ... }: +with builtins // lib; +let + cfg = config.nickle; +in { + options.nickle = { + enable = mkEnableOption "NickleMenu"; + + path = mkOption { + type = types.str; + default = ".adds/nm"; + description = "Relative path to NickleMenu within src"; + }; + + file = mkOption { + type = options.file.type; + default = {}; + description = "Files relative to ${cfg.path}"; + }; + + menuItems = mkOption { + type = with types; attrsOf (listOf str); + default = {}; + }; + }; + + config = mkIf cfg.enable { + nickle = { + file = mapAttrs (_: v: { + text = concatStringsSep " : " ([ "menu_item" ] ++ v) + }) cfg.menuItems + }; + + file = mapAttrs' (n: v: { + name = "nm-${n}"; + value = v // { + target = "${cfg.path}/${v.target}"; + }; + }) cfg.file + }; +} diff --git a/modules/redux/plato.nix b/modules/redux/plato.nix new file mode 100644 index 0000000..6735133 --- /dev/null +++ b/modules/redux/plato.nix @@ -0,0 +1,179 @@ +{ config, pkgs, lib, options, ... }: +with builtins // lib; +let + cfg = config.plato; + toml = pkgs.formats.toml {}; +in { + imports = [ + ./nickle.nix + ]; + + options.plato = { + enable = mkEnableOpiton "Plato"; + + path = mkOption { + type = types.srt; + default = ".adds/plato"; + description = "Relative path to Plato within src"; + }; + + file = mkOption { + type = options.file.type; + default = {}; + description = "Files relative to ${cfg.path}"; + }; + + nickleEntry = mkEnableOption "Launch Plato directly from NickleMenu"; + + config = mkOption { + type = types.lines; + default = ""; + description = "Plato config.sh"; + }; + + hyphenation = mkEnableOption "hyphenation" // { default = true; }; + + convertDictionaries = mkEnableOption "convert StarDict" // { default = true; }; + + setFramebufferDepth = mkEnableOption "set framebuffer's depth" // { default = true; }; + + settings = mkOption { + type = toml.type; + default = {}; + description = "Plato Settings.toml"; + }; + + css = mkOption { + default = {}; + type = with types; attrsOf lines; + description = '' + css style sheets. + Only use this for epub-user dictionary-user html-user + ''; + }; + + scripts = mkOption { + default = {}; + type = with types; attrsOf lines; + description = '' + Only use this for wifi-post-up wifi-post-down wifi-pre-up wifi-pre-down. + ''; + # apply = mapAttrs (_: text: '' + # #!/bin/sh + + # ${text} + # '') + }; + + dictionaries = mkOption { + type = with types; listOf package; + default = []; + }; + + allDictionaries = mkOption { + type = types.package; + readOnly = true; + internal = true; + default = pkgs.runCommand "PlatoDictionaries" {} ('' + mkdir -p $out + '' + concatMapStringsSep "\n" (pkg: '' + find ${pkg}/share/dictd/ -type f -regex '.*\.\(dict\(\.dz\)?\|index\)' -exec cp -v {} $out/ \; + '') cfg.dictionaries + ); + + }; + + fonts = mkOption { + type = with types; listOf package; + default = []; + }; + + allFonts = mkOption { + type = types.package; + readOnly = true; + internal = true; + default = pkgs.runCommand "PlatoFonts" {} ('' + mkdir -p $out + '' + concatMapStringsSep "\n" (pkg: '' + find ${pkg}/share/fonts/truetype/ -type f -regex '.*\.\(ttf\|otf\)' -exec cp -v {} $out/ \; + '') cfg.fonts + ); + + }; + }; + + config = mkIf cfg.enable { + plato = { + config = concatStringsSep "\n\n" ( + optional (!cfg.convertDictionaries) '' + unset PLATO_CONVERT_DICTIONARIES + '' + ++ + optional (!cfg.setFramebufferDepth) '' + unset PLATO_SET_FRAMEBUFFER_DEPTH + '' + ++ + optional (!cfg.hyphenation) '' + [ -d hyphenation-patterns ] && rm -rf hyphenation-patterns + '' + ); + file = mkMerge [ + (mkIf (cfg.settings != {}) { + "Settings.toml".source = toml.generate "Settings.toml" cfg.settings; + }) + + (mkIf (cfg.config != "") { + "config.sh".text = cfg.config; + }) + + (mapAttrs' (n: text: + let + n1 = if hasPrefix "css/" n then n else "css/${n}"; + name = if hasSuffix ".css" n1 then n1 else "{n1}.css"; + in { + inherit name; + value = { inherit text; } + }) cfg.css) + + (mapAttrs' (n: t: + let + n1 = if hasPrefix "scripts/" n then n else "scripts/${n}"; + name = if hasSuffix ".sh" n1 then n1 else "${n1}.sh"; + text = if hasPrefix "#!/" t then t else "#!/bin/sh\n\n${t}"; + in { + inherit name; + value = { inherit text; }; + }) cfg.scripts) + + { + dictionaries = { + directory = true; + source = cfg.allDictionaries; + }; + fonts = { + directory = true; + source = cfg.allFonts; + }; + } + ]; + }; + + file = mapAttrs' (n: v: { + name = "plato-${n}"; + value = v // { + target = "${cfg.path}/${v.target}"; + }; + }) cfg.file; + + nickle.menuItems = mkIf cfg.nickleEntry { + "plato" = [ + "main" + "Plato" + "cmd_spawn" + "quiet" + "exec /mnt/onboard/${cfg.path}/plato.sh" + ]; + } + + }; +} diff --git a/overlays/default.nix b/overlays/default.nix new file mode 100644 index 0000000..561e8c2 --- /dev/null +++ b/overlays/default.nix @@ -0,0 +1,7 @@ +final: prev: { + kobo = { + src = prev.callPackage ../pkgs/source.nix {}; + + install = prev.callpackage ../pkgs/install.nix {}; + }; +} diff --git a/pkgs/install.nix b/pkgs/install.nix new file mode 100644 index 0000000..e25108d --- /dev/null +++ b/pkgs/install.nix @@ -0,0 +1,110 @@ +{ writeShellApplication +, coreutils +, gnugrep +, rsync +, util-linux +, koboLabel ? "KOBOeReader" +, ... }: writeShellApplication { + name = "KoboInstaller"; + runtimeInputs = [ + coreutils + gnugrep + rsync + util-linux + ]; + text = '' + INPUT=${1-} + + if [[ -z "$INPUT" ]]; then + echo "Must provide an input directory" + exit 255 + fi + + if [[ ! -d "$INPUT/.kobo" ]] || [[ ! -d "$INPUT/.adds" ]]; then + echo "Missing one of $INPUT/.kobo or $INPUT/.adds. Are you sure your input directory is correct?" + exit 255 + fi + + KOBO_MOUNTPOINT="$(findmnt -nlo TARGET LABEL="${koboLabel}")" + if [[ -z "$KOBO_MOUNTPOINT" ]]; then + echo "Couldn't find a Kobo eReader volume! Is one actually mounted?" + exit 255 + fi + + KOBO_DIR="$KOBO_MOUNTPOINT/.kobo" + if [[ ! -d "$KOBO_DIR" ]]; then + echo "It doesn't seem like $KOBO_MOUNTPOINT actually points to a Kobo eReader since it doesn't contain a .kobo directory..." + exit 255 + fi + + # Tiny DRY helper for the failure message... + recap_failure() { + echo "* As a result, the device may have been left in an inconsistent state!" + echo "* Please note that NO automatic cleanup will be done!" + echo "* As such, before unplugging your device, consider removing:" + echo "* The following folders:" + for kfm_folder in .adds/kfmon .adds/koreader .adds/plato ; do + local current_folder="$KOBO_MOUNTPOINT/$kfm_folder" + if [[ -d "$current_folder" ]] ; then + printf "\t%s\n" "$current_folder" + else + printf "\t%-96s%32s\n" "$current_folder" "[OK: Already gone]" + fi + done + echo "* As well as the following files:" + for kfm_file in .kobo/KoboRoot.tgz kfmon.png koreader.png icons/plato.png ; do + local current_file="$KOBO_MOUNTPOINT/$kfm_file" + if [[ -f "$current_file" ]] ; then + printf "\t%s\n" "$current_file" + else + printf "\t%-96s%32s\n" "$current_file" "[OK: Already gone]" + fi + done + } + + KOBO_CONFIG="$KOBO_DIR/Kobo/Kobo eReader.conf" + echo "* Preventing Nickel from scanning hidden directories . . ." + cat >> "${KOBO_CONFIG}" <<-\EoM + + [FeatureSettings] + ExcludeSyncFolders=(\\.(?!kobo|adobe).+|([^.][^/]*/)+\\..+) + EoM + + # Check for a cat failure, as unlikely as it might be (permissions?)... + ret=$? + if [[ $ret -ne 0 ]] ; then + echo "* Installation FAILED: Failed to update Nickel config!" + echo "* No permanent changes have been made." + exit $ret + fi + + # Double-check that it was updated, in case of gremlins attack... + if ! grep -Fq 'ExcludeSyncFolders=(\\.(?!kobo|adobe).+|([^.][^/]*/)+\\..+)' "${KOBO_CONFIG}" ; then + echo "* Installation FAILED: Nickel config update was ineffective o_O !" + echo "* No permanent changes have been made." + exit 255 + fi + + echo "* Installing Kobo package" + rsync -vr "$INPUT/" "$KOBO_MOUNTPOINT" + + ret=$? + if [[ $ret -ne 0 ]]; then + echo "* Installation FAILED: Failed to copy archive!" + recap_failure + exit $ret + fi + + echo "* Sanity check . . ." + if [[ ! "$KOBO_DIR/KoboRoot.tgz" ]]; then + echo "* Installation FAILED: Copying was ineffective (no KoboRoot tarball) o_O !" + recap_failure + exit 255 + fi + + echo "* Installation successful!" + echo "* Please make sure to eject & unplug your device safely!" + + exit 0 + ''; +} diff --git a/pkgs/plato.nix b/pkgs/plato.nix new file mode 100644 index 0000000..815ac76 --- /dev/null +++ b/pkgs/plato.nix @@ -0,0 +1,46 @@ +{ stdenvNoCC +, formats +, runCommand +, writeScript +, lib + +, platoSrc +, platoSettings ? {} +, platoCfg ? "" +, dictionaries ? [] +, fonts ? [] +, ... }: +with builtins // lib; +stdenvNoCC.mkDerivation (finalAttrs: { + name = "plato"; + + src = platoSrc; + + dontFix = true; + dontPatch = true; + dontConfigure = true; + dontBuild = true; + + installPhase = '' + mkdir -p $out + cp -rv . $out + + # mkdir -p $out/.adds/plato/dictionaries + for d in ${concatMapStringsSep " " toString dictionaries}; do + find $d/share/dictd/ -type f -regex '.*\.\(dict\(\.dz\)?\|index\)' -exec cp -v {} $out/.adds/plato/dictionaries/ \; + done + + for d in ${concatMapStringsSep " " toString fonts}; do + find $d/share/fonts/truetype/ -type f -regex '.*\.\(ttf\|otf\)' -exec cp -v {} $out/.adds/plato/fonts/ \; + done + + ${optionalString (platoSettings != {}) '' + cp -v ${(formats.toml {}).generate "Settings.toml" platoSettings} $out/.adds/plato/Settings.toml + ''} + ${optionalString (platoCfg != "") '' + cp -v ${writeScript "config.sh" platoCfg} $out/.adds/plato/config.sh + ''} + '' + ; + +}) diff --git a/pkgs/source.nix b/pkgs/source.nix new file mode 100644 index 0000000..dcf40fe --- /dev/null +++ b/pkgs/source.nix @@ -0,0 +1,21 @@ +{ fetchzip, ... }: +{ + KOReader = fetchzip { + stripRoot = false; + url = "https://storage.gra.cloud.ovh.net/v1/AUTH_2ac4bfee353948ec8ea7fd1710574097/kfmon-pub/OCP-KOReader-v2024.01.zip"; + }; + Plato = fetchzip { + stripRoot = false; + url = "https://storage.gra.cloud.ovh.net/v1/AUTH_2ac4bfee353948ec8ea7fd1710574097/kfmon-pub/OCP-Plato-0.9.40.zip"; + sha256 = "sha256-jOCC3p9Q/DNtSADgMgmCwPvoZ4XA4ka1zH7dfg8r/kg="; + }; + KOReader-Plato = fetchzip { + stripRoot = false; + url = "https://storage.gra.cloud.ovh.net/v1/AUTH_2ac4bfee353948ec8ea7fd1710574097/kfmon-pub/OCP-Plato-0.9.40_KOReader-v2024.01.zip"; + sha256 = "sha256-A0ImPLQ+5/MLg5Iw3U3WnoKc2Cd/g6lSiSzAiBnrfZM="; + }; + KFMon = fetchzip { + stripRoot = false; + url = "https://storage.gra.cloud.ovh.net/v1/AUTH_2ac4bfee353948ec8ea7fd1710574097/kfmon-pub/OCP-KFMon-1.4.6-43-g2c54087.zip"; + }; +} -- cgit v1.2.3