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:
parent
d87c4e99e6
commit
177ca6eb2b
4 changed files with 167 additions and 1 deletions
43
apps/api/src/video/import-sources.test.ts
Normal file
43
apps/api/src/video/import-sources.test.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { Readable } from "node:stream";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { BadRequestException } from "@nestjs/common";
|
||||
import { IMPORT_MAX_BYTES, bufferWithCap, matrixMediaConfig, parseMxcUri } from "./import-sources.js";
|
||||
|
||||
describe("parseMxcUri", () => {
|
||||
it("accepts well-formed mxc uris and rejects everything else", () => {
|
||||
expect(parseMxcUri("mxc://chat.occupymedia.cc/AbC123_-")).toEqual({ server: "chat.occupymedia.cc", mediaId: "AbC123_-" });
|
||||
expect(parseMxcUri(" mxc://server/abc ")).toEqual({ server: "server", mediaId: "abc" });
|
||||
for (const bad of ["https://server/abc", "mxc:///abc", "mxc://server/", "mxc://server/a/b", "mxc://server/abc?x=1", ""]) {
|
||||
expect(() => parseMxcUri(bad)).toThrow(BadRequestException);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("bufferWithCap", () => {
|
||||
const streamOf = (chunks: Buffer[]) => new Readable({ read() { for (const c of chunks) this.push(c); this.push(null); } });
|
||||
|
||||
it("concatenates a stream under the cap", async () => {
|
||||
const bytes = await bufferWithCap(streamOf([Buffer.alloc(10, 1), Buffer.alloc(10, 2)]), 100);
|
||||
expect(bytes).toHaveLength(20);
|
||||
});
|
||||
|
||||
it("refuses streams beyond the cap and destroys them", async () => {
|
||||
await expect(bufferWithCap(streamOf([Buffer.alloc(60), Buffer.alloc(60)]), 64)).rejects.toBeInstanceOf(BadRequestException);
|
||||
expect(IMPORT_MAX_BYTES).toBe(256 * 1024 * 1024);
|
||||
});
|
||||
});
|
||||
|
||||
describe("matrixMediaConfig", () => {
|
||||
it("is null without a token and reads the fallback homeserver", () => {
|
||||
const previous = { ...process.env };
|
||||
delete process.env.MATRIX_MEDIA_TOKEN;
|
||||
expect(matrixMediaConfig()).toBeNull();
|
||||
process.env.MATRIX_MEDIA_TOKEN = "tok";
|
||||
delete process.env.MATRIX_MEDIA_HOMESERVER_URL;
|
||||
process.env.ARCHIVE_PROVISIONER_HOMESERVER_URL = "https://chat.example/";
|
||||
expect(matrixMediaConfig()).toEqual({ homeserver: "https://chat.example", token: "tok" });
|
||||
process.env.MATRIX_MEDIA_HOMESERVER_URL = "https://media.example";
|
||||
expect(matrixMediaConfig()?.homeserver).toBe("https://media.example");
|
||||
process.env = previous;
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue