朋友推薦了一個比較隱蔽的盜版電影觀看網站,網速特別慢觀看不爽,就想是否是能夠下載下來看,因而就寫了這個小工具java
首先,你要有能力在網頁裏面找到這個M3U8的索引文件,相信對於一個開發人員這個應該很容易,經過瀏覽器F12找到了這個索引文件,以下,我只截取了一部分,這個文件簡單講一下就是把一個視頻切分紅了好多小片斷,而這個文件就是他們的目錄文件,找到這個就容易了,接下來,把裏面的每一個視頻片斷下載下來合成就能夠了。node
第一步,下載索引文件瀏覽器
public static String getIndexFile(String urlpath){ try{ URL url = new URL(urlpath); //下在資源 BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(),"UTF-8")); String content = "" ; String line; while ((line = in.readLine()) != null) { content += line + "\n"; } in.close(); System.out.println(content); return content; }catch (Exception e){ e.printStackTrace(); } return null; }
#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:8 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:4.971633, ba1620338f71656.ts #EXTINF:3.336667, ba1620338f71657.ts #EXTINF:3.336667, ba1620338f71658.ts #EXTINF:3.336667, ba1620338f71659.ts #EXTINF:3.336667, ba1620338f71660.ts #EXTINF:0.266933, ba1620338f71661.ts #EXT-X-ENDLIST
第二步,解析索引文件多線程
public static List analysisIndex(String content){ Pattern pattern = Pattern.compile(".*ts"); Matcher ma = pattern.matcher(content); List<String> list = new ArrayList<String>(); while(ma.find()){ String s = ma.group(); list.add(s); System.out.println(s); } return list; }
解析的數據結果,把索引中的ts部分解析出來,方法不少dom
ba1620338f7035.ts ba1620338f7036.ts ba1620338f7037.ts ba1620338f7038.ts ba1620338f7039.ts ba1620338f7040.ts ba1620338f7041.ts ba1620338f7042.ts ba1620338f7043.ts ba1620338f7044.ts ba1620338f7045.ts ba1620338f7046.ts ba1620338f7047.ts
第三步,根據第二步的解析結果下載視頻片斷ide
public static List<String> downLoadIndexFile(String preUrlPath,List<String> urlList){ try{ List<String> filePathList = new ArrayList<String>(); String uuid = UUID.randomUUID().toString().replaceAll("-",""); for(String urlpath:urlList){ URL url = new URL(preUrlPath+urlpath); //下在資源 DataInputStream dataInputStream = new DataInputStream(url.openStream()); String fileOutPath = rootPath+File.separator+uuid+File.separator+urlpath; File file = new File(rootPath+File.separator+uuid); if(!file.exists()){ file.mkdirs(); } FileOutputStream fileOutputStream = new FileOutputStream(new File(fileOutPath)); byte[] bytes = new byte[1024]; int length = 0; while ((length = dataInputStream.read(bytes)) != -1) { fileOutputStream.write(bytes, 0, length); } System.out.println("下載完成..."+fileOutPath); dataInputStream.close(); filePathList.add(fileOutPath); } return filePathList; }catch (Exception e){ e.printStackTrace(); } return null; }
到這裏基本大功告成了,寫一個 main方法執行一下就ok了工具
private static String rootPath = "F:\\m3u8dir"; public static void main(String[] args) { String indexPath = "https://youku.cdn2-youku.com/20180710/12991_efbabf56/1000k/hls/index.m3u8"; String prePath = indexPath.substring(0,indexPath.lastIndexOf("/")+1); System.out.println(prePath); //下載索引文件 String indexStr = getIndexFile(indexPath); //解析索引文件 List videoUrlList = analysisIndex(indexStr); //下載視頻片斷 List<String> fileList = downLoadIndexFile(prePath,videoUrlList); }
其實還能夠補充一步代碼合成,額不過着急看電影,就直接用格式工廠合成了,後面會補充網站
--------------------------------------------------------------------------------------------------------------------ui
補充代碼,多線程的寫法,上面的單線程太慢了,同時補充了一個文件合成的方法,不須要格式工廠了this
import java.io.*; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; public class DownFileUtil { private static String rootPath = "F:\\m3u8dir"; public static void main(String[] args) { String indexPath = "https://youku.cdn2-youku.com/20180710/12991_efbabf56/1000k/hls/index.m3u8"; String prePath = indexPath.substring(0,indexPath.lastIndexOf("/")+1); System.out.println(prePath); //下載索引文件 String indexStr = getIndexFile(indexPath); //解析索引文件 List videoUrlList = analysisIndex(indexStr); //生成文件下載目錄 String uuid = UUID.randomUUID().toString().replaceAll("-",""); String fileRootPath = rootPath+File.separator+uuid; File fileDir = new File(fileRootPath); if(!fileDir.exists()){ fileDir.mkdirs(); } //下載視頻片斷,分紅50個線程切片下載 HashMap keyFileMap = new HashMap(); int downForThreadCount = videoUrlList.size()/50; for(int i=0;i<videoUrlList.size();i+=downForThreadCount){ int end = i+downForThreadCount-1; if(end>videoUrlList.size()){ end = videoUrlList.size()-1; } new DownFileUtil().new downLoadNode(videoUrlList,i,end,keyFileMap,prePath,fileRootPath).start(); } //等待下載 while (keyFileMap.size()<videoUrlList.size()){ System.out.println("當前下載數量"+keyFileMap.size()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } //合成視頻片斷 composeFile(fileRootPath+File.separator+uuid+".mp4",keyFileMap); } /** * 視頻片斷合成 * @param fileOutPath * @param keyFileMap */ public static void composeFile(String fileOutPath,HashMap<Integer,String> keyFileMap){ try { FileOutputStream fileOutputStream = new FileOutputStream(new File(fileOutPath)); byte[] bytes = new byte[1024]; int length = 0; for(int i=0;i<keyFileMap.size();i++){ String nodePath = keyFileMap.get(i); File file = new File(nodePath); if(!file.exists()) continue; FileInputStream fis = new FileInputStream(file); while ((length = fis.read(bytes)) != -1) { fileOutputStream.write(bytes, 0, length); } } }catch (Exception e){ } } public static List analysisIndex(String content){ Pattern pattern = Pattern.compile(".*ts"); Matcher ma = pattern.matcher(content); List<String> list = new ArrayList<String>(); while(ma.find()){ String s = ma.group(); list.add(s); System.out.println(s); } return list; } class downLoadNode extends Thread{ private List<String> list ; private int start; private int end; public HashMap keyFileMap ; private String preUrlPath ; private String fileRootPath ; public downLoadNode(List list,int start,int end,HashMap keyFileMap,String preUrlPath,String fileRootPath){ this.list = list; this.end = end; this.start = start; this.keyFileMap = keyFileMap; this.preUrlPath = preUrlPath; this.fileRootPath = fileRootPath; } @Override public void run(){ try{ String uuid = UUID.randomUUID().toString().replaceAll("-",""); for( int i = start;i<=end;i++){ String urlpath = list.get(i); URL url = new URL(preUrlPath+urlpath); //下在資源 DataInputStream dataInputStream = new DataInputStream(url.openStream()); String fileOutPath = fileRootPath+File.separator+urlpath; FileOutputStream fileOutputStream = new FileOutputStream(new File(fileOutPath)); byte[] bytes = new byte[1024]; int length = 0; while ((length = dataInputStream.read(bytes)) != -1) { fileOutputStream.write(bytes, 0, length); } dataInputStream.close(); keyFileMap.put(i,fileOutPath); } System.out.println("第"+start/(end-start)+"組完成,"+"開始位置"+start+",結束位置"+end); }catch (Exception e){ e.printStackTrace(); } } } public static String getIndexFile(String urlpath){ try{ URL url = new URL(urlpath); //下在資源 BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(),"UTF-8")); String content = "" ; String line; while ((line = in.readLine()) != null) { content += line + "\n"; } in.close(); System.out.println(content); return content; }catch (Exception e){ e.printStackTrace(); } return null; } }
補充:部分ts文件被加密的問題
第一步:找到解密的key,通常會在某個請求中返回(額找不到無法解密)
#EXT-X-KEY:METHOD=AES-128,URI="/20180125/NfJJpxIH/1482kb/hls/key.key"
第二步:根據 AES 對ts進行解密,java能夠實現能夠自行百度一下
技術交流qq羣:208779755