树莓派解决异地亲人的多媒体影音需求

树莓派解决异地亲人的多媒体影音需求

前言 #

解决异地亲人的多媒体影音需求,克服条件:

  • 异地亲人没有维护服务器能力的;
  • 本地和异地的上行带宽皆有限;
  • 异地无公网;
  • 尽可能不占用本地资源;

方法一 | Plex直接访问 #

在已有Plex服务的情况下,最直接的方法当然是在异地电视上安装Plex客户端访问,但受限于本地上行带宽低,异地电视配置差、且不支持HDR,实际测试效果并不理想。

方法二 | P2P文件传输 #

通过在异地安装轻量化服务器,并安装Jellyfin服务,用P2P传输媒体文件至异地。虽然传输慢,但观看过程是流畅的。这种方法可能会给本地宽带来较大负担,并且我担忧上行流量过大,引起运营商的关注(之前网上有过不少PCDN误判案例)。

方法三 | 混合 PT+P2P+异地组网 #

  • 在家宽下行大、上行小的现状下,还是得依赖BT/PT实现文件的快速获取,查阅了几个PT站点的规则,发现有的站点也支持多IP下载,只要不同时下载同一个torrent即可(具体的规则需要谨慎研究各个PT站点规则,必要时向站点管理员询问或备案);
  • 我同时保留了P2P的方案,以供非BT/PT的大文件传输和torrent文件的同步;
  • 由于异地宽带无公网IP,亲人也没有足够的网络知识来维护异地服务器,所以要在本地(城市1)实现对异地(城市2)的管理,需要设计一个自动化的异地组网方案。我在本地(城市1)开启一个Webhook,异地(城市2)设置轮询,通过在本地设置Webhook的应答,异地自动加入/离开组网(自动离开非常重要,避免占用本地(城市1)宽带资源);

如图所示,本地(城市1)是一个X86架构服务器,异地(城市2)是一个树莓派5,城市x则是任意一台终端设备;通过wireguard实现异地组网,组网内的设备可以相互访问、管理。树莓派5装有Jellyfin、rtorrent、resilio软件,具体的设置过程在下一章节详解。多媒体文件的获取以PT为主,Resilio为辅,PT通过flood&rtorrent客户端管理,rtorrent下载任务可以由rss、torrent文件触发。

混合 PT+P2P+异地组网 混合 PT+P2P+异地组网

Raspberry Pi OS 设置 #

根据官方方法将Raspberry Pi OS烧录进树莓派后,即可开始以下配置。

必要设置 #

  • 开启ssh;
sudo service ssh start
sudo systemctl enable ssh # 开机自启动
  • 安装Docker, Debian | Docker Docs
  • 安装Vim: sudo apt install vim
  • 换源: sudo vim /etc/apt/sources.list
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware

deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware

deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware

# 以下安全更新软件源包含了官方源与镜像站配置,如有需要可自行修改注释切换
deb https://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
# deb-src https://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware

Flood | Torrent下载器 #

  • 新建必要文件夹
mkdir flood

cd flood

mkdir config media

vi docker-compose.yml
  • docker-compose.yml配置
# docker-compose.yml
services:
  flood:
    image: jesec/flood
    user: 1000:1001
    restart: unless-stopped
    command: --port 3001 --allowedpath /data
    environment:
      HOME: /config
    volumes:
      - ./config:/config
      - ./media:/data/media
    ports:
      - 3001:3001

  rtorrent:
    image: jesec/rtorrent
    user: 1000:1001
    restart: unless-stopped
    command: -o network.port_range.set=16888-16888,system.daemon.set=true,dht.mode.set=disable
    environment:
      HOME: /config
    volumes:
      - ./config:/config
      - ./media:/data/media
    ports:
      - 16888:16888
  • 运行容器
