Youtube 视频下载 (Android/Java) [英] Youtube Video Download (Android/Java)

查看:38
本文介绍了Youtube 视频下载 (Android/Java)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:原始问题已删除

原始问题是 https://stackoverflow.com/questions/15240011/get-the-download-url-for-youtube-video-android-java/15240012#15240012

这里的答案已经过时,不起作用,所以我会发布一个新问题并回答我自己

旧代码

new YouTubePageStreamUriGetter().execute("https://www.youtube.com/watch?v=4GuqB1BQVr4");类元{公共字符串编号;公共字符串类型;公共字符串分机;元(字符串编号,字符串扩展,字符串类型){this.num = num;this.ext = ext;this.type = 类型;}}类视频{公共字符串 ext = "";公共字符串类型 = "";公共字符串 url = "";视频(字符串分机,字符串类型,字符串网址){this.ext = ext;this.type = 类型;this.url = url;}}公共 ArrayList<视频>getStreamingUrisFromYouTubePage(String ytUrl)抛出 IOException {如果(ytUrl == null){返回空;}//在 watch?v=<vid> 之后删除查询字符串中的所有查询参数在//例如//http://www.youtube.com/watch?v=0RUPACpf8Vs&feature=youtube_gdata_playerint andIdx = ytUrl.indexOf('&');如果 (andIdx >= 0) {ytUrl = ytUrl.substring(0, andIdx);}//获取 HTML 响应String userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0.1)";HttpClient 客户端 = 新的 DefaultHttpClient();client.getParams().setParameter(CoreProtocolPNames.USER_AGENT,用户代理);HttpGet 请求 = 新的 HttpGet(ytUrl);HttpResponse 响应 = client.execute(request);字符串 html = "";InputStream in = response.getEntity().getContent();BufferedReader reader = new BufferedReader(new InputStreamReader(in));StringBuilder str = new StringBuilder();字符串行 = null;while ((line = reader.readLine()) != null) {str.append(line.replace("\u0026", "&"));}附寄();html = str.toString();//解析 HTML 响应并提取流式 URIif (html.contains("verify-age-thumb")) {CLog.w("YouTube 要求进行年龄验证.抱歉,我们无法处理.");返回空;}如果(html.contains(das_captcha")){CLog.w("找到验证码,请尝试使用不同的IP地址.");返回空;}Pattern p = Pattern.compile("stream_map": "(.*?)?"");//模式 p = Pattern.compile("/stream_map=(.[^&]*?)"/");匹配器 m = p.matcher(html);列表<字符串>匹配 = 新的 ArrayList();而(m.find()){匹配.添加(m.group());}如果(匹配.大小()!= 1){CLog.w("发现零个或太多的流映射.");返回空;}String urls[] =matches.get(0).split(",");HashMap<字符串,字符串>foundArray = new HashMap();for (String ppUrl : urls) {String url = URLDecoder.decode(ppUrl, "UTF-8");Pattern p1 = Pattern.compile("itag=([0-9]+?)[&]");匹配器 m1 = p1.matcher(url);字符串 itag = null;如果(m1.find()){itag = m1.group(1);}Pattern p2 = Pattern.compile("sig=(.*?)[&]");匹配器 m2 = p2.matcher(url);字符串 sig = null;如果(m2.find()){sig = m2.group(1);}Pattern p3 = Pattern.compile("url=(.*?)[&]");匹配器 m3 = p3.matcher(ppUrl);字符串 um = null;如果(m3.find()){嗯 = m3.group(1);}if (itag != null && sig != null && um != null) {foundArray.put(itag, URLDecoder.decode(um, "UTF-8") + "&"+ "签名=" + 签名);}}如果 (foundArray.size() == 0) {CLog.w("找不到任何网址和相应的签名");返回空;}HashMaptypeMap = new HashMap();typeMap.put("13", new Meta("13", "3GP", "低质量 - 176x144"));typeMap.put("17", new Meta("17", "3GP", "中等质量 - 176x144"));typeMap.put("36", new Meta("36", "3GP", "高质量 - 320x240"));typeMap.put("5", new Meta("5", "FLV", "低质量 - 400x226"));typeMap.put("6", new Meta("6", "FLV", "中等质量 - 640x360"));typeMap.put("34", new Meta("34", "FLV", "中等质量 - 640x360"));typeMap.put("35", new Meta("35", "FLV", "高质量 - 854x480"));typeMap.put("43", new Meta("43", "WEBM", "低质量 - 640x360"));typeMap.put("44", new Meta("44", "WEBM", "中等质量 - 854x480"));typeMap.put("45", new Meta("45", "WEBM", "高质量 - 1280x720"));typeMap.put("18", new Meta("18", "MP4", "中等质量 - 480x360"));typeMap.put("22", new Meta("22", "MP4", "高质量 - 1280x720"));typeMap.put("37", new Meta("37", "MP4", "高质量 - 1920x1080"));typeMap.put("33", new Meta("38", "MP4", "高质量 - 4096x230"));ArrayList<视频>视频 = 新的 ArrayList();for(字符串格式:typeMap.keySet()){元元 = typeMap.get(format);如果(foundArray.containsKey(格式)){Video newVideo = new Video(meta.ext, meta.type,foundArray.get(format));视频.添加(新视频);CLog.d("YouTube 视频流详细信息:ext:" + newVideo.ext+ ", 类型:" + newVideo.type + ", url:" + newVideo.url);}}返回视频;}私有类 YouTubePageStreamUriGetter 扩展AsyncTask<字符串、字符串、字符串>{ProgressDialog 进度对话框;@覆盖受保护的无效 onPreExecute() {super.onPreExecute();progressDialog = ProgressDialog.show(ARViewer.this, "","正在连接到 YouTube...", true);}@覆盖受保护的字符串 doInBackground(String... params) {字符串 url = params[0];尝试 {ArrayList<视频>视频 = getStreamingUrisFromYouTubePage(url);if (videos != null && !videos.isEmpty()) {字符串 retVidUrl = null;对于(视频视频:视频){if (video.ext.toLowerCase().contains("mp4")&&video.type.toLowerCase().contains("medium")) {retVidUrl = video.url;休息;}}如果(retVidUrl == null){对于(视频视频:视频){if (video.ext.toLowerCase().contains("3gp")&&video.type.toLowerCase().contains(中等的")) {retVidUrl = video.url;休息;}}}如果(retVidUrl == null){对于(视频视频:视频){if (video.ext.toLowerCase().contains("mp4")&&video.type.toLowerCase().contains("low")) {retVidUrl = video.url;休息;}}}如果(retVidUrl == null){对于(视频视频:视频){if (video.ext.toLowerCase().contains("3gp")&&video.type.toLowerCase().contains("low")) {retVidUrl = video.url;休息;}}}返回retVidUrl;}} 捕获(异常 e){CLog.e("无法获取 YouTube 流媒体 URL", e);}CLog.w("无法获取" + url 的流URI);返回空;}@覆盖protected void onPostExecute(String streamingUrl) {super.onPostExecute(streamingUrl);progressDialog.dismiss();如果(streamingUrl != null){/* 用 streamUrl 做你想做的事 */}}}

此代码无效

解决方案

编辑 3

您可以使用 Lib :https://github.com/HaarigerHarald/android-youtubeExtractor

例如:

String youtubeLink = "http://youtube.com/watch?v=xxxx";新的 YouTubeExtractor(this) {@覆盖public void onExtractionComplete(SparseArray<YtFile> ytFiles, VideoMeta vMeta) {如果(ytFiles != null){内部标签 = 22;String downloadUrl = ytFiles.get(itag).getUrl();}}}.extract(youtubeLink, true, true);

他们使用:

private boolean decipherSignature(final SparseArray encSignatures) 抛出 IOException {//假设函数变化不大if (decipherFunctionName == null || decipherFunctions == null) {String decipherFunctUrl = "https://s.ytimg.com/yts/jsbin/" + decipherJsFileName;BufferedReader 阅读器 = null;字符串 javascriptFile;URL url = 新 URL(decipherFunctUrl);HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();urlConnection.setRequestProperty("用户代理", USER_AGENT);尝试 {reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));StringBuilder sb = new StringBuilder("");字符串线;while ((line = reader.readLine()) != null) {sb.append(line);sb.append(" ");}javascriptFile = sb.toString();} 最后 {如果(读者!= null)reader.close();urlConnection.disconnect();}如果(记录)Log.d(LOG_TAG, "Decipher FunctURL: " + decipherFunctUrl);匹配垫 = patSignatureDecFunction.matcher(javascriptFile);如果(mat.find()){decipherFunctionName = mat.group(1);如果(记录)Log.d(LOG_TAG, "解密函数名:" + decipherFunctionName);Pattern patMainVariable = Pattern.compile("(var |\s|,|;)" + decipherFunctionName.replace("$", "\$") +"(=function\((.{1,3})\)\{)");字符串 mainDecipherFunct;mat = patMainVariable.matcher(javascriptFile);如果(mat.find()){mainDecipherFunct = "var " + decipherFunctionName + mat.group(2);} 别的 {Pattern patMainFunction = Pattern.compile("function" + decipherFunctionName.replace("$", "\$") +"(\((.{1,3})\)\{)");mat = patMainFunction.matcher(javascriptFile);如果 (!mat.find())返回假;mainDecipherFunct = "function" + decipherFunctionName + mat.group(2);}int startIndex = mat.end();for (int 大括号 = 1, i = startIndex; i < javascriptFile.length(); i++) {如果(大括号 == 0 && startIndex + 5 < i){mainDecipherFunct += javascriptFile.substring(startIndex, i) + ";";休息;}如果 (javascriptFile.charAt(i) == '{')大括号++;否则如果 (javascriptFile.charAt(i) == '}')大括号--;}decipherFunctions = mainDecipherFunct;//在主函数中搜索额外的函数和变量//需要解密//搜索变量mat = patVariableFunction.matcher(mainDecipherFunct);而 (mat.find()) {String variableDef = "var " + mat.group(2) + "={";if (decipherFunctions.contains(variableDef)) {继续;}startIndex = javascriptFile.indexOf(variableDef) + variableDef.length();for (int 大括号 = 1, i = startIndex; i < javascriptFile.length(); i++) {如果(大括号 == 0){decipherFunctions += variableDef + javascriptFile.substring(startIndex, i) + ";";休息;}如果 (javascriptFile.charAt(i) == '{')大括号++;否则如果 (javascriptFile.charAt(i) == '}')大括号--;}}//搜索函数mat = patFunction.matcher(mainDecipherFunct);而 (mat.find()) {String functionDef = "function " + mat.group(2) + "(";if (decipherFunctions.contains(functionDef)) {继续;}startIndex = javascriptFile.indexOf(functionDef) + functionDef.length();for (int 大括号 = 0, i = startIndex; i < javascriptFile.length(); i++) {如果(大括号 == 0 && startIndex + 5 < i){decipherFunctions += functionDef + javascriptFile.substring(startIndex, i) + ";";休息;}如果 (javascriptFile.charAt(i) == '{')大括号++;否则如果 (javascriptFile.charAt(i) == '}')大括号--;}}如果(记录)Log.d(LOG_TAG, "解密函数:" + decipherFunctions);decipherViaWebView(encSignatures);如果(缓存){writeDeciperFunctToChache();}} 别的 {返回假;}} 别的 {decipherViaWebView(encSignatures);}返回真;}

现在使用这个库高质量视频丢失音频所以我使用MediaMuxer 用于 Murging Audio 和视频以用于最终输出

编辑 1

https://stackoverflow.com/a/15240012/9909365

为什么以前的答案不起作用

 Pattern p2 = Pattern.compile("sig=(.*?)[&]");匹配器 m2 = p2.matcher(url);字符串 sig = null;如果(m2.find()){sig = m2.group(1);}

<块引用>

截至 2016 年 11 月,边缘有点粗糙,但是显示基本原理.今天的 url_encoded_fmt_stream_map冒号后没有空格(最好将其设为可选)和sig"已更改为signature"

当我调试代码时,我发现了 new 关键字signature&s 在许多视频的 URL 中

这里编辑了答案

private static final HashMaptypeMap = new HashMap();

initTypeMap();先打电话

class Meta {公共字符串编号;公共字符串类型;公共字符串分机;元(字符串编号,字符串扩展,字符串类型){this.num = num;this.ext = ext;this.type = 类型;}}类视频{公共字符串 ext = "";公共字符串类型 = "";公共字符串 url = "";视频(字符串分机,字符串类型,字符串网址){this.ext = ext;this.type = 类型;this.url = url;}}公共 ArrayList<视频>getStreamingUrisFromYouTubePage(String ytUrl)抛出 IOException {如果(ytUrl == null){返回空;}//在 watch?v=<vid> 之后删除查询字符串中的所有查询参数在//例如//http://www.youtube.com/watch?v=0RUPACpf8Vs&feature=youtube_gdata_playerint andIdx = ytUrl.indexOf('&');如果 (andIdx >= 0) {ytUrl = ytUrl.substring(0, andIdx);}//获取 HTML 响应/* String userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0.1)";*//* HttpClient 客户端 = 新的 DefaultHttpClient();client.getParams().setParameter(CoreProtocolPNames.USER_AGENT,用户代理);HttpGet 请求 = 新的 HttpGet(ytUrl);HttpResponse 响应 = client.execute(request);*/字符串 html = "";HttpsURLConnection c = (HttpsURLConnection) 新 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();字符串行 = null;while ((line = reader.readLine()) != null) {str.append(line.replace("\u0026", "&"));}附寄();html = str.toString();//解析 HTML 响应并提取流式 URIif (html.contains("verify-age-thumb")) {Log.e("Downloader", "YouTube 要求进行年龄验证.抱歉,我们无法处理.");返回空;}如果(html.contains(das_captcha")){Log.e("Downloader", "找到验证码,请尝试使用不同的IP地址.");返回空;}Pattern p = Pattern.compile("stream_map":"(.*?)?"");//模式 p = Pattern.compile("/stream_map=(.[^&]*?)"/");匹配器 m = p.matcher(html);列表<字符串>匹配 = 新的 ArrayList();而(m.find()){匹配.添加(m.group());}如果(匹配.大小()!= 1){Log.e("Downloader", "发现零个或太多流映射.");返回空;}String urls[] =matches.get(0).split(",");HashMap<字符串,字符串>foundArray = new HashMap();for (String ppUrl : urls) {String url = URLDecoder.decode(ppUrl, "UTF-8");Log.e("URL","URL :"+url);Pattern p1 = Pattern.compile("itag=([0-9]+?)[&]");匹配器 m1 = p1.matcher(url);字符串 itag = null;如果(m1.find()){itag = m1.group(1);}Pattern p2 = Pattern.compile("signature=(.*?)[&]");匹配器 m2 = p2.matcher(url);字符串 sig = null;如果(m2.find()){sig = m2.group(1);} 别的 {Pattern p23 = Pattern.compile("signature&s=(.*?)[&]");匹配器 m23 = p23.matcher(url);如果(m23.find()){sig = m23.group(1);}}Pattern p3 = Pattern.compile("url=(.*?)[&]");匹配器 m3 = p3.matcher(ppUrl);字符串 um = null;如果(m3.find()){嗯 = m3.group(1);}if (itag != null && sig != null && um != null) {Log.e("foundArray","添加值");foundArray.put(itag, URLDecoder.decode(um, "UTF-8") + "&"+ "签名=" + 签名);}}Log.e("foundArray","Size : "+foundArray.size());如果 (foundArray.size() == 0) {Log.e("Downloader", "找不到任何网址和相应的签名");返回空;}ArrayList<视频>视频 = 新的 ArrayList

编辑 2:

<块引用>

有时此代码无法正常工作

同源政策

https://en.wikipedia.org/wiki/Same-origin_policy

https://en.wikipedia.org/wiki/Cross-origin_resource_sharing

同源策略问题.本质上,您不能从 www.youtube.com 下载此文件,因为它们是不同的域.此问题的解决方法是 [CORS][1].

参考:https://superuser.com/questions/773719/how-do-all-of-these-save-video-from-youtube-services-work/773998#773998

url_encoded_fmt_stream_map//传统:包含视频和音频流adaptive_fmts//DASH:包含视频或音频流

每一个都是一个逗号分隔的数组,我称之为流对象".每个流对象"将包含这样的值

url//指向视频的直接 HTTP 链接itag//指定质量的代码s//签名,防止下载的安全措施

每个 URL 都将被编码,因此您需要对其进行解码.现在是棘手的部分.

YouTube 的视频至少有 3 个安全级别

unsecured//正如预期的那样,您可以仅使用未编码的 URL 下载这些s//见下文RTMPE//使用rtmpe://"协议,这些没有已知的方法

RTMPE 视频通常用于官方全长电影,并受 SWF 验证类型 2 保护.这自 2011 年以来一直存在,尚未进行逆向工程.

s"类型的视频是最难下载的.您通常会在 VEVO 视频等中看到这些.它们以一个签名开头,例如

AA5D05FA7771AD4868BA4C977C3DEAAC620DE020E.0F421820F42978A1F8EAFCDAC4EF507DB5然后签名用这样的函数打乱

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);返回 a.join("")}

此功能是动态的,通常每天都在变化.为了增加难度,该函数托管在诸如

之类的 URL 上

http://s.ytimg.com/yts/jsbin/html5player-en_US-vflycBCEX.js

这引入了同源策略的问题.本质上,您不能从 www.youtube.com 下载此文件,因为它们是不同的域.此问题的解决方法是 CORS.使用 CORS,s.ytimg.com 可以添加此标题

Access-Control-Allow-Origin:http://www.youtube.com

它允许从 www.youtube.com 下载 JavaScript.当然,他们不这样做.此变通方法的变通方法是使用 CORS 代理.这是一个代理,对所有请求使用以下标头进行响应

Access-Control-Allow-Origin:*

所以,既然您已经代理了您的 JS 文件,并使用该函数对签名进行了加扰,您可以在查询字符串中使用它来下载视频.

Note: Original Question Was Deleted

Original Question is https://stackoverflow.com/questions/15240011/get-the-download-url-for-youtube-video-android-java/15240012#15240012

here the answer is outdated, not working so i will post a new question and answering my self

OLDCODE

new YouTubePageStreamUriGetter().execute("https://www.youtube.com/watch?v=4GuqB1BQVr4");

class Meta {
    public String num;
    public String type;
    public String ext;

    Meta(String num, String ext, String type) {
        this.num = num;
        this.ext = ext;
        this.type = type;
    }
}

class Video {
    public String ext = "";
    public String type = "";
    public String url = "";

    Video(String ext, String type, String url) {
        this.ext = ext;
        this.type = type;
        this.url = url;
    }
}

public ArrayList<Video> getStreamingUrisFromYouTubePage(String ytUrl)
        throws IOException {
    if (ytUrl == null) {
        return null;
    }

    // Remove any query params in query string after the watch?v=<vid> in
    // e.g.
    // http://www.youtube.com/watch?v=0RUPACpf8Vs&feature=youtube_gdata_player
    int andIdx = ytUrl.indexOf('&');
    if (andIdx >= 0) {
        ytUrl = ytUrl.substring(0, andIdx);
    }

    // Get the HTML response
    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 = "";
    InputStream in = response.getEntity().getContent();
    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();

    // Parse the HTML response and extract the streaming URIs
    if (html.contains("verify-age-thumb")) {
        CLog.w("YouTube is asking for age verification. We can't handle that sorry.");
        return null;
    }

    if (html.contains("das_captcha")) {
        CLog.w("Captcha found, please try with different IP address.");
        return null;
    }

    Pattern p = Pattern.compile("stream_map": "(.*?)?"");
    // Pattern p = Pattern.compile("/stream_map=(.[^&]*?)"/");
    Matcher m = p.matcher(html);
    List<String> matches = new ArrayList<String>();
    while (m.find()) {
        matches.add(m.group());
    }

    if (matches.size() != 1) {
        CLog.w("Found zero or too many stream maps.");
        return null;
    }

    String urls[] = matches.get(0).split(",");
    HashMap<String, String> foundArray = new HashMap<String, String>();
    for (String ppUrl : urls) {
        String url = URLDecoder.decode(ppUrl, "UTF-8");

        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("sig=(.*?)[&]");
        Matcher m2 = p2.matcher(url);
        String sig = null;
        if (m2.find()) {
            sig = m2.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) {
            foundArray.put(itag, URLDecoder.decode(um, "UTF-8") + "&"
                    + "signature=" + sig);
        }
    }

    if (foundArray.size() == 0) {
        CLog.w("Couldn't find any URLs and corresponding signatures");
        return null;
    }

    HashMap<String, Meta> typeMap = new HashMap<String, Meta>();
    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"));

    ArrayList<Video> videos = new ArrayList<ARViewer.Video>();

    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);
            CLog.d("YouTube Video streaming details: ext:" + newVideo.ext
                    + ", type:" + newVideo.type + ", url:" + newVideo.url);
        }
    }

    return videos;
}

private class YouTubePageStreamUriGetter extends
        AsyncTask<String, String, String> {
    ProgressDialog progressDialog;

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        progressDialog = ProgressDialog.show(ARViewer.this, "",
                "Connecting to YouTube...", true);
    }

    @Override
    protected String doInBackground(String... params) {
        String url = params[0];
        try {
            ArrayList<Video> videos = getStreamingUrisFromYouTubePage(url);
            if (videos != null && !videos.isEmpty()) {
                String retVidUrl = null;
                for (Video video : videos) {
                    if (video.ext.toLowerCase().contains("mp4")
                            && video.type.toLowerCase().contains("medium")) {
                        retVidUrl = video.url;
                        break;
                    }
                }
                if (retVidUrl == null) {
                    for (Video video : videos) {
                        if (video.ext.toLowerCase().contains("3gp")
                                && video.type.toLowerCase().contains(
                                        "medium")) {
                            retVidUrl = video.url;
                            break;

                        }
                    }
                }
                if (retVidUrl == null) {

                    for (Video video : videos) {
                        if (video.ext.toLowerCase().contains("mp4")
                                && video.type.toLowerCase().contains("low")) {
                            retVidUrl = video.url;
                            break;

                        }
                    }
                }
                if (retVidUrl == null) {
                    for (Video video : videos) {
                        if (video.ext.toLowerCase().contains("3gp")
                                && video.type.toLowerCase().contains("low")) {
                            retVidUrl = video.url;
                            break;
                        }
                    }
                }

                return retVidUrl;
            }
        } catch (Exception e) {
            CLog.e("Couldn't get YouTube streaming URL", e);
        }
        CLog.w("Couldn't get stream URI for " + url);
        return null;
    }

    @Override
    protected void onPostExecute(String streamingUrl) {
        super.onPostExecute(streamingUrl);
        progressDialog.dismiss();
        if (streamingUrl != null) {
                         /* Do what ever you want with streamUrl */
        }
    }
}

