UBERF-9731 Some recorder fixes

Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
Alexander Onnikov 2025-05-30 12:21:05 +07:00
parent 6f26cf0511
commit 59b8cdb5a1
No known key found for this signature in database
GPG Key ID: 3320C3B3324E934C
7 changed files with 66 additions and 16 deletions

View File

@ -180,7 +180,7 @@
{:else}
{#await getBlobRef(value.file, value.name, sizeToWidth('large')) then valueRef}
<a
class="no-line"
class="no-line no-underline"
style:flex-shrink={0}
href={valueRef.src}
download={value.name}
@ -211,7 +211,7 @@
</a>
</div>
<div class="info-content flex-row-center">
{#if value.size != null}{filesize(value.size, { spacer: '' })}{/if}
{#if value.size != null && value.size !== 0}{filesize(value.size, { spacer: '' })}{/if}
<span class="actions inline-flex clear-mins ml-1 gap-1">
<span></span>
<a class="no-line colorInherit" href={valueRef.src} download={value.name} bind:this={download}>

View File

@ -103,6 +103,7 @@
$: if (state !== null && state.state === 'stopped') {
dispatch('close')
recording.set(null)
}
async function updateCameraStream (
@ -269,6 +270,8 @@
async function handleStopRecording (): Promise<void> {
await stopRecording()
camEnabled = false
micEnabled = false
}
async function handleRestartRecording (): Promise<void> {
@ -412,6 +415,8 @@
}
function onKeyDown (e: KeyboardEvent): void {
if (state?.state === 'stopping') return
if (e.key === 'Enter') {
e.preventDefault()
e.stopPropagation()
@ -455,7 +460,7 @@
>
<div class="container p-3">
{#if state !== null && state.state === 'stopped'}
<div class="placeholder flex-col-center justify-center">OK</div>
<!-- Maybe show recording result -->
{:else if mainStream === null}
<div class="placeholder flex-col-center justify-center">
{#if cameraStreamPromise === null}
@ -590,6 +595,8 @@
icon={IconStop}
iconProps={{ size: 'small' }}
label={plugin.string.Stop}
disabled={state.state === 'stopping'}
loading={state.state === 'stopping'}
noFocus
on:click={handleStopRecording}
/>

View File

@ -1,15 +1,15 @@
<script lang="ts">
import { DropdownLabels, DropdownTextItem, Label } from '@hcengineering/ui'
import { DropdownLabelsIntl, DropdownIntlItem, Label } from '@hcengineering/ui'
import plugin from '../plugin'
import { recordingCameraPosition, recordingCameraSize } from '../stores'
const sizes: DropdownTextItem[] = [
const sizes: DropdownIntlItem[] = [
{ label: plugin.string.Small, id: 'small' },
{ label: plugin.string.Medium, id: 'medium' },
{ label: plugin.string.Large, id: 'large' }
]
const poses: DropdownTextItem[] = [
const poses: DropdownIntlItem[] = [
{ label: plugin.string.TopLeft, id: 'top-left' },
{ label: plugin.string.TopRight, id: 'top-right' },
{ label: plugin.string.BottomLeft, id: 'bottom-left' },
@ -19,7 +19,7 @@
<div class="antiPopup p-4 grid">
<Label label={plugin.string.CameraSize} />
<DropdownLabels
<DropdownLabelsIntl
items={sizes}
placeholder={plugin.string.CameraSize}
enableSearch={false}
@ -32,7 +32,7 @@
/>
<Label label={plugin.string.CameraPos} />
<DropdownLabels
<DropdownLabelsIntl
items={poses}
placeholder={plugin.string.CameraPos}
enableSearch={false}

View File

@ -96,13 +96,56 @@ export class Recorder {
return this.chunkStream
}
public stop (): void {
public async stop (): Promise<void> {
if (this.lastStartTime !== null) {
this.elapsedMs += Date.now() - this.lastStartTime
this.lastStartTime = null
}
// If the recorder is paused, we need to resume it before stopping
try {
if (this.mediaRecorder.state === 'paused') {
const resumed = new Promise<void>((resolve) => {
const timeout = setTimeout(resolve, 500)
this.mediaRecorder.addEventListener(
'resume',
() => {
clearTimeout(timeout)
resolve()
},
{ once: true }
)
})
this.mediaRecorder.resume()
await resumed
}
} catch (err) {
console.error('Recorder: error resuming MediaRecorder:', err)
}
// Ensure we wait for any pending data to be available
try {
if (this.mediaRecorder.state === 'recording') {
const dataavailable = new Promise<void>((resolve) => {
const timeout = setTimeout(resolve, 500)
this.mediaRecorder.addEventListener(
'dataavailable',
() => {
clearTimeout(timeout)
resolve()
},
{ once: true }
)
})
this.mediaRecorder.requestData()
await dataavailable
}
} catch (err) {
console.error('Recorder: error requesting data from MediaRecorder:', err)
}
try {
// After requesting data, we can safely stop the recorder
this.mediaRecorder.stop()
} catch (err) {
console.error('Recorder: error stopping MediaRecorder:', err)
@ -111,6 +154,7 @@ export class Recorder {
track.stop()
}
}
console.debug('Recorder: recording stopped, duration:', this.getRecordedTimeMs(), 'ms')
}

View File

@ -51,12 +51,11 @@ export async function stopRecording (): Promise<void> {
const current = get(recording)
const popup = get(recorder)
if (current !== null && current.state === 'recording') {
recording.set({ ...current, state: 'stopped' })
recording.set({ ...current, state: 'stopping' })
const result = await current.recorder.stop()
await current.options.onSuccess?.(result)
recording.set(null)
recording.set({ ...current, state: 'stopped' })
popup?.close()
} else {
console.warn('Recording is not in `recording` state', current)
@ -80,7 +79,7 @@ export async function restartRecording (): Promise<void> {
await current.recorder.start()
recording.set({ ...current, state: 'recording' })
} else {
console.warn('Recording is not in `paused` state', current)
console.warn('Recording not started', current)
}
}

View File

@ -67,12 +67,12 @@ export class ScreenRecorder {
}
public async stop (): Promise<RecordingResult> {
this.recorder.stop()
await this.recorder.stop()
return await this.uploader.wait()
}
public async cancel (): Promise<void> {
this.recorder.stop()
await this.recorder.stop()
await this.uploader.cancel()
}
}

View File

@ -20,7 +20,7 @@ export interface RecordingState {
recorder: ScreenRecorder
options: RecordingOptions
stream: MediaStream
state: 'recording' | 'paused' | 'stopped'
state: 'recording' | 'paused' | 'stopping' | 'stopped'
}
export interface RecordingOptions {