sudo docker compose up -d
  • 访问http://RASPBERRY_IP_ADDRESS,在设置界面,连接类型选择Socket,Socket内容填写~/.local/share/rtorrent/rtorrent.sock
  • 设置界面默认下载位置修改为/data/media
  • 检查确认DHT节点交换为关闭状态;
  • (可选)点击rss图标,可以使用PT站点提供的rss链接实现自动下载;

Resilio Sync | P2P同步 #

  • Package: https://help.resilio.com/hc/en-us/articles/206178924
  • 下载DEB arm64安装包;
  • 安装sudo dpkg -i <resilio-sync.deb>
  • 访问http://YOUR_RASPBERRY_IP:8888完成设置;
  • 开机自启动,sudo systemctl enable resilio-sync
  • 设置同步文件夹,如该文件夹为用户raspberry所有,确保rslsync权限足够:
ls -ld /YOUR_SYNC_FOLDER
# drwxr-xr-x 2 raspberry raspberry 4096 YOUR_SYNC_FOLDER
# 更改权限
sudo chmod 777 YOUR_SYNC_FOLDER -R
  • 新建同步文件夹,标准文件夹高级文件夹均可,完成后共享权限改成读写,将链接复制到服务端输入密钥或链接完成设置;
  • (可选,预定义同步主机)服务端可将Resilio的端口映射至路由进而在公网开放,在树莓派端,选择共享链接的首选项->预定义主机->填入服务端网址和端口即可;
  • 通过Resilio Sync实现自动自动导入种子开始下载:将路径/home/raspberry/project/flood/config/.local/share/rtorrent/watch建立同步。完成后,只需将种子文件放入/watch/start文件夹中,rtorrent将自动开启下载任务,将种子文件放入/watch/load文件夹中,rtorrent将自动新建下载任务(但不开始下载);

Jellyfin | 多媒体服务器 #

curl -s https://repo.jellyfin.org/install-debuntu.sh | sudo bash
systemctl status jellyfin
# 打印状态为 Active: active (running)
  • http://RASPBERRY_IP_ADDRESS:8096,按照指示完成设置;

  • 文件夹权限:

find ./project/ -exec ls -ld {} \;
sudo -u jellyfin ls /home/raspberry/project/media
# "ls: cannot access '/home/raspberry/project/media': Permission denied"
cd /home
sudo chmod 755 -R raspberry/ #raspberry为用户名称

Wireguard | 异地组网 #

本地(城市1) | Server #

  • 安装Wireguard,根据提示一步一步完成配置;
curl -O https://raw.githubusercontent.com/angristan/wireguard-install/master/wireguard-install.sh
chmod +x wireguard-install.sh
./wireguard-install.sh 
  • 将配置过程中选择的端口port映射至公网;
  • 生成的配置文件:~/wg0-client-YOUR_WG_NAME.conf
[Interface]
PrivateKey = <你的私钥>
Address = 192.168.1.2/24  # 替换为分配的 IP 地址
DNS = 8.8.8.8  # 可选,设置 DNS 服务器

[Peer]
PublicKey = <服务器的公钥>
Endpoint = <服务器的IP或域名>:<端口号>
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

异地(城市2) | Client #

  • 安装Wireguard;
sudo apt update
sudo apt install wireguard -y
  • 将服务端生成的~/wg0-client-YOUR_WG_NAME.conf内容填入以下路径文件中;
sudo vim /etc/wireguard/wg0.conf
  • 启动;
# 启动连接
sudo wg-quick up wg0
# 验证连接
sudo wg show
ping 10.0.0.1 # Ping 服务端的内部 IP 地址(例如 10.0.0.1)
curl ifconfig.me # 应该显示服务端外部 IP
# 关闭连接
sudo wg-quick down wg0
  • 若遇到/usr/bin/wg-quick: line 32: resolvconf: command not found
sudo apt install resolvconf -y
  • 若安装resolvconf后无法解析域名
sudo vim /etc/resolv.conf
# 确保其中有一个有效的 nameserver 条目,如nameserver 8.8.8.8,否则手动添加

轮询 #

  • 在服务端设置一个webhook,我用的是n8n,也可以使用adnanh/webhook或flask构建。用Response Code来控制树莓派客户端是否开启Wireguard(200为连接,201为断开连接)。
  • 为避免违反PT规则,开启wireguard前关闭rtorent任务,轮询脚本如下:
# /home/raspberry/project/wireguard_poller.py
import os
import time
import requests
import logging

WEBHOOK_URL = "https://xxxx.xxx"
wg_active = False
LOG_FILE = "/var/log/wireguard_poller.log"
DOCKER_CONTAINERS = ["flood-flood-1", "flood-rtorrent-1"]

logging.basicConfig(
    filename=LOG_FILE,
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s"
)

def manage_docker_containers(action):
    for container in DOCKER_CONTAINERS:
        try:
            logging.info(f"{action.capitalize()}ing Docker container: {container}...")
            os.system(f"sudo docker {action} {container}")
            logging.info(f"Container {container} {action}ed successfully.")
        except Exception as e:
            logging.error(f"Failed to {action} container {container}: {e}")

def toggle_wireguard(enable):
    global wg_active
    if enable and not wg_active:
        logging.info("Stopping Docker containers before starting WireGuard...")
        manage_docker_containers("stop")
        logging.info("Starting WireGuard service...")
        os.system("sudo wg-quick up wg0")
        wg_active = True
        logging.info("WireGuard service started.")
    elif not enable and wg_active:
        logging.info("Stopping WireGuard service...")
        os.system("sudo wg-quick down wg0")
        wg_active = False
        logging.info("WireGuard service stopped.")
        logging.info("Starting Docker containers after stopping WireGuard...")
        manage_docker_containers("start")

def poll_webhook(interval):
    global wg_active
    while True:
        try:
            logging.info(f"Polling {WEBHOOK_URL}...")
            response = requests.get(WEBHOOK_URL, timeout=10)
            logging.info(f"Response code: {response.status_code}")

            if response.status_code == 200:
                toggle_wireguard(True)
                interval = 60
            elif response.status_code == 201:
                toggle_wireguard(False)
                interval = 300
        except requests.exceptions.RequestException as e:
            logging.warning(f"Request failed: {e}")

        time.sleep(interval)

def main():
    try:
        logging.info("Starting webhook poller...")
        poll_webhook(interval=300)
    except KeyboardInterrupt:
        logging.info("Exiting...")
    finally:
        if wg_active:
            toggle_wireguard(False)

if __name__ == "__main__":
    main()
  • 创建 systemd 服务;
# sudo vim /etc/systemd/system/wireguard_poller.service
[Unit]
Description=WireGuard Poller Service
After=network.target

[Service]
ExecStart=/usr/bin/python /home/raspberry/project/wireguard_poller.py
Restart=always
User=root

[Install]
WantedBy=multi-user.target
  • 启用;
sudo systemctl daemon-reload
sudo systemctl enable wireguard_poller.service
sudo systemctl start wireguard_poller.service
  • 查询日志cat /var/log/wireguard_poller.log

管理和使用 #

  • 树莓派的Wireguard连上服务器后,在服务端sudo wg show可以看到连接状态和局域网ip,如10.66.66.2
  • 重复使用./wireguard-install.sh为另一台任意设备(PC或移动设备)创建连接,即可访问树莓派的服务了,如Flood10.66.66.2:3001,Jellyfin10.66.66.2:8096,Resilio10.66.66.2:8888
  • 可在任意组网终端使用SSH连接管理树莓派ssh [email protected]
  • 可在任意终端使用resilio上传种子文件至/watch文件夹中,自动触发下载任务;
  • 可任意终端使用resilio上传多媒体文件至Jellyfin监控的文件中;
前一篇 Hugo博客快速搭建... 随机阅读 自助更... 后一篇