This Code is Not Working

解决方案

Edit 3

You can use the Lib : https://github.com/HaarigerHarald/android-youtubeExtractor

Ex :

String youtubeLink = "http://youtube.com/watch?v=xxxx";

new YouTubeExtractor(this) {
@Override
public void onExtractionComplete(SparseArray<YtFile> ytFiles, VideoMeta vMeta) {
    if (ytFiles != null) {
        int itag = 22;
    String downloadUrl = ytFiles.get(itag).getUrl();
    }
}
}.extract(youtubeLink, true, true);

They decipherSignature using :

private boolean decipherSignature(final SparseArray<String> encSignatures) throws IOException {
    // Assume the functions don't change that much
    if (decipherFunctionName == null || decipherFunctions == null) {
        String decipherFunctUrl = "https://s.ytimg.com/yts/jsbin/" + decipherJsFileName;

        BufferedReader reader = null;
        String javascriptFile;
        URL url = new URL(decipherFunctUrl);
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestProperty("User-Agent", USER_AGENT);
        try {
            reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            StringBuilder sb = new StringBuilder("");
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
                sb.append(" ");
            }
            javascriptFile = sb.toString();
        } finally {
            if (reader != null)
                reader.close();
            urlConnection.disconnect();
        }

        if (LOGGING)
            Log.d(LOG_TAG, "Decipher FunctURL: " + decipherFunctUrl);
        Matcher mat = patSignatureDecFunction.matcher(javascriptFile);
        if (mat.find()) {
            decipherFunctionName = mat.group(1);
            if (LOGGING)
                Log.d(LOG_TAG, "Decipher Functname: " + decipherFunctionName);

            Pattern patMainVariable = Pattern.compile("(var |\s|,|;)" + decipherFunctionName.replace("$", "\$") +
                    "(=function\((.{1,3})\)\{)");

            String mainDecipherFunct;

            mat = patMainVariable.matcher(javascriptFile);
            if (mat.find()) {
                mainDecipherFunct = "var " + decipherFunctionName + mat.group(2);
            } else {
                Pattern patMainFunction = Pattern.compile("function " + decipherFunctionName.replace("$", "\$") +
                        "(\((.{1,3})\)\{)");
                mat = patMainFunction.matcher(javascriptFile);
                if (!mat.find())
                    return false;
                mainDecipherFunct = "function " + decipherFunctionName + mat.group(2);
            }

            int startIndex = mat.end();

            for (int braces = 1, i = startIndex; i < javascriptFile.length(); i++) {
                if (braces == 0 && startIndex + 5 < i) {
                    mainDecipherFunct += javascriptFile.substring(startIndex, i) + ";";
                    break;
                }
                if (javascriptFile.charAt(i) == '{')
                    braces++;
                else if (javascriptFile.charAt(i) == '}')
                    braces--;
            }
            decipherFunctions = mainDecipherFunct;
            // Search the main function for extra functions and variables
            // needed for deciphering
            // Search for variables
            mat = patVariableFunction.matcher(mainDecipherFunct);
            while (mat.find()) {
                String variableDef = "var " + mat.group(2) + "={";
                if (decipherFunctions.contains(variableDef)) {
                    continue;
                }
                startIndex = javascriptFile.indexOf(variableDef) + variableDef.length();
                for (int braces = 1, i = startIndex; i < javascriptFile.length(); i++) {
                    if (braces == 0) {
                        decipherFunctions += variableDef + javascriptFile.substring(startIndex, i) + ";";
                        break;
                    }
                    if (javascriptFile.charAt(i) == '{')
                        braces++;
                    else if (javascriptFile.charAt(i) == '}')
                        braces--;
                }
            }
            // Search for functions
            mat = patFunction.matcher(mainDecipherFunct);
            while (mat.find()) {
                String functionDef = "function " + mat.group(2) + "(";
                if (decipherFunctions.contains(functionDef)) {
                    continue;
                }
                startIndex = javascriptFile.indexOf(functionDef) + functionDef.length();
                for (int braces = 0, i = startIndex; i < javascriptFile.length(); i++) {
                    if (braces == 0 && startIndex + 5 < i) {
                        decipherFunctions += functionDef + javascriptFile.substring(startIndex, i) + ";";
                        break;
                    }
                    if (javascriptFile.charAt(i) == '{')
                        braces++;
                    else if (javascriptFile.charAt(i) == '}')
                        braces--;
                }
            }

            if (LOGGING)
                Log.d(LOG_TAG, "Decipher Function: " + decipherFunctions);
            decipherViaWebView(encSignatures);
            if (CACHING) {
                writeDeciperFunctToChache();
            }
        } else {
            return false;
        }
    } else {
        decipherViaWebView(encSignatures);
    }
    return true;
}

Now with use of this library High Quality Videos Lossing Audio so i use the MediaMuxer for Murging Audio and Video for Final Output

Edit 1

https://stackoverflow.com/a/15240012/9909365

Why the previous answer not worked

 Pattern p2 = Pattern.compile("sig=(.*?)[&]");
        Matcher m2 = p2.matcher(url);
        String sig = null;
        if (m2.find()) {
            sig = m2.group(1);
        }

As of November 2016, this is a little rough around the edges, but displays the basic principle. The url_encoded_fmt_stream_map today does not have a space after the colon (better make this optional) and "sig" has been changed to "signature"

and while i am debuging the code i found the new keyword its signature&s in many video's URL

here edited answer

private static final HashMap<String, Meta> typeMap = new HashMap<String, Meta>();

initTypeMap(); call first

class Meta {
    public String num;
    public String type;
    public String ext;

    Meta(String num, String ext, String type) {
        this.num = num;
        this.ext = ext;
        this.type = type;
    }
}

class Video {
    public String ext = "";
    public String type = "";
    public String url = "";

    Video(String ext, String type, String url) {
        this.ext = ext;
        this.type = type;
        this.url = url;
    }
}

public ArrayList<Video> getStreamingUrisFromYouTubePage(String ytUrl)
        throws IOException {
    if (ytUrl == null) {
        return null;
    }

    // Remove any query params in query string after the watch?v=<vid> in
    // e.g.
    // http://www.youtube.com/watch?v=0RUPACpf8Vs&feature=youtube_gdata_player
    int andIdx = ytUrl.indexOf('&');
    if (andIdx >= 0) {
        ytUrl = ytUrl.substring(0, andIdx);
    }

    // Get the HTML response
    /* 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();

    // Parse the HTML response and extract the streaming URIs
    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":"(.*?)?"");
    // Pattern p = Pattern.compile("/stream_map=(.[^&]*?)"/");
    Matcher m = p.matcher(html);
    List<String> matches = new ArrayList<String>();
    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(",");
    HashMap<String, String> foundArray = new HashMap<String, String>();
    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;
    }


    ArrayList<Video> videos = new ArrayList<Video>();

    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:" + newVideo.type + ", url:" + newVideo.url);
        }
    }

    return videos;
}

private class YouTubePageStreamUriGetter extends AsyncTask<String, String, ArrayList<Video>> {
    ProgressDialog progressDialog;

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        progressDialog = ProgressDialog.show(webViewActivity.this, "",
                "Connecting to YouTube...", true);
    }

    @Override
    protected ArrayList<Video> doInBackground(String... params) {
        ArrayList<Video> fVideos = new ArrayList<>();
        String url = params[0];
        try {
            ArrayList<Video> videos = getStreamingUrisFromYouTubePage(url);
            /*                Log.e("Downloader","Size of Video : "+videos.size());*/
            if (videos != null && !videos.isEmpty()) {
                for (Video video : videos)
                {
                    Log.e("Downloader", "ext : " + video.ext);
                    if (video.ext.toLowerCase().contains("mp4") || video.ext.toLowerCase().contains("3gp") || video.ext.toLowerCase().contains("flv") || video.ext.toLowerCase().contains("webm")) {
                        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
    protected void onPostExecute(ArrayList<Video> streamingUrl) {
        super.onPostExecute(streamingUrl);
        progressDialog.dismiss();
        if (streamingUrl != null) {
            if (!streamingUrl.isEmpty()) {
                //Log.e("Steaming Url", "Value : " + streamingUrl);

                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);
                }
                //new ProgressBack().execute(new String[]{streamingUrl, filename + "." + 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"));
}

Edit 2:

Some time This Code Not worked proper

Same-origin policy

https://en.wikipedia.org/wiki/Same-origin_policy

https://en.wikipedia.org/wiki/Cross-origin_resource_sharing

problem of Same-origin policy. Essentially, you cannot download this file from www.youtube.com because they are different domains. A workaround of this problem is [CORS][1]. 

Ref : https://superuser.com/questions/773719/how-do-all-of-these-save-video-from-youtube-services-work/773998#773998

url_encoded_fmt_stream_map // traditional: contains video and audio stream
adaptive_fmts              // DASH: contains video or audio stream

Each of these is a comma separated array of what I would call "stream objects". Each "stream object" will contain values like this

url  // direct HTTP link to a video
itag // code specifying the quality
s    // signature, security measure to counter downloading

Each URL will be encoded so you will need to decode them. Now the tricky part.

YouTube has at least 3 security levels for their videos

unsecured // as expected, you can download these with just the unencoded URL
s         // see below
RTMPE     // uses "rtmpe://" protocol, no known method for these

The RTMPE videos are typically used on official full length movies, and are protected with SWF Verification Type 2. This has been around since 2011 and has yet to be reverse engineered.

The type "s" videos are the most difficult that can actually be downloaded. You will typcially see these on VEVO videos and the like. They start with a signature such as

AA5D05FA7771AD4868BA4C977C3DEAAC620DE020E.0F421820F42978A1F8EAFCDAC4EF507DB5 Then the signature is scrambled with a function like this

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

This function is dynamic, it typically changes every day. To make it more difficult the function is hosted at a URL such as

http://s.ytimg.com/yts/jsbin/html5player-en_US-vflycBCEX.js

this introduces the problem of Same-origin policy. Essentially, you cannot download this file from www.youtube.com because they are different domains. A workaround of this problem is CORS. With CORS, s.ytimg.com could add this header

Access-Control-Allow-Origin: http://www.youtube.com

and it would allow the JavaScript to download from www.youtube.com. Of course they do not do this. A workaround for this workaround is to use a CORS proxy. This is a proxy that responds with the following header to all requests

Access-Control-Allow-Origin: *

So, now that you have proxied your JS file, and used the function to scramble the signature, you can use that in the querystring to download a video.

这篇关于Youtube 视频下载 (Android/Java)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