mirror of
https://gitlab.com/upRootNutrition/dotfiles.git
synced 2025-06-15 09:35:12 -05:00
feat: refactored glance
This commit is contained in:
parent
3c230de4fd
commit
8d4ce8d2f9
17 changed files with 571 additions and 149 deletions
|
@ -71,6 +71,7 @@ in
|
|||
printManager
|
||||
finamp
|
||||
lingot
|
||||
keymapp
|
||||
;
|
||||
};
|
||||
};
|
||||
|
|
11
modules/home/gui/apps/tools/keymapp/default.nix
Executable file
11
modules/home/gui/apps/tools/keymapp/default.nix
Executable file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
home.packages = builtins.attrValues {
|
||||
inherit (pkgs)
|
||||
keymapp
|
||||
;
|
||||
};
|
||||
}
|
|
@ -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"
|
||||
|
|
8
modules/nixos/services/glance/config/branding.nix
Normal file
8
modules/nixos/services/glance/config/branding.nix
Normal file
|
@ -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";
|
||||
}
|
40
modules/nixos/services/glance/config/pages.nix
Normal file
40
modules/nixos/services/glance/config/pages.nix
Normal file
|
@ -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";
|
||||
}
|
||||
]
|
9
modules/nixos/services/glance/config/server.nix
Normal file
9
modules/nixos/services/glance/config/server.nix
Normal file
|
@ -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;
|
||||
# };
|
||||
}
|
7
modules/nixos/services/glance/config/theme.nix
Normal file
7
modules/nixos/services/glance/config/theme.nix
Normal file
|
@ -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";
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
type = "calendar";
|
||||
title = "Calendar";
|
||||
style = "vertical-cards";
|
||||
}
|
22
modules/nixos/services/glance/config/widgets/clock.nix
Normal file
22
modules/nixos/services/glance/config/widgets/clock.nix
Normal file
|
@ -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";
|
||||
}
|
||||
];
|
||||
}
|
|
@ -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" }}
|
||||
<div class="widget-error-header">
|
||||
<div class="color-negative size-h3">ERROR</div>
|
||||
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="break-all">{{ . }}</p>
|
||||
{{ 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) }}
|
||||
<p>Nothing has been played. Start streaming something!</p>
|
||||
{{ else }}
|
||||
<div class="carousel-container show-right-cutoff">
|
||||
<div class="cards-horizontal carousel-items-container">
|
||||
{{ range $n, $item := $history }}
|
||||
{{ $mediaType := "" }}
|
||||
{{ $isMovie := false }}
|
||||
{{ $isShows := false }}
|
||||
{{ $isMusic := false }}
|
||||
{{ $title := "" }}
|
||||
{{ $showTitle := "" }}
|
||||
{{ $showSeason := "" }}
|
||||
{{ $showEpisode := "" }}
|
||||
{{ $artist := "" }}
|
||||
{{ $albumTitle := "" }}
|
||||
{{ $thumbURL := "" }}
|
||||
{{ $playedAt := "" }}
|
||||
|
||||
{{ if eq $mediaServer "plex" }}
|
||||
{{ $userID = $item.Int "accountID" }}
|
||||
{{ range $n, $u := $users }}
|
||||
{{ if eq $userID ($u.Int "id") }}
|
||||
{{ $userName = $u.String "name" }}
|
||||
{{ break }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $mediaType = $item.String "type" }}
|
||||
{{ $isMovie = eq $mediaType "movie" }}
|
||||
{{ $isShows = eq $mediaType "episode" }}
|
||||
{{ $isMusic = eq $mediaType "track" }}
|
||||
|
||||
{{ $title = $item.String "title" }}
|
||||
{{ if $isShows }}
|
||||
{{ $showTitle = $item.String "grandparentTitle" }}
|
||||
{{ $showSeason = $item.String "parentIndex" }}
|
||||
{{ $showEpisode = $item.String "index" }}
|
||||
{{ else if $isMusic }}
|
||||
{{ $artist = $item.String "grandparentTitle" }}
|
||||
{{ $albumTitle = $item.String "parentTitle" }}
|
||||
{{ end }}
|
||||
|
||||
{{ $thumbID := $item.String "thumb" }}
|
||||
{{ if or $isShows $isMusic}}
|
||||
{{ $thumbID = $item.String "parentThumb" }}
|
||||
{{ end }}
|
||||
{{ $thumbURL = concat $baseURL $thumbID "?X-Plex-Token=" $apiKey }}
|
||||
|
||||
{{ $time := $item.String "viewedAt" }}
|
||||
{{ if $timeAbsolute }}
|
||||
{{ $playedAt = $time | parseLocalTime "unix" | formatTime $timeFormat }}
|
||||
{{ else }}
|
||||
{{ $playedAt = $time | parseRelativeTime "unix" }}
|
||||
{{ end }}
|
||||
|
||||
{{ else if eq $mediaServer "tautulli" }}
|
||||
{{ $userName = $item.String "user" }}
|
||||
{{ $mediaType = $item.String "media_type" }}
|
||||
{{ $isMovie = eq $mediaType "movie" }}
|
||||
{{ $isShows = eq $mediaType "episode" }}
|
||||
{{ $isMusic = eq $mediaType "track" }}
|
||||
|
||||
{{ $title = $item.String "title" }}
|
||||
{{ if $isShows }}
|
||||
{{ $showTitle = $item.String "grandparent_title" }}
|
||||
{{ $showSeason = $item.String "parent_media_index" }}
|
||||
{{ $showEpisode = $item.String "media_index" }}
|
||||
{{ else if $isMusic }}
|
||||
{{ $artist = $item.String "grandparent_title" }}
|
||||
{{ $albumTitle = $item.String "parent_title" }}
|
||||
{{ end }}
|
||||
|
||||
{{ $thumbID := $item.String "thumb" }}
|
||||
{{ $thumbURL = concat $baseURL "/api/v2?apikey=" $apiKey "&cmd=pms_image_proxy&img=" $thumbID }}
|
||||
|
||||
{{ $time := $item.String "date" }}
|
||||
{{ if $timeAbsolute }}
|
||||
{{ $playedAt = $time | parseLocalTime "unix" | formatTime $timeFormat }}
|
||||
{{ else }}
|
||||
{{ $playedAt = $time | parseRelativeTime "unix" }}
|
||||
{{ end }}
|
||||
|
||||
{{ else if or (eq $mediaServer "jellyfin") (eq $mediaServer "emby") }}
|
||||
{{ $mediaType = $item.String "Type" }}
|
||||
{{ $isMovie = eq $mediaType "Movie" }}
|
||||
{{ $isShows = eq $mediaType "Episode" }}
|
||||
{{ $isMusic = eq $mediaType "Audio" }}
|
||||
|
||||
{{ $title = $item.String "Name" }}
|
||||
{{ if $isShows }}
|
||||
{{ $showTitle = $item.String "SeriesName" }}
|
||||
{{ $showSeason = $item.String "ParentIndexNumber" }}
|
||||
{{ $showEpisode = $item.String "IndexNumber" }}
|
||||
{{ else if $isMusic }}
|
||||
{{ $artist = $item.String "AlbumArtist" }}
|
||||
{{ $albumTitle = $item.String "Album" }}
|
||||
{{ end }}
|
||||
|
||||
{{ $thumbID := $item.String "Id" }}
|
||||
{{ if $isShows }}
|
||||
{{ $thumbID = $item.String "SeasonId" }}
|
||||
{{ end }}
|
||||
{{ $thumbURL = concat $baseURL "/Items/" $thumbID "/Images/Primary?api_key=" $apiKey }}
|
||||
|
||||
{{ $time := $item.String "UserData.LastPlayedDate" }}
|
||||
{{ if $timeAbsolute }}
|
||||
{{ $playedAt = $time | parseLocalTime "rfc3339" | formatTime $timeFormat }}
|
||||
{{ else }}
|
||||
{{ $playedAt = $time | parseRelativeTime "rfc3339" }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $showInfoFormat := concat "Season " $showSeason " Episode " $showEpisode}}
|
||||
{{ if $isCompact }}
|
||||
{{ $showInfoFormat = concat "S" $showSeason "E" $showEpisode}}
|
||||
{{ end }}
|
||||
|
||||
<div class="card widget-content-frame">
|
||||
{{ if $showThumbnail }}
|
||||
<img src="{{ $thumbURL | safeURL }}"
|
||||
alt="{{ $title }} thumbnail"
|
||||
loading="lazy"
|
||||
class="media-server-thumbnail shrink-0"
|
||||
style="
|
||||
object-fit: cover;
|
||||
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
||||
{{ if eq $thumbAspectRatio "square" }}
|
||||
aspect-ratio: 1;
|
||||
{{ else if eq $thumbAspectRatio "portrait" }}
|
||||
aspect-ratio: 3/4;
|
||||
{{ else if eq $thumbAspectRatio "landscape" }}
|
||||
aspect-ratio: 4/3;
|
||||
{{ else }}
|
||||
aspect-ratio: initial;
|
||||
{{ end }}
|
||||
"
|
||||
/>
|
||||
{{ end }}
|
||||
|
||||
<div class="grow padding-inline-widget margin-top-10 margin-bottom-10">
|
||||
<ul class="flex flex-column justify-evenly margin-bottom-3 {{if $isSmallColumn}}size-h6{{end}}" style="height: 100%;">
|
||||
{{ if $isCompact }}
|
||||
<ul class="list-horizontal-text flex-nowrap">
|
||||
{{ if $showUser }}
|
||||
<li class="color-primary text-truncate">{{ $userName }}</li>
|
||||
{{ end }}
|
||||
|
||||
{{ if $timeAbsolute }}
|
||||
<li class="text-truncate">{{ $playedAt }}</li>
|
||||
{{ else }}
|
||||
<li class="shrink-0">
|
||||
<span {{ $playedAt }}></span>
|
||||
{{ if not $showUser }}
|
||||
<span> ago</span>
|
||||
{{ end }}
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
|
||||
{{ if $isShows }}
|
||||
<ul class="list-horizontal-text flex-nowrap">
|
||||
<li class="text-truncate">{{ $showInfoFormat }}</li>
|
||||
<li class="text-truncate">{{ $showTitle }}</li>
|
||||
</ul>
|
||||
{{ else if $isMusic }}
|
||||
<ul class="list-horizontal-text flex-nowrap">
|
||||
<li class="text-truncate">{{ $artist }}</li>
|
||||
<li class="text-truncate">{{ $albumTitle }}</li>
|
||||
</ul>
|
||||
{{ end }}
|
||||
|
||||
<li class="text-truncate">{{ $title }}</li>
|
||||
{{ else }}
|
||||
{{ if $showUser }}
|
||||
<li class="color-primary text-truncate">{{ $userName }}</li>
|
||||
{{ end }}
|
||||
|
||||
{{ if $timeAbsolute }}
|
||||
<li class="text-truncate">{{ $playedAt }}</li>
|
||||
{{ else }}
|
||||
<li class="text-truncate">
|
||||
<span {{ $playedAt }}></span>
|
||||
<span> ago</span>
|
||||
</li>
|
||||
{{ end }}
|
||||
|
||||
{{ if $isShows }}
|
||||
<li class="text-truncate">{{ $showTitle }}</li>
|
||||
<li class="text-truncate">{{ $showInfoFormat }}</li>
|
||||
{{ else if $isMusic }}
|
||||
<li class="text-truncate">{{ $artist }}</li>
|
||||
<li class="text-truncate">{{ $albumTitle }}</li>
|
||||
{{ end }}
|
||||
|
||||
<li class="text-truncate">{{ $title }}</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
''
|
|
@ -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;
|
||||
}
|
20
modules/nixos/services/glance/config/widgets/podcasts.nix
Normal file
20
modules/nixos/services/glance/config/widgets/podcasts.nix
Normal file
|
@ -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";
|
||||
}
|
||||
];
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
''
|
||||
<ul class="list list-gap-10 collapsible-container" data-collapse-after="5">
|
||||
{{ range .JSON.Array "specials.items" }}
|
||||
<li>
|
||||
<a class="size-h4 color-highlight block text-truncate" href="https://store.steampowered.com/app/{{ .Int "id" }}/">{{ .String "name" }}</a>
|
||||
<ul class="list-horizontal-text">
|
||||
<li>{{ .Int "final_price" | toFloat | mul 0.01 | printf "$%.2f" }}</li>
|
||||
{{ $discount := .Int "discount_percent" }}
|
||||
<li{{ if ge $discount 40 }} class="color-positive"{{ end }}>{{ $discount }}% off</li>
|
||||
</ul>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
''
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
type = "custom-api";
|
||||
title = "Steam Specials";
|
||||
cache = "12h";
|
||||
url = "https://store.steampowered.com/api/featuredcategories?cc=ca";
|
||||
template = import ./config;
|
||||
}
|
7
modules/nixos/services/glance/config/widgets/weather.nix
Normal file
7
modules/nixos/services/glance/config/widgets/weather.nix
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
type = "weather";
|
||||
title = "Weather";
|
||||
units = "metric";
|
||||
hour-format = "12h";
|
||||
location = "Winnipeg, Manitoba, Canada";
|
||||
}
|
|
@ -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 = [
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue