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(" ts: " + 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 }