利用 Koel 搭建在线音乐流

Koel 是著名的个人音乐在线播放与电台程序。

Prerequisite

  • 硬件要求:一台最低内存为 1G 的服务器
  • 系统:Ubuntu20.04 LTS,或其他任何 Linux 发行版
  • 数据库:MySQL,MariaDB,PostgreSQL,SQLite
  • 一个域名

基础架构

Koel 后端使用 Laravel PHP 框架,前端使用 Vue,还使用了 SASS 的 CSS 框架。数据库使用 MariaDB(MySQL)。

Docker 安装

使用 Docker compose:

version: '3'

services:
  koel:
    container_name: koel
    image: hyzual/koel
    restart: always
    ports:
      - 8081:80
    environment:
      - DB_CONNECTION=mysql
      - DB_HOST=${DB_HOST}
      - DB_USERNAME=${DB_USERNAME}
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_DATABASE=${DB_DATABASE}
    volumes:
      - ${MUSIC_PATH}:/music:ro
      - ${COVERS_PATH}:/var/www/html/public/img/covers
      - ${SEARCH_INDEX_PATH}:/var/www/html/storage/search-indexes

安装完成后进行初始化:

docker exec -it <container_name_for_koel> bash
php artisan koel:init --no-assets

从 v5.1.0 开始 Koel 不会在安装的时候去设置 admin 账户,会使用一个默认的用户名:

email: admin@koel.dev
password: KoelIsCool

可以通过界面重置密码,或者执行命令:

docker exec -it <container_name_for_koel> php artisan koel:admin:change-password

当前的 Koel 可识别这些音频扩展:.mp3,.ogg,.m4a(实验)和 .flac

快捷键

也有几个快捷键呀:

  • F:移动到搜索框
  • Enter:播放一首歌曲。如果有多首歌曲被选中,Enter 将它们添加到播放队列的底部,Shift+Enter 将它们排到顶部。在组合中加入 Cmd 或 Ctrl,可以立即播放第一首被选中的歌曲。
  • Space:切换播放/停止
  • J:播放队列中的下一首歌曲
  • K:播放队列中的上一首歌曲
  • Ctrl/Cmd+A:选择当前视图中的所有歌曲
  • Delete:从当前队列/播放列表中删除所选歌曲

reference


2022-03-08 self-hosted , navidrome , music-server , music-library , music-player

macOS 下启动器 Raycast 简单使用

在逛 Twitter 的时候看到有人分享了一款 Launcher,叫做 [[Raycast]],因为平时一直使用 [[Alfred]] 倒是没有引起我的太多注意,但今天又看到了一次,所以就想好好来了解一下这个新出来的启动器。

在 macOS 上其实有非常多的启动器,包括了系统默认的 [[Spotlight]],以及很多第三方应用,包括了 [[Alfred]], [[LaunchBar]] 在内的无数优质应用。这些应用有着各自的特点。但最重要的一点就是提高我们打开应用的速度,以及在各个小细节方面提高易用程度,比如我最喜欢的 Alfred 的一个贴心功能就是历史粘贴板,在 Linux 上我一直都是用的 [[fcitx]] 默认的快捷键 Ctrl+;,所以在 macOS 也用一样的快捷键。

安装

安装:

brew install --cask raycast

特性

  • 最基本的功能,启动 macOS 上的应用,搜索文件
  • 历史粘贴板
  • 搜索打开书签
  • 集成第三方应用
    • 在 GitHub,Jira 等等中创建、搜索、关闭 issues
    • 批准、合并和关闭 GitHub Pull Request
    • 调用 Zoom 会议
  • 支持快捷键设置日程、待办事项
  • 支持扩展脚本

使用

Auto-expand Snippet

写过代码的人应该对 Snippet 非常熟悉,我们可以定义一些常用的代码块,然后通过简单的几个字母来快速补充一段内容。

在 Raycast 中,可以通过输入 Create snippet 来创建 Snippet,在弹出的设置窗口中输入想要设置的内容。

raycast-snippet-create

注意下面的 Keyword, 输入 Keyword 的内容就是唤起整个 Snippet 的快捷键。

比如当我输入 :blog 的时候就会自动补全成https://blog.einverne.info

如果有读者记得的话,我在不就之前介绍过一款跨平台的 Text Expander —- Espanso,也能够做到相同的事情。有兴趣可以查看espanso:Rust 编写的跨平台开源文本扩展工具

Clipboard History

在 Raycast 中输入 Clipboard History 可以打开粘贴板历史。

这里值得注意的是 Raycast 所有数据都是本地加密存储,所以可以不用担心粘贴板的隐私问题。

同样,在应用设置中,可以设置快捷键,我就设置成和 Linux 下一致的 Ctrl+;

raycast clipboard history hotkey

在 Raycast 中输入 f 可以看到 File Search,授权 Raycast 搜索本地文件之后,就可以直接在 Raycast 中根据文件名一键搜索到。

Define Word

在 Raycast 中输入 dw 就可以看到 Define Word 的选项,这个可以用来查单词。

Switch Windows

在 Raycast 的扩展中有一个 Switch Windows 的插件,可以设置一个快捷键,比如 Option + Tab,这样就可以快速调用出窗口切换功能,直接输入关键字 Enter,就能切换窗口,再不用 Cmd + Tab 来换切换了。当然可以根据自己的需要设置成任意的快捷键。

Search Screenshot

Raycast 支持直接搜索图片中的内容,还可以选中图片之后使用,cmd + Shift + a 使用 Cleanshot 对图片标注。

Store

输入 Store 可以打开 Raycast 的商店,然后可以在其中安装、启用非常多的三方插件。

目前Raycast已经接入了 GitHub、Jira、G Suite、Linear、Asana、zoom,Notion,Things,Raindrop,Todolist 等服务,能够快速完成特定操作。

Browser Bookmarks

首先在 Store 中安装 Browser Bookmarks

raycast-store-install-bookmarks

然后在设置中启用 Chrome:

raycast browser bookmarks

注意选择正确的 Profile。

然后就可以在 Raycast 中输入 Browser Bookmark 来搜索 Chrome 的书签了。

browser-bookmarks-search

不过需要注意的是,只有书签存在中文、英文才能被搜索出来,如果书签没有描述,或者你忘记了域名,那么也是无可奈何的。

悬浮标签 Floating Notes

在 Raycast 中输入 Floating Notes 之后就会在桌面最顶层开启一个悬浮便签,便签可以移动位置,但是永远在所有窗口的最上面。非常方便用来记录快速的想法,待办事项等等。

