feeds: convert to module
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
{
|
||||
imports = [
|
||||
./bluetooth.nix
|
||||
./feeds.nix
|
||||
./fs.nix
|
||||
./hardware
|
||||
./i2p.nix
|
||||
|
@@ -1,5 +1,4 @@
|
||||
{ lib }:
|
||||
|
||||
{ ... }:
|
||||
let
|
||||
hourly = { freq = "hourly"; };
|
||||
daily = { freq = "daily"; };
|
||||
@@ -13,24 +12,15 @@ let
|
||||
tech = { cat = "tech"; };
|
||||
uncat = { cat = "uncat"; };
|
||||
|
||||
text = { format = "text"; };
|
||||
image = { format = "image"; };
|
||||
podcast = { format = "podcast"; };
|
||||
|
||||
mkRss = format: url: { inherit url format; } // uncat // infrequent;
|
||||
# format-specific helpers
|
||||
mkText = mkRss text;
|
||||
mkImg = mkRss image;
|
||||
mkPod = mkRss podcast;
|
||||
mkText = mkRss "text";
|
||||
mkImg = mkRss "image";
|
||||
mkPod = mkRss "podcast";
|
||||
|
||||
# host-specific helpers
|
||||
mkSubstack = subdomain: mkText "https://${subdomain}.substack.com/feed";
|
||||
mkSubstack = subdomain: { substack = subdomain; };
|
||||
|
||||
# merge the attrs `new` into each value of the attrs `addTo`
|
||||
addAttrs = new: addTo: builtins.mapAttrs (k: v: v // new) addTo;
|
||||
# for each value in `attrs`, add a value to the child attrs which holds its key within the parent attrs.
|
||||
withInverseMapping = key: attrs: builtins.mapAttrs (k: v: v // { "${key}" = k; }) attrs;
|
||||
in rec {
|
||||
podcasts = [
|
||||
(mkPod "https://lexfridman.com/feed/podcast/" // rat // weekly)
|
||||
## Astral Codex Ten
|
||||
@@ -154,41 +144,7 @@ in rec {
|
||||
# ART
|
||||
(mkImg "https://miniature-calendar.com/feed" // art // daily)
|
||||
];
|
||||
|
||||
all = texts ++ images ++ podcasts;
|
||||
|
||||
# return only the feed items which match this category (e.g. "tech")
|
||||
filterCat = cat: feeds: builtins.filter (item: item.cat == cat) feeds;
|
||||
# return only the feed items which match this format (e.g. "podcast")
|
||||
filterFormat = format: feeds: builtins.filter (item: item.format == format) feeds;
|
||||
|
||||
# transform a list of feeds into an attrs mapping cat => [ feed0 feed1 ... ]
|
||||
partitionByCat = feeds: builtins.groupBy (f: f.cat) feeds;
|
||||
|
||||
# represents a single RSS feed.
|
||||
opmlTerminal = feed: ''<outline xmlUrl="${feed.url}" type="rss"/>'';
|
||||
# a list of RSS feeds.
|
||||
opmlTerminals = feeds: lib.strings.concatStringsSep "\n" (builtins.map opmlTerminal feeds);
|
||||
# one node which packages some flat grouping of terminals.
|
||||
opmlGroup = title: feeds: ''
|
||||
<outline text="${title}" title="${title}">
|
||||
${opmlTerminals feeds}
|
||||
</outline>
|
||||
'';
|
||||
# a list of groups (`groupMap` is an attrs mapping groupName => [ feed0 feed1 ... ]).
|
||||
opmlGroups = groupMap: lib.strings.concatStringsSep "\n" (
|
||||
builtins.attrValues (builtins.mapAttrs opmlGroup groupMap)
|
||||
);
|
||||
# top-level OPML file which could be consumed by something else.
|
||||
opmlTopLevel = body: ''
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<opml version="2.0">
|
||||
<body>
|
||||
${body}
|
||||
</body>
|
||||
</opml>
|
||||
'';
|
||||
|
||||
# **primary API**: generate a OPML file from the provided feeds
|
||||
feedsToOpml = feeds: opmlTopLevel (opmlGroups (partitionByCat feeds));
|
||||
in
|
||||
{
|
||||
sane.feeds = texts ++ images ++ podcasts;
|
||||
}
|
@@ -9,7 +9,7 @@
|
||||
# $ sudo -u freshrss -g freshrss FRESHRSS_DATA_PATH=/var/lib/freshrss ./result/cli/export-opml-for-user.php --user admin
|
||||
# ```
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
{
|
||||
sops.secrets.freshrss_passwd = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
@@ -30,8 +30,8 @@
|
||||
systemd.services.freshrss-import-feeds =
|
||||
let
|
||||
fresh = config.systemd.services.freshrss-config;
|
||||
feeds = import ../../../modules/home-manager/feeds.nix { inherit lib; };
|
||||
opml = pkgs.writeText "sane-freshrss.opml" (feeds.feedsToOpml feeds.all);
|
||||
feeds = config.sane.feeds;
|
||||
opml = pkgs.writeText "sane-freshrss.opml" (sane-lib.feeds.feedsToOpml feeds);
|
||||
in {
|
||||
inherit (fresh) wantedBy environment;
|
||||
serviceConfig = {
|
||||
|
@@ -3,6 +3,7 @@
|
||||
{
|
||||
imports = [
|
||||
./allocations.nix
|
||||
./feeds.nix
|
||||
./fs
|
||||
./gui
|
||||
./home-manager
|
||||
|
50
modules/feeds.nix
Normal file
50
modules/feeds.nix
Normal file
@@ -0,0 +1,50 @@
|
||||
{ lib, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
feed = types.submodule ({ config, ... }: {
|
||||
options = {
|
||||
freq = mkOption {
|
||||
type = types.enum [ "hourly" "daily" "weekly" "infrequent" ];
|
||||
default = "infrequent";
|
||||
};
|
||||
cat = mkOption {
|
||||
type = types.enum [ "art" "humor" "pol" "rat" "tech" "uncat" ];
|
||||
default = "uncat";
|
||||
};
|
||||
format = mkOption {
|
||||
type = types.enum [ "text" "image" "podcast" ];
|
||||
};
|
||||
url = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
url to a RSS feed
|
||||
'';
|
||||
};
|
||||
substack = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
if the feed is a substack domain, just enter the subdomain here and the url/format field can be populated automatically
|
||||
'';
|
||||
};
|
||||
};
|
||||
config = lib.mkIf (config.substack != null) {
|
||||
url = "https://${config.substack}.substack.com/feed";
|
||||
format = "text";
|
||||
};
|
||||
});
|
||||
in
|
||||
{
|
||||
# we don't explicitly generate anything from the feeds here.
|
||||
# instead, config.sane.feeds is used by a variety of services at their definition site.
|
||||
options = {
|
||||
sane.feeds = mkOption {
|
||||
type = types.listOf feed;
|
||||
default = [];
|
||||
description = ''
|
||||
RSS feeds indexed by a human-readable name.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
@@ -11,7 +11,6 @@ let
|
||||
cfg = config.sane.home-manager;
|
||||
# extract `pkg` from `sane.packages.enabledUserPkgs`
|
||||
pkg-list = pkgspec: builtins.map (e: e.pkg) pkgspec;
|
||||
feeds = import ./feeds.nix { inherit lib; };
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
|
@@ -1,40 +1,41 @@
|
||||
# gnome feeds RSS viewer
|
||||
{ lib, sane-lib, ... }:
|
||||
{ config, lib, sane-lib, ... }:
|
||||
|
||||
let
|
||||
feeds = import ./feeds.nix { inherit lib; };
|
||||
feeds = sane-lib.feeds;
|
||||
all-feeds = config.sane.feeds;
|
||||
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
||||
in {
|
||||
sane.fs."/home/colin/.config/org.gabmus.gfeeds.json" =
|
||||
let
|
||||
myFeeds = feeds.texts ++ feeds.images;
|
||||
in sane-lib.fs.wantedText (builtins.toJSON {
|
||||
# feed format is a map from URL to a dict,
|
||||
# with dict["tags"] a list of string tags.
|
||||
feeds = builtins.foldl' (acc: feed: acc // {
|
||||
"${feed.url}".tags = [ feed.cat feed.freq ];
|
||||
}) {} myFeeds;
|
||||
dark_reader = false;
|
||||
new_first = true;
|
||||
# windowsize = {
|
||||
# width = 350;
|
||||
# height = 650;
|
||||
# };
|
||||
max_article_age_days = 90;
|
||||
enable_js = false;
|
||||
max_refresh_threads = 3;
|
||||
# saved_items = {};
|
||||
# read_items = [];
|
||||
show_read_items = true;
|
||||
full_article_title = true;
|
||||
# views: "webview", "reader", "rsscont"
|
||||
default_view = "rsscont";
|
||||
open_links_externally = true;
|
||||
full_feed_name = false;
|
||||
refresh_on_startup = true;
|
||||
tags = lib.lists.unique (
|
||||
(builtins.catAttrs "cat" myFeeds) ++ (builtins.catAttrs "freq" myFeeds)
|
||||
);
|
||||
open_youtube_externally = false;
|
||||
media_player = "vlc"; # default: mpv
|
||||
});
|
||||
sane.fs."/home/colin/.config/org.gabmus.gfeeds.json" = sane-lib.fs.wantedText (
|
||||
builtins.toJSON {
|
||||
# feed format is a map from URL to a dict,
|
||||
# with dict["tags"] a list of string tags.
|
||||
feeds = builtins.foldl' (acc: feed: acc // {
|
||||
"${feed.url}".tags = [ feed.cat feed.freq ];
|
||||
}) {} wanted-feeds;
|
||||
dark_reader = false;
|
||||
new_first = true;
|
||||
# windowsize = {
|
||||
# width = 350;
|
||||
# height = 650;
|
||||
# };
|
||||
max_article_age_days = 90;
|
||||
enable_js = false;
|
||||
max_refresh_threads = 3;
|
||||
# saved_items = {};
|
||||
# read_items = [];
|
||||
show_read_items = true;
|
||||
full_article_title = true;
|
||||
# views: "webview", "reader", "rsscont"
|
||||
default_view = "rsscont";
|
||||
open_links_externally = true;
|
||||
full_feed_name = false;
|
||||
refresh_on_startup = true;
|
||||
tags = lib.unique (
|
||||
(builtins.catAttrs "cat" wanted-feeds) ++ (builtins.catAttrs "freq" wanted-feeds)
|
||||
);
|
||||
open_youtube_externally = false;
|
||||
media_player = "vlc"; # default: mpv
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
# gnome feeds RSS viewer
|
||||
{ lib, sane-lib, ... }:
|
||||
{ config, sane-lib, ... }:
|
||||
|
||||
let
|
||||
feeds = import ./feeds.nix { inherit lib; };
|
||||
feeds = sane-lib.feeds;
|
||||
all-feeds = config.sane.feeds;
|
||||
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
|
||||
in {
|
||||
sane.fs."/home/colin/.config/gpodderFeeds.opml" = sane-lib.fs.wantedText (
|
||||
feeds.feedsToOpml feeds.podcasts
|
||||
feeds.feedsToOpml wanted-feeds
|
||||
);
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
# news-flash RSS viewer
|
||||
{ lib, sane-lib, ... }:
|
||||
{ config, sane-lib, ... }:
|
||||
|
||||
let
|
||||
feeds = import ./feeds.nix { inherit lib; };
|
||||
feeds = sane-lib.feeds;
|
||||
all-feeds = config.sane.feeds;
|
||||
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
||||
in {
|
||||
sane.fs."/home/colin/.config/newsflashFeeds.opml" = sane-lib.fs.wantedText (
|
||||
feeds.feedsToOpml (feeds.texts ++ feeds.images)
|
||||
feeds.feedsToOpml wanted-feeds
|
||||
);
|
||||
}
|
||||
|
@@ -1,16 +1,18 @@
|
||||
{ config, lib, sane-lib, ... }:
|
||||
|
||||
let
|
||||
feeds = sane-lib.feeds;
|
||||
all-feeds = config.sane.feeds;
|
||||
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
|
||||
podcast-urls = lib.concatStringsSep "|" (
|
||||
builtins.map (feed: feed.url) wanted-feeds
|
||||
);
|
||||
in
|
||||
lib.mkIf config.sane.home-manager.enable
|
||||
{
|
||||
sane.fs."/home/colin/.config/vlc/vlcrc" =
|
||||
let
|
||||
feeds = import ./feeds.nix { inherit lib; };
|
||||
podcastUrls = lib.strings.concatStringsSep "|" (
|
||||
builtins.map (feed: feed.url) feeds.podcasts
|
||||
);
|
||||
in sane-lib.fs.wantedText ''
|
||||
sane.fs."/home/colin/.config/vlc/vlcrc" = sane-lib.fs.wantedText ''
|
||||
[podcast]
|
||||
podcast-urls=${podcastUrls}
|
||||
podcast-urls=${podcast-urls}
|
||||
[core]
|
||||
metadata-network-access=0
|
||||
[qt]
|
||||
|
@@ -1,6 +1,7 @@
|
||||
{ lib, ... }@moduleArgs:
|
||||
|
||||
{
|
||||
feeds = import ./feeds.nix moduleArgs;
|
||||
fs = import ./fs.nix moduleArgs;
|
||||
path = import ./path.nix moduleArgs;
|
||||
types = import ./types.nix moduleArgs;
|
||||
|
38
modules/lib/feeds.nix
Normal file
38
modules/lib/feeds.nix
Normal file
@@ -0,0 +1,38 @@
|
||||
{ lib, ... }:
|
||||
|
||||
rec {
|
||||
# PRIMARY API: generate a OPML file from a list of feeds
|
||||
feedsToOpml = feeds: opmlTopLevel (opmlGroups (partitionByCat feeds));
|
||||
|
||||
# only keep feeds whose category is one of the provided
|
||||
filterByFormat = fmts: builtins.filter (feed: builtins.elem feed.format fmts);
|
||||
|
||||
## INTERNAL APIS
|
||||
|
||||
# transform a list of feeds into an attrs mapping cat => [ feed0 feed1 ... ]
|
||||
partitionByCat = feeds: builtins.groupBy (f: f.cat) feeds;
|
||||
|
||||
# represents a single RSS feed.
|
||||
opmlTerminal = feed: ''<outline xmlUrl="${feed.url}" type="rss"/>'';
|
||||
# a list of RSS feeds.
|
||||
opmlTerminals = feeds: lib.concatStringsSep "\n" (builtins.map opmlTerminal feeds);
|
||||
# one node which packages some flat grouping of terminals.
|
||||
opmlGroup = title: feeds: ''
|
||||
<outline text="${title}" title="${title}">
|
||||
${opmlTerminals feeds}
|
||||
</outline>
|
||||
'';
|
||||
# a list of groups (`groupMap` is an attrs mapping groupName => [ feed0 feed1 ... ]).
|
||||
opmlGroups = groupMap: lib.concatStringsSep "\n" (
|
||||
builtins.attrValues (builtins.mapAttrs opmlGroup groupMap)
|
||||
);
|
||||
# top-level OPML file which could be consumed by something else.
|
||||
opmlTopLevel = body: ''
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<opml version="2.0">
|
||||
<body>
|
||||
${body}
|
||||
</body>
|
||||
</opml>
|
||||
'';
|
||||
}
|
Reference in New Issue
Block a user