feat: video library imports from Drive and chat media (WS2 backend)

POST /video/import-drive {resourceId} pulls through the oCIS WebDAV
service-account client; POST /video/import-chat {mxcUri} pulls from the
Matrix content repository (MATRIX_MEDIA_TOKEN, homeserver falls back to
ARCHIVE_PROVISIONER_HOMESERVER_URL). Both cap at 256 MiB (buffered v1,
streaming follow-up), store through the same encrypted content-
addressed path as uploads, probe, and audit video.imported with
provenance.
This commit is contained in:
glass beadian 2026-10-14 14:16:19 +00:00
parent d87c4e99e6
commit 177ca6eb2b
4 changed files with 167 additions and 1 deletions

View file

@ -103,6 +103,23 @@ export class VideoService {
return this.registerAndProbe(workspaceId, stored.key, stored.contentType, stored.sizeBytes);
}
async importVideoBytes(
workspaceId: string,
bytes: Buffer,
mimeType: string,
source: "drive" | "chat",
sourceRef: string,
): Promise<VideoUploadResult> {
validateUpload("pending", mimeType, bytes.length);
const storage = this.requireStorage();
const stored = await storage.putContent(bytes, mimeType).catch(() => {
throw new ServiceUnavailableException("Video storage unavailable: could not store imported video.");
});
const result = await this.registerAndProbe(workspaceId, stored.key, stored.contentType, stored.sizeBytes);
await this.audit(workspaceId, "video.imported", result.videoAsset.id, { source, sourceRef, sizeBytes: bytes.length });
return result;
}
async uploadVideo(
workspaceId: string,
storageKey: string,
@ -356,7 +373,7 @@ export class VideoService {
private async audit(
workspaceId: string,
action: "video.uploaded" | "video.probed" | "video.deleted",
action: "video.uploaded" | "video.probed" | "video.deleted" | "video.imported",
targetId: string,
diff: Record<string, unknown>,
): Promise<void> {