python 实现多线程下载m3u8格式视频并使用fmmpeg合并


Posted in Python onNovember 15, 2019

电影之类的长视频好像都用m3u8格式了,这就导致了多线程下载视频的意义不是很大,都是短视频,线不线程就没什么意义了嘛。

我们知道,m3u8的链接会下载一个文档,相当长,半小时的视频,应该有接近千行ts链接。

这些ts链接下载成ts文件,就是碎片化的视频,加以合并,就成了需要的视频。

那,即便网速很快,下几千行视频,效率也就低了,更何况还要合并。我就琢磨了一下午,怎么样才能多线程下载m3u8格式的视频呢?

先上代码,再说重难点:

import datetime
import os
import re
import threading
import requests
from queue import Queue
# 预下载,获取m3u8文件,读出ts链接,并写入文档
def down():
  # m3u8链接
  url = 'https://ali-video.acfun.cn/mediacloud/acfun/acfun_video/segment/3zf_GAW6nFMuDXrTLL89OZYOZ4mwxGoASH6UcZbsj1_6eAxUxtp3xm8wFmGMNOnZ.m3u8?auth_key=1573739375-474267152-0-a5aa2b6df4cb4168381bf8b04d88ddb1'
  # 当ts文件链接不完整时,需拼凑
  # 大部分网站可使用该方法拼接,部分特殊网站需单独拼接
  base_url = re.split(r"[a-zA-Z0-9-_\.]+\.m3u8", url)[0]
  # print(base_url)
  resp = requests.get(url)
  m3u8_text = resp.text
  # print(m3u8_text)
  # 按行拆分m3u8文档
  ts_queue = Queue(10000)
  lines = m3u8_text.split('\n')
  # 找到文档中含有ts字段的行
  concatfile = 'cache/' + "s" + '.txt'
  for line in lines:
    if '.ts' in line:
      if 'http' in line:
        # print("ts>>", line)
        ts_queue.put(line)
      else:
        line = base_url + line
        ts_queue.put(line)
        # print('ts>>',line)
      filename = re.search('([a-zA-Z0-9-]+.ts)', line).group(1).strip()
      # 一定要先写文件,因为线程的下载是无序的,文件无法按照
      # 123456。。。去顺序排序,而文件中的命名也无法保证是按顺序的
      # 这会导致下载的ts文件无序,合并时,就会顺序错误,导致视频有问题。
      open(concatfile, 'a+').write("file %s\n" % filename)
  return ts_queue,concatfile
# 线程模式,执行线程下载
def run(ts_queue):
  tt_name = threading.current_thread().getName()
  while not ts_queue.empty():
    url = ts_queue.get()
    r = requests.get(url, stream=True)
    filename = re.search('([a-zA-Z0-9-]+.ts)', url).group(1).strip()
    with open('cache/' + filename, 'wb') as fp:
      for chunk in r.iter_content(5242):
        if chunk:
          fp.write(chunk)
    print(tt_name + " " + filename + ' 下载成功')
# 视频合并方法,使用ffmpeg
def merge(concatfile, name):
  try:
    path = 'cache/' + name + '.mp4'
    command = 'ffmpeg -y -f concat -i %s -crf 18 -ar 48000 -vcodec libx264 -c:a aac -r 25 -g 25 -keyint_min 25 -strict -2 %s' % (concatfile, path)
    os.system(command)
    print('视频合并完成')
  except:
    print('合并失败')
if __name__ == '__main__':
  name = input('请输入视频名称:')
  start = datetime.datetime.now().replace(microsecond=0)
  s,concatfile = down()
  # print(s,concatfile)
  threads = []
  for i in range(15):
    t = threading.Thread(target=run, name='th-'+str(i), kwargs={'ts_queue': s})
    threads.append(t)
  for t in threads:
    t.start()
  for t in threads:
    t.join()
  end = datetime.datetime.now().replace(microsecond=0)
  print('下载耗时:' + str(end - start))
  merge(concatfile,name)
  over = datetime.datetime.now().replace(microsecond=0)
  print('合并耗时:' + str(over - end))

效果图:

python 实现多线程下载m3u8格式视频并使用fmmpeg合并

代码开始:自己输入视频名称(也可以去原网站爬名称)

查看下载耗时,fmmpeg开始合并:

python 实现多线程下载m3u8格式视频并使用fmmpeg合并

合并耗时:

python 实现多线程下载m3u8格式视频并使用fmmpeg合并

7分多钟,90个ts文件,接近40MB。两秒下载完成。

更大的文件,开更多的线程。

然后我们画画重难点:

第一:ts文件命名问题。

我们知道,每一个线程启动,除了队列不会重复,那么代码里都会重新跑(线程里的代码),那么,1.ts,2.ts....这种命名是不可能的了,文件会被覆盖。命名我使用了ts链接中的部分链接。

第二:合并问题。

