This commit is contained in:
matty 2024-06-20 17:45:31 -04:00
commit 4148444e91
12 changed files with 187 additions and 39 deletions

View File

@ -1,5 +1,16 @@
# Changelog
## 10.1.2
* Fix: clicking on the import custom emojis button, without selected any file, was resulting in a state with all action button disabled.
## 10.1.1
* Fix #436: Saving emojis per batch, to avoid hitting max payload limit.
* Fix: the emojis import function could add more entries than max allowed emoji count.
* Fix #437: removing last line if empty when importing emojis.
* Updated translations: de, sk.
## 10.1.0
### New features

View File

@ -83,6 +83,7 @@ declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_NICKNAME: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FOR_MORE_INFO: string
declare const LOC_VALIDATION_ERROR: string
declare const LOC_TOO_MANY_ENTRIES: string
declare const LOC_INVALID_VALUE: string
declare const LOC_INVALID_VALUE_MISSING: string
declare const LOC_INVALID_VALUE_WRONG_TYPE: string

View File

@ -102,9 +102,13 @@ export class ChannelEmojisElement extends LivechatElement {
try {
this.actionDisabled = true
await this._channelDetailsService.saveEmojisConfiguration(this.channelId, this.channelEmojisConfiguration.emojis)
this.channelEmojisConfiguration = await this._channelDetailsService.saveEmojisConfiguration(
this.channelId,
this.channelEmojisConfiguration.emojis
)
this.validationError = undefined
this.ptNotifier.info(await this.ptTranslate(LOC_SUCCESSFULLY_SAVED))
this.requestUpdate('channelEmojisConfiguration')
this.requestUpdate('_validationError')
} catch (error) {
this.validationError = undefined
@ -128,7 +132,6 @@ export class ChannelEmojisElement extends LivechatElement {
*/
public async importEmojis (ev: Event): Promise<void> {
ev.preventDefault()
this.actionDisabled = true
try {
// download a json file:
const file = await new Promise<File>((resolve, reject) => {
@ -149,6 +152,8 @@ export class ChannelEmojisElement extends LivechatElement {
input.remove()
})
this.actionDisabled = true
const content = await new Promise<string>((resolve, reject) => {
const fileReader = new FileReader()
fileReader.onerror = reject
@ -170,6 +175,15 @@ export class ChannelEmojisElement extends LivechatElement {
if (!Array.isArray(json)) {
throw new Error('Invalid data, an array was expected')
}
// Before adding new entries, we check if the last current line is empty,
// and remove it in such case.
// See https://github.com/JohnXLivingston/peertube-plugin-livechat/issues/437
const last = this.channelEmojisConfiguration?.emojis.customEmojis.slice(-1)[0]
if (last && last.sn === '' && last.url === '') {
this.channelEmojisConfiguration?.emojis.customEmojis.pop()
}
for (const entry of json) {
if (typeof entry !== 'object') {
throw new Error('Invalid data')

View File

@ -5,10 +5,12 @@
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
import type {
ChannelLiveChatInfos, ChannelConfiguration, ChannelConfigurationOptions, ChannelEmojisConfiguration, ChannelEmojis
ChannelLiveChatInfos, ChannelConfiguration, ChannelConfigurationOptions, ChannelEmojisConfiguration, ChannelEmojis,
CustomEmojiDefinition
} from 'shared/lib/types'
import { ValidationError, ValidationErrorType } from '../../lib/models/validation'
import { getBaseRoute } from '../../../utils/uri'
import { maxEmojisPerChannel } from 'shared/lib/emojis'
export class ChannelDetailsService {
public _registerClientOptions: RegisterClientOptions
@ -179,6 +181,16 @@ export class ChannelDetailsService {
public async validateEmojisConfiguration (channelEmojis: ChannelEmojis): Promise<boolean> {
const propertiesError: ValidationError['properties'] = {}
if (channelEmojis.customEmojis.length > maxEmojisPerChannel) {
// This can happen when using the import function.
const validationError = new ValidationError(
'ChannelEmojisValidationError',
await this._registerClientOptions.peertubeHelpers.translate(LOC_TOO_MANY_ENTRIES),
propertiesError
)
throw validationError
}
const seen = new Map<string, true>()
for (const [i, e] of channelEmojis.customEmojis.entries()) {
propertiesError[`emojis.${i}.sn`] = []
@ -213,11 +225,57 @@ export class ChannelDetailsService {
public async saveEmojisConfiguration (
channelId: number,
channelEmojis: ChannelEmojis
): Promise<void> {
): Promise<ChannelEmojisConfiguration> {
if (!await this.validateEmojisConfiguration(channelEmojis)) {
throw new Error('Invalid form data')
}
// Note: API request body size is limited to 100Kb (expressjs body-parser defaut limit, and Peertube nginx config).
// So we must send new emojis 1 by 1, to be sure to not reach the limit.
if (!channelEmojis.customEmojis.find(e => e.url.startsWith('data:'))) {
// No new emojis, just saving.
return this._saveEmojisConfiguration(channelId, channelEmojis)
}
let lastResult: ChannelEmojisConfiguration | undefined
let customEmojis: CustomEmojiDefinition[] = [...channelEmojis.customEmojis] // copy the original array
let i = customEmojis.findIndex(e => e.url.startsWith('data:'))
let watchDog = 0
while (i >= 0) {
watchDog++
if (watchDog > channelEmojis.customEmojis.length + 10) { // just to avoid infinite loop
throw new Error('Seems we have sent too many emojis, this was not expected')
}
const data: CustomEmojiDefinition[] = customEmojis.slice(0, i + 1) // all elements until first new file
data.push(
// all remaining elements that where already uploaded (to not loose them):
...customEmojis.slice(i + 1).filter((e) => !e.url.startsWith('data:'))
)
lastResult = await this._saveEmojisConfiguration(channelId, {
customEmojis: data
})
// Must inject the result in customEmojis
const temp = lastResult.emojis.customEmojis.slice(0, i + 1) // last element should have been replace by a http url
temp.push(
...customEmojis.slice(i + 1) // remaining elements in the previous array
)
customEmojis = temp
// and searching again next new emojis
i = customEmojis.findIndex(e => e.url.startsWith('data:'))
}
if (!lastResult) {
// This should not happen...
throw new Error('Unexpected: no last result')
}
return lastResult
}
private async _saveEmojisConfiguration (
channelId: number,
channelEmojis: ChannelEmojis
): Promise<ChannelEmojisConfiguration> {
const response = await fetch(
getBaseRoute(this._registerClientOptions) +
'/api/configuration/channel/emojis/' +
@ -230,10 +288,9 @@ export class ChannelDetailsService {
)
if (!response.ok) {
if (response.status === 404) {
// File does not exist yet, that is a normal use case.
}
throw new Error('Can\'t get channel emojis options.')
}
return response.json()
}
}

View File

@ -511,3 +511,4 @@ token_action_revoke_confirm: Sind Sie sicher, dass Sie diesen Token widerrufen w
auth_description: "<h3>Authentifizierung</h3>\n"
livechat_token_disabled_label: Livechat-Token deaktivieren
share_chat_dock: Dock
token_date: Datum

View File

@ -455,6 +455,7 @@ invalid_value_wrong_format: "Value is in the wrong format."
invalid_value_not_in_range: "Value is not in authorized range."
invalid_value_file_too_big: "File size is too big (max size: %s)."
invalid_value_duplicate: "Duplicate value"
too_many_entries: "Too many entries"
slow_mode_info: "Slow mode is enabled, users can send a message every %1$s seconds."

View File

@ -522,3 +522,4 @@ livechat_token_disabled_description: "Les utilisateur⋅rices peuvent générer
pour inclure le tchat dans les docks web dans OBS.\nConsultez <a href=\"https://livingston.frama.io/peertube-plugin-livechat/fr/documentation/user/obs\"\
\ target=\"_blank\">la documentation</a> pour plus d'informations.\nVous pouvez
désactiver cette fonctionnalité en cochant ce paramètre.\n"
too_many_entries: "Trop d'entrées"

8
languages/sk.yml Normal file
View File

@ -0,0 +1,8 @@
online_help: "Online pomoc"
open_chat: "Otvoriť chat"
open_chat_new_window: "Otvoriť chat na novom okne"
close_chat: "Zavrieť čet"
use_chat: "Použite chat"
use_chat_help: "Ak je povolená, vedľa videa sa zobrazí chat."
share_chat_link: "Zdieľať odkaz na čet"
read_only: "Iba na čítanie"

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "peertube-plugin-livechat",
"version": "10.1.0",
"version": "10.1.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "peertube-plugin-livechat",
"version": "10.1.0",
"version": "10.1.2",
"license": "AGPL-3.0",
"dependencies": {
"@xmpp/jid": "^0.13.1",

View File

@ -168,7 +168,16 @@ async function initConfigurationApiRouter (options: RegisterServerOptions, route
await emojis.saveChannelDefinition(channelInfos.id, emojisDefinitionSanitized, bufferInfos)
res.sendStatus(200)
// Reloading data, to send them back to front:
const channelEmojis =
(await emojis.channelCustomEmojisDefinition(channelInfos.id)) ??
emojis.emptyChannelDefinition()
const result: ChannelEmojisConfiguration = {
channel: channelInfos,
emojis: channelEmojis
}
res.status(200)
res.json(result)
} catch (err) {
logger.error(err)
res.sendStatus(500)

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// Note: API request body size is limited to 100Kb (expressjs body-parser defaut limit, and Peertube nginx config).
// So we must be sure to never send more than 100Kb. The front end sends new emojis by batch, but maxSize must remain
// as little as possible, so that we never reach 100Kb in JSON/base64 format.
export const maxSize: number = 30 * 1024
export const allowedExtensions = ['png', 'jpg', 'jpeg', 'gif']
export const inputFileAccept = ['image/jpg', 'image/png', 'image/gif']

View File

@ -8,15 +8,17 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-06-19 12:16+0200\n"
"PO-Revision-Date: 2024-06-18 20:10+0000\n"
"Last-Translator: Victor Hampel <v.hampel@users.noreply.weblate.framasoft.org>\n"
"Language-Team: German <https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat-documentation/de/>\n"
"PO-Revision-Date: 2024-06-20 11:44+0000\n"
"Last-Translator: Victor Hampel <v.hampel@users.noreply.weblate.framasoft.org>"
"\n"
"Language-Team: German <https://weblate.framasoft.org/projects/"
"peertube-livechat/peertube-plugin-livechat-documentation/de/>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Weblate 5.5.5\n"
"X-Generator: Weblate 5.6\n"
#. type: Yaml Front Matter Hash Value: description
#: support/documentation/content/en/contact/_index.md
@ -1165,6 +1167,17 @@ msgstr "Jetzt können Sie dieses Konto zu Gateways hinzufügen und bestimmte Liv
#: support/documentation/content/en/documentation/admin/advanced/matterbridge.md
msgid "This documentation use an anonymous account to connect the bridge to the chat. But since the livechat v10.1.0, there is a new way to generate long term authentication token, that allows to connect using your account. This is used for [OBS docks](/peertube-plugin-livechat/documentation/user/obs). Using this feature for other purposes is not documented and not officially supported yet. If you want to use it anyway, you can request a token by calling then `/plugins/livechat/router/api/auth/tokens` endpoint. To get needed headers and request body, just check what happens when you generate a new token for OBS docks."
msgstr ""
"In dieser Dokumentation wird ein anonymes Konto verwendet, um die Brücke mit "
"dem Chat zu verbinden. Aber seit dem Livechat v10.1.0 gibt es eine neue "
"Möglichkeit, ein langfristiges Authentifizierungs-Token zu generieren, das "
"es erlaubt, sich mit dem eigenen Konto zu verbinden. Dies wird für [OBS "
"docks](/peertube-plugin-livechat/de/documentation/user/obs) verwendet. Die "
"Verwendung dieser Funktion für andere Zwecke ist nicht dokumentiert und wird "
"noch nicht offiziell unterstützt. Wenn Sie es trotzdem benutzen wollen, "
"können Sie ein Token anfordern, indem Sie den Endpunkt `/plugins/livechat/"
"router/api/auth/tokens` aufrufen. Um die benötigten Header und den Request "
"Body zu erhalten, prüfe einfach, was passiert, wenn du ein neues Token für "
"OBS Docks generierst."
#. type: Yaml Front Matter Hash Value: description
#: support/documentation/content/en/documentation/admin/advanced/xmpp_clients.md
@ -2508,10 +2521,8 @@ msgstr "OBS Overlay"
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
#, fuzzy
#| msgid "You can easily include the chat in your stream."
msgid "You can easily include the chat in your video stream."
msgstr "Sie können den Chat ganz einfach in Ihren Stream integrieren."
msgstr "Sie können den Chat ganz einfach in Ihren Videostream integrieren."
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
@ -2574,7 +2585,7 @@ msgstr "Hinweis: Sie können vollständig die Chat-Farben anpassen. Dies ist noc
#: support/documentation/content/en/documentation/user/obs.md
#, no-wrap
msgid "OBS Dock"
msgstr ""
msgstr "OBS Dock"
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
@ -2592,70 +2603,95 @@ msgstr "Diese Funktion kann von den Administratoren der Instanz deaktiviert werd
#: support/documentation/content/en/documentation/user/obs.md
msgid "You can use OBS \"Custom browser docks\" to integrate the chat in your OBS while you are streaming. The livechat plugin offers a way to create long term token that can identify you automatically to join the chat, so you don't have to enter your password in OBS."
msgstr ""
"Sie können OBS \"Benutzerdefinierte Browser-Docks\" verwenden, um den Chat "
"in OBS zu integrieren, während Sie streamen. Das Livechat-Plugin bietet "
"eine Möglichkeit, ein langfristiges Token zu erstellen, das Sie automatisch "
"identifiziert, um dem Chat beizutreten, so dass Sie Ihr Passwort nicht in "
"OBS eingeben müssen."
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
msgid "To do so, just use the \"{{% livechat_label share_chat_link %}}\", and open the \"{{% livechat_label share_chat_dock %}}\" tab. From there, you can create a new token using the \"+\" button."
msgstr ""
"Verwenden Sie dazu einfach \"{{% livechat_label share_chat_link %}}\", und "
"öffnen Sie die Registerkarte \"{{% livechat_label share_chat_dock %}}\". "
"Von dort aus können Sie mit der Schaltfläche \"+\" ein neuen Token erstellen."
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
#: support/documentation/content/en/documentation/user/streamers/basics.md
#, fuzzy
#| msgid "![Share link popup](/peertube-plugin-livechat/images/share_readonly.png?classes=shadow,border&height=200px)"
msgid "![Share link popup - dock tab](/peertube-plugin-livechat/images/share_dock.png?classes=shadow,border&height=200px)"
msgstr "![Link Teilen Popup](/peertube-plugin-livechat/images/share_readonly.png?classes=shadow,border&height=200px)"
msgstr ""
"![Link teilen Fenster - Dock Reiter](/peertube-plugin-livechat/images/"
"share_dock.png?classes=shadow,border&height=200px)"
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
msgid "Then, copy the url, and use the \"Docks / Custom browser docks\" menu from your OBS to add a dock with this URL."
msgstr ""
"Kopieren Sie dann die URL und verwenden Sie das Menü \"Docks / "
"Benutzerdefinierte Browser-Docks\" in Ihrem OBS, um ein Dock mit dieser URL "
"hinzuzufügen."
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
#, fuzzy
#| msgid "![Chat menu](/peertube-plugin-livechat/images/top_menu.png?classes=shadow,border&height=200px)"
msgid "![OBS - Dock menu](/peertube-plugin-livechat/images/obs_dock_menu.png?classes=shadow,border&height=200px)"
msgstr "![Chat Menü](/peertube-plugin-livechat/images/top_menu.png?classes=shadow,border&height=200px)"
msgstr ""
"![OBS - Dock Menü](/peertube-plugin-livechat/images/obs_dock_menu."
"png?classes=shadow,border&height=200px)"
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
#, fuzzy
#| msgid "![External login dialog](/peertube-plugin-livechat/images/external_login_dialog.png?classes=shadow,border&height=200px)"
msgid "![OBS - Dock dialog](/peertube-plugin-livechat/images/obs_dock_dialog.png?classes=shadow,border&height=200px)"
msgstr "![Externer Anmeldedialog](/peertube-plugin-livechat/images/external_login_dialog.png?classes=shadow,border&height=200px)"
msgstr ""
"![OBS - Dock-Dialog](/peertube-plugin-livechat/images/obs_dock_dialog."
"png?classes=shadow,border&height=200px)"
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
msgid "Once you have done, you will have a new dock connected to the chat with your account."
msgstr ""
"Danach haben Sie ein neues Dock, das mit dem Chat und Ihrem Konto verbunden "
"ist."
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
#, fuzzy
#| msgid "![Share button](/peertube-plugin-livechat/images/share_button.png?classes=shadow,border&height=200px)"
msgid "![OBS - Dock](/peertube-plugin-livechat/images/obs_dock.png?classes=shadow,border&height=200px)"
msgstr "![Teilen Schaltfläche](/peertube-plugin-livechat/images/share_button.png?classes=shadow,border&height=200px)"
msgstr ""
"![OBS - Dock](/peertube-plugin-livechat/images/obs_dock."
"png?classes=shadow,border&height=200px)"
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
msgid "Tokens are valid to join any chat room. You don't have to generate separate tokens for each of your rooms. You can also customize the nickame that will be used by changing the `n` parameter in the url."
msgstr ""
"Die Token sind für die Teilnahme an jedem Chatraum gültig. Sie müssen nicht "
"für jeden Ihrer Räume ein eigenes Token erstellen. Sie können auch den "
"Spitznamen, der verwendet wird, anpassen, indem Sie den Parameter `n` in der "
"URL ändern."
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
msgid "Don't share these links to anyone, as it would allow them to connect as yourself."
msgstr ""
"Geben Sie diese Links nicht weiter, da sie es anderen Personen ermöglichen "
"würden, eine Verbindung mit Ihrem Konto herzustellen."
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
msgid "If a token is compromised, or no more needed, you can revoke them."
msgstr ""
"Wenn ein Token kompromittiert ist oder nicht mehr benötigt wird, können Sie "
"ihn widerrufen."
#. type: Plain text
#: support/documentation/content/en/documentation/user/obs.md
msgid "These tokens can be used for other purposes, as connecting to your account with XMPP bots or clients. This feature is not documented yet, and not officially supported. So use with care."
msgstr ""
"Diese Token können für andere Zwecke verwendet werden, z. B. für die "
"Verbindung mit XMPP-Bots oder -Clients zu Ihrem Konto. Diese Funktion ist "
"noch nicht dokumentiert und wird offiziell nicht unterstützt. Verwenden Sie "
"sie also mit Vorsicht."
#. type: Title ##
#: support/documentation/content/en/documentation/user/obs.md
@ -2747,6 +2783,8 @@ msgstr "Diese Schaltfläche öffnet ein Popup-Fenster, in dem Sie eine URL erhal
#: support/documentation/content/en/documentation/user/streamers/basics.md
msgid "The \"{{% livechat_label share_chat_embed %}}\" tab provide some links to embed the chat in websites, or in your live stream."
msgstr ""
"Auf der Registerkarte \"{{% livechat_label share_chat_embed %}}\" finden Sie "
"einige Links zum Einbetten des Chats in Websites oder in Ihre Livestream."
#. type: Plain text
#: support/documentation/content/en/documentation/user/streamers/basics.md
@ -2770,22 +2808,26 @@ msgstr "{{% livechat_label generate_iframe %}}: Anstelle einer URL erhalten Sie
#. type: Plain text
#: support/documentation/content/en/documentation/user/streamers/basics.md
#, fuzzy
#| msgid "Please refer to the [OBS documentation](/peertube-plugin-livechat/documentation/user/obs)."
msgid "For more information on the \"{{% livechat_label share_chat_dock %}}\" tab, check the [OBS documentation](/peertube-plugin-livechat/documentation/user/obs)."
msgstr "Bitte lesen Sie die [OBS-Dokumentation](/peertube-plugin-livechat/de/documentation/user/obs)."
msgstr ""
"Weitere Informationen über die Registerkarte \"{{% livechat_label "
"share_chat_dock %}}\" finden Sie in der [OBS-Dokumentation](/peertube-"
"plugin-livechat/de/documentation/user/obs)."
#. type: Plain text
#: support/documentation/content/en/documentation/user/streamers/basics.md
msgid "In the \"{{% livechat_label web %}}\" tab, the provided url opens the chat in the Peertube interface. You can share this link to other users to invite them to join the chat."
msgstr ""
"Auf der Registerkarte \"{{% livechat_label web %}}\" öffnet die angegebene "
"URL den Chat in der Peertube-Oberfläche. Sie können diesen Link an andere "
"Benutzer weitergeben, um sie zum Chat einzuladen."
#. type: Plain text
#: support/documentation/content/en/documentation/user/streamers/basics.md
#, fuzzy
#| msgid "![Share link popup](/peertube-plugin-livechat/images/share_readonly.png?classes=shadow,border&height=200px)"
msgid "![Share link popup - web tab](/peertube-plugin-livechat/images/share_web.png?classes=shadow,border&height=200px)"
msgstr "![Link Teilen Popup](/peertube-plugin-livechat/images/share_readonly.png?classes=shadow,border&height=200px)"
msgstr ""
"![Link teilen Fenster - Web Reiter](/peertube-plugin-livechat/images/"
"share_web.png?classes=shadow,border&height=200px)"
#. type: Plain text
#: support/documentation/content/en/documentation/user/streamers/basics.md
@ -2794,10 +2836,10 @@ msgstr "Das \"{{% livechat_label share_chat_link %}}\" Popup-Fenster kann auch e
#. type: Plain text
#: support/documentation/content/en/documentation/user/streamers/basics.md
#, fuzzy
#| msgid "![Share XMPP](/peertube-plugin-livechat/images/share_xmpp_dialog.png?classes=shadow,border&height=200px)"
msgid "![Share link popup - xmpp tab](/peertube-plugin-livechat/images/share_xmpp_dialog.png?classes=shadow,border&height=200px)"
msgstr "![XMPP Link teilen](/peertube-plugin-livechat/images/share_xmpp_dialog.png?classes=shadow,border&height=200px)"
msgstr ""
"![Link teilen Fenster - xmpp Reiter](/peertube-plugin-livechat/images/"
"share_xmpp_dialog.png?classes=shadow,border&height=200px)"
#. type: Title ##
#: support/documentation/content/en/documentation/user/streamers/basics.md