mirror of https://github.com/calzoneman/sync.git
Tweaks
This commit is contained in:
parent
0fe2d9afb9
commit
9d9f638496
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
Copyright (c) 2013-2021 Calvin Montgomery and contributors
|
Copyright (c) 2013-2022 Calvin Montgomery and contributors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|
9
NEWS.md
9
NEWS.md
|
@ -1,3 +1,12 @@
|
||||||
|
2022-08-28
|
||||||
|
==========
|
||||||
|
|
||||||
|
This release integrates Xaekai's added support for Bandcamp, BitChute, Odysee,
|
||||||
|
and Nicovideo playback support into the main repository. The updated support
|
||||||
|
for custom fonts and audio tracks in custom media manifests is also included,
|
||||||
|
but does not work out of the box -- it requires a separate channel script; this
|
||||||
|
may be addressed in the future.
|
||||||
|
|
||||||
2021-08-14
|
2021-08-14
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
|
|
@ -15,20 +15,26 @@ var order = [
|
||||||
'vimeo.coffee',
|
'vimeo.coffee',
|
||||||
'youtube.coffee',
|
'youtube.coffee',
|
||||||
|
|
||||||
|
// playerjs-based players
|
||||||
'playerjs.coffee',
|
'playerjs.coffee',
|
||||||
'iframechild.coffee',
|
'iframechild.coffee',
|
||||||
'odysee.coffee',
|
'odysee.coffee',
|
||||||
'streamable.coffee',
|
'streamable.coffee',
|
||||||
|
|
||||||
|
// iframe embed-based players
|
||||||
'embed.coffee',
|
'embed.coffee',
|
||||||
'custom-embed.coffee',
|
'custom-embed.coffee',
|
||||||
'livestream.com.coffee',
|
'livestream.com.coffee',
|
||||||
'twitchclip.coffee',
|
'twitchclip.coffee',
|
||||||
|
|
||||||
|
// video.js-based players
|
||||||
'videojs.coffee',
|
'videojs.coffee',
|
||||||
'gdrive-player.coffee',
|
'gdrive-player.coffee',
|
||||||
'hls.coffee',
|
'hls.coffee',
|
||||||
'raw-file.coffee',
|
'raw-file.coffee',
|
||||||
'rtmp.coffee',
|
'rtmp.coffee',
|
||||||
|
|
||||||
|
// mediaUpdate handler
|
||||||
'update.coffee'
|
'update.coffee'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
"author": "Calvin Montgomery",
|
"author": "Calvin Montgomery",
|
||||||
"name": "CyTube",
|
"name": "CyTube",
|
||||||
"description": "Online media synchronizer and chat",
|
"description": "Online media synchronizer and chat",
|
||||||
"version": "3.82.11",
|
"version": "3.83.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "http://github.com/calzoneman/sync"
|
"url": "http://github.com/calzoneman/sync"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@calzoneman/jsli": "^2.0.1",
|
"@calzoneman/jsli": "^2.0.1",
|
||||||
"@cytube/mediaquery": "github:CyTube/mediaquery#4f803961d72a4fc7a1e09c0babaf8ea685013b1b",
|
"@cytube/mediaquery": "github:CyTube/mediaquery#1efa3253ead853d977564ce677f2fb94d618b49e",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"bluebird": "^3.7.2",
|
"bluebird": "^3.7.2",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
|
|
|
@ -23,7 +23,8 @@ window.IframeChild = class IframeChild extends PlayerJSPlayer
|
||||||
|
|
||||||
setupFrame: (iframe, data) ->
|
setupFrame: (iframe, data) ->
|
||||||
iframe.addEventListener('load', =>
|
iframe.addEventListener('load', =>
|
||||||
iframe.contentWindow.VOLUME = VOLUME;
|
# TODO: ideally, communication with the child frame should use postMessage()
|
||||||
|
iframe.contentWindow.VOLUME = VOLUME
|
||||||
iframe.contentWindow.loadMediaPlayer(Object.assign({}, data, { type: 'cm' } ))
|
iframe.contentWindow.loadMediaPlayer(Object.assign({}, data, { type: 'cm' } ))
|
||||||
iframe.contentWindow.document.querySelector('#ytapiplayer').classList.add('vjs-16-9')
|
iframe.contentWindow.document.querySelector('#ytapiplayer').classList.add('vjs-16-9')
|
||||||
adapter = iframe.contentWindow.playerjs.VideoJSAdapter(iframe.contentWindow.PLAYER.player)
|
adapter = iframe.contentWindow.playerjs.VideoJSAdapter(iframe.contentWindow.PLAYER.player)
|
||||||
|
|
|
@ -21,7 +21,7 @@ window.PeerPlayer = class PeerPlayer extends Player
|
||||||
|
|
||||||
site = new URL(document.URL).hostname
|
site = new URL(document.URL).hostname
|
||||||
embedSrc = data.meta.embed.domain
|
embedSrc = data.meta.embed.domain
|
||||||
link = "<a href=\"http://#{embedSrc}\" target=\"_blank\"><strong>#{embedSrc}</strong></a>"
|
link = "<a href=\"http://#{embedSrc}\" target=\"_blank\" rel=\"noopener noreferer\"><strong>#{embedSrc}</strong></a>"
|
||||||
alert = makeAlert('Privacy Advisory', PEERTUBE_EMBED_WARNING.replace('%link%', link).replace('%site%', site),
|
alert = makeAlert('Privacy Advisory', PEERTUBE_EMBED_WARNING.replace('%link%', link).replace('%site%', site),
|
||||||
'alert-warning')
|
'alert-warning')
|
||||||
.removeClass('col-md-12')
|
.removeClass('col-md-12')
|
||||||
|
|
|
@ -176,6 +176,14 @@ export function validate(data) {
|
||||||
validateSources(data.sources, data);
|
validateSources(data.sources, data);
|
||||||
validateAudioTracks(data.audioTracks);
|
validateAudioTracks(data.audioTracks);
|
||||||
validateTextTracks(data.textTracks);
|
validateTextTracks(data.textTracks);
|
||||||
|
/*
|
||||||
|
* TODO: Xaekai's Octopus subtitle support uses a separate subTracks array
|
||||||
|
* in a slightly different format than textTracks. That currently requires
|
||||||
|
* a channel script to use, but if that is integrated in core then it needs
|
||||||
|
* to be validated here (and ideally merged with textTracks so there is only
|
||||||
|
* one array).
|
||||||
|
*/
|
||||||
|
validateFonts(data.fonts);
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateSources(sources, data) {
|
function validateSources(sources, data) {
|
||||||
|
@ -198,8 +206,7 @@ function validateSources(sources, data) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
`contentType "${source.contentType}" requires live: true`
|
`contentType "${source.contentType}" requires live: true`
|
||||||
);
|
);
|
||||||
|
// TODO (Xaekai): This should be allowed
|
||||||
// TODO: This should be allowed
|
|
||||||
if (/*!AUDIO_ONLY_CONTENT_TYPES.has(source.contentType) && */!SOURCE_QUALITIES.has(source.quality))
|
if (/*!AUDIO_ONLY_CONTENT_TYPES.has(source.contentType) && */!SOURCE_QUALITIES.has(source.quality))
|
||||||
throw new ValidationError(`unacceptable source quality "${source.quality}"`);
|
throw new ValidationError(`unacceptable source quality "${source.quality}"`);
|
||||||
|
|
||||||
|
@ -288,6 +295,20 @@ function validateTextTracks(textTracks) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateFonts(fonts) {
|
||||||
|
if (typeof textTracks === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(fonts))
|
||||||
|
throw new ValidationError('fonts must be a list of URLs');
|
||||||
|
|
||||||
|
for (let f of fonts) {
|
||||||
|
if (typeof f !== 'string')
|
||||||
|
throw new ValidationError('fonts must be a list of URLs');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function parseURL(urlstring) {
|
function parseURL(urlstring) {
|
||||||
const url = urlParse(urlstring);
|
const url = urlParse(urlstring);
|
||||||
|
|
||||||
|
|
|
@ -196,7 +196,7 @@ class IOServer {
|
||||||
|
|
||||||
handleConnection(socket) {
|
handleConnection(socket) {
|
||||||
if (!this.checkIPLimit(socket)) {
|
if (!this.checkIPLimit(socket)) {
|
||||||
//return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
patchTypecheckedFunctions(socket);
|
patchTypecheckedFunctions(socket);
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { fetchPeertubeDomains, setDomains } from '@cytube/mediaquery/lib/provider/peertube';
|
||||||
|
import { stat, readFile, writeFile } from 'node:fs/promises';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const LOGGER = require('@calzoneman/jsli')('peertubelist');
|
||||||
|
const ONE_DAY = 24 * 3600 * 1000;
|
||||||
|
const FILENAME = path.join(__dirname, '..', 'peertube-hosts.json');
|
||||||
|
|
||||||
|
export async function setupPeertubeDomains() {
|
||||||
|
try {
|
||||||
|
let mtime;
|
||||||
|
try {
|
||||||
|
mtime = (await stat(FILENAME)).mtime;
|
||||||
|
} catch (_error) {
|
||||||
|
mtime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Date.now() - mtime > ONE_DAY) {
|
||||||
|
LOGGER.info('Updating peertube host list');
|
||||||
|
const hosts = await fetchPeertubeDomains();
|
||||||
|
await writeFile(FILENAME, JSON.stringify(hosts));
|
||||||
|
}
|
||||||
|
|
||||||
|
const hosts = JSON.parse(await readFile(FILENAME));
|
||||||
|
setDomains(hosts);
|
||||||
|
} catch (error) {
|
||||||
|
LOGGER.error('Failed to initialize peertube host list: %s', error.stack);
|
||||||
|
}
|
||||||
|
}
|
|
@ -204,6 +204,8 @@ var Server = function () {
|
||||||
// background tasks init ----------------------------------------------
|
// background tasks init ----------------------------------------------
|
||||||
require("./bgtask")(self);
|
require("./bgtask")(self);
|
||||||
|
|
||||||
|
require("./peertubelist").setupPeertubeDomains().then(() => {});
|
||||||
|
|
||||||
// prometheus server
|
// prometheus server
|
||||||
const prometheusConfig = Config.getPrometheusConfig();
|
const prometheusConfig = Config.getPrometheusConfig();
|
||||||
if (prometheusConfig.isEnabled()) {
|
if (prometheusConfig.isEnabled()) {
|
||||||
|
|
|
@ -205,17 +205,20 @@
|
||||||
return "https://clips.twitch.tv/" + id;
|
return "https://clips.twitch.tv/" + id;
|
||||||
case "cm":
|
case "cm":
|
||||||
return id;
|
return id;
|
||||||
case "pt":
|
case "pt": {
|
||||||
const [domain,uuid] = id.split(';');
|
const [domain,uuid] = id.split(';');
|
||||||
return `https://${domain}/videos/watch/${uuid}`;
|
return `https://${domain}/videos/watch/${uuid}`;
|
||||||
|
}
|
||||||
case "bc":
|
case "bc":
|
||||||
return `https://www.bitchute.com/video/${id}/`;
|
return `https://www.bitchute.com/video/${id}/`;
|
||||||
case "bn":
|
case "bn": {
|
||||||
const [artist,track] = id.split(';');
|
const [artist,track] = id.split(';');
|
||||||
return `https://${artist}.bandcamp.com/track/${track}`;
|
return `https://${artist}.bandcamp.com/track/${track}`;
|
||||||
case "od":
|
}
|
||||||
|
case "od": {
|
||||||
const [user,video] = id.split(';');
|
const [user,video] = id.split(';');
|
||||||
return `https://odysee.com/@${user}/${video}`;
|
return `https://odysee.com/@${user}/${video}`;
|
||||||
|
}
|
||||||
case "nv":
|
case "nv":
|
||||||
return `https://www.nicovideo.jp/watch/${id}`;
|
return `https://www.nicovideo.jp/watch/${id}`;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -233,7 +233,8 @@ describe('custom-media', () => {
|
||||||
contentType: 'text/vtt',
|
contentType: 'text/vtt',
|
||||||
name: 'English Subtitles'
|
name: 'English Subtitles'
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
thumbnail: 'https://example.com/thumb.jpg',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -321,7 +322,8 @@ describe('custom-media', () => {
|
||||||
contentType: 'text/vtt',
|
contentType: 'text/vtt',
|
||||||
name: 'English Subtitles'
|
name: 'English Subtitles'
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
thumbnail: 'https://example.com/thumb.jpg',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
* Written by Xaekai
|
* Written by Xaekai
|
||||||
* Copyright (c) 2022 Radiant Feather; Licensed AGPLv3
|
* Copyright (c) 2022 Radiant Feather; Licensed AGPLv3
|
||||||
*
|
*
|
||||||
|
* Dual-licensed MIT when distributed with CyTube/sync.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
class NicovideoEmbed {
|
class NicovideoEmbed {
|
||||||
static origin = 'https://embed.nicovideo.jp';
|
static origin = 'https://embed.nicovideo.jp';
|
||||||
|
@ -65,6 +67,7 @@ class NicovideoEmbed {
|
||||||
const source = new URL(`${NicovideoEmbed.origin}/watch/${videoId}`);
|
const source = new URL(`${NicovideoEmbed.origin}/watch/${videoId}`);
|
||||||
source.search = new URLSearchParams({
|
source.search = new URLSearchParams({
|
||||||
jsapi: 1,
|
jsapi: 1,
|
||||||
|
autoplay: 1,
|
||||||
playerId
|
playerId
|
||||||
});
|
});
|
||||||
iframe.setAttribute('src', source);
|
iframe.setAttribute('src', source);
|
||||||
|
|
|
@ -3102,6 +3102,7 @@ CSEmoteList.prototype.loadPage = function (page) {
|
||||||
var row = document.createElement("tr");
|
var row = document.createElement("tr");
|
||||||
tbody.appendChild(row);
|
tbody.appendChild(row);
|
||||||
|
|
||||||
|
// TODO: refactor this garbage
|
||||||
(function (emote, row) {
|
(function (emote, row) {
|
||||||
// Add delete button
|
// Add delete button
|
||||||
var tdDelete = document.createElement("td");
|
var tdDelete = document.createElement("td");
|
||||||
|
@ -3201,25 +3202,58 @@ CSEmoteList.prototype.loadPage = function (page) {
|
||||||
$(tdImage).find(".popover").remove();
|
$(tdImage).find(".popover").remove();
|
||||||
$urlDisplay.detach();
|
$urlDisplay.detach();
|
||||||
|
|
||||||
|
var inputGroup = document.createElement("div");
|
||||||
|
inputGroup.className = "input-group";
|
||||||
|
|
||||||
var editInput = document.createElement("input");
|
var editInput = document.createElement("input");
|
||||||
editInput.className = "form-control";
|
editInput.className = "form-control";
|
||||||
editInput.type = "text";
|
editInput.type = "text";
|
||||||
editInput.value = emote.image;
|
editInput.value = emote.image;
|
||||||
tdImage.appendChild(editInput);
|
inputGroup.appendChild(editInput);
|
||||||
|
|
||||||
|
var btnGroup = document.createElement("div");
|
||||||
|
btnGroup.className = "input-group-btn";
|
||||||
|
|
||||||
|
var saveBtn = document.createElement("button");
|
||||||
|
saveBtn.className = "btn btn-success";
|
||||||
|
saveBtn.textContent = "Save";
|
||||||
|
saveBtn.type = "button";
|
||||||
|
btnGroup.appendChild(saveBtn);
|
||||||
|
|
||||||
|
var cancelBtn = document.createElement("button");
|
||||||
|
cancelBtn.className = "btn btn-danger";
|
||||||
|
cancelBtn.textContent = "Cancel";
|
||||||
|
cancelBtn.type = "button";
|
||||||
|
btnGroup.appendChild(cancelBtn);
|
||||||
|
|
||||||
|
inputGroup.appendChild(btnGroup);
|
||||||
|
tdImage.appendChild(inputGroup);
|
||||||
|
|
||||||
editInput.focus();
|
editInput.focus();
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
var val = editInput.value;
|
var val = editInput.value;
|
||||||
tdImage.removeChild(editInput);
|
|
||||||
tdImage.appendChild(urlDisplay);
|
if (val === emote.image) {
|
||||||
|
cleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
socket.emit("updateEmote", {
|
socket.emit("updateEmote", {
|
||||||
name: emote.name,
|
name: emote.name,
|
||||||
image: val
|
image: val
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
editInput.onblur = save;
|
function cleanup() {
|
||||||
|
tdImage.removeChild(inputGroup);
|
||||||
|
tdImage.appendChild(urlDisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelBtn.onclick = cleanup;
|
||||||
|
saveBtn.onclick = save;
|
||||||
editInput.onkeyup = function (event) {
|
editInput.onkeyup = function (event) {
|
||||||
if (event.keyCode === 13) {
|
if (event.keyCode === 13) {
|
||||||
save();
|
save();
|
||||||
|
|
Loading…
Reference in New Issue