Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-audio-inifinity-length.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: patch
---

Fix crash during scrubbing before duration duration appears.
27 changes: 26 additions & 1 deletion src/app/components/message/MsgTypeRenderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,28 @@ export function MVideo({ content, renderAsFile, renderVideoContent, outlined }:
);
}

const getAudioDurationMs = (content: IAudioContent, info?: IAudioInfo): number | undefined => {
const fromInfo = info?.duration;
if (typeof fromInfo === 'number' && Number.isFinite(fromInfo) && fromInfo > 0) {
return fromInfo;
}
const voiceV2 = (content as Record<string, unknown>)['org.matrix.msc3245.voice.v2'];
if (voiceV2 && typeof voiceV2 === 'object') {
const seconds = (voiceV2 as { duration?: number }).duration;
if (typeof seconds === 'number' && Number.isFinite(seconds) && seconds > 0) {
return seconds * 1000;
}
}
const msc1767Audio = (content as Record<string, unknown>)['org.matrix.msc1767.audio'];
if (msc1767Audio && typeof msc1767Audio === 'object') {
const ms = (msc1767Audio as { duration?: number }).duration;
if (typeof ms === 'number' && Number.isFinite(ms) && ms > 0) {
return ms;
}
}
return undefined;
};

type RenderAudioContentProps = {
info: IAudioInfo;
mimeType: string;
Expand All @@ -536,6 +558,9 @@ export function MAudio({ content, renderAsFile, renderAudioContent, outlined }:
}

const filename = content.filename ?? content.body ?? 'Audio';
const durationMs = getAudioDurationMs(content, audioInfo);
const resolvedInfo =
durationMs !== undefined ? { ...audioInfo, duration: durationMs } : audioInfo;
return (
<Attachment outlined={outlined}>
<AttachmentHeader>
Expand All @@ -555,7 +580,7 @@ export function MAudio({ content, renderAsFile, renderAudioContent, outlined }:
<AttachmentBox>
<AttachmentContent>
{renderAudioContent({
info: audioInfo,
info: resolvedInfo,
mimeType: safeMimeType,
url: mxcUrl,
encInfo: content.file,
Expand Down
35 changes: 24 additions & 11 deletions src/app/components/message/content/AudioContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,26 @@ export function AudioContent({

const [currentTime, setCurrentTime] = useState(0);
// duration in seconds. (NOTE: info.duration is in milliseconds)
const infoDuration = info.duration ?? 0;
const [duration, setDuration] = useState((infoDuration >= 0 ? infoDuration : 0) / 1000);
const infoDurationMs = info.duration ?? 0;
const initialDurationSec =
Number.isFinite(infoDurationMs) && infoDurationMs > 0 ? infoDurationMs / 1000 : 0;
const [duration, setDuration] = useState(initialDurationSec);

const getAudioRef = useCallback(() => audioRef.current, []);
const { loading } = useMediaLoading(getAudioRef);
const { playing, setPlaying } = useMediaPlay(getAudioRef);
const { seek } = useMediaSeek(getAudioRef);
const { volume, mute, setMute, setVolume } = useMediaVolume(getAudioRef);
const handlePlayTimeCallback: PlayTimeCallback = useCallback((d, ct) => {
setDuration(d);
setCurrentTime(ct);
if (Number.isFinite(d) && d > 0) setDuration(d);
if (Number.isFinite(ct) && ct >= 0) setCurrentTime(ct);
}, []);

const trackMax = duration > 0 ? duration : 1;
const trackTime =
duration > 0 ? Math.min(Number.isFinite(currentTime) ? currentTime : 0, duration) : 0;
const displayDuration = duration > 0 ? duration : 0;
const displayCurrentTime = Number.isFinite(currentTime) && currentTime >= 0 ? currentTime : 0;
useMediaPlayTimeCallback(
getAudioRef,
useThrottle(handlePlayTimeCallback, PLAY_TIME_THROTTLE_OPS)
Expand All @@ -102,9 +110,14 @@ export function AudioContent({
<Range
step={1}
min={0}
max={duration || 1}
values={[currentTime]}
onChange={(values) => seek(values[0] ?? 0)}
max={trackMax}
values={[trackTime]}
onChange={(values) => {
if (!(duration > 0)) return;
const next = values[0] ?? 0;
if (!Number.isFinite(next)) return;
seek(Math.max(0, Math.min(next, duration)));
}}
renderTrack={(params) => {
const { key, ...restProps } = params.props as unknown as {
key?: string;
Expand All @@ -118,8 +131,8 @@ export function AudioContent({
variant="Secondary"
size="300"
min={0}
max={duration}
value={currentTime}
max={trackMax}
value={trackTime}
radii="300"
/>
</div>
Expand Down Expand Up @@ -168,8 +181,8 @@ export function AudioContent({
</Chip>

<Text size="T200">{`${secondsToMinutesAndSeconds(
currentTime
)} / ${secondsToMinutesAndSeconds(duration)}`}</Text>
displayCurrentTime
)} / ${secondsToMinutesAndSeconds(displayDuration)}`}</Text>
</>
),
rightControl: (
Expand Down
1 change: 1 addition & 0 deletions src/app/components/upload-card/UploadCardRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function PreviewVideo({ fileItem }: Readonly<PreviewVideoProps>) {
const BAR_COUNT = 44;

function formatAudioTime(s: number): string {
if (!Number.isFinite(s) || s < 0) return '0:00';
const m = Math.floor(s / 60);
const sec = Math.floor(s % 60);
return `${m}:${sec.toString().padStart(2, '0')}`;
Expand Down
6 changes: 5 additions & 1 deletion src/app/hooks/media/useMediaPlayTimeCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ export const useMediaPlayTimeCallback = (
const targetEl = getTargetElement();
const handleChange = () => {
if (!targetEl) return;
onPlayTimeCallback(targetEl.duration, targetEl.currentTime);
const { duration, currentTime } = targetEl;
onPlayTimeCallback(
duration,
Number.isFinite(currentTime) && currentTime >= 0 ? currentTime : 0
);
};
targetEl?.addEventListener('timeupdate', handleChange);
targetEl?.addEventListener('loadedmetadata', handleChange);
Expand Down
Loading