custom-media: implement queueing and playback changes

This commit is contained in:
Calvin Montgomery 2017-08-08 20:35:17 -07:00
parent a6de8731b3
commit 04c9d48779
10 changed files with 93 additions and 21 deletions

View file

@ -2,7 +2,7 @@
"author": "Calvin Montgomery",
"name": "CyTube",
"description": "Online media synchronizer and chat",
"version": "3.44.4",
"version": "3.45.0",
"repository": {
"url": "http://github.com/calzoneman/sync"
},

View file

@ -19,6 +19,7 @@ TYPE_MAP =
hl: HLSPlayer
sb: VideoJSPlayer
tc: VideoJSPlayer
cm: VideoJSPlayer
window.loadMediaPlayer = (data) ->
try

View file

@ -69,6 +69,9 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
).appendTo(video)
)
# TODO: Refactor VideoJSPlayer to use a preLoad()/load()/postLoad() pattern
# VideoJSPlayer should provide the core functionality and logic for specific
# dependent player types (gdrive) should be an extension
if data.meta.gdrive_subtitles
data.meta.gdrive_subtitles.available.forEach((subt) ->
label = subt.lang_original
@ -83,6 +86,17 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
).appendTo(video)
)
if data.meta.textTracks
data.meta.textTracks.forEach((track) ->
label = track.name
$('<track/>').attr(
src: track.url
kind: 'subtitles'
type: track.type
label: label
).appendTo(video)
)
@player = videojs(video[0],
autoplay: true,
controls: true,

View file

@ -386,7 +386,7 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
id: id
});
return;
} else if (type === "fi" && !perms.canAddRawFile(user)) {
} else if ((type === "fi" || type === "cm") && !perms.canAddRawFile(user)) {
user.socket.emit("queueFail", {
msg: "You don't have permission to add raw video files",
link: link,

View file

@ -4,6 +4,9 @@ import net from 'net';
import Media from './media';
import { hash } from './util/hash';
import { get as httpGet } from 'http';
import { get as httpsGet } from 'https';
const LOGGER = require('@calzoneman/jsli')('custom-media');
const SOURCE_QUALITIES = new Set([
240,
@ -39,7 +42,20 @@ export function lookup(url, opts) {
Object.assign(options, parseURL(url));
const req = httpGet(options);
if (!/^https?:$/.test(options.protocol)) {
reject(new ValidationError(
`Unacceptable protocol "${options.protocol}". Custom metadata must be`
+ ' retrieved by HTTP or HTTPS'
));
return;
}
LOGGER.info('Looking up %s', url);
// this is fucking stupid
const get = options.protocol === 'https:' ? httpsGet : httpGet;
const req = get(options);
req.setTimeout(opts.timeout, () => {
const error = new Error('Request timed out');
@ -48,6 +64,7 @@ export function lookup(url, opts) {
});
req.on('error', error => {
LOGGER.warn('Request for %s failed: %s', url, error);
reject(error);
});
@ -89,11 +106,11 @@ export function lookup(url, opts) {
});
});
}).then(body => {
return convert(JSON.parse(body));
return convert(url, JSON.parse(body));
});
}
export function convert(data) {
export function convert(id, data) {
validate(data);
if (data.live) data.duration = 0;
@ -118,12 +135,6 @@ export function convert(data) {
live: !!data.live // Currently ignored by Media
};
const id = hash('sha256', JSON.stringify([
data.title,
data.duration,
meta
]), 'base64');
return new Media(id, data.title, data.duration, 'cm', meta);
}

View file

@ -14,6 +14,7 @@ var GoogleDrive = require("cytube-mediaquery/lib/provider/googledrive");
var TwitchVOD = require("cytube-mediaquery/lib/provider/twitch-vod");
var TwitchClip = require("cytube-mediaquery/lib/provider/twitch-clip");
import { Counter } from 'prom-client';
import { lookup as lookupCustomMetadata } from './custom-media';
const LOGGER = require('@calzoneman/jsli')('get-info');
const lookupCounter = new Counter({
@ -539,6 +540,16 @@ var Getters = {
}).catch(function (err) {
callback(err.message || err, null);
});
},
/* 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);
}
}
};

View file

@ -246,6 +246,8 @@
return "https://streamable.com/" + id;
case "tc":
return "https://clips.twitch.tv/" + id;
case "cm":
return id;
default:
return "";
}

View file

@ -207,9 +207,11 @@ describe('custom-media', () => {
describe('#convert', () => {
let expected;
let id = 'testing';
beforeEach(() => {
expected = {
id: 'testing',
title: 'Test Video',
seconds: 10,
duration: '00:10',
@ -237,7 +239,6 @@ describe('custom-media', () => {
function cleanForComparison(actual) {
actual = actual.pack();
delete actual.id;
// Strip out extraneous undefineds
for (let key in actual.meta) {
@ -248,10 +249,7 @@ describe('custom-media', () => {
}
it('converts custom metadata to a CyTube Media object', () => {
const media = convert(valid);
assert(media.id != null, 'should have generated id');
const media = convert(id, valid);
const actual = cleanForComparison(media);
assert.deepStrictEqual(actual, expected);
@ -262,10 +260,7 @@ describe('custom-media', () => {
expected.duration = '00:00';
expected.seconds = 0;
const media = convert(valid);
assert(media.id != null, 'should have generated id');
const media = convert(id, valid);
const actual = cleanForComparison(media);
assert.deepStrictEqual(actual, expected);
@ -404,6 +399,18 @@ describe('custom-media', () => {
});
});
it('rejects URLs with non-http(s) protocols', () => {
return lookup('ftp://127.0.0.1:10111/').then(() => {
throw new Error('Expected failure due to unacceptable URL protocol');
}).catch(error => {
assert.strictEqual(
error.message,
'Unacceptable protocol "ftp:". Custom metadata must be retrieved'
+ ' by HTTP or HTTPS'
);
});
});
it('rejects invalid URLs', () => {
return lookup('not valid').then(() => {
throw new Error('Expected failure due to invalid URL');

View file

@ -545,6 +545,18 @@
}).appendTo(video);
});
}
if (data.meta.textTracks) {
data.meta.textTracks.forEach(function(track) {
var label;
label = track.name;
return $('<track/>').attr({
src: track.url,
kind: 'subtitles',
type: track.type,
label: label
}).appendTo(video);
});
}
_this.player = videojs(video[0], {
autoplay: true,
controls: true,
@ -1529,7 +1541,8 @@
vm: VideoJSPlayer,
hl: HLSPlayer,
sb: VideoJSPlayer,
tc: VideoJSPlayer
tc: VideoJSPlayer,
cm: VideoJSPlayer
};
window.loadMediaPlayer = function(data) {

View file

@ -56,6 +56,8 @@ function formatURL(data) {
return "https://streamable.com/" + data.id;
case "tc":
return "https://clips.twitch.tv/" + data.id;
case "cm":
return data.id;
default:
return "#";
}
@ -1413,6 +1415,12 @@ function parseMediaLink(url) {
type: "fi"
};
}
if ((m = url.match(/^cm:(.*)/))) {
return {
id: m[1],
type: "cm"
};
}
// Generic for the rest.
if ((m = url.match(/^([a-z]{2}):([^\?&#]+)/))) {
return {
@ -1430,6 +1438,11 @@ function parseMediaLink(url) {
msg: "Raw files must begin with 'https'. Plain http is not supported."
});
throw new Error("ERROR_QUEUE_HTTP");
} else if (tmp.match(/\.json$/)) {
return {
id: url,
type: "cm"
};
} else if (tmp.match(/\.(mp4|flv|webm|og[gv]|mp3|mov|m4a)$/)) {
return {
id: url,