From d2064d873bc434ada862960fee3f99644681ea39 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jul 2025 09:32:11 +0200 Subject: [PATCH 1/8] Fix dock accessibility --- .../player/src/sass/shared/dock.scss | 23 +++++++++---------- .../shared/dock/peertube-dock-component.ts | 4 ++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/client/src/standalone/player/src/sass/shared/dock.scss b/client/src/standalone/player/src/sass/shared/dock.scss index a21b54678..6e1e08f29 100644 --- a/client/src/standalone/player/src/sass/shared/dock.scss +++ b/client/src/standalone/player/src/sass/shared/dock.scss @@ -1,10 +1,9 @@ -@use 'sass:math'; -@use '_variables' as *; -@use '_mixins' as *; -@use './_player-variables' as *; +@use "sass:math"; +@use "_variables" as *; +@use "_mixins" as *; +@use "./_player-variables" as *; .video-js.vjs-peertube-skin { - .peertube-dock { --dock-avatar-size: 48px; @@ -18,9 +17,8 @@ left: 0; width: 100%; - font-size: 23px; padding: $dock-padding; - background: linear-gradient(to bottom, rgba(20, 20, 20, .7) 0, rgba(20, 20, 20, 0)); + background: linear-gradient(to bottom, rgba(20, 20, 20, 0.7) 0, rgba(20, 20, 20, 0)); @include padding-right(60px); } @@ -44,11 +42,13 @@ letter-spacing: 1px; line-height: normal; min-width: 0; + margin: 0; + font-size: 23px; } .peertube-dock-title, .peertube-dock-description { - text-shadow: 0 1px 3px rgba(0, 0, 0, .5); + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5); @include ellipsis; } @@ -73,7 +73,7 @@ --dock-avatar-size: 40px; } - .peertube-dock { + .peertube-dock-title { font-size: 16px; } @@ -82,13 +82,12 @@ } } - .video-js.vjs-peertube-skin.vjs-size-570 { .peertube-dock-avatar { --dock-avatar-size: 35px; } - .peertube-dock { + .peertube-dock-title { font-size: 14px; } } @@ -98,7 +97,7 @@ --dock-avatar-size: 30px; } - .peertube-dock { + .peertube-dock-title { font-size: 13px; } } diff --git a/client/src/standalone/player/src/shared/dock/peertube-dock-component.ts b/client/src/standalone/player/src/shared/dock/peertube-dock-component.ts index 78e87e9dc..ad5a62bb1 100644 --- a/client/src/standalone/player/src/shared/dock/peertube-dock-component.ts +++ b/client/src/standalone/player/src/shared/dock/peertube-dock-component.ts @@ -23,7 +23,7 @@ class PeerTubeDockComponent extends Component { const avatar = videojs.dom.createEl('img', { className: 'peertube-dock-avatar', src: this.options_.avatarUrl - }) + }, { alt: '' }) el.appendChild(avatar) } @@ -33,7 +33,7 @@ class PeerTubeDockComponent extends Component { }) if (this.options_.title) { - const title = videojs.dom.createEl('div', { + const title = videojs.dom.createEl('h2', { className: 'peertube-dock-title', title: this.options_.title, innerText: this.options_.title From c958f6271f6c36dabcd2b7d5bc41c032483da781 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jul 2025 09:47:07 +0200 Subject: [PATCH 2/8] Fix P2P player info accessibility --- .../src/shared/control-bar/p2p-info-button.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts b/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts index ce4bbd15d..89db46818 100644 --- a/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts +++ b/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts @@ -4,7 +4,6 @@ import { bytes } from '../common' const Button = videojs.getComponent('Button') class P2PInfoButton extends Button { - createEl () { const div = videojs.dom.createEl('div', { className: 'vjs-peertube' }) const subDivP2P = videojs.dom.createEl('div', { @@ -12,7 +11,11 @@ class P2PInfoButton extends Button { }) as HTMLDivElement div.appendChild(subDivP2P) - const downloadIcon = videojs.dom.createEl('span', { className: 'icon icon-download' }) + const downloadIcon = videojs.dom.createEl('span', { + className: 'icon icon-download', + role: 'img', + ariaLabel: this.player().localize('Download speed:') + }) subDivP2P.appendChild(downloadIcon) const downloadSpeedText = videojs.dom.createEl('span', { className: 'download-speed-text' }) @@ -22,7 +25,11 @@ class P2PInfoButton extends Button { downloadSpeedText.appendChild(downloadSpeedUnit) subDivP2P.appendChild(downloadSpeedText) - const uploadIcon = videojs.dom.createEl('span', { className: 'icon icon-upload' }) + const uploadIcon = videojs.dom.createEl('span', { + role: 'img', + ariaLabel: this.player().localize('Upload speed:'), + className: 'icon icon-upload' + }) subDivP2P.appendChild(uploadIcon) const uploadSpeedText = videojs.dom.createEl('span', { className: 'upload-speed-text' }) @@ -64,8 +71,7 @@ class P2PInfoButton extends Button { const downloadedFromServer = bytes(httpStats.downloaded).join(' ') const downloadedFromPeers = bytes(p2pStats.downloaded).join(' ') - subDivP2P.title += - ' * ' + this.player().localize('From servers: ') + downloadedFromServer + '\n' + + subDivP2P.title += ' * ' + this.player().localize('From servers: ') + downloadedFromServer + '\n' + ' * ' + this.player().localize('From peers: ') + downloadedFromPeers + '\n' } subDivP2P.title += this.player().localize('Total uploaded: ') + totalUploaded.join(' ') From 91002ac0422a40ceb724d04bae7d338989687cd9 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jul 2025 11:07:18 +0200 Subject: [PATCH 3/8] Fix settings menu escape key --- .../src/shared/control-bar/p2p-info-button.ts | 8 +++----- .../src/shared/peertube/peertube-plugin.ts | 17 +++++++++++++++++ .../src/shared/settings/menu-focus-fixed.ts | 14 -------------- .../shared/settings/settings-menu-button.ts | 6 +++++- .../src/shared/settings/settings-menu-item.ts | 19 +++++++++---------- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts b/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts index 89db46818..e521b8fea 100644 --- a/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts +++ b/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts @@ -11,8 +11,7 @@ class P2PInfoButton extends Button { }) as HTMLDivElement div.appendChild(subDivP2P) - const downloadIcon = videojs.dom.createEl('span', { - className: 'icon icon-download', + const downloadIcon = videojs.dom.createEl('span', { className: 'icon icon-download' }, { role: 'img', ariaLabel: this.player().localize('Download speed:') }) @@ -25,10 +24,9 @@ class P2PInfoButton extends Button { downloadSpeedText.appendChild(downloadSpeedUnit) subDivP2P.appendChild(downloadSpeedText) - const uploadIcon = videojs.dom.createEl('span', { + const uploadIcon = videojs.dom.createEl('span', { className: 'icon icon-upload' }, { role: 'img', - ariaLabel: this.player().localize('Upload speed:'), - className: 'icon icon-upload' + ariaLabel: this.player().localize('Upload speed:') }) subDivP2P.appendChild(uploadIcon) diff --git a/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts b/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts index cc6346ef7..897716930 100644 --- a/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts +++ b/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts @@ -254,6 +254,7 @@ class PeerTubePlugin extends Plugin { if (isMobile()) this.player.addClass('vjs-is-mobile') this.initSmoothProgressBar() + this.patchMenuEscapeKey() this.player.ready(() => { this.listenControlBarMouse() @@ -654,6 +655,22 @@ class PeerTubePlugin extends Plugin { } } + private patchMenuEscapeKey () { + const Menu = videojs.getComponent('Menu') as any + + const fn = Menu.prototype.handleKeyDown + Menu.prototype.handleKeyDown = function handleKeyDown (event: KeyboardEvent) { + if (event.key === 'Escape') { + event.preventDefault() + event.stopPropagation() + this.trigger('escaped-key') + return + } + + fn.call(this, event) + } + } + private getCaptionsButton () { const settingsButton = this.player.controlBar.getDescendant([ 'settingsButton' ]) as SettingsButton diff --git a/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts b/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts index 9089615a2..96256e993 100644 --- a/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts +++ b/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts @@ -8,20 +8,6 @@ const Component = videojs.getComponent('Component') class MenuFocusFixed extends Menu { declare private focusedChild_: number - handleKeyDown (event: KeyboardEvent) { - if (event.key === 'Escape') { - event.preventDefault() - event.stopPropagation() - this.trigger('escaped-key') - return - } - - // FIXME: super misses handleKeyDown - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - return super.handleKeyDown(event) - } - stepForward () { let stepChild = 0 diff --git a/client/src/standalone/player/src/shared/settings/settings-menu-button.ts b/client/src/standalone/player/src/shared/settings/settings-menu-button.ts index 63ff1e739..38969a250 100644 --- a/client/src/standalone/player/src/shared/settings/settings-menu-button.ts +++ b/client/src/standalone/player/src/shared/settings/settings-menu-button.ts @@ -145,7 +145,6 @@ class SettingsButton extends Button { showDialog () { this.player().peertube().onMenuOpened() - ;(this.menu.el() as HTMLElement).style.opacity = '1' this.dialog.show() @@ -252,6 +251,11 @@ class SettingsButton extends Button { // Whether to add or remove selected class on the settings sub menu element settingsMenuItem.on('click', openSubMenu) + + settingsMenuItem.on('escaped-key', () => { + this.hideDialog() + this.focus() + }) } resetChildren () { diff --git a/client/src/standalone/player/src/shared/settings/settings-menu-item.ts b/client/src/standalone/player/src/shared/settings/settings-menu-item.ts index 38dfc8290..417612702 100644 --- a/client/src/standalone/player/src/shared/settings/settings-menu-item.ts +++ b/client/src/standalone/player/src/shared/settings/settings-menu-item.ts @@ -139,7 +139,6 @@ class SettingsMenuItem extends MenuItem { /** * Create the component's DOM element - * */ createEl () { const el = videojs.dom.createEl('li', { @@ -176,9 +175,8 @@ class SettingsMenuItem extends MenuItem { // Remove open class to ensure only the open submenu gets this class videojs.dom.removeClass(this.el(), 'open') - super.handleClick(event); - - (this.mainMenu.el() as HTMLElement).style.opacity = '0' + super.handleClick(event) + ;(this.mainMenu.el() as HTMLElement).style.opacity = '0' // Whether to add or remove vjs-hidden class on the settingsSubMenuEl element if (videojs.dom.hasClass(this.settingsSubMenuEl_, 'vjs-hidden')) { videojs.dom.removeClass(this.settingsSubMenuEl_, 'vjs-hidden') @@ -206,9 +204,8 @@ class SettingsMenuItem extends MenuItem { const button = this.subMenu.menu.addChild('MenuItem', {}, 0) button.setAttribute('aria-label', this.player().localize('Go back')) - button.addClass('vjs-back-button'); - - (button.el() as HTMLElement).innerHTML = this.player().localize(this.subMenu.controlText()) + button.addClass('vjs-back-button') + ;(button.el() as HTMLElement).innerHTML = this.player().localize(this.subMenu.controlText()) } onTransitionEnd (event: any) { @@ -284,7 +281,6 @@ class SettingsMenuItem extends MenuItem { const subMenuItemExtended = subMenuItem as MenuItemExtended if (subMenuItemExtended.isSelected_) { - // Prefer to use the function if (typeof subMenuItemExtended.getLabel === 'function') { this.settingsSubMenuValueEl_.innerHTML = subMenuItemExtended.getLabel() @@ -354,6 +350,10 @@ class SettingsMenuItem extends MenuItem { private rebuildAfterMenuChange () { debugLogger('Rebuilding menu ' + this.subMenu.name() + ' after change') + this.subMenu.menu.on('escaped-key', () => { + this.trigger('escaped-key') + }) + this.settingsSubMenuEl_.innerHTML = '' this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el()) this.update() @@ -361,10 +361,9 @@ class SettingsMenuItem extends MenuItem { this.setSize() this.bindClickEvents() } - } -(SettingsMenuItem as any).prototype.contentElType = 'button' +;(SettingsMenuItem as any).prototype.contentElType = 'button' videojs.registerComponent('SettingsMenuItem', SettingsMenuItem) export { SettingsMenuItem } From 2011ea37e620f63da395de5089998fd767452c7d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jul 2025 14:53:38 +0200 Subject: [PATCH 4/8] Fix settings menu arrow left/right navigation --- .../src/shared/peertube/peertube-plugin.ts | 14 ++++++++++ .../shared/settings/settings-menu-button.ts | 11 +++++++- .../src/shared/settings/settings-menu-item.ts | 27 ++++++++++++++----- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts b/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts index 897716930..7d731ca7f 100644 --- a/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts +++ b/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts @@ -667,6 +667,20 @@ class PeerTubePlugin extends Plugin { return } + if (event.key === 'ArrowRight') { + event.preventDefault() + event.stopPropagation() + this.trigger('arrow-right', event.target) + return + } + + if (event.key === 'ArrowLeft') { + event.preventDefault() + event.stopPropagation() + this.trigger('arrow-left') + return + } + fn.call(this, event) } } diff --git a/client/src/standalone/player/src/shared/settings/settings-menu-button.ts b/client/src/standalone/player/src/shared/settings/settings-menu-button.ts index 38969a250..715a0e2d3 100644 --- a/client/src/standalone/player/src/shared/settings/settings-menu-button.ts +++ b/client/src/standalone/player/src/shared/settings/settings-menu-button.ts @@ -1,10 +1,13 @@ +import debug from 'debug' import videojs from 'video.js' import { toTitleCase } from '../common' +import { MenuFocusFixed } from './menu-focus-fixed' import { SettingsDialog } from './settings-dialog' import { SettingsMenuItem } from './settings-menu-item' import { SettingsPanel } from './settings-panel' import { SettingsPanelChild } from './settings-panel-child' -import { MenuFocusFixed } from './menu-focus-fixed' + +const debugLogger = debug('peertube:player:settings') const Button = videojs.getComponent('Button') const Component = videojs.getComponent('Component') @@ -213,6 +216,12 @@ class SettingsButton extends Button { this.focus() }) + this.menu.on('arrow-right', (_, el) => { + debugLogger('Detected arrow right on menu item', el) + + el.click() + }) + this.menu.addClass('vjs-main-menu') const entries = this.settingsButtonOptions.entries diff --git a/client/src/standalone/player/src/shared/settings/settings-menu-item.ts b/client/src/standalone/player/src/shared/settings/settings-menu-item.ts index 417612702..91ab0c794 100644 --- a/client/src/standalone/player/src/shared/settings/settings-menu-item.ts +++ b/client/src/standalone/player/src/shared/settings/settings-menu-item.ts @@ -81,6 +81,10 @@ class SettingsMenuItem extends MenuItem { // Update on rate change if (subMenuName === 'PlaybackRateMenuButton') { player.on('ratechange', this.submenuClickHandler) + + player.on('playbackrateschange', () => { + setTimeout(() => this.rebuildAfterMenuChange()) + }) } if (subMenuName === 'CaptionsButton') { @@ -262,6 +266,7 @@ class SettingsMenuItem extends MenuItem { this.createBackButton() this.setSize() this.bindClickEvents() + this.bindKeyEvents() this.settingsSubMenuEl_.addEventListener('transitionend', this.transitionEndHandler, false) } @@ -306,13 +311,24 @@ class SettingsMenuItem extends MenuItem { bindClickEvents () { for (const item of this.subMenu.menu.children()) { - if (!(item instanceof Component)) { - continue - } + if (!(item instanceof Component)) continue + item.on([ 'tap', 'click' ], this.submenuClickHandler) } } + bindKeyEvents () { + this.subMenu.menu.on('escaped-key', () => { + this.trigger('escaped-key') + }) + + this.subMenu.menu.on('arrow-left', () => { + debugLogger('Detected arrow left on sub menu ' + this.subMenu.name()) + + this.loadMainMenu() + }) + } + // save size of submenus on first init // if number of submenu items change dynamically more logic will be needed setSize () { @@ -350,16 +366,13 @@ class SettingsMenuItem extends MenuItem { private rebuildAfterMenuChange () { debugLogger('Rebuilding menu ' + this.subMenu.name() + ' after change') - this.subMenu.menu.on('escaped-key', () => { - this.trigger('escaped-key') - }) - this.settingsSubMenuEl_.innerHTML = '' this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el()) this.update() this.createBackButton() this.setSize() this.bindClickEvents() + this.bindKeyEvents() } } From 27f197692954b5763404134e44b9636dc8c3a36f Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jul 2025 15:02:21 +0200 Subject: [PATCH 5/8] Add missing aria-controls attribute --- .../standalone/player/src/shared/settings/settings-dialog.ts | 5 ++--- .../player/src/shared/settings/settings-menu-button.ts | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/standalone/player/src/shared/settings/settings-dialog.ts b/client/src/standalone/player/src/shared/settings/settings-dialog.ts index d9d75a0ab..7c2fbd8c3 100644 --- a/client/src/standalone/player/src/shared/settings/settings-dialog.ts +++ b/client/src/standalone/player/src/shared/settings/settings-dialog.ts @@ -3,7 +3,6 @@ import videojs from 'video.js' const Component = videojs.getComponent('Component') class SettingsDialog extends Component { - constructor (player: videojs.Player) { super(player) @@ -12,15 +11,15 @@ class SettingsDialog extends Component { /** * Create the component's DOM element - * */ createEl () { - const uniqueId = this.id() + const uniqueId = this.player().id() const dialogLabelId = 'TTsettingsDialogLabel-' + uniqueId const dialogDescriptionId = 'TTsettingsDialogDescription-' + uniqueId return super.createEl('div', { className: 'vjs-settings-dialog vjs-modal-overlay', + id: 'vjs-settings-dialog-' + uniqueId, tabIndex: -1 }, { 'role': 'dialog', diff --git a/client/src/standalone/player/src/shared/settings/settings-menu-button.ts b/client/src/standalone/player/src/shared/settings/settings-menu-button.ts index 715a0e2d3..d7731709a 100644 --- a/client/src/standalone/player/src/shared/settings/settings-menu-button.ts +++ b/client/src/standalone/player/src/shared/settings/settings-menu-button.ts @@ -47,6 +47,7 @@ class SettingsButton extends Button { this.panelChild = this.panel.addChild('settingsPanelChild') this.addClass('vjs-settings') + this.setAttribute('aria-controls', 'vjs-settings-dialog-' + this.player().id()) // Event handlers this.addSettingsItemHandler = this.onAddSettingsItem.bind(this) From faeacaec6831802a8599f0f044eb82240e9cfc82 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jul 2025 15:31:54 +0200 Subject: [PATCH 6/8] Fix menu focus --- .../player/src/shared/settings/menu-focus-fixed.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts b/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts index 96256e993..ad148e823 100644 --- a/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts +++ b/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts @@ -26,7 +26,17 @@ class MenuFocusFixed extends Menu { this.focus(stepChild) } - focus (item = 0): void { + focus (item?: number): void { + // Reset focus + if (item === undefined) { + this.focusedChild_ = -1 + item = 0 + } + + this._focus(item) + } + + private _focus (item: number) { const children = this.children().slice() const haveTitle = children.length && children[0].hasClass('vjs-menu-title') From 3f4c267ec9d05a516c903c466efc9d09a5983a5a Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jul 2025 15:47:26 +0200 Subject: [PATCH 7/8] Add player strings to translate --- scripts/i18n/create-custom-files.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/i18n/create-custom-files.ts b/scripts/i18n/create-custom-files.ts index c514f0a7a..c06c54d2d 100755 --- a/scripts/i18n/create-custom-files.ts +++ b/scripts/i18n/create-custom-files.ts @@ -91,7 +91,9 @@ const playerKeys = { 'Content warning': 'Content warning', 'Violence': 'Violence', 'Shocking Content': 'Shocking Content', - 'Explicit Sex': 'Explicit Sex' + 'Explicit Sex': 'Explicit Sex', + 'Upload speed:': 'Upload speed:', + 'Download speed:': 'Download speed:' } Object.assign(playerKeys, videojs) From 56b8bff325b5d07d269495d06b29b81834f8ca94 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jul 2025 16:38:58 +0200 Subject: [PATCH 8/8] Convert video short uuid to uuid --- .../fixtures/peertube-plugin-test/main.js | 4 ++-- packages/tests/src/plugins/filter-hooks.ts | 20 +++--------------- server/core/controllers/client.ts | 21 +++++++++++++------ server/core/helpers/custom-validators/misc.ts | 4 ++-- server/core/lib/html/client-html.ts | 16 +++++++------- server/core/lib/html/shared/playlist-html.ts | 9 ++------ server/core/lib/html/shared/video-html.ts | 9 ++------ 7 files changed, 34 insertions(+), 49 deletions(-) diff --git a/packages/tests/fixtures/peertube-plugin-test/main.js b/packages/tests/fixtures/peertube-plugin-test/main.js index 11669d326..5a13d3be6 100644 --- a/packages/tests/fixtures/peertube-plugin-test/main.js +++ b/packages/tests/fixtures/peertube-plugin-test/main.js @@ -382,7 +382,7 @@ async function register ({ registerHook, registerSetting, settingsManager, stora handler: (result, params) => { return { allowed: false, - html: 'Lu Bu' + html: 'Lu Bu ' + params.req.params.id } } }) @@ -392,7 +392,7 @@ async function register ({ registerHook, registerSetting, settingsManager, stora handler: (result, params) => { return { allowed: false, - html: 'Diao Chan' + html: 'Diao Chan ' + params.req.params.id } } }) diff --git a/packages/tests/src/plugins/filter-hooks.ts b/packages/tests/src/plugins/filter-hooks.ts index ac7be95c4..71a84dda3 100644 --- a/packages/tests/src/plugins/filter-hooks.ts +++ b/packages/tests/src/plugins/filter-hooks.ts @@ -49,7 +49,7 @@ describe('Test plugin filter hooks', function () { await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath() }) await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath('-filter-translations') }) { - ({ uuid: videoPlaylistUUID } = await servers[0].playlists.create({ + ;({ uuid: videoPlaylistUUID } = await servers[0].playlists.create({ attributes: { displayName: 'my super playlist', privacy: VideoPlaylistPrivacy.PUBLIC, @@ -100,7 +100,6 @@ describe('Test plugin filter hooks', function () { }) describe('Videos', function () { - it('Should run filter:api.videos.list.params', async function () { const { data } = await servers[0].videos.list({ start: 0, count: 2 }) @@ -198,7 +197,6 @@ describe('Test plugin filter hooks', function () { }) describe('Video/live/import accept', function () { - it('Should run filter:api.video.upload.accept.result', async function () { const options = { attributes: { name: 'video with bad word' }, expectedStatus: HttpStatusCode.FORBIDDEN_403 } await servers[0].videos.upload({ mode: 'legacy', ...options }) @@ -312,7 +310,6 @@ describe('Test plugin filter hooks', function () { }) describe('Video comments accept', function () { - it('Should run filter:api.video-thread.create.accept.result', async function () { await servers[0].comments.createThread({ videoId: videoUUID, @@ -386,7 +383,6 @@ describe('Test plugin filter hooks', function () { }) describe('Video comments', function () { - it('Should run filter:api.video-threads.list.params', async function () { const { data } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 }) @@ -419,7 +415,6 @@ describe('Test plugin filter hooks', function () { }) describe('filter:video.auto-blacklist.result', function () { - async function checkIsBlacklisted (id: number | string, value: boolean) { const video = await servers[0].videos.getWithToken({ id }) expect(video.blacklisted).to.equal(value) @@ -483,7 +478,6 @@ describe('Test plugin filter hooks', function () { }) describe('Should run filter:api.user.signup.allowed.result', function () { - before(async function () { await servers[0].config.updateExistingConfig({ newConfig: { signup: { requiresApproval: false } } }) }) @@ -508,7 +502,6 @@ describe('Test plugin filter hooks', function () { }) describe('Should run filter:api.user.request-signup.allowed.result', function () { - before(async function () { await servers[0].config.updateExistingConfig({ newConfig: { signup: { requiresApproval: true } } }) }) @@ -635,12 +628,12 @@ describe('Test plugin filter hooks', function () { it('Should run filter:html.embed.video.allowed.result', async function () { const res = await makeGetRequest({ url: servers[0].url, path: embedVideos[0].embedPath, expectedStatus: HttpStatusCode.OK_200 }) - expect(res.text).to.equal('Lu Bu') + expect(res.text).to.equal('Lu Bu ' + embedVideos[0].uuid) }) it('Should run filter:html.embed.video-playlist.allowed.result', async function () { const res = await makeGetRequest({ url: servers[0].url, path: embedPlaylists[0].embedPath, expectedStatus: HttpStatusCode.OK_200 }) - expect(res.text).to.equal('Diao Chan') + expect(res.text).to.equal('Diao Chan ' + embedPlaylists[0].uuid) }) }) @@ -666,7 +659,6 @@ describe('Test plugin filter hooks', function () { }) describe('Search filters', function () { - before(async function () { await servers[0].config.updateExistingConfig({ newConfig: { @@ -758,7 +750,6 @@ describe('Test plugin filter hooks', function () { }) describe('Upload/import/live attributes filters', function () { - before(async function () { await servers[0].config.enableLive({ transcoding: false, allowReplay: false }) await servers[0].config.enableVideoImports() @@ -827,13 +818,11 @@ describe('Test plugin filter hooks', function () { }) describe('Stats filters', function () { - it('Should run filter:api.server.stats.get.result', async function () { const data = await servers[0].stats.get() expect((data as any).customStats).to.equal(14) }) - }) describe('Job queue filters', function () { @@ -883,7 +872,6 @@ describe('Test plugin filter hooks', function () { }) describe('Transcoding filters', async function () { - it('Should run filter:transcoding.auto.resolutions-to-transcode.result', async function () { const { uuid } = await servers[0].videos.quickUpload({ name: 'transcode-filter' }) @@ -896,7 +884,6 @@ describe('Test plugin filter hooks', function () { }) describe('Video channel filters', async function () { - it('Should run filter:api.video-channels.list.params', async function () { const { data } = await servers[0].channels.list({ start: 0, count: 0 }) @@ -918,7 +905,6 @@ describe('Test plugin filter hooks', function () { }) describe('Activity Pub', function () { - it('Should run filter:activity-pub.activity.context.build.result', async function () { const { body } = await makeActivityPubGetRequest(servers[0].url, '/w/' + videoUUID) expect(body.type).to.equal('Video') diff --git a/server/core/controllers/client.ts b/server/core/controllers/client.ts index e23716bd9..4d7909a10 100644 --- a/server/core/controllers/client.ts +++ b/server/core/controllers/client.ts @@ -1,13 +1,14 @@ +import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@peertube/peertube-core-utils' +import { HttpStatusCode } from '@peertube/peertube-models' +import { currentDir, root } from '@peertube/peertube-node-utils' +import { toCompleteUUID } from '@server/helpers/custom-validators/misc.js' +import { logger } from '@server/helpers/logger.js' +import { CONFIG } from '@server/initializers/config.js' +import { Hooks } from '@server/lib/plugins/hooks.js' import express from 'express' import { constants, promises as fs } from 'fs' import { readFile } from 'fs/promises' import { join } from 'path' -import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@peertube/peertube-core-utils' -import { HttpStatusCode } from '@peertube/peertube-models' -import { logger } from '@server/helpers/logger.js' -import { CONFIG } from '@server/initializers/config.js' -import { Hooks } from '@server/lib/plugins/hooks.js' -import { currentDir, root } from '@peertube/peertube-node-utils' import { STATIC_MAX_AGE } from '../initializers/constants.js' import { ClientHtml, sendHTML, serveIndexHTML } from '../lib/html/client-html.js' import { asyncMiddleware, buildRateLimiter, embedCSP } from '../middlewares/index.js' @@ -129,6 +130,8 @@ function serveServerTranslations (req: express.Request, res: express.Response) { } async function generateVideoEmbedHtmlPage (req: express.Request, res: express.Response) { + req.params.id = toCompleteUUID(req.params.id) + const allowParameters = { req } const allowedResult = await Hooks.wrapFun( @@ -149,6 +152,8 @@ async function generateVideoEmbedHtmlPage (req: express.Request, res: express.Re } async function generateVideoPlaylistEmbedHtmlPage (req: express.Request, res: express.Response) { + req.params.id = toCompleteUUID(req.params.id) + const allowParameters = { req } const allowedResult = await Hooks.wrapFun( @@ -176,12 +181,16 @@ async function generateWatchHtmlPage (req: express.Request, res: express.Respons const threadIdIndex = videoId.indexOf(';threadId') if (threadIdIndex !== -1) videoId = videoId.substring(0, threadIdIndex) + videoId = toCompleteUUID(videoId) + const html = await ClientHtml.getWatchHTMLPage(videoId, req, res) return sendHTML(html, res, true) } async function generateWatchPlaylistHtmlPage (req: express.Request, res: express.Response) { + req.params.id = toCompleteUUID(req.params.id) + const html = await ClientHtml.getWatchPlaylistHTMLPage(req.params.id + '', req, res) return sendHTML(html, res, true) diff --git a/server/core/helpers/custom-validators/misc.ts b/server/core/helpers/custom-validators/misc.ts index b77cb5c92..fb8ae3b37 100644 --- a/server/core/helpers/custom-validators/misc.ts +++ b/server/core/helpers/custom-validators/misc.ts @@ -1,8 +1,8 @@ -import 'multer' +import { isShortUUID, shortToUUID } from '@peertube/peertube-node-utils' import { UploadFilesForCheck } from 'express' +import 'multer' import { sep } from 'path' import validator from 'validator' -import { isShortUUID, shortToUUID } from '@peertube/peertube-node-utils' export function exists (value: any) { return value !== undefined && value !== null diff --git a/server/core/lib/html/client-html.ts b/server/core/lib/html/client-html.ts index e5ad6e398..b8695b960 100644 --- a/server/core/lib/html/client-html.ts +++ b/server/core/lib/html/client-html.ts @@ -18,22 +18,22 @@ class ClientHtml { // --------------------------------------------------------------------------- - static getWatchHTMLPage (videoIdArg: string, req: express.Request, res: express.Response) { - return VideoHtml.getWatchVideoHTML(videoIdArg, req, res) + static getWatchHTMLPage (videoId: string, req: express.Request, res: express.Response) { + return VideoHtml.getWatchVideoHTML(videoId, req, res) } - static getVideoEmbedHTML (videoIdArg: string) { - return VideoHtml.getEmbedVideoHTML(videoIdArg) + static getVideoEmbedHTML (videoId: string) { + return VideoHtml.getEmbedVideoHTML(videoId) } // --------------------------------------------------------------------------- - static getWatchPlaylistHTMLPage (videoPlaylistIdArg: string, req: express.Request, res: express.Response) { - return PlaylistHtml.getWatchPlaylistHTML(videoPlaylistIdArg, req, res) + static getWatchPlaylistHTMLPage (videoPlaylistId: string, req: express.Request, res: express.Response) { + return PlaylistHtml.getWatchPlaylistHTML(videoPlaylistId, req, res) } - static getVideoPlaylistEmbedHTML (playlistIdArg: string) { - return PlaylistHtml.getEmbedPlaylistHTML(playlistIdArg) + static getVideoPlaylistEmbedHTML (playlistId: string) { + return PlaylistHtml.getEmbedPlaylistHTML(playlistId) } // --------------------------------------------------------------------------- diff --git a/server/core/lib/html/shared/playlist-html.ts b/server/core/lib/html/shared/playlist-html.ts index 89b4d8047..5adf72304 100644 --- a/server/core/lib/html/shared/playlist-html.ts +++ b/server/core/lib/html/shared/playlist-html.ts @@ -1,6 +1,5 @@ import { addQueryParams, escapeHTML, getDefaultRSSFeeds } from '@peertube/peertube-core-utils' import { HttpStatusCode, VideoPlaylistPrivacy } from '@peertube/peertube-models' -import { toCompleteUUID } from '@server/helpers/custom-validators/misc.js' import { Memoize } from '@server/helpers/memoize.js' import { VideoPlaylistModel } from '@server/models/video/video-playlist.js' import { MVideoPlaylist, MVideoPlaylistFull } from '@server/types/models/index.js' @@ -13,9 +12,7 @@ import { PageHtml } from './page-html.js' import { TagsHtml } from './tags-html.js' export class PlaylistHtml { - static async getWatchPlaylistHTML (videoPlaylistIdArg: string, req: express.Request, res: express.Response) { - const videoPlaylistId = toCompleteUUID(videoPlaylistIdArg) - + static async getWatchPlaylistHTML (videoPlaylistId: string, req: express.Request, res: express.Response) { // Let Angular application handle errors if (!validator.default.isInt(videoPlaylistId) && !validator.default.isUUID(videoPlaylistId, 4)) { res.status(HttpStatusCode.NOT_FOUND_404) @@ -46,9 +43,7 @@ export class PlaylistHtml { } @Memoize({ maxAge: MEMOIZE_TTL.EMBED_HTML }) - static async getEmbedPlaylistHTML (playlistIdArg: string) { - const playlistId = toCompleteUUID(playlistIdArg) - + static async getEmbedPlaylistHTML (playlistId: string) { const playlistPromise: Promise = validator.default.isInt(playlistId) || validator.default.isUUID(playlistId, 4) ? VideoPlaylistModel.loadWithAccountAndChannel(playlistId, null) : Promise.resolve(undefined) diff --git a/server/core/lib/html/shared/video-html.ts b/server/core/lib/html/shared/video-html.ts index b7dcc1a64..d2ce8d466 100644 --- a/server/core/lib/html/shared/video-html.ts +++ b/server/core/lib/html/shared/video-html.ts @@ -1,6 +1,5 @@ import { addQueryParams, escapeHTML, getVideoWatchRSSFeeds } from '@peertube/peertube-core-utils' import { HttpStatusCode, VideoPrivacy } from '@peertube/peertube-models' -import { toCompleteUUID } from '@server/helpers/custom-validators/misc.js' import { Memoize } from '@server/helpers/memoize.js' import express from 'express' import validator from 'validator' @@ -15,9 +14,7 @@ import { PageHtml } from './page-html.js' import { TagsHtml } from './tags-html.js' export class VideoHtml { - static async getWatchVideoHTML (videoIdArg: string, req: express.Request, res: express.Response) { - const videoId = toCompleteUUID(videoIdArg) - + static async getWatchVideoHTML (videoId: string, req: express.Request, res: express.Response) { // Let Angular application handle errors if (!validator.default.isInt(videoId) && !validator.default.isUUID(videoId, 4)) { res.status(HttpStatusCode.NOT_FOUND_404) @@ -47,9 +44,7 @@ export class VideoHtml { } @Memoize({ maxAge: MEMOIZE_TTL.EMBED_HTML }) - static async getEmbedVideoHTML (videoIdArg: string) { - const videoId = toCompleteUUID(videoIdArg) - + static async getEmbedVideoHTML (videoId: string) { const videoPromise: Promise = validator.default.isInt(videoId) || validator.default.isUUID(videoId, 4) ? VideoModel.loadWithBlacklist(videoId) : Promise.resolve(undefined)