Work in progress: builtin converseJS.

This commit is contained in:
John Livingston 2021-02-20 18:31:21 +01:00
parent 7f5d955cb0
commit fbfb38392d
2 changed files with 249 additions and 179 deletions

View File

@ -1,203 +1,219 @@
'use strict' 'use strict'
const logger = { function register ({ registerHook, peertubeHelpers }) {
log: (s) => console.log('[peertube-plugin-livechat] ' + s), const logger = {
info: (s) => console.info('[peertube-plugin-livechat] ' + s), log: (s) => console.log('[peertube-plugin-livechat] ' + s),
error: (s) => console.error('[peertube-plugin-livechat] ' + s), info: (s) => console.info('[peertube-plugin-livechat] ' + s),
warn: (s) => console.warn('[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 []
} }
let a = s.split('\n')
a = a.map(line => {
return line.replace(/#.*$/, '')
.replace(/^\s+/, '')
.replace(/\s+$/, '')
})
return a.filter(line => line !== '')
}
function getIframeUri (uuid) { const videoCache = {}
if (!settings) { let lastUUID = null
logger.error('Settings are not initialized, too soon to compute the iframeUri') let settings = {}
return null
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 === '') { function getBaseRoute () {
logger.error('No iframe uri') // FIXME: should be provided by PeertubeHelpers (does not exists for now)
return null return '/plugins/livechat/router'
} }
iframeUri = iframeUri.replace('{{VIDEO_UUID}}', uuid)
if (!/^https?:\/\//.test(iframeUri)) { function getIframeUri (uuid) {
logger.error('The webchaturi must begin with https://') if (!settings) {
return null 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) { function displayButton (buttons, name, label, callback) {
const button = document.createElement('button') const button = document.createElement('button')
button.classList.add( button.classList.add(
'action-button', 'action-button',
'peertube-plugin-livechat-stuff', 'peertube-plugin-livechat-stuff',
'peertube-plugin-livechat-button-' + name 'peertube-plugin-livechat-button-' + name
) )
button.setAttribute('type', 'button') button.setAttribute('type', 'button')
button.textContent = label button.textContent = label
button.onclick = callback button.onclick = callback
buttons.prepend(button) buttons.prepend(button)
} }
function displayChatButtons (peertubeHelpers, uuid) { function displayChatButtons (peertubeHelpers, uuid) {
logger.log('Adding buttons in the DOM...') logger.log('Adding buttons in the DOM...')
const p = new Promise((resolve) => { const p = new Promise((resolve, reject) => {
Promise.all([ Promise.all([
peertubeHelpers.translate('Open chat'), peertubeHelpers.translate('Open chat'),
peertubeHelpers.translate('Open chat in a new window'), peertubeHelpers.translate('Open chat in a new window'),
peertubeHelpers.translate('Close chat') peertubeHelpers.translate('Close chat')
]).then(labels => { ]).then(labels => {
const labelOpen = labels[0] const labelOpen = labels[0]
const labelOpenBlank = labels[1] const labelOpenBlank = labels[1]
const labelClose = labels[2] const labelClose = labels[2]
const buttons = document.querySelector('.video-actions') const buttons = document.querySelector('.video-actions')
displayButton(buttons, 'openblank', labelOpenBlank, () => { const iframeUri = getIframeUri(uuid)
closeChat() if (!iframeUri) {
window.open(getIframeUri(uuid)) 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() resolve()
}) })
}) return p
return p }
}
function toggleShowHideButtons (chatOpened) { function closeChat () {
// showing/hiding buttons... document.querySelectorAll('.peertube-plugin-livechat-iframe-stuff')
document.querySelectorAll('.peertube-plugin-livechat-button-open') .forEach(dom => dom.remove())
.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... // showing/hiding buttons...
toggleShowHideButtons(true) toggleShowHideButtons(false)
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
} }
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 => { function initChat () {
settings = s const el = document.querySelector('#videojs-wrapper')
const liveOn = !!settings['chat-all-lives'] if (!el) {
const nonLiveOn = !!settings['chat-all-non-lives'] logger.error('The required div is not present in the DOM.')
const uuids = parseUUIDs(settings['chat-videos-list'])
const iframeUri = settings['chat-uri'] || ''
if (iframeUri === '') {
logger.log('no uri, can\'t add chat.')
return return
} }
if (!uuids.length && !liveOn && !nonLiveOn) { if (el.classList.contains('peertube-plugin-livechat-init')) {
logger.log('not activated.') logger.log('The chat seems already initialized...')
return 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...') peertubeHelpers.getSettings().then(s => {
const uuid = lastUUID settings = s
const video = videoCache[uuid] const liveOn = !!settings['chat-all-lives']
if (!video) { const nonLiveOn = !!settings['chat-all-non-lives']
logger.error('Can\'t find the video ' + uuid + ' in the videoCache') const uuids = parseUUIDs(settings['chat-videos-list'])
return if (!uuids.length && !liveOn && !nonLiveOn) {
} logger.log('not activated.')
if (uuids.indexOf(uuid) >= 0) { return
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)
} }
})
})
}
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({ registerHook({
target: 'filter:api.video-watch.video.get.result', target: 'filter:api.video-watch.video.get.result',
handler: (video) => { handler: (video) => {
@ -208,7 +224,7 @@ function register ({ registerHook, peertubeHelpers }) {
// FIXME: this should be made in action:video-watch.video.loaded. // FIXME: this should be made in action:video-watch.video.loaded.
// But with Peertube 3.0.1, this hook is not called for lives // But with Peertube 3.0.1, this hook is not called for lives
// in WAITING_FOR_LIVE and LIVE_ENDED states. // in WAITING_FOR_LIVE and LIVE_ENDED states.
initChat(peertubeHelpers) initChat()
return video return video
} }
}) })
@ -216,7 +232,7 @@ function register ({ registerHook, peertubeHelpers }) {
// registerHook({ // registerHook({
// target: 'action:video-watch.video.loaded', // target: 'action:video-watch.video.loaded',
// handler: () => { // handler: () => {
// initChat(peertubeHelpers) // initChat()
// } // }
// }) // })
} }

60
main.js
View File

@ -5,7 +5,9 @@ async function register ({
_storageManager, _storageManager,
_videoCategoryManager, _videoCategoryManager,
_videoLicenceManager, _videoLicenceManager,
_videoLanguageManager _videoLanguageManager,
getRouter,
peertubeHelpers
}) { }) {
registerSetting({ registerSetting({
name: 'chat-auto-display', name: 'chat-auto-display',
@ -41,16 +43,39 @@ async function register ({
'Don\'t add private videos, the UUIDs will be send to frontend.', 'Don\'t add private videos, the UUIDs will be send to frontend.',
private: false 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.<br>' +
'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:<br>' +
'URI of the external BOSH server. Please make sure it accept cross origin request from your domain.',
private: true
})
registerSetting({ registerSetting({
name: 'chat-uri', name: 'chat-uri',
label: 'Webchat url', label: 'Webchat url',
type: 'input', type: 'input',
default: '', default: '',
descriptionHTML: 'The webchat url. An iframe will be created pointing to this url. ' + descriptionHTML: '<b>If you dont want to use the builtin ConverseJS webchat:</b><br>' +
'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. ' + '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}}.<br>' +
'If this field is empty, it will use the builtin ConverseJS webchat.',
private: false private: false
}) })
registerSetting({ registerSetting({
name: 'chat-style', name: 'chat-style',
label: 'Webchat iframe style attribute', label: 'Webchat iframe style attribute',
@ -60,6 +85,35 @@ async function register ({
'Example: height:400px;', 'Example: height:400px;',
private: false 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 () { async function unregister () {