sync/src/get-info.js

393 lines
12 KiB
JavaScript
Raw Normal View History

2018-08-26 22:02:36 -07:00
const https = require("https");
const Media = require("./media");
const CustomEmbedFilter = require("./customembed").filter;
const Config = require("./config");
const ffmpeg = require("./ffmpeg");
2020-10-23 11:51:59 -07:00
const mediaquery = require("@cytube/mediaquery");
const YouTube = require("@cytube/mediaquery/lib/provider/youtube");
const Vimeo = require("@cytube/mediaquery/lib/provider/vimeo");
const Streamable = require("@cytube/mediaquery/lib/provider/streamable");
const TwitchVOD = require("@cytube/mediaquery/lib/provider/twitch-vod");
const TwitchClip = require("@cytube/mediaquery/lib/provider/twitch-clip");
import { Counter } from 'prom-client';
import { lookup as lookupCustomMetadata } from './custom-media';
2017-04-04 23:02:31 -07:00
2017-07-08 20:11:54 -07:00
const LOGGER = require('@calzoneman/jsli')('get-info');
const lookupCounter = new Counter({
name: 'cytube_media_lookups_total',
help: 'Count of media lookups',
labelNames: ['shortCode']
});
2013-10-11 16:31:40 -05:00
var urlRetrieve = function (transport, options, callback) {
var req = transport.request(options, function (res) {
2015-01-06 10:54:14 -05:00
res.on("error", function (err) {
2017-04-04 23:02:31 -07:00
LOGGER.error("HTTP response " + options.host + options.path + " failed: "+
2015-01-06 10:54:14 -05:00
err);
callback(503, "");
});
var buffer = "";
res.setEncoding("utf-8");
res.on("data", function (chunk) {
buffer += chunk;
});
res.on("end", function () {
callback(res.statusCode, buffer);
});
});
2013-08-03 10:52:24 -04:00
req.on("error", function (err) {
2017-04-04 23:02:31 -07:00
LOGGER.error("HTTP request " + options.host + options.path + " failed: " +
err);
callback(503, "");
2013-10-11 16:31:40 -05:00
});
req.end();
2013-10-11 16:31:40 -05:00
};
var mediaTypeMap = {
"youtube": "yt",
"googledrive": "gd",
"google+": "gp"
};
function convertMedia(media) {
return new Media(media.id, media.title, media.duration, mediaTypeMap[media.type],
media.meta);
}
2013-10-11 16:31:40 -05:00
var Getters = {
/* youtube.com */
yt: function (id, callback) {
if (!Config.get("youtube-v3-key")) {
return callback("The YouTube API now requires an API key. Please see the " +
"documentation for youtube-v3-key in config.template.yaml");
2013-10-11 16:31:40 -05:00
}
YouTube.lookup(id).then(function (video) {
var meta = {};
if (video.meta.blocked) {
meta.restricted = video.meta.blocked;
}
if (video.meta.ytRating) {
2021-01-03 20:57:37 +02:00
meta.ytRating = video.meta.ytRating;
}
var media = new Media(video.id, video.title, video.duration, "yt", meta);
callback(false, media);
}).catch(function (err) {
callback(err.message || err, null);
2013-10-11 16:31:40 -05:00
});
},
/* youtube.com playlists */
yp: function (id, callback) {
if (!Config.get("youtube-v3-key")) {
return callback("The YouTube API now requires an API key. Please see the " +
"documentation for youtube-v3-key in config.template.yaml");
2013-10-11 16:31:40 -05:00
}
YouTube.lookupPlaylist(id).then(function (videos) {
videos = videos.map(function (video) {
var meta = {};
if (video.meta.blocked) {
meta.restricted = video.meta.blocked;
}
return new Media(video.id, video.title, video.duration, "yt", meta);
});
callback(null, videos);
}).catch(function (err) {
callback(err.message || err, null);
2013-10-11 16:31:40 -05:00
});
},
/* youtube.com search */
ytSearch: function (query, callback) {
if (!Config.get("youtube-v3-key")) {
return callback("The YouTube API now requires an API key. Please see the " +
"documentation for youtube-v3-key in config.template.yaml");
2014-05-20 19:30:14 -07:00
}
2013-10-11 16:31:40 -05:00
YouTube.search(query).then(function (res) {
var videos = res.results;
videos = videos.map(function (video) {
var meta = {};
if (video.meta.blocked) {
meta.restricted = video.meta.blocked;
}
var media = new Media(video.id, video.title, video.duration, "yt", meta);
media.thumb = { url: video.meta.thumbnail };
return media;
});
callback(null, videos);
}).catch(function (err) {
callback(err.message || err, null);
2013-10-11 16:31:40 -05:00
});
},
/* vimeo.com */
vi: function (id, callback) {
var m = id.match(/([\w-]+)/);
if (m) {
id = m[1];
} else {
callback("Invalid ID", null);
return;
}
Vimeo.lookup(id).then(video => {
video = new Media(video.id, video.title, video.duration, "vi");
callback(null, video);
}).catch(error => {
callback(error.message);
2013-10-11 16:31:40 -05:00
});
},
/* dailymotion.com */
dm: function (id, callback) {
var m = id.match(/([\w-]+)/);
if (m) {
id = m[1].split("_")[0];
2013-10-11 16:31:40 -05:00
} else {
callback("Invalid ID", null);
return;
}
var options = {
host: "api.dailymotion.com",
port: 443,
path: "/video/" + id + "?fields=duration,title",
method: "GET",
dataType: "jsonp",
timeout: 1000
};
urlRetrieve(https, options, function (status, data) {
2014-05-20 19:30:14 -07:00
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);
}
2013-07-31 23:26:11 -04:00
2013-10-11 16:31:40 -05:00
try {
data = JSON.parse(data);
var title = data.title;
var seconds = data.duration;
2014-05-20 19:30:14 -07:00
/**
* This is a rather hacky way to indicate that a video has
* been deleted...
*/
if (title === "Deleted video" && seconds === 10) {
2013-10-07 00:10:16 -05:00
callback("Video not found", null);
return;
2013-07-31 23:26:11 -04:00
}
2013-10-11 16:31:40 -05:00
var media = new Media(id, title, seconds, "dm");
callback(false, media);
} catch(e) {
2014-06-25 16:13:54 -04:00
callback(e, null);
2013-10-11 16:31:40 -05:00
}
});
},
2021-08-12 19:46:47 -07:00
/* soundcloud.com - see https://github.com/calzoneman/sync/issues/916 */
2013-10-11 16:31:40 -05:00
sc: function (id, callback) {
2021-08-12 19:46:47 -07:00
callback(
"Soundcloud is not supported anymore due to requiring OAuth but not " +
"accepting new API key registrations."
);
2013-10-11 16:31:40 -05:00
},
/* livestream.com */
li: function (id, callback) {
var m = id.match(/([\w-]+)/);
if (m) {
id = m[1];
} else {
callback("Invalid ID", null);
return;
}
var title = "Livestream.com - " + id;
var media = new Media(id, title, "--:--", "li");
callback(false, media);
},
/* twitch.tv */
tw: function (id, callback) {
var m = id.match(/([\w-]+)/);
if (m) {
id = m[1];
} else {
callback("Invalid ID", null);
return;
}
var title = "Twitch.tv - " + id;
var media = new Media(id, title, "--:--", "tw");
callback(false, media);
},
2016-09-04 18:53:38 -07:00
/* twitch VOD */
tv: function (id, callback) {
var m = id.match(/([cv]\d+)/);
if (m) {
id = m[1];
} else {
process.nextTick(callback, "Invalid Twitch VOD ID");
return;
}
TwitchVOD.lookup(id).then(video => {
const media = new Media(video.id, video.title, video.duration,
"tv", video.meta);
process.nextTick(callback, false, media);
}).catch(function (err) {
callback(err.message || err, null);
});
},
2017-05-27 11:49:27 -07:00
/* twitch clip */
tc: function (id, callback) {
var m = id.match(/^([A-Za-z]+)$/);
if (m) {
id = m[1];
} else {
process.nextTick(callback, "Invalid Twitch VOD ID");
return;
}
TwitchClip.lookup(id).then(video => {
const media = new Media(video.id, video.title, video.duration,
"tc", video.meta);
process.nextTick(callback, false, media);
}).catch(function (err) {
callback(err.message || err, null);
});
},
2013-10-11 16:31:40 -05:00
/* rtmp stream */
rt: function (id, callback) {
var title = "Livestream";
var media = new Media(id, title, "--:--", "rt");
callback(false, media);
},
2016-08-06 21:14:52 -07:00
/* HLS stream */
hl: function (id, callback) {
2019-03-27 21:05:45 -07:00
if (!/^https/.test(id)) {
callback(
"HLS links must start with HTTPS due to browser security " +
"policy. See https://git.io/vpDLK for details."
);
return;
}
2016-08-06 21:14:52 -07:00
var title = "Livestream";
var media = new Media(id, title, "--:--", "hl");
callback(false, media);
},
2013-10-11 16:31:40 -05:00
/* custom embed */
cu: function (id, callback) {
2015-06-18 18:46:33 -04:00
var media;
try {
media = CustomEmbedFilter(id);
} catch (e) {
if (/invalid embed/i.test(e.message)) {
return callback(e.message);
} else {
2017-04-04 23:02:31 -07:00
LOGGER.error(e.stack);
2015-06-18 18:46:33 -04:00
return callback("Unknown error processing embed");
}
}
2013-10-11 16:31:40 -05:00
callback(false, media);
2013-11-07 17:19:36 -06:00
},
/* google docs */
gd: function (id, callback) {
if (!/^[a-zA-Z0-9_-]+$/.test(id)) {
callback("Invalid ID: " + id);
return;
}
var data = {
type: "googledrive",
kind: "single",
id: id
2013-11-07 17:19:36 -06:00
};
2015-05-15 01:19:08 -05:00
mediaquery.lookup(data).then(function (video) {
callback(null, convertMedia(video));
}).catch(function (err) {
callback(err.message || err);
});
},
/* ffmpeg for raw files */
fi: function (id, cb) {
ffmpeg.query(id, function (err, data) {
if (err) {
return cb(err);
}
var m = new Media(id, data.title, data.duration, "fi", {
bitrate: data.bitrate,
codec: data.codec
});
cb(null, m);
});
2014-07-09 20:41:11 -07:00
},
2016-08-02 22:35:00 -07:00
/* streamable */
sb: function (id, callback) {
if (!/^[\w-]+$/.test(id)) {
process.nextTick(callback, "Invalid streamable.com ID");
return;
}
Streamable.lookup(id).then(video => {
const media = new Media(video.id, video.title, video.duration,
"sb", video.meta);
process.nextTick(callback, false, media);
}).catch(function (err) {
callback(err.message || err, null);
2016-06-25 17:09:48 -07:00
});
},
/* custom media - https://github.com/calzoneman/sync/issues/655 */
cm: async function (id, callback) {
try {
const media = await lookupCustomMetadata(id);
process.nextTick(callback, false, media);
} catch (error) {
process.nextTick(callback, error.message);
}
2018-08-26 22:02:36 -07:00
},
2013-10-11 16:31:40 -05:00
};
module.exports = {
Getters: Getters,
getMedia: function (id, type, callback) {
if(type in this.Getters) {
LOGGER.info("Looking up %s:%s", type, id);
lookupCounter.labels(type).inc(1, new Date());
2013-10-11 16:31:40 -05:00
this.Getters[type](id, callback);
} else {
callback("Unknown media type '" + type + "'", null);
}
2015-06-28 09:42:21 -07:00
}
2013-10-11 16:31:40 -05:00
};