raycast-floating-notes

在输入 Float 之后,有两个选项:

  • Toggle Floating Notes Focus,这个选项会将光标移动到便签中
  • Toggle Floating Notes Windows 会显示隐藏便签窗口

日程管理和待办事项

Raycast 可以直接连接 Calendar,因为我的 macOS 上的日历同步了 Google Calendar 的数据,所以可以直接看到我的日程。

在 Raycast 中输入 My Schedule 即可。第一次可能需要授权。

同样可以输入 reminder 来查看待办事项,或者根据提示来创建待办事项。

系统设置

比如输入 volume 可以调整音量。

其他比如计算器、汇率转换、时间查询等等基础功能就不再展开。

如果有时间会再讲讲如何在 Raycast 中执行自定义脚本,或者如何编写自己的扩展。 Raycast 支持的脚本语言,Bash、Swift、AppleScript。


2022-03-01 launcher , alfred , macos , mac-application

电子邮件是如何工作的

还记得之前听的一个播客,里面提到基于协议的应用一般不会轻易地被历史淘汰,想想基于 HTTP 协议的 Web 浏览器,基于 SMTP 的电子邮件,基于 [[BitTorrent]] 协议的文件交换协议,在过去的几十年里,基于 HTTP 上层的网站,以及技术更新换代了好几波,但底层的协议依然还是 HTTP(HTTPS),而倒闭的电子邮箱服务提供商也不在少数。但如今互联网依然还依赖 HTTP 协议,电子邮箱虽然没有那么流行但也是基础服务之一。

过去很多年里面,我陆陆续续一直在寻找一个完美代替 Gmail 的存在,我的代办事项中一直存在一条叫做如何自建邮箱服务的 TODO,我陆陆续续体验过很多新出现的加密邮箱服务(Proton Mail,Skiff),但也还是一直用着 Gmail。陆陆续续把很多数据从 Google 的服务中迁移出来,但唯独摆脱不了 Gmail。但是最近体验了一些自建的邮箱服务之后,感觉自己可以现在再来尝试一下自建邮件服务器了,只需要花费很少的钱去购买一个属于自己的域名就可以拥有无限制的邮箱。

过去我曾经体验过的自建邮箱服务器:

  • [[Poste]] 一个单一 Docker 镜像的邮件服务器,存在免费版本和收费版本
  • [[Mailu]],一个 Python 编写的邮件服务器,可以使用 Docker 部署
  • [[mailcow]],同样是一个可以使用 Docker 部署的邮件服务器
  • [[Mail-in-a-Box]],一个非常方便部署的邮箱服务器
  • [[postal]],一个使用 Rust 实现的邮件服务器
  • [[Maddy]] 一个使用 Go 语言实现的邮件服务器,没有 Webmail
  • [[Salmon]] Python 实现的邮件服务器

还有一些比如 [[iRedMail]]、[[modoboa]]、[[zimbra]] 等等的服务还没有仔细看,但都能实现邮件服务器。

在尝试的过程中觉得自己还需要补习一些基本的知识,因为在自建邮箱服务的时候不可避免的需要配置多个 DNS 记录,包括了 [[MX 记录]],[[SPF]],[[DMARC]] 等等。如果理解了邮件的发送过程,会对这些有更多的理解。所以下面就再学习记录一下邮件发送过程。

邮件服务器发送与接受邮件

假设用户 a@gmail.com 发送一封邮件到 b@qq.com,会执行如下的流程。

Webmail

为了简化理解,下面的所有操作都是通过网页版的 Web 应用触发。

当我们在 Gmail 网页上撰写一封邮件,并点击发送按钮之后。Gmail 会用自己的内部协议链接 Gmail 的 Outgoing SMTP 邮件服务器。

Outgoing SMTP 验证用户权限,然后将邮件以 MIME 格式发送到发送队列中。

查询 MX 记录

Gmail SMTP 服务器会通过 DNS 查询到域名 qq.com MX 记录(dig MX qq.com),找到邮件服务器的 IP 所在。

在 Linux 下也可以通过 dig mx qq.com 来查询到。这一步在对应到自建的邮件服务器的时候,就是通过配置 DNS 的 MX 记录来实现的。

一般情况下会配置一个 A 记录 mx.example.com 指向服务器的 IP 地址。然后再配置一个 [[MX 记录]],@ 全部域名的 MX 请求全部转发给 mx.example.com

MX 这里指的是 Mail Exchanger。

SMTP 发送

当 Gmail 的服务器找到 QQ 邮箱的 IP 地址之后,邮件就会通过 SMTP 协议连接服务器的连接,尝试发送给 QQ 的服务器。

为了简化理解,SMTP 传输的时候就直接声明,我 a@gmail.com ,我要发送邮件到 b@qq.com ,内容是某某某。

这中间会发现不存在任何验证发送方身份的过程,这也就意味着任何人都可以伪装一个任意的发送邮箱以一个伪装的邮箱发送邮件。SMTP 最早是建立在相互信任的基础之上的,所以也给后面的恶意使用留下了一些漏洞,为了修复这个漏洞发明了 [[SPF]]。具体见下文。

接收

QQ 邮箱的服务器接收到 Gmail 的邮件之后,再根据用户名决定发给具体谁的邮箱。

SMTP

[[SMTP]] 是 Simple Mail Transfer Protocol 的缩写,从协议名称上也能看出来是一个邮件传输协议。

SPF

上文提到过 SMTP 协议发送邮件的过程中没有验证发送方,这也就意味着发信方可以任意指定发件人邮箱地址,这会存在一些安全问题。

具体来说,本来我的 Gmail 邮箱是 a@gmail.com,假如有不法分子,就可以利用这个漏洞,伪装成自己是 a@gmail.com 给别人发送邮件。

[[SPF]] 的目的就是为了防止伪造发信人。

SPF 的原理

SPF 的实现原理非常简单,就是通过添加一条 DNS 记录。

如果邮件服务器收到一封来自主机 1.1.1.1 的邮件,并且发件人是 a@gmai.com,为了确认发件人,邮件服务器就会去查询 gmail.com 的 SPF 记录。如果域名设置了 SPF 记录,允许 1.1.1.1 的 IP 地址发送邮件,那么收件的邮件服务器就会认为邮件是合法的,否则就会退信。

有了 SPF 记录之后,如果有人想要伪装成 a@gmail.com 他既不能修改 gmail.com 的 DNS 解析,也无法伪造 IP 地址,就有效的防止了伪装。

SPF 的语法

在自建邮件服务器的时候,经常会让我们设置一个 TXT 记录,配置值为 v=spf1 mx ~all,这表示的意思是允许当前域名的 MX 记录对应的 IP 地址。

下面再举个非常常见的例子:

v=spf1 a mx ip4:173.10.10.10 -all

表示允许当前域名配置的 A 记录,MX 记录的 IP 地址,以及一个额外的 IP 进行发信。

SPF 存在的问题

通过上面的描述我们知道通过 SPF 机制可以有效地规避了发送邮件方伪造发件人的问题。但实际使用的时候,如果你使用多个邮箱,然后设置了其中 c@163.com 邮箱自动转发到 a@gmail.com 中。

那么这个时候如果 b@qq.com QQ 邮箱发送了一封邮件到 c@163.com 邮箱,163 邮箱原封不动地将邮件转发到 Gmail 邮箱,这个时候发件人是 b@qq.com,但是 Gmail 回去查询 qq.com 的 SPF 记录,但会发现并不包含 163 邮箱的 IP 地址,会误判转发的邮件。

所以又诞生了 DKIM

DKIM

[[DKIM]] 是 DomainKeys Identified Mail 的缩写,允许发送者通过在邮件的 header 中包含一段数字签名来验证邮件。DKIM 使用公私密钥来确保邮件内容是从授信的邮件服务器发送的。

还是利用上面的例子,因为我们把所有发送到 163 邮箱的邮件都转发到了 Gmail 邮箱,所以来自 QQ 邮箱的邮件在验证 SPF 时会失败。

那么在 DKIM 中,发送邮件的服务器,比如 QQ 邮箱,会使用公私钥对邮件内容进行签名,并将签名和邮件内容一起发送。当 Gmail 收到从 163 邮箱转发过来的 QQ 邮箱邮件的时候,就会去查询 qq.com 的 DNS 记录,拿到公钥。然后使用公钥和签名来验证邮件内容。如果验签不通过,则将邮件判定为伪造。

这里就需要我们再配置一个 DKIM 的 DNS 记录。TXT,键值是页面中的内容。

在 Mailu 的后台可以看到 DKIM 的设置

mailu dkim

DMARC

经过了 SPF 和 DKIM 的保证是不是就可以完美的发送接收邮件了,其实并不能,我们通过邮件后台来看一下邮件的原始文本。

MIME-Version: 1.0
Return-Path: xxx@fake.com
DKIM-Signature: d=fake.com,b=adceabkekd12
Date: Tue, 22 Mar 2022 06:37:58 +0000
Content-Type: multipart/alternative;
 boundary="--=_RainLoop_587_997816661.1647931078"
From: admin@a.com
Message-ID: <a67d96a38592cdad46cca89e98dda26d@techfm.club>
Subject: Seems it works
To: "Somebody" <a@gmail.com>


----=_RainLoop_587_997816661.1647931078
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

~~

----=_RainLoop_587_997816661.1647931078
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE html><html><head><meta http-equiv=3D"Content-Type" content=3D"t=
ext/html; charset=3Dutf-8" /></head><body><div data-html-editor-font-wrap=
per=3D"true" style=3D"font-family: arial, sans-serif; font-size: 13px;"><=
br>~~<signature></signature></div></body></html>

----=_RainLoop_587_997816661.1647931078--

SPF 解决了接收方验证发件人域名 SPF 记录内 IP 地址从而验证发件人的问题。但是因为 SPF 定义的发件人是 RFC5321 协议中规定的 Return-Path,而 DKIM 在邮件头中直接包含了域名,只要使用该域名的公钥验证通过即可。

而现在的邮件服务给用户展示的发件人都是 From 字段,而不是 SPF 的 Return-Path,也不是 DKIM 的 DKIM-Sginatur: d=,所以攻击者可以通过伪造这两个字段,发送如上的邮件,完美通过 SPF 和 DKIM 检查,因为 SPF 检查 Return-Path 而 DKIM 验证的 d= 也是 fake.com 所以最终用户看到的发件人却是 admin@q.com

所以就诞生了 [[DMARC]]。DMARC 结合了 SPF 和 DKIM,规定了 Return-PathDKIM-Signature: d= 两个至少需要有一个与 From 头对应,否则判定为失败。

当邮件服务器接收到邮件时,先验证 DKIM,SPF,然后再根据 DMARC 的配置,检查。这样就能确保最终用户看到的 From 字段和 SPF、DKIM 认证的发件人一致了。

reference


2022-03-01 email , mail , self-hosted , mail-server , gmail , smtp , linux , pop , imap , dns , spf

腾讯轻量云服务器挂载云硬盘

之前看到轻量云服务器做活动,500 GB 的存储空间一年只需要 9.9 元,配合之前的轻量服务器正好买了 3 年。

挂载到之前的轻量云服务器作为数据盘。

  • 限国内轻量云服务器
  • 仅有100GB、500GB、1TB,分别售价一年为 5 元,9.9 元,和 19.9 元,可以一年或者三年购买。
  • 选择区域时需要与轻量云服务器位置一致

tencent cloud drive discount

如果感兴趣可以 Follow 我的 Twitter,之后有更多的优惠会及时发出来。

我本人的轻量云并没有那么多的流量,所以放弃了 1T 的空间选择了折中的 500G。

初始化云硬盘

sudo fdisk -l
# 创建文件系统
sudo mkfs -t ext4 /dev/vdb
# 新建挂载点
sudo mkdir /mnt
# 挂载
sudo mount /dev/vdb
sudo df -TH

Linux 系统开机自动挂载磁盘

sudo blkid /dev/vdb
/dev/vdb: UUID="8cxxxxxx-9a49-49bf-8185-xxxxxxxxxxxx" TYPE="ext4"

修改 /etc/fstab 文件

《设备信息》 《挂载点》 《文件系统格式》 《文件系统安装选项》 《文件系统转储频率》 《启动时的文件系统检查顺序》
UUID=d489ca1c-5057-4536-81cb-ceb2847f9954 /mnt ext4 defaults 0 0

最后执行:

sudo mount -a

可以用 df -h 来验证。

reference


2022-02-26 tencent , cloud , vps , cloud-drive , mount , linux

使用 Beancount 记账篇三:周期账单

在 Beancount 使用的过程中难免会出现周期性重复的账单,比如房租,水电,网费,以及可能的分期付款账单,每个月的订阅费用等等。在之前我都是在 Vim 下复制粘贴然后改改,倒是没有那么麻烦,比如房租,基本上几秒钟就能搞定,但是长期下来我发现这样一来比较繁琐,因为我基本上每个月只对账一次,也不会一直开着 fava,所以有可能有遗漏,排查起来麻烦,二来为了管理方便我需要单独设立一个文件来管理,比如房租会单独有一个 rent.bean 文件,所以最后并没有向日常的账单那样是按照月份来管理的,如果要从时间上来统一管理就比较麻烦。

不过随手一查文档,发现 Beancount 提供了一个插件 plugin "beancount.plugins.forecast 专门用来处理周期性账单(交易),可以按照每月费用的自动生成。并且只需要记录一次,之后可以按照设定的周期截止时间,或者执行几次来自动完成对账。体验了一下感觉非常不错,下面就记录一下使用过程。

首先在 main.bean 中引入插件:

plugin "beancount.plugins.forecast"

然后就可以在 *.bean 文件中定义分期语法。

这里举一个例子,比如每个月3000的房租:

plugin "beancount.plugins.forecast"

2021-01-01 open Expenses:House:Rent
2021-01-01 open Assets:DebitCard:CMB

2021-05-17 # "House Rent [MONTHLY]"
    Expenses:House:Rent   3000 CNY
    Assets:DebitCard:CMB

使用上了上述的交易之后,Beancount 会每个月自动记录一笔,在 Fava 中查看的时候会默认把今年剩余的月份都补全。注意上面的语法中 # 是必须的,并且 [MONTHLY] 定义了记账周期。

同样的,这里的 MONTHLY 可以替换成

  • YEARLY,每年
  • WEEKLY,每周
  • DAILY,每天

更进一步,如果已知了账单的循环次数,比如分期付款的时候分了 12 期,那么可以在 MONTHLY 后面使用如下的语法:

2022-02-08 # "Computer [MONTHLY REPEAT 12 TIMES]"
  Expenses:DigitalDevice                      20000 USD
  Liabilities:CreditCard:CMB

其中的 [MONTHLY REPEAT 12 TIMES] 就定义了循环的次数。

另外一种情况就是不清楚次数,但是知道账单结束的时间,那么可以使用 UNTIL 语法:

2022-02-08 # "Electricity bill [MONTHLY UNTIL 2022-12-31]"
  Expenses:Electricity                      50.10 USD
  Assets:Checking                          -50.10 USD

交易按照年循环,循环 10 次:

2014-03-08 # "Electricity bill [YEARLY REPEAT 10 TIMES]"
  Expenses:Electricity                      50.10 USD
  Assets:Checking                          -50.10 USD

SKIP 语法可以用来跳过某一次记账:

2014-03-08 # "Electricity bill [WEEKLY SKIP 1 TIME REPEAT 10 TIMES]"
  Expenses:Electricity                      50.10 USD
  Assets:Checking                          -50.10 USD

2014-03-08 # "Electricity bill [DAILY SKIP 3 TIMES REPEAT 1 TIME]"
  Expenses:Electricity                      50.10 USD
  Assets:Checking                          -50.10 USD

reference


2022-02-25 beancount , accounting , double-entry

Playbook 使用体验

Playbook 是一个为创意人员而设计的文件管理器,同时强调了非常方便的在线协作能力。Playbook 称自己为 Dropbox for designers。

看其官网的描述就可以知道,Playbook 是为了 Designers,Artist,Photographers,marketer 而设计的。设计师可以用它来管理设计素材,交互设计;艺术家可以用它来管理灵感;摄影师可以用它来分享摄影作品。1

playbook online showcase

在 Playbook 中每一个资源都是使用 Board 来管理,每一个 Board 都在 Playbook 中,所以从上到下的层级关系大概是,Playbook,Board,Sub-Boards, 最后就是要管理的图片资源。

现在注册 Early Access Pro account,可以得到 4TB 的永久存储空间。当然前提是 Playbook 可以一直运营下去。

总结

最后来总结一下 Playbook,一句话说就是产品目前都是在发展阶段,目前只有基于 Web 的管理端,也没有推出任何 Native 的客户端,这也就意味着所有的用户的数据都是在其服务器中,这也就违背了我个人选择软件的第一条原则,由我产生的数据都必须有非常方便可以导出及备份的能力。所以在我这里如果以后要重度使用 Playbook ,那么我也只能将 Playbook 作为一个分享工具来使用。而本地还是保留一个与 Playbook 相似的文件结构来备份这些内容。

初步的使用感受来说,Playbook 非常适合于分享,以及多人协作的场景,如果只是个人使用的话似乎只能用来它作为产出的展示页面。

reference


2022-02-20 playbook , artist , idea , file-manage , photographer , design

利用 ruTorrent 与 rTorrent 限制单独种子的速度

之前的几篇文章就提到过 rTorrent 和 ruTorrent 是我最喜欢的 BT 客户端。之前几篇文章:

今天就再来分享一个非常简单的小技巧,那就是针对个别种子单独进行限速。通常情况下整体的限速,上传和下载,其他的客户端基本上都做得非常完善,基本上是任何一个客户端的基本功能,但是非常少的客户端可以针对个别的种子可以进行限速,而借助 rTorrent 和 ruTorrent 客户端,可以完美的实现。

众所周知有一些私有的 tracker 是会对上传速度做要求的,如果一直以非常高的速度上传可能会被误判,所以非常有必要对个别的种子进行上传的限速。而有些时候可能不希望下载过快以至于占满全部的带宽,尤其是在国内有限的下载带宽的情况下。

Channel

在 rTorrent 和 ruTorrent 的组合中可以通过 Channel 来实现。在 ruTorrent 的设置中,有 Channel 这样一个设定:

rutorrent setting channel

可以看到默认情况下有 10 个不同级别的速度限制,这里以第一条 up16 举例,表示的就是限制上传速度 16Kib/s 而下载不限制。同样 down 的也是类似。

然后在主界面中,右击单个种子就可以看到其中可以对 Channel 设置,比如设置上传不能超过 50MB/s,就可以选择 up50000

rutorrent right click menu

Ratio Rules

如果觉得上面的手动方式比较笨拙的话,ruTorrent 还可以通过 Ratio Rules 来自动设置限速。在菜单栏点击 Plugins,然后选择 Ratio Rules,会展示如下界面。

rutorrent ratio rules

在这个对话框中就可以根据自己的需要设定,如下的条件:

  • Torrent 的标签包含
  • Torrent 的 Tracker URL 包含
  • Tracker 是公开的
  • Tracker 是私有的

当满足这些条件的时候,设置 ratio 以及限速,其中 Set throttle to 就可以选择之前设置的 Channel 中的内容。

当然如果你使用的是 rTorrent 没有使用 ruTorrent 界面,也可以通过配置的方式来实现,这里就不再具体展开。可以参考我之前的文章,以及官网的内容自行设置。

reference


2022-02-19 rtorrent , rutorrent , bittorrent , bt , private-tracker , tracker , bittorrent

rTorrent 配置之根据用户标签移动完成的下载文件

好几年前从接触了 QNAP 上的 ruTorrent 开始就一直在用 [[rTorrent]] 和 ruTorrent 的组合。在 QNAP 上运行稳定,后来又迁移到 docker 上,中间还经历了从 LinuxServer 维护的 rTorrent 迁移到 crazymax/rtorrent-rutorrent 维护的镜像上。一直都没有出现过问题。

今天在 Twitter 上向人推荐的时候被问到能不能在完成之后将文件移动到特定的目录。我知道很多人会分门别类的管理自己的文件,并且希望在移动文件之后还能一直 seeding, 这个对于 rTorrent 来说也特别简单,这里就简单记录一下。

我个人对 rTorrent 的使用流程

之前的文章中也曾经提到过 rTorrent 是 C++ 实现的基于终端的 torrent 客户端,而 ruTorrent 是 rTorrent 的 WEB 界面。为了不引入更复杂的 ruTorrent 插件,这篇文章就仅仅围绕 rTorrent 的配置,通过几行简单的配置就能实现 rTorrent 在下载完成之后的目录规划。

我个人一般通过如下两种方式添加种子到 rTorrent:

  • watch directory,通过监听硬盘上的某一个目录,只要发现 *.torrent 文件自动加载并开始下载
  • 通过 Web 界面 ([[ruTorrent]])

而这两种方式都可以通过给种子文件添加 Label 的方式来实现。

配置 rTorrent 完成下载后移动目录

首先要先认识一下 rTorrent 的配置文件 .rtorrent.rc 文件,一般情况下会在 HOME 目录中,如果是通过 Docker 安装会出现在配置目录中。rTorrent 的文档非常详细,rTorrent 一般把这个配置称作 rTorrent Scripting

比如说最简单的限制下载和上传的速度,可以通过如下两行实现:

# Global upload and download rate in KiB. "0" for unlimited¬
throttle.global_down.max_rate.set_kb = 102400¬
throttle.global_up.max_rate.set_kb = 102400¬

简单了解了 rTorrent 的配置之后,那么就直接进入正题,要实现完成之后自动移动到相应的目录,首先需要给种子自动加上标签。

Watch directories

而我上面提到过,我通常使用 Watch 目录来自动添加种子。

通过配置不同的 watch 目录,每一个都对应着一个 category 或者 label,那么在 /path/to/rtorrent/watch/tvshows 中的 torrent 文件,就会自动有一个 custom1 的值,这个值在下一步会使用到。

注意下面的配置在 rTorrent 0.9 版本以后可用1

# TV shows
schedule2 = watch_directory_1,10,10,"load.start_verbose=/path/to/rtorrent/watch/tvshows/*.torrent,d.custom1.set=tvshows"

# Movies
schedule2 = watch_directory_2,10,10,"load.start_verbose=/path/to/rtorrent/watch/movies/*.torrent,d.custom1.set=movies"

# Comics
schedule2 = watch_directory_3,10,10,"load.start_verbose=/path/to/rtorrent/watch/comics/*.torrent,d.custom1.set=comics"

# Music
schedule2 = watch_directory_4,10,10,"load.start_verbose=/path/to/rtorrent/watch/music/*.torrent,d.custom1.set=music"

这些配置中有两个关键部分:

  • /path/to/rtorrent/watch/x 部分是 watch 的目录,注意这里需要根据自己的情况进行修改
  • set_custom1=后面的内容是需要设置的标签

然后需要再进行如下配置,设置的目的就是让种子下载完成之后移动到上面打标签的目录:

# Add new method to get finished dir
method.insert = d.get_finished_dir,simple,"cat=/path/to/rtorrent/finished/,$d.custom1="
method.set_key = event.download.finished,move_complete,"d.directory.set=$d.get_finished_dir=;execute=mkdir,-p,$d.get_finished_dir=;execute=mv,-u,$d.base_path=,$d.get_finished_dir="

第一行 method.insert 定义了一个方法 get_finished_dir 每一个 torrent 都会返回一个字符串 /path/to/rtorrent/finished/ 和一个标签(custom1 的值)。

带有标签 tvshows 的就会返回 /path/to/rtorrent/finished/tvshows,修改第一部分,就可以根据自己的需要移动到任何想要的目录。

第二行配置是,当发生下载完成事件的时候,移动完成的内容,然后调用上一步定义好的 get_finished_dir 方法返回这个文件最终应该在的目录,并且通过 set_directory 方法设置最终的目录,如果最终的目录不存在则自动创建,最后再将 torrent 文件移动到最终的目录中。

  • 没有标签的 torrent 会被放到 rTorrent 定义的下载目录
  • 带有标签的 torrent 文件会被以移动到标签定义的目录,如果目录不存在则会自动创建

还有另外一种写法,下面的配置中,第二行配置定义了 move_to_complete 方法,接受两个参数,第一个参数是下载完成后的文件实际路径,第二个参数是要移动到的目录。然后在第三行配置中就直接调用该方法了:

# Move finished (no need Autotools/Automove plugin on ruTorrent)
method.insert = d.get_finished_dir, simple, "cat=$cfg.download_complete=,$d.custom1="
method.insert = d.move_to_complete, simple, "d.directory.set=$argument.1=; execute=mkdir,-p,$argument.1=; execute=mv,-u,$argument.0=,$argument.1=; d.save_full_session="
method.set_key = event.download.finished,move_complete,"d.move_to_complete=$d.data_path=,$d.get_finished_dir="

当以上配置生效之后,每当我将一部电影的种子放入到 /watch/movies 文件夹中的时候,该种子会自动被添加到 rTorrent 中,并且自动添加标签 movies,当完成下载后,会被放置到 /path/to/rtorrent/finished/movies 文件夹中。

通过上面的方式可以不需要依赖 [[ruTorrent]] 的 Autotools/Automove 插件。

完成了上面的操作之后有人就要问,那怎么把 torrent 文件自动放到对应的 watch 目录呢?那不妨看看我是如何用 flexget 来实现的。

reference


2022-02-17 rtorrent , rutorrent , config , rtorrent-config , bittorrent , torrent , private-tracker , tracker , bt

Pulsed Media seedbox 测评和使用

2022 年 6 月更新

不再推荐 Pulsed Media 这一家 Seedbox 服务提供商。服务提供商傲慢的态度,以及随意关停账号的行为,Pulsed Media 已经被 Reddit 禁言,在 LowEndBox 中也有非常多的关于不推荐 Pulsed Media 的帖子。

Pulsed Media 是一家 [[Seedbox]] 服务提供商,Seedbox 通常指的是专门为 Seeding(做种)而设计的下载上传服务器,通常有比较大的硬盘,带宽。Seedbox 通常有被称为「盒子」。

Pulsed Media 是一家共享盒子,也就是意味着多人共享一台独立主机,多人可能共享同一个 IP 地址。Pulsed Media 成立于 2009 年,机房在芬兰赫尔辛基。

我购买的是 12 周年促销款 V10G L,包含 8T 空间,8G RAM,10Gbps 带宽,以及 32000 GiB 的外部流量,无线的内部机房流量。价格是 11.99€ 一个月。

从官网及促销页面来看,自带支持 [[rTorrent]] 和 [[ruTorrent]],以及可以选择开启的 [[Dulege]] 和 [[qBittorrent]]。支持公开的 tracker。

自带 HTTP, HTTPS, FTP, SFTP, LFTP, SSH 访问。还允许安装使用 rclone, sonarr, pyload, resilio, syncthing, btsync (1.4 and 2.2), ffmpeg, fuse (unionfs, sshfs, s3fs etc.) 等等。

而串流支持 [[Jellyfin]] 和 [[Emby]],不支持 [[Emby]],因为创始人对于 Plex 曾经的一些行为不满。

购买及初始化使用

Pulsed Media 支持 Paypal,加密货币(BTC, LTC, XMR, ETH, DOGE 等等),购买 14 天内支持无条件退款。

在购买之后很快就会收到邮件,里面会包含后台登录地址,用户名和密码,以及登录 SSH,SFTP 等等的信息。

用里面的信息登录后台之后会发现 Pulsed Media 自己开发了一套 Seedbox 系统,在一台独立的主机上会开通分配多个用户。每一个用户都有一个自己独立的空间。可以通过简单的管理后台来启动一些简单的服务,比如 rTorrent, Deluge, qBittorrent 等等。

pulsed media dashboard services

在右上角可以看到当前的空间以及流量使用情况。

pulsed media quota info and traffic info

Pulsed Media 有一点非常不错的是,它会奖励始终用它服务的用户,看到上面截图 Quota Info 下面的一行 Bonus disk space 就是奖励的空间。Pulsed Media 会有脚本每小时执行两次检查,每一次都随机挑选 10 位用户,如果这些用户存在剩余空间,就会给用户一些额外的硬盘奖励。具体奖励的策略可以查看其官网

我购买 Pulsed Media 的一大理由就是其支持的服务,囊括了我曾经在博客中介绍过的非常好用的一些软件,比如自从发现了使用至今的 Syncthing,还有从 btsync 改名成 Resilio Sync 的同步工具。还有常常使用的 flexget。同时还支持很多我没有用过,但是一直想尝试一下的工具,比如 [[rclone]],[[Sonarr]], [[Radarr]] 等等。

下面就直接进入正题来记录一下我的使用过程及感受。

在 Pulsed Media 上使用 rTorrent 和 ruTorrent

Pulsed Media 最重要的就是 Seeding 了,登录后台之后能看到 Pulsed Media 默认就启用了 rTorrent 和 ruTorrent,我之前也说过,自从我在 QNAP 上发现了 rTorrent 和 ruTorrent 之后我就一直使用了这个组合。ruTorrent 的界面也非常的完善和强大,可以查看种子的 tracker 信息,制作信息,连接的 peers 信息,是我见过的显示信息最详细的一个客户端。并且 rTorrent 本身的配置就非常强大,通过简单的几行配置就可以实现按照标签自动移动完成的文件,也可以在不安装任何扩展的情况下实现 RSS 下载。总之我还有很多不懂的地方,每一次看 rTorrent 的文档总是会发现更多有趣的东西。

设定 Transdroid

[[Transdroid]] 是一款 Android 上的 rTorrent 控制端,可以对 rTorrent 等等其他客户端进行远程控制。

其中一些重要的配置如下:

Port 设置为 80 如果登录链接是: server.pulsedmedia.com/user-yourusername/ 那么在高级设置中记住设置 SCGI mount point: /user-yoursername/rutorrent/plugins/httprpc/action.php

如果登录链接是: username.server.pulsedmedia.com 那么设置 SCGI mount point: /rutorrent/plugins/httprpc/action.php

详细的步骤可以参考官网

后台最重要的功能我认为就是这些了,接下来用邮件中的信息来登录 SSH 后台看看。

同理如果要在 [[PT Plugin Plus]] 中配置下载服务器,也需要使用上面的配置,需要在个人的登录链接之后加上 SCGI mount point 的路径才能连接上。

使用 SSH 登录

使用 SSH 登录后台之后可以看到用户 HOME 目录中非常干净,就这些目录。

❯ ls
data  rTorrentLog  session  watch  www

当然还有一些隐藏文件,比如 .rtorrent.rc, .rtorrent.rc.custom 等等,一眼看上去就是 rTorrent 的配置文件,其他的文件可以自行查看。上面的几个文件夹

  • data 是 rTorrent 的默认下载目录
  • watch 是 rTorrent 的监控目录,这个文件夹下的 torrent 文件都会被自动加载并下载,后文使用的 flexget 就可以将这个目录作为下载 torrent 的默认目录,另外也可以自行修改 .rtorrent.rc 来自定义监控目录

zsh

既然我可以登录 SSH,虽然我没有 sudo 和 root 权限,但是我也可以在我自己的 HOME 目录下使用 zsh。我之前的一键初始化脚本在 Pulsed Media 上无法使用,因为用到了 chsh 命令,这个需要 sudo 权限将默认的 SHELL 更换成 zsh。不过那也没有关系。Pulsed Media 默认是安装了 zsh 的,用 which zsh 可以确认。

我只需要在 $HOME 目录下创建 .bash_profile 文件,然后填入:

export SHELL=`which zsh`
[ -z "$ZSH_VERSION" ] && exec "$SHELL" -l

然后还是使用我的脚本:

git clone https://github.com/einverne/dotfiles.git
cd dotfiles
make bootstrap

会自动初始化我的基础配置,然后退出 SSH,然后重新登录,就会启用 zsh。

在 Pulsed Media 上使用 flexget

Pulsed Media 上无法使用 Docker,所以之前文章中的安装方式就不行了,但是 Pulsed Media 上默认已经安装了 flexget 工具,直接可以运行。

flexget 默认会按照下的顺序寻找配置文件 config.yml:

  • 当前目录
  • virtualenv 目录
  • ~/.flexget/
  • ~/.config/flexget

所以可以参考之前文章中的配置写法,在配置文件 .config/flexget/config.yml 中配置:

tasks:
  avistaz-free:
    rss: https://avistaz.to/rss/feed?fid=2&pid=722xxxx
    download: ~/watch
    accept_all: yes

schedules:
  - tasks: '*'
    interval:
      minutes: 30

然后 crontab -e 增加:

*/30 * * * * /usr/local/bin/flexget --cron execute

这样就会每隔 30 分钟执行一次,然后下载 torrent 到 ~/watch 目录。flexget 更多的配置详情可以参考之前的文章

在 Pulsed Media 上安装使用 Syncthing

Syncthing 和 flexget 也类似,机器上已经安装了 Syncthing,可以直接启动,同样我也把配置放在了 .config/syncthing 目录下。

/usr/bin/syncthing --no-browser --home="/home/einverne/.config/syncthing"

Syncthing 默认的配置在: $HOME/.config/syncthing 目录下。第一次启动会自动放入默认的配置。

启动之后再根据自己的情况修改 config.xml 配置。

通常情况下可以选择一个没有被占用的端口,修改如下行:

<address>0.0.0.0:18384</address>

因为 Syncthing 需要一直在后台运行所以我启用 Tmux,然后在 Tmux 中执行上面的命令。

如果你使用我的 dotfiles,那么可以直接

tm syncthing

会自动创建一个 syncthing 名字的 Session。

如果手动创建可以使用:

tmux new -s syncthing

然后在 Tmux 中执行命令。然后用 Ctrl+B 然后按 D 来是 Tmux Syncthing 在后台执行。更多的 Tmux 的使用可以参考这篇文章

在 Pulsed Media 上安装使用 Resilio Sync

同样 Resilio Sync 机器上也安装了,可以使用 whereis btsync 或者 whereis rslsync 来查看。

不过我嫌机器上安装的版本太老,所以自己去官网下载的一份最新的 rslsync 放到了 $HOME/einverne/bin 目录下,并且把这个 PATH 加入到了系统 PATH。

然后创建配置模板:

rslsync --dump-sample-config > .config/rslsync/rslsync.conf

然后修改模板配置,几个重要的配置:

{
   "device_name": "PM Sync Device",  // 自己设定名字
   "listening_port" : 17888, // 0 - 随机端口,或者自行配置未被占用的端口

/* storage_path dir contains auxilliary app files if no storage_path field: .sync dir created in current working directory */
 "storage_path" : "/home/einverne/rslsync",

/* set location of pid file */
 "pid_file" : "/home/einverne/var/run/resilio/resilio.pid",

/* use UPnP for port mapping */
  "use_upnp" : true,

/* limits in kB/s. 0 - no limit */
  "download_limit" : 10240,
  "upload_limit" : 10240,

/* proxy configuration */
// "proxy_type" : "socks4", // Valid types: "socks4", "socks5", "http_connect". Any other value means no proxy
// "proxy_addr" : "192.168.1.2", // IP address of proxy server.
// "proxy_port" : 1080,
// "proxy_auth" : false, // Use authentication for proxy. Note: only username/password for socks5 (RFC 1929) is supported, and it is not really secure
// "proxy_username" : "user",
// "proxy_password" : "password",

/* directory_root path defines where the WebUI Folder browser starts (linux only). Default value is / */
  "directory_root" : "/home/einverne/rslsync/MySharedFolders/",

/* directory_root_policy defines how directory_root is used (linux only).
   Valid values are:
     "all" - accepts directory_root and its subdirectories for 'getdir' and 'adddir' actions
     "belowroot" - accepts directory_root's subdirectories for 'getdir' and 'adddir' actions,
      but denies attempts to use 'adddir' to create directories directly within directory_root
   Default value is "all". */
//  "directory_root_policy" : "all",

  "webui" :
  {
    "listen" : "0.0.0.0:8888" // remove field to disable WebUI

/* preset credentials. Use password or password_hash */
  ,"login" : ""
  ,"password" : "" // (not recommended, better use 'password_hash_unified')
//  ,"password_hash" : "<crypt() 3 format password hash>" // (not recommended) Works on *nix only!
// Use either 'password_hash' or 'password_hash_unified' (recommended), but not both of them!
//  ,"password_hash_unified" : "<SHA2-256 hash in HEX format>" // Works on all platforms.
//  ,"password_hash_salt_unified" : "<any text>" // Salt for unified password's hash. Works on all platforms.
  ,"allow_empty_password" : false // Defaults to true
/* ssl configuration */
//  ,"force_https" : true // disable http
//  ,"ssl_certificate" : "/path/to/cert.pem"
//  ,"ssl_private_key" : "/path/to/private.key"

/* dir_whitelist defines which directories can be shown to user or have folders added (linux only)
   relative paths are relative to directory_root setting */
  ,"dir_whitelist" : [ "/home/einverne/rslsync/MySharedFolders/", "/home/einverne/rslsync" ]
  }

/* !!! if you set shared folders in config file WebUI will be DISABLED !!!
   shared directories specified in config file  override the folders previously added from WebUI. */
/*,
  "shared_folders" :
  [
    {
      "secret" : "MY_SECRET_1", // required field - use --generate-secret in command line to create new secret
      "dir" : "/home/user/resilio/sync_test", // * required field
      "use_relay_server" : true, //  use relay server when direct connection fails
      "use_tracker" : true,
      "search_lan" : true,
      "use_sync_trash" : true, // enable SyncArchive to store files deleted on remote devices
      "overwrite_changes" : false, // restore modified files to original version, ONLY for Read-Only folders
      "selective_sync" : false, // add folder in selective sync mode
      "known_hosts" : // specify hosts to attempt connection without additional search
      [
        "192.168.1.2:44444"
      ]
    }
  ]
*/

/* Advanced preferences can be added to config file. Info is available at "https://help.getsync.com/hc/en-us/articles/207371636"
For example see folder_rescan_interval below */
//, "folder_rescan_interval" : 600

}

注意根据自己的需要,设定用户名和密码。

然后启动 btsync:

btsync --config .config/rslsync/rslsync.conf --nodaemon

这个命令会以前台方式执行。通常用上面的方式放入到 Tmux 在后台执行。

一键安装其他组件

一键安装 Sonarr, Raddarr, Prowllar, SABnzbd and Jellyfin:

curl https://gist.githubusercontent.com/gsj1377/a7eb727e079a1cefc9baff4e130d8900/raw/app-installation.sh | bash && source ~/.bashrc

Pulsed Media 流量限制

在登录账号之后,在后台右上角可以看到最近 30 天的流量使用情况。在 Pulsed Media 这里,流量的充值日期并不是每个月初,而是滚动限制,也就是当前看到的是过去 30 天的流量使用情况,Pulsed Media 在一篇很久之前的文章中解释了这么做的理由。

总结

总之这么一顿折腾以及完全可以使用,并且可以自动下载一些 Free 的种子,剩余的空间我也可以用来同步自己的文件。

如果你也想购买可以点击我的邀请链接,目前官方的 12 周年几年活动还在继续,页面还有几台特价的机器。并且如果只是小需求,500GB 的套餐 V1000, 1TB 的套餐 M10G,等等首月还可以免费使用,不满意直接申请下个月不续费即可。

pulsed media 12th anniversary special first month free

如果需求比较大也可以购买 V10G

pulsed media 12th anniversary v10g

reference


2022-02-17 pulsedmedia , seedbox , torrent , btsync , resilio , resilio-sync , syncthing

使用 Nginx Proxy Manager 管理 Nginx 代理

Nginx Proxy Manager 是一个可以自动暴露网络服务,并申请 Let’s Encrypt 免费证书的 Nginx 反向代理。可以非常方便的和 Docker 一起使用。Nginx Proxy Manager 还提供了一个不错的界面。

在没有使用 Nginx Proxy Manager 之前我一直使用 Nginx Proxy 来自动暴露 Docker 服务,并生成 SSL 证书。

安装

首先创建 network:

docker network create nginx-proxy

docker-compose 配置:

version: "3"
services:
  nginx-proxy-manager:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    ports:
      # These ports are in format <host-port>:<container-port>
      - '80:80' # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81' # Admin Web Port
      # Add any other Stream port you want to expose
      # - '21:21' # FTP
    environment:
      DB_SQLITE_FILE: "/data/database.sqlite"
      # Uncomment this if IPv6 is not enabled on your host
      # DISABLE_IPV6: 'true'
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt

使用 docker-compose up -d 之后,访问浏览器 IP:81 端口。

默认的用户名和密码:

  • admin@example.com
  • changeme

使用

创建一个 Nginx 反向代理到 81 端口,可以设置域名解析,将 npm.example.com 解析到 IP 地址,然后在 Nginx Proxy Manager 后台,创建 Proxy Host,按照自己的配置填入:

nginx proxy manager

等待 DNS 解析生效,访问 npm.example.com 即可登录管理后台。

使用 Docker network

在使用 Nginx Proxy Manager 的时候,可以不用让每一个容器都暴露一个端口,可以使用 Docker network 来将容器放到同一个网络下,然后使用名字来关联。

创建网络:

docker network create nginx-proxy

然后分别将 Nginx Proxy Manager 和其他服务放到同一个网络下,添加如下配置:

networks:
  default:
    external:
      name: nginx-proxy

以 Portainer 举例:

version: '3'
services:

  portainer:
    image: portainer/portainer
    privileged: true
    volumes:
      - './data:/data'
      - '/var/run/docker.sock:/var/run/docker.sock'
    restart: unless-stopped

networks:
  default:
    external:
      name: nginx-proxy

在配置中可以看到 Portainer 没有显示定义暴露的 9000 端口,这个时候可以在 Nginx Proxy Manager 后台,通过 portainer 作为 hostname 来创建反向代理,关联到这个容器。这种方法只需要保证每一个容器都有一个唯一的名字即可。

本地静态网站

首先添加一个额外的 Volume:

/opt/website:/var/www/website

Forward Hostname/IP 填写服务器 IP 地址。

在 Advanced 中配置:

location / {
  root /var/www/website;
}

保存。然后将静态网站内容放到宿主机的 /opt/website 中即可。

reference


2022-02-16 nginx , docker , docker-compose , linux , http , container , reverse-proxy

电子书

本站提供服务

最近文章

  • Dinox 又一款 AI 语音实时转录工具 前两天介绍过 [[Voicenotes]],也是一款 AI 转录文字的笔记软件,之前在调查 Voicenotes 的时候就留意到了 Dinox,因为是在小红书留意到的,所以猜测应该是国内的某位独立开发者的作品,整个应用使用起来也比较舒服,但相较于 Voicenotes,Dinox 更偏向于一个手机端的笔记软件,因为他整体的设计中没有将语音作为首选,用户也可以添加文字的笔记,反而在 Voicenotes 中,语音作为了所有笔记的首选,当然 Voicenotes 也可以自己编辑笔记,但是语音是它的核心。
  • 音流:一款支持 Navidrom 兼容 Subsonic 的跨平台音乐播放器 之前一篇文章介绍了Navidrome,搭建了一个自己在线音乐流媒体库,把我本地通过 [[Syncthing]] 同步的 80 G 音乐导入了。自己也尝试了 Navidrome 官网列出的 Subsonic 兼容客户端 [[substreamer]],以及 macOS 上面的 [[Sonixd]],体验都还不错。但是在了解的过程中又发现了一款中文名叫做「音流」(英文 Stream Music)的应用,初步体验了一下感觉还不错,所以分享出来。
  • 泰国 DTV 数字游民签证 泰国一直是 [[Digital Nomad]] 数字游民青睐的选择地,尤其是清迈以其优美的自然环境、低廉的生活成本和友好的社区氛围而闻名。许多数字游民选择在泰国清迈定居,可以在清迈租用廉价的公寓或民宿,享受美食和文化,并与其他数字游民分享经验和资源。
  • VoceChat 一款可以自托管的在线聊天室 VoceChat 是一款使用 Rust(后端),React(前端),Flutter(移动端)开发的,开源,支持独立部署的在线聊天服务。VoceChat 非常轻量,后端服务只有 15MB 的大小,打包的 Docker 镜像文件也只有 61 MB,VoceChat 可部署在任何的服务器上。
  • 结合了 Google 和 AI 的对话搜索引擎:Perplexity AI 在日本,因为 SoftBank 和 Perplexity AI 开展了合作 ,所以最近大量的使用 Perplexity ,这一篇文章就总结一下 Perplexity 的优势和使用技巧。