Added a way for admin to download and manage recordings on the server.

This commit is contained in:
Storm Dragon
2026-07-07 16:22:40 -04:00
parent 21fa07ee64
commit 4e210db57a
3 changed files with 19 additions and 1 deletions
+8 -1
View File
@@ -61,6 +61,11 @@ first entry in the user list) opens the *hall menu*, a menu with actions
that apply to the hall as a whole. Clicking on a different user opens that apply to the hall as a whole. Clicking on a different user opens
a *user menu*, a menu that applies to that specific user. a *user menu*, a menu that applies to that specific user.
For operators who are allowed to record, the hall menu includes the current
recording control and an *Open recordings* action. *Open recordings* opens
the hall's recording archive, which lists completed recordings when there
are multiple files.
### Chat pane ### Chat pane
The centre pane is a traditional chat interface, with an input form at the The centre pane is a traditional chat interface, with an input form at the
@@ -119,7 +124,9 @@ kicking them out from a hall.
The hall menu (opened by clicking on one's own entry in the user's list) The hall menu (opened by clicking on one's own entry in the user's list)
is extended with options to lock or to unlock a hall (a locked hall is is extended with options to lock or to unlock a hall (a locked hall is
one that non-operator users cannot join). one that non-operator users cannot join). Operators with recording
permission can also start or stop recording and open the hall's recording
archive from the hall menu.
All of the moderation commands are also available as command-line commands All of the moderation commands are also available as command-line commands
(see above), which is helpful when moderating large halls. (see above), which is helpful when moderating large halls.
+8
View File
@@ -1662,6 +1662,11 @@ function canRecordHall() {
serverConnection.permissions.indexOf('record') >= 0); serverConnection.permissions.indexOf('record') >= 0);
} }
function recordingsUrl() {
return '/recordings/' +
hall.split('/').map(part => encodeURIComponent(part)).join('/') + '/';
}
/** /**
* @param {{label?: string, type?: string, onClick?: function(): void}[]} items * @param {{label?: string, type?: string, onClick?: function(): void}[]} items
* @param {HTMLElement} trigger * @param {HTMLElement} trigger
@@ -1789,6 +1794,9 @@ function userMenu(elt) {
onClick: () => { onClick: () => {
serverConnection.hallAction(hallRecording ? 'unrecord' : 'record'); serverConnection.hallAction(hallRecording ? 'unrecord' : 'record');
}}); }});
items.push({label: 'Open recordings', onClick: () => {
window.location.href = recordingsUrl();
}});
} }
if(findUpMedia('audio')) if(findUpMedia('audio'))
items.push({label: 'Turn microphone off', onClick: () => { items.push({label: 'Turn microphone off', onClick: () => {
+3
View File
@@ -111,8 +111,11 @@ func TestRecordingControlLivesInSelfMenu(t *testing.T) {
js := readStaticFile(t, "skald.js") js := readStaticFile(t, "skald.js")
requireFunctionContains(t, js, "canRecordHall", "serverConnection.permissions.indexOf('record') >= 0") requireFunctionContains(t, js, "canRecordHall", "serverConnection.permissions.indexOf('record') >= 0")
requireFunctionContains(t, js, "recordingsUrl", "hall.split('/').map(part => encodeURIComponent(part)).join('/')")
requireFunctionContains(t, js, "recordingPredicate", "if(canRecordHall())") requireFunctionContains(t, js, "recordingPredicate", "if(canRecordHall())")
requireFunctionContains(t, js, "userMenu", "if(canRecordHall())") requireFunctionContains(t, js, "userMenu", "if(canRecordHall())")
requireFunctionContains(t, js, "userMenu", "hallRecording ? 'Stop recording' : 'Start recording'") requireFunctionContains(t, js, "userMenu", "hallRecording ? 'Stop recording' : 'Start recording'")
requireFunctionContains(t, js, "userMenu", "serverConnection.hallAction(hallRecording ? 'unrecord' : 'record')") requireFunctionContains(t, js, "userMenu", "serverConnection.hallAction(hallRecording ? 'unrecord' : 'record')")
requireFunctionContains(t, js, "userMenu", "Open recordings")
requireFunctionContains(t, js, "userMenu", "window.location.href = recordingsUrl()")
} }