B站缓存视频数据m4s转mp4

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。

关键部分代码:

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

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

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

python 复制代码
#!/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()

具体效果如下:


相关推荐
YWamy6 小时前
如何降低RTC SDK在复杂网络环境下的运行成本
网络·音视频·实时音视频
FlightYe8 小时前
视界原理之3D视频(四):裸眼、眼镜和头显
android·linux·c++·3d·ffmpeg·音视频
利威尔xu8 小时前
Go IM 源码实战(14):本地缓存快 3000 倍,失效却难 10 倍
后端·缓存·微服务·面试·golang
小豆庄9 小时前
本地视频打码:多模型检测、帧间追踪与18种模糊的选型
opencv·目标检测·ai·自然语言处理·音视频·视频
程序猿编码14 小时前
解决 LLM 算力浪费,一套统一 KV 管理层实现缓存复用
人工智能·神经网络·机器学习·缓存·llm·大模型推理
TST-魚爷14 小时前
小型视频会议室音频踩坑:一体化拾音扩音方案实测分享(易视讯)
stm32·电脑·音视频·钉钉·腾讯会议·zoom会议
Sayai15 小时前
让动漫角色复刻真人动作!MiniMax H3 动作控制实战:一段视频驱动角色动画(12G 显存,附模型下载)
人工智能·aigc·音视频·comfyui·动作捕捉·minimax
zrc00700717 小时前
steam无法登录 steam错误 登录时出现错误。请稍后再试。解决办法 steam无法登录解决办法 湖南地区steam DNS问题解决办法
windows·经验分享·游戏·缓存·游戏程序·创业创新·玩游戏
CCblandy18 小时前
缓存淘汰实测:一次全表扫描把 LRU 命中率从 46% 打到 38%,W-TinyLFU 守住 50% 却贵 11 倍
后端·python·缓存·性能优化·架构
Wang's Blog18 小时前
Java 项目实战: 交友项目 - 小视频模块实战:访客记录、FastDFS 存储与 SpringCache 缓存
java·缓存·交友