diff --git a/modules/home/default.nix b/modules/home/default.nix index 330692f..f85cd43 100755 --- a/modules/home/default.nix +++ b/modules/home/default.nix @@ -71,6 +71,7 @@ in printManager finamp lingot + keymapp ; }; }; diff --git a/modules/home/gui/apps/tools/keymapp/default.nix b/modules/home/gui/apps/tools/keymapp/default.nix new file mode 100755 index 0000000..4a56879 --- /dev/null +++ b/modules/home/gui/apps/tools/keymapp/default.nix @@ -0,0 +1,11 @@ +{ + pkgs, + ... +}: +{ + home.packages = builtins.attrValues { + inherit (pkgs) + keymapp + ; + }; +} diff --git a/modules/home/gui/desktop/hypr/land/config/bind.nix b/modules/home/gui/desktop/hypr/land/config/bind.nix index 2a30e61..c9d5a45 100755 --- a/modules/home/gui/desktop/hypr/land/config/bind.nix +++ b/modules/home/gui/desktop/hypr/land/config/bind.nix @@ -23,15 +23,14 @@ let "G, exec, scrcpy" "R, exec, thunar" "S, exec, steam" - "E, exec, ghostty -e y" "N, exec, signal-desktop" "T, exec, zeditor" "B, exec, floorp" - "Y, exec, freetube" + "X, exec, freetube" "V, exec, vesktop" "M, exec, element-desktop" "D, exec, ghostty" - "W, exec, bitwarden" + "P, exec, bitwarden" # Workspaces "1, workspace, 1" "2, workspace, 2" @@ -40,13 +39,18 @@ let "5, workspace, 5" # Windows "Tab, killactive" - "F, togglefloating" "Backspace, layoutmsg, togglesplit" # Window Focus + "C, movefocus, l" + "A, movefocus, u" + "E, movefocus, d" + "I, movefocus, r" "Left, movefocus, l" "Up, movefocus, u" "Down, movefocus, d" "Right, movefocus, r" + "F, splitratio, -0.33" + "O, splitratio, 0.33" "bracketleft, splitratio, -0.33" "bracketright, splitratio, 0.33" # Audio @@ -64,7 +68,7 @@ let # Super+shift binds "S, exec, flameshot gui" "period, exec, emote" - "Tab, fullscreen, 0" + "T, fullscreen, 0" # "Print, exec, grim -g \"$(slurp)\"" ]; superCtrlBinds = builtins.map (x: "SUPER CTRL, " + x) [ @@ -73,6 +77,18 @@ let altBinds = builtins.map (x: "CTRL ALT, " + x) [ # Alt binds + # "Tab, togglefloating" + # Window + "1, movetoworkspacesilent, 1" + "2, movetoworkspacesilent, 2" + "3, movetoworkspacesilent, 3" + "4, movetoworkspacesilent, 4" + "5, movetoworkspacesilent, 5" + # Window Move + "C, movewindow, l" + "A, movewindow, u" + "E, movewindow, d" + "I, movewindow, r" ]; shiftBinds = builtins.map (x: "SHIFT, " + x) [ @@ -85,13 +101,13 @@ let ctrlShiftBinds = builtins.map (x: "CTRL SHIFT, " + x) [ # Ctrl+shift binds - # Window + # # Window + "Tab, togglefloating" "1, movetoworkspacesilent, 1" "2, movetoworkspacesilent, 2" "3, movetoworkspacesilent, 3" "4, movetoworkspacesilent, 4" "5, movetoworkspacesilent, 5" - # Window Move "Left, movewindow, l" "Up, movewindow, u" "Down, movewindow, d" diff --git a/modules/nixos/services/glance/config/branding.nix b/modules/nixos/services/glance/config/branding.nix new file mode 100644 index 0000000..f18db52 --- /dev/null +++ b/modules/nixos/services/glance/config/branding.nix @@ -0,0 +1,8 @@ +{ + hide-footer = true; + # logo-url = /assets/logo.png; + # favicon-url = /assets/logo.png; + # app-name = "My Dashboard"; + # app-icon-url = "/assets/app-icon.png"; + # app-background-color = "#151519"; +} diff --git a/modules/nixos/services/glance/config/pages.nix b/modules/nixos/services/glance/config/pages.nix new file mode 100644 index 0000000..0756854 --- /dev/null +++ b/modules/nixos/services/glance/config/pages.nix @@ -0,0 +1,40 @@ +{ config, flake, ... }: +let + widgetsPath = ./widgets; + widgets = { + jellyfin = import (widgetsPath + /jellyfin) { inherit config flake; }; + steam = import (widgetsPath + /steam); + podcasts = import (widgetsPath + /podcasts.nix); + calendar = import (widgetsPath + /calendar.nix); + clock = import (widgetsPath + /clock.nix); + weather = import (widgetsPath + /weather.nix); + }; +in +[ + { + columns = [ + { + size = "full"; + widgets = [ + widgets.podcasts + ]; + } + { + size = "small"; + widgets = [ + widgets.jellyfin + widgets.steam + ]; + } + { + size = "small"; + widgets = [ + widgets.calendar + widgets.weather + widgets.clock + ]; + } + ]; + name = "Dashboard"; + } +] diff --git a/modules/nixos/services/glance/config/server.nix b/modules/nixos/services/glance/config/server.nix new file mode 100644 index 0000000..28c7f53 --- /dev/null +++ b/modules/nixos/services/glance/config/server.nix @@ -0,0 +1,9 @@ +{ service, ... }: +{ + # assets-path = "/home/${user0}/Files/Projects/dotfiles/modules/nixos/services/glance/assets"; + port = service.ports.port0; + # auth = { + # secret-key = config.sops.secrets."${service.name}-key".path; + # users.${user0}.password = config.sops.secrets."${service.name}-${user0}-pass".path; + # }; +} diff --git a/modules/nixos/services/glance/config/theme.nix b/modules/nixos/services/glance/config/theme.nix new file mode 100644 index 0000000..37b276e --- /dev/null +++ b/modules/nixos/services/glance/config/theme.nix @@ -0,0 +1,7 @@ +{ + background-color = "232 23 18"; + contrast-multiplier = 1.2; + primary-color = "220 83 75"; + positive-color = "105 48 72"; + negative-color = "351 74 73"; +} diff --git a/modules/nixos/services/glance/config/widgets/calendar.nix b/modules/nixos/services/glance/config/widgets/calendar.nix new file mode 100644 index 0000000..35ee310 --- /dev/null +++ b/modules/nixos/services/glance/config/widgets/calendar.nix @@ -0,0 +1,5 @@ +{ + type = "calendar"; + title = "Calendar"; + style = "vertical-cards"; +} diff --git a/modules/nixos/services/glance/config/widgets/clock.nix b/modules/nixos/services/glance/config/widgets/clock.nix new file mode 100644 index 0000000..57cd116 --- /dev/null +++ b/modules/nixos/services/glance/config/widgets/clock.nix @@ -0,0 +1,22 @@ +{ + type = "clock"; + hour-format = "12h"; + timezones = [ + { + timezone = "America/Winnipeg"; + label = "Winnipeg, MB"; + } + { + timezone = "Europe/Berlin"; + label = "Berlin, DE"; + } + { + timezone = "Asia/Kolkata"; + label = "Kolkata, IN"; + } + { + timezone = "Asia/Tokyo"; + label = "Tokyo, JP"; + } + ]; +} diff --git a/modules/nixos/services/glance/config/widgets/jellyfin/config/default.nix b/modules/nixos/services/glance/config/widgets/jellyfin/config/default.nix new file mode 100644 index 0000000..d65425e --- /dev/null +++ b/modules/nixos/services/glance/config/widgets/jellyfin/config/default.nix @@ -0,0 +1,326 @@ +'' + {{ $mediaServer := .Options.StringOr "media-server" "" }} + {{ $baseURL := .Options.StringOr "base-url" "" }} + {{ $apiKey := .Options.StringOr "api-key" "" }} + {{ $userName := .Options.StringOr "user-name" "" }} + + {{ define "errorMsg" }} +
+
ERROR
+ + + +
+

