Merge pull request #487 from calzoneman/remove-yt2
Remove YouTube v2 API fallback since v2 is dead
This commit is contained in:
commit
3f79e9f858
|
@ -120,10 +120,14 @@ mail:
|
||||||
|
|
||||||
# YouTube v3 API key
|
# YouTube v3 API key
|
||||||
# See https://developers.google.com/youtube/registering_an_application
|
# See https://developers.google.com/youtube/registering_an_application
|
||||||
# Google is closing the v2 API (which allowed anonymous requests) on
|
# YouTube links will not work without this!
|
||||||
# April 20, 2015 so you must register a v3 API key now.
|
# Instructions:
|
||||||
# NOTE: You must generate a Server key under Public API access, NOT a
|
# 1. Go to https://console.developers.google.com/project
|
||||||
# browser key.
|
# 2. Create a new API project
|
||||||
|
# 3. On the left sidebar, click "Credentials" under "APIs & auth"
|
||||||
|
# 4. Click "Create new Key" under "Public API access"
|
||||||
|
# 5. Click "Server key"
|
||||||
|
# 6. Under "APIs & auth" click "YouTube Data API" and then click "Enable API"
|
||||||
youtube-v3-key: ''
|
youtube-v3-key: ''
|
||||||
# Minutes between saving channel state to disk
|
# Minutes between saving channel state to disk
|
||||||
channel-save-interval: 5
|
channel-save-interval: 5
|
||||||
|
|
|
@ -352,9 +352,8 @@ function preprocessConfig(cfg) {
|
||||||
require("cytube-mediaquery/lib/provider/youtube").setApiKey(
|
require("cytube-mediaquery/lib/provider/youtube").setApiKey(
|
||||||
cfg["youtube-v3-key"]);
|
cfg["youtube-v3-key"]);
|
||||||
} else {
|
} else {
|
||||||
Logger.errlog.log("Warning: No YouTube v3 API key set. YouTube lookups will " +
|
Logger.errlog.log("Warning: No YouTube v3 API key set. YouTube links will " +
|
||||||
"fall back to the v2 API, which is scheduled for closure soon after " +
|
"not work. See youtube-v3-key in config.template.yaml and " +
|
||||||
"April 20, 2015. See " +
|
|
||||||
"https://developers.google.com/youtube/registering_an_application for " +
|
"https://developers.google.com/youtube/registering_an_application for " +
|
||||||
"information on registering an API key.");
|
"information on registering an API key.");
|
||||||
}
|
}
|
||||||
|
|
257
lib/get-info.js
257
lib/get-info.js
|
@ -66,7 +66,8 @@ var Getters = {
|
||||||
/* youtube.com */
|
/* youtube.com */
|
||||||
yt: function (id, callback) {
|
yt: function (id, callback) {
|
||||||
if (!Config.get("youtube-v3-key")) {
|
if (!Config.get("youtube-v3-key")) {
|
||||||
return Getters.yt2(id, callback);
|
return callback("The YouTube API now requires an API key. Please see the " +
|
||||||
|
"documentation for youtube-v3-key in config.template.yaml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,7 +87,8 @@ var Getters = {
|
||||||
/* youtube.com playlists */
|
/* youtube.com playlists */
|
||||||
yp: function (id, callback) {
|
yp: function (id, callback) {
|
||||||
if (!Config.get("youtube-v3-key")) {
|
if (!Config.get("youtube-v3-key")) {
|
||||||
return Getters.yp2(id, callback);
|
return callback("The YouTube API now requires an API key. Please see the " +
|
||||||
|
"documentation for youtube-v3-key in config.template.yaml");
|
||||||
}
|
}
|
||||||
|
|
||||||
YouTube.lookupPlaylist(id).then(function (videos) {
|
YouTube.lookupPlaylist(id).then(function (videos) {
|
||||||
|
@ -108,7 +110,8 @@ var Getters = {
|
||||||
/* youtube.com search */
|
/* youtube.com search */
|
||||||
ytSearch: function (query, callback) {
|
ytSearch: function (query, callback) {
|
||||||
if (!Config.get("youtube-v3-key")) {
|
if (!Config.get("youtube-v3-key")) {
|
||||||
return Getters.ytSearch2(query.split(" "), callback);
|
return callback("The YouTube API now requires an API key. Please see the " +
|
||||||
|
"documentation for youtube-v3-key in config.template.yaml");
|
||||||
}
|
}
|
||||||
|
|
||||||
YouTube.search(query).then(function (res) {
|
YouTube.search(query).then(function (res) {
|
||||||
|
@ -730,254 +733,6 @@ var Getters = {
|
||||||
var media = new Media(id, title, "--:--", "hb");
|
var media = new Media(id, title, "--:--", "hb");
|
||||||
callback(false, media);
|
callback(false, media);
|
||||||
},
|
},
|
||||||
|
|
||||||
/* youtube.com - old v2 API */
|
|
||||||
yt2: function (id, callback) {
|
|
||||||
var sv = Server.getServer();
|
|
||||||
|
|
||||||
var m = id.match(/([\w-]{11})/);
|
|
||||||
if (m) {
|
|
||||||
id = m[1];
|
|
||||||
} else {
|
|
||||||
callback("Invalid ID", null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
host: "gdata.youtube.com",
|
|
||||||
port: 443,
|
|
||||||
path: "/feeds/api/videos/" + id + "?v=2&alt=json",
|
|
||||||
method: "GET",
|
|
||||||
dataType: "jsonp",
|
|
||||||
timeout: 1000
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Config.get("youtube-v2-key")) {
|
|
||||||
options.headers = {
|
|
||||||
"X-Gdata-Key": "key=" + Config.get("youtube-v2-key")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
urlRetrieve(https, options, function (status, data) {
|
|
||||||
switch (status) {
|
|
||||||
case 200:
|
|
||||||
break; /* Request is OK, skip to handling data */
|
|
||||||
case 400:
|
|
||||||
return callback("Invalid request", null);
|
|
||||||
case 403:
|
|
||||||
return callback("Private video", null);
|
|
||||||
case 404:
|
|
||||||
return callback("Video not found", null);
|
|
||||||
case 500:
|
|
||||||
case 503:
|
|
||||||
return callback("Service unavailable", null);
|
|
||||||
default:
|
|
||||||
return callback("HTTP " + status, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = data;
|
|
||||||
try {
|
|
||||||
data = JSON.parse(data);
|
|
||||||
/* Check for embedding restrictions */
|
|
||||||
if (data.entry.yt$accessControl) {
|
|
||||||
var ac = data.entry.yt$accessControl;
|
|
||||||
for (var i = 0; i < ac.length; i++) {
|
|
||||||
if (ac[i].action === "embed") {
|
|
||||||
if (ac[i].permission === "denied") {
|
|
||||||
callback("Embedding disabled", null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var seconds = data.entry.media$group.yt$duration.seconds;
|
|
||||||
var title = data.entry.title.$t;
|
|
||||||
var meta = {};
|
|
||||||
/* Check for country restrictions */
|
|
||||||
if (data.entry.media$group.media$restriction) {
|
|
||||||
var rest = data.entry.media$group.media$restriction;
|
|
||||||
if (rest.length > 0) {
|
|
||||||
if (rest[0].relationship === "deny") {
|
|
||||||
meta.restricted = rest[0].$t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var media = new Media(id, title, seconds, "yt", meta);
|
|
||||||
callback(false, media);
|
|
||||||
} catch (e) {
|
|
||||||
// Gdata version 2 has the rather silly habit of
|
|
||||||
// returning error codes in XML when I explicitly asked
|
|
||||||
// for JSON
|
|
||||||
var m = buffer.match(/<internalReason>([^<]+)<\/internalReason>/);
|
|
||||||
if (m === null)
|
|
||||||
m = buffer.match(/<code>([^<]+)<\/code>/);
|
|
||||||
|
|
||||||
var err = e;
|
|
||||||
if (m) {
|
|
||||||
if(m[1] === "too_many_recent_calls") {
|
|
||||||
err = "YouTube is throttling the server right "+
|
|
||||||
"now for making too many requests. "+
|
|
||||||
"Please try again in a moment.";
|
|
||||||
} else {
|
|
||||||
err = m[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(err, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/* youtube.com playlists - old v2 api */
|
|
||||||
yp2: function (id, callback, url) {
|
|
||||||
/**
|
|
||||||
* NOTE: callback may be called multiple times, once for each <= 25 video
|
|
||||||
* batch of videos in the list. It will be called in order.
|
|
||||||
*/
|
|
||||||
var m = id.match(/([\w-]+)/);
|
|
||||||
if (m) {
|
|
||||||
id = m[1];
|
|
||||||
} else {
|
|
||||||
callback("Invalid ID", null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var path = "/feeds/api/playlists/" + id + "?v=2&alt=json";
|
|
||||||
/**
|
|
||||||
* NOTE: the third parameter, url, is used to chain this retriever
|
|
||||||
* multiple times to get all the videos from a playlist, as each
|
|
||||||
* request only returns 25 videos.
|
|
||||||
*/
|
|
||||||
if (url !== undefined) {
|
|
||||||
path = "/" + url.split("gdata.youtube.com")[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
host: "gdata.youtube.com",
|
|
||||||
port: 443,
|
|
||||||
path: path,
|
|
||||||
method: "GET",
|
|
||||||
dataType: "jsonp",
|
|
||||||
timeout: 1000
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Config.get("youtube-v2-key")) {
|
|
||||||
options.headers = {
|
|
||||||
"X-Gdata-Key": "key=" + Config.get("youtube-v2-key")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
urlRetrieve(https, options, function (status, data) {
|
|
||||||
switch (status) {
|
|
||||||
case 200:
|
|
||||||
break; /* Request is OK, skip to handling data */
|
|
||||||
case 400:
|
|
||||||
return callback("Invalid request", null);
|
|
||||||
case 403:
|
|
||||||
return callback("Private playlist", null);
|
|
||||||
case 404:
|
|
||||||
return callback("Playlist not found", null);
|
|
||||||
case 500:
|
|
||||||
case 503:
|
|
||||||
return callback("Service unavailable", null);
|
|
||||||
default:
|
|
||||||
return callback("HTTP " + status, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
data = JSON.parse(data);
|
|
||||||
var vids = [];
|
|
||||||
for(var i in data.feed.entry) {
|
|
||||||
try {
|
|
||||||
/**
|
|
||||||
* FIXME: This should probably check for embed restrictions
|
|
||||||
* and country restrictions on each video in the list
|
|
||||||
*/
|
|
||||||
var item = data.feed.entry[i];
|
|
||||||
var id = item.media$group.yt$videoid.$t;
|
|
||||||
var title = item.title.$t;
|
|
||||||
var seconds = item.media$group.yt$duration.seconds;
|
|
||||||
var media = new Media(id, title, seconds, "yt");
|
|
||||||
vids.push(media);
|
|
||||||
} catch(e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(false, vids);
|
|
||||||
|
|
||||||
var links = data.feed.link;
|
|
||||||
for (var i in links) {
|
|
||||||
if (links[i].rel === "next") {
|
|
||||||
/* Look up the next batch of videos from the list */
|
|
||||||
Getters["yp2"](id, callback, links[i].href);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
callback(e, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/* youtube.com search - old v2 api */
|
|
||||||
ytSearch2: function (terms, callback) {
|
|
||||||
/**
|
|
||||||
* terms is a list of words from the search query. Each word must be
|
|
||||||
* encoded properly for use in the request URI
|
|
||||||
*/
|
|
||||||
for (var i in terms) {
|
|
||||||
terms[i] = encodeURIComponent(terms[i]);
|
|
||||||
}
|
|
||||||
var query = terms.join("+");
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
host: "gdata.youtube.com",
|
|
||||||
port: 443,
|
|
||||||
path: "/feeds/api/videos/?q=" + query + "&v=2&alt=json",
|
|
||||||
method: "GET",
|
|
||||||
dataType: "jsonp",
|
|
||||||
timeout: 1000
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Config.get("youtube-v2-key")) {
|
|
||||||
options.headers = {
|
|
||||||
"X-Gdata-Key": "key=" + Config.get("youtube-v2-key")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
urlRetrieve(https, options, function (status, data) {
|
|
||||||
if (status !== 200) {
|
|
||||||
callback("YouTube search: HTTP " + status, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
data = JSON.parse(data);
|
|
||||||
var vids = [];
|
|
||||||
for(var i in data.feed.entry) {
|
|
||||||
try {
|
|
||||||
/**
|
|
||||||
* FIXME: This should probably check for embed restrictions
|
|
||||||
* and country restrictions on each video in the list
|
|
||||||
*/
|
|
||||||
var item = data.feed.entry[i];
|
|
||||||
var id = item.media$group.yt$videoid.$t;
|
|
||||||
var title = item.title.$t;
|
|
||||||
var seconds = item.media$group.yt$duration.seconds;
|
|
||||||
var media = new Media(id, title, seconds, "yt");
|
|
||||||
media.thumb = item.media$group.media$thumbnail[0];
|
|
||||||
vids.push(media);
|
|
||||||
} catch(e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(false, vids);
|
|
||||||
} catch(e) {
|
|
||||||
callback(e, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue