fix: jobless video assets no longer 500 the library click

Legacy/edge-case video_assets rows without a processing job threw 'has no
processing job' from getVideoStatus — the library detail click 500ed.
VideoStatus.processingJob is now nullable end to end (api type, DTO, web
consumers null-guarded); a jobless asset renders as queued.
This commit is contained in:
glass beadian 2026-10-18 08:19:42 +00:00
parent 215952fe5e
commit 31c97a0b0a
13 changed files with 95 additions and 25 deletions

View file

@ -82,7 +82,7 @@ export interface VideoLibraryPage {
export interface VideoStatus {
videoAsset: VideoAssetRecord;
processingJob: VideoProcessingJobRecord;
processingJob: VideoProcessingJobRecord | null;
}
export type VideoUploadResult = VideoStatus;
@ -341,7 +341,7 @@ export class VideoService {
} catch (error) {
// Probe errors are recorded on the job; return its ID and failure in status.
const status = await this.getVideoStatus(uploaded.videoAsset.id, workspaceId);
if (status.processingJob.state !== "failed") throw error;
if (status.processingJob === null || status.processingJob.state !== "failed") throw error;
}
await this.audit(workspaceId, "video.uploaded", uploaded.videoAsset.id, {
mimeType,
@ -369,8 +369,10 @@ export class VideoService {
);
if (result.rowCount !== 1) throw new NotFoundException(`video ${assetId} not found`);
const row = requiredRow(result.rows[0], "video status");
// A jobless asset is a legitimate state (pre-probe direct registrations,
// legacy rows): surface it instead of 500ing the library click.
if (row.job_id === null || row.job_id === undefined) {
throw new Error(`video ${assetId} has no processing job`);
return { videoAsset: assetFromRow(row), processingJob: null };
}
return { videoAsset: assetFromRow(row), processingJob: joinedJobFromRow(row) };
});