Nemo2011 / bilibili-api

哔哩哔哩常用API调用。支持视频、番剧、用户、频道、音频等功能。原仓库地址:https://github.com/MoyuScript/bilibili-api
https://nemo2011.github.io/bilibili-api/
GNU General Public License v3.0
2.17k stars 209 forks source link

[提问] 如何下载视频片段? #602

Closed TZFC closed 10 months ago

TZFC commented 10 months ago

Python 版本: 3.11.6

模块版本: dev

运行环境: Linux


我想实现下载视频片段的功能。需求来自直播切片,下载完整的好几小时录播导入剪辑软件对网络和电脑都是没必要的压力。

看了示例代码, 感觉大概应该在获取到的url上添加额外的参数?类似视频精准空降是的"t=78.7”? 然而直接附上“&t=78.7”并没有从第78.7秒开始,参数名称不一样。

观察到访问精准空降链接时,“78.7”这个值始终出现在 referer 里面,于是尝试在 download_url 时

HEADERS["Referer"]="https://www.bilibili.com/video/BV1Zp4y1T7iu/?t=78.7"

然而这么做并没有任何作用,还是把视频完整得下载下来了

请问应该怎么达成从视频中途下载的效果呢?

另外,resp = await sess.get(url) 似乎会直接把视频下载完整后再return?有没有什么办法可以在下载到某个时间点打断呢?

TZFC commented 10 months ago

找到了,从空降链接打开视频网页,所有请求的 Headers 里面包含

Range: bytes=13327530-15109143

我把这个塞到HEADERS里去试试

Update:

HEADERS["range"] = 'bytes=13327530-15109143'

成功只下载了一小段视频和音频,然而 ffmpeg却打不开这样的文件了

[in#0 @ 000001efb1a8b380] Error opening input: Invalid data found when processing input
Error opening input file video_temp.m4s.
Error opening input files: Invalid data found when processing input

Update2: range 从0开始就能打得开!

这解决了如何切断下载的问题!

但是如何从中途开始下载,又要让文件打得开呢?大概是音视频的时间码对不齐的原因?对于视频编码我是一点也不懂www 我尝试的是:

async def get_max_range(url: str) -> int:
    HEADERS["range"] = 'bytes=0-1'
    async with AsyncClient(headers=HEADERS) as sess:
        resp = await sess.get(url)
        return int(resp.headers.get('content-range').split("/")[-1])

async def download_url(url: str, out: str, info: str, range: str):
    HEADERS["range"] = range
    (下同原函数)

max_video_range = await get_max_range(best_streams[0].url)
max_audio_range = await get_max_range(best_streams[1].url)
await download_url(best_streams[0].url, "video_temp.m4s", "视频流", range=f'bytes={int(max_video_range*0.4)}-{int(max_video_range*0.6)}')
await download_url(best_streams[1].url, "audio_temp.m4s", "音频流", range=f'bytes={int(max_audio_range*0.4)}-{int(max_audio_range*0.6)}')

感觉是强行取整到bytes导致的音视频时间码不一致。这可能需要些ffmpeg的技巧来对齐了,对此我一窍不通w求大佬指路

把网上找到的修复MP4的ffmpeg指令揉到一起了,也不管用

{FFMPEG_PATH} -err_detect ignore_err -i video_temp.m4s -i audio_temp.m4s -copy_unknown -vcodec copy -acodec copy output.mp4'
{FFMPEG_PATH} -err_detect ignore_err -i video_temp.m4s -i audio_temp.m4s -ignore_unknown -vcodec copy -acodec copy output.mp4'
TZFC commented 10 months ago

我的完整代码:bv2video.py

z0z0r4 commented 10 months ago

你得参考web端下载片段m4s,周末再看看

z0z0r4 commented 10 months ago

评价是太麻烦了...做不到,我不了解dash规范,拿到了segment index也不会用

z0z0r4 commented 10 months ago

你可以直接用 yt-dlp 来下载指定片段,具体好像是调的 ffmpeg 看不懂,这个方式不会下完整视频再剪辑

yt-dlp https://www.bilibili.com/video/BV1du4y1K7Di --download-section "*00:01:30-00:02:38"