// jDownloader - Downloadmanager
// Copyright (C) 2009 JD-Team support@jdownloader.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package songstreams;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TbCm {
static class Info {
public String link;
public long size;
public int fmt;
public String desc;
}
static public final Pattern YT_FILENAME_PATTERN = Pattern.compile("<meta name=\"title\" content=\"(.*?)\">", Pattern.CASE_INSENSITIVE);
private static final Pattern VIDEO_ID = Pattern.compile("watch\\?v=([\\w_\\-]+)");
private static final Pattern VIDEO_FILE = Pattern.compile("&title=([^&$]+)");
private static final Pattern HTML5_FMT_MAP = Pattern.compile("\"html5_fmt_map\": \\[(.*?)\\]");
private static final Pattern NEW_HTML5_FMT_MAP = Pattern.compile("\"url_encoded_fmt_stream_map\": \"(.*?)\"");
private static final Pattern FMT_MAP_COLUMN = Pattern.compile("\\{(.*?)\\}");
private static final Pattern FMT_HIT_URL = Pattern.compile("url\": \"(http:.*?)\"");
private static final Pattern NEW_FMT_HIT_URL = Pattern.compile("url=(http.*?)\\&");
private static final Pattern FMT_HIT_FMT = Pattern.compile("itag\": (\\d+)");
private static final Pattern NEW_FMT_HIT_FMT = Pattern.compile("itag=(\\d+)");
private static final Pattern FMT_HIT_QUALITY = Pattern.compile("quality\": \"(.*?)\"");
private static final Pattern NEW_FMT_HIT_QUALITY = Pattern.compile("quality=(.*?)&");
private static final Pattern FMT_LIST = Pattern.compile("&fmt_list=(.+?)&");
private static final Pattern NEW_FMT_LIST = Pattern.compile("\"fmt_list\":\\s+\"(.+?)\",");
private static final Pattern FMT_LIST_MAP = Pattern.compile("(\\d+)/(\\d+x\\d+)/\\d+/\\d+/\\d+,");
private static final Pattern FALLBACK = Pattern.compile("url%3D(.*?)($|%2C)");
private static final Pattern FALLBACK_URL = Pattern.compile("(.*?)%26");
private static final Pattern FALLBACK_QUALITY = Pattern.compile("%26quality%3D(.*?)%");
public HashMap<Integer, String[]> decryptIt(final URL param) throws Exception {
String parameter = param.toString().replace("watch#!v", "watch?v");
parameter = parameter.replaceFirst("(verify_age\\?next_url=\\/?)", "");
parameter = parameter.replaceFirst("(%3Fv%3D)", "?v=");
parameter = parameter.replaceFirst("(watch\\?.*?v)", "watch?v");
parameter = parameter.replaceFirst("/embed/", "/watch?v=");
parameter = parameter.replaceFirst("https", "http");
return getLinks(parameter);
}
public HashMap<Integer, String[]> getLinks(final String video) throws Exception {
/* this cookie makes html5 available and skip controversy check */
HttpURLConnection conn = (HttpURLConnection) new URL(video).openConnection();
conn.setRequestProperty("Cookie", "f2=40100000");
conn.setRequestProperty("User-Agent", "Wget/1.12");
conn.setRequestMethod("GET");
StringBuffer page = new StringBuffer();
InputStreamReader isr = new InputStreamReader(conn.getInputStream());
BufferedReader bufR = new BufferedReader(isr);
String line;
while((line = bufR.readLine()) != null)
page.append(line).append("\n");
bufR.close();
String pageString = page.toString();
if(pageString.contains("id=\"unavailable-submessage\" class\"watch-unavailable-submessage\""))
return null;
Matcher matcher = VIDEO_ID.matcher(video);
String VIDEOID = "";
if(matcher.find())
VIDEOID = matcher.group(1);
boolean fileNameFound = false;
String YT_FILENAME = VIDEOID;
matcher = VIDEO_FILE.matcher(pageString);
if(pageString.contains("&title=")) {
matcher.find();
YT_FILENAME = URLDecoder.decode(matcher.group(1).replaceAll("\\+", " ").trim(), conn.getContentEncoding());
}
final String url = conn.getURL().toString();
boolean ythack = false;
if (url != null && !url.equals(video)) {
if(url.toLowerCase(Locale.ENGLISH).indexOf("youtube.com/index?ytsession=") != -1 || url.toLowerCase(Locale.ENGLISH).indexOf("youtube.com/verify_age?next_url=") != -1) {
ythack = true;
URL info = new URL("http://www.youtube.com/get_video_info?video_id=" + VIDEOID);
HttpURLConnection infoConn = (HttpURLConnection) info.openConnection();
infoConn.setRequestProperty("Cookie", "f2=40100000");
infoConn.setRequestProperty("User-Agent", "Wget/1.12");
page = new StringBuffer();
isr = new InputStreamReader(infoConn.getInputStream());
bufR = new BufferedReader(isr);
while((line = bufR.readLine()) != null)
page.append(line).append("\n");
bufR.close();
pageString = page.toString();
if(pageString.contains("&title=") && !fileNameFound) {
matcher = VIDEO_FILE.matcher(pageString);
matcher.find();
YT_FILENAME = URLDecoder.decode(matcher.group(1).replaceAll("\\+", " ").trim(), conn.getContentEncoding());
fileNameFound = true;
}
} else if(url.toLowerCase(Locale.ENGLISH).indexOf("google.com/accounts/servicelogin?") != -1) {
return null;
}
}
/* html5_fmt_map */
matcher = YT_FILENAME_PATTERN.matcher(pageString);
matcher.find();
if(matcher.groupCount() != 0 && !fileNameFound) {
YT_FILENAME = URLDecoder.decode(matcher.group(1).trim(), "UTF-8");
fileNameFound = true;
}
final HashMap<Integer, String[]> links = new HashMap<Integer, String[]>();
matcher = HTML5_FMT_MAP.matcher(pageString);
String html5_fmt_map;
Vector<String> fmtHitsVector = new Vector<String>();
if (matcher.find()) {
html5_fmt_map = matcher.group(1);
matcher = FMT_MAP_COLUMN.matcher(html5_fmt_map);
while(matcher.find())
fmtHitsVector.add(matcher.group(1));
for(String hit : fmtHitsVector) {
matcher = FMT_HIT_URL.matcher(hit);
matcher.find();
String hitURL = matcher.group(1);
matcher = FMT_HIT_FMT.matcher(hit);
matcher.find();
String hitFMT = matcher.group(1);
matcher = FMT_HIT_QUALITY.matcher(hit);
String hitQ = matcher.group(1);
if(hitURL != null && hitFMT != null && hitQ != null) {
hitURL = unescape(hitURL.replaceAll("\\\\/", "/"));
links.put(Integer.parseInt(hitFMT), new String[] { URLDecoder.decode(hitURL, "UTF-8"), hitQ});
}
}
}
else {
matcher = NEW_HTML5_FMT_MAP.matcher(pageString);
if(matcher.find()) {
html5_fmt_map = matcher.group(1);
matcher = FMT_MAP_COLUMN.matcher(html5_fmt_map);
while(matcher.find())
fmtHitsVector.add(matcher.group(1));
if(fmtHitsVector.isEmpty())
fmtHitsVector.addAll(Arrays.asList(html5_fmt_map.split(",")));
for(String hit : fmtHitsVector) {
hit = unescape(hit);
matcher = NEW_FMT_HIT_URL.matcher(hit);
matcher.find();
String hitURL = matcher.group(1);
matcher = NEW_FMT_HIT_FMT.matcher(hit);
matcher.find();
String hitFMT = matcher.group(1);
matcher = NEW_FMT_HIT_QUALITY.matcher(hit);
matcher.find();
String hitQ = matcher.group(1);
if (hitURL != null && hitFMT != null && hitQ != null) {
hitURL = unescape(hitURL.replaceAll("\\\\/", "/"));
links.put(Integer.parseInt(hitFMT), new String[] { URLDecoder.decode(hitURL, "UTF-8"), hitQ });
}
}
}
}
final HashMap<String, String> fmt_list = new HashMap<String, String>();
String fmt_list_str = "";
if (ythack) {
matcher = FMT_LIST.matcher(pageString);
matcher.find();
fmt_list_str = (matcher.group(1) + ",").replaceAll("%2F", "/").replaceAll("%2C", ",");
} else {
matcher = NEW_FMT_LIST.matcher(pageString);
matcher.find();
fmt_list_str = (matcher.group(1) + ",").replaceAll("\\\\/", "/");
}
matcher = FMT_LIST_MAP.matcher(fmt_list_str);
while(matcher.find()) {
fmt_list.put(matcher.group(1), matcher.group(2));
}
if (links.size() == 0 && ythack) {
/* try to find fallback links */
matcher = FALLBACK.matcher(pageString);
while (matcher.find()) {
Matcher tempMatcher = FALLBACK_URL.matcher(matcher.group(1));
tempMatcher.find();
String hitUrl = tempMatcher.group(1);
tempMatcher = FALLBACK_QUALITY.matcher(matcher.group(1));
tempMatcher.find();
String hitQ = tempMatcher.group(1);
if (hitUrl != null && hitQ != null) {
hitUrl = unescape(hitUrl.replaceAll("\\\\/", "/"));
links.put(Integer.parseInt(matcher.group(2)), new String[] { URLDecoder.decode(hitUrl, "UTF-8"), hitQ });
}
}
}
for (Integer fmt : links.keySet()) {
String fmt2 = fmt + "";
if (fmt_list.containsKey(fmt2)) {
String Videoq = links.get(fmt)[1];
final Integer q = Integer.parseInt(fmt_list.get(fmt2).split("x")[1]);
if (fmt == 40) {
Videoq = "240p Light";
} else if (q > 1080) {
Videoq = "Original";
} else if (q > 720) {
Videoq = "1080p";
} else if (q > 576) {
Videoq = "720p";
} else if (q > 360) {
Videoq = "480p";
} else if (q > 240) {
Videoq = "360p";
} else {
Videoq = "240p";
}
links.get(fmt)[1] = Videoq;
}
}
if (YT_FILENAME != null) {
links.put(-1, new String[] { YT_FILENAME });
}
return links;
}
private static synchronized String unescape(final String s) {
char ch;
final StringBuilder sb = new StringBuilder();
int ii;
int i;
for (i = 0; i < s.length(); i++) {
ch = s.charAt(i);
switch (ch) {
case '\\':
ch = s.charAt(++i);
StringBuilder sb2 = null;
switch (ch) {
case 'u':
/* unicode */
sb2 = new StringBuilder();
i++;
ii = i + 4;
for (; i < ii; i++) {
ch = s.charAt(i);
if (sb2.length() > 0 || ch != '0') {
sb2.append(ch);
}
}
i--;
sb.append((char) Long.parseLong(sb2.toString(), 16));
continue;
case 'x':
/* normal hex coding */
sb2 = new StringBuilder();
i++;
ii = i + 2;
for (; i < ii; i++) {
ch = s.charAt(i);
sb2.append(ch);
}
i--;
sb.append((char) Long.parseLong(sb2.toString(), 16));
continue;
default:
sb.append(ch);
continue;
}
}
sb.append(ch);
}
return sb.toString();
}
}