From baf48ed36c5828c6c7ae09f340f50f02f1986091 Mon Sep 17 00:00:00 2001 From: Guy Edwards Date: Wed, 17 Apr 2024 23:14:15 +0100 Subject: [PATCH] feat: add openers for links (#62) * feat: add openers for links allows custom commands to open links bsed on a regex closes #53 --- README.md | 47 ++++++++++++++++++++++++----------- internal/commands/commands.go | 21 ++++++++++++++++ internal/commands/tui.go | 6 ++--- internal/config/config.go | 8 ++++++ 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3b4e58f..7caffd7 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,20 @@ See [releases](https://github.com/guyfedwards/nom/releases) for binaries. E.g. $ curl -L https://github.com/guyfedwards/nom/releases/download/v2.1.4/nom_2.1.4_darwin_amd64.tar.gz | tar -xzvf - ``` -## Config -Config lives by default in `$XDG_CONFIG_HOME/nom/config.yml` -### Feeds -Add feeds with the `add` command +## Usage ```sh -$ nom add +$ nom # start TUI +$ nom list -n 20 # list feed items in $PAGER, optionally show more +$ nom add +$ nom --feed # preview feed without adding to config ``` -or add directly to the config at `$XDG_CONFIG_HOME/nom/config.yml` on unix systems and `$HOME/Library/Application Support/nom/config.yml` on darwin. + +## Config +Config lives by default in `$XDG_CONFIG_HOME/nom/config.yml` or `$HOME/Library/Application Support/nom/config.yml` on darwin. +You can customise the location of the config file with the `--config-path` flag. + +### Feeds +Feeds are added to the config file and have a url and name. ```yaml feeds: - url: https://dropbox.tech/feed @@ -30,7 +36,15 @@ feeds: name: dropbox - url: https://snyk.io/blog/feed ``` -You can customise the location of the config file with the `--config-path` flag. +You can also add feeds with the `add` command: +```sh +$ nom add +``` +#### Youtube feeds +To add youtube feeds you can go to a channel and run the following in the browser console to get the rss feed link: +```js +console.log(`https://www.youtube.com/feeds/videos.xml?channel_id=${document.querySelector("link[rel='canonical']").href.split('/channel/').reverse()[0]}`) +``` ### Show read (default: false) Show read items by default. (can be toggled with M) @@ -38,7 +52,7 @@ Show read items by default. (can be toggled with M) showread: true ``` ### Auto read (default: false) -Automatically mark items as read on selection or navigation through items. () +Automatically mark items as read on selection or navigation through items. ```yaml autoread: true ``` @@ -56,16 +70,19 @@ backends: password: muchstrong ``` +### Openers +By default links are opened in the browser, you can specify commands to open certain links based on a regex string. +`regex` can be any valid golang regex string, it will be matched against the feed item link. +`cmd` is run as a child command. The `%s` denotes the position of the link in the command. +```yaml +openers: + - regex: "youtube" + cmd: "mpv %s" +``` + ## Store `nom` uses sqlite as a store for feeds and metadata. It is stored next to the config in `$XDG_CONFIG_HOME/nom/nom.db`. This can be backed up like any file and will store articles, read state etc. It can also be deleted to start from scratch redownloading all articles and no state. -## Usage -```sh -$ nom # open TUI -$ nom list -n 20 # optionally show more -$ nom add -$ nom --feed # preview feed without adding to config -``` ## Building and Running via Docker Build nom image ```sh diff --git a/internal/commands/commands.go b/internal/commands/commands.go index af2b8f7..e4e9059 100644 --- a/internal/commands/commands.go +++ b/internal/commands/commands.go @@ -38,6 +38,27 @@ func convertItems(its []store.Item) []list.Item { return items } +func (c Commands) OpenLink(url string) error { + for _, o := range c.config.Openers { + match, err := regexp.MatchString(o.Regex, url) + if err != nil { + return fmt.Errorf("OpenLink: regex: %w", err) + } + + if match { + c := fmt.Sprintf(o.Cmd, url) + parts := strings.Fields(c) + + cmd := exec.Command(parts[0], parts[1:]...) + if err := cmd.Run(); err != nil { + return fmt.Errorf("OpenLink: exec: %w", err) + } + } + } + + return c.OpenInBrowser(url) +} + func (c Commands) OpenInBrowser(url string) error { var cmd string var args []string diff --git a/internal/commands/tui.go b/internal/commands/tui.go index 63b0a5c..db762b2 100644 --- a/internal/commands/tui.go +++ b/internal/commands/tui.go @@ -233,7 +233,7 @@ func updateList(msg tea.Msg, m model) (tea.Model, tea.Cmd) { break } current := m.list.SelectedItem().(TUIItem) - err := m.commands.OpenInBrowser(current.URL) + err := m.commands.OpenLink(current.URL) if err != nil { return m, tea.Quit } @@ -287,7 +287,7 @@ func updateViewport(msg tea.Msg, m model) (tea.Model, tea.Cmd) { case "o": current := m.list.SelectedItem().(TUIItem) - err := m.commands.OpenInBrowser(current.URL) + err := m.commands.OpenLink(current.URL) if err != nil { return m, tea.Quit } @@ -431,7 +431,7 @@ func Render(items []list.Item, cmds Commands, errors []string) error { return []key.Binding{ key.NewBinding( key.WithKeys("o"), - key.WithHelp("o", "open in browser"), + key.WithHelp("o", "open link"), ), } } diff --git a/internal/config/config.go b/internal/config/config.go index 229e2fe..7866773 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -31,6 +31,12 @@ type Backends struct { FreshRSS *FreshRSSBackend `yaml:"freshrss,omitempty"` } +type Opener struct { + Regex string `yaml:"regex"` + Cmd string `yaml:"cmd"` +} + +// need to add to Load() below if loading from config file type Config struct { configPath string ConfigDir string `yaml:"-"` @@ -42,6 +48,7 @@ type Config struct { ShowRead bool `yaml:"showread,omitempty"` AutoRead bool `yaml:"autoread,omitempty"` ShowFavourites bool + Openers []Opener `yaml:"openers,omitempty"` } func (c *Config) ToggleShowRead() { @@ -110,6 +117,7 @@ func (c *Config) Load() error { c.AutoRead = fileConfig.AutoRead c.Feeds = fileConfig.Feeds + c.Openers = fileConfig.Openers // only set pager if it's not defined already, config file is lower // precidence than flags/env that can be passed to New if c.Pager == "" {