From fbfb38392d600f1fce11438abc44a3d2e49f26cb Mon Sep 17 00:00:00 2001 From: John Livingston Date: Sat, 20 Feb 2021 18:31:21 +0100 Subject: [PATCH] Work in progress: builtin converseJS. --- client/videowatch-client-plugin.js | 368 +++++++++++++++-------------- main.js | 60 ++++- 2 files changed, 249 insertions(+), 179 deletions(-) diff --git a/client/videowatch-client-plugin.js b/client/videowatch-client-plugin.js index 43dbc96d..b0e9e07c 100644 --- a/client/videowatch-client-plugin.js +++ b/client/videowatch-client-plugin.js @@ -1,203 +1,219 @@ 'use strict' -const logger = { - log: (s) => console.log('[peertube-plugin-livechat] ' + s), - info: (s) => console.info('[peertube-plugin-livechat] ' + s), - error: (s) => console.error('[peertube-plugin-livechat] ' + s), - warn: (s) => console.warn('[peertube-plugin-livechat] ' + s) -} - -const videoCache = {} -let lastUUID = null -let settings = {} - -function parseUUIDs (s) { - if (!s) { - return [] +function register ({ registerHook, peertubeHelpers }) { + const logger = { + log: (s) => console.log('[peertube-plugin-livechat] ' + s), + info: (s) => console.info('[peertube-plugin-livechat] ' + s), + error: (s) => console.error('[peertube-plugin-livechat] ' + s), + warn: (s) => console.warn('[peertube-plugin-livechat] ' + s) } - let a = s.split('\n') - a = a.map(line => { - return line.replace(/#.*$/, '') - .replace(/^\s+/, '') - .replace(/\s+$/, '') - }) - return a.filter(line => line !== '') -} -function getIframeUri (uuid) { - if (!settings) { - logger.error('Settings are not initialized, too soon to compute the iframeUri') - return null + const videoCache = {} + let lastUUID = null + let settings = {} + + function parseUUIDs (s) { + if (!s) { + return [] + } + let a = s.split('\n') + a = a.map(line => { + return line.replace(/#.*$/, '') + .replace(/^\s+/, '') + .replace(/\s+$/, '') + }) + return a.filter(line => line !== '') } - let iframeUri = settings['chat-uri'] || '' - if (iframeUri === '') { - logger.error('No iframe uri') - return null + + function getBaseRoute () { + // FIXME: should be provided by PeertubeHelpers (does not exists for now) + return '/plugins/livechat/router' } - iframeUri = iframeUri.replace('{{VIDEO_UUID}}', uuid) - if (!/^https?:\/\//.test(iframeUri)) { - logger.error('The webchaturi must begin with https://') - return null + + function getIframeUri (uuid) { + if (!settings) { + logger.error('Settings are not initialized, too soon to compute the iframeUri') + return null + } + let iframeUri = '' + if (!settings['chat-use-builtin']) { + iframeUri = settings['chat-uri'] || '' + iframeUri = iframeUri.replace('{{VIDEO_UUID}}', uuid) + if (!/^https?:\/\//.test(iframeUri)) { + logger.error('The webchaturi must begin with https://') + return null + } + } else { + // Using the builtin converseJS + // FIXME: with Peertube 3.0.1 there is no loadByIdOrUUID method, + // we need to pass the complete url. + const video = videoCache[uuid] + if (video) { + const url = video.originInstanceUrl + '/videos/watch/' + uuid + iframeUri = getBaseRoute() + '/webchat?url=' + encodeURIComponent(url) + } + } + if (iframeUri === '') { + logger.error('No iframe uri') + return null + } + return iframeUri } - return iframeUri -} -function displayButton (buttons, name, label, callback) { - const button = document.createElement('button') - button.classList.add( - 'action-button', - 'peertube-plugin-livechat-stuff', - 'peertube-plugin-livechat-button-' + name - ) - button.setAttribute('type', 'button') - button.textContent = label - button.onclick = callback - buttons.prepend(button) -} + function displayButton (buttons, name, label, callback) { + const button = document.createElement('button') + button.classList.add( + 'action-button', + 'peertube-plugin-livechat-stuff', + 'peertube-plugin-livechat-button-' + name + ) + button.setAttribute('type', 'button') + button.textContent = label + button.onclick = callback + buttons.prepend(button) + } -function displayChatButtons (peertubeHelpers, uuid) { - logger.log('Adding buttons in the DOM...') - const p = new Promise((resolve) => { - Promise.all([ - peertubeHelpers.translate('Open chat'), - peertubeHelpers.translate('Open chat in a new window'), - peertubeHelpers.translate('Close chat') - ]).then(labels => { - const labelOpen = labels[0] - const labelOpenBlank = labels[1] - const labelClose = labels[2] - const buttons = document.querySelector('.video-actions') + function displayChatButtons (peertubeHelpers, uuid) { + logger.log('Adding buttons in the DOM...') + const p = new Promise((resolve, reject) => { + Promise.all([ + peertubeHelpers.translate('Open chat'), + peertubeHelpers.translate('Open chat in a new window'), + peertubeHelpers.translate('Close chat') + ]).then(labels => { + const labelOpen = labels[0] + const labelOpenBlank = labels[1] + const labelClose = labels[2] + const buttons = document.querySelector('.video-actions') - displayButton(buttons, 'openblank', labelOpenBlank, () => { - closeChat() - window.open(getIframeUri(uuid)) + const iframeUri = getIframeUri(uuid) + if (!iframeUri) { + return reject(new Error('No uri, cant display the buttons.')) + } + displayButton(buttons, 'openblank', labelOpenBlank, () => { + closeChat() + window.open(iframeUri) + }) + displayButton(buttons, 'open', labelOpen, () => openChat()) + displayButton(buttons, 'close', labelClose, () => closeChat()) + + toggleShowHideButtons(null) + resolve() }) - displayButton(buttons, 'open', labelOpen, () => openChat()) - displayButton(buttons, 'close', labelClose, () => closeChat()) + }) + return p + } + + function toggleShowHideButtons (chatOpened) { + // showing/hiding buttons... + document.querySelectorAll('.peertube-plugin-livechat-button-open') + .forEach(button => (button.style.display = (chatOpened === true || chatOpened === null ? 'none' : ''))) + + document.querySelectorAll('.peertube-plugin-livechat-button-close') + .forEach(button => (button.style.display = (chatOpened === false || chatOpened === null ? 'none' : ''))) + } + + function openChat () { + const p = new Promise((resolve, reject) => { + const uuid = lastUUID + if (!uuid) { + logger.log('No current uuid.') + return reject(new Error('No current uuid.')) + } + + logger.info('Trying to load the chat for video ' + uuid + '.') + const iframeUri = getIframeUri(uuid) + if (!iframeUri) { + logger.error('Incorrect iframe uri') + return reject(new Error('Incorrect iframe uri')) + } + const additionalStyles = settings['chat-style'] || '' + + logger.info('Opening the chat...') + const videoWrapper = document.querySelector('#video-wrapper') + + // Creating the iframe... + const iframe = document.createElement('iframe') + iframe.setAttribute('src', iframeUri) + iframe.classList.add( + 'peertube-plugin-livechat', + 'peertube-plugin-livechat-stuff', + 'peertube-plugin-livechat-iframe-stuff' + ) + iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-popups allow-forms') + iframe.setAttribute('frameborder', '0') + if (additionalStyles) { + iframe.setAttribute('style', additionalStyles) + } + videoWrapper.append(iframe) + + // showing/hiding buttons... + toggleShowHideButtons(true) - toggleShowHideButtons(null) resolve() }) - }) - return p -} + return p + } -function toggleShowHideButtons (chatOpened) { - // showing/hiding buttons... - document.querySelectorAll('.peertube-plugin-livechat-button-open') - .forEach(button => (button.style.display = (chatOpened === true || chatOpened === null ? 'none' : ''))) - - document.querySelectorAll('.peertube-plugin-livechat-button-close') - .forEach(button => (button.style.display = (chatOpened === false || chatOpened === null ? 'none' : ''))) -} - -function openChat () { - const p = new Promise((resolve, reject) => { - const uuid = lastUUID - if (!uuid) { - logger.log('No current uuid.') - return reject(new Error('No current uuid.')) - } - - logger.info('Trying to load the chat for video ' + uuid + '.') - const iframeUri = getIframeUri(uuid) - if (!iframeUri) { - logger.error('Incorrect iframe uri') - return reject(new Error('Incorrect iframe uri')) - } - const additionalStyles = settings['chat-style'] || '' - - logger.info('Opening the chat...') - const videoWrapper = document.querySelector('#video-wrapper') - - // Creating the iframe... - const iframe = document.createElement('iframe') - iframe.setAttribute('src', iframeUri) - iframe.classList.add( - 'peertube-plugin-livechat', - 'peertube-plugin-livechat-stuff', - 'peertube-plugin-livechat-iframe-stuff' - ) - iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-popups allow-forms') - iframe.setAttribute('frameborder', '0') - if (additionalStyles) { - iframe.setAttribute('style', additionalStyles) - } - videoWrapper.append(iframe) + function closeChat () { + document.querySelectorAll('.peertube-plugin-livechat-iframe-stuff') + .forEach(dom => dom.remove()) // showing/hiding buttons... - toggleShowHideButtons(true) - - resolve() - }) - return p -} - -function closeChat () { - document.querySelectorAll('.peertube-plugin-livechat-iframe-stuff') - .forEach(dom => dom.remove()) - - // showing/hiding buttons... - toggleShowHideButtons(false) -} - -function initChat (peertubeHelpers) { - const el = document.querySelector('#videojs-wrapper') - if (!el) { - logger.error('The required div is not present in the DOM.') - return + toggleShowHideButtons(false) } - if (el.classList.contains('peertube-plugin-livechat-init')) { - logger.log('The chat seems already initialized...') - return - } - // Adding a custom class in the dom, so we know initChat was already called. - el.classList.add('peertube-plugin-livechat-init') - peertubeHelpers.getSettings().then(s => { - settings = s - const liveOn = !!settings['chat-all-lives'] - const nonLiveOn = !!settings['chat-all-non-lives'] - const uuids = parseUUIDs(settings['chat-videos-list']) - const iframeUri = settings['chat-uri'] || '' - if (iframeUri === '') { - logger.log('no uri, can\'t add chat.') + function initChat () { + const el = document.querySelector('#videojs-wrapper') + if (!el) { + logger.error('The required div is not present in the DOM.') return } - if (!uuids.length && !liveOn && !nonLiveOn) { - logger.log('not activated.') + if (el.classList.contains('peertube-plugin-livechat-init')) { + logger.log('The chat seems already initialized...') return } + // Adding a custom class in the dom, so we know initChat was already called. + el.classList.add('peertube-plugin-livechat-init') - logger.log('Checking if this video should have a chat...') - const uuid = lastUUID - const video = videoCache[uuid] - if (!video) { - logger.error('Can\'t find the video ' + uuid + ' in the videoCache') - return - } - if (uuids.indexOf(uuid) >= 0) { - logger.log('This video is in the list for chats.') - } else if (video.isLive && liveOn) { - logger.log('This video is live and we want all lives.') - } else if (!video.isLive && nonLiveOn) { - logger.log('This video is not live and we want all non-lives.') - } else { - logger.log('This video will not have a chat.') - return - } - - displayChatButtons(peertubeHelpers, uuid).then(() => { - if (settings['chat-auto-display']) { - openChat() - } else { - toggleShowHideButtons(false) + peertubeHelpers.getSettings().then(s => { + settings = s + const liveOn = !!settings['chat-all-lives'] + const nonLiveOn = !!settings['chat-all-non-lives'] + const uuids = parseUUIDs(settings['chat-videos-list']) + if (!uuids.length && !liveOn && !nonLiveOn) { + logger.log('not activated.') + return } - }) - }) -} -function register ({ registerHook, peertubeHelpers }) { + logger.log('Checking if this video should have a chat...') + const uuid = lastUUID + const video = videoCache[uuid] + if (!video) { + logger.error('Can\'t find the video ' + uuid + ' in the videoCache') + return + } + if (uuids.indexOf(uuid) >= 0) { + logger.log('This video is in the list for chats.') + } else if (video.isLive && liveOn) { + logger.log('This video is live and we want all lives.') + } else if (!video.isLive && nonLiveOn) { + logger.log('This video is not live and we want all non-lives.') + } else { + logger.log('This video will not have a chat.') + return + } + + displayChatButtons(peertubeHelpers, uuid).then(() => { + if (settings['chat-auto-display']) { + openChat() + } else { + toggleShowHideButtons(false) + } + }) + }) + } + registerHook({ target: 'filter:api.video-watch.video.get.result', handler: (video) => { @@ -208,7 +224,7 @@ function register ({ registerHook, peertubeHelpers }) { // FIXME: this should be made in action:video-watch.video.loaded. // But with Peertube 3.0.1, this hook is not called for lives // in WAITING_FOR_LIVE and LIVE_ENDED states. - initChat(peertubeHelpers) + initChat() return video } }) @@ -216,7 +232,7 @@ function register ({ registerHook, peertubeHelpers }) { // registerHook({ // target: 'action:video-watch.video.loaded', // handler: () => { - // initChat(peertubeHelpers) + // initChat() // } // }) } diff --git a/main.js b/main.js index 6773f869..8580de49 100644 --- a/main.js +++ b/main.js @@ -5,7 +5,9 @@ async function register ({ _storageManager, _videoCategoryManager, _videoLicenceManager, - _videoLanguageManager + _videoLanguageManager, + getRouter, + peertubeHelpers }) { registerSetting({ name: 'chat-auto-display', @@ -41,16 +43,39 @@ async function register ({ 'Don\'t add private videos, the UUIDs will be send to frontend.', private: false }) + + registerSetting({ + name: 'chat-use-builtin', + label: 'Use builtin ConverseJS', + type: 'input-checkbox', + default: true, + private: false, + descriptionHTML: 'If checked, use a builtin ConverseJS iframe.
' + + 'You still have to configure an external XMPP service. Please see the documentation.' + }) + registerSetting({ + name: 'chat-bosh-uri', + label: 'Builtin webchat: BOSH uri', + type: 'input', + default: true, + descriptionHTML: 'When using the built-in converseJS webchat:
' + + 'URI of the external BOSH server. Please make sure it accept cross origin request from your domain.', + private: true + }) + registerSetting({ name: 'chat-uri', label: 'Webchat url', type: 'input', default: '', - descriptionHTML: 'The webchat url. An iframe will be created pointing to this url. ' + + descriptionHTML: 'If you dont want to use the builtin ConverseJS webchat:
' + + 'Put here your webchat url. An iframe will be created pointing to this url. ' + 'The placeholder {{VIDEO_UUID}} will be replace by the video UUID if present. ' + - 'Example : https://my_domain/conversejs.html?room=video_{{VIDEO_UUID}}.', + 'Example : https://my_domain/conversejs.html?room=video_{{VIDEO_UUID}}.
' + + 'If this field is empty, it will use the builtin ConverseJS webchat.', private: false }) + registerSetting({ name: 'chat-style', label: 'Webchat iframe style attribute', @@ -60,6 +85,35 @@ async function register ({ 'Example: height:400px;', private: false }) + + const router = getRouter() + router.get('/ping', (req, res) => res.json({ message: 'pong' })) + router.get('/webchat', async (req, res, next) => { + try { + // FIXME: with Peertube 3.0.1 the following method is not available... + // When loadByIdOrUUID is available, change the entry point to + // be /webchat/:videoId + // const id = req.param('videoId') + // const video = await peertubeHelpers.videos.loadByIdOrUUID(id) + let url = req.query.url + if (!url) { + throw new Error('Missing url parameter)') + } + let video = await peertubeHelpers.videos.loadByUrl(url) + if (!video) { + // FIXME: remove this when loadByIdOrUUID will be available... + // This is a dirty Hack for dev environnements... + url = url.replace(/^https:/, 'http:') + video = await peertubeHelpers.videos.loadByUrl(url) + } + if (!video) { + throw new Error('Video not found') + } + res.send('ok') + } catch (error) { + return next(error) + } + }) } async function unregister () {