banner
ximu

ximu

吃饭,睡觉,做梦!
twitter

使用strm讓jellyfin播放onedrive直鏈

cover

前言#

之前用 jellyfin 搭建了一个動漫媒體庫,但是 vps 的线路实在是有点不尽如人意,而且流量还有不够用的风险。于是凭借伟大的白嫖免費精神😇,我发现可以用 strm 文件来实现 jellyfin 直链播放 onedrive 里的视频,而不会走服务器的宽带和流量。我还在 github 上找到了一个转换脚本,一切都刚刚好😜。

原理大概是这样:
thought

准备#

  • 装有 jellyfin 的服务器
  • 结构清晰的 onedrive 媒體庫
  • 挂载了 onedrive 媒體庫的 alist
  • 一些耐心:D

脚本运行环境准备#

确保你的服务器中安装了python3pip,使用以下命令检查是否安装:

python3 --version
pip --version

如果未安装,请依照以下步骤安装:

  1. 更新包列表

    打开终端,首先更新你的包列表。这确保你下载的是最新的软件包。运行以下命令:

    apt update
    
  2. 安装 Python 3

    debian 的软件仓库中包含了 python 3 的安装包。运行以下命令来安装 python 3

    apt install python3
    
  3. 安装 pip

    pip 是 python 的包管理器,用于安装和管理 python 包。通过以下命令安装:

    apt install python3-pip
    
  4. 验证安装

    安装完成后,运行以下命令来验证 python 3 和 pip 是否正确安装:

    python3 --version
    
    pip3 --version
    

确保安装成功后,创建一个虚拟环境并在此环境中安装脚本的环境依赖:

  1. 创建虚拟环境

    创建一个虚拟环境可以避免某些冲突🤗

    python3 -m venv /python_env/auto_film/
    

    请将/python_env/auto_film/替换为你想要安装虚拟环境的路径。

  2. 激活虚拟环境

    创建后当然要激活的咯🧐

    source /python_env/auto_film/bin/activate
    

    /python_env/auto_film/替换为你刚刚安装虚拟环境的路径。

  3. 安装依赖

    webdavclient3 是一个用于与 webdav 服务器交互的 python 库。

    pip install webdavclient3
    

ok,你已经把脚本运行所需的环境搭建好了👌

因为脚本运行时间较长,你可以选择安装一个screen来让命令可以在后台运行,这样你就可以把运行脚本的终端窗口关掉了😉

  1. 安装 screen

    使用以下命令安装screen:

    apt-get install screen
    
  2. 启动 screen:

    创建一个新的会话:

    screen -S mysession
    

    mysession更换为你想要的会话名称。

  3. 使用 screen 会话:

    screen会话中,你可以像平常一样使用命令行。而且你可以关闭这个终端窗口,这个窗口的命令会在后台运行。

  4. 重新连接到一个 screen 会话:

    要重新连接到一个已经存在的screen会话,使用:

    screen -r mysession
    

    其中 mysession 是你自定义的会话名称。

准备 strm 脚本#

把以下代码保存为autofilm.py
脚本内容来自Akimio521佬的 github 项目 AutoFilm
给大佬递 star✨

from webdav3.client import Client
import argparse, os, requests, time

'''
遍历Webdav服务器函数
如果depth为None,则会递归遍历整个WebDAV服务器
如果depth为正整数,则会递归遍历到指定深度
如果depth为0,则只会遍历当前文件夹中的文件和文件夹,不会继续递归遍历下一级文件夹。
'''
def list_files(webdav_url, username, password, show_path, depth=None, path='', count=0, proxies=None):
    options = {
        'webdav_hostname': webdav_url,
        'webdav_login': username,
        'webdav_password': password,
        'proxies': proxies
    }

    client = Client(options)
    directory = []
    files = []
    q = 1
    while q < 15:
        try:
            items = client.list()
        except:
            print(f'第{q}次连接失败,{q+1}秒后重试...')
            q += 1
            time.sleep(q)
        else:
            if q > 1:
                print('重连成功...')
            break

    if q == 15:
        print('连接失败,请检查网络设置!')
        exit()

    for item in items[1:]:
        if item[-1] == '/':
            if depth is None or depth > 0:
                subdirectory, subfiles, count = list_files(webdav_url + item, username, password, show_path, depth=None if depth is None else depth - 1, path=path+item, count=count)
                directory += [item + subitem for subitem in subdirectory]
                files += [item + subitem for subitem in subfiles]
            else:
                directory.append(item)
        else:
            files.append(item)
            count += 1
    if show_path and path:
        print(f'当前文件夹路径:{path}')
    return directory, files, count

'''
下载函数
用于'ASS', 'SRT', 'SSA','NFO','JPG', 'PNG'文件的下载
'''   
def download_file(url, local_path, filename, total_count):
    p = 1
    while p < 10:
        try:
            print('正在下载:' + filename)
            r = requests.get(url.replace('/dav', '/d'), proxies=proxies)
            os.makedirs(os.path.dirname(local_path), exist_ok=True)
            with open(local_path, 'wb') as f:
                f.write(r.content)
                f.close()
        except:
            print(f'第{p}次下载失败,{p + 1}秒后重试...')
            p += 1
            time.sleep(p)
        else:
            if p > 1:
                print('重新下载成功!')
            print(filename + '下载成功!')
            break
        progress = int((p / 10) * 100)
        print(f'已完成 {progress}%,共 {total_count} 个文件')

parser = argparse.ArgumentParser(description='Autofilm script')
parser.add_argument('--webdav_url', type=str, help='WebDAV服务器地址', required=True)
parser.add_argument('--username', type=str, help='WebDAV账号', required=True)
parser.add_argument('--password', type=str, help='WebDAV密码', required=True)
parser.add_argument('--output_path', type=str, help='输出文件目录', default='./Media/')
parser.add_argument('--subtitle', type=str, help='是否下载字幕文件', choices=['true', 'false'], default='true')
parser.add_argument('--nfo', type=str, help='是否下载NFO文件', choices=['true', 'false'], default='false')
parser.add_argument('--img', type=str, help='是否下载JPG和PNG文件', choices=['true', 'false'], default='false')
parser.add_argument('--show_path', type=str, help='遍历时是否显示文件夹路径', choices=['true', 'false'], default='false')
parser.add_argument('--proxy', type=str, help='HTTP代理服务器,格式为IP:端口号')
args = parser.parse_args()

print('启动参数:')
print(f'Webdav服务器地址:{args.webdav_url}')
print(f'Webdav登入用户名:{args.username}')
print(f'Webdav登入密码:{args.password}')
print(f'文件输出路径:{args.output_path}')
print(f'是否下载字幕:{args.subtitle}')
print(f'是否下载电影信息:{args.nfo}')
print(f'是否下载图片:{args.img}')
print(f'遍历时是否显示文件夹路径:{args.show_path}')

proxies = None
if args.proxy:
    proxies = {
        'http': f'http://{args.proxy}',
        'https': f'http://{args.proxy}'
    }

directory, files, count = list_files(args.webdav_url, args.username, args.password, args.show_path, depth=None, path='', count=0, proxies=proxies)

urls = [args.webdav_url + item for item in directory + files]

download_count = 0

for url in urls:
    if url[-1] == '/':
        continue
    filename = os.path.basename(url)
    local_path = os.path.join(args.output_path, url.replace(args.webdav_url, '').lstrip('/'))
    file_ext = filename[-3:].upper()

    if file_ext in ['MP4', 'MKV', 'FLV', 'AVI', 'WMV']:
        if not os.path.exists(os.path.join(args.output_path, filename[:-3] + 'strm')):
            print('正在处理:' + filename)
            try:
                os.makedirs(os.path.dirname(local_path), exist_ok=True)
                with open(os.path.join(local_path[:-3] + 'strm'), "w", encoding='utf-8') as f:
                    f.write(url.replace('/dav', '/d'))
            except:
                print(filename + '处理失败,文件名包含特殊符号,建议重命名!')
    elif args.subtitle == 'true' and file_ext in ['ASS', 'SRT', 'SSA', 'SUB']:
        if not os.path.exists(local_path):
            download_file(url, local_path, filename, count)
            download_count += 1
    elif args.nfo == 'true' and file_ext == 'NFO':
        if not os.path.exists(local_path):
            download_file(url, local_path, filename, count)
            download_count += 1
    elif args.img == 'true' and file_ext in ['JPG', 'PNG']:
        if not os.path.exists(local_path):
            download_file(url, local_path, filename, count)
            download_count += 1

    progress = int((download_count / count) * 100)
    print(f'已完成 {progress}%,共 {count} 个文件')

print('处理完毕!')

运行 strm 脚本#

  1. 启动一个 screen 会话(可选):

    使用 screen 可以让你关闭正在运行脚本的终端窗口,使其在后台运行。

    screen -S autofilm
    
  2. 激活之前搭建好的虚拟环境

    source /python_env/auto_film/bin/activate
    

    /python_env/auto_film/是你之前创建虚拟环境的路径。

  3. 运行脚本

    请在运行脚本前确定媒体库里所有路径和文件名不包含#《等特殊字符,否则会报网络错误。

    python3 autofilm.py --webdav_url https://alist.example.com/dav/your/media/ --username uname --password pword  --output_path /your/strm/ --nfo true --img true
    
    • https://alist.example.com/dav/替换为你的 alist 网址
    • your/media/替换为你媒体库在 alist 上的路径
    • unamepword替换为你 alist 的用户名和密码
    • /your/strm/替换为你准备储存 strm 的位置

    一些参数说明:

    必要参数

    • webdav_url:webdav 服务器地址
    • username:webdav 账号
    • password:webdav 密码

    非必要参数

    • output_path:输出文件目录,默认为当前文件夹的子目录 Media
    • subtitle:是否下载字幕文件,默认 true
    • nfo:是否下载 NFO 文件,默认 false
    • img:是否下载 JPG 和 PNG 图片文件,默认 false

ok,请喝十杯茶🍵或看个电影🎞,脚本运行时间取决于你的媒体库大小和服务器配置,通常来说会🤏慢。

jellyfin 播放 strm 文件#

在 jellyfin 中添加媒体库,路径就选你储存 strm 文件的位置,jellyfin 会自动识别并刮削。

尝试播放,你会发现,根本不占用服务器带宽!🎉

后言#

这下可以不用担心服务器带宽,把 jellyfin 服务器公开出来了,等我再补补老番资源,咕~

参考

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。