{{ . }}

+ {{ end }} + + {{ if or + (eq $mediaServer "") + (eq $baseURL "") + (eq $apiKey "") + (and (eq $mediaServer "jellyfin") (eq $userName "")) + }} + {{ template "errorMsg" "Some required options are not set" }} + {{ else }} + + {{ $historyLength := .Options.StringOr "history-length" "10" }} + {{ $mediaTypes := .Options.StringOr "media-types" "" }} + {{ if eq $mediaServer "tautulli" }} + {{ $mediaTypes = .Options.StringOr "media-types" "movie,episode,track" }} + {{ else if or (eq $mediaServer "jellyfin") (eq $mediaServer "emby") }} + {{ $mediaTypes = .Options.StringOr "media-types" "Movie,Episode,Audio" }} + {{ end }} + {{ $isSmallColumn := .Options.BoolOr "small-column" false }} + {{ $isCompact := .Options.BoolOr "compact" true }} + {{ $showThumbnail := .Options.BoolOr "show-thumbnail" false }} + {{ $thumbAspectRatio := .Options.StringOr "thumbnail-aspect-ratio" "" }} + {{ $showUser := .Options.BoolOr "show-user" true }} + {{ $timeAbsolute := .Options.BoolOr "time-absolute" false }} + {{ $timeFormat := .Options.StringOr "time-format" "Jan 02 15:04" }} + + {{ $userID := "" }} + {{ $historyRequestURL := "" }} + {{ $usersRequestURL := "" }} + {{ $historyCall := "" }} + {{ $usersCall := "" }} + {{ $history := "" }} + {{ $users := "" }} + + {{ if eq $mediaServer "plex" }} + {{ $historyRequestURL = concat $baseURL "/status/sessions/history/all" }} + {{ $historyCall = newRequest $historyRequestURL + | withParameter "limit" $historyLength + | withParameter "sort" "viewedAt:desc" + | withHeader "Accept" "application/json" + | withHeader "X-Plex-Token" $apiKey + | getResponse }} + + {{ if $historyCall.JSON.Exists "MediaContainer" }} + {{ $history = $historyCall.JSON.Array "MediaContainer.Metadata" }} + {{ else }} + {{ template "errorMsg" (concat "Could not fetch " $mediaServer " API.") }} + {{ end }} + + {{ $usersRequestURL = concat $baseURL "/accounts" }} + {{ $usersCall = newRequest $usersRequestURL + | withHeader "Accept" "application/json" + | withHeader "X-Plex-Token" $apiKey + | getResponse }} + {{ $users = $usersCall.JSON.Array "MediaContainer.Account" }} + + {{ else if eq $mediaServer "tautulli" }} + {{ $historyRequestURL = concat $baseURL "/api/v2" }} + {{ $historyCall = newRequest $historyRequestURL + | withParameter "apikey" $apiKey + | withParameter "cmd" "get_history" + | withParameter "length" $historyLength + | withParameter "media_type" $mediaTypes + | withHeader "Accept" "application/json" + | getResponse }} + + {{ if eq $historyCall.Response.StatusCode 200 }} + {{ $history = $historyCall.JSON.Array "response.data.data" }} + {{ else }} + {{ template "errorMsg" (concat "Could not fetch " $mediaServer " API.") }} + {{ end }} + + {{ else if or (eq $mediaServer "jellyfin") (eq $mediaServer "emby") }} + {{ $usersRequestURL = concat $baseURL "/Users" }} + {{ $usersCall = newRequest $usersRequestURL + | withParameter "api_key" $apiKey + | withHeader "Accept" "application/json" + | getResponse }} + + {{ $usersList := $usersCall.JSON.Array "" }} + {{ range $i, $user := $usersList }} + {{ if eq ($user.String "Name") $userName }} + {{ $userID = $user.String "Id" }} + {{ break }} + {{ end }} + {{ end }} + {{ if eq $userID "" }} + {{ template "errorMsg" (concat "User '" $userName "' not found.") }} + {{ end }} + + {{ $historyRequestURL = concat $baseURL "/Users/" $userID "/Items" }} + {{ $historyCall = newRequest $historyRequestURL + | withParameter "api_key" $apiKey + | withParameter "Limit" $historyLength + | withParameter "IncludeItemTypes" $mediaTypes + | withParameter "Recursive" "true" + | withParameter "isPlayed" "true" + | withParameter "sortBy" "DatePlayed" + | withParameter "sortOrder" "Descending" + | withParameter "Fields" "UserDataLastPlayedDate" + | withHeader "Accept" "application/json" + | getResponse }} + + {{ $history = $historyCall.JSON.Array "Items" }} + {{ end }} + + {{ if and (eq $historyCall.Response.StatusCode 200) (eq (len $history) 0) }} +

