目录

使用 Python 将 YouTube 和 Bilibili 视频收藏到 Google Drive

提示
  • 代码于 2023–07-22 进行修改,第一版的代码下载的视频没有声音。
  • 代码于 2023–08-20 进行修改,第二版的代码没有考虑到 bilibili cookies 过期的问题。
  • 代码于 2023–10-10 进行修改,第三版的代码使用了 pyrogram。
介绍
我经常在YouTube和Bilibili上看到我喜欢的视频,但是想要在收藏中找到这些视频却比较困难,因此我决定将它们保存到云盘中。

本篇文章是使用 python 编写一个脚本,下载 youtube 和 bilibili 视频,然后将其上传到 Google Drive 里。但不仅限于 Google Drive,任何可以通过 rclone 到 VPS 上的云盘都可以上传。为了方便,我将其制作成了一个 telegram bot。

准备操作

  1. 最开始需要一个 telegram bot 的 BOT_TOKEN,去 BotFather 里创建一个新的 bot 即可获取。
  2. 挂载云盘到 VPS,具体步骤: https://www.westinyou.com/%E8%87%AA%E5%BB%BA-emby/#rclone
  3. 因为使用 bilibili 要下载 1080P 的视频要 cookies,所以自行使用 Get cookies.txt LOCALLY 进行获取 bilibili 的 cookies,然后将其保存到 VPS 中

python 脚本

运行前

  • your_bot_token 替换为自己去 BotFather 中获取的 token;
  • <your_cloud_path> 替换为自己云盘挂载的位置,比如:/home/google_drive
  • <your_cookies_path> 替换为自己获取的 bilibili cookies 存放的位置
  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
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import os
import subprocess
from urllib.parse import urlparse
from pyrogram import Client, filters, types, enums

# Telegram bot token
API_ID =   # 替换成您的API ID
API_HASH = ''  # 替换成您的API Hash
BOT_TOKEN = ''  # 替换成您的Bot Token

# 定义Pyrogram客户端
app = Client('my_bot', api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN)
# app = Client('my_bot')
AUTHORIZED_USER="" #替换为您的用户ID
MAX_LOG_LINES = 100  # 最大保留的日志行数

def log_output(output):
    # 读取当前日志内容,最多保留 MAX_LOG_LINES 行
    with open('download_log.txt', 'r') as log_file:
        lines = log_file.readlines()
    lines = lines[-MAX_LOG_LINES + 1:]  # 保留最新的 MAX_LOG_LINES - 1 行

    # 将新的日志追加到列表末尾
    lines.append(output + '\n')

    # 将新的日志写入文件
    with open('download_log.txt', 'w') as log_file:
        log_file.writelines(lines)


def private_use(func):
    def wrapper(client: Client, message: types.Message):
        chat_id = getattr(message.from_user, "id", None)

        # message type check
        if message.chat.type != enums.ChatType.PRIVATE and not message.text.lower().startswith("/ytdl"):
            logging.debug("%s, it's annoying me...🙄️ ", message.text)
            return

        # authorized users check
        if AUTHORIZED_USER:
            users = [int(i) for i in AUTHORIZED_USER.split(",")]
        else:
            users = []

        if users and chat_id and chat_id not in users:
            message.reply_text("This is a private bot.", quote=True)
            return

        return func(client, message)

    return wrapper

@app.on_message(filters.command(["start"]))
@private_use
def start_command(client, message):
    message.reply_text("The bot is running.")

@app.on_message(filters.text & filters.private)
@private_use
def handle_text_message(client, message):
    video_link = message.text.strip()
    domain = urlparse(video_link).hostname

    if domain in ('www.youtube.com', 'youtu.be', 'youtube.com'):
        paths = '<your_cloud_path>/youtube'
        cookies = None
    elif domain in ('www.bilibili.com', 'b23.tv'):
        paths = '<your_cloud_path>/bilibili'
        cookies = '<your_cookies_path>'
    else:
        message.reply_text("Invalid video link. Please provide a link from YouTube or Bilibili.")
        return

    command = [
        'yt-dlp',
        '-f', 'bestvideo+bestaudio/best',
        video_link,
        '--paths', paths,
        '--write-subs',
        '--write-auto-subs',
        '--sub-langs', 'zh.*',
        '--merge-output-format', 'mkv',
        '--external-downloader', 'aria2c',
        '--external-downloader-args', '-x 5 -k 4M'
    ]

    if cookies:
        command.extend(['--cookies', cookies])

    # 发送 "Downloading in progress." 消息
    downloading_message = message.reply_text("Downloading in progress.", reply_to_message_id=message.id)

    process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while True:
        output = process.stdout.readline().decode().strip()
        if output == '' and process.poll() is not None:
            break

        log_output(output)

        if "ERROR" in output:
            downloading_message.delete()
            message.reply_text("An error occurred during video download.", reply_to_message_id=message.id)
            break
        elif "has already been downloaded" in output:
            downloading_message.delete()
            message.reply_text("Video has already been downloaded.", reply_to_message_id=message.id)
            break
        elif "Deleting original file" in output:
            downloading_message.delete()
            message.reply_text("Video download complete.", reply_to_message_id=message.id)
            break
        elif "are missing" in output:
            downloading_message.delete()
            message.reply_text("Bilibili cookies expired, please update.", reply_to_message_id=message.id)
            process.terminate()  # 停止下载进程
            break

if __name__ == '__main__':
    app.run()

运行

  • 运行安装 yt-dlp
1
2
sudo apt update                                 # Update package list
sudo apt install yt-dlp                         # Install yt-dlp

如果报错,配置 ppa

1
sudo add-apt-repository ppa:tomtomtom/yt-dlp    # Add ppa repo to apt
  • 脚本需要 pyrogram,使用 pip 进行安装
1
pip install pyrogram
  • 运行脚本
1
python bot.py

脚本解释

对于 YouTube、Bilibili 视频,脚本将视频保存到预先设置的路径。脚本使用 yt-dlp 工具下载最佳质量的视频,并保存为 mkv 格式。脚本还会下载视频的字幕,并自动合并到视频文件中。此外,对于 Bilibili 视频下载,脚本还使用 cookies 文件来进行身份验证。

脚本会优先匹配下载 HEVC(H.265) 格式的视频,其次匹配 H.264 格式的视频,最后匹配 AVI 格式的视频。

脚本仅支持单个视频的下载,暂不支持视频列表下载。

使用

发送 /start 如果 bot 回复,表示正在运行,没有回复,则 bot 没有运行:

https://image.westinyou.com/blog-imgs/2023/07/14/bot_running.png
The bot is running.

直接发送 YouTube 视频或者 bilibili 视频即可,直接进行下载:

https://image.westinyou.com/blog-imgs/2023/07/14/d_y_video.png
下载 YouTube 视频

https://image.westinyou.com/blog-imgs/2023/07/14/d_b_video.png
下载 bilibili 视频

https://image.westinyou.com/blog-imgs/2023/07/14/d_b_video_4k.png
下载 bilibili 4k 视频

总结

代码实现难度不高,很简单的一个 bot。