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");
|
2017-08-02 21:24:44 -07:00
|
|
|
import { Counter } from 'prom-client';
|
2017-08-08 20:35:17 -07:00
|
|
|
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');
|
2017-08-02 21:24:44 -07:00
|
|
|
const lookupCounter = new Counter({
|
2017-08-12 13:12:58 -07:00
|
|
|
name: 'cytube_media_lookups_total',
|
2017-08-02 21:24:44 -07:00
|
|
|
help: 'Count of media lookups',
|
|
|
|
|
labelNames: ['shortCode']
|
|
|
|
|
});
|
2013-10-11 16:31:40 -05:00
|
|
|
|
|
|
|
|
var urlRetrieve = function (transport, options, callback) {
|
2014-12-26 10:39:47 -05:00
|
|
|
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, "");
|
|
|
|
|
});
|
|
|
|
|
|
2014-12-26 10:39:47 -05:00
|
|
|
var buffer = "";
|
|
|
|
|
res.setEncoding("utf-8");
|
|
|
|
|
res.on("data", function (chunk) {
|
|
|
|
|
buffer += chunk;
|
2014-12-04 00:42:25 -06:00
|
|
|
});
|
2014-12-26 10:39:47 -05:00
|
|
|
res.on("end", function () {
|
|
|
|
|
callback(res.statusCode, buffer);
|
2013-08-16 21:35:21 -05:00
|
|
|
});
|
2014-12-26 10:39:47 -05:00
|
|
|
});
|
2013-08-03 10:52:24 -04:00
|
|
|
|
2014-12-26 10:39:47 -05:00
|
|
|
req.on("error", function (err) {
|
2017-04-04 23:02:31 -07:00
|
|
|
LOGGER.error("HTTP request " + options.host + options.path + " failed: " +
|
2014-12-26 10:39:47 -05:00
|
|
|
err);
|
|
|
|
|
callback(503, "");
|
2013-10-11 16:31:40 -05:00
|
|
|
});
|
2014-12-26 10:39:47 -05:00
|
|
|
|
|
|
|
|
req.end();
|
2013-10-11 16:31:40 -05:00
|
|
|
};
|
|
|
|
|
|
2015-05-15 00:03:05 -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) {
|
2015-03-27 18:44:46 -05:00
|
|
|
if (!Config.get("youtube-v3-key")) {
|
2015-06-07 11:45:23 -04:00
|
|
|
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
|
|
|
}
|
2013-09-03 22:23:05 -05:00
|
|
|
|
2013-08-03 10:58:21 -04:00
|
|
|
|
2015-03-27 18:44:46 -05:00
|
|
|
YouTube.lookup(id).then(function (video) {
|
|
|
|
|
var meta = {};
|
|
|
|
|
if (video.meta.blocked) {
|
|
|
|
|
meta.restricted = video.meta.blocked;
|
2013-08-03 16:13:19 -04:00
|
|
|
}
|
2020-12-31 16:50:05 +02:00
|
|
|
if (video.meta.ytRating) {
|
2021-01-03 20:57:37 +02:00
|
|
|
meta.ytRating = video.meta.ytRating;
|
2020-12-31 16:50:05 +02:00
|
|
|
}
|
2014-04-04 11:37:30 -05:00
|
|
|
|
2015-03-27 18:44:46 -05:00
|
|
|
var media = new Media(video.id, video.title, video.duration, "yt", meta);
|
|
|
|
|
callback(false, media);
|
|
|
|
|
}).catch(function (err) {
|
2015-04-07 15:42:24 -05:00
|
|
|
callback(err.message || err, null);
|
2013-10-11 16:31:40 -05:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* youtube.com playlists */
|
2015-03-27 18:44:46 -05:00
|
|
|
yp: function (id, callback) {
|
|
|
|
|
if (!Config.get("youtube-v3-key")) {
|
2015-06-07 11:45:23 -04:00
|
|
|
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
|
|
|
}
|
2013-08-01 10:27:32 -04:00
|
|
|
|
2015-03-27 18:44:46 -05:00
|
|
|
YouTube.lookupPlaylist(id).then(function (videos) {
|
|
|
|
|
videos = videos.map(function (video) {
|
|
|
|
|
var meta = {};
|
|
|
|
|
if (video.meta.blocked) {
|
|
|
|
|
meta.restricted = video.meta.blocked;
|
2013-08-01 10:27:32 -04:00
|
|
|
}
|
|
|
|
|
|
2015-03-27 18:44:46 -05:00
|
|
|
return new Media(video.id, video.title, video.duration, "yt", meta);
|
|
|
|
|
});
|
2013-08-03 10:58:21 -04:00
|
|
|
|
2015-03-27 18:44:46 -05:00
|
|
|
callback(null, videos);
|
|
|
|
|
}).catch(function (err) {
|
2015-04-07 15:42:24 -05:00
|
|
|
callback(err.message || err, null);
|
2013-10-11 16:31:40 -05:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* youtube.com search */
|
2015-03-27 18:44:46 -05:00
|
|
|
ytSearch: function (query, callback) {
|
|
|
|
|
if (!Config.get("youtube-v3-key")) {
|
2015-06-07 11:45:23 -04:00
|
|
|
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
|
|
|
|
2015-03-27 18:44:46 -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;
|
2013-08-03 10:58:21 -04:00
|
|
|
}
|
|
|
|
|
|
2015-03-27 18:44:46 -05:00
|
|
|
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) {
|
2015-04-07 15:42:24 -05:00
|
|
|
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;
|
|
|
|
|
}
|
2014-04-04 11:37:30 -05:00
|
|
|
|
2016-01-07 17:38:05 -08:00
|
|
|
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) {
|
2015-02-16 00:50:15 -08:00
|
|
|
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-09-03 22:23:05 -05:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* soundcloud.com */
|
|
|
|
|
sc: function (id, callback) {
|
2014-05-20 19:30:14 -07:00
|
|
|
/* TODO: require server owners to register their own API key, put in config */
|
2013-10-11 16:31:40 -05:00
|
|
|
const SC_CLIENT = "2e0c82ab5a020f3a7509318146128abd";
|
|
|
|
|
|
2018-04-07 15:30:30 -07:00
|
|
|
var m = id.match(/([\w-/.:]+)/);
|
2013-10-11 16:31:40 -05:00
|
|
|
if (m) {
|
|
|
|
|
id = m[1];
|
|
|
|
|
} else {
|
|
|
|
|
callback("Invalid ID", null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-07-31 23:26:11 -04:00
|
|
|
|
2013-10-11 16:31:40 -05:00
|
|
|
var options = {
|
|
|
|
|
host: "api.soundcloud.com",
|
|
|
|
|
port: 443,
|
|
|
|
|
path: "/resolve.json?url=" + id + "&client_id=" + SC_CLIENT,
|
|
|
|
|
method: "GET",
|
|
|
|
|
dataType: "jsonp",
|
|
|
|
|
timeout: 1000
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
urlRetrieve(https, options, function (status, data) {
|
2014-05-20 19:30:14 -07:00
|
|
|
switch (status) {
|
|
|
|
|
case 200:
|
2014-05-21 20:53:13 -07:00
|
|
|
case 302:
|
2014-05-20 19:30:14 -07:00
|
|
|
break; /* Request is OK, skip to handling data */
|
|
|
|
|
case 400:
|
|
|
|
|
return callback("Invalid request", null);
|
|
|
|
|
case 403:
|
|
|
|
|
return callback("Private sound", null);
|
|
|
|
|
case 404:
|
|
|
|
|
return callback("Sound not found", null);
|
|
|
|
|
case 500:
|
|
|
|
|
case 503:
|
|
|
|
|
return callback("Service unavailable", null);
|
|
|
|
|
default:
|
|
|
|
|
return callback("HTTP " + status, null);
|
2013-10-11 16:31:40 -05:00
|
|
|
}
|
2013-08-01 10:12:22 -04:00
|
|
|
|
2013-10-11 16:31:40 -05:00
|
|
|
var track = null;
|
|
|
|
|
try {
|
|
|
|
|
data = JSON.parse(data);
|
|
|
|
|
track = data.location;
|
|
|
|
|
} catch(e) {
|
|
|
|
|
callback(e, null);
|
2013-09-03 22:23:05 -05:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-11 16:31:40 -05:00
|
|
|
var options2 = {
|
2013-08-03 10:58:21 -04:00
|
|
|
host: "api.soundcloud.com",
|
|
|
|
|
port: 443,
|
2013-10-11 16:31:40 -05:00
|
|
|
path: track,
|
2013-08-03 10:58:21 -04:00
|
|
|
method: "GET",
|
|
|
|
|
dataType: "jsonp",
|
|
|
|
|
timeout: 1000
|
|
|
|
|
};
|
|
|
|
|
|
2014-05-20 19:30:14 -07:00
|
|
|
/**
|
|
|
|
|
* There has got to be a way to directly get the data I want without
|
|
|
|
|
* making two requests to Soundcloud...right?
|
|
|
|
|
* ...right?
|
|
|
|
|
*/
|
2013-10-11 16:31:40 -05:00
|
|
|
urlRetrieve(https, options2, 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 sound", null);
|
|
|
|
|
case 404:
|
|
|
|
|
return callback("Sound not found", null);
|
|
|
|
|
case 500:
|
|
|
|
|
case 503:
|
|
|
|
|
return callback("Service unavailable", null);
|
|
|
|
|
default:
|
|
|
|
|
return callback("HTTP " + status, null);
|
2013-08-03 10:58:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
data = JSON.parse(data);
|
2013-10-11 16:31:40 -05:00
|
|
|
var seconds = data.duration / 1000;
|
|
|
|
|
var title = data.title;
|
2015-01-11 12:10:09 -06:00
|
|
|
var meta = {};
|
|
|
|
|
if (data.sharing === "private" && data.embeddable_by === "all") {
|
|
|
|
|
meta.scuri = data.uri;
|
|
|
|
|
}
|
|
|
|
|
var media = new Media(id, title, seconds, "sc", meta);
|
2013-10-11 16:31:40 -05:00
|
|
|
callback(false, media);
|
2013-08-03 10:58:21 -04:00
|
|
|
} catch(e) {
|
2013-10-07 00:10:16 -05:00
|
|
|
callback(e, null);
|
2013-08-03 10:58:21 -04:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
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
|
|
|
/* ustream.tv */
|
|
|
|
|
us: function (id, callback) {
|
2018-11-16 19:52:09 -08:00
|
|
|
var m = id.match(/(channel\/[^?&#]+)/);
|
2013-10-11 16:31:40 -05:00
|
|
|
if (m) {
|
|
|
|
|
id = m[1];
|
|
|
|
|
} else {
|
|
|
|
|
callback("Invalid ID", null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-05-20 19:30:14 -07:00
|
|
|
|
2013-10-11 16:31:40 -05:00
|
|
|
var options = {
|
|
|
|
|
host: "www.ustream.tv",
|
2018-11-16 19:52:09 -08:00
|
|
|
port: 443,
|
2013-10-11 16:31:40 -05:00
|
|
|
path: "/" + id,
|
|
|
|
|
method: "GET",
|
|
|
|
|
timeout: 1000
|
|
|
|
|
};
|
|
|
|
|
|
2018-11-16 19:52:09 -08:00
|
|
|
urlRetrieve(https, options, function (status, data) {
|
2013-10-11 16:31:40 -05:00
|
|
|
if(status !== 200) {
|
|
|
|
|
callback("Ustream HTTP " + status, null);
|
2013-09-03 22:23:05 -05:00
|
|
|
return;
|
|
|
|
|
}
|
2013-08-03 10:58:21 -04:00
|
|
|
|
2018-11-16 19:52:09 -08:00
|
|
|
/*
|
|
|
|
|
* Yes, regexing this information out of the HTML sucks.
|
|
|
|
|
* No, there is not a better solution -- it seems IBM
|
|
|
|
|
* deprecated the old API (or at least replaced with an
|
|
|
|
|
* enterprise API marked "Contact sales") so fuck it.
|
2014-05-20 19:30:14 -07:00
|
|
|
*/
|
2015-03-23 16:10:00 -07:00
|
|
|
var m = data.match(/https:\/\/www\.ustream\.tv\/embed\/(\d+)/);
|
|
|
|
|
if (m) {
|
2013-10-11 16:31:40 -05:00
|
|
|
var title = "Ustream.tv - " + id;
|
|
|
|
|
var media = new Media(m[1], title, "--:--", "us");
|
|
|
|
|
callback(false, media);
|
2013-09-03 22:23:05 -05:00
|
|
|
} else {
|
2015-03-23 16:10:00 -07:00
|
|
|
callback("Channel ID not found", null);
|
2013-09-03 22:23:05 -05:00
|
|
|
}
|
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
|
|
|
/* imgur.com albums */
|
|
|
|
|
im: function (id, callback) {
|
2014-05-20 19:30:14 -07:00
|
|
|
/**
|
|
|
|
|
* TODO: Consider deprecating this in favor of custom embeds
|
|
|
|
|
*/
|
2013-10-11 16:31:40 -05:00
|
|
|
var m = id.match(/([\w-]+)/);
|
|
|
|
|
if (m) {
|
|
|
|
|
id = m[1];
|
|
|
|
|
} else {
|
|
|
|
|
callback("Invalid ID", null);
|
|
|
|
|
return;
|
2013-08-03 10:58:21 -04:00
|
|
|
}
|
2013-10-11 16:31:40 -05:00
|
|
|
var title = "Imgur Album - " + id;
|
|
|
|
|
var media = new Media(id, title, "--:--", "im");
|
|
|
|
|
callback(false, media);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* 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) {
|
2018-01-14 15:15:59 -08:00
|
|
|
if (!/^[a-zA-Z0-9_-]+$/.test(id)) {
|
|
|
|
|
callback("Invalid ID: " + id);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-15 00:03:05 -05:00
|
|
|
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);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2014-06-01 11:43:18 -07:00
|
|
|
/* 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
|
|
|
},
|
|
|
|
|
|
2017-05-20 16:50:00 -07:00
|
|
|
/* hitbox.tv / smashcast.tv */
|
2015-01-22 23:21:31 -06:00
|
|
|
hb: function (id, callback) {
|
|
|
|
|
var m = id.match(/([\w-]+)/);
|
|
|
|
|
if (m) {
|
|
|
|
|
id = m[1];
|
|
|
|
|
} else {
|
|
|
|
|
callback("Invalid ID", null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-05-20 16:50:00 -07:00
|
|
|
var title = "Smashcast - " + id;
|
2015-01-22 23:21:31 -06:00
|
|
|
var media = new Media(id, title, "--:--", "hb");
|
|
|
|
|
callback(false, media);
|
|
|
|
|
},
|
2016-06-25 17:09:48 -07:00
|
|
|
|
|
|
|
|
/* vid.me */
|
|
|
|
|
vm: function (id, callback) {
|
2017-12-24 11:19:30 -08:00
|
|
|
process.nextTick(
|
|
|
|
|
callback,
|
|
|
|
|
"As of December 2017, vid.me is no longer in service."
|
|
|
|
|
);
|
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);
|
2016-08-02 22:40:29 -07:00
|
|
|
}).catch(function (err) {
|
|
|
|
|
callback(err.message || err, null);
|
2016-06-25 17:09:48 -07:00
|
|
|
});
|
2017-08-08 20:35:17 -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
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* mixer.com */
|
|
|
|
|
mx: function (id, callback) {
|
2020-07-26 10:24:36 -07:00
|
|
|
process.nextTick(
|
|
|
|
|
callback,
|
|
|
|
|
"As of July 2020, Mixer is no longer in service."
|
|
|
|
|
);
|
2016-06-25 17:09:48 -07:00
|
|
|
}
|
2013-10-11 16:31:40 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
Getters: Getters,
|
|
|
|
|
getMedia: function (id, type, callback) {
|
|
|
|
|
if(type in this.Getters) {
|
2017-07-16 22:35:33 -07:00
|
|
|
LOGGER.info("Looking up %s:%s", type, id);
|
2017-08-12 13:12:58 -07:00
|
|
|
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);
|
2013-08-03 10:58:21 -04:00
|
|
|
}
|
2015-06-28 09:42:21 -07:00
|
|
|
}
|
2013-10-11 16:31:40 -05:00
|
|
|
};
|