Nginx
upstream emby-backend {
server 127.0.0.1:8096;
keepalive 10240;
}
server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name domain.com;
index index.html;
ssl_certificate /path/to/certificate.pem;
ssl_certificate_key /path/to/certificate_key.pem;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=15552000; preload" always;
error_page 497 https://$host$request_uri;
location ~ \.well-known {
allow all;
}
client_body_buffer_size 512k;
client_max_body_size 20M;
add_header 'Referrer-Policy' 'origin-when-cross-origin';
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
keepalive_timeout 120s;
keepalive_requests 10240;
proxy_hide_header X-Powered-By;
proxy_buffer_size 32k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
proxy_connect_timeout 1h;
proxy_send_timeout 1h;
proxy_read_timeout 1h;
location = / {
return 302 web/index.html;
}
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Accept-Encoding "";
proxy_http_version 1.1;
location / {
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_cache off;
}
location /web/ {
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
# 如果你使用cloudflare或其他cdn缓存js、css等文件,可取消下一行的注释来关闭本机缓存
# proxy_cache off;
proxy_cache_revalidate on;
proxy_cache_background_update on;
}
location = /embywebsocket {
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "upgrade";
proxy_cache off;
}
location /emby/videos/ {
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_cache off;
proxy_buffering off;
}
access_log off;
error_log off;
}
缓存分为普通文件缓存和slice分片缓存。
开启slice缓存的作用:
1. 可在播放时提前缓存后几十秒到几分钟的视频文件,所以可实现向后小距离秒调进度。(对于视频源使用rclone挂载的服务器非常有效)
2. 多用户播放热门视频资源只需要请求一起emby或rclone,大大减轻服务器负载和速度。
3. 如果视频需要实时转码,那么同一个转码配置(如同一个码率)只需要转码一次则会被缓存,那么下一次观看不需要实时转码。
4. 只要是缓存过的视频都能实现秒播,且调进度也不会卡顿。
5. 如果你有多条线路反向代理源emby服务器,那么强烈推荐在反向代理服务器上部署视频流缓存。
开启emby的视频slice缓存需要nginx拥有slice插件,可以运行命令
nginx -V
查看输出的
configure arguments
是否有
--with-http_slice_module
这一段参数,如果有则代表可以开启视频流缓存,如果没有需要重新编译nginx并添加此参数即可。
具体配置如下:
# 首先定义缓存位置和缓存大小(定义在http模块中,与upstream和server同级):
# proxy_cache_path 缓存位置(需要提前创建好目录,并赋予nginx的读写权限)
# keys_zone 的值和后面反向代理里面的 proxy_cache 的值要对应一样
# inactive 缓存时间
# max_size 最大缓存大小
# use_temp_path 设置为off则直接将缓存文件写入proxy_cache_path,否则会先写入temp_path,缓存完成后再移动到proxy_cache_path,这样可以保证当缓存出现错误的时候不会出现在缓存文件夹中。注意,如果使用slice缓存,请不要设置use_temp_path为off!!!
proxy_cache_path /var/cache/nginx/emby-videos levels=1:2 keys_zone=emby-videos:100m inactive=15d max_size=500g;
# 然后定义相关变量在http模块中:
map $request_uri $h264Level {
~(h264-level=)(.+?)& $2;
}
map $request_uri $h264Profile {
~(h264-profile=)(.+?)& $2;
}
# 接下来添加反向代理配置,添加到上面配置好的反向代理配置的最后即可(server模块中)
location ~ ^/emby/videos/(?<Id>\d+)/stream(?:\.?(?<Container>\w*)) {
slice 16m;
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header Range $slice_range;
proxy_ignore_headers Expires Cache-Control Set-Cookie X-Accel-Expires;
proxy_connect_timeout 15s;
proxy_cache emby-videos;
proxy_cache_bypass $no_cache;
proxy_no_cache $no_cache;
proxy_cache_valid 200 206 301 302 7d;
proxy_cache_lock on;
proxy_cache_lock_age 60s;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
if ($Container = "") {
set $container $arg_Container;
}
if ($Container != "") {
set $container $Container;
}
if ($container = "") {
set $no_cache 1;
}
set $cache_key "Id=$Id&Container=$container&MediaSourceId=$arg_MediaSourceId";
if ($arg_Static != "true") {
set $cache_key "$cache_key&VideoCodec=$arg_VideoCodec&AudioCodec=$arg_AudioCodec&AudioStreamIndex=$arg_AudioStreamIndex&AudioSampleRate=$arg_AudioSampleRate&VideoStreamIndex=$arg_VideoStreamIndex&Width=$arg_Width&Height=$arg_Height&MaxWidth=$arg_MaxWidth&MaxHeight=$arg_MaxHeight&ManifestSubtitles=$arg_ManifestSubtitles&VideoBitrate=$arg_VideoBitrate&AudioBitrate=$arg_AudioBitrate&SubtitleStreamIndex=$arg_SubtitleStreamIndex&SubtitleMethod=$arg_SubtitleMethod&AudioChannels=$arg_AudioChannels&MaxAudioChannels=$arg_MaxAudioChannels&Framerate=$arg_Framerate&MaxFramerate=$arg_MaxFramerate&h264-profile=$h264Profile&h264-level=$h264Level";
# 如果出现bug可以尝试解除下一行开头的注释
# set $no_cache 1;
}
proxy_cache_key "$cache_key&slicerange=$slice_range";
}
# 同样在http模块中添加此缓存定义配置(建议提前创建好目录,并赋予nginx的读写权限)
proxy_cache_path /var/cache/nginx/emby levels=1:2 keys_zone=emby:100m max_size=25g inactive=30d use_temp_path=off;
# 接下来添加反向代理配置,添加到上面配置好的反向代理配置的最后即可
location ~ ^/emby/Items/.*/Images/ {
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header If-None-Match $http_if_none_match;
proxy_cache emby;
proxy_cache_key $request_uri;
proxy_cache_revalidate on;
proxy_cache_background_update on;
proxy_cache_lock on;
}
# 同样在http模块中添加此缓存定义配置(建议提前创建好目录,并赋予nginx的读写权限)
proxy_cache_path /var/cache/nginx/emby-audios levels=1:2 keys_zone=emby-audios:100m inactive=15d max_size=1g;
location ~ ^/emby/Audio/(?<Id>\d+)/universal(?:\.?(?<Container>\w*)) {
slice 8m;
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header Range $slice_range;
proxy_ignore_headers Expires Cache-Control Set-Cookie X-Accel-Expires;
proxy_connect_timeout 15s;
proxy_cache emby-audios;
proxy_cache_bypass $no_cache;
proxy_no_cache $no_cache;
proxy_cache_valid 200 206 301 302 7d;
proxy_cache_lock on;
proxy_cache_lock_age 60s;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
if ($Container = "") {
set $container $arg_Container;
}
if ($Container != "") {
set $container $Container;
}
if ($container = "") {
set $no_cache 1;
}
set $cache_key "Id=$Id&Container=$container&MaxStreamingBitrate=$arg_MaxStreamingBitrate&MaxSampleRate=$arg_MaxSampleRate&TranscodingProtocol=$arg_TranscodingProtocol&TranscodingContainer=$arg_TranscodingContainer&AudioCodec=$arg_AudioCodec&EnableRedirection=$arg_EnableRedirection&EnableRemoteMedia=$arg_EnableRemoteMedia&StartTimeTicks=$arg_StartTimeTicks";
proxy_cache_key "$cache_key&slicerange=$slice_range";
}
proxy_cache_path /var/cache/nginx/emby-videos levels=1:2 keys_zone=emby-videos:100m inactive=15d max_size=500g;
map $request_uri $h264Level {
~(h264-level=)(.+?)& $2;
}
map $request_uri $h264Profile {
~(h264-profile=)(.+?)& $2;
}
proxy_cache_path /var/cache/nginx/emby levels=1:2 keys_zone=emby:100m max_size=25g inactive=30d use_temp_path=off;
proxy_cache_path /var/cache/nginx/emby-audios levels=1:2 keys_zone=emby-audios:100m inactive=15d max_size=1g;
upstream emby-backend {
server 127.0.0.1:8096;
keepalive 10240;
}
server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name domain.com;
index index.html;
ssl_certificate /path/to/certificate.pem;
ssl_certificate_key /path/to/certificate_key.pem;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=15552000; preload" always;
error_page 497 https://$host$request_uri;
location ~ \.well-known {
allow all;
}
client_body_buffer_size 512k;
client_max_body_size 20M;
add_header 'Referrer-Policy' 'origin-when-cross-origin';
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
keepalive_timeout 120s;
keepalive_requests 10240;
proxy_hide_header X-Powered-By;
proxy_buffer_size 32k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
proxy_connect_timeout 1h;
proxy_send_timeout 1h;
proxy_read_timeout 1h;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Accept-Encoding "";
proxy_http_version 1.1;
location = / {
return 302 web/index.html;
}
location / {
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_cache off;
}
location /web/ {
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
# 如果你使用cloudflare或其他cdn缓存js、css等文件,可取消下一行的注释来关闭本机缓存
# proxy_cache off;
proxy_cache_revalidate on;
proxy_cache_background_update on;
}
location = /embywebsocket {
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "upgrade";
proxy_cache off;
}
location /emby/videos/ {
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_cache off;
proxy_buffering off;
}
location ~ ^/emby/videos/(?<Id>\d+)/stream(?:\.?(?<Container>\w*)) {
slice 16m;
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header Range $slice_range;
proxy_ignore_headers Expires Cache-Control Set-Cookie X-Accel-Expires;
proxy_connect_timeout 15s;
proxy_cache emby-videos;
proxy_cache_bypass $no_cache;
proxy_no_cache $no_cache;
proxy_cache_valid 200 206 301 302 7d;
proxy_cache_lock on;
proxy_cache_lock_age 60s;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
if ($Container = "") {
set $container $arg_Container;
}
if ($Container != "") {
set $container $Container;
}
if ($container = "") {
set $no_cache 1;
}
set $cache_key "Id=$Id&Container=$container&MediaSourceId=$arg_MediaSourceId";
if ($arg_Static != "true") {
set $cache_key "$cache_key&VideoCodec=$arg_VideoCodec&AudioCodec=$arg_AudioCodec&AudioStreamIndex=$arg_AudioStreamIndex&AudioSampleRate=$arg_AudioSampleRate&VideoStreamIndex=$arg_VideoStreamIndex&Width=$arg_Width&Height=$arg_Height&MaxWidth=$arg_MaxWidth&MaxHeight=$arg_MaxHeight&ManifestSubtitles=$arg_ManifestSubtitles&VideoBitrate=$arg_VideoBitrate&AudioBitrate=$arg_AudioBitrate&SubtitleStreamIndex=$arg_SubtitleStreamIndex&SubtitleMethod=$arg_SubtitleMethod&AudioChannels=$arg_AudioChannels&MaxAudioChannels=$arg_MaxAudioChannels&Framerate=$arg_Framerate&MaxFramerate=$arg_MaxFramerate&h264-profile=$h264Profile&h264-level=$h264Level";
# 如果出现bug可以尝试解除下一行开头的注释
# set $no_cache 1;
}
proxy_cache_key "$cache_key&slicerange=$slice_range";
}
location ~ ^/emby/Items/.*/Images/ {
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header If-None-Match $http_if_none_match;
proxy_set_header Accept-Encoding "";
proxy_cache emby;
proxy_cache_key $request_uri;
proxy_cache_revalidate on;
proxy_cache_background_update on;
proxy_cache_lock on;
}
location ~ ^/emby/Audio/(?<Id>\d+)/universal(?:\.?(?<Container>\w*)) {
slice 8m;
proxy_pass http://emby-backend;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header Range $slice_range;
proxy_ignore_headers Expires Cache-Control Set-Cookie X-Accel-Expires;
proxy_connect_timeout 15s;
proxy_cache emby-audios;
proxy_cache_bypass $no_cache;
proxy_no_cache $no_cache;
proxy_cache_valid 200 206 301 302 7d;
proxy_cache_lock on;
proxy_cache_lock_age 60s;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
if ($Container = "") {
set $container $arg_Container;
}
if ($Container != "") {
set $container $Container;
}
if ($container = "") {
set $no_cache 1;
}
set $cache_key "Id=$Id&Container=$container&MaxStreamingBitrate=$arg_MaxStreamingBitrate&MaxSampleRate=$arg_MaxSampleRate&TranscodingProtocol=$arg_TranscodingProtocol&TranscodingContainer=$arg_TranscodingContainer&AudioCodec=$arg_AudioCodec&EnableRedirection=$arg_EnableRedirection&EnableRemoteMedia=$arg_EnableRemoteMedia&StartTimeTicks=$arg_StartTimeTicks";
proxy_cache_key "$cache_key&slicerange=$slice_range";
}
access_log off;
error_log off;
}
CloudFlare
众所周知,Cloudflare不允许你的视频流大量经过他们的服务器,即使你设置了绕过缓存,也算流量经过Cloudflare服务器:Section 2.8 of the Self-Serve Subscription Agreement (TOS)
但是我们希望html,css,js等文件经过Cloudflare的路由优化和缓存,那怎么办呢?
解决办法
我们可以使用两个域名,主域名开启CDN代理,次域名不开启代理,当使用主域名访问EMBY的视频流文件使,使用307重定向到次域名即可。
准备工作:
主域名 emby.domain.com 开启cdn,次域名 src-emby.domain.com 不开启cdn
主/次域名均已经使用Nginx反向代理到EMBY服务器
Cloudflare的速度->优化->Auto Minify功能全部关闭,否则会导致各种意想不到的Bug
Cloudflare的速度->优化->Rocket Loader™也要关掉,不然会导致很多js莫名其妙的Bug
反向代理Emby后如果启用了https,则需要在emby的 网络 -> 安全连接模式 选项中选择由反向代理处理
开启cdn后可正常访问emby服务器
使用Cloudflare的重定向规则实现重定向
打开Cloudflare规则->重定向规则,新建一个规则,规则名称随意
规则填写(替换域名即可),注意大小写:
#表达式预览(可以复制粘贴到Cloudflare)
(http.host eq "emby.domain.com" and starts_with(http.request.uri.path, "/emby/videos")) or (http.host eq "emby.domain.com" and starts_with(http.request.uri.path, "/emby/Items") and ends_with(http.request.uri.path, "/Download"))
concat("https://src-", http.host, http.request.uri)
保存后,所有视频流的请求都会307重定向到直连源服务器的地址,就不用担心被Cloudflare查封了。
Cloudflare缓存配置
进入Cloudflare 缓存 -> Cache Rules
创建一个规则,传入规则如下:
缓存状态 选 符合缓存条件
边缘 TTL 选 接受源服务器
状态代码 TTL 分别设置为 单一代码 – 200/206 – 一年
缓存键 开启 缓存欺骗盔甲 和 启用查询字符串排序
测试
随便点开一个视频播放几秒,然后查看缓存文件夹内有没有生成文件即可
暂无评论内容