Python+FFmpeg提取哔哩哔哩安卓缓存

38 阅读3分钟

主要参考360个人博物馆的一个文章1,在评论区看到好像这个头条2的才是原出处?

首先说一下注意事项:

  • 手机直接连在电脑上,可以复制文件,但是不能够通过命令行访问。其原因大概是没有挂载盘符,导致其路径一直在/此电脑下面。

  • FFmpeg的安装等不再赘述,我这里已经安装好了。而且这里的FFmpeg已经加入环境变量,即全局可调用。如果没有加入环境变量,则需要通过安装路径/ffmpeg.exe或者进入安装文件夹,通过.\ffmpeg.exe来调用

E:\Android\data\tv.danmaku.bili\download\10590361\c_17482191
|   entry.json
|   danmaku.xml
|
\---64
        index.json
        audio.m4s
        video.m4s

在上图当中,10590361是缓存视频的文件夹,c_17482191是该分P的文件夹。其中64这个文件夹是该分P的视频video.m4s,音频audio.m4s等信息,而entry.json则是该分P的信息及整个系列的信息。

不同的缓存当中,存储视频和音频的文件夹名不一定相同,这个是64,我也见过16的,一般都是数字

代码如下:

import json
# 用于解析json
import os
# 用于新建文件夹等
import re
# 用于替换字符
import time
# 用于计时

At = time.time()
# 记录总耗时

def validateTitle(title):
    rstr = r"[\/\\\:\*\?\"\<\>\|]"
    # '/ \ : * ? " < > |'
    new_title = re.sub(rstr, "", title)
    # 替换为下划线
    return new_title

pathI = "E:/Android/data/tv.danmaku.bili/download/929386644/"
# 缓存所在的目录

listF = os.listdir(pathI)
# 该缓存下有几个视频文件夹

pathF = pathI + listF[0] + '/entry.json'
# 该缓存下P1的信息文件路径

with open(pathF, encoding='utf-8') as fileI:
    infoJ = json.load(fileI)
# 读入文件

dirT = validateTitle(infoJ['title'])

dirN = 'D:/NetVideo/'+dirT + '/'
# 新的文件夹路径,包含中文

dirO = 'D:/NetVideo/Tmp/'
# 旧的临时文件夹路径,用于FFmpeg的命令

if not(os.path.exists(dirO)):
    os.mkdir(dirO)
    # 按照临时文件夹的名字创立文件

listFL = len(listF)
handleP = []

# 循环该缓存下的每一个分P
for index,itemF in enumerate(listF):
    print(index+1,'/',listFL,' ','*'*20)
    trnT = time.time()

    pathF = pathI + itemF + '/'
    # 该分P的路径
    
    os.chdir(pathF)
    # 将工作目录改为当前路径
    
    with open('entry.json',encoding='utf-8') as fileI:
        infoJ = json.load(fileI)
    # 读入该分P的信息
    
    fileO = dirO+'O.mp4'
    # 该分P视频的临时文件名,不含中文

    fileNC = validateTitle(infoJ['page_data']['part'])
    # 去除文件名当中不能存在的字符
    
    fileN = dirO + fileNC + '.mp4'
    # 该分P的实际文件名,包含中文
    print(fileNC)
    
    commandS = 'ffmpeg -loglevel quiet -i video.m4s -i audio.m4s -codec copy '+fileO
    # 拼接命令
    
    # print(commandS)
    # # 查看命令
    
    # print(fileN)
    # # 查看文件名

    sunDir = [i for i in os.listdir(pathF) if os.path.isdir(i) ]
    # 找到该文件夹下面的子文件夹
    os.chdir(pathF+sunDir[0]+'/')
    # 将工作目录调整到分P文件夹下,包含视频的文件夹
    # 一般每一份缓存,各分P文件夹的名字不不相同
    # 但是各分P文件夹下保存视频的文件夹名字一般相同,为数字
    print(pathF+sunDir[0]+'/')
    # os.system('echo %cd%')
    # # 打印当前目录



# ffmpeg -f concat -i N.txt -c copy D:/NetVideo/output.mp4
    # # 判断文件是否存在
    if not(os.path.exists(fileN)):
        if (os.path.exists(pathF+sunDir[0]+'/'+'video.m4s')):
            print("Get video.m4s !!!")
            os.system(commandS)
        else:
            print("No video.m4s !!!")
            handleP.append(pathF+sunDir[0]+'/')
            # continue
            commandS = 'ffmpeg -loglevel quiet -f concat -i N.txt -c copy'+fileO

            listN = os.listdir(pathF+sunDir[0]+'/')
            listN.remove('index.json')
            with open("N.txt",'w') as F:
                for i in listN:
                    F.write('file '+i+'\n')
            os.system(commandS)
            os.remove('N.txt')
            os.rename(fileO, fileN)
            # 将视频文件重命名回实际文件名,包含中文
        os.rename(fileO, fileN)
        # 将视频文件重命名回实际文件名,包含中文
    
    print('Iter Cost: {}'.format(time.time() - trnT))
    
os.rename(dirO,dirN)
# 将视频文件夹重命名为实际文件名,包含中文

print(handleP)


print('\nTotal Cost: {}\n\n\n\n'.format(time.time() - At))

# import os
# os.chdir('E:/Android/data/tv.danmaku.bili/download/10590361/c_17482207/lua.flv720.bili2api.64/')
# listN = os.listdir()
# listN.remove('index.json')
# with open('tmp.txt','w') as F:
#     for i in listN:
#         F.write('file '+i+'\n');
# fileO = "D:/NetVideo/O.mp4"
# # -loglevel quiet
# # 13 Unsupervised Learning - Linear Methods
# commandS = "ffmpeg -loglevel quiet -f concat -i tmp.txt -c copy "+fileO
# os.system(commandS)
# os.remove('tmp.txt')

给两个参考文献:

使用 FFmpeg(bilibili视频m4s合成mp4)

使用 FFmpeg(bilibili视频blv合成mp4)

ffmpeg 取消控制台打印信息,不打印log日志

[^]:

[^]:

[^]:

[^]:

[^]:

Footnotes

  1. 史上最详细!如何将B站缓存的m4s文件无损转换为mp4格式

  2. 超级详细!如何将B站缓存m4s文件无损转换为mp4格式