_
2024年3月18日 1:33:09 PM
4961 字
11 分钟

minaplay

写了一个基于 RSS 订阅自动追番/追剧的工具,加上了同步放映的功能。

GitHub 传送门
文档传送门

干嘛用的?

先说一下之前的追番体验,之前很长一段时间都是在某些网站上在线看的视频,但是大部分网站的观看体验并不好,主要问题集中在:

  • 网站页面东西太多太杂,还带着很多 AdBlock 搞不定的广告。
  • 视频画质忽高忽低,能看的高画质视频有些时候加载又会很慢导致体验大打折扣。
  • 我想看的番剧网站上面不一定有更新,导致还要多找几个备选的网站。

其中不乏有一些做的相对比较好的网站,但是与其在网站上面靠运气抽奖,不如自己找 BT 资源下载下来。

后来开始在一些 RSS 资源站上下载视频资源。 这种方式确实规避了很多在线网站的不足,但是使用起来相对比较麻烦,时不时还会忘记自己看到第几集导致下载了错误的资源。

于是自己做一个追番工具的想法油然而生,刚好本科毕业设计时实现了一个多人同步观影系统,干脆在此基础上加上了 RSS 自动订阅下载的功能。 它现在可以:

  • 通过添加的 RSS 订阅源和订阅规则自动下载媒体文件,配合整理规则整理成对应剧集。
  • 多人同步实时观影,包含了文字聊天、多人语音等互动方式。
  • 网页端直接看下载好的视频,自带了 ASS 字幕渲染的支持,HEVC 要靠系统本身支持。
  • 多用户分权限使用,搭建后不只可以一个人单独使用,也可供一个团队使用。
  • 支持插件,虽然不晓得到底会不会有人开发。

这个项目是2022年6月份开始的,在此之前已近有一些类似的项目了。这里放出来大家也可以看看。

AutoBangumi
ani-cli
弹弹play

长啥样?

在宽屏设备下截了一些图片,手机等窄屏设备上有额外的显示样式。 图片分辨率较高,没有经过压缩,手机设备放大查看更清楚。

预览图片

用在哪些场景?

我在腾讯云刚好有一台 2C8G 150GB 的闲置服务器,不用也是浪费,刚好可以部署这个项目自动追番。 配置好 RSS 订阅源和订阅规则,番剧更新的时候直接下载到服务器上,下载完成后通过 email 通知我,之后打开网页就可以直接在线看番了。

大多时候都是在手机上看的视频,但是手机浏览器直接播放实在是不太方便,于是直接用 VLC 代替网页播放器的任务了。

MinaPlay 的使用场景大致可以归为两类:自己的追番/追剧库或者是拉上朋友同步看番吐槽。

对于大多数人来说,这个项目可以部署在自己使用的主机上,代替自己手动追番/追剧的各项操作。 如果你手上刚好有台 nas,那把项目部署在 nas 上可以随时随地享受追番/追剧自由。

想要多人同步观影对环境的要求相对要高一点,可能需要宿主机的网络环境有足够大的带宽; 如果要使用多人语音服务,还有必要提供公网 IP 或是通过组网工具进行组网。

该怎么用?

首先用 Docker 部署到服务器/本地主机上,实在不推荐手动克隆源码编译部署,里面的坑太多。

详细的部署方式可以直接到 用户手册 查看。

这里只说 Docker Compose 的部署方式,将下面的配置写到文件 docker-compose.yml 里面。

version: '3.8'

services:
  minaplay-mysql:
    image: "mysql:8"
    container_name: minaplay-mysql
    networks:
      - minaplay-network
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ALLOW_EMPTY_PASSWORD=yes
      - MYSQL_DATABASE=minaplay
    restart: always

  minaplay-redis:
    image: "redis:latest"
    container_name: minaplay-redis
    networks:
      - minaplay-network
    restart: always

  minaplay:
    image: "nepsyn/minaplay:latest"
    container_name: minaplay
    networks:
      - minaplay-network
    volumes:
      - ./data:/app/data
    environment:
      - DB_HOST=minaplay-mysql
      - REDIS_HOST=minaplay-redis
      - MS_ANNOUNCED_IP=127.0.0.1  # 在需要放映室语音通话服务的情况下改为宿主机外部访问 IP
    ports:
      - "3000:3000"
      - "12000-12999:12000-12999"
    depends_on:
      - minaplay-mysql
      - minaplay-redis
    restart: unless-stopped

networks:
  minaplay-network:
YAML

程序有很多的配置项,通过环境变量配置,其中大多都不需要手动配置。 这里只着重说几个常用的配置,可以根据需要修改。

参数名说明默认值
APP_SECRET_KEY应用程序密钥(缺省时自动生成)缺省
APP_HTTP_PROXY应用程序代理地址缺省
APP_ENABLE_CORS是否允许跨域请求1
MS_ANNOUNCED_IP宿主机外网 IP (用于 WebRTC 语音服务)127.0.0.1
NOTIFY_EMAIL是否启用电子邮件通知0
NOTIFY_EMAIL_SMTP_HOSTSMTP 地址mail.example.com
NOTIFY_EMAIL_SMTP_PORTSMTP 端口25
NOTIFY_EMAIL_SMTP_SECURESMTP 是否使用安全协议0
NOTIFY_EMAIL_SMTP_USERSMTP 用户no-reply@example.com
NOTIFY_EMAIL_SMTP_PASSWORDSMTP 密码password
NOTIFY_EMAIL_ORIGINSMTP 发信来源MinaPlay <minaplay@example.com>
NOTIFY_EMAIL_SUBJECTSMTP 发信主题MinaPlay Email Notification

之后用 Docker Compose 加载 docker-compose.yml 文件。

docker compose up -d
SHELL

启动完成后,在部署环境下访问 http://127.0.0.1:3000 就可以看到 MinaPlay 的登录界面了。

超级用户的用户名和密码在首次启动时会在日志中输出,可以通过命令查看。

docker logs minaplay
SHELL

日志中输出的超级用户的用户名和密码类似:

[Nest] 14  - 02/28/2024, 3:25:37 PM     LOG [UserManagerPlugin] Default root user created, username: minaplay, password: xxxxxxx
TEXT

在网页上登录之后就可以愉快地使用啦~

怎么实现的?

MinaPlay 的实现分为了服务器端和 web 应用端。这里写一下主要功能的实现思路和手段,具体的源码实现欢迎参考 GitHub 仓库

服务器端

整体用 NestJS + TypeORM 编写的服务器端代码。

NestJS 真的是 TypeScript 做后端开发的黄金搭档!

RSS 订阅自动下载

这部分实现比较简单,使用 @nestjs/schedule 库在固定周期读取 RSS 订阅源的内容, 结合用户自己编写的订阅规则代码判断媒体资源是否需要下载,并在下载后通过自定义的规则进行整理。 具体的实现流程如下:

  1. 定期检查 RSS 订阅源内容。
  2. 针对每个 RSS 订阅源的单个项目,检查数据库中是否已经下载过此项目,未下载过则继续。
  3. 依次匹配该 RSS 订阅源对应的所有订阅规则,如果命中下载规则则继续。
  4. 将匹配的 RSS 订阅项目发送到 Aria2 下载器。
  5. 下载完成后,根据订阅规则整理此媒体资源,并通过 FFMpeg 读取媒体文件元信息、生成海报图片。
  6. 整理完成后,通过 Email 等方式通知用户媒体文件更新。

实际的开发过程中,用到了 @nestjs/bull 等库做任务队列,保证应用程序的性能。

多人放映室

多人放映室的视频直播用了多种方式实现,但是无论哪种方式都对客户端有硬性的网络要求。

  1. 客户端同步
    这是最简单的同步方式,房主只需要选择媒体文件,多个客户端同时加载这个媒体文件。 房主通过 Socket.io 向同一放映室的所有用户发送媒体文件信息、播放地址、开始播放时间等消息,各个客户端根据收到的消息同步播放进度。 这种方式足够简单,但是由于是客户端自行同步,可能导致部分客户端出现播放进度跳跃的现象。
  2. 服务端推流
    顾名思义,通过服务端的 FFMpeg 将媒体推流到 MinaPlay 自带的 Node-Media-Server 服务上,各个客户端通过拉流的方式保持进度同步。 这种方式可以一定程度保证各个客户端播放进度的一致性,但对部署环境的网络带宽要求非常高,通常的云服务器可能没有提供较高的上行带宽。
  3. 第三方直播流
    最直接的同步方式,直接使用第三方的直播流,例如使用 SRS 架设了单独的直播服务器的情况。

多人语音

多人语音通话是使用 mediasoup 基于 WebRTC SFU 模式实现的。

使用 Yarn 安装 mediasoup 相当麻烦,希望各位读者职业生涯的项目开发中不会遇到这种情况。

mediasoup 是一个 WebRTC SFU 框架,可以直接作为 Node.js 的模块使用。 使用 mediasoup 时需要手动实现一个信令服务,MinaPlay 中使用 Socket.io 作为信令服务的载体。

插件系统

NestJS 并没有原生的插件系统,MinaPlay 中通过 NestJS 提供的 LazyModuleLoader 类实现了动态插件服务。

具体的实现方案是:将插件写成 NestJS Module 并移动到专门的插件目录下。 MinaPlay 在启动时遍历插件目录下的文件,如果文件名为 xxx.plugin.[m]ts ,则加载该文件并遍历模块的导出(exports)内容,如果发现了 NestJS Module,则通过 LazyModuleLoader#load 方法加载该模块。 插件拥有和 NestJS Module 相同的特性,但需要额外定义 MinaPlay 插件信息的元数据。

需要注意的是,如果插件的目录下存在 node_modules 文件夹并且安装了 MinaPlay 的开发库,会导致模块加载时所引用的类的上下文与 MinaPlay 运行时不一致,导致插件运行时出错。 对此的解决方法是使用 Node.js 的 resolve hook 技巧将插件中所有以 @minaplay/server 开头的导入替换为当前运行时的绝对路径引用。

web 端

Vue + Vuetify + Vite 构建的应用程序界面。

其他也用到了 vue-i18nvue-routerpinia 等 vue 项目常用的配套工具。

没人能拒绝 3V 组合带来的开发效率提升!

ASS 字幕渲染

首先考虑的是使用 FFMpeg.js 或是 FFMpeg.wasm 等库来实现字幕的转换与渲染,但是 FFMpeg 二进制文件的下载开销往往是前端页面不乐于见到的。

之后在 GitHub 找到一款十分优秀的 ASS/SSA 字幕渲染工具叫 jassub ,配置容易用起来简单顺手。

视频和直播播放器

在众多的开源播放器项目中选择了 plyr ,虽然功能没有 Video.js 强大,但用很起来方便。

直播为了支持 FLV 和 HLS 两种格式,分别使用了 mpegts.jshls.js

有问题?要催更?

建了一些群组以防失联:

QQ群

MinaPlay 个人追番/追剧媒体库

作者Nepsyn
发布于2024年3月18日
许可协议