朋友推荐了一个比较隐蔽的盗版电影观看网站,网速特别慢观看不爽,就想是不是可以下载下来看,于是就写了这个小工具

首先,你要有能力在网页里面找到这个M3U8的索引文件,相信对于一个开发人员这个应该很容易,通过浏览器F12找到了这个索引文件,如下,我只截取了一部分,这个文件简单讲一下就是把一个视频切分成了好多小片段,而这个文件就是他们的目录文件,找到这个就容易了,接下来,把里面的每个视频片段下载下来合成就可以了。

第一步,下载索引文件

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 list = new ArrayList();

while(ma.find()){

String s = ma.group();

list.add(s);

System.out.println(s);

}

return list;

}

解析的数据结果,把索引中的ts部分解析出来,方法很多

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

第三步,根据第二步的解析结果下载视频片段

public static List downLoadIndexFile(String preUrlPath,List urlList){

try{

List filePathList = new ArrayList();

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 fileList = downLoadIndexFile(prePath,videoUrlList);

}

其实还可以补充一步代码合成,额不过着急看电影,就直接用格式工厂合成了,后面会补充

--------------------------------------------------------------------------------------------------------------------

补充代码,多线程的写法,上面的单线程太慢了,同时补充了一个文件合成的方法,不需要格式工厂了

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

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()

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 keyFileMap){

try {

FileOutputStream fileOutputStream = new FileOutputStream(new File(fileOutPath));

byte[] bytes = new byte[1024];

int length = 0;

for(int i=0;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 list = new ArrayList();

while(ma.find()){

String s = ma.group();

list.add(s);

System.out.println(s);

}

return list;

}

class downLoadNode extends Thread{

private List 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

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