M3U8在線視頻文件下載合成MP4視頻(本身想看電影)

朋友推薦了一個比較隱蔽的盜版電影觀看網站,網速特別慢觀看不爽,就想是否是能夠下載下來看,因而就寫了這個小工具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

相關文章
相關標籤/搜索