文件的合并是根据文档内的顺序,也就是,如果边下载边合并,那么,线程的无序性导致下载无序,文件写入也就无序化了,合并时,时间线会错误,合出来的视频就无法看。因此,文件要提前写好才行,这和命名有很大的关联,看代码即知。

第三:有的m3u8是特殊处理的,代码具有一定的局限性。

写的时候挺难的,脑子都乱了,就这些吧,记录一下。

对了,贴一下下载的图:90个ts文件,一个mp4文件,一个文档。

python 实现多线程下载m3u8格式视频并使用fmmpeg合并

总结

以上所述是小编给大家介绍的python 实现多线程下载m3u8格式视频并使用fmmpeg合并,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
详细解读Python的web.py框架下的application.py模块
May 02 Python
在Linux命令行终端中使用python的简单方法(推荐)
Jan 23 Python
python获取外网IP并发邮件的实现方法
Oct 01 Python
Python使用pymongo模块操作MongoDB的方法示例
Jul 20 Python
对python实时得到鼠标位置的示例讲解
Oct 14 Python
springboot配置文件抽离 git管理统 配置中心详解
Sep 02 Python
Python3.7黑帽编程之病毒篇(基础篇)
Feb 04 Python
Python 日期时间datetime 加一天,减一天,加减一小时一分钟,加减一年
Apr 16 Python
详解向scrapy中的spider传递参数的几种方法(2种)
Sep 28 Python
Python实现机器学习算法的分类
Jun 03 Python
详解Python常用的魔法方法
Jun 03 Python
5行Python代码实现一键批量扣图
Jun 29 Python
浅析python内置模块collections
Nov 15 #Python
Python树莓派学习笔记之UDP传输视频帧操作详解
Nov 15 #Python
Python numpy数组转置与轴变换
Nov 15 #Python
python修改文件内容的3种方法详解
Nov 15 #Python
Python实现基于socket的udp传输与接收功能详解
Nov 15 #Python
python根据文本生成词云图代码实例
Nov 15 #Python
解决django后台管理界面添加中文内容乱码问题
Nov 15 #Python
You might like
星际初学者游戏中永远要做的事
2020/03/04 星际争霸
环境会对咖啡种植有什么影响
2021/03/03 咖啡文化
mysql下创建字段并设置主键的php代码
2010/05/16 PHP
zend framework多模块多布局配置
2011/02/26 PHP
php实现的简单数据库操作Model类
2016/11/16 PHP
php中类和对象:静态属性、静态方法
2017/04/09 PHP
PHP使用OB缓存实现静态化功能示例
2019/03/23 PHP
清除网页历史记录,屏蔽后退按钮!
2008/12/22 Javascript
IE8 中使用加速器(Activities)
2010/05/14 Javascript
JavaScript基础篇之变量作用域、传值、传址的简单介绍与实例
2013/06/29 Javascript
JS Jquery 遍历,筛选页面元素 自动完成(实现代码)
2013/07/08 Javascript
JavaScript中的Math.LN2属性用法详解
2015/06/12 Javascript
AngularJs入门教程之环境搭建+创建应用示例
2016/11/01 Javascript
ThinkJS中如何使用MongoDB的CURD操作
2016/12/13 Javascript
详解vue-validator(vue验证器)
2017/01/16 Javascript
JavaScript 实现 Tab 点击切换实例代码
2017/03/25 Javascript
vue.js组件vue-waterfall-easy实现瀑布流效果
2017/08/22 Javascript
JS大坑之19位数的Number型精度丢失问题详解
2019/04/22 Javascript
js纯前端实现腾讯cos文件上传功能的示例代码
2019/05/14 Javascript
深入浅析golang zap 日志库使用(含文件切割、分级别存储和全局使用等)
2020/02/19 Javascript
javascript设计模式 ? 代理模式原理与用法实例分析
2020/04/16 Javascript
js实现带积分弹球小游戏
2020/07/21 Javascript
[51:39]DOTA2-DPC中国联赛 正赛 Magma vs LBZS BO3 第二场 2月7日
2021/03/11 DOTA
python文件比较示例分享
2014/01/10 Python
使用cx_freeze把python打包exe示例
2014/01/24 Python
python重试装饰器示例
2014/02/11 Python
Python 利用scrapy爬虫通过短短50行代码下载整站短视频
2018/10/29 Python
python版本五子棋的实现代码
2018/12/11 Python
python采集微信公众号文章
2018/12/20 Python
python爬虫的一个常见简单js反爬详解
2019/07/09 Python
python os.path.isfile 的使用误区详解
2019/11/29 Python
使用python编写一个语音朗读闹钟功能的示例代码
2020/07/14 Python
波兰最早的运动鞋精品店之一:Street Supply
2019/08/29 全球购物
高中生逃课检讨书
2014/10/10 职场文书
2015年元旦晚会活动总结(学生会)
2014/11/28 职场文书
spring cloud 配置中心native配置方式
2021/09/25 Java/Android