mirror of
https://github.com/wezm/wezm.net.git
synced 2024-12-18 10:19:54 +00:00
Add Feedlynx post
This commit is contained in:
parent
c3e3f76f09
commit
02b5bffb41
6 changed files with 164 additions and 9 deletions
131
v2/content/posts/2024/announcing-feedlynx/index.md
Normal file
131
v2/content/posts/2024/announcing-feedlynx/index.md
Normal file
|
@ -0,0 +1,131 @@
|
|||
+++
|
||||
title = "Announcing Feedlynx"
|
||||
date = 2024-07-29T09:43:57+10:00
|
||||
|
||||
#[extra]
|
||||
#updated = 2024-07-26T10:34:50+10:00
|
||||
+++
|
||||
|
||||
{{ float_svg(image="images/feedlynx.svg", width=75, float="left",
|
||||
alt="Feedlynx logo: a caricature of a Lynx with a stem in its mouth. At the end of the stem is the orange RSS logo.") }}
|
||||
|
||||
My latest project, [Feedlynx], is a self-hosted tool that allows you to
|
||||
collect links in an RSS feed[^1]. You subscribe to the feed in your RSS reader of
|
||||
choice and read or watch later at your leisure. Plus it has an adorable mascot!
|
||||
|
||||
Feedlynx runs on most mainstream operating systems including Linux, macOS, BSD,
|
||||
and Windows and has no runtime dependencies. Check out [the latest release][releases] to
|
||||
download pre-compiled binaries for some common platforms.
|
||||
|
||||
After a few weeks using Feedlynx myself I think it's ready for others to check out.
|
||||
Read on for more information about my motivations behind building Feedlynx.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
### Motivation
|
||||
|
||||
Since [moving all my YouTube subscriptions to my RSS
|
||||
reader](@/posts/2024/youtube-subscriptions-opml/index.md) there was one thing I
|
||||
missed from using YouTube directly: Watch Later. For videos that I'd encounter
|
||||
on social media, or shared in chats I missed having a way to quickly stash
|
||||
them for later. This is what motivated me to build Feedlynx.
|
||||
|
||||
You might wonder, though, why I didn't use one of the existing bookmarking
|
||||
or read-later services. Well, the main reason was that I wanted the links to
|
||||
show up in the same place that I was already watching videos: in my RSS reader.
|
||||
Of course, I also really like building little self-hosted tools to solve my own
|
||||
problems.
|
||||
|
||||
### Usage
|
||||
|
||||
The Feedlynx server is implemented in Rust (as is tradition) and provides HTTP
|
||||
endpoints to accept new links and serve the RSS feed.
|
||||
|
||||
On a real computer new links are added via [the Firefox browser extension][feedlynx-ext] I
|
||||
wrote. Click the icon and a moment later a notification is shown indicating the
|
||||
link was added.
|
||||
|
||||
{{ figure(image="posts/2024/announcing-feedlynx/notification.png",
|
||||
link="posts/2024/announcing-feedlynx/notification.png",
|
||||
width=303,
|
||||
alt="TODO",
|
||||
caption="Notification from Firefox extension.") }}
|
||||
|
||||
On my phone, I've set up a workflow using the Shortcuts app that lets me add
|
||||
links directly from the share sheet.. The Shortcut can be installed on iOS,
|
||||
iPadOS, and macOS. It's linked in [the README][Feedlynx].
|
||||
|
||||
When a new link is submitted to the server it fetches the page to try to
|
||||
extract [OpenGraph metadata][OpenGraph] to help fill out the item in the RSS
|
||||
feed. The title of the tab is also submitted by the browser extension as a fall
|
||||
back.
|
||||
|
||||
The fall back is particularly necessary for YouTube since it seems they often
|
||||
[block simple requests for the HTML of the video page][block]. Instead of the
|
||||
video page they return a `200 Ok` response with a "prove you're not a robot
|
||||
page" and a generic title and description.
|
||||
|
||||
Adding new links via `cURL` is also quite simple, should you want to do so from
|
||||
a script:
|
||||
|
||||
curl -d 'url=https://example.com/' \
|
||||
-d 'token=ExampleExampleExampleExample1234' \
|
||||
http://localhost:8001/add
|
||||
|
||||
### Implementation Notes
|
||||
|
||||
Some notes on the implementation:
|
||||
|
||||
- I _tried_ to keep the number of dependencies low and favoured dependencies
|
||||
that had few dependencies of their own.
|
||||
- I vendored code from a few crates since I only needed a small piece
|
||||
of their functionality. These are acknowledged in the README and in
|
||||
the code.
|
||||
- Since this is only intended to serve sporadic requests for one person it
|
||||
doesn't use async Rust. Regular synchronous code is more than enough and
|
||||
avoids the need to pull in a whole async runtime.
|
||||
- The RSS feed is the data store, no need for a DB or anything like that.
|
||||
The file is guarded by a lock.
|
||||
- Adding a feed and fetching the feed requires two different tokens that
|
||||
are read from the environment when the server starts.
|
||||
- The browser extension requests the bare minimum permissions necessary to get
|
||||
the job done. It is not able to see the content of pages.
|
||||
- YouTube links are detected and an `iframe` embed is included in the RSS
|
||||
item to allow watching in your RSS reader.
|
||||
|
||||
### Future Work/Ideas
|
||||
|
||||
My primary use-case was stashing videos to watch later but as I was building
|
||||
Feedlynx it made sense to make it work for any link. It seems like an logical
|
||||
extension to have Feedlynx manage multiple feeds so that you can send videos
|
||||
to one feed and other links to one or more other feeds.
|
||||
|
||||
I only use Firefox so that's the only browser extension that exists so far. It
|
||||
should be straightforward to port the extension to other browsers
|
||||
(contributions welcome). I haven't submitted the extension to
|
||||
addons.mozilla.org yet. I'll do that soon.
|
||||
|
||||
Since Feedlynx manages an RSS feed on disk it could be useful to have a mode
|
||||
where the server is not run, instead relying on an existing HTTP server like
|
||||
`nginx` to serve the feed. A command like `feedlynx add some-url` could be
|
||||
used to add new entries.
|
||||
|
||||
I've been considering offering low-cost paid hosting for tools like Feedlynx
|
||||
and [RSS Please] for folks that don't want to deal with their own server. If
|
||||
you'd be interested in that let me know.
|
||||
|
||||
### Links
|
||||
|
||||
- [Source code][Feedlynx]
|
||||
- [Browser extension code][feedlynx-ext]
|
||||
|
||||
----
|
||||
|
||||
[^1]: I refer to the feed as an RSS feed throughout but it's actually an Atom feed.
|
||||
|
||||
[Feedlynx]: https://github.com/wezm/feedlynx
|
||||
[RSS Please]: https://rsspls.7bit.org/
|
||||
[feedlynx-ext]: https://github.com/wezm/feedlynx-ext
|
||||
[OpenGraph]: https://ogp.me/
|
||||
[block]: https://github.com/iv-org/invidious/issues/4734
|
||||
[releases]: https://github.com/wezm/feedlynx/releases/latest
|
BIN
v2/content/posts/2024/announcing-feedlynx/notification.png
Normal file
BIN
v2/content/posts/2024/announcing-feedlynx/notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -358,7 +358,7 @@ ul.projects {
|
|||
img, svg {
|
||||
width: 32px;
|
||||
vertical-align: middle;
|
||||
margin-right: 0.5em;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
.emoji-img {
|
||||
display: inline-block;
|
||||
|
@ -409,6 +409,10 @@ ul.projects {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.image-left {
|
||||
float: left;
|
||||
margin: 0.5em 1em 1em 0;
|
||||
}
|
||||
.image-right {
|
||||
float: right;
|
||||
margin: 0.5em 0 1em 1em;
|
||||
|
|
12
v2/static/images/feedlynx.svg
Normal file
12
v2/static/images/feedlynx.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 14 KiB |
|
@ -34,6 +34,13 @@
|
|||
<p>A selection of projects I've built or contributed to:</p>
|
||||
|
||||
<ul class="projects">
|
||||
<li>
|
||||
<a href="https://github.com/wezm/feedlynx" class="no-border">
|
||||
<img src="{{ config.base_url }}/images/feedlynx.svg" width="32" alt="">
|
||||
</a>
|
||||
<a href="https://github.com/wezm/feedlynx">Feedlynx</a>
|
||||
<p>Collect links to read or watch later in an RSS feed.</p>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/wezm/rsspls" class="no-border">
|
||||
<span class="emoji-img">📰</span>
|
||||
|
@ -43,17 +50,10 @@
|
|||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/yeslogic/allsorts">
|
||||
<img src="images/allsorts.svg">Allsorts
|
||||
<img src="images/allsorts.svg" width="32" alt="" style="margin-right: 0.5em">Allsorts
|
||||
</a>
|
||||
<p>Font parser, shaping engine, and subsetter implemented in Rust.</p>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://dewpoint.7bit.org/" class="no-border">
|
||||
<span class="emoji-img">💧</span>
|
||||
</a>
|
||||
<a href="https://dewpoint.7bit.org/">Dewpoint</a>
|
||||
<p>View a 7-day dewpoint forecast for a selected location.</p>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/wezm/git-grab" class="no-border">
|
||||
<span class="emoji-img">⤵️</span>
|
||||
|
@ -61,6 +61,13 @@
|
|||
<a href="https://github.com/wezm/git-grab">Git Grab</a>
|
||||
<p>Clone a git repository into a standard location organised by domain and path.</p>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://dewpoint.7bit.org/" class="no-border">
|
||||
<span class="emoji-img">💧</span>
|
||||
</a>
|
||||
<a href="https://dewpoint.7bit.org/">Dewpoint</a>
|
||||
<p>View a 7-day dewpoint forecast for a selected location.</p>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://bitcannon.net/">
|
||||
<svg id="bookmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="#85144b">
|
||||
|
|
1
v2/templates/shortcodes/float_svg.html
Normal file
1
v2/templates/shortcodes/float_svg.html
Normal file
|
@ -0,0 +1 @@
|
|||
<img src="{{ config.base_url }}/{{ image }}" width="{{ width }}" alt="{{ alt }}" class="image-{{ float }}" />
|
Loading…
Reference in a new issue