Chat can be enabled in video properties.

This commit is contained in:
John Livingston 2021-06-08 18:08:58 +02:00
parent 07b7f4d732
commit 385a0074c1
11 changed files with 154 additions and 16 deletions

View File

@ -9,11 +9,14 @@
### Features ### Features
* New simpler settings screen. * New simpler settings screen.
* New field in live video form, to activate the webchat per video. There is a setting for enabling this new feature.
### Changes ### Changes
* Removed compatibility with Peertube previous to 3.2.0. * Removed compatibility with Peertube previous to 3.2.0.
* Restoring default values for checkboxes in settings (there was a bug with Peertube previous to 3.2.0) * Restoring default values for checkboxes in settings (there was a bug with Peertube previous to 3.2.0)
* New settings
* By default, the «activate chat for all lives» is disabled (now that we can enable the webchat per video)
### Fixes ### Fixes

View File

@ -37,7 +37,7 @@ interface RegisterClientFormFieldOptions {
type: 'input' | 'input-checkbox' | 'input-password' | 'input-textarea' | 'markdown-text' | 'markdown-enhanced' type: 'input' | 'input-checkbox' | 'input-password' | 'input-textarea' | 'markdown-text' | 'markdown-enhanced'
descriptionHTML?: string descriptionHTML?: string
default?: string | boolean default?: string | boolean
private: boolean hidden?: (options: any) => boolean
} }
interface RegisterClientSettingsScript { interface RegisterClientSettingsScript {
isSettingHidden: (options: { isSettingHidden: (options: {
@ -46,10 +46,18 @@ interface RegisterClientSettingsScript {
}) => boolean }) => boolean
} }
interface RegisterClientVideoFieldOptions {
type: 'update' | 'upload' | 'import-url' | 'import-torrent' | 'go-live'
}
interface RegisterOptions { interface RegisterOptions {
registerHook: (options: RegisterClientHookOptions) => void registerHook: (options: RegisterClientHookOptions) => void
peertubeHelpers: RegisterClientHelpers peertubeHelpers: RegisterClientHelpers
registerSettingsScript: (options: RegisterClientSettingsScript) => void registerSettingsScript: (options: RegisterClientSettingsScript) => void
registerVideoField: (
commonOptions: RegisterClientFormFieldOptions,
videoFormOptions: RegisterClientVideoFieldOptions
) => void
} }
interface Video { interface Video {

View File

@ -23,6 +23,8 @@ function register ({ registerSettingsScript }: RegisterOptions): void {
return options.formValues['chat-type'] === 'disabled' return options.formValues['chat-type'] === 'disabled'
case 'chat-only-locals-warning': case 'chat-only-locals-warning':
return options.formValues['chat-only-locals'] === true return options.formValues['chat-only-locals'] === true
case 'chat-per-live-video-warning':
return !(options.formValues['chat-all-lives'] === true && options.formValues['chat-per-live-video'] === true)
} }
return false return false

View File

@ -1,5 +1,5 @@
function register ({ registerHook }: RegisterOptions): void { async function register ({ peertubeHelpers, registerHook, registerVideoField }: RegisterOptions): Promise<void> {
registerHook({ registerHook({
target: 'action:router.navigation-end', target: 'action:router.navigation-end',
handler: () => { handler: () => {
@ -9,6 +9,34 @@ function register ({ registerHook }: RegisterOptions): void {
} }
} }
}) })
const [label, description, settings] = await Promise.all([
peertubeHelpers.translate('Use chat'),
peertubeHelpers.translate('If enabled, there will be a chat next to the video.'),
peertubeHelpers.getSettings()
])
const webchatFieldOptions: RegisterClientFormFieldOptions = {
name: 'livechat-active',
label: label,
descriptionHTML: description,
type: 'input-checkbox',
default: true,
hidden: ({ liveVideo }) => {
if (!liveVideo) {
return true
}
if (!settings['chat-per-live-video']) {
return true
}
if (settings['chat-all-lives']) {
// No need to add this field if live is active for all live videos
return true
}
return false
}
}
registerVideoField(webchatFieldOptions, { type: 'update' })
registerVideoField(webchatFieldOptions, { type: 'go-live' })
} }
export { export {

View File

@ -61,6 +61,7 @@ enum VideoState {
} }
interface MVideoThumbnail { // FIXME: this interface is not complete. interface MVideoThumbnail { // FIXME: this interface is not complete.
id?: number
uuid: string uuid: string
name: string name: string
category: number category: number
@ -81,6 +82,7 @@ interface MVideoThumbnail { // FIXME: this interface is not complete.
downloadEnabled: boolean downloadEnabled: boolean
state: VideoState state: VideoState
channelId: number channelId: number
pluginData?: any
} }
// Keep the order // Keep the order

View File

@ -0,0 +1,63 @@
async function initCustomFields (options: RegisterServerOptions): Promise<void> {
const registerHook = options.registerHook
const storageManager = options.storageManager
const logger = options.peertubeHelpers.logger
registerHook({
target: 'action:api.video.updated',
handler: async (params: any) => {
logger.debug('Saving a video, checking for custom fields')
const body: any = params.body
const video: MVideoThumbnail | undefined = params.video
if (!video || !video.id) {
return
}
if (!body.pluginData) return
const value = body.pluginData['livechat-active']
// NB: on Peertube 3.2.1, value is "true", "false", or "null", as strings.
if (value === true || value === 'true') {
logger.info(`Saving livechat-active=true for video ${video.id}`)
await storageManager.storeData(`livechat-active-${video.id}`, true)
} else if (value === false || value === 'false' || value === 'null') {
logger.info(`Saving livechat-active=false for video ${video.id}`)
await storageManager.storeData(`livechat-active-${video.id}`, false)
} else {
logger.error('Unknown value ' + JSON.stringify(value) + ' for livechat-active field.')
}
}
})
registerHook({
target: 'filter:api.video.get.result',
handler: async (video: MVideoThumbnail): Promise<MVideoThumbnail> => {
logger.debug('Getting a video, searching for custom fields')
await fillVideoCustomFields(options, video)
return video
}
})
}
async function fillVideoCustomFields (options: RegisterServerOptions, video: MVideoThumbnail): Promise<void> {
if (!video) return video
if (!video.pluginData) video.pluginData = {}
if (!video.id) return
const storageManager = options.storageManager
const logger = options.peertubeHelpers.logger
if (video.isLive) {
const result: any = await storageManager.getData(`livechat-active-${video.id}`)
logger.debug(`Video ${video.id} has livechat-active=` + JSON.stringify(result))
// NB: I got weird stuff here. Maybe Peertube 3.2.1 bug?
if (result === true || result === 'true') {
video.pluginData['livechat-active'] = true
} else if (result === false || result === 'false' || result === 'null') {
video.pluginData['livechat-active'] = false
}
}
}
export {
initCustomFields,
fillVideoCustomFields
}

View File

@ -7,6 +7,7 @@ export async function diagVideo (test: string, { settingsManager }: RegisterServ
'chat-auto-display', 'chat-auto-display',
'chat-open-blank', 'chat-open-blank',
'chat-only-locals', 'chat-only-locals',
'chat-per-live-video',
'chat-all-lives', 'chat-all-lives',
'chat-all-non-lives', 'chat-all-non-lives',
'chat-videos-list' 'chat-videos-list'
@ -26,6 +27,10 @@ export async function diagVideo (test: string, { settingsManager }: RegisterServ
} }
let atLeastOne: boolean = false let atLeastOne: boolean = false
if (videoSettings['chat-per-live-video']) {
result.messages.push('Chat can be enabled on live videos.')
atLeastOne = true
}
if (videoSettings['chat-all-lives']) { if (videoSettings['chat-all-lives']) {
result.messages.push('Chat is enabled for all lives.') result.messages.push('Chat is enabled for all lives.')
atLeastOne = true atLeastOne = true

View File

@ -7,6 +7,7 @@ import { getUserNickname } from '../helpers'
import { Affiliations, getVideoAffiliations } from '../prosody/config/affiliations' import { Affiliations, getVideoAffiliations } from '../prosody/config/affiliations'
import { getProsodyDomain } from '../prosody/config/domain' import { getProsodyDomain } from '../prosody/config/domain'
import type { ChatType } from '../../../shared/lib/types' import type { ChatType } from '../../../shared/lib/types'
import { fillVideoCustomFields } from '../custom-fields'
// See here for description: https://modules.prosody.im/mod_muc_http_defaults.html // See here for description: https://modules.prosody.im/mod_muc_http_defaults.html
interface RoomDefaults { interface RoomDefaults {
@ -45,10 +46,15 @@ async function initApiRouter (options: RegisterServerOptions): Promise<Router> {
res.sendStatus(403) res.sendStatus(403)
return return
} }
// Adding the custom fields:
await fillVideoCustomFields(options, video)
// check settings (chat enabled for this video?) // check settings (chat enabled for this video?)
const settings = await options.settingsManager.getSettings([ const settings = await options.settingsManager.getSettings([
'chat-type', 'chat-type',
'chat-only-locals', 'chat-only-locals',
'chat-per-live-video',
'chat-all-lives', 'chat-all-lives',
'chat-all-non-lives', 'chat-all-non-lives',
'chat-videos-list' 'chat-videos-list'
@ -59,9 +65,10 @@ async function initApiRouter (options: RegisterServerOptions): Promise<Router> {
return return
} }
if (!videoHasWebchat({ if (!videoHasWebchat({
'chat-only-locals': settings['chat-only-locals'] as boolean, 'chat-only-locals': !!settings['chat-only-locals'],
'chat-all-lives': settings['chat-all-lives'] as boolean, 'chat-per-live-video': !!settings['chat-per-live-video'],
'chat-all-non-lives': settings['chat-all-non-lives'] as boolean, 'chat-all-lives': !!settings['chat-all-lives'],
'chat-all-non-lives': !!settings['chat-all-non-lives'],
'chat-videos-list': settings['chat-videos-list'] as string 'chat-videos-list': settings['chat-videos-list'] as string
}, video)) { }, video)) {
logger.warn(`Video ${jid} has not chat activated`) logger.warn(`Video ${jid} has not chat activated`)
@ -122,11 +129,7 @@ async function initApiRouter (options: RegisterServerOptions): Promise<Router> {
router.get('/user/check_password', asyncMiddleware( router.get('/user/check_password', asyncMiddleware(
async (req: Request, res: Response, _next: NextFunction) => { async (req: Request, res: Response, _next: NextFunction) => {
const settings = await options.settingsManager.getSettings([ const settings = await options.settingsManager.getSettings([
'chat-type', 'chat-type'
'chat-only-locals',
'chat-all-lives',
'chat-all-non-lives',
'chat-videos-list'
]) ])
if (settings['chat-type'] !== ('builtin-prosody' as ChatType)) { if (settings['chat-type'] !== ('builtin-prosody' as ChatType)) {
logger.warn('Prosody chat is not active') logger.warn('Prosody chat is not active')
@ -153,11 +156,7 @@ async function initApiRouter (options: RegisterServerOptions): Promise<Router> {
router.get('/user/user_exists', asyncMiddleware( router.get('/user/user_exists', asyncMiddleware(
async (req: Request, res: Response, _next: NextFunction) => { async (req: Request, res: Response, _next: NextFunction) => {
const settings = await options.settingsManager.getSettings([ const settings = await options.settingsManager.getSettings([
'chat-type', 'chat-type'
'chat-only-locals',
'chat-all-lives',
'chat-all-non-lives',
'chat-videos-list'
]) ])
if (settings['chat-type'] !== ('builtin-prosody' as ChatType)) { if (settings['chat-type'] !== ('builtin-prosody' as ChatType)) {
logger.warn('Prosody chat is not active') logger.warn('Prosody chat is not active')

View File

@ -195,13 +195,31 @@ function initSettings (options: RegisterServerOptions): void {
<span class="peertube-plugin-livechat-warning"> <span class="peertube-plugin-livechat-warning">
The plugin is not compatible with video federation yet. The plugin is not compatible with video federation yet.
The webchat will only be accessible for people watching videos on your server. The webchat will only be accessible for people watching videos on your server.
</span>`
})
registerSetting({
name: 'chat-per-live-video',
label: 'Users can activate the chat for their lives',
type: 'input-checkbox',
default: true,
descriptionHTML: 'If checked, all live videos will have a checkbox in there properties for enabling the webchat.',
private: false
})
registerSetting({
name: 'chat-per-live-video-warning',
type: 'html',
private: true,
descriptionHTML: `
<span class="peertube-plugin-livechat-warning">
You have enabled the setting «Users can activate the chat for their lives».
It is redundant with the «Activate chat for all lives» setting.
</span>` </span>`
}) })
registerSetting({ registerSetting({
name: 'chat-all-lives', name: 'chat-all-lives',
label: 'Activate chat for all lives', label: 'Activate chat for all lives',
type: 'input-checkbox', type: 'input-checkbox',
default: true, default: false,
descriptionHTML: 'If checked, the chat will be enabled for all lives.', descriptionHTML: 'If checked, the chat will be enabled for all lives.',
private: false private: false
}) })

View File

@ -1,5 +1,6 @@
import { migrateSettings } from './lib/migration/settings' import { migrateSettings } from './lib/migration/settings'
import { initSettings } from './lib/settings' import { initSettings } from './lib/settings'
import { initCustomFields } from './lib/custom-fields'
import { initRouters } from './lib/routers/index' import { initRouters } from './lib/routers/index'
import { ensureProsodyRunning, ensureProsodyNotRunning } from './lib/prosody/ctl' import { ensureProsodyRunning, ensureProsodyNotRunning } from './lib/prosody/ctl'
import decache from 'decache' import decache from 'decache'
@ -19,6 +20,7 @@ async function register (options: RegisterServerOptions): Promise<any> {
await migrateSettings(options) await migrateSettings(options)
await initSettings(options) await initSettings(options)
await initCustomFields(options)
await initRouters(options) await initRouters(options)
await ensureProsodyRunning(options) await ensureProsodyRunning(options)

View File

@ -2,6 +2,7 @@ import { parseConfigUUIDs } from './config'
interface SharedSettings { interface SharedSettings {
'chat-only-locals': boolean 'chat-only-locals': boolean
'chat-per-live-video': boolean
'chat-all-lives': boolean 'chat-all-lives': boolean
'chat-all-non-lives': boolean 'chat-all-non-lives': boolean
'chat-videos-list': string 'chat-videos-list': string
@ -10,6 +11,9 @@ interface SharedSettings {
interface SharedVideoBase { interface SharedVideoBase {
uuid: string uuid: string
isLive: boolean isLive: boolean
pluginData?: {
'livechat-active'?: boolean
}
} }
interface SharedVideoFrontend extends SharedVideoBase { interface SharedVideoFrontend extends SharedVideoBase {
@ -31,6 +35,10 @@ function videoHasWebchat (settings: SharedSettings, video: SharedVideo): boolean
} }
} }
if (settings['chat-per-live-video'] && video.isLive && video.pluginData && video.pluginData['livechat-active']) {
return true
}
if (settings['chat-all-lives']) { if (settings['chat-all-lives']) {
if (video.isLive) return true if (video.isLive) return true
} }