custom-media: implement queueing and playback changes
This commit is contained in:
parent
a6de8731b3
commit
04c9d48779
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -19,6 +19,7 @@ TYPE_MAP =
|
|||
hl: HLSPlayer
|
||||
sb: VideoJSPlayer
|
||||
tc: VideoJSPlayer
|
||||
cm: VideoJSPlayer
|
||||
|
||||
window.loadMediaPlayer = (data) ->
|
||||
try
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -246,6 +246,8 @@
|
|||
return "https://streamable.com/" + id;
|
||||
case "tc":
|
||||
return "https://clips.twitch.tv/" + id;
|
||||
case "cm":
|
||||
return id;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue