更新文件: README.txt, rclonemount.sh, docker-compose.yml, webdav_simulator.amd64, runembysim.sh 等9个文件
This commit is contained in:
105
README.txt
105
README.txt
@ -1,75 +1,42 @@
|
||||
有缘人注意:本zip目前僅支持"影視","OK影視"使用,其他播放器或基於影視魔改的播放器使用本zip都會導致網盤内容無法播放。對本zip内的核心jar所作的任何魔改、縫合都會導致網盤原畫不可播放。
|
||||
* 本ZIP所加载的资源完全来自网上公开分享的内容,若有版权问题请联系相关网站删除,本ZIP只读取和播放网络公开资源,既不维护也不储存任何网络资源。
|
||||
1.确认宿主系统中安装了rclone
|
||||
|
||||
****************************************************************
|
||||
* 把本zip文件解壓縮到安卓設備的任意目錄 *
|
||||
* 然後在播放器的點播接口設定中,指定到解壓後目錄中的jsm.json *
|
||||
****************************************************************
|
||||
* 每次更新zip都可以覆蓋到同一個目錄,覆蓋后無需重新掃碼就可以繼續使用網盤
|
||||
* 可以使用影視的内部http服務器實現zip上傳和自動解壓,方法:用手機或PC打開http://播放器IP:9978/,
|
||||
* 然後點擊最後一個TAB(本地),然後創建一個新文件夾,例如“tvbox”,然後進入"tvbox",然後創建"js"和"lib"兩個子目錄,
|
||||
* 然後點擊“上傳檔案”,把本zip上傳到該目錄就會自動解壓。
|
||||
以Ubuntu/Debian为例:
|
||||
apt update;apt install -y rclone
|
||||
|
||||
================================================================
|
||||
以下所有说明不看也可以正常使用本zip,只是给动手能力强的有缘人更多定制化的可能性。默认设置就可以欣赏绝大部分网络资源,只需要切换到“网盘及弹幕设置”这个视频源扫不同网盘的二维码即可。(切换方法:播放器首页点击左上角图标或文字,找到“网盘及弹幕设置”点击)
|
||||
================================================================
|
||||
以openwrt为例:
|
||||
opkg update; opkg install rclone
|
||||
|
||||
提示0: 多个播放器或多次外挂本zip情况下,需要只保留一个播放器或1个外挂运行,其他的要主动杀掉,否则可能出现网盘播放异常.
|
||||
提示1:發現影视壳并不能加载最新的jar,如果遇到jar表現異常,或者最新的jar承諾的功能改進沒有實現,請清除播放殼app的緩存后强杀播放壳后再試,清除方法1:在殼app的設置裏點擊“緩存”,清除方法2:設備的應用管理中,清除殼app的數據及緩存。
|
||||
提示2:迅雷云盘限制极为严格,不要尝试单账号多用户异地使用,或多线程使用,随时可能封号。
|
||||
提示3:播放原盘ISO时,可能会呼叫外部播放器,此时需要把原播放器在任务列表中锁定,防止原播放器切入后台被杀掉,具体方法:按任务列表按钮,找到原播放器,点击图标在弹出菜单中选择锁定或点击锁头标志
|
||||
以alpine为例:
|
||||
apk add rclone
|
||||
|
||||
可以透过配置中的“網盤及彈幕配置”的視頻源來實現快捷方便的獲取32位token及opentoken的功能。
|
||||
2.使用install.sh把压缩文件展开
|
||||
|
||||
複製lib/tokentemplate.json成爲lib/tokenm.json,并填寫必要的内容
|
||||
sh install.sh
|
||||
|
||||
tokenm.json格式説明:
|
||||
{
|
||||
"token":"這裏填寫阿里云盤的32位token,也可以不填寫,在播放阿里云盤内容時會彈出窗口,點擊QrCode,用阿里云盤app掃碼",
|
||||
"open_token":"這裏填寫通過alist或其他openapi提供方申請的aliyun openapi token",
|
||||
"is_vip":true, //是否是阿里云盤的VIP用戶,設置為true后,使用vip_thread_limit設置的數值來并發加速
|
||||
"vip_thread_limit":32, //這裏是阿里云盤的轉存原畫并發綫程數
|
||||
"vip_thread_limit_night":"19-23=10", //這裏是阿里云盤的轉存原畫夜间并發綫程數, 等号前标识夜间时段,等号后标识线程数
|
||||
"quark_thread_limit":32, //這裏是夸克網盤GO代理的并發協程數或java代理的并發綫程數,若遇到賬號被限制並發數,請將此數值改爲10
|
||||
"quark_vip_thread_limit":32, //這裏是夸克網盤設置quark_is_vip:true之後的并發綫程數,若遇到賬號被限制并發數,請將此數值改爲10
|
||||
"quark_thread_limit_night":"19-23=10", //這裏是夸克網盤GO代理的夜间并發協程數或java代理的并發綫程數,若遇到賬號被限制並發數,請將此數值改爲10
|
||||
"quark_vip_thread_limit_night":"19-23=10", //這裏是夸克網盤設置quark_is_guest:false之後的夜间并發綫程數,若遇到賬號被限制并發數,請將此數值改爲10
|
||||
"quark_is_guest":false, //本項目設置爲false表示是夸克的VIP或88VIP用戶,使用更快的多綫程加載方式,設置爲true表示是純免費的夸克用戶,使用優化限速的多綫程加載方式
|
||||
"vod_flags":"4kz|auto", //這裏是播放阿里雲的畫質選項,4kz代表轉存GO原畫,4ko代表轉存Open原畫,其他都代表預覽畫質,可選的預覽畫質包括qhd,fhd,hd,sd,ld,
|
||||
"quark_flags":"4kz|auto", //這裏是播放夸克網盤的畫質選項,4kz代表轉存原畫(GO原畫),其他都代表轉碼畫質,可選的預覽畫質包括4k,2k,super,high,low,normal
|
||||
"uc_thread_limit":0,
|
||||
"uc_is_vip":false,
|
||||
"uc_flags":"4kz|auto",
|
||||
"uc_vip_thread_limit":0,
|
||||
"thunder_thread_limit":0,
|
||||
"thunder_is_vip":false,
|
||||
"thunder_vip_thread_limit":0,
|
||||
"thunder_flags":"4kz",
|
||||
"aliproxy":"這裏填寫外部的加速代理,用於在盒子性能不夠的情況下,使用外部的加速代理來加速播放,可以不填寫",
|
||||
"proxy":"這裏填寫用於科學上網的地址,連接openapi或某些資源站可能會需要用到,可以不填寫",
|
||||
"open_api_url":"https://api.xhofe.top/alist/ali_open/token", //這是alist的openapi接口地址,也可使用其他openapi提供商的地址。
|
||||
"danmu":true,//是否全局開啓阿里云盤所有csp的彈幕支持,聚合類CSP仍需單獨設置,例如Wogg, Wobg
|
||||
"quark_danmu":true,//是否全局開啓夸克網盤的所有csp的彈幕支持, 聚合類CSP仍需單獨設置,例如Wogg, Wobg
|
||||
"quark_cookie":"這裏填寫通過https://pan.quark.cn網站獲取到的cookie,會很長,全數填入即可。"
|
||||
"uc_cookie":"這裏填寫通過https://drive.uc.cn網站登錄獲取的cookie",
|
||||
"thunder_username":"這裏填入用戶名或手機號,如果是手機號,記得是類似'+86 139123457'這樣的格式,+86后有空格才對",
|
||||
"thunder_password":"密碼",
|
||||
"thunder_captchatoken":"首次使用迅雷網盤時,需要使用app彈出的登陸地址去接碼登錄,並獲取captchaToken,具體方法參考alist網站的文檔:https://alist.nn.ci/zh/guide/drivers/thunder.html",
|
||||
"pikpak_username":"PikPak網盤的用戶名",
|
||||
"pikpak_password":"PikPak網盤的密碼",
|
||||
"pikpak_flags":"4kz",
|
||||
"pikpak_thread_limit":2,
|
||||
"pikpak_vip_thread_limit":2,
|
||||
"pikpak_proxy":"用於科學上網連接PikPak網盤的代理服務器地址",
|
||||
"pikpak_proxy_onlyapi":false,
|
||||
"pan115_cookie":"",
|
||||
"pan115_thread_limit":0,
|
||||
"pan115_vip_thread_limit":0,
|
||||
"pan115_is_vip":false,
|
||||
"pan115_flags":"4kz",
|
||||
"pan115_auto_delete":true,
|
||||
"pan115_delete_code":"",
|
||||
"pan115_speed_limit":0,
|
||||
"pan115_speed_limit_mobile":10485760,
|
||||
"pan_order":"ali|quark|uc|115|yd|thunder|pikpak"
|
||||
}
|
||||
3.进入embysim目录
|
||||
|
||||
cd embysim
|
||||
|
||||
4.按照emby.external目录中的alistservers.template.txt的样子自己生成alistservers.txt
|
||||
|
||||
alistservers.txt中的内容是要指向真实的alist地址,每行一个服务器,格式类似:
|
||||
|
||||
http://192.168.1.1:5678/,,
|
||||
|
||||
英文逗号分隔字段,其中第一段是服务器地址,第二段是USER:PASS,或ALIST-TOKEN,第三段忽略。
|
||||
|
||||
5.执行runembysim.sh
|
||||
|
||||
sh runembysim.sh
|
||||
|
||||
|
||||
程序会自动探测本机IP,如果探测失败,需要到runembysim.sh和rclonemount.sh中修改SELFIP成为真实的ip
|
||||
|
||||
emby的元数据自动匹配服务连接的服务器都是被墙的,需要科学上网,所以建议embysim
|
||||
docker以bridge方式运行,因为host模式会共享主机的ip,有可能会把ipv6引入,导致翻墙困难。
|
||||
可以修改runembysim.sh,加入-e HTTP_PROXY和-e HTTPS_PROXY
|
||||
指向实际的翻墙代理,或直接在软路由上做分流,分流域名是:
|
||||
*.themoviedb.org
|
||||
*.tmdb.org
|
||||
*.thetvdb.com
|
||||
*.fanart.tv
|
||||
|
34
docker-compose.yml
Normal file
34
docker-compose.yml
Normal file
@ -0,0 +1,34 @@
|
||||
version: '3.5'
|
||||
|
||||
services:
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: embyUrl-nginx
|
||||
ports:
|
||||
- 8097:80
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./nginx/conf.d:/etc/nginx/conf.d
|
||||
restart: always
|
||||
|
||||
emby:
|
||||
image: emby/embyserver
|
||||
container_name: embysim
|
||||
sysctls:
|
||||
net.ipv6.conf.all.disable_ipv6: "1"
|
||||
environment:
|
||||
- UID=1000
|
||||
- GID=100
|
||||
- GIDLIST=100
|
||||
volumes:
|
||||
- /www/webdavsim/emby.config:/config
|
||||
- /www/webdavsim/external:/external:rshared
|
||||
- /www/webdavsim/emby.tmp:/tmp
|
||||
- /www/webdavsim/emby.cache:/cache
|
||||
ports:
|
||||
- 8096:8096
|
||||
- 8920:8920
|
||||
devices:
|
||||
- /dev/dri:/dev/dri
|
||||
restart: on-failure
|
BIN
embysim.tar.xz
BIN
embysim.tar.xz
Binary file not shown.
61
nginx/conf.d/emby.conf
Normal file
61
nginx/conf.d/emby.conf
Normal file
@ -0,0 +1,61 @@
|
||||
# Load the njs script
|
||||
js_path /etc/nginx/conf.d/;
|
||||
js_import addExternalUrl from externalUrl.js;
|
||||
|
||||
server{
|
||||
gzip on;
|
||||
listen 80;
|
||||
server_name default;
|
||||
set $emby http://172.17.0.1:8096; #emby address
|
||||
|
||||
# Proxy sockets traffic for jellyfin-mpv-shim and webClient
|
||||
location ~ /(socket|embywebsocket) {
|
||||
# Proxy Emby Websockets traffic
|
||||
proxy_pass $emby;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
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;
|
||||
}
|
||||
|
||||
location ~* /Users/(.*)/Items/(\d+)$ {
|
||||
proxy_buffering off;
|
||||
js_body_filter addExternalUrl.addExternalUrl buffer_type=string;
|
||||
proxy_pass $emby;
|
||||
proxy_pass_request_body off;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
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 Accept-Encoding "identity";
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
js_header_filter addExternalUrl.HeaderFilter;
|
||||
}
|
||||
|
||||
location ~* /redirect2external {
|
||||
js_content addExternalUrl.redirectUrl;
|
||||
}
|
||||
|
||||
location / {
|
||||
# Proxy main Emby traffic
|
||||
proxy_pass $emby;
|
||||
proxy_set_header Host $host;
|
||||
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;
|
||||
# Disable buffering when the nginx proxy gets very resource heavy upon streaming
|
||||
proxy_buffering off;
|
||||
}
|
||||
}
|
217
nginx/conf.d/externalUrl.js
Normal file
217
nginx/conf.d/externalUrl.js
Normal file
@ -0,0 +1,217 @@
|
||||
//author: @bpking https://github.com/bpking1/embyExternalUrl
|
||||
let serverAddr = 'http://192.168.101.13:8097';
|
||||
const tags = ['BluRay', 'REMUX', 'WEB-DL']; //添加视频tag
|
||||
const groups = ['CMCT', 'WIKI', 'Z0N3', 'EbP', 'PTer', 'EPSILON', 'FRDS', 'SMURF']; //添加制作组
|
||||
|
||||
|
||||
let api_key = '';
|
||||
let domain = '';
|
||||
let oriData = '';
|
||||
const redirectKey = 'redirect2external';
|
||||
|
||||
const addExternalUrl = async (r, data, flags) => {
|
||||
api_key = r.args['X-Emby-Token'] ? r.args['X-Emby-Token'] : r.headersIn['X-Emby-Token'];
|
||||
api_key = api_key ? api_key : r.args.api_key;
|
||||
|
||||
//外链地址默认http,如果是https,则需要将这里的 http 改为 https,如果是反代服务器两种都有,可以将这一行注释掉,统一使用第一行填写的地址
|
||||
serverAddr = r.headersIn.Host ? `http://${r.headersIn.Host}` : serverAddr;
|
||||
|
||||
domain = `${serverAddr}/emby/videos/${r.uri.split('Items/')[1]}`;
|
||||
r.error(`api_key: ${api_key}`);
|
||||
r.error(`domain: ${domain}`);
|
||||
|
||||
if (flags.last === false) {
|
||||
oriData += data;
|
||||
r.error(`flags.last: ${flags.last} , data.length: ${data.length}`);
|
||||
return;
|
||||
} else {
|
||||
r.error(`flags.last: ${flags.last}`);
|
||||
data = JSON.parse(oriData);
|
||||
r.error(`data.length: ${JSON.stringify(data).length}`);
|
||||
}
|
||||
|
||||
|
||||
if (data.MediaSources && data.MediaSources.length > 0) {
|
||||
try {
|
||||
data = addUrl(r, data);
|
||||
} catch (error) {
|
||||
r.error(`addUrl error: ${error}`);
|
||||
}
|
||||
}
|
||||
r.error(`addUrldata.length: ${JSON.stringify(data).length}`)
|
||||
r.sendBuffer(JSON.stringify(data), flags);
|
||||
r.done();
|
||||
}
|
||||
|
||||
const addUrl = (r, data) => {
|
||||
data.MediaSources.map(mediaSource => {
|
||||
const streamUrl = `${domain}/stream.${mediaSource.Container}?api_key=${api_key}&Static=true&MediaSourceId=${mediaSource.Id}`;
|
||||
//get subtitle
|
||||
let subUrl = '';
|
||||
try {
|
||||
subUrl = getSubUrl(r, mediaSource);
|
||||
} catch (error) {
|
||||
r.error(`get sub url error: ${error}`);
|
||||
}
|
||||
//get displayTitle
|
||||
let displayTitle = '';
|
||||
try {
|
||||
displayTitle = mediaSource.MediaStreams.find(s => s.Type === 'Video').DisplayTitle;
|
||||
displayTitle = typeof displayTitle === 'undefined' ? '' : displayTitle;
|
||||
} catch (error) {
|
||||
r.error(`get displayTitle error: ${error}`);
|
||||
}
|
||||
//get position
|
||||
const position = parseInt(data.UserData.PlaybackPositionTicks / 10000);
|
||||
//get tagName
|
||||
let tagName = '';
|
||||
try {
|
||||
tagName = tags.find(t => mediaSource.Name.toUpperCase().includes(t.toUpperCase()));
|
||||
tagName = typeof tagName === 'undefined' ? '' : tagName;
|
||||
} catch (error) {
|
||||
r.error(`get tagName error: ${mediaSource.Name}`);
|
||||
}
|
||||
//get groupName
|
||||
let groupName = '';
|
||||
try {
|
||||
groupName = groups.find(g => mediaSource.Name.toUpperCase().includes(g.toUpperCase()));
|
||||
groupName = typeof groupName === 'undefined' ? '' : groupName;
|
||||
} catch (error) {
|
||||
r.error(`get groupName error: ${mediaSource.Name}`);
|
||||
}
|
||||
const mediaInfo = {
|
||||
title: data.Name,
|
||||
streamUrl,
|
||||
subUrl,
|
||||
position,
|
||||
displayTitle,
|
||||
mediaSourceName: (tagName + groupName).length > 1 ? `${tagName}-${groupName}` : mediaSource.Name
|
||||
}
|
||||
data.ExternalUrls.push(getPotUrl(mediaInfo));
|
||||
data.ExternalUrls.push(getVlcUrl(mediaInfo));
|
||||
data.ExternalUrls.push(getIinaUrl(mediaInfo));
|
||||
data.ExternalUrls.push(getMXUrl(mediaInfo));
|
||||
data.ExternalUrls.push(getNPlayerUrl(mediaInfo));
|
||||
data.ExternalUrls.push(getInfuseUrl(mediaInfo));
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
const getPotUrl = (mediaInfo) => {
|
||||
return {
|
||||
Name: `potplayer-${mediaInfo.mediaSourceName}-${mediaInfo.displayTitle}`,
|
||||
Url: `potplayer://${encodeURI(mediaInfo.streamUrl)} /sub=${encodeURI(mediaInfo.subUrl)} /seek=${getSeek(mediaInfo.position)}`
|
||||
//双引号不能直接放,可能要base64编码一下
|
||||
//Url: `potplayer://${encodeURI(mediaInfo.streamUrl)} /sub="${encodeURI(mediaInfo.subUrl)}" /current /title="${encodeURI(mediaInfo.title)}" /seek=${getSeek(mediaInfo.position)}`
|
||||
}
|
||||
}
|
||||
|
||||
//https://wiki.videolan.org/Android_Player_Intents/
|
||||
const getVlcUrl = (mediaInfo) => {
|
||||
//安卓:
|
||||
//android subtitles: https://code.videolan.org/videolan/vlc-android/-/issues/1903
|
||||
//const vlcUrl = `intent:${encodeURI(mediaInfo.streamUrl)}#Intent;package=org.videolan.vlc;type=video/*;S.subtitles_location=${encodeURI(mediaInfo.subUrl)};S.title=${encodeURI(mediaInfo.title)};i.position=${mediaInfo.position};end`;
|
||||
//const vlcUrl64 = Buffer.from(vlcUrl, 'utf8').toString('base64');
|
||||
//PC:
|
||||
//PC端需要额外设置,参考这个项目,MPV也是类似的方法: https://github.com/stefansundin/vlc-protocol
|
||||
//const vlcUrl = `vlc://${encodeURI(mediaInfo.streamUrl)}`;
|
||||
|
||||
//ios:
|
||||
//https://code.videolan.org/videolan/vlc-ios/-/commit/55e27ed69e2fce7d87c47c9342f8889fda356aa9
|
||||
const vlcUrl = `vlc-x-callback://x-callback-url/stream?url=${encodeURIComponent(mediaInfo.streamUrl)}&sub=${encodeURIComponent(mediaInfo.subUrl)}`;
|
||||
const vlcUrl64 = Buffer.from(vlcUrl, 'utf8').toString('base64');
|
||||
return {
|
||||
Name: `vlc-${mediaInfo.mediaSourceName}-${mediaInfo.displayTitle}`,
|
||||
Url: `${serverAddr}/${redirectKey}?link=${vlcUrl64}`
|
||||
}
|
||||
}
|
||||
|
||||
//https://github.com/iina/iina/issues/1991
|
||||
const getIinaUrl = (mediaInfo) => {
|
||||
return {
|
||||
Name: `IINA-${mediaInfo.mediaSourceName}-${mediaInfo.displayTitle}`,
|
||||
Url: `iina://weblink?url=${encodeURIComponent(mediaInfo.streamUrl)}&new_window=1`
|
||||
}
|
||||
}
|
||||
|
||||
//infuse
|
||||
const getInfuseUrl = (mediaInfo) => {
|
||||
const infuseUrl = `infuse://x-callback-url/play?url=${encodeURIComponent(mediaInfo.streamUrl)}`;
|
||||
const infuseUrl64 = Buffer.from(infuseUrl, 'utf8').toString('base64');
|
||||
return {
|
||||
Name: `Infuse-${mediaInfo.mediaSourceName}-${mediaInfo.displayTitle}`,
|
||||
Url: `${serverAddr}/${redirectKey}?link=${infuseUrl64}`
|
||||
}
|
||||
}
|
||||
|
||||
//https://sites.google.com/site/mxvpen/api
|
||||
const getMXUrl = (mediaInfo) => {
|
||||
//mxPlayer free
|
||||
const mxUrl = `intent:${encodeURI(mediaInfo.streamUrl)}#Intent;package=com.mxtech.videoplayer.ad;S.title=${encodeURI(mediaInfo.title)};i.position=${mediaInfo.position};end`;
|
||||
const mxUrl64 = Buffer.from(mxUrl, 'utf8').toString('base64');
|
||||
//mxPlayer Pro
|
||||
//const mxUrl = `intent:${encodeURI(mediaInfo.streamUrl)}#Intent;package=com.mxtech.videoplayer.pro;S.title=${encodeURI(mediaInfo.title)};i.position=${mediaInfo.position};end`;
|
||||
return {
|
||||
Name: `mxPlayer-${mediaInfo.mediaSourceName}-${mediaInfo.displayTitle}`,
|
||||
Url: `${serverAddr}/${redirectKey}?link=${mxUrl64}`
|
||||
}
|
||||
}
|
||||
|
||||
const getNPlayerUrl = (mediaInfo) => {
|
||||
const nplayerUrl = `nplayer-${encodeURI(mediaInfo.streamUrl)}`;
|
||||
const nplayerUrl64 = Buffer.from(nplayerUrl, 'utf8').toString('base64');
|
||||
return {
|
||||
Name: `nplayer-${mediaInfo.mediaSourceName}-${mediaInfo.displayTitle}`,
|
||||
Url: `${serverAddr}/${redirectKey}?link=${nplayerUrl64}`
|
||||
}
|
||||
}
|
||||
|
||||
const getSeek = (position) => {
|
||||
let ticks = position * 10000;
|
||||
let parts = []
|
||||
, hours = ticks / 36e9;
|
||||
(hours = Math.floor(hours)) && parts.push(hours);
|
||||
let minutes = (ticks -= 36e9 * hours) / 6e8;
|
||||
ticks -= 6e8 * (minutes = Math.floor(minutes)),
|
||||
minutes < 10 && hours && (minutes = "0" + minutes),
|
||||
parts.push(minutes);
|
||||
let seconds = ticks / 1e7;
|
||||
return (seconds = Math.floor(seconds)) < 10 && (seconds = "0" + seconds),
|
||||
parts.push(seconds),
|
||||
parts.join(":")
|
||||
}
|
||||
|
||||
|
||||
const getSubUrl = (r, mediaSource) => {
|
||||
let subTitleUrl = '';
|
||||
//尝试返回第一个外挂中字
|
||||
const chiSubIndex = mediaSource.MediaStreams.findIndex(m => m.Language == "chi" && m.IsExternal);
|
||||
r.error('chisubINdex: ' + chiSubIndex);
|
||||
if (chiSubIndex > -1) {
|
||||
const subtitleCodec = mediaSource.MediaStreams[chiSubIndex].Codec;
|
||||
subTitleUrl = `${domain}/${mediaSource.Id}/Subtitles/${chiSubIndex}/Stream.${subtitleCodec}?api_key=${api_key}`;
|
||||
} else {
|
||||
//尝试返回第一个外挂字幕
|
||||
const externalSubIndex = mediaSource.MediaStreams.findIndex(m => m.IsExternal);
|
||||
r.error('subIndex: ' + externalSubIndex);
|
||||
if (externalSubIndex > -1) {
|
||||
const subtitleCodec = mediaSource.MediaStreams[externalSubIndex].Codec;
|
||||
subTitleUrl = `${domain}/${mediaSource.Id}/Subtitles/${externalSubIndex}/Stream.${subtitleCodec}?api_key=${api_key}`;
|
||||
}
|
||||
}
|
||||
return subTitleUrl;
|
||||
}
|
||||
|
||||
function HeaderFilter(r) {
|
||||
r.headersOut['Content-Length'] = null;
|
||||
}
|
||||
|
||||
const redirectUrl = (r) => {
|
||||
const baseLink = r.args.link;
|
||||
r.error(`baseLink: ${baseLink}`);
|
||||
const link = Buffer.from(baseLink, 'base64').toString('utf8');
|
||||
r.return(302, link);
|
||||
}
|
||||
|
||||
|
||||
export default { addExternalUrl, redirectUrl, HeaderFilter };
|
33
nginx/nginx.conf
Normal file
33
nginx/nginx.conf
Normal file
@ -0,0 +1,33 @@
|
||||
load_module modules/ngx_http_js_module.so;
|
||||
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
@ -39,7 +39,7 @@ if [ "$SELFIP" == "" ]; then
|
||||
echo "DETECT SELF IP:${SELFIP}, if this is not correct, change SELFIP to your real ip"
|
||||
fi
|
||||
|
||||
echo "TRY CONNECT webdavsim, please wait... abount(30s)"
|
||||
echo "TRY CONNECT webdavsim, please wait... about(30s)"
|
||||
webdavsimok="0"
|
||||
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
||||
do
|
||||
@ -64,7 +64,7 @@ if [ "$webdavsimok" != "1" ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "ready to rclone mount, please wait...(abount 10s)"
|
||||
echo "ready to rclone mount, please wait...(about 10s)"
|
||||
rclone --config rclone.conf mount webdavsim-nas:/dav external/rclone.media --daemon --daemon-wait 10s --no-check-certificate --allow-other --umask 000 --default-permissions --dir-cache-time 1h --webdav-vendor rclone --vfs-cache-mode writes --vfs-read-chunk-size 32M --vfs-read-chunk-size-limit 1G --buffer-size 64M --vfs-cache-max-age 1h --vfs-cache-max-size 10G --timeout 1800s --contimeout 10s --daemon-timeout 300s --poll-interval 1800s --low-level-retries 2 --cache-dir rclone.cache --log-file rclone.log
|
||||
#rclone --config rclone.conf mount webdavsim-nas:/dav external/rclone.media --daemon --daemon-wait 10s --no-check-certificate --allow-other --umask 000 --default-permissions --dir-cache-time 1h --webdav-vendor rclone --vfs-cache-mode off --timeout 1800s --contimeout 10s --daemon-timeout 300s --poll-interval 1800s --low-level-retries 2 --log-file rclone.log
|
||||
if [ $? -eq 0 ]; then
|
||||
|
@ -28,8 +28,8 @@ fi
|
||||
mkdir -p emby.tmp emby.cache >/dev/null 2>&1
|
||||
chmod 777 emby.tmp emby.cache
|
||||
|
||||
docker stop embysim
|
||||
docker rm embysim
|
||||
#docker stop embysim
|
||||
#docker rm embysim
|
||||
|
||||
num=$(mount |grep "/external"|wc -l)
|
||||
if [ $num -lt 1 ]; then
|
||||
@ -95,24 +95,38 @@ fi
|
||||
#echo "use cpus:$cpus"
|
||||
# --cpus $cpus \
|
||||
|
||||
docker run -d \
|
||||
--sysctl net.ipv6.conf.all.disable_ipv6=1 \
|
||||
--name embysim \
|
||||
-v $progdir/emby.config:/config \
|
||||
-v $progdir/external:/external:rshared \
|
||||
-v $progdir/emby.tmp:/tmp \
|
||||
-v $progdir/emby.cache:/cache \
|
||||
--device /dev/dri:/dev/dri \
|
||||
-p 8096:8096 \
|
||||
-p 8920:8920 \
|
||||
--env UID=1000 \
|
||||
--env GID=100 \
|
||||
--env GIDLIST=100 \
|
||||
--env SELFIP=${SELFIP} \
|
||||
--restart on-failure \
|
||||
emby/embyserver:latest
|
||||
docker-compose -v
|
||||
if [ $? -eq 0 ]; then
|
||||
sed -i -e "s|let serverAddr =.*|let serverAddr = 'http://${SELFIP}:8097';|g" nginx/conf.d/externalUrl.js
|
||||
sed -i -e "s|- .*emby.config:/config|- ${progdir}/emby.config:/config|g" docker-compose.yml
|
||||
sed -i -e "s|- .*external:/external|- ${progdir}/external:/external|g" docker-compose.yml
|
||||
sed -i -e "s|- .*emby.tmp:/tmp|- ${progdir}/emby.tmp:/tmp|g" docker-compose.yml
|
||||
sed -i -e "s|- .*emby.cache:/cache|- ${progdir}/emby.cache:/cache|g" docker-compose.yml
|
||||
docker stop embysim
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
else
|
||||
docker stop embysim
|
||||
docker rm embysim
|
||||
docker run -d \
|
||||
--sysctl net.ipv6.conf.all.disable_ipv6=1 \
|
||||
--name embysim \
|
||||
-v $progdir/emby.config:/config \
|
||||
-v $progdir/external:/external:rshared \
|
||||
-v $progdir/emby.tmp:/tmp \
|
||||
-v $progdir/emby.cache:/cache \
|
||||
--device /dev/dri:/dev/dri \
|
||||
-p 8096:8096 \
|
||||
-p 8920:8920 \
|
||||
--env UID=1000 \
|
||||
--env GID=100 \
|
||||
--env GIDLIST=100 \
|
||||
--env SELFIP=${SELFIP} \
|
||||
--restart on-failure \
|
||||
emby/embyserver:latest
|
||||
|
||||
#pandagroove/embysim:1.0.0
|
||||
#pandagroove/embysim:1.0.0
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
docker cp "externalPlayer.js" "embysim":"/system/dashboard-ui/externalPlayer.js"
|
||||
|
Binary file not shown.
Reference in New Issue
Block a user