Nothing has been played. Start streaming something!

+ {{ else }} + + {{ end }} + {{ end }} +'' diff --git a/modules/nixos/services/glance/config/widgets/jellyfin/default.nix b/modules/nixos/services/glance/config/widgets/jellyfin/default.nix new file mode 100644 index 0000000..546402c --- /dev/null +++ b/modules/nixos/services/glance/config/widgets/jellyfin/default.nix @@ -0,0 +1,30 @@ +{ config, flake, ... }: +let + inherit (flake.config.people) user0; + inherit (flake.config.people.users.${user0}) name; + inherit (flake.config.services.instances) glance jellyfin web; + service = glance; + jellyfinUserName = name; + jellyfinHost = "https://${jellyfin.subdomain}.${web.domains.url0}"; +in +{ + type = "custom-api"; + title = "Jellyfin History"; + frameless = true; + cache = "5m"; + options = { + media-server = "jellyfin"; + base-url = jellyfinHost; + api-key = config.sops.secrets."${service.name}-${jellyfin.name}".path; + user-name = jellyfinUserName; + history-length = "10"; + small-column = true; + compact = true; + show-thumbnail = false; + thumbnail-aspect-ratio = "default"; + show-user = true; + time-absolute = false; + time-format = "Jan 02 15:04"; + }; + template = import ./config; +} diff --git a/modules/nixos/services/glance/config/widgets/podcasts.nix b/modules/nixos/services/glance/config/widgets/podcasts.nix new file mode 100644 index 0000000..3ebedce --- /dev/null +++ b/modules/nixos/services/glance/config/widgets/podcasts.nix @@ -0,0 +1,20 @@ +{ + type = "rss"; + title = "Podcasts"; + style = "detailed-list"; + collapse-after = 6; + feeds = [ + { + url = "https://sigmanutrition.libsyn.com/rss/"; + title = "Sigma Nutrition Radio"; + } + { + url = "https://wakingup.libsyn.com/rss"; + title = "Making Sense with Sam Harris"; + } + { + url = "https://feeds.simplecast.com/uNKL_XD_"; + title = "Docs Who Lift"; + } + ]; +} diff --git a/modules/nixos/services/glance/config/widgets/steam/config/default.nix b/modules/nixos/services/glance/config/widgets/steam/config/default.nix new file mode 100644 index 0000000..ca815e3 --- /dev/null +++ b/modules/nixos/services/glance/config/widgets/steam/config/default.nix @@ -0,0 +1,14 @@ +'' + +'' diff --git a/modules/nixos/services/glance/config/widgets/steam/default.nix b/modules/nixos/services/glance/config/widgets/steam/default.nix new file mode 100644 index 0000000..05c30ef --- /dev/null +++ b/modules/nixos/services/glance/config/widgets/steam/default.nix @@ -0,0 +1,7 @@ +{ + type = "custom-api"; + title = "Steam Specials"; + cache = "12h"; + url = "https://store.steampowered.com/api/featuredcategories?cc=ca"; + template = import ./config; +} diff --git a/modules/nixos/services/glance/config/widgets/weather.nix b/modules/nixos/services/glance/config/widgets/weather.nix new file mode 100644 index 0000000..66bd4d2 --- /dev/null +++ b/modules/nixos/services/glance/config/widgets/weather.nix @@ -0,0 +1,7 @@ +{ + type = "weather"; + title = "Weather"; + units = "metric"; + hour-format = "12h"; + location = "Winnipeg, Manitoba, Canada"; +} diff --git a/modules/nixos/services/glance/default.nix b/modules/nixos/services/glance/default.nix index 0f07158..ca0b63e 100755 --- a/modules/nixos/services/glance/default.nix +++ b/modules/nixos/services/glance/default.nix @@ -1,149 +1,52 @@ { config, flake, ... }: let inherit (flake.config.people) user0; - inherit (flake.config.services.instances) glance web; + inherit (flake.config.services.instances) glance jellyfin web; inherit (flake.config.machines.devices) ceres; service = glance; + configPath = ./config; + configImports = { + server = import (configPath + /server.nix) { inherit service; }; + branding = import (configPath + /branding.nix); + theme = import (configPath + /theme.nix); + pages = import (configPath + /pages.nix) { inherit config flake; }; + }; in { services = { glance = { enable = true; - settings = { - server = { - # assets-path = "/home/${user0}/Files/Projects/dotfiles/modules/nixos/services/glance/assets"; - port = service.ports.port0; - }; - # auth = { - # secret-key = config.sops.secrets."${service.name}-key".path; - # users.${user0}.password = config.sops.secrets."${service.name}-${user0}-pass".path; - # }; - branding = { - hide-footer = true; - # logo-url = /assets/logo.png; - # favicon-url = /assets/logo.png; - # app-name = "My Dashboard"; - # app-icon-url = "/assets/app-icon.png"; - # app-background-color = "#151519"; - }; - theme = { - background-color = "232 23 18"; - contrast-multiplier = 1.2; - primary-color = "220 83 75"; - positive-color = "105 48 72"; - negative-color = "351 74 73"; - }; - pages = [ - { - columns = [ - { - size = "full"; - widgets = [ - { - type = "rss"; - title = "Podcasts"; - style = "detailed-list"; - collapse-after = 6; - feeds = [ - { - url = "https://sigmanutrition.libsyn.com/rss/"; - title = "Sigma Nutrition Radio"; - } - { - url = "https://wakingup.libsyn.com/rss"; - title = "Making Sense with Sam Harris"; - } - { - url = "https://feeds.simplecast.com/uNKL_XD_"; - title = "Docs Who Lift"; - } - ]; - } - ]; - } - { - size = "small"; - widgets = [ - { - type = "calendar"; - title = "Calendar"; - style = "vertical-cards"; - } - { - type = "weather"; - title = "Weather"; - units = "metric"; - hour-format = "12h"; - location = "Winnipeg, Manitoba, Canada"; - } - { - type = "clock"; - hour-format = "12h"; - timezones = [ - { - timezone = "America/Winnipeg"; - label = "Winnipeg, MB"; - } - { - timezone = "Europe/Berlin"; - label = "Berlin, DE"; - } - { - timezone = "Asia/Kolkata"; - label = "Kolkata, IN"; - } - { - timezone = "Asia/Tokyo"; - label = "Tokyo, JP"; - } - ]; - } - ]; - } - ]; - name = "Dashboard"; - } - ]; - }; + settings = configImports; }; - # sops = - # let - # sopsPath = secret: { - # path = "${service.sops.path0}/${service.name}-${secret}"; - # owner = service.name; - # mode = "600"; - # }; - # in - # { - # secrets = builtins.listToAttrs ( - # map - # (secret: { - # name = "${service.name}-${secret}"; - # value = sopsPath secret; - # }) - # [ - # "key" - # "${user0}-pass" - # ] - # ); - # }; - # fileSystems."/var/lib/${service.name}" = { - # device = service.paths.path0; - # fsType = "none"; - # options = [ - # "bind" - # ]; - # depends = [ - # ceres.storage0.mount - # ]; - # }; - - # systemd.tmpfiles.rules = [ - # "Z ${service.paths.path0} 755 ${service.name} ${service.name} -" - # "Z ${service.sops.path0} 755 ${service.name} ${service.name} -" - # ]; - }; + sops = + let + sopsPath = secret: { + path = "${service.sops.path0}/${service.name}-${secret}"; + owner = "root"; + mode = "600"; + }; + in + { + secrets = builtins.listToAttrs ( + map + (secret: { + name = "${service.name}-${secret}"; + value = sopsPath secret; + }) + [ + # "key" + # "${user0}-pass" + jellyfin.name + ] + ); + }; + + systemd.tmpfiles.rules = [ + # "Z ${service.paths.path0} 755 ${service.name} ${service.name} -" + # "Z ${service.sops.path0} 755 root root -" + ]; + networking = { firewall = { allowedTCPPorts = [ diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml index f6bfdf6..ebc1267 100755 --- a/secrets/secrets.yaml +++ b/secrets/secrets.yaml @@ -34,11 +34,8 @@ wireguard-CA363: ENC[AES256_GCM,data:iGiAjP5Dbw0kXR3iM50YTS8jBXODNr//W/0OPMAiu1G wireguard-CA220: ENC[AES256_GCM,data:rNy/IMKqAOsgMUu5r8BZsjTCu0L5fDDDV3/g+pkhW1y44Y2rqhhsZgcXG5M=,iv:onyHBn4npqiwC/v37SOMJLLhdfcrtvPmKbMVTgxaSQg=,tag:OmXDL3oYCDPwH1yBsKAYKQ==,type:str] wireguard-CA358: ENC[AES256_GCM,data:/VewmiNfRc9/wSE7TT+z1F9LLIvr/5wPsQZ/zBwAh3dEi9yswOGyde2b/XQ=,iv:7U5dmqFiwhCoL1moGSfHprv85o5TdMr6T2sNk5gH82I=,tag:T1hqh8CiO2iBa+ksaiKCtA==,type:str] wireguard-CA627: ENC[AES256_GCM,data:chmDsH2nE0nagjFRZWuxX08/Ykt+rIgCHYkMHd+7nIqihK5SebF7MJlrp84=,iv:NVOlGE7W70nQ0UM/i5WixJvDULO3Y4cLf8h+OAGHhQQ=,tag:L123ShCnr9+kIg1itIoqBA==,type:str] +glance-jellyfin: ENC[AES256_GCM,data:3ZR8OOgysWNqkJGqKjdDg/0UUgMBbrrgdHbuRQcM+po=,iv:EDG4DEBWmwu//qf+K+V0HqTYc8q78fF9dDb+37zHQIk=,tag:fSok//JeM635gWZ37+cItw==,type:str] sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] age: - recipient: age19dpncsdphdt2tmknjs99eghk527pvdrw0m29qjn2z2gg3et5tdtqycqhl0 enc: | @@ -49,8 +46,7 @@ sops: bXBOa1VSakoyaWxpODJEOU11QUZCaUEK8Ch9Ten3DdrPHF1DTH2qei85AlHUOaLD aNfzakake7ej+MxJYdKEU0bcWofNMKzIlZa2uM10KZSENDP8d8qlig== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-04-01T03:33:13Z" - mac: ENC[AES256_GCM,data:zGnWN7U7d2+REQ1Iy2JEY92hWtS3Lxl6uqG4/kVGwE5fxj65gv1cv/38ulNUhCGY9BEiOpDzQBgoAy9WmvsKathHb7z9NEXrHpVtvNgRJVfVjuduZgGvrAFRLFXV1iLfQXk8wl64/e5YXD1Cbs80+ky9kmA4nl/rM0rlEkK+WOo=,iv:YL+Jv6yfe7/EASfDNkdFhOw29iXRS3rdPAplEE3i1hE=,tag:7NLlenTFk0hIyf+FEa3oJg==,type:str] - pgp: [] + lastmodified: "2025-05-25T21:49:29Z" + mac: ENC[AES256_GCM,data:7JD1leZhMyHg7twZXf/0chy2PbGVS6IE4wayXwzHemFccS0j64JY7/T1IWnL5bCj1V2aszocUWvwZrLhPnYinF6rjDRzLnRCtZ5Nb88S0WyTpDOBAj6GDEgs+e08e+RjCwfWdIr068HD4b/B+9M8S8qJtEZ++Uib7awzIsQphgQ=,iv:TY2wisEJiv2TODvaLl4qjILuSclzKyGVRMjrbrLCvms=,tag:+C9DW8443OOOo7a1Cbl9fQ==,type:str] unencrypted_suffix: _unencrypted - version: 3.9.4 + version: 3.10.2