M3U8下载器
By
jerryxjr1220
at 2021-11-07 • 0人收藏 • 1063人看过
最近网络突然变卡了,看在线视频断断续续的,没办法只能把m3u8下载下来看了.
写了个多线程下载器,有需要的可以拿去用。
PS:只针对一般未加密的m3u8视频
import win.ui; /*DSG{{*/ mainForm = win.form(text="M3U8下载器";right=793;bottom=308;border="dialog frame";max=false) mainForm.add( btnAnalyze={cls="button";text="解析视频地址";left=670;top=18;right=764;bottom=46;z=12}; btnCombine={cls="button";text="合并ts";left=696;top=136;right=764;bottom=164;z=9}; btnMovieUrl={cls="button";text="视频地址:";left=28;top=20;right=129;bottom=46;z=10}; btnRClip={cls="button";text="M3U8地址:";left=27;top=59;right=128;bottom=85;z=1}; btnRun={cls="button";text="开始解析";left=607;top=136;right=675;bottom=164;z=7}; btnSaveDir={cls="button";text="保存目录";left=27;top=101;right=128;bottom=127;z=3}; edtLog={cls="edit";left=27;top=180;right=764;bottom=295;edge=1;hscroll=1;multiline=1;vscroll=1;z=8}; edtM3U8url={cls="edit";left=149;top=57;right=764;bottom=85;edge=1;multiline=1;z=2}; edtMovieUrl={cls="edit";left=150;top=18;right=658;bottom=46;edge=1;multiline=1;z=11}; edtName={cls="edit";left=149;top=136;right=584;bottom=164;edge=1;multiline=1;z=6}; edtSaveDir={cls="edit";left=149;top=97;right=764;bottom=125;edge=1;multiline=1;z=4}; static={cls="static";text="电影名称(可选)";left=27;top=143;right=145;bottom=164;transparent=1;z=5} ) /*}}*/ import fsys; import fsys.ini; import fsys.file; import fsys.dlg; import win.clip; import inet.http; import thread; import thread.command; listener = thread.command(); listener.res_print = function(...){ mainForm.edtLog.print(...); } ini = fsys.ini("/m3u8.ini"); cfg = ini.getSection("M3U8"); var url = cfg.url; if url { mainForm.edtM3U8url.text = url; }; var savedir = cfg.savedir; if savedir { mainForm.edtSaveDir.text = savedir; } var movie = cfg.movie; if movie { mainForm.edtName.text = movie; } var movieurl = cfg.movieurl; if movieurl { mainForm.edtMovieUrl.text = movieurl; } mainForm.running = false; mainForm.btnRClip.oncommand = function(id,event){ var url = win.clip.read(); if url { import inet.url; if inet.url.is(url,0x0/*_URLIS_URL*/) and ..string.endWith(url, "m3u8") { mainForm.edtM3U8url.text = url; mainForm.edtLog.print("粘贴M3U8文件URL") } else { mainForm.edtLog.print("剪切板中的文字不是有效的M3U8文件的URL!") } } else { mainForm.edtLog.print("请先复制M3U8文件URL至剪切板!") } } mainForm.btnSaveDir.oncommand = function(id,event){ var fs = fsys.dlg.opendir(); if fs { mainForm.edtSaveDir.text = fs; mainForm.edtLog.print("设置保存目录成功!") } } mainForm.btnRun.oncommand = function(id,event){ if ..string.len(mainForm.edtM3U8url.text)<1 { mainForm.edtLog.print("请先复制M3U8文件URL至剪切板!") return; } if !inet.url.is(mainForm.edtM3U8url.text,0x0/*_URLIS_URL*/) { mainForm.edtLog.print("此index.m3u8的URL无效!") return; } if ..string.len(mainForm.edtSaveDir.text)<1 { var fs = ..io.fullpath("/") mainForm.edtSaveDir.text = fs; mainForm.edtLog.print("保存目录默认设置为:",fs); } if ..string.len(mainForm.edtName.text)<1 { import time.ole; var tm = ..math.floor(tonumber(time.ole())*1000000); mainForm.edtName.text = tm; mainForm.edtLog.print("默认名称为:",tm); } tsdir = ..string.trim(mainForm.edtSaveDir.text,"\")++"\"++mainForm.edtName.text; http = inet.http("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4506.400",false) var res = http.get(mainForm.edtM3U8url.text); if res and ..string.startWith(res, "#EXTM3U") { ..string.save(..string.trim(tsdir,"\")++"\"++"index.m3u8", res); mainForm.edtLog.print("下载M3U8文件成功!"); } else { mainForm.edtLog.print("下载M3U8文件失败,请重新尝试!"); return; } host = ..string.join(..table.slice(..string.splitEx(mainForm.edtM3U8url.text,"/"),1,#..string.splitEx(mainForm.edtM3U8url.text,"/")-1),"/") tslist = {}; for line in ..string.lines(res) { if ..string.startWith(line, "#EXT") { continue; } else { if !..string.startWith(line, "http") { line = host ++ "/" ++ line; } if ..string.endWith(line, "ts") { ..table.push(tslist, line); } else { mainForm.edtLog.print("不是ts文件,视频可能被加密"); return; } } } mainForm.edtLog.printf("M3U8文件解析成功,共有%d个片段", #tslist); cfg.url = mainForm.edtM3U8url.text; cfg.savedir = mainForm.edtSaveDir.text; cfg.movie = mainForm.edtName.text; cfg.movieurl = mainForm.edtMovieUrl.text; cfg.save(); fsys.createDir(tsdir); mainForm.edtLog.print("创建目录:", tsdir); mainForm.edtLog.print("下载开始......"); //创建下载线程管理器 import thread.dlManager; dlmgr = thread.dlManager(5/*最多允许五个线程同时下载*/); mainForm.running = true; for i,url in tslist { if !mainForm.running { break; } //响应下载事件 dlmgr.onReceiveBegin = function(id,url,filename,statusText,httpStatusCode,totalSize,downSize){ //mainForm.edtLog.print( "片段"++id,statusText,url,"->",filename,fsys.formatSize(totalSize) ) } dlmgr.onReceive = function(id,sizePs,downSize){ //mainForm.edtLog.print( "片段"++id, fsys.formatSize(downSize),fsys.formatSize(sizePs) + "/s"); } dlmgr.onEnd = function(id,savepath,resumePath,contentLength){ if( savepath ){ var rpt = ..math.floor(id/#tslist*70); mainForm.edtLog.text="|"++..string.repeat(rpt, ">")++..string.repeat(70-rpt, "#")++"| 片段"++id++".ts已完成"; } else { mainForm.edtLog.text="|"++..string.repeat(rpt, ">")++..string.repeat(70-rpt, "#")++"| 片段"++id++".ts已停止"; } //fsys.delete(resumePath) } dlmgr.onError = function(id,err){ mainForm.edtLog.print("片段"++id,err); } if ..io.exist(tsdir++"\"++i++".ts") { var rpt = ..math.floor(i/#tslist*70); mainForm.edtLog.text="|"++..string.repeat(rpt, ">")++..string.repeat(70-rpt, "#")++"| 片段"++i++".ts 已存在"; win.delay(30) continue; } //添加下载任务非常简单,push下载参数就可以了 dlmgr.push( id = i; url = url; filename=i++".ts"; //文件名可以省略 savedir = tsdir; ) win.delay(50); } } import thread.event; mainForm.onClose = function(hwnd,message,wParam,lParam){ mainForm.text = "正在等待关闭"; mainForm.running = false; if dlmgr { dlmgr.quit(); } } mainForm.btnCombine.oncommand = function(id,event){ if ..string.len(mainForm.edtSaveDir.text)<1 { var fs = ..io.fullpath("/") mainForm.edtSaveDir.text = fs; mainForm.edtLog.print("保存目录默认设置为:",fs); } if ..string.len(mainForm.edtName.text)<1 { import time.ole; var tm = ..math.floor(tonumber(time.ole())*1000000); mainForm.edtName.text = tm; mainForm.edtLog.print("默认名称为:",tm); } tsdir = ..string.trim(mainForm.edtSaveDir.text,"\")++"\"++mainForm.edtName.text; if !..io.exist(..string.trim(tsdir,"\")++"\"++"index.m3u8") { mainForm.edtLog.print("index.m3u8下载未完成,无法合并!") return; } res = ..string.load(..string.trim(tsdir,"\")++"\"++"index.m3u8"); tslist = {}; for line in ..string.lines(res) { if ..string.startWith(line, "#EXT") { continue; } else { if ..string.endWith(line, "ts") { ..table.push(tslist, line); } } } if !..io.exist(tsdir++"\"++#tslist++".ts") { mainForm.edtLog.print("下载未完成,无法合并!") return; } mainForm.edtLog.print("开始合并ts文件:", tsdir++"\"++mainForm.edtName.text++".mp4"); f = fsys.file(tsdir++"\"++mainForm.edtName.text++".mp4","a+b"); for i=1;#tslist { try { tf = fsys.file(tsdir++"\"++i++".ts", "rb"); f.write(tf.readAll()); tf.close(); } catch (e) { mainForm.edtLog.print(e); } var rpt = ..math.floor(i/#tslist*70); mainForm.edtLog.text="|"++..string.repeat(rpt, ">")++..string.repeat(70-rpt, "#")++"| 片段"++i++".ts 已写入"; win.delay(20); } f.close(); mainForm.edtLog.print("ts文件合并完成!",tsdir++"\"++mainForm.edtName.text++".mp4") } mainForm.btnMovieUrl.oncommand = function(id,event){ var url = win.clip.read(); if url { import inet.url; if inet.url.is(url,0x0/*_URLIS_URL*/) { mainForm.edtMovieUrl.text = url; mainForm.edtLog.print("粘贴视频URL") } else { mainForm.edtLog.print("剪切板中的文字不是有效的视频URL!") } } else { mainForm.edtLog.print("请先复制视频URL至剪切板!") } } mainForm.btnAnalyze.oncommand = function(id,event){ if ..string.len(mainForm.edtMovieUrl.text)<1 { mainForm.edtLog.print("请先复制视频URL至剪切板!") return; } if !inet.url.is(mainForm.edtMovieUrl.text,0x0/*_URLIS_URL*/) { mainForm.edtLog.print("此视频URL无效!") return; } mainForm.edtLog.print("开始解析视频URL...") cfg.movieurl = mainForm.edtMovieUrl.text; cfg.save(); var mhttp = inet.http("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4506.400",false) var res = mhttp.get(mainForm.edtMovieUrl.text); var m3u8s = ..string.matches(res, `(https*\:[^\"]+?index\.m3u8)`); var moviename = ..string.match(res, `vod_name\'\s*\:\s*\"([^\"]+?)\"`); if moviename { mainForm.edtLog.print("视频名称:",moviename) mainForm.edtName.text = ..string.replace(moviename, "\s",""); } if ..table.len(m3u8s)>=1 { mainForm.edtLog.print("找到如下index.m3u8的URL:") mainForm.edtM3U8url.text = ..string.replace(m3u8s[1][1],"\\",""); } for k,v in m3u8s { var s = ..string.replace(v[1],"\\","") mainForm.edtLog.print(s); } } mainForm.show(); return win.loopMessage();
4 个回复 | 最后更新于 2021-11-08
2021-11-08
#2
改进了一下,现在可以自动识别是否是aes加密,并自动进行解密。
import win.ui; /*DSG{{*/ mainForm = win.form(text="M3U8下载器";right=793;bottom=308;border="dialog frame";max=false) mainForm.add( btnAnalyze={cls="button";text="解析视频地址";left=670;top=18;right=764;bottom=46;z=12}; btnCombine={cls="button";text="合并ts";left=696;top=136;right=764;bottom=164;z=9}; btnMovieUrl={cls="button";text="视频地址:";left=28;top=20;right=129;bottom=46;z=10}; btnRClip={cls="button";text="M3U8地址:";left=27;top=59;right=128;bottom=85;z=1}; btnRun={cls="button";text="开始解析";left=607;top=136;right=675;bottom=164;z=7}; btnSaveDir={cls="button";text="保存目录";left=27;top=101;right=128;bottom=127;z=3}; edtLog={cls="edit";left=27;top=180;right=764;bottom=295;edge=1;hscroll=1;multiline=1;vscroll=1;z=8}; edtM3U8url={cls="edit";left=149;top=57;right=764;bottom=85;edge=1;multiline=1;z=2}; edtMovieUrl={cls="edit";left=150;top=18;right=658;bottom=46;edge=1;multiline=1;z=11}; edtName={cls="edit";left=149;top=136;right=584;bottom=164;edge=1;multiline=1;z=6}; edtSaveDir={cls="edit";left=149;top=97;right=764;bottom=125;edge=1;multiline=1;z=4}; static={cls="static";text="电影名称(可选)";left=27;top=143;right=145;bottom=164;transparent=1;z=5} ) /*}}*/ import fsys; import fsys.ini; import fsys.file; import fsys.dlg; import win.clip; import inet.http; import thread; import thread.command; listener = thread.command(); listener.res_print = function(...){ mainForm.edtLog.print(...); } ini = fsys.ini("/m3u8.ini"); cfg = ini.getSection("M3U8"); var url = cfg.url; if url { mainForm.edtM3U8url.text = url; }; var savedir = cfg.savedir; if savedir { mainForm.edtSaveDir.text = savedir; } var movie = cfg.movie; if movie { mainForm.edtName.text = movie; } var movieurl = cfg.movieurl; if movieurl { mainForm.edtMovieUrl.text = movieurl; } mainForm.running = false; mainForm.btnRClip.oncommand = function(id,event){ var url = win.clip.read(); if url { import inet.url; if inet.url.is(url,0x0/*_URLIS_URL*/) and ..string.endWith(url, "m3u8") { mainForm.edtM3U8url.text = url; mainForm.edtLog.print("粘贴M3U8文件URL") } else { mainForm.edtLog.print("剪切板中的文字不是有效的M3U8文件的URL!") } } else { mainForm.edtLog.print("请先复制M3U8文件URL至剪切板!") } } mainForm.btnSaveDir.oncommand = function(id,event){ var fs = fsys.dlg.opendir(); if fs { mainForm.edtSaveDir.text = fs; mainForm.edtLog.print("设置保存目录成功!") } } mainForm.btnRun.oncommand = function(id,event){ if ..string.len(mainForm.edtM3U8url.text)<1 { mainForm.edtLog.print("请先复制M3U8文件URL至剪切板!") return; } if !inet.url.is(mainForm.edtM3U8url.text,0x0/*_URLIS_URL*/) { mainForm.edtLog.print("此index.m3u8的URL无效!") return; } if ..string.len(mainForm.edtSaveDir.text)<1 { var fs = ..io.fullpath("/") mainForm.edtSaveDir.text = fs; mainForm.edtLog.print("保存目录默认设置为:",fs); } if ..string.len(mainForm.edtName.text)<1 { import time.ole; var tm = ..math.floor(tonumber(time.ole())*1000000); mainForm.edtName.text = tm; mainForm.edtLog.print("默认名称为:",tm); } tsdir = ..string.trim(mainForm.edtSaveDir.text,"\")++"\"++mainForm.edtName.text; http = inet.http("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4506.400",false) var res = http.get(mainForm.edtM3U8url.text); if res and ..string.startWith(res, "#EXTM3U") { ..string.save(..string.trim(tsdir,"\")++"\"++"index.m3u8", res); mainForm.edtLog.print("下载M3U8文件成功!"); } else { mainForm.edtLog.print("下载M3U8文件失败,请重新尝试!"); return; } tslist = {}; for line in ..string.lines(res) { if ..string.startWith(line, "#EXT") { var keyurl = ..string.match(line, `URI\s*\=\s*\"(http.*?key\.key)`); if keyurl { var key = http.get(keyurl); ..string.save(..string.trim(tsdir,"\")++"\"++"key.key", key); } continue; } else { if ..string.endWith(line, "ts") { ..table.push(tslist, line); } else { if ..string.len(line)<1 { continue; } mainForm.edtLog.print(line); mainForm.edtLog.print("不是ts文件,视频可能被加密"); return; } } } mainForm.edtLog.printf("M3U8文件解析成功,共有%d个片段", #tslist); cfg.url = mainForm.edtM3U8url.text; cfg.savedir = mainForm.edtSaveDir.text; cfg.movie = mainForm.edtName.text; cfg.movieurl = mainForm.edtMovieUrl.text; cfg.save(); fsys.createDir(tsdir); mainForm.edtLog.print("创建目录:", tsdir); mainForm.edtLog.print("下载开始......"); //创建下载线程管理器 import thread.dlManager; dlmgr = thread.dlManager(5/*最多允许五个线程同时下载*/); mainForm.running = true; for i,url in tslist { if !mainForm.running { break; } //响应下载事件 dlmgr.onReceiveBegin = function(id,url,filename,statusText,httpStatusCode,totalSize,downSize){ //mainForm.edtLog.print( "片段"++id,statusText,url,"->",filename,fsys.formatSize(totalSize) ) } dlmgr.onReceive = function(id,sizePs,downSize){ //mainForm.edtLog.print( "片段"++id, fsys.formatSize(downSize),fsys.formatSize(sizePs) + "/s"); } dlmgr.onEnd = function(id,savepath,resumePath,contentLength){ if( savepath ){ var rpt = ..math.floor(id/#tslist*70); mainForm.edtLog.text="|"++..string.repeat(rpt, ">")++..string.repeat(70-rpt, "#")++"| 片段"++id++".ts已完成"; } else { mainForm.edtLog.text="|"++..string.repeat(rpt, ">")++..string.repeat(70-rpt, "#")++"| 片段"++id++".ts已停止"; } //fsys.delete(resumePath) } dlmgr.onError = function(id,err){ mainForm.edtLog.print("片段"++id,err); } if ..io.exist(tsdir++"\"++i++".ts") { var rpt = ..math.floor(i/#tslist*70); mainForm.edtLog.text="|"++..string.repeat(rpt, ">")++..string.repeat(70-rpt, "#")++"| 片段"++i++".ts 已存在"; win.delay(30) continue; } //添加下载任务非常简单,push下载参数就可以了 dlmgr.push( id = i; url = url; filename=i++".ts"; //文件名可以省略 savedir = tsdir; ) win.delay(50); } } import thread.event; mainForm.onClose = function(hwnd,message,wParam,lParam){ mainForm.text = "正在等待关闭"; mainForm.running = false; if dlmgr { dlmgr.quit(); } } mainForm.btnCombine.oncommand = function(id,event){ http = inet.http("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4506.400",false) if ..string.len(mainForm.edtSaveDir.text)<1 { var fs = ..io.fullpath("/") mainForm.edtSaveDir.text = fs; mainForm.edtLog.print("保存目录默认设置为:",fs); } if ..string.len(mainForm.edtName.text)<1 { import time.ole; var tm = ..math.floor(tonumber(time.ole())*1000000); mainForm.edtName.text = tm; mainForm.edtLog.print("默认名称为:",tm); } tsdir = ..string.trim(mainForm.edtSaveDir.text,"\")++"\"++mainForm.edtName.text; if !..io.exist(..string.trim(tsdir,"\")++"\"++"index.m3u8") { mainForm.edtLog.print("index.m3u8下载未完成,无法合并!") return; } res = ..string.load(..string.trim(tsdir,"\")++"\"++"index.m3u8"); tslist = {}; for line in ..string.lines(res) { if ..string.startWith(line, "#EXT") { var keyurl = ..string.match(line, `URI\s*\=\s*\"(http.*?key\.key)`); if keyurl { var key = http.get(keyurl); ..string.save(..string.trim(tsdir,"\")++"\"++"key.key", key); } continue; } else { if ..string.endWith(line, "ts") { ..table.push(tslist, line); } } } if !..io.exist(tsdir++"\"++#tslist++".ts") { mainForm.edtLog.print("下载未完成,无法合并!") return; } mainForm.edtLog.print("开始合并ts文件:", tsdir++"\"++mainForm.edtName.text++".mp4"); f = fsys.file(tsdir++"\"++mainForm.edtName.text++".mp4","a+b"); if ..io.exist(..string.trim(tsdir,"\")++"\"++"key.key") { import crypt.aes; aes = crypt.aes(); var fkey = ..io.open(..string.trim(tsdir,"\")++"\"++"key.key","r"); aes.setPassword(fkey.read(-1)); fkey.close(); for i=1;#tslist { try { tf = fsys.file(tsdir++"\"++i++".ts", "rb"); f.write(aes.decrypt(tf.readAll())); tf.close(); } catch (e) { mainForm.edtLog.print(e); } var rpt = ..math.floor(i/#tslist*70); mainForm.edtLog.text="|"++..string.repeat(rpt, ">")++..string.repeat(70-rpt, "#")++"| 片段"++i++".ts 已写入"; win.delay(20); } } else { for i=1;#tslist { try { tf = fsys.file(tsdir++"\"++i++".ts", "rb"); f.write(tf.readAll()); tf.close(); } catch (e) { mainForm.edtLog.print(e); } var rpt = ..math.floor(i/#tslist*70); mainForm.edtLog.text="|"++..string.repeat(rpt, ">")++..string.repeat(70-rpt, "#")++"| 片段"++i++".ts 已写入"; win.delay(20); } } f.close(); mainForm.edtLog.text = null; mainForm.edtLog.print("ts文件合并完成!",tsdir++"\"++mainForm.edtName.text++".mp4") } mainForm.btnMovieUrl.oncommand = function(id,event){ var url = win.clip.read(); if url { import inet.url; if inet.url.is(url,0x0/*_URLIS_URL*/) { mainForm.edtMovieUrl.text = url; mainForm.edtLog.print("粘贴视频URL") } else { mainForm.edtLog.print("剪切板中的文字不是有效的视频URL!") } } else { mainForm.edtLog.print("请先复制视频URL至剪切板!") } } mainForm.btnAnalyze.oncommand = function(id,event){ if ..string.len(mainForm.edtMovieUrl.text)<1 { mainForm.edtLog.print("请先复制视频URL至剪切板!") return; } if !inet.url.is(mainForm.edtMovieUrl.text,0x0/*_URLIS_URL*/) { mainForm.edtLog.print("此视频URL无效!") return; } mainForm.edtLog.print("开始解析视频URL...") cfg.movieurl = mainForm.edtMovieUrl.text; cfg.save(); var mhttp = inet.http("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4506.400",false) var res = mhttp.get(mainForm.edtMovieUrl.text); var m3u8s = ..string.matches(res, `(https*\:[^\"]+?index\.m3u8)`); var moviename = ..string.match(res, `vod_name\'\s*\:\s*\"([^\"]+?)\"`); if moviename { mainForm.edtLog.print("视频名称:",moviename) mainForm.edtName.text = ..string.replace(moviename, "\s",""); } if ..table.len(m3u8s)>=1 { mainForm.edtLog.print("找到如下index.m3u8的URL:") mainForm.edtM3U8url.text = ..string.replace(m3u8s[1][1],"\\",""); } else { mainForm.edtLog.print("未找到index.m3u8,请确认是否是有效的URL!") } for k,v in m3u8s { var s = ..string.replace(v[1],"\\","") mainForm.edtLog.print(s); } } mainForm.show(); return win.loopMessage();
登录后方可回帖
我是主要针对 https://www.bk520.com/ 上面的视频进行解析,不保证适用所有的m3u8文件