feeds: convert to module

This commit is contained in:
2023-01-08 05:24:56 +00:00
parent 488036beb3
commit e8f778fecd
12 changed files with 158 additions and 105 deletions

View File

@@ -2,6 +2,7 @@
{ {
imports = [ imports = [
./bluetooth.nix ./bluetooth.nix
./feeds.nix
./fs.nix ./fs.nix
./hardware ./hardware
./i2p.nix ./i2p.nix

View File

@@ -1,5 +1,4 @@
{ lib }: { ... }:
let let
hourly = { freq = "hourly"; }; hourly = { freq = "hourly"; };
daily = { freq = "daily"; }; daily = { freq = "daily"; };
@@ -13,24 +12,15 @@ let
tech = { cat = "tech"; }; tech = { cat = "tech"; };
uncat = { cat = "uncat"; }; uncat = { cat = "uncat"; };
text = { format = "text"; };
image = { format = "image"; };
podcast = { format = "podcast"; };
mkRss = format: url: { inherit url format; } // uncat // infrequent; mkRss = format: url: { inherit url format; } // uncat // infrequent;
# format-specific helpers # format-specific helpers
mkText = mkRss text; mkText = mkRss "text";
mkImg = mkRss image; mkImg = mkRss "image";
mkPod = mkRss podcast; mkPod = mkRss "podcast";
# host-specific helpers # 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 = [ podcasts = [
(mkPod "https://lexfridman.com/feed/podcast/" // rat // weekly) (mkPod "https://lexfridman.com/feed/podcast/" // rat // weekly)
## Astral Codex Ten ## Astral Codex Ten
@@ -154,41 +144,7 @@ in rec {
# ART # ART
(mkImg "https://miniature-calendar.com/feed" // art // daily) (mkImg "https://miniature-calendar.com/feed" // art // daily)
]; ];
in
all = texts ++ images ++ podcasts; {
sane.feeds = 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));
} }

View File

@@ -9,7 +9,7 @@
# $ sudo -u freshrss -g freshrss FRESHRSS_DATA_PATH=/var/lib/freshrss ./result/cli/export-opml-for-user.php --user admin # $ 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 = { sops.secrets.freshrss_passwd = {
sopsFile = ../../../secrets/servo.yaml; sopsFile = ../../../secrets/servo.yaml;
@@ -30,8 +30,8 @@
systemd.services.freshrss-import-feeds = systemd.services.freshrss-import-feeds =
let let
fresh = config.systemd.services.freshrss-config; fresh = config.systemd.services.freshrss-config;
feeds = import ../../../modules/home-manager/feeds.nix { inherit lib; }; feeds = config.sane.feeds;
opml = pkgs.writeText "sane-freshrss.opml" (feeds.feedsToOpml feeds.all); opml = pkgs.writeText "sane-freshrss.opml" (sane-lib.feeds.feedsToOpml feeds);
in { in {
inherit (fresh) wantedBy environment; inherit (fresh) wantedBy environment;
serviceConfig = { serviceConfig = {

View File

@@ -3,6 +3,7 @@
{ {
imports = [ imports = [
./allocations.nix ./allocations.nix
./feeds.nix
./fs ./fs
./gui ./gui
./home-manager ./home-manager

50
modules/feeds.nix Normal file
View 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.
'';
};
};
}

View File

@@ -11,7 +11,6 @@ let
cfg = config.sane.home-manager; cfg = config.sane.home-manager;
# extract `pkg` from `sane.packages.enabledUserPkgs` # extract `pkg` from `sane.packages.enabledUserPkgs`
pkg-list = pkgspec: builtins.map (e: e.pkg) pkgspec; pkg-list = pkgspec: builtins.map (e: e.pkg) pkgspec;
feeds = import ./feeds.nix { inherit lib; };
in in
{ {
imports = [ imports = [

View File

@@ -1,18 +1,18 @@
# gnome feeds RSS viewer # gnome feeds RSS viewer
{ lib, sane-lib, ... }: { config, lib, sane-lib, ... }:
let 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 { in {
sane.fs."/home/colin/.config/org.gabmus.gfeeds.json" = sane.fs."/home/colin/.config/org.gabmus.gfeeds.json" = sane-lib.fs.wantedText (
let builtins.toJSON {
myFeeds = feeds.texts ++ feeds.images;
in sane-lib.fs.wantedText (builtins.toJSON {
# feed format is a map from URL to a dict, # feed format is a map from URL to a dict,
# with dict["tags"] a list of string tags. # with dict["tags"] a list of string tags.
feeds = builtins.foldl' (acc: feed: acc // { feeds = builtins.foldl' (acc: feed: acc // {
"${feed.url}".tags = [ feed.cat feed.freq ]; "${feed.url}".tags = [ feed.cat feed.freq ];
}) {} myFeeds; }) {} wanted-feeds;
dark_reader = false; dark_reader = false;
new_first = true; new_first = true;
# windowsize = { # windowsize = {
@@ -31,10 +31,11 @@ in {
open_links_externally = true; open_links_externally = true;
full_feed_name = false; full_feed_name = false;
refresh_on_startup = true; refresh_on_startup = true;
tags = lib.lists.unique ( tags = lib.unique (
(builtins.catAttrs "cat" myFeeds) ++ (builtins.catAttrs "freq" myFeeds) (builtins.catAttrs "cat" wanted-feeds) ++ (builtins.catAttrs "freq" wanted-feeds)
); );
open_youtube_externally = false; open_youtube_externally = false;
media_player = "vlc"; # default: mpv media_player = "vlc"; # default: mpv
}); }
);
} }

View File

@@ -1,10 +1,12 @@
# gnome feeds RSS viewer # gnome feeds RSS viewer
{ lib, sane-lib, ... }: { config, sane-lib, ... }:
let let
feeds = import ./feeds.nix { inherit lib; }; feeds = sane-lib.feeds;
all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
in { in {
sane.fs."/home/colin/.config/gpodderFeeds.opml" = sane-lib.fs.wantedText ( sane.fs."/home/colin/.config/gpodderFeeds.opml" = sane-lib.fs.wantedText (
feeds.feedsToOpml feeds.podcasts feeds.feedsToOpml wanted-feeds
); );
} }

View File

@@ -1,10 +1,12 @@
# news-flash RSS viewer # news-flash RSS viewer
{ lib, sane-lib, ... }: { config, sane-lib, ... }:
let 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 { in {
sane.fs."/home/colin/.config/newsflashFeeds.opml" = sane-lib.fs.wantedText ( sane.fs."/home/colin/.config/newsflashFeeds.opml" = sane-lib.fs.wantedText (
feeds.feedsToOpml (feeds.texts ++ feeds.images) feeds.feedsToOpml wanted-feeds
); );
} }

View File

@@ -1,16 +1,18 @@
{ config, lib, sane-lib, ... }: { 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 lib.mkIf config.sane.home-manager.enable
{ {
sane.fs."/home/colin/.config/vlc/vlcrc" = sane.fs."/home/colin/.config/vlc/vlcrc" = sane-lib.fs.wantedText ''
let
feeds = import ./feeds.nix { inherit lib; };
podcastUrls = lib.strings.concatStringsSep "|" (
builtins.map (feed: feed.url) feeds.podcasts
);
in sane-lib.fs.wantedText ''
[podcast] [podcast]
podcast-urls=${podcastUrls} podcast-urls=${podcast-urls}
[core] [core]
metadata-network-access=0 metadata-network-access=0
[qt] [qt]

View File

@@ -1,6 +1,7 @@
{ lib, ... }@moduleArgs: { lib, ... }@moduleArgs:
{ {
feeds = import ./feeds.nix moduleArgs;
fs = import ./fs.nix moduleArgs; fs = import ./fs.nix moduleArgs;
path = import ./path.nix moduleArgs; path = import ./path.nix moduleArgs;
types = import ./types.nix moduleArgs; types = import ./types.nix moduleArgs;

38
modules/lib/feeds.nix Normal file
View 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>
'';
}