diff --git a/modules/universal/env/feeds.nix b/modules/universal/env/feeds.nix index b24fb0c36..590561546 100644 --- a/modules/universal/env/feeds.nix +++ b/modules/universal/env/feeds.nix @@ -1,148 +1,175 @@ { lib }: -rec { - # TODO: fold this into RSS, with an `audio` category - podcastUrls = [ - "https://lexfridman.com/feed/podcast/" +let + hourly = { freq = "hourly"; }; + daily = { freq = "daily"; }; + weekly = { freq = "weekly"; }; + infrequent = { freq = "infrequent"; }; + + art = { cat = "art"; }; + humor = { cat = "humor"; }; + pol = { cat = "pol"; }; # or maybe just "social" + rat = { cat = "rat"; }; + tech = { cat = "tech"; }; + uncat = { cat = "uncat"; }; + + text = { format = "text"; }; + image = { format = "image"; }; + podcast = { format = "podcast"; }; + + mkRss = format: url: { inherit url format; } // uncat // infrequent; + mkText = mkRss text; + mkImg = mkRss image; + mkPod = mkRss podcast; + + # 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 - "http://feeds.libsyn.com/108018/rss" + (mkPod "http://feeds.libsyn.com/108018/rss" // rat // daily) ## Econ Talk - "https://feeds.simplecast.com/wgl4xEgL" + (mkPod "https://feeds.simplecast.com/wgl4xEgL" // rat // daily) ## Cory Doctorow - "https://feeds.feedburner.com/doctorow_podcast" - "https://congressionaldish.libsyn.com/rss" + (mkPod "https://feeds.feedburner.com/doctorow_podcast" // pol // infrequent) + (mkPod "https://congressionaldish.libsyn.com/rss" // pol // infrequent) ## Civboot - "https://anchor.fm/s/34c7232c/podcast/rss" - "https://feeds.feedburner.com/80000HoursPodcast" - "https://allinchamathjason.libsyn.com/rss" - "https://acquired.libsyn.com/rss" - "https://rss.acast.com/deconstructed" + (mkPod "https://anchor.fm/s/34c7232c/podcast/rss" // tech // infrequent) + (mkPod "https://feeds.feedburner.com/80000HoursPodcast" // rat // weekly) + (mkPod "https://allinchamathjason.libsyn.com/rss" // pol // weekly) + (mkPod "https://acquired.libsyn.com/rss" // tech // infrequent) + (mkPod "https://rss.acast.com/deconstructed" // pol // infrequent) ## The Daily - "https://feeds.simplecast.com/54nAGcIl" - "https://rss.acast.com/intercepted-with-jeremy-scahill" - "https://podcast.posttv.com/itunes/post-reports.xml" + (mkPod "https://feeds.simplecast.com/54nAGcIl" // pol // daily) + (mkPod "https://rss.acast.com/intercepted-with-jeremy-scahill" // pol // weekly) + (mkPod "https://podcast.posttv.com/itunes/post-reports.xml" // pol // weekly) ## Eric Weinstein - "https://rss.art19.com/the-portal" - "https://feeds.megaphone.fm/darknetdiaries" - "http://feeds.wnyc.org/radiolab" - "https://wakingup.libsyn.com/rss" + (mkPod "https://rss.art19.com/the-portal" // rat // infrequent) + (mkPod "https://feeds.megaphone.fm/darknetdiaries" // tech // infrequent) + (mkPod "http://feeds.wnyc.org/radiolab" // pol // infrequent) + (mkPod "https://wakingup.libsyn.com/rss" // pol // infrequent) ## 99% Invisible - "https://feeds.simplecast.com/BqbsxVfO" - "https://rss.acast.com/ft-tech-tonic" - "https://feeds.feedburner.com/dancarlin/history?format=xml" + (mkPod "https://feeds.simplecast.com/BqbsxVfO" // pol // infrequent) + (mkPod "https://rss.acast.com/ft-tech-tonic" // tech // infrequent) + (mkPod "https://feeds.feedburner.com/dancarlin/history?format=xml" // rat // infrequent) ## 60 minutes (NB: this features more than *just* audio?) - "https://www.cbsnews.com/latest/rss/60-minutes" + (mkPod "https://www.cbsnews.com/latest/rss/60-minutes" // pol // infrequent) ]; - rss = - let - hourly = { freq = "hourly"; }; - daily = { freq = "daily"; }; - weekly = { freq = "weekly"; }; - infrequent = { freq = "infrequent"; }; - rat = { cat = "rat"; }; - tech = { cat = "tech"; }; - pol = { cat = "pol"; }; - uncat = { cat = "uncat"; }; - visual = { cat = "visual"; }; - in { + texts = [ # AGGREGATORS (> 1 post/day) - "https://www.lesswrong.com/feed.xml" = rat // hourly; - "http://www.econlib.org/index.xml" = pol // hourly; + (mkText "https://www.lesswrong.com/feed.xml" // rat // hourly) + (mkText "http://www.econlib.org/index.xml" // pol // hourly) # AGGREGATORS (< 1 post/day) - "https://palladiummag.com/feed" = uncat // weekly; - "https://profectusmag.com/feed" = uncat // weekly; - "https://semiaccurate.com/feed" = tech // weekly; - "https://linuxphoneapps.org/blog/atom.xml" = tech // infrequent; - "https://spectrum.ieee.org/rss" = tech // weekly; + (mkText "https://palladiummag.com/feed" // uncat // weekly) + (mkText "https://profectusmag.com/feed" // uncat // weekly) + (mkText "https://semiaccurate.com/feed" // tech // weekly) + (mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent) + (mkText "https://spectrum.ieee.org/rss" // tech // weekly) ## No Moods, Ads or Cutesy Fucking Icons - "https://www.rifters.com/crawl/?feed=rss2" = uncat // weekly; + (mkText "https://www.rifters.com/crawl/?feed=rss2" // uncat // weekly) # DEVELOPERS - "https://mg.lol/blog/rss/" = infrequent // tech; + (mkText "https://mg.lol/blog/rss/" // infrequent // tech) ## Ken Shirriff - "https://www.righto.com/feeds/posts/default" = tech // infrequent; + (mkText "https://www.righto.com/feeds/posts/default" // tech // infrequent) ## Vitalik Buterin - "https://vitalik.ca/feed.xml" = tech // infrequent; + (mkText "https://vitalik.ca/feed.xml" // tech // infrequent) ## ian (Sanctuary) - "https://sagacioussuricata.com/feed.xml" = tech // infrequent; + (mkText "https://sagacioussuricata.com/feed.xml" // tech // infrequent) ## Bunnie Juang - "https://www.bunniestudios.com/blog/?feed=rss2" = tech // infrequent; - "https://blog.danieljanus.pl/atom.xml" = tech // infrequent; - "https://ianthehenry.com/feed.xml" = tech // infrequent; - "https://bitbashing.io/feed.xml" = tech // infrequent; - "https://idiomdrottning.org/feed.xml" = uncat // daily; + (mkText "https://www.bunniestudios.com/blog/?feed=rss2" // tech // infrequent) + (mkText "https://blog.danieljanus.pl/atom.xml" // tech // infrequent) + (mkText "https://ianthehenry.com/feed.xml" // tech // infrequent) + (mkText "https://bitbashing.io/feed.xml" // tech // infrequent) + (mkText "https://idiomdrottning.org/feed.xml" // uncat // daily) # (TECH; POL) COMMENTATORS - "http://benjaminrosshoffman.com/feed" = pol // weekly; + (mkText "http://benjaminrosshoffman.com/feed" // pol // weekly) ## Ben Thompson - "https://www.stratechery.com/rss" = pol // weekly; + (mkText "https://www.stratechery.com/rss" // pol // weekly) ## Balaji - "https://balajis.com/rss" = pol // weekly; - "https://www.ben-evans.com/benedictevans/rss.xml" = pol // weekly; - "https://www.lynalden.com/feed" = pol // infrequent; - "https://austinvernon.site/rss.xml" = tech // infrequent; - "https://oversharing.substack.com/feed" = pol // daily; - "https://doomberg.substack.com/feed" = tech // weekly; + (mkText "https://balajis.com/rss" // pol // weekly) + (mkText "https://www.ben-evans.com/benedictevans/rss.xml" // pol // weekly) + (mkText "https://www.lynalden.com/feed" // pol // infrequent) + (mkText "https://austinvernon.site/rss.xml" // tech // infrequent) + (mkText "https://oversharing.substack.com/feed" // pol // daily) + (mkText "https://doomberg.substack.com/feed" // tech // weekly) ## David Rosenthal - "https://blog.dshr.org/rss.xml" = pol // weekly; + (mkText "https://blog.dshr.org/rss.xml" // pol // weekly) ## Matt Levine - "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" = pol // weekly; + (mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly) # RATIONALITY/PHILOSOPHY/ETC - "https://samkriss.substack.com/feed" = uncat // infrequent; # ... satire? phil? - "https://unintendedconsequenc.es/feed" = rat // infrequent; - "https://applieddivinitystudies.com/atom.xml" = rat // weekly; - "https://slimemoldtimemold.com/feed.xml" = rat // weekly; - "https://www.richardcarrier.info/feed" = rat // weekly; - "https://www.gwern.net/feed.xml" = uncat // infrequent; + (mkText "https://samkriss.substack.com/feed" // humor // infrequent) + (mkText "https://unintendedconsequenc.es/feed" // rat // infrequent) + (mkText "https://applieddivinitystudies.com/atom.xml" // rat // weekly) + (mkText "https://slimemoldtimemold.com/feed.xml" // rat // weekly) + (mkText "https://www.richardcarrier.info/feed" // rat // weekly) + (mkText "https://www.gwern.net/feed.xml" // uncat // infrequent) ## Jason Crawford - "https://rootsofprogress.org/feed.xml" = rat // weekly; + (mkText "https://rootsofprogress.org/feed.xml" // rat // weekly) ## Robin Hanson - "https://www.overcomingbias.com/feed" = rat // daily; + (mkText "https://www.overcomingbias.com/feed" // rat // daily) ## Scott Alexander - "https://astralcodexten.substack.com/feed.xml" = rat // daily; + (mkText "https://astralcodexten.substack.com/feed.xml" // rat // daily) ## Paul Christiano - "https://sideways-view.com/feed" = rat // infrequent; + (mkText "https://sideways-view.com/feed" // rat // infrequent) ## Sean Carroll - "https://www.preposterousuniverse.com/rss" = rat // infrequent; - - # COMICS - "https://www.smbc-comics.com/comic/rss" = visual // daily; - "https://xkcd.com/atom.xml" = visual // daily; - "http://dilbert.com/feed" = visual // daily; - - # ART - "https://miniature-calendar.com/feed" = visual // daily; + (mkText "https://www.preposterousuniverse.com/rss" // rat // infrequent) # CODE - "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" = tech // infrequent; - }; + (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent) + ]; - # return only the URLs which match this category - filterCat = cat: builtins.filter (url: rss."${url}".cat == cat) (builtins.attrNames rss); + images = [ + (mkImg "https://www.smbc-comics.com/comic/rss" // humor // daily) + (mkImg "https://xkcd.com/atom.xml" // humor // daily) + (mkImg "http://dilbert.com/feed" // humor // daily) + + # 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 = url: ''''; + opmlTerminal = feed: ''''; # a list of RSS feeds. - opmlTerminals = urls: lib.strings.concatStringsSep "\n" (builtins.map opmlTerminal urls); + opmlTerminals = feeds: lib.strings.concatStringsSep "\n" (builtins.map opmlTerminal feeds); # one node which packages some flat grouping of terminals. - opmlGroup = title: urls: '' + opmlGroup = title: feeds: '' - ${opmlTerminals urls} + ${opmlTerminals feeds} ''; + # 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 = bodies: - let - body = lib.strings.concatStringsSep "\n" bodies; - in '' + opmlTopLevel = body: '' - ${body} + + ${body} ''; + + # **primary API**: generate a OPML file from the provided feeds + feedsToOpml = feeds: opmlTopLevel (opmlGroups (partitionByCat feeds)); } diff --git a/modules/universal/env/home-manager.nix b/modules/universal/env/home-manager.nix index 20b94d081..c872388ae 100644 --- a/modules/universal/env/home-manager.nix +++ b/modules/universal/env/home-manager.nix @@ -206,35 +206,35 @@ in xdg.configFile."vlc/vlcrc".text = let - podcastUrls = lib.strings.concatStringsSep "|" feeds.podcastUrls; + podcastUrls = lib.strings.concatStringsSep "|" ( + builtins.map (feed: feed.url) feeds.podcasts + ); in '' - [podcast] - podcast-urls=${podcastUrls} - [core] - metadata-network-access=0 - [qt] - qt-privacy-ask=0 + [podcast] + podcast-urls=${podcastUrls} + [core] + metadata-network-access=0 + [qt] + qt-privacy-ask=0 ''; xdg.configFile."gpodderFeeds.opml".text = with feeds; - opmlToplevel [(opmlTerminals podcastUrls)]; + feedsToOpml feeds.podcasts; # news-flash RSS viewer xdg.configFile."newsflashFeeds.opml".text = with feeds; - let - opmlForCat = cat: opmlGroup cat (filterCat cat); - outlines = [ - (opmlForCat "uncat") - (opmlForCat "rat") - (opmlForCat "tech") - (opmlForCat "pol") - (opmlForCat "visual") - ]; - in opmlToplevel outlines; + feedsToOpml (feeds.texts ++ feeds.images); # gnome feeds RSS viewer - xdg.configFile."org.gabmus.gfeeds.json".text = builtins.toJSON { - feeds = builtins.mapAttrs (r: p: { tags = [ p.cat p.freq ]; }) feeds.rss; + xdg.configFile."org.gabmus.gfeeds.json".text = + let + myFeeds = feeds.texts ++ feeds.images; + in 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 = { @@ -253,17 +253,9 @@ in open_links_externally = true; full_feed_name = false; refresh_on_startup = true; - tags = [ - # hourly => aggregator - # daily => prolifiq writer - # weekly => i can keep up with most -- but maybe not all -- of their content - # infrequent => i can read everything in this category - "hourly" "daily" "weekly" "infrequent" - # rat[ionality] gets used interchangably with philosophy, here. - # pol[itical] gets used for social commentary and economics as well. - # visual gets used for comics/art - "uncat" "rat" "tech" "pol" "visual" - ]; + tags = lib.lists.unique ( + (builtins.catAttrs "cat" myFeeds) ++ (builtins.catAttrs "freq" myFeeds) + ); open_youtube_externally = false; media_player = "vlc"; # default: mpv };