Work in progress: builtin converseJS.
This commit is contained in:
parent
7f5d955cb0
commit
fbfb38392d
@ -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
60
main.js
@ -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 () {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user