Emby媒体库302重定向:Openlist+115网盘+go-emby2openlist

最简单的302重定向方法,进一步优化媒体库,让外网也能无须转码观看蓝光原盘。使用Openlist+Emby+Clouddrive+go-emby2openlist+autosymlink的完整解决方案。

原文体验更佳:hiripple.com/zh/blog/emb... Emby媒体库302重定向:Openlist+115网盘+go-emby2openlist

注:本博文基于Strm+Emby=无痛刮削,应对网盘风控新策略的后续内容,强烈建议先阅读前篇博文。

前言

在部署完Emby+strm方案后,外网播放高达几十GB的蓝光原盘仍然是个难题。出门在外想看电影,只能提前下载好,确实有些麻烦。

目前最佳的解决方案是使用302重定向,让客户端直接向115网盘的CDN请求资源,完全绕过家庭/本地服务器的转发开销。这样一来,电影的传输带宽仅受限于网速,而非服务器上传带宽。

当然,这种方案也存在一些权衡:

  • 302重定向的播放启动速度略逊于本地转发

  • 115账号存在被封风险(个人自用概率较低,但需避免多IP多线程下载)

  • 部署流程相对复杂

网上虽然教程不少,但很多是复制粘贴的旧版方案,大部分停留在24年的过时方法,比如老旧的alist+nginx组合。这些方案不仅复杂繁琐,而且缺乏对ISO格式的刮削支持。

因此,本文的目标是提供最简洁的302重定向部署教程,同时保留原有的本地转发能力,实现iOS原盘刮削,并支持电影自动同步与刮削。最终达到在家享受本地转发的快速启动,在公网环境也能302流畅观影的双重体验。

市面上确实有更简单的一站式方案,比如Symmedia: www.symedia.top, 它集成了strm链接与302emby功能。不过这是非开源的付费项目(¥179),是否选择就看个人需求了。

Openlist 部署

Alist被收购后,Openlist成为了绝佳的替代方案:github.com/OpenListTea...

Openlist的作用很明确:提供302重定向能力,满足外网访问需求。这是Clouddrive无法胜任的功能。

使用Docker可以轻松部署(以Unraid为例):

yaml 复制代码
services:

openlist:

image: 'openlistteam/openlist:latest'

container_name: openlist

user: '0:0'

volumes:

# 将容器数据目录映射到Unraid的appdata共享区

- '/mnt/user/appdata/openlist:/opt/openlist/data'

ports:

- '5244:5244'

environment:

- UMASK=022

- TZ=Asia/Shanghai

restart: unless-stopped

容器启动后,访问 http://localhost:5244,按照官方文档完成登录→添加115网盘→获取API Key(后续步骤需要用到)。

go-emby2openlist 配置

官方地址:github.com/AmbitiousJu...

go-emby2openlist是一个Go语言编写的emby反向代理服务,功能强大且使用便捷。它可以反向代理emby页面,智能拦截并重定向电影资源路径(从本地路径转换为webdav 302重定向)。

引用官方说明:

网盘直链反向代理机制

传统模式下,Emby通过磁盘挂载间接读取网盘资源,采用服务器代理模式,数据流向为:

客户端 → Emby源服务器 → 磁盘挂载服务 → OpenList → 网盘
客户端 ← Emby源服务器 ← 磁盘挂载服务(将视频数据缓存到本地,供Emby读取) ← OpenList ← 网盘

这种模式的局限性显而易见:

  1. 视频需经过服务器中转,观看速度受限于服务器上传带宽

  2. 服务器性能不足时,能流畅播放1080p已属不易,4K更是奢望

  3. 服务器流量和资源消耗巨大

使用网盘直链反向代理后,数据流向变为:

客户端 → Emby反代服务器 → Emby源服务器(请求Emby API接口)
客户端 ← Emby反代服务器 ← Emby源服务器(返回数据)

对于普通API接口,反代服务器将请求转发至源服务器,缓存适当结果后返回给客户端。从客户端角度看,这与直连源服务器无异。

客户端 → Emby反代服务器 ⇒ OpenList ⇒ 网盘(请求视频直链)
客户端 ← Emby反代服务器 ← OpenList ← 网盘(返回视频直链,发出重定向响应)
客户端 ⇒ 网盘(客户端获取网盘直链后直接观看,无需再消耗服务器流量)

同样使用Docker安装,Unraid配置示例:

yaml 复制代码
version: '3.8'

services:

go-emby2openlist:

image: ambitiousjun/go-emby2openlist:v2.2.9

container_name: go-emby2openlist

restart: always

environment:

- TZ=Asia/Shanghai

- GIN_MODE=release

ports:

# 格式: - '宿主机端口:容器端口'

# HTTP端口(8095端口预留给Autosymlink)

- '8097:8095'

# HTTPS端口(如需启用SSL)

- '8094:8094'

volumes:

# --- 核心配置 ---

# 将宿主机配置文件映射到容器中

- ./config.yml:/app/config.yml

# --- 可选挂载 ---

# SSL证书文件目录

- /mnt/user/appdata/go-emby2openlist/ssl:/app/ssl

  


# 自定义注入JS/CSS文件目录

- /mnt/user/appdata/go-emby2openlist/custom-js:/app/custom-js

- /mnt/user/appdata/go-emby2openlist/custom-css:/app/custom-css

  


# 使用"OpenList本地目录树生成"功能时的必要挂载

# ffmpeg等库文件存放位置

- /mnt/user/appdata/go-emby2openlist/lib:/app/lib

# 生成的本地目录树存放位置

- /mnt/user/appdata/go-emby2openlist/openlist-local-tree:/app/openlist-local-tree

需要说明的是,博主使用了Cloudflare Tunnel反代+autosymlink的组合,因此没有使用项目自带的本地目录树和SSL功能。另外,该项目目前似乎不支持ISO原盘的刮削,且无法在添加电影后立即更新媒体库(需使用cron定时刷新)。综合考虑,保留原有的clouddrive+autosymlink方案是更明智的选择。

方案总结:在博主的架构中,clouddrive+autosymlink负责本地转发和媒体库刮削,而openlist+go-emby2openlist实现外网302重定向。由于go-emby2openlist本质上是反向代理服务,媒体库的刮削和管理依然由前者承担。

接下来是关键步骤:路径映射配置。Unraid环境下的config.yml示例:

yaml 复制代码
# emby访问配置

emby:

# Emby服务访问地址

host: http://192.168.xxx.xxx:8096

# 以下参数保持默认或根据个人喜好调整

episodes-unplay-prior: true

resort-random-items: true

proxy-error-strategy: origin

images-quality: 100

download-strategy: direct

strm:

# strm文件内容为本地路径而非URL,因此留空

path-map:

  


# openlist访问配置

openlist:

# 填写OpenList服务的实际访问地址

host: http://192.168.xxx.xxx:5244

# 填写OpenList的API Token

token: openlist-xxx

  


# 当前使用自建strm工具,暂时禁用此功能

local-tree-gen:

enable: false

ffmpeg-enable: false

virtual-containers:

strm-containers: mkv,mp4,mov,ts,avi,webm,iso

music-containers:

auto-remove-max-count: 10000

refresh-interval: 60

scan-prefixes:

- /115/云下载

- /115/剧集

ignore-containers: jpg,jpeg,png,txt,nfo,md

  


# 视频预览刮削

# 非阿里云盘用户建议禁用以减少不必要的请求

video-preview:

enable: false

containers:

- mp4

- mkv

ignore-template-ids:

- LD

- SD

  


# 路径映射(核心配置)

path:

# emby挂载路径与openlist真实路径的前缀映射

# 程序会提取strm文件中的路径 /mnt/user/embydata/local/movie/云下载/阿凡达.mkv

# 使用下方规则进行替换,将前缀 /mnt/user/embydata/local 替换为 /

# 最终得到OpenList路径:/movie/云下载/阿凡达.mkv

emby2openlist:

- /mnt/user/embydata/local/movie:/115

- /mnt/user/embydata/local/tv:/115

  


# 缓存配置

cache:

enable: true

expired: 1d

  


# SSL配置

ssl:

# 未配置HTTPS,保持禁用状态

enable: false

single-port: false

key: testssl.cn.key

crt: testssl.cn.crt

  


# 日志配置

log:

disable-color: false

重要提醒:确保config.yml的路径与docker-compose.yml中的映射路径完全一致,否则容器将无法正常启动。

最后执行 docker compose up -d,访问 http://localhost:8097 即可看到与emby完全相同的Web界面。此时下载/播放视频的地址会被302重定向至115的CDN服务器。

小技巧 :go-emby2openlist支持注入外部JS/CSS,推荐注入 emby-external-url.7o7o.cc/embyWebAddE... 实现直接跳转到本地播放器的功能。

错误排查

如果出现"没有兼容的流"错误,请按以下步骤排查:

  1. 首先访问emby源地址 http://localhost:8096,如果源服务异常,说明是autosymlink与emby容器的路径配置问题,请参考前篇博文进行排查。

  2. 如果源服务正常,大概率是config.yaml中的路径映射配置错误。您需要检查webdav的实际地址与config.yaml中规则的匹配度。查看go-emby2openlist的容器日志可以获取实际请求路径信息,据此调整为正确的真实路径即可。

ISO刮削

这部分内容与之前的博文基本一致,在Unraid系统中调用ffprobe进行ISO刮削。现在Ripp已将JSON格式化接口开放为公共API,大家可以自由使用。

脚本示例:

bash 复制代码
#!/bin/bash

  


# 设置脚本在遇到错误时退出

set -euo pipefail

  


# 定义目录路径

FFPROBE_DIR="/mnt/user/embydata/ffmpeg-master-latest-linux64-gpl/bin"

LINKS_DIR="/mnt/user/embydata/links"

TEMP_DIR="/mnt/user/embydata/temp"

MOVIES_DIR="/mnt/user/embydata/local/movie/云下载"

API_URL="http://probe.hiripple.com/format"

  


# 定义锁文件路径,使用/tmp目录避免权限问题

LOCK_FILE="/tmp/process_strm.lock"

  


# 函数:释放锁文件

cleanup() {

rm -f "$LOCK_FILE"

exit

}

  


# 检查是否已有实例在运行

if [ -e "$LOCK_FILE" ]; then

echo "锁文件已存在,脚本可能正在运行。退出。"

exit 1

fi

  


# 创建锁文件

touch "$LOCK_FILE"

  


# 确保在脚本退出时删除锁文件

trap cleanup EXIT

  


# 进入ffprobe所在目录

echo "进入目录: $FFPROBE_DIR"

cd "$FFPROBE_DIR" || { echo "无法进入目录 $FFPROBE_DIR"; exit 1; }

  


if [ ! -d "$TEMP_DIR" ]; then

echo "创建temp目录: $TEMP_DIR"

mkdir -p "$TEMP_DIR"

fi

  


# 遍历所有.strm文件

echo "扫描目录: $LINKS_DIR 以查找.strm文件"

find "$LINKS_DIR" -maxdepth 1 -type f -name "*.strm" | while read -r strm_file; do

# 提取电影名(不带.strm扩展名)

filename=$(basename "$strm_file" .strm)

echo "处理电影: $filename"

  


# 定义路径

mediainfo_json_link="${LINKS_DIR}/${filename}-mediainfo.json"

mediainfo_json_temp="${TEMP_DIR}/${filename}-mediainfo.json"

iso_file="${MOVIES_DIR}/${filename}.iso"

  


# 检查是否已存在mediainfo.json

if [ -f "$mediainfo_json_link" ]; then

echo "已存在mediainfo.json文件,跳过: $mediainfo_json_link"

continue

fi

  


# 检查ISO文件是否存在

if [ ! -f "$iso_file" ]; then

echo "ISO文件不存在,跳过: $iso_file"

continue

fi

  


# 执行ffprobe命令,生成mediainfo.json

echo "运行ffprobe生成mediainfo.json文件"

set +e # 临时禁用错误退出,以处理可能的错误

./ffprobe -v error \

-print_format json \

-show_format \

-show_streams \

-show_chapters \

-show_programs \

bluray:"$iso_file" > "$mediainfo_json_temp"

ffprobe_status=$?

set -e # 重新启用错误退出

  


# 检查ffprobe是否成功以及文件是否创建

if [ $ffprobe_status -ne 0 ] || [ ! -s "$mediainfo_json_temp" ]; then

echo "ffprobe执行失败或文件创建失败,跳过电影: $filename"

continue

fi

  


# 调用API格式化JSON

echo "调用API进行格式化"

response=$(curl -s -X POST "$API_URL" \

-H "Content-Type: application/json" \

-d @"$mediainfo_json_temp")

  


# 检查API调用是否成功

if [ $? -ne 0 ] || [ -z "$response" ]; then

echo "API调用失败或无响应,跳过电影: $filename"

continue

fi

  


# 保存API返回的格式化JSON到LINKS_DIR

echo "保存格式化后的JSON到 $mediainfo_json_link"

echo "$response" > "$mediainfo_json_link"

  


# 等待5秒

echo "等待5秒后处理下一个文件..."

sleep 5

  


echo "完成处理电影: $filename"

echo "----------------------------------------"

  


done

  


echo "所有.strm文件处理完成。"

在Emby中,如果电影缺少时长信息,将无法保存播放进度。因此,对ISO文件进行刮削显得尤为重要。

小结

最后的内网穿透可以直接使用Cloudflare Tunnel,这样通过公网访问自己的域名就能流畅播放影片了。

如果在部署过程中遇到任何问题,欢迎在下方留言交流~

相关推荐
IT_陈寒26 分钟前
Python开发者必知的5个高效技巧,让你的代码速度提升50%!
前端·人工智能·后端
zm43542 分钟前
浅记Monaco-editor 初体验
前端
超凌1 小时前
vue element-ui 对表格的单元格边框加粗
前端
前端搬运侠1 小时前
🚀 TypeScript 中的 10 个隐藏技巧,让你的代码更优雅!
前端·typescript
CodeTransfer1 小时前
css中animation与js的绑定原来还能这样玩。。。
前端·javascript
liming4951 小时前
运行node18报错
前端
20261 小时前
14.7 企业级脚手架-制品仓库发布使用
前端·vue.js
coding随想1 小时前
揭秘HTML5的隐藏开关:监控资源加载状态readyState属性全解析!
前端
coding随想1 小时前
等待页面加载事件用window.onload还是DOMContentLoaded,一文给你讲清楚
前端