B站缓存视频数据m4s转mp4

发布于:2025-06-04 ⋅ 阅读:(18) ⋅ 点赞:(0)

B站缓存视频数据m4s转mp4

结构分析

在没有改变数据存储目录的情况下,b站默认数据保存目录为:
`Android->data->tv.danmaku.bili->download`

在这里插入图片描述
每个文件夹代表一个集合的视频,比如,我下载的”java从入门到精通“,那么就会保存到一个目录里面:
在这里插入图片描述
每个以c_开头的都是一个小章节。
在这里插入图片描述
每个小章节包含entry.json(视频标题及章节名称等信息),danmaku.xml(弹幕),64或80等(视频文件及音频文件等)
在这里插入图片描述
将audio.m4s以及video.m4s合成后就是一个完整的视频。本地得安装ffmpeg或者使用python-ffmpeg。
关键部分代码:

command = [
            "ffmpeg",
            "-i", video_m4s,  # 输入视频文件
            "-i", audio_m4s,  # 输入音频文件
            '-c:v', 'copy',  # 不重新编码视频
            '-c:a', 'copy',  # 不重新编码音频
            "-y",  # 覆盖已存在的文件
            output_mp4  # 输出文件
        ]

        # 使用 subprocess.run 执行命令
        result = subprocess.run(command)

将整个标题的视频转换出来。

#!/usr/bin/python3
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
import subprocess
import os
import json
import ffmpeg


def select_source():
    # 选择视频源目录
    dir_path = filedialog.askdirectory(title="选择视频源目录")
    if dir_path:
        source_entry.delete(0, END)
        source_entry.insert(0, dir_path)


def select_target():
    # 选择保存目录
    dir_path = filedialog.askdirectory(title="选择视频保存目录")
    if dir_path:
        target_entry.delete(0, END)
        target_entry.insert(0, dir_path)


def convert_with_ffmpeg(video_m4s, audio_m4s, output_mp4):
    if os.path.getsize(video_m4s) < 0 or os.path.getsize(audio_m4s) <= 0:
        print("非法的音视频文件,{audio_m4s},视频文件:{video_m4s}")
        return False
    try:
        # 输入视频和音频
        video = ffmpeg.input(video_m4s)
        audio = ffmpeg.input(audio_m4s)

        # 构造 FFmpeg 命令
        command = [
            "ffmpeg",
            "-i", video_m4s,  # 输入视频文件
            "-i", audio_m4s,  # 输入音频文件
            '-c:v', 'copy',  # 不重新编码视频
            '-c:a', 'copy',  # 不重新编码音频
            "-y",  # 覆盖已存在的文件
            output_mp4  # 输出文件
        ]

        # 使用 subprocess.run 执行命令
        result = subprocess.run(command)

        # 检查命令是否成功执行
        if result.returncode == 0:
            print(f"{output_mp4} -> 合并成功!")
            return True
        else:
            print(f"合并失败, 视频文件:{video_m4s}, 音频文件:{audio_m4s}")
            print(f"错误信息: {result.stderr}")
            return False
    except ffmpeg.Error as e:
        print(f"合并失败,音频文件:{audio_m4s},视频文件:{video_m4s}")
        return False


def batch_convert_vedio(source_path, target_path):
    for dir in os.listdir(source_path):
        dir_path = os.path.join(source_path, dir)

        entry_file = os.path.join(dir_path, "entry.json")
        data_dir = os.listdir(dir_path).pop(0)
        audio_path = os.path.join(dir_path, data_dir, "audio.m4s")
        vedio_path = os.path.join(dir_path, data_dir, "video.m4s")

        with open(entry_file, 'r', encoding='utf-8') as f:
            entry_sjon = f.read()
            json_data = json.loads(entry_sjon)
            part_content = json_data.get("page_data", {}).get("part")
            title = json_data.get("title")

            if not os.path.exists(os.path.join(target_path, f'{title}')):
                os.makedirs(os.path.join(target_path, f'{title}'))
            target_vedio_path = os.path.join(target_path, f'{title}/{part_content}.mp4')

            convert_with_ffmpeg(vedio_path, audio_path, target_vedio_path)
            print(target_vedio_path)


def convert_video():
    source_path = source_entry.get()
    target_path = target_entry.get()

    if not source_path or not target_path:
        messagebox.showerror("错误", "请选择源目录和目标目录!")
    else:
        batch_convert_vedio(source_path, target_path)
        messagebox.showinfo("成功", f"视频将从 {source_path} 转换保存到 {target_path}")


root = Tk()
root.title("bili视频转换工具")

# 设置窗口大小并居中
window_width = 500  # 稍微加宽窗口
window_height = 200
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
x = (screen_width - window_width) // 2
y = (screen_height - window_height) // 2
root.geometry(f"{window_width}x{window_height}+{x}+{y}")

# 配置网格布局权重
for i in range(5):
    root.grid_columnconfigure(i, weight=1)
for i in range(4):
    root.grid_rowconfigure(i, weight=1)

# 视频源目录选择
source_label = Label(root, text="视频源目录:")
source_label.grid(row=0, column=0, sticky="e", padx=5, pady=5)

source_entry = Entry(root)
source_entry.grid(row=0, column=1, columnspan=3, sticky="ew", padx=5, pady=5)

source_button = Button(root, text="浏览...", command=select_source)
source_button.grid(row=0, column=4, sticky="ew", padx=5, pady=5)

# 视频保存目录选择
target_label = Label(root, text="视频保存目录:")
target_label.grid(row=1, column=0, sticky="e", padx=5, pady=5)

target_entry = Entry(root)
target_entry.grid(row=1, column=1, columnspan=3, sticky="ew", padx=5, pady=5)

target_button = Button(root, text="浏览...", command=select_target)
target_button.grid(row=1, column=4, sticky="ew", padx=5, pady=5)

# 转换按钮
button_convert = Button(root, text="立即转换", command=convert_video)
button_convert.grid(row=2, column=1, columnspan=3, sticky="ew", padx=50, pady=20)

# 进入消息循环
root.mainloop()

具体效果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到