python 手写m3u8多线程下载器

现在几乎所有的视频都是m3u8视频流了。自己尝试手写了一个简单的m3u8下载器,调试了之后,发现挺好用的,只需输入m3u8链接,文件名及线程数n,就可以下载了。理论上线程数越大,下载速度越快,无上限,当然要看自己电脑配置和网速了。

之所以设置线程,是因为如果对m3u8文件中的片段一个一个的下载,耗时会非常长。同时如果某个片段下载很慢,就会极大的影响整体的下载速度。简单的设置成多线程,就相当于有多个片段同时下载,可以成倍的缩短下载时间,即便某个片段下载缓慢,对整体的进度也影响不大。30个线程下一般可以达到6M/s的下载速度。

直接放代码,复制后可直接运行。windows电脑中,建议脚本在cmd下运行,这样进度条可以正常显示

import os
import threading
from queue import Queue
import requests

def downm3u8(m3u8_url, name, directory, t_num):
    def get_content(url):#不停尝试,直至成功获取内容
        while True:
            try:
                content = requests.get(url).content
                return content
            except:
                continue

    def download(pre, n, out_folder):
        while True:
            ts = task.get()#获取单个视频片段后缀
            lock.acquire()
            count[0] += 1
            print('[' + '#' * int((count[0] + 1) / (n / float(100))) + ' ' * (
                    100 - int((count[0] + 1) / (n / float(100)))) + ']' + '--[' + str(count[0]) + '/' + str(
                n) + ']', end='\r', flush=True)#输出进度条
#            print('已下载:[ %s/%s ]'%(count[0],n))#如果不用上面的进度条,可以把上面的print的三行内容前面加#,然后把当前行前面的#去掉,显示进度
            lock.release()
            ts_url = pre + ts#拼接单个视频片段下载链接
            number = count[0] - 1
            content = get_content(ts_url)
            with open('%s/%s.mp4' % (out_folder, number), 'ab') as f:
                f.write(content)#写入文件
            task.task_done()

    try:#在当前目录下建文件夹,保存下载结果
        os.mkdir('%s/%s' % (directory, name))
    except:
        pass
    pre = m3u8_url.rstrip(m3u8_url.split('/')[-1])#m3u8链接前缀
    lines = requests.get(m3u8_url).text.strip().split('\n')#获取m3u8文件内容
    ts_list = [line.split('/')[-1] for line in lines if line.startswith('#') == False]#获取m3u8文件下视频流后缀
    n = len(ts_list)#视频流片段数
    count = [0]#用来对下载片段命名及计算进度
    dict = {
     }
    lock = threading.Lock()#线程锁防止几个线程同时输出错乱
    task = Queue()#设置队列
    for i in range(int(t_num)):
        t = threading.Thread(target=download, args=(pre, n, '%s/%s' % (directory, name)))
        t.daemon = True
        t.start()
    for ts in ts_list:
        task.put(ts)
    task.join()

    print('\n' + '%s has downloaded successfully' % name)
    print('***Merging film***')#对下载结果进行合并
    fo = open('%s/%s/%s.mp4' % (directory, name, name), 'ab')
    for i in range(n):
        fl = open('%s/%s/%s.mp4' % (directory, name, i), 'rb')
        fo.write(fl.read())
        fl.close()
        os.remove('%s/%s/%s.mp4' % (directory, name, i))
    fo.close()
    print('Film is in %s' % directory)


if __name__ == '__main__':
    input1 = input('m3u8 link:')#输入m3u8链接(例如:https://wuji.zhulong-zuida.com/20190706/762_c260ca6c/800k/hls/index.m3u8)
    input2 = input('film name:')#输入文件名称(支持中文名)
    thread_number = input('threading number:')#输入要设置的线程数字,一般可以设置10-30,电脑都跑得动。
    directory = os.getcwd().replace('\\', '/')
    downm3u8(input1, input2, directory, thread_number)

上面是简单版的下载,平时使用上面的脚本即可。m3u8链接可以通过建议通过猫抓插件获取,比较方便。
如果m3u8有加密,可以使用下面的脚本。其实下面的脚本一样可以下载未加密的m3u8链接,但是Crypto.Cipher模块安装的时候容易报错,所以,如果并非必须要用,就不推荐了。

import os
import threading
from queue import Queue
from Crypto.Cipher import AES
import requests


def downm3u8(m3u8_url, name, directory, t_num):
    def get_content(url):
        while True:
            try:
                content = requests.get(url).content
                return content
            except:
                continue

    def download(pre, n, key, out_folder):
        while True:
            ts = task.get()
            lock.acquire()
            count[0] += 1
            print('[' + '#' * int((count[0] + 1) / (n / float(100))) + ' ' * (
                    100 - int((count[0] + 1) / (n / float(100)))) + ']' + '--[' + str(count[0]) + '/' + str(
                n) + ']', end='\r', flush=True)
#            print('已下载:[ %s/%s ]'%(count[0],n))
            lock.release()
            ts_url = pre + ts
            number = count[0] - 1
            content = get_content(ts_url)
            if len(key) != 0:
                cryptor = AES.new(key, AES.MODE_CBC, key)
                content = cryptor.decrypt(content)
            with open('%s/%s.mp4' % (out_folder, number), 'ab') as f:
                f.write(content)
            task.task_done()

    try:
        os.mkdir('%s/%s' % (directory, name))
    except:
        pass
    pre = m3u8_url.rstrip(m3u8_url.split('/')[-1])
    lines = requests.get(m3u8_url).text.strip().split('\n')
    ts_list = [line.split('/')[-1] for line in lines if line.startswith('#') == False]
    key = b''
    for line in lines:
        if 'AES-128' in line:
            key_url = pre + line.split('"')[1].split('/')[-1]
            key = requests.get(key_url).content
    n = len(ts_list)
    count = [0]
    dict = {
     }
    lock = threading.Lock()
    task = Queue()
    for i in range(int(t_num)):
        t = threading.Thread(target=download, args=(pre, n, key, '%s/%s' % (directory, name)))
        t.daemon = True
        t.start()
    for ts in ts_list:
        task.put(ts)
    task.join()

    print('\n' + '%s has downloaded successfully' % name)
    print('***Merging film***')
    fo = open('%s/%s/%s.mp4' % (directory, name, name), 'ab')
    for i in range(n):
        fl = open('%s/%s/%s.mp4' % (directory, name, i), 'rb')
        fo.write(fl.read())
        fl.close()
        os.remove('%s/%s/%s.mp4' % (directory, name, i))
    fo.close()
    print('Film is in %s' % directory)


if __name__ == '__main__':
    input1 = input('m3u8 link:')
    input2 = input('film name:')
    thread_number = input('threading number:')
    directory = os.getcwd().replace('\\', '/')
    downm3u8(input1, input2, directory, thread_number)

github项目传送门:github链接

你可能感兴趣的:(脚本,python,多线程,m3u8下载)