体验Follow与回归TTRSS #
前几个月,赶着Follow Alpha内测的末班车拿到邀请码,恰巧也收到六千多Power的空投,由此动力十足,深度体验了Follow,有一些感受。
- Follow似乎将RSSer孤岛连接起来了,原来还有这么多人在使用RSS。通过列表分享和查看别人的订阅,能发现一些优质订阅源;
- Follow降低了RSS的门槛。现在几乎可以无成本的获取订阅源并阅读,不用担心RSSHub风控(现已上线第三方RSSHub订阅),不用再为阅读器付费,不用费劲部署自托管程序;
- 仅作为一个RSS阅读器,目前的Follow做得并不比其它工具好。网页端的全文获取时常不可用、图片防盗链没解决、更新时间间隔无法自定义等。但是Follow对图片、视频、社交分类浏览做的处理是我用过的最好的。
用了一段时间后,觉得Follow并没有提升我个人的阅读体验(相较TTRSS+FeedMe):我自制的订阅源反而受到了抓取间隔的限制;部分自制订阅源没有发布在公网上,仅由内网Nginx托管;多媒体仍然习惯在来源网站/APP观看,即便Follow已经做得不错了,但似乎永远不可能媲美来源网站;Follow似乎有被墙的趋势,直连有时不太顺畅。
且让Follow再成长一段时间,我先回归TTRSS,借此对其进行升级和打理。
整理订阅源 #
精简订阅 #
由于年末的忙碌和体验了一段时间Follow,TTRSS里的未读文章数已经积压到五位数了。有些订阅源居然给我“灌水”了几百篇。大量同质和我不感兴趣的内容淹没了对我有价值的文章,我的信息获取效率实质上降低了。
- 开刀操作当然是取消订阅,根据TTRSS历史收藏量数据,我排除了一些可能一直是RSS圈的热门订阅,但我却很少阅读的频道。我取消了
reddit
、果壳
、丁香园
、知乎日报
、掘金
、机核
等十几个订阅,每日的新增文章居然下降了快一半; - 下一步是取消同质化内容订阅。典型的就是中文AI三大顶会:
机器之心
、量子位
与新智元
(雾)。至于取消哪些,后面再观察比较一下;
做分类 #
根据订阅源的类别,重新优化了一下分类。
- 分类很重要:不同类别的内容混杂在一起,浏览效率非常低,按类别浏览可以让大脑预设好严肃或放松状态;
- 分类也可以让过滤更有效(详见"过滤器"章节);
- 我做了9个大类,16个子类,比如
科技(大类)
,视听(大类)
,学术 | 机器人
,学术 | 综合
,学术 | 力学
小类。
过滤器 #
在之前的TTRSS设置中,我没有清理旧文章,RSS的优势体现出来了:我很方便的按订阅源查看历史文章,为关键词的设置提供了帮助。TTRSS的过滤器支持正则表达式。
- 以
自动标记已读
操作为例,我订阅了一些材料方向的频道,但材料领域太大了,我是个外行,且感兴趣的只是一小撮,因此,在学术 | 材料
分类内作过滤催化|单晶|原子|太阳能|环氧|蛋白质|量子|铁电|隔膜|高熵|电池|陶瓷|烯|光谱|肽|腐蚀|硅基|自旋|基因|硅酸|拉曼|RNA|DNA|芯片|手性|雷达|合金|电磁
,同时在学术 | 综合
内作过滤催化|化学|祝贺|细胞|纳米|微信订阅|肿瘤|分子|喜报|癌|基因|免疫|淋巴|病毒
。 - 同理,用过滤器过滤出若干
预定义标签
,如人形机器人
,在全部订阅源中过滤关键词具身|人形机器人|仿人机器人|类人机器人|通用机器人|双足.*机器
。
做完以上精简操作后,顿感轻松,每日的新文章数少于一百五,由预定义标签
过滤而来的也不过十来篇。有时忙起来而积压的文章,不读也罢,一键标记已读
!
升级TTRSS #
我的TTRSS已经一年多没更新了,借此做一下升级,也发现一些小问题。我使用的是HenryQW@Github编制的Awesome-TTRSS,他添加了一些额外插件,以提升用户体验。关于TTRSS的更新,[[https://ttrss.henry.wang/zh/#数据库更新或迁移]]写道:在升级时,请勿跳过多个大版本,例如直接从 13.x 升级到 16.x 是不支援的,并可能导致数据丢失。
我现在使用的Postgres版本是13,好吧,那就老老实实按步骤升级吧,由于Postgres加密方式由Version13中的md5
改为Version14及以上的SCRAM-SHA-256
,因此,Postgres升级13->14时,多加一步重置密码,文档里没有写明这一点。正确的步骤如下:
- 备份数据库;
mkdir backup
cd backup/
sudo cp -r /var/lib/docker/volumes/ttrss_feed-icons ./
sudo cp -r /var/lib/docker/volumes/ttrss_postgres ./
sudo cp -r /var/lib/docker/volumes/ttrss_themes ./
- 导出数据库
docker exec postgres pg_dumpall -c -U 数据库用户名 > export.sql
,wangqiru/ttrss
默认数据库用户名为postgres
; - 停止容器
docker compose down
; - 拉取新镜像
docker compose pull
; - 启动容器
docker compose up -d
; - 导入数据库
cat export.sql | docker exec -i postgres psql -U 数据库用户名
; - 由于postgres加密方式由Version13中的
md5
改为Version14及以上的SCRAM-SHA-256
,因此,postgres升级13->14时,多加以下一步重置密码:
docker exec -i postgres psql -U 数据库用户名
\password
# Enter the password at the prompt!
# ctrl+D to exit
- 重复以上2-6步,完成postgres14->15,15->16的升级。
此外,几点说明:
- 我原来只用docker创建了卷,没有在
docker-compose.yml
所在文件夹内设置映射路径,现将/var/lib/docker/volumes/ttrss_postgres
迁移至docker-compose.yml
文件夹内;
# docker-compose.yml
services:
database.postgres:
volumes:
- ./postgres:/var/lib/postgresql/data
#volumes:
# postgres:
- 升级完发现订阅源的自定义图标消失了,翻看了
wangqiru/ttrss
容器内的文件树,发现图标文件存储在了/var/www/cache/feed-icons
内,而不是wangqiru/ttrss
仓库里写的/var/www/feed-icons
;主题文件则是保存在了/var/www/themes
和/var/www/themes.local
里,而不是/var/www/ttrss/themes
。
自定义TTRSS主题 #
用了多年的浅色主题,有点腻了,换个暗色主题。自带的feedly-sepia_night
也不那么好看,根据CrackTC/ttrss-nord-theme作了一点细节修改,在偏好设置
-主题
-自定义
弹框内填入以下CSS片段即可:
::selection {
color: #2e3440;
background: #88c0d0;
}
body.flat #main {
color: #eceff4;
background-color: #2e3440 !important;
}
body.flat {
background-color: #2e3440 !important;
}
body.flat .dijitContentPane,
body.flat .dijitDialog,
body.flat .dijitTab.dijitChecked,
body.flat .dijitTabPaneWrapper {
background-color: #2e3440;
}
body.flat .dijitAccordionInnerContainer.dijitSelected .dijitAccordionTitle {
background-color: #3b4252;
}
.flat .dijitTab i.material-icons,
.flat .dijitAccordionInnerContainer:not(.dijitSelected) i.material-icons {
color: #88c0d0;
}
body.flat a:any-link:any-link {
color: #88c0d0;
text-decoration: none;
border-radius: 0.2em;
padding: 0 0.2em;
transition: background-color 0.2s ease-in-out;
}
body.flat a:any-link:any-link:hover {
background-color: #4c566aaa;
}
body.flat a:any-link:any-link:hover:has(>img) {
background-color: transparent;
}
body.flat a:any-link:any-link:any-link>img {
filter: brightness(80%);
transition: all 0.3s ease-in-out;
}
body.flat a:any-link:any-link:any-link>img:hover {
filter: brightness(100%);
}
body.flat .Error:not(.dijitTreeRowSelected) .dijitTreeLabel,
body.flat #feeds-holder .Error .dijitTreeLabel {
color: #bf616a !important;
}
#content-wrap#content-wrap .dijitContentPane {
background-color: #2e3440
}
#toolbar-frame#toolbar-frame #toolbar {
background-color: #2e3440;
}
#feeds-holder#feeds-holder {
background: #242933;
margin-left: -25px;
padding-left: 25px;
border-top-right-radius: 19px;
border-bottom-right-radius: 19px;
box-shadow: rgb(15, 17, 21, 0.2) 2px 0px 6px 0px;
z-index: 3;
}
#headlines-frame#headlines-frame#headlines-frame>.hl {
background-color: #2e3440;
box-shadow: rgb(15, 17, 21, 0.2) 2px 0px 6px 0px;
}
#headlines-frame#headlines-frame#headlines-frame>.hl:where(.Unread) {
background-color: #242933;
box-shadow: rgb(15, 17, 21, 0.2) 2px 0px 6px 0px;
}
#headlines-frame>.hl:first-child {
border-top-left-radius: 15px;
border-top-right-radius: 15px;
}
#headlines-frame>.hl:nth-last-child(2) {
border-bottom-left-radius: 15px;
border-bottom-right-radius: 15px;
}
#headlines-frame#headlines-frame>.hl>* {
transition: background-color 0.2s ease-in-out;
}
#headlines-frame#headlines-frame>.hl:hover>* {
background-color: #3b4252;
}
#headlines-frame>.hl:first-child>* {
border-top-left-radius: 15px;
border-top-right-radius: 15px;
}
#headlines-frame>.hl:nth-last-child(2)>* {
border-bottom-left-radius: 15px;
border-bottom-right-radius: 15px;
}
#headlines-frame#headlines-frame>.hl>.title {
padding-left: 5px;
padding-right: 5px;
}
#headlines-frame#headlines-frame>.hl>.left {
padding-right: 10px;
}
#headlines-frame#headlines-frame>.hl>.feed.vfeedMenuAttach {
padding-left: 10px;
max-width: 120px;
text-overflow: ellipsis;
}
#headlines-frame#headlines-frame>.hl>.right>.icon-feed {
left: 10px;
}
#content-wrap#content-wrap#content-wrap .hl:is(.active, .Selected) {
z-index: 2;
color: #88c0d0;
border-color: #88c0d0;
background-color: #3b4252 !important;
}
#content-wrap#content-wrap .hl.marked {
z-index: 2;
color: #8fbcbb;
border-color: #8fbcbb;
border-width: 2px;
background-color: #2e3440 !important;
}
#headlines-frame#headlines-frame .hl.hl .title {
color: #b4b4b4; /*#d8dee9*/
}
#headlines-frame#headlines-frame .hl.hl.Unread .title {
color: #e5e9f0;
}
#headlines-frame>.hl .feed.vfeedMenuAttach a {
box-shadow: rgb(15, 17, 21, 0.2) 2px 0px 6px 0px;
}
#content-wrap#content-wrap .icon-no-feed {
color: #d8dee9;
}
#content-wrap#content-wrap .post .header {
background-color: #3b4252;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
margin-right: 6px;
border-width: 0px;
box-shadow: rgb(15, 17, 21, 0.2) 2px 0px 6px 0px;
}
#content-wrap#content-wrap .content img,
#content-wrap#content-wrap .content video {
border-radius: 15px !important;
box-shadow: rgb(15, 17, 21, 0.2) 2px 0px 6px 0px;
}
#headlines-frame#headlines-frame#headlines-frame>.hl .dijitCheckBox.dijitChecked {
color: #ebcb8b;
}
body.ttrss_main .hl.marked .left i.marked-pic {
color: #a3be8c;
}
body.ttrss_main .hl.published .left i.pub-pic {
color: #d08770;
}
#content-wrap#content-wrap .icon-score {
color: #b48ead;
}
body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Feed .loadingNode {
filter: invert(50%) brightness(100%) !important;
}
#feeds-holder#feeds-holder #feedTree .dijitTreeRow .loadingNode {
filter: none;
}
#content-wrap#content-wrap :is(.content, .content-inner) {
color: #eceff4;
}
body.flat :is(.dijitButton, .dijitDropDownButton) .dijitButtonNode {
background-color: #3b4252;
}
body.flat :is(.dijitButton, .dijitCheckBox, .dijitComboButton, .dijitDropDownButton, .dijitMultiSelect, .dijitSelect, .dijitTextBox):is(.dijitActive, .dijitFocused, :focus-within):not(.dijitDisabled) {
box-shadow: 0 0 0 2px #88c0d0;
}
body.flat .dijitCheckBox,
body.flat .dijitComboBoxMenu,
body.flat .dijitComboBoxMenuPopup,
body.flat .dijitMenu,
body.flat .dijitMenuPopup,
body.flat .dijitMultiSelect,
body.flat .dijitSelect,
body.flat .dijitSelect .dijitButtonNode,
body.flat .dijitSelectMenu,
body.flat .dijitTextBox.dijitTextBox.dijitTextBox {
background-color: #3b4252;
}
.flat .dijitMenuItemHover td {
background-color: #4c566a;
}
.flat .dijitMenuItemSelected {
background-color: #88c0d0
}
.flat .dijitMenuItemSelected td {
background-color: #88c0d0
}
body.flat.ttrss_index .dijitPopup[aria-label=dijit_Menu_0] .dijitMenuItemDisabled td {
box-shadow: inset 0 1px #4c566a;
}
.flat .dijitMenuPopup .dijitMenu {
border-color: #88c0d0;
border-radius: 10px;
}
body.flat.ttrss_index .dijitPopup {
border-radius: 10px;
}
body.ttrss_main #headlines-frame #headlines-spacer a {
border-radius: 15px;
}
body.ttrss_main #headlines-frame #headlines-spacer a:hover {
color: #88c0d0;
}
#toolbar-frame#toolbar-frame #toolbar #selected_prompt {
color: #d8dee9;
}
body.flat .dijitDialog {
background-color: #3b4252;
border: 1px solid #88c0d0;
border-radius: 15px;
box-shadow: 0 7px 3px -4px rgba(0, 0, 0, .3), 0 8px 8px rgba(0, 0, 0, .2);
}
body.flat :is(.dijitCheckBox, .dijitMultiSelect, .dijitSelect, .dijitTextBox):is(.dijitActive, .dijitFocused, .dijitHover):not(.dijitDisabled) {
border-color: #81A1C1;
}
.dijitReset {
color: #d8dee9;
}
.flat .dijitTabContainerTop-tabs .dijitTabChecked:before {
height: 0;
}
body.flat .alt-primary:is(.dijitButton, .dijitComboButton, .dijitDropDownButton, .dijitToggleButton) .dijitButtonNode {
background-color: #88c0d0;
}
body.flat .alt-primary:is(.dijitButton, .dijitComboButton, .dijitDropDownButton, .dijitToggleButton) .dijitButtonNode * {
color: #2e3440 !important;
}
body.flat .alt-success:is(.dijitButton, .dijitComboButton, .dijitDropDownButton, .dijitToggleButton) .dijitButtonNode * {
color: #2e3440 !important;
}
body.flat .alt-primary:not(.dijitComboButton):is(.dijitFocused, .dijitHover):is(.dijitButton, .dijitComboButton, .dijitDropDownButton, .dijitToggleButton) .dijitButtonNode,
body.flat .alt-primary.dijitComboButton :is(.dijitButtonNodeHover, .dijitDownArrowButtonFocused, .dijitDownArrowButtonHover),
body.flat .alt-primary.dijitComboButton .dijitButtonNode:has(.dijitButtonContentsFocused) {
background-color: #88c0d0;
}
body.flat .alt-danger:is(.dijitButton, .dijitComboButton, .dijitDropDownButton, .dijitToggleButton) .dijitButtonNode {
background-color: #bf616a;
}
body.flat .alt-success:is(.dijitButton, .dijitComboButton, .dijitDropDownButton, .dijitToggleButton) .dijitButtonNode {
background-color: #a3be8c;
}
body.flat .alt-success:not(.dijitComboButton):is(.dijitFocused, .dijitHover):is(.dijitButton, .dijitComboButton, .dijitDropDownButton, .dijitToggleButton) .dijitButtonNode,
body.flat .alt-success.dijitComboButton :is(.dijitButtonNodeHover, .dijitDownArrowButtonFocused, .dijitDownArrowButtonHover),
body.flat .alt-success.dijitComboButton .dijitButtonNode:has(.dijitButtonContentsFocused) {
background-color: #a3be8c;
}
body.flat .dijitAccordionInnerContainer,
body.flat label.dijitButton.dijitButton,
body.flat :is(.dijitButton, .dijitDropDownButton) .dijitButtonNode,
body.flat :is(.dijitButton, .dijitDropDownButton).alt-info .dijitButtonNode,
body.flat .dijitTextBox :is(.dijitDownArrowButton, .dijitUpArrowButton) {
background-color: #3b4252;
}
body.flat .alt-info.alt-info:is(.dijitFocused, .dijitHover) .dijitButtonNode {
color: #88c0d0 !important;
}
.flat .dijitCheckBox.dijitCheckBoxChecked {
background-color: #a3be8c;
border-color: #d8dee9;
}
body.flat.flat.flat .text-info {
color: #88c0d0;
}
body.flat .dijitAccordionInnerContainer.dijitHover,
body.flat label.dijitButton.dijitButton:is(:hover, :focus-within),
body.flat :is(.dijitButton, .dijitDropDownButton):where(.dijitFocused, .dijitHover) .dijitButtonNode,
body.flat :is(.dijitButton, .dijitDropDownButton).alt-info:where(.dijitFocused, .dijitHover) .dijitButtonNode,
body.flat .dijitTextBox :is(.dijitDownArrowButtonHover, .dijitUpArrowButtonHover),
body.flat .dijitToolbar .dijitHover:not(.dijitSelect) .dijitButtonNode {
background-color: #4c566a;
}
body.flat :is(.dijitButton, .dijitDropDownButton):where(.dijitActive) .dijitButtonNode,
body.flat .dijitToolbar .dijitActive:not(.dijitSelect) .dijitButtonNode {
background-color: #4c566a;
}
body.flat .alt-warning:is(.dijitButton, .dijitComboButton, .dijitDropDownButton, .dijitToggleButton) .dijitButtonNode {
background-color: #d08770;
}
body.flat .alt-warning:not(.dijitComboButton):is(.dijitFocused, .dijitHover):is(.dijitButton, .dijitComboButton, .dijitDropDownButton, .dijitToggleButton) .dijitButtonNode,
body.flat .alt-warning.dijitComboButton :is(.dijitButtonNodeHover, .dijitDownArrowButtonFocused, .dijitDownArrowButtonHover),
body.flat .alt-warning.dijitComboButton .dijitButtonNode:has(.dijitButtonContentsFocused) {
background-color: #d08770;
}
body.flat .alt-warning:not(.dijitComboButton).dijitActive:is(.dijitButton, .dijitComboButton, .dijitDropDownButton, .dijitToggleButton) .dijitButtonNode,
body.flat .alt-warning.dijitComboButton :is(.dijitButtonNodeActive.dijitButtonNodeActive, .dijitDownArrowButtonActive) {
background-color: #d08770;
}
#pref-tabs_tablist>div.dijitTabListWrapper.dijitTabContainerTopNone.dijitAlignCenter>div>div.dijitTabInner.dijitTabContent.dijitTab {
border-top-right-radius: 7px;
border-top-left-radius: 7px;
}
.flat .dijitTabContainerLeft-tabs .dijitTabChecked:before {
width: 0px;
}
.flat .dijitTabContainerLeft-tabs * {
border-top-left-radius: 7px;
border-bottom-left-radius: 7px;
}
body.flat.flat.flat .alert-info {
color: #88c0d0;
background-color: #3b4252;
border-color: #88c0d0;
}
.flat .dijitTreeExpando {
color: #d8dee9;
}
body.ttrss_prefs #header>a:before {
color: #88c0d0;
}
body.flat #pref_feeds_errors_btn,
body.flat .dijitValidationTextBoxError .dijitValidationContainer,
body.flat .exception-contents h3,
body.flat .Error:not(.dijitTreeRowSelected) .dijitTreeLabel,
body.flat #feeds-holder .Error .dijitTreeLabel,
body.flat .icon-error,
body.flat .net-alert {
color: #bf616a !important;
}
body.flat #pref_feeds_errors_btn,
body.flat .dijitValidationTextBoxError .dijitValidationContainer,
body.flat .exception-contents h3,
body.flat .Error:not(.dijitTreeRowSelected) .dijitTreeLabel,
body.flat #feeds-holder .Error .dijitTreeLabel,
body.flat .icon-error,
body.flat .net-alert {
color: #bf616a !important
}
body.ttrss_main .dijitTooltipContents {
color: #2e3440;
background: #88c0d0;
}
body.ttrss_main .dijitTooltipRight .dijitTooltipConnector {
border-right-color: #88c0d0;
}
body.flat.ttrss_main .dijitDialog .panel {
background-color: #3b4252;
}
body.flat.ttrss_main code {
color: #88c0d0 !important;
}
body.flat.ttrss_main #overlay {
background: #2e3440 !important;
}
body.flat .alt-primary.dijitComboButton :is(.dijitButtonNodeActive.dijitButtonNodeActive, .dijitDownArrowButtonActive) {
background-color: #88c0d0;
}
.flat .dijitProgressBarTile {
background: #88c0d0 !important;
}
#overlay #overlay_inner {
color: #eceff4;
}
body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode {
font-size: 12px;
}
body.flat .dijitTreeLabel,
body.flat #feeds-holder .dijitTreeLabel {
color: #e5e9f0 !important;
}