mirror of https://github.com/calzoneman/sync.git
Implement raw file queues
This commit is contained in:
parent
ac7f0ac47a
commit
1d1630fb50
|
@ -175,3 +175,10 @@ aggressive-gc: false
|
||||||
# Allows you to blacklist certain channels. Users will be automatically kicked
|
# Allows you to blacklist certain channels. Users will be automatically kicked
|
||||||
# upon trying to join one.
|
# upon trying to join one.
|
||||||
channel-blacklist: []
|
channel-blacklist: []
|
||||||
|
|
||||||
|
# If you have ffmpeg installed, you can query metadata from raw files, allowing
|
||||||
|
# server-synched raw file playback. This requires the following:
|
||||||
|
# * ffmpeg must be installed on the server
|
||||||
|
# * you must install the fluent-ffmpeg module (npm install fluent-ffmpeg)
|
||||||
|
ffmpeg:
|
||||||
|
enabled: false
|
||||||
|
|
|
@ -36,7 +36,8 @@ LibraryModule.prototype.getItem = function (id, cb) {
|
||||||
if (err) {
|
if (err) {
|
||||||
cb(err, null);
|
cb(err, null);
|
||||||
} else {
|
} else {
|
||||||
cb(null, new Media(row.id, row.title, row.seconds, row.type, {}));
|
var meta = JSON.parse(row.meta || "{}");
|
||||||
|
cb(null, new Media(row.id, row.title, row.seconds, row.type, meta));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,7 @@ const DEFAULT_PERMISSIONS = {
|
||||||
oplaylistjump: 1.5,
|
oplaylistjump: 1.5,
|
||||||
oplaylistaddlist: 1.5,
|
oplaylistaddlist: 1.5,
|
||||||
playlistaddcustom: 3, // Add custom embed to the playlist
|
playlistaddcustom: 3, // Add custom embed to the playlist
|
||||||
|
playlistaddrawfile: 2, // Add raw file to the playlist
|
||||||
playlistaddlive: 1.5, // Add a livestream to the playlist
|
playlistaddlive: 1.5, // Add a livestream to the playlist
|
||||||
exceedmaxlength: 2, // Add a video longer than the maximum length set
|
exceedmaxlength: 2, // Add a video longer than the maximum length set
|
||||||
addnontemp: 2, // Add a permanent video to the playlist
|
addnontemp: 2, // Add a permanent video to the playlist
|
||||||
|
@ -195,6 +196,10 @@ PermissionsModule.prototype.canAddCustom = function (account) {
|
||||||
return this.hasPermission(account, "playlistaddcustom");
|
return this.hasPermission(account, "playlistaddcustom");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PermissionsModule.prototype.canAddRawFile = function (account) {
|
||||||
|
return this.hasPermission(account, "playlistaddrawfile");
|
||||||
|
};
|
||||||
|
|
||||||
PermissionsModule.prototype.canMoveVideo = function (account) {
|
PermissionsModule.prototype.canMoveVideo = function (account) {
|
||||||
return this.hasPermission(account, "playlistmove");
|
return this.hasPermission(account, "playlistmove");
|
||||||
};
|
};
|
||||||
|
|
|
@ -110,9 +110,8 @@ PlaylistModule.prototype.load = function (data) {
|
||||||
var i = 0;
|
var i = 0;
|
||||||
playlist.pos = parseInt(playlist.pos);
|
playlist.pos = parseInt(playlist.pos);
|
||||||
playlist.pl.forEach(function (item) {
|
playlist.pl.forEach(function (item) {
|
||||||
/* Backwards compatibility */
|
|
||||||
var m = new Media(item.media.id, item.media.title, item.media.seconds,
|
var m = new Media(item.media.id, item.media.title, item.media.seconds,
|
||||||
item.media.type);
|
item.media.type, item.media.meta || {});
|
||||||
var newitem = new PlaylistItem(m, {
|
var newitem = new PlaylistItem(m, {
|
||||||
uid: self._nextuid++,
|
uid: self._nextuid++,
|
||||||
temp: item.temp,
|
temp: item.temp,
|
||||||
|
@ -134,7 +133,12 @@ PlaylistModule.prototype.load = function (data) {
|
||||||
|
|
||||||
PlaylistModule.prototype.save = function (data) {
|
PlaylistModule.prototype.save = function (data) {
|
||||||
var arr = this.items.toArray().map(function (m) {
|
var arr = this.items.toArray().map(function (m) {
|
||||||
delete m.meta;
|
/* Clear Google Docs and Vimeo meta */
|
||||||
|
if (m.meta) {
|
||||||
|
delete m.meta.object;
|
||||||
|
delete m.meta.params;
|
||||||
|
delete m.meta.direct;
|
||||||
|
}
|
||||||
return m;
|
return m;
|
||||||
});
|
});
|
||||||
var pos = 0;
|
var pos = 0;
|
||||||
|
@ -309,7 +313,10 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Specifying a custom title is currently only allowed for custom media */
|
/**
|
||||||
|
* Specifying a custom title is currently only allowed for custom media
|
||||||
|
* and raw files
|
||||||
|
*/
|
||||||
if (typeof data.title !== "string" || (data.type !== "cu" && data.type !== "fi")) {
|
if (typeof data.title !== "string" || (data.type !== "cu" && data.type !== "fi")) {
|
||||||
data.title = false;
|
data.title = false;
|
||||||
}
|
}
|
||||||
|
@ -344,6 +351,12 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
||||||
link: link
|
link: link
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
} else if (type === "fi" && !perms.canAddRawFile(user)) {
|
||||||
|
user.socket.emit("queueFail", {
|
||||||
|
msg: "You don't have permission to add raw video files",
|
||||||
|
link: link
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var temp = data.temp || !perms.canAddNonTemp(user);
|
var temp = data.temp || !perms.canAddNonTemp(user);
|
||||||
|
@ -393,6 +406,7 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
||||||
title: data.title,
|
title: data.title,
|
||||||
link: link,
|
link: link,
|
||||||
temp: temp,
|
temp: temp,
|
||||||
|
shouldAddToLibrary: temp,
|
||||||
queueby: queueby,
|
queueby: queueby,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
maxlength: maxlength
|
maxlength: maxlength
|
||||||
|
@ -426,6 +440,8 @@ PlaylistModule.prototype.queueStandard = function (user, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item !== null) {
|
if (item !== null) {
|
||||||
|
/* Don't re-cache data we got from the library */
|
||||||
|
data.shouldAddToLibrary = false;
|
||||||
self._addItem(item, data, user, function () {
|
self._addItem(item, data, user, function () {
|
||||||
lock.release();
|
lock.release();
|
||||||
self.channel.activeLock.release();
|
self.channel.activeLock.release();
|
||||||
|
@ -868,14 +884,24 @@ PlaylistModule.prototype._addItem = function (media, data, user, cb) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Warn about possibly unsupported formats */
|
||||||
|
if (media.type === "fi" && media.meta.codec !== "mov/h264" &&
|
||||||
|
media.meta.codec !== "flv/h264") {
|
||||||
|
user.socket.emit("queueWarn", {
|
||||||
|
msg: "The codec <code>" + media.meta.codec + "</code> is not supported " +
|
||||||
|
"by all browsers, and is not supported by the flash fallback layer. " +
|
||||||
|
"This video may not play for some users."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var item = new PlaylistItem(media, {
|
var item = new PlaylistItem(media, {
|
||||||
uid: self._nextuid++,
|
uid: self._nextuid++,
|
||||||
temp: data.temp,
|
temp: data.temp,
|
||||||
queueby: data.queueby
|
queueby: data.queueby
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data.title && media.type === "cu") {
|
if (data.title && (media.type === "cu" || media.type === "fi")) {
|
||||||
media.title = data.title;
|
media.setTitle(data.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
var success = function () {
|
var success = function () {
|
||||||
|
@ -897,7 +923,7 @@ PlaylistModule.prototype._addItem = function (media, data, user, cb) {
|
||||||
u.socket.emit("setPlaylistMeta", self.meta);
|
u.socket.emit("setPlaylistMeta", self.meta);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!data.temp && !util.isLive(media.type)) {
|
if (data.shouldAddToLibrary && !util.isLive(media.type)) {
|
||||||
if (self.channel.modules.library) {
|
if (self.channel.modules.library) {
|
||||||
self.channel.modules.library.cacheMedia(media);
|
self.channel.modules.library.cacheMedia(media);
|
||||||
}
|
}
|
||||||
|
|
|
@ -440,9 +440,14 @@ module.exports = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.query("INSERT INTO `chan_" + chan + "_library` (id, title, seconds, type) " +
|
var meta = JSON.stringify({
|
||||||
"VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE id=id",
|
bitrate: media.meta.bitrate,
|
||||||
[media.id, media.title, media.seconds, media.type], callback);
|
codec: media.meta.codec
|
||||||
|
});
|
||||||
|
|
||||||
|
db.query("INSERT INTO `chan_" + chan + "_library` (id, title, seconds, type, meta) " +
|
||||||
|
"VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE id=id",
|
||||||
|
[media.id, media.title, media.seconds, media.type, meta], callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -39,6 +39,8 @@ function update(version, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function addMetaColumnToLibraries(cb) {
|
function addMetaColumnToLibraries(cb) {
|
||||||
|
Logger.syslog.log("[database] db version indicates channel libraries don't have " +
|
||||||
|
"meta column. Updating...");
|
||||||
Q.nfcall(db.query, "SHOW TABLES")
|
Q.nfcall(db.query, "SHOW TABLES")
|
||||||
.then(function (rows) {
|
.then(function (rows) {
|
||||||
rows = rows.map(function (r) {
|
rows = rows.map(function (r) {
|
||||||
|
|
|
@ -18,7 +18,10 @@ function init() {
|
||||||
|
|
||||||
var acceptedCodecs = {
|
var acceptedCodecs = {
|
||||||
"mov/h264": true,
|
"mov/h264": true,
|
||||||
"matroska/vp8": true
|
"flv/h264": true,
|
||||||
|
"matroska/vp8": true,
|
||||||
|
"matroska/vp9": true,
|
||||||
|
"ogg/theora": true,
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.query = function (filename, cb) {
|
exports.query = function (filename, cb) {
|
||||||
|
@ -43,7 +46,7 @@ exports.query = function (filename, cb) {
|
||||||
var codec = video.container + "/" + video.codec;
|
var codec = video.container + "/" + video.codec;
|
||||||
|
|
||||||
if (!(codec in acceptedCodecs)) {
|
if (!(codec in acceptedCodecs)) {
|
||||||
return cb("Unsupported codec " + codec);
|
return cb("Unsupported video codec " + codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
|
|
12
lib/media.js
12
lib/media.js
|
@ -6,10 +6,7 @@ function Media(id, title, seconds, type, meta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.title = title;
|
this.setTitle(title);
|
||||||
if (this.title.length > 100) {
|
|
||||||
this.title = this.title.substring(0, 97) + "...";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.seconds = seconds === "--:--" ? 0 : parseInt(seconds);
|
this.seconds = seconds === "--:--" ? 0 : parseInt(seconds);
|
||||||
this.duration = util.formatTime(seconds);
|
this.duration = util.formatTime(seconds);
|
||||||
|
@ -20,6 +17,13 @@ function Media(id, title, seconds, type, meta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Media.prototype = {
|
Media.prototype = {
|
||||||
|
setTitle: function (title) {
|
||||||
|
this.title = title;
|
||||||
|
if (this.title.length > 100) {
|
||||||
|
this.title = this.title.substring(0, 97) + "...";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
pack: function () {
|
pack: function () {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
|
|
|
@ -261,6 +261,10 @@
|
||||||
return "http://imgur.com/a/" + id;
|
return "http://imgur.com/a/" + id;
|
||||||
case "us":
|
case "us":
|
||||||
return "http://ustream.tv/" + id;
|
return "http://ustream.tv/" + id;
|
||||||
|
case "gd":
|
||||||
|
return "https://docs.google.com/file/d/" + id;
|
||||||
|
case "fi":
|
||||||
|
return id;
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
33
www/js/ui.js
33
www/js/ui.js
|
@ -340,12 +340,16 @@ function queue(pos, src) {
|
||||||
var link = $("#mediaurl").val();
|
var link = $("#mediaurl").val();
|
||||||
var data = parseMediaLink(link);
|
var data = parseMediaLink(link);
|
||||||
var duration = undefined;
|
var duration = undefined;
|
||||||
|
var title = undefined;
|
||||||
if (link.indexOf("jw:") === 0) {
|
if (link.indexOf("jw:") === 0) {
|
||||||
duration = parseInt($("#addfromurl-duration-val").val());
|
duration = parseInt($("#addfromurl-duration-val").val());
|
||||||
if (duration <= 0 || isNaN(duration)) {
|
if (duration <= 0 || isNaN(duration)) {
|
||||||
duration = undefined;
|
duration = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (data.type === "fi") {
|
||||||
|
title = $("#addfromurl-title-val").val();
|
||||||
|
}
|
||||||
|
|
||||||
if (data.id == null || data.type == null) {
|
if (data.id == null || data.type == null) {
|
||||||
makeAlert("Error", "Failed to parse link. Please check that it is correct",
|
makeAlert("Error", "Failed to parse link. Please check that it is correct",
|
||||||
|
@ -354,11 +358,13 @@ function queue(pos, src) {
|
||||||
} else {
|
} else {
|
||||||
$("#mediaurl").val("");
|
$("#mediaurl").val("");
|
||||||
$("#addfromurl-duration").remove();
|
$("#addfromurl-duration").remove();
|
||||||
|
$("#addfromurl-title").remove();
|
||||||
socket.emit("queue", {
|
socket.emit("queue", {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
type: data.type,
|
type: data.type,
|
||||||
pos: pos,
|
pos: pos,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
|
title: title,
|
||||||
temp: $(".add-temp").prop("checked")
|
temp: $(".add-temp").prop("checked")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -373,7 +379,8 @@ $("#ce_queue_end").click(queue.bind(this, "end", "customembed"));
|
||||||
$("#mediaurl").keyup(function(ev) {
|
$("#mediaurl").keyup(function(ev) {
|
||||||
if (ev.keyCode === 13) {
|
if (ev.keyCode === 13) {
|
||||||
queue("end", "url");
|
queue("end", "url");
|
||||||
} else if ($("#mediaurl").val().indexOf("jw:") === 0) {
|
} else {
|
||||||
|
if ($("#mediaurl").val().indexOf("jw:") === 0) {
|
||||||
var duration = $("#addfromurl-duration");
|
var duration = $("#addfromurl-duration");
|
||||||
if (duration.length === 0) {
|
if (duration.length === 0) {
|
||||||
duration = $("<div/>")
|
duration = $("<div/>")
|
||||||
|
@ -389,6 +396,30 @@ $("#mediaurl").keyup(function(ev) {
|
||||||
} else {
|
} else {
|
||||||
$("#addfromurl-duration").remove();
|
$("#addfromurl-duration").remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var url = $("#mediaurl").val().split("?")[0];
|
||||||
|
if (url.match(/^https?:\/\/(.*)?\.(flv|mp4|ogg|webm)$/)) {
|
||||||
|
var title = $("#addfromurl-title");
|
||||||
|
if (title.length === 0) {
|
||||||
|
title = $("<div/>")
|
||||||
|
.attr("id", "addfromurl-title")
|
||||||
|
.appendTo($("#addfromurl"));
|
||||||
|
$("<span/>").text("Title (optional)")
|
||||||
|
.appendTo(title);
|
||||||
|
$("<input/>").addClass("form-control")
|
||||||
|
.attr("type", "text")
|
||||||
|
.attr("id", "addfromurl-title-val")
|
||||||
|
.keyup(function (ev) {
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
queue("end", "url");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo($("#addfromurl-title"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$("#addfromurl-title").remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#customembed-content").keydown(function(ev) {
|
$("#customembed-content").keydown(function(ev) {
|
||||||
|
|
|
@ -58,6 +58,8 @@ function formatURL(data) {
|
||||||
return "http://ustream.tv/" + data.id;
|
return "http://ustream.tv/" + data.id;
|
||||||
case "gd":
|
case "gd":
|
||||||
return "https://docs.google.com/file/d/" + data.id;
|
return "https://docs.google.com/file/d/" + data.id;
|
||||||
|
case "fi":
|
||||||
|
return data.id;
|
||||||
default:
|
default:
|
||||||
return "#";
|
return "#";
|
||||||
}
|
}
|
||||||
|
@ -1284,6 +1286,24 @@ function parseMediaLink(url) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Raw file */
|
||||||
|
var tmp = url.split("?")[0];
|
||||||
|
if (tmp.match(/^https?:\/\//)) {
|
||||||
|
if (tmp.match(/\.(mp4|flv|webm|ogg)$/)) {
|
||||||
|
return {
|
||||||
|
id: url,
|
||||||
|
type: "fi"
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
Callbacks.queueFail({
|
||||||
|
link: url,
|
||||||
|
msg: "The file you are attempting to queue does not match the supported " +
|
||||||
|
"file extensions mp4, flv, webm, ogg."
|
||||||
|
});
|
||||||
|
throw new Error("ERROR_QUEUE_UNSUPPORTED_EXTENSION");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: null,
|
id: null,
|
||||||
type: null
|
type: null
|
||||||
|
@ -1728,6 +1748,7 @@ function genPermissionsEditor() {
|
||||||
makeOption("Queue playlist", "playlistaddlist", standard, CHANNEL.perms.playlistaddlist+"");
|
makeOption("Queue playlist", "playlistaddlist", standard, CHANNEL.perms.playlistaddlist+"");
|
||||||
makeOption("Queue livestream", "playlistaddlive", standard, CHANNEL.perms.playlistaddlive+"");
|
makeOption("Queue livestream", "playlistaddlive", standard, CHANNEL.perms.playlistaddlive+"");
|
||||||
makeOption("Embed custom media", "playlistaddcustom", standard, CHANNEL.perms.playlistaddcustom + "");
|
makeOption("Embed custom media", "playlistaddcustom", standard, CHANNEL.perms.playlistaddcustom + "");
|
||||||
|
makeOption("Add raw video file", "playlistaddrawfile", standard, CHANNEL.perms.playlistaddrawfile + "");
|
||||||
makeOption("Exceed maximum media length", "exceedmaxlength", standard, CHANNEL.perms.exceedmaxlength+"");
|
makeOption("Exceed maximum media length", "exceedmaxlength", standard, CHANNEL.perms.exceedmaxlength+"");
|
||||||
makeOption("Add nontemporary media", "addnontemp", standard, CHANNEL.perms.addnontemp+"");
|
makeOption("Add nontemporary media", "addnontemp", standard, CHANNEL.perms.addnontemp+"");
|
||||||
makeOption("Temp/untemp playlist item", "settemp", standard, CHANNEL.perms.settemp+"");
|
makeOption("Temp/untemp playlist item", "settemp", standard, CHANNEL.perms.settemp+"");
|
||||||
|
|
Loading…
Reference in New Issue