部署yt-dlp — 通过NAS远程下载网页上的视频

最近换了iphone,发现缺乏下载网页视频的软件,于是想办法在群晖NAS上部署了github上的yt-dlp项目。
功能:使用iOS的分享表单将网页链接整理为json格式的api请求,并发送到运行在NAS上的yt-dlp来进行远程下载视频。

  1. 首先开启NAS的SSH连接。
在群晖DSM7.0+的控制面板中选择终端机和SNMP,并启动SSH,设定端口号并保存。

2. 安装python3.9及其以上版本。
2.1 可以选择在套件中心通过第三方源进行安装。或者在SSH下通过命令行进行安装,具体请网络搜索教程。
2.2 安装后在SSH内使用命令行 python3.11 --version 查看是否安装成功。
2.3 通过命令行 which python3.11 查看路径并记录下来。

3. 安装yt-dlp。
3.1 在SSH中可以直接通过pip管理器来安装,运行命令 pip install yt-dlp
3.2 使用Shebang行来检查其解释器,运行命令 head -n l $(which yt-dlp)
3.3 使用vim或者nano来将第一行的python更改为需要的python版本。

4. 保存具有登录信息的浏览器的Cookies(可选,可用chrome插件”get cookies”来导出netscape格式的Cookies文件,具体查看yt-dlp的github说明)。

5. 如果你的域名商允许你下载安全证书,万岁!
如果像我一样是域名商是Squarespace或者不允许你下载安全证书,那么就要导出安全证书给flask使用。
5.1 使用root登录SSH,找到目录/usr/syno/etc/certificate/system/default/
5.2 导出其中的fullchain和privetkey证书

6. 安装flask以建立api服务。
6.1 在SSH中运行命令 pip install flask

7. 创建flask脚本。
脚本内容大致如下:

# flask-yt-dlp.py 开通api服务
from flask import Flask, request, jsonify
import subprocess
import threading
import uuid
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

# 设置日志记录器
logger = logging.getLogger('werkzeug')
logger.setLevel(logging.INFO)

# 创建日志处理器
handler = RotatingFileHandler('/volume1/video/Download/临时/logs/flask_app.log', maxBytes=10*1024*1024, backupCount=5)
handler.setLevel(logging.INFO)

# 设置日志格式
formatter = logging.Formatter(
    '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
)
handler.setFormatter(formatter)

# 将处理器添加到日志记录器
logger.addHandler(handler)
app.logger.addHandler(handler)

tasks_lock = threading.Lock()

# 下载任务的字典,用于存储任务状态
tasks = {}

# 下载视频的函数
def download_video(url, task_id):
    try:
        app.logger.info(f"开始下载任务 {task_id}: {url}")
        command = [
            '/var/services/homes/myname/.local/bin/yt-dlp',
            #'--cookies',
            #'/volume1/homes/myname/services/login-cookies.txt',  # 替换为实际的Cookie文件路径
            '--add-header', 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0.1 Safari/605.1.15',
            #'-f', 'bestvideo+bestaudio/best',
            url,
            '-o',
            '/volume1/video/Download/临时/%(title)s.%(ext)s'
        ]
        # 执行下载命令
        process = subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True  # 如果Python版本低于3.7,使用universal_newlines=True
        )
        stdout, stderr = process.communicate()

        app.logger.info(f"任务 {task_id} 的下载进程返回码:{process.returncode}")

        # 使用锁来同步对tasks字典的访问
        with tasks_lock:
            if process.returncode == 0:
                tasks[task_id]['status'] = 'completed'
                tasks[task_id]['output'] = stdout
                app.logger.info(f"任务 {task_id} 下载完成")
            else:
                tasks[task_id]['status'] = 'failed'
                tasks[task_id]['error'] = stderr
                app.logger.error(f"任务 {task_id} 下载失败,错误信息:{stderr}")
    except Exception as e:
        with tasks_lock:
            tasks[task_id]['status'] = 'failed'
            tasks[task_id]['error'] = str(e)
        app.logger.exception(f"任务 {task_id} 发生异常")


# 定义API路由
@app.route('/download', methods=['POST'])
def download():
    # 获取请求头中的API密钥
    api_key = request.headers.get('API-Key')
    if api_key != '***************':  # 请替换为自己设定的API密钥
        app.logger.warning("未经授权的访问尝试")
        return jsonify({'error': 'Unauthorized'}), 401


    # 获取请求体中的视频URL
    data = request.get_json()
    if not data:
        return jsonify({'error': 'Invalid JSON data'}), 400

    video_url = data.get('url')
    if not video_url:
        return jsonify({'error': 'No URL provided'}), 400

    # 生成任务ID
    task_id = str(uuid.uuid4())
    app.logger.info(f"收到下载请求,任务ID:{task_id}")

    # 使用锁来同步对tasks字典的访问
    with tasks_lock:
        tasks[task_id] = {'status': 'in_progress'}

    # 启动异步线程进行下载
    threading.Thread(target=download_video, args=(video_url, task_id)).start()

    return jsonify({'status': 'Download started', 'task_id': task_id}), 200

@app.route('/status/<task_id>', methods=['GET'])
def status(task_id):
    app.logger.info(f"查询任务状态,任务ID:{task_id}")
    with tasks_lock:
        task = tasks.get(task_id)
    if task:
        return jsonify(task), 200
    else:
        return jsonify({'error': 'Task not found'}), 404

if __name__ == '__main__':
    app.run(
        host='0.0.0.0',
        port=1234, # 设定一个监听的端口号
        ssl_context=(
            '/volume1/homes/myname/services/cer/f.pem', # 证书位置
            '/volume1/homes/myname/services/cer/p.pem'
        )
    )

注意修改脚本中的个别信息以符合你的配置,包括建立日志储存的目录。其中Cookies我添加了注释,因为我还没搞懂怎么弄。如果你有这方面的需要的话可以自己修改。除了使用netscape格式的cookies文件之外还可以使用wget命令,或者创建~/.netrc文件来储存凭据(记得给600权限),这些进阶操作这里就不展开说明了。

8. 建立NAS的开机自启动脚本,在群晖DSM的控制面板中找到任务计划,点击新增,触发的任务,用户自定义的脚本,设定为开机启动。在任务设置选项卡下的运行命令中输入 nohup python3 /volume1/homes/yourname/flask-yt-dlp.py & (调整你的脚本位置,最后的&符号不要漏掉)

9. 重启NAS。

10. 检查端口是否在被监听,在SSH中输入命令 netstat -tulnp | grep 你设定的端口号

11. 在路由器中设定端口转发。

12. 在iOS中建立一个快捷指令:

长按快捷指令,详细信息,开启在共享表单中显示。网址和端口号按照你的来填,注意端口号后面的/download是yt-dlp的默认设置,不需要更改。
这里的通知可以删除,因为实际上无论下载是否成功返回的文本都是下载已经开始,所以意义不大。

13. 可以通过iOS的共享到快捷指令进行远程下载视频了,如果下载失败的话请检查你的文件下载目录是否有访问权限,或者查看日志来调试。


谢谢你的阅读,希望对你有帮助!

谢谢你的阅读,欢迎评论和分享!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