warning 警告
本内容仅供学习交流,所有资料请于2小时内删除,严禁侵犯相关视频版权。
哔哩哔哩的windows客户端可以方便地缓存视频至本地。然而,其缓存的文件为.m4s格式,无法直接播放,也无法直接通过ffmpeg进行转码。
经查询,发现其原因是视频二进制文件开头嵌入了9个字节的0。如图所示。
因此,只需删去这些0,就可以播放,或者通过ffmpeg把分离的音频文件和视频文件不经转码直接复制封装进一个mp4容器,实现缓存视频转换。
这个方法不同于网页脚本、第三方脚本直接爬取哔哩哔哩网站,而是通过官方方法缓存至本地后再破解转码,既快速又安全。
本脚本需要依赖于python环境和ffmpeg,自动将所有缓存下来的哔哩哔哩视频批量转换为mp4格式。需要填写ffmpeg路径、bilibili缓存根路径(可在bilibili设置中查看)。
#Code by ZZX https://blog.xzzzx.xyz
import json,subprocess,os
# specify where you want to store the videos here
storeDir = 'D:/videos'
# specity your bilibili download dir here
bliDownDir = 'C:/Users/Administrator/Videos/bilibili'
# specify your ffmpeg.exe path here
ffmpegPath = 'C:/ffmpeg/bin/ffmpeg.exe'
tmpDir = storeDir + '/tmp'
class videos():
def __init__(self, bliVideoDir) -> None:
self.bliVideoDir = bliVideoDir
self.videoInfoPath = bliVideoDir + '/.videoInfo'
self.videoName, self.videoId = getInfo(self.videoInfoPath)
self.rawVideoPath = f'{bliVideoDir}/{self.videoId}_nb2-1-30064.m4s'
self.rawAudioPath = f'{bliVideoDir}/{self.videoId}_nb2-1-30280.m4s'
self.tmpVideoPath = f'{tmpDir}/{self.videoId}.mp4'
self.tmpAudioPath = f'{tmpDir}/{self.videoId}.mp3'
self.finalPath = f'{storeDir}/{self.videoName}.mp4'
self.checkRawDir()
def handle(self):
RmZero(self.rawVideoPath, self.tmpVideoPath)
RmZero(self.rawAudioPath, self.tmpAudioPath)
print(self.finalPath)
mix(self.tmpVideoPath, self.tmpAudioPath, self.finalPath)
def checkRawDir(self): # some videos are awfully named
if not (
os.path.exists(self.rawVideoPath) and
os.path.exists(self.rawAudioPath)
):
b = os.listdir(bliVideoDir)
m4ss = filter(lambda x: x.endswith('.m4s'), b)
dirs = list(map(lambda x: os.path.join(bliVideoDir, x), m4ss))
self.rawVideoPath = dirs[0]
self.rawAudioPath = dirs[1]
def getInfo(videoInfoPath):
with open(videoInfoPath, 'r', encoding='utf8') as f:
tmp = f.read()
tmp = json.loads(tmp)
videoName = tmp['title']
videoId = tmp['cid']
return videoName, videoId
def RmZero(inBinPath: str, outBinPath: str):
with open(inBinPath, 'rb') as inFile:
inFile.seek(9) # skip the first nine b'0'
with open(outBinPath, 'wb') as outFile:
for part in iter(lambda: inFile.read(32768), b''):
outFile.write(part)
def mix(videoPath, audioPath, outPath):
command = f'\"{ffmpegPath}\" -i \"{videoPath}\" -i \"{audioPath}\" -codec copy \"{outPath}\"'
subprocess.run(command, shell=True)
if __name__ == '__main__':
try:
os.mkdir(tmpDir)
except FileExistsError:
print('tmp folder already exists, continuing...')
except Exception as e:
print(e)
print('please confirm storeDir exists')
dirs = os.listdir(bliDownDir)
rawdirs = map(lambda x: os.path.join(bliDownDir, x), dirs)
bliVideoDirs = filter(os.path.isdir, rawdirs)
for bliVideoDir in bliVideoDirs:
video = videos(bliVideoDir)
video.handle()
print('###################')
print('Finished')
print(f'Don\'t forget to delete {tmpDir} !!!!!')
参考资料:
Comments NOTHING