1 import java.io.BufferedReader;
2 import java.io.File;
3 import java.io.FileInputStream;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.InputStreamReader;
7 import java.net.URL;
8 import java.util.ArrayList;
9 import java.util.List;
10 import java.util.concurrent.ExecutorService;
11 import java.util.concurrent.Executors;
12
13 import org.apache.commons.io.IOUtils;
14
15 public class M3U8Downloader {
16
17 static String url = "http://ftp.luplayer.com/20161103/mo93Hc9o/index.m3u8";
18 static String tofile = "/Users/jieyuefeng/Dev/output/20170210/Movie.ts";
19
20 public static void main(String[] args) throws IOException, InterruptedException {
21
22 System.out.println("get index for: " + url);
23
24 M3U8 m3u8 = parseIndex(url);
25
26 float f = 0;
27 for (M3U8Ts ts : m3u8.getTsList()) {
28 f += ts.getSeconds();
29 }
30 System.out.println("movie length: " + ((int) f / 60) + "min" + (int) f % 60 + "sec");
31
32 download(m3u8, tofile);
33
34 executor.shutdown();
35 System.out.println("Wait for downloader...");
36 while (!executor.isTerminated()) {
37 Thread.sleep(100);
38 }
39
40 merge(m3u8, tofile);
41
42 System.out.println("download completed for: " + tofile);
43 }
44
45 public static void merge(M3U8 m3u8, String tofile) throws IOException {
46 File file = new File(tofile);
47 FileOutputStream fos = new FileOutputStream(file);
48 for (M3U8Ts ts : m3u8.getTsList()) {
49 IOUtils.copyLarge(new FileInputStream(new File(file.getParentFile(), ts.getFile())), fos);
50 }
51 fos.close();
52 }
53
54 private static ExecutorService executor = Executors.newFixedThreadPool(10);
55
56 public static void download(final M3U8 m3u8, final String tofile) throws IOException {
57 final File dir = new File(tofile).getParentFile();
58 if (!dir.exists()) {
59 dir.mkdirs();
60 } else if (dir.list().length > 0) {
61 throw new IOException("tofile dir must be empty or not exists");
62 }
63
64 for (final M3U8Ts ts : m3u8.getTsList()) {
65 executor.execute(new Runnable() {
66
67 @Override
68 public void run() {
69 try {
70 System.out.println(
71 "download " + (m3u8.getTsList().indexOf(ts) + 1) + "/" + m3u8.getTsList().size() + ": " + ts);
72 FileOutputStream writer = new FileOutputStream(new File(dir, ts.getFile()));
73 IOUtils.copyLarge(new URL(m3u8.getBasepath() + ts.getFile()).openStream(), writer);
74 writer.close();
75 System.out.println("download ok for: " + ts);
76 } catch (IOException e) {
77 e.printStackTrace();
78 }
79
80 }
81 });
82
83 }
84
85 }
86
87 static M3U8 parseIndex(String url) throws IOException {
88
89 BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(url).openStream()));
90
91 String basepath = url.substring(0, url.lastIndexOf("/") + 1);
92
93 M3U8 ret = new M3U8();
94 ret.setBasepath(basepath);
95
96 String line;
97 float seconds = 0;
98 while ((line = reader.readLine()) != null) {
99 if (line.startsWith("#")) {
100 if (line.startsWith("#EXTINF:")) {
101 line = line.substring(8);
102 if (line.endsWith(",")) {
103 line = line.substring(0, line.length() - 1);
104 }
105 seconds = Float.parseFloat(line);
106 }
107 continue;
108 }
109 if (line.endsWith("m3u8")) {
110 return parseIndex(basepath + line);
111 }
112 ret.addTs(new M3U8Ts(line, seconds));
113 seconds = 0;
114 }
115 reader.close();
116
117 return ret;
118 }
119
120 static class M3U8 {
121 private String basepath;
122 private List<M3U8Ts> tsList = new ArrayList<M3U8Ts>();
123
124 public String getBasepath() {
125 return basepath;
126 }
127
128 public void setBasepath(String basepath) {
129 this.basepath = basepath;
130 }
131
132 public List<M3U8Ts> getTsList() {
133 return tsList;
134 }
135
136 public void setTsList(List<M3U8Ts> tsList) {
137 this.tsList = tsList;
138 }
139
140 public void addTs(M3U8Ts ts) {
141 this.tsList.add(ts);
142 }
143
144 @Override
145 public String toString() {
146 StringBuilder sb = new StringBuilder();
147 sb.append("basepath: " + basepath);
148 for (M3U8Ts ts : tsList) {
149 sb.append("\nts: " + ts);
150 }
151
152 return sb.toString();
153 }
154
155 }
156
157 static class M3U8Ts {
158 private String file;
159 private float seconds;
160
161 public M3U8Ts(String file, float seconds) {
162 this.file = file;
163 this.seconds = seconds;
164 }
165
166 public String getFile() {
167 return file;
168 }
169
170 public void setFile(String file) {
171 this.file = file;
172 }
173
174 public float getSeconds() {
175 return seconds;
176 }
177
178 public void setSeconds(float seconds) {
179 this.seconds = seconds;
180 }
181
182 @Override
183 public String toString() {
184 return file + " (" + seconds + "sec)";
185 }
186
187 }
188
189 }