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()

具体效果如下:


相关推荐
DogDaoDao3 小时前
Android 硬件编码器参数完全指南:MediaCodec 深度解析
android·音视频·视频编解码·h264·硬编码·视频直播·mediacodec
栗子~~4 小时前
JAVA - 二层缓存设计(本地缓冲+redis缓冲+广播所有本地缓冲失效) demo
java·redis·缓存
隔窗听雨眠4 小时前
多活部署、CDN加速与边缘缓存全链路优化实战
缓存
音视频牛哥4 小时前
大牛直播SDK(SmartMediaKit)Windows平台RTSP/RTMP直播播放SDK集成说明(C#版)
音视频·低延迟rtsp播放器·windows rtsp播放器·windows rtmp播放器·低延迟rtmp播放器·c# rtsp播放器·c# rtmp播放器
未若君雅裁5 小时前
MyBatis 一级缓存、二级缓存与清理机制
java·缓存·mybatis
薛定猫AI7 小时前
【深度解析】Gemini Omni 多模态生成与 Agent 化创作工作流:从视频编辑到 UI 生成的技术演进
人工智能·ui·音视频
189228048618 小时前
NY352固态MT29F32T08GWLBHD6-24QJ:B
大数据·服务器·人工智能·科技·缓存
丷丩9 小时前
三级缓存下MVT地图瓦片服务性能优化策略
算法·缓存·性能优化·gis·geoai-up
柿柿快乐10 小时前
Redis 入门第一课:全局命令、内部编码与单线程模型
redis·学习·缓存·基础教学
磊 子10 小时前
1.4CPU缓存一致性
java·spring cloud·缓存·系统