initTypeMap (); gọi trước
class Meta { public String num; public String type; public String ext; Meta(String num, String ext, String type) { chúng tôi = num; chúng tôi = ext; chúng tôi = type; } } class Video { public String ext = ""; public String type = ""; public String url = ""; Video(String ext, String type, String url) { chúng tôi = ext; chúng tôi = type; chúng tôi = url; } } throws IOException { if (ytUrl == null) { return null; } int andIdx = ytUrl.indexOf('&'); ytUrl = ytUrl.substring(0, andIdx); } /* String userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0.1)";*/ /* HttpClient client = new DefaultHttpClient(); client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, userAgent); HttpGet request = new HttpGet(ytUrl); HttpResponse response = client.execute(request);*/ String html = ""; HttpsURLConnection c = (HttpsURLConnection) new URL(ytUrl).openConnection(); c.setRequestMethod("GET"); c.setDoOutput(true); c.connect(); InputStream in = c.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); StringBuilder str = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { str.append(line.replace("\u0026", "&")); } in.close(); html = str.toString(); if (html.contains("verify-age-thumb")) { Log.e("Downloader", "YouTube is asking for age verification. We can't handle that sorry."); return null; } if (html.contains("das_captcha")) { Log.e("Downloader", "Captcha found, please try with different IP address."); return null; } Pattern p = Pattern.compile("stream_map":"(.*?)?""); Matcher m = p.matcher(html); while (m.find()) { matches.add(m.group()); } if (matches.size() != 1) { Log.e("Downloader", "Found zero or too many stream maps."); return null; } String urls[] = matches.get(0).split(","); for (String ppUrl : urls) { String url = URLDecoder.decode(ppUrl, "UTF-8"); Log.e("URL","URL : "+url); Pattern p1 = Pattern.compile("itag=([0-9]+?)[&]"); Matcher m1 = p1.matcher(url); String itag = null; if (m1.find()) { itag = m1.group(1); } Pattern p2 = Pattern.compile("signature=(.*?)[&]"); Matcher m2 = p2.matcher(url); String sig = null; if (m2.find()) { sig = m2.group(1); } else { Pattern p23 = Pattern.compile("signature&s=(.*?)[&]"); Matcher m23 = p23.matcher(url); if (m23.find()) { sig = m23.group(1); } } Pattern p3 = Pattern.compile("url=(.*?)[&]"); Matcher m3 = p3.matcher(ppUrl); String um = null; if (m3.find()) { um = m3.group(1); } if (itag != null && sig != null && um != null) { Log.e("foundArray","Adding Value"); foundArray.put(itag, URLDecoder.decode(um, "UTF-8") + "&" + "signature=" + sig); } } Log.e("foundArray","Size : "+foundArray.size()); if (foundArray.size() == 0) { Log.e("Downloader", "Couldn't find any URLs and corresponding signatures"); return null; } for (String format : typeMap.keySet()) { Meta meta = typeMap.get(format); if (foundArray.containsKey(format)) { Video newVideo = new Video(meta.ext, meta.type, foundArray.get(format)); videos.add(newVideo); Log.d("Downloader", "YouTube Video streaming details: ext:" + newVideo.ext + ", type:" + chúng tôi + ", url:" + newVideo.url); } } return videos; } ProgressDialog progressDialog; @Override protected void onPreExecute() { super.onPreExecute(); progressDialog = ProgressDialog.show(webViewActivity.this, "", "Connecting to YouTube...", true); } @Override String url = params[0]; try { /* Log.e("Downloader","Size of Video : "+videos.size());*/ if (videos != null && !videos.isEmpty()) { for (Video video : videos) { Log.e("Downloader", "ext : " + video.ext); ext = video.ext.toLowerCase(); fVideos.add(new Video(video.ext,video.type,video.url)); } } return fVideos; } } catch (Exception e) { e.printStackTrace(); Log.e("Downloader", "Couldn't get YouTube streaming URL", e); } Log.e("Downloader", "Couldn't get stream URI for " + url); return null; } @Override super.onPostExecute(streamingUrl); progressDialog.dismiss(); if (streamingUrl != null) { if (!streamingUrl.isEmpty()) { for (int i = 0; i < streamingUrl.size(); i++) { Video fX = streamingUrl.get(i); Log.e("Founded Video", "URL : " + fX.url); Log.e("Founded Video", "TYPE : " + fX.type); Log.e("Founded Video", "EXT : " + fX.ext); } } } } } public void initTypeMap() { typeMap.put("13", new Meta("13", "3GP", "Low Quality - 176x144")); typeMap.put("17", new Meta("17", "3GP", "Medium Quality - 176x144")); typeMap.put("36", new Meta("36", "3GP", "High Quality - 320x240")); typeMap.put("5", new Meta("5", "FLV", "Low Quality - 400x226")); typeMap.put("6", new Meta("6", "FLV", "Medium Quality - 640x360")); typeMap.put("34", new Meta("34", "FLV", "Medium Quality - 640x360")); typeMap.put("35", new Meta("35", "FLV", "High Quality - 854x480")); typeMap.put("43", new Meta("43", "WEBM", "Low Quality - 640x360")); typeMap.put("44", new Meta("44", "WEBM", "Medium Quality - 854x480")); typeMap.put("45", new Meta("45", "WEBM", "High Quality - 1280x720")); typeMap.put("18", new Meta("18", "MP4", "Medium Quality - 480x360")); typeMap.put("22", new Meta("22", "MP4", "High Quality - 1280x720")); typeMap.put("37", new Meta("37", "MP4", "High Quality - 1920x1080")); typeMap.put("33", new Meta("38", "MP4", "High Quality - 4096x230")); }
Chỉnh sửa 2:
Đôi khi Mã này không hoạt động đúng
Chính sách cùng nguồn gốc
https://en.wikipedia.org/wiki/Same-Origin_policy
https://en.wikipedia.org/wiki/Cross-Origin_resource_shaming
problem of Same-Origin policy. Essentially, you cannot download this file from chúng tôi because they are different domains. A workaround of this problem is [CORS][1].
Tham chiếu: https://superuser.com/questions/773719/how-do-all-of-these-save-video-from-youtube-service-work/773998#773998
url_encoded_fmt_stream_map adaptive_fmts
Mỗi trong số này là một mảng được phân tách bằng dấu phẩy của cái mà tôi sẽ gọi là “đối tượng luồng”. Mỗi “đối tượng luồng” sẽ chứa các giá trị như thế này
url itag s
Mỗi URL sẽ được mã hóa, do đó bạn sẽ cần giải mã chúng. Bây giờ là phần khó khăn.
YouTube có ít nhất 3 cấp độ bảo mật cho video của họ
unsecured s RTMPE
Các video RTMPE thường được sử dụng trên các bộ phim có thời lượng đầy đủ chính thức và được bảo vệ với Loại xác minh SWF 2. Điều này đã có từ năm 2011 và vẫn chưa được thiết kế ngược.
Các loại video “s” là khó nhất thực sự có thể được tải xuống. Bạn sẽ thấy những thứ này trên các video của VEVO và những thứ tương tự. Họ bắt đầu bằng một chữ ký như
AA5D05FA7771AD4868BA4C977C3DEAAC620DE020E.0F421820F42978A1F8EAFCDAC4EF507DB5 Sau đó, chữ ký được xáo trộn với chức năng như thế này
function mo(a) { a = a.split(""); a = lo.rw(a, 1); a = lo.rw(a, 32); a = lo.IC(a, 1); a = lo.wS(a, 77); a = lo.IC(a, 3); a = lo.wS(a, 77); a = lo.IC(a, 3); a = lo.wS(a, 44); return a.join("") }
Chức năng này là năng động, nó thường thay đổi mỗi ngày. Để làm cho khó khăn hơn, chức năng được lưu trữ tại một URL như
http://s.ytimg.com/yts/jsbin/html5player-en_US-vflycBCEX.js
điều này giới thiệu vấn đề của chính sách Cùng nguồn gốc. Về cơ bản, bạn không thể tải xuống tệp này từ chúng tôi vì chúng là các miền khác nhau. Một cách giải quyết của vấn đề này là CORS. Với CORS, chúng tôi có thể thêm tiêu đề này
Access-Control-Allow-Origin: http://www.youtube.com
và nó sẽ cho phép JavaScript tải xuống từ chúng tôi Tất nhiên họ không làm điều này. Một cách giải quyết cho cách giải quyết này là sử dụng proxy CORS. Đây là một proxy đáp ứng với tiêu đề sau cho tất cả các yêu cầu
Access-Control-Allow-Origin: *
Vì vậy, bây giờ bạn đã ủy quyền tệp JS của mình và sử dụng hàm để xáo trộn chữ ký, bạn có thể sử dụng nó trong chuỗi truy vấn để tải xuống video.