зеркало из https://github.com/mozilla/gecko-dev.git
Bug 825698 - Move YouTube query code from chrome to content. r=jdm
DONTBUILD
This commit is contained in:
Родитель
8d5a1d1584
Коммит
320e25d2f3
|
@ -31,155 +31,27 @@ YoutubeProtocolHandler.prototype = {
|
||||||
|
|
||||||
// Sample URL:
|
// Sample URL:
|
||||||
// vnd.youtube:iNuKL2Gy_QM?vndapp=youtube_mobile&vndclient=mv-google&vndel=watch&vnddnc=1
|
// vnd.youtube:iNuKL2Gy_QM?vndapp=youtube_mobile&vndclient=mv-google&vndel=watch&vnddnc=1
|
||||||
|
// Note that there is no hostname, so we use URLTYPE_NO_AUTHORITY
|
||||||
newURI: function yt_phNewURI(aSpec, aOriginCharset, aBaseURI) {
|
newURI: function yt_phNewURI(aSpec, aOriginCharset, aBaseURI) {
|
||||||
let uri = Cc["@mozilla.org/network/standard-url;1"]
|
let uri = Cc["@mozilla.org/network/standard-url;1"]
|
||||||
.createInstance(Ci.nsIStandardURL);
|
.createInstance(Ci.nsIStandardURL);
|
||||||
let id = aSpec.substring(this.scheme.length + 1);
|
uri.init(Ci.nsIStandardURL.URLTYPE_NO_AUTHORITY, this.defaultPort,
|
||||||
id = id.substring(0, id.indexOf('?'));
|
aSpec, aOriginCharset, aBaseURI);
|
||||||
uri.init(Ci.nsIStandardURL.URLTYPE_STANDARD, -1, this.scheme + "://dummy_host/" + id, aOriginCharset,
|
|
||||||
aBaseURI);
|
|
||||||
return uri.QueryInterface(Ci.nsIURI);
|
return uri.QueryInterface(Ci.nsIURI);
|
||||||
},
|
},
|
||||||
|
|
||||||
newChannel: function yt_phNewChannel(aURI) {
|
newChannel: function yt_phNewChannel(aURI) {
|
||||||
// Get a list of streams for this video id.
|
/*
|
||||||
let infoURI = "http://www.youtube.com/get_video_info?&video_id=" +
|
* This isn't a real protocol handler. Instead of creating a channel
|
||||||
aURI.path.substring(1);
|
* we just send a message and throw an exception. This 'content-handler'
|
||||||
|
* message is handled in b2g/chrome/content/shell.js where it starts
|
||||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
* an activity request that will open the Video app. The video app
|
||||||
.createInstance(Ci.nsIXMLHttpRequest);
|
* includes code to handle this fake 'video/youtube' mime type
|
||||||
xhr.open("GET", infoURI, true);
|
*/
|
||||||
xhr.addEventListener("load", function() {
|
cpmm.sendAsyncMessage("content-handler", {
|
||||||
try {
|
type: 'video/youtube', // A fake MIME type for the activity handler
|
||||||
let info = parseYoutubeVideoInfo(xhr.responseText);
|
url: aURI.spec // The path component of this URL is the video id
|
||||||
cpmm.sendAsyncMessage("content-handler", info);
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
// If parseYoutubeVideoInfo() can't find a video URL, it
|
|
||||||
// throws an Error. We report the error message here and do
|
|
||||||
// nothing. This shouldn't happen often. But if it does, the user
|
|
||||||
// will find that clicking on a video doesn't do anything.
|
|
||||||
log(e.message);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
xhr.send(null);
|
|
||||||
|
|
||||||
function log(msg) {
|
|
||||||
msg = "YoutubeProtocolHandler.js: " + (msg.join ? msg.join(" ") : msg);
|
|
||||||
Cc["@mozilla.org/consoleservice;1"]
|
|
||||||
.getService(Ci.nsIConsoleService)
|
|
||||||
.logStringMessage(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Parse the response from a youtube get_video_info query.
|
|
||||||
//
|
|
||||||
// If youtube's response is a failure, this function returns an object
|
|
||||||
// with status, errorcode, type and reason properties. Otherwise, it returns
|
|
||||||
// an object with status, url, and type properties, and optional
|
|
||||||
// title, poster, and duration properties.
|
|
||||||
//
|
|
||||||
function parseYoutubeVideoInfo(response) {
|
|
||||||
// Splits parameters in a query string.
|
|
||||||
function extractParameters(q) {
|
|
||||||
let params = q.split("&");
|
|
||||||
let result = {};
|
|
||||||
for(let i = 0, n = params.length; i < n; i++) {
|
|
||||||
let param = params[i];
|
|
||||||
let pos = param.indexOf('=');
|
|
||||||
if (pos === -1)
|
|
||||||
continue;
|
|
||||||
let name = param.substring(0, pos);
|
|
||||||
let value = param.substring(pos+1);
|
|
||||||
result[name] = decodeURIComponent(value);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
let params = extractParameters(response);
|
|
||||||
|
|
||||||
// If the request failed, return an object with an error code
|
|
||||||
// and an error message
|
|
||||||
if (params.status === 'fail') {
|
|
||||||
//
|
|
||||||
// Hopefully this error message will be properly localized.
|
|
||||||
// Do we need to add any parameters to the XMLHttpRequest to
|
|
||||||
// specify the language we want?
|
|
||||||
//
|
|
||||||
// Note that we include fake type and url properties in the returned
|
|
||||||
// object. This is because we still need to trigger the video app's
|
|
||||||
// view activity handler to display the error message from youtube,
|
|
||||||
// and those parameters are required.
|
|
||||||
//
|
|
||||||
return {
|
|
||||||
status: params.status,
|
|
||||||
errorcode: params.errorcode,
|
|
||||||
reason: (params.reason || '').replace(/\+/g, ' '),
|
|
||||||
type: 'video/3gpp',
|
|
||||||
url: 'https://m.youtube.com'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, the query was successful
|
|
||||||
let result = {
|
|
||||||
status: params.status,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now parse the available streams
|
|
||||||
let streamsText = params.url_encoded_fmt_stream_map;
|
|
||||||
if (!streamsText)
|
|
||||||
throw Error("No url_encoded_fmt_stream_map parameter");
|
|
||||||
let streams = streamsText.split(',');
|
|
||||||
for(let i = 0, n = streams.length; i < n; i++) {
|
|
||||||
streams[i] = extractParameters(streams[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the list of youtube video formats, ordered from worst
|
|
||||||
// (but playable) to best. These numbers are values used as the
|
|
||||||
// itag parameter of each stream description. See
|
|
||||||
// https://en.wikipedia.org/wiki/YouTube#Quality_and_codecs
|
|
||||||
let formats = [
|
|
||||||
"17", // 144p 3GP
|
|
||||||
"36", // 240p 3GP
|
|
||||||
"43", // 360p WebM
|
|
||||||
#ifdef MOZ_WIDGET_GONK
|
|
||||||
"18", // 360p H.264
|
|
||||||
#endif
|
|
||||||
];
|
|
||||||
|
|
||||||
// Sort the array of stream descriptions in order of format
|
|
||||||
// preference, so that the first item is the most preferred one
|
|
||||||
streams.sort(function(a, b) {
|
|
||||||
let x = a.itag ? formats.indexOf(a.itag) : -1;
|
|
||||||
let y = b.itag ? formats.indexOf(b.itag) : -1;
|
|
||||||
return y - x;
|
|
||||||
});
|
|
||||||
|
|
||||||
let bestStream = streams[0];
|
|
||||||
|
|
||||||
// If the best stream is a format we don't support just return
|
|
||||||
if (formats.indexOf(bestStream.itag) === -1)
|
|
||||||
throw Error("No supported video formats");
|
|
||||||
|
|
||||||
result.url = bestStream.url + '&signature=' + (bestStream.sig || '');
|
|
||||||
result.type = bestStream.type;
|
|
||||||
// Strip codec information off of the mime type
|
|
||||||
if (result.type && result.type.indexOf(';') !== -1) {
|
|
||||||
result.type = result.type.split(';',1)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.title) {
|
|
||||||
result.title = params.title.replace(/\+/g, ' ');
|
|
||||||
}
|
|
||||||
if (params.length_seconds) {
|
|
||||||
result.duration = params.length_seconds;
|
|
||||||
}
|
|
||||||
if (params.thumbnail_url) {
|
|
||||||
result.poster = params.thumbnail_url;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Components.results.NS_ERROR_ILLEGAL_VALUE;
|
throw Components.results.NS_ERROR_ILLEGAL_VALUE;
|
||||||
},
|
},
|
||||||
|
|
Загрузка…
Ссылка в новой задаче