feedi
feedi 是一个网页订阅阅读器,具有类似 Mastodon 或 Twitter 信息流的极简界面。
特点:
- 简易的本地和自托管环境设置。
- 支持 Mastodon 主页和通知流。
- 移动设备友好的用户界面。
- 可作为渐进式网络应用安装。
- 为 Reddit、Github 和 Goodreads 提供自定义解析器。
- 可定制的 RSS 解析器和临时网页抓取器。
- 智能订阅源排序选项(突出显示不常更新的来源,自动标记为已读)。
- 使用 Mozilla 的阅读模式进行本地文章阅读和预览。
- 支持发送到 Kindle 设备。
关于此项目开发的背景,请参阅这篇博客文章。
文档
- 安装
- 基本使用
- 添加源
- 浏览订阅源
- 阅读文章
- 删除旧文章
- 高级功能
- 从CSV和OPML文件批量导入/导出订阅源
- Mastodon账号设置
- GitHub通知订阅源
- Goodreads主页订阅源
- Reddit订阅源
- Kindle设备支持
- 订阅源解析
- RSS/Atom订阅源
- 自定义订阅源
- 键盘快捷键
- 用户管理
- 使用Docker运行
- 非本地设置
安装
feedi 需要 Python 3.9 或更高版本。如果你还没有安装,可以考虑使用 pyenv 或 asdf。
在本地环境安装 feedi:
git clone https://github.com/facundoolano/feedi.git
cd feedi
make
然后,运行应用:
make run
应用将在 http://localhost:9988/
可用。
或者,参考使用 Docker 运行或非本地设置的说明。
基本用法
添加来源
默认情况下,您的信息流为空。您可以通过多种方式加载内容:
- 点击"+ 添加信息源"按钮或导航至"/feeds/new"手动添加信息源。
- 通过粘贴或拖动网站URL到搜索框来发现网站的RSS源。
- 运行
make feed-load feed-sync
加载此仓库中包含的一组默认网站。 - 从OPML或CSV文件导入一系列信息源。
- 登录Mastodon账号。
首次添加信息源时,应用会获取其最新文章,然后会定期检查新内容(默认每30分钟一次)。
浏览信息流
- 可以将来源放入文件夹,可通过左侧边栏(桌面端)或导航栏菜单(移动端)访问。
- 可以通过右侧边栏(桌面端)或导航栏菜单上的控件调整信息流行为:
- 用户向下滚动时,条目会自动标记为已查看。默认情况下,下次打开应用时会跳过已看过的条目。
- 默认排序将更新频率较低的来源置顶。另外,还有严格的时间顺序排序选项。
阅读文章
与信息流条目交互有多种方式:
- 点击文章标题会在新浏览器标签页中打开原始网站。
- 在某些情况下,如Reddit、Hacker News或Lobste.rs等链接聚合器,会有一个单独的链接用于文章讨论。
- 点击内容或在聚焦条目时按Enter键,将获取并在本地阅读器中显示文章内容。这将是文章的精简版本(移除了一些网站链接、广告和付费墙),由mozilla/readability库提供支持。请注意,要使用此功能,需要在设置项目时安装node 20或更高版本。
- 还可以通过将任意文章的URL拖到搜索框来使用阅读器预览。
删除旧文章
条目会在发布后的一段时间内自动删除(默认为7天)。 置顶和收藏的条目永不删除。此外,所有来源都会保留最少数量的条目(默认为5条),不考虑发布日期。
高级功能
从CSV和OPML文件批量导入/导出订阅源
make feed-load
将从本地feeds.csv
文件加载订阅源。仓库中包含了一个示例文件,以便您可以立即查看一些内容。
还有一个make feed-load-opml
命令,可以从OPML格式的feeds.opml
文件导入RSS订阅源列表。
相应地,还有make feed-dump
和make feed-dump-opml
命令用于从应用程序导出订阅源数据。
Mastodon账户设置
Mastodon集成允许将用户主页feed和通知收件箱都作为订阅源获取。
feedi首先需要通过导航到管理订阅源 > Mastodon登录
或访问/auth/mastodon
URL来获得访问Mastodon账户的授权。填写实例名称并授予访问权限后,feedi将重定向到添加订阅源表单,在那里可以添加Mastodon或Mastodon通知订阅源(或两者都添加)。
GitHub通知订阅源
您可以将GitHub的通知导入feedi。为此,请导航到您的GitHub主页https://github.com/,打开页面HTML源代码并搜索atom feed链接。它应该看起来像这样:
<link rel="alternate" type="application/atom+xml" title="ATOM" href="/facundoolano.private.atom?token=<TOKEN>" />
复制href URL并使用它在feedi中添加新的RSS订阅源。
Goodreads主页订阅源
您可以将Goodreads.com的通知导入feedi。为此,请导航到您的Goodreads主页https://www.goodreads.com/,打开页面HTML源代码并搜索atom feed链接。它应该看起来像这样:
<link href='https://www.goodreads.com/home/index_rss/<ID>?key=<KEY>' rel='alternate' title='Goodreads' type='application/atom+xml'>
复制href URL并使用它在feedi中添加新的RSS订阅源。
Reddit订阅源
Reddit通过在URL后附加.rss
来将公共页面作为RSS订阅源公开,例如https://www.reddit.com/r/selfhosted.rss
或www.reddit.com/user/someuser.rss
。
此外,经过身份验证的用户可以访问私人账户页面的RSS订阅源:首页、已保存的链接、消息收件箱等。可以在这里找到链接。
feedi为Reddit列表消息和链接以及用户收件箱使用特殊用途的订阅源解析器。
Kindle设备支持
该应用程序允许注册Kindle邮箱地址,以便将清理过的文章HTML发送到设备上,比默认的Amazon Send to Kindle Chrome扩展获得更好的效果。这需要设置SMTP凭据以发送电子邮件。 使用基本Gmail账户实现的步骤:
- 注册一个Google电子邮件账户。
- 为该账户开启两步验证。
- 导航到应用密码设置并为应用生成密码(需要激活两步验证才能使用)。
- 在应用配置中添加SMTP设置:
FEEDI_EMAIL = "YOUR.EMAIL@gmail.com"
FEEDI_EMAIL_PASSWORD = "GENERATED APP PASSWORD"
FEEDI_EMAIL_SERVER = "smtp.gmail.com"
FEEDI_EMAIL_PORT = 587
- 导航到
/auth/kindle
或在搜索框中输入"kindle",然后输入您设备的@kindle.com电子邮件地址。 - 转到您的Amazon个人文档设置,并将您的feedi电子邮件地址添加到已批准的文档电子邮件列表中。 完成此设置后,浏览文章时将可以使用"发送到Kindle"命令。
订阅源解析
该应用程序通过定期从不同的订阅源(RSS/Atom、Mastodon嘟文和通知、自定义爬虫)获取项目,并将它们调整为Entry数据库模型,该模型大致匹配我们期望在前端显示的内容。
RSS/Atom订阅源
大多数RSS订阅源应该能被默认解析器正确处理,但有时需要添加自定义功能来清理或扩展数据以获得更好的外观和体验。这可以通过继承feedi.parsers.rss.BaseParser来实现。静态方法is_compatible
确定是否应使用该特定类解析给定的订阅源;parse_*
方法覆盖了前端预期的每个字段的默认逻辑。
例如,这个lobste.rs链接聚合器的解析器被调整为内联显示外部链接提交的摘要,并区分源文章URL和lobste.rs讨论URL:
class LobstersParser(BaseParser):
def is_compatible(_feed_url, feed_data):
return 'lobste.rs' in feed_data['feed'].get('link', '')
def parse_content_short(self, entry):
# 只有外部链接提交才有'Comments'链接
if 'Comments' in entry['summary']:
url = self.parse_content_url(entry)
return (self.fetch_meta(url, 'og:description') or
self.fetch_meta(url, 'description'))
return entry['summary']
def parse_entry_url(self, entry):
# 返回讨论URL,对于外部链接,它与entry['link']不同
if 'Comments' in entry['summary']:
soup = BeautifulSoup(entry['summary'], 'lxml')
return soup.find("a", string="Comments")['href']
return entry['link']
您可以在这个模块中看到几个自定义RSS解析器。
自定义订阅源
除了RSS和Mastodon订阅源外,该应用程序还可以通过自定义解析器获取任意来源的内容。这对于抓取不提供订阅源的网站或直接消费JSON API很有用。
要添加自定义解析器,请继承feedi.parsers.custom.CustomParser。is_compatible
方法确定是否应使用该解析器解析给定的URL。fetch
方法执行实际的条目获取和解析。有关一些示例,请参阅feedi.parsers.custom模块。
一旦实现了解析器,当在Web应用程序中添加带有预期URL的"自定义"类型的新订阅源时,就会使用它。
键盘快捷键
快捷键 | 场景 | 动作 |
---|---|---|
Cmd+k | 聚焦搜索输入框 | |
Enter | 搜索框聚焦 | 提交第一个建议 |
Escape | 搜索框或建议聚焦 | 隐藏建议 |
向下箭头, Ctrl+n | 搜索框或建议聚焦 | 下一个建议 |
向上箭头, Ctrl+n | 建议聚焦 | 上一个建议 |
Enter | 条目聚焦 | 打开条目内容 |
Cmd+Enter, Cmd+左键点击 | 条目聚焦 | 在新标签页打开条目内容 |
Cmd+Shift+Enter, Cmd+Shift+左键点击 | 条目聚焦 | 在新窗口打开条目讨论 |
向下箭头, Tab | 条目聚焦 | 聚焦下一个条目 |
向上箭头, Shift+Tab | 条目聚焦 | 聚焦上一个条目 |
p | 条目聚焦 | 固定条目 |
f | 条目聚焦 | 收藏条目 |
Escape | 查看条目内容 | 返回 |
用户管理
默认的应用程序配置假设是单用户未认证设置,但如果需要安全性,可以启用身份验证,例如将应用程序部署到互联网上或支持多个账户。 要启用用户身份验证:
- 从配置中删除
DEFAULT_AUTH_USER
设置。 - 如果您已经创建了数据库,请使用
make db-reset
重置它。或者,使用make user-del EMAIL=admin@admin.com
删除默认用户。请注意,这也会从数据库中删除与之关联的订阅源和条目。 - 您可以通过运行
make user-add EMAIL=some@email.address
来创建新用户。该命令将提示输入密码。 请注意,前端没有开放的用户注册功能,但如果需要,添加这个功能应该很简单。有关详细信息,请查看auth模块和flask-login文档。
使用Docker运行
从GitHub包获取镜像: docker pull ghcr.io/facundoolano/feedi:latest
创建一个卷以持久化数据库数据: docker volume create feedidb
将默认订阅源加载到默认管理员用户: docker run -v feedidb:/app/instance ghcr.io/facundoolano/feedi flask --app feedi/app.py feed load feeds.csv admin@admin.com docker run -v feedidb:/app/instance ghcr.io/facundoolano/feedi flask --app feedi/app.py feed sync
以开发模式运行: docker run -p 9988:9988 -v feedidb:/app/instance ghcr.io/facundoolano/feedi
要在生产模式下运行,需要一个至少包含密钥的配置文件: echo "SECRET_KEY = '$(python -c 'import secrets; print(secrets.token_hex())')'" >> production.py docker run -p 9988:9988 -e FLASK_ENV=production -v feedidb:/app/instance -v $(pwd)/production.py:/app/feedi/config/production.py ghcr.io/facundoolano/feedi
要启用身份验证,请在生产配置文件中添加DEFAULT_AUTH_USER=None
。
非本地设置
您可以参考Flask文档以获取有关如何将feedi部署到非本地环境的说明。存储库中包含的设置脚本展示了Debian服务器的示例设置。您可以通过ssh远程运行它,如make prod-install SSH=user@server
。