@ttt-productions/media-processing-core 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/run-pipeline.d.ts.map +1 -1
- package/dist/run-pipeline.js +23 -20
- package/dist/run-pipeline.js.map +1 -1
- package/dist/server/firebase-media-io.d.ts +10 -3
- package/dist/server/firebase-media-io.d.ts.map +1 -1
- package/dist/server/firebase-media-io.js +10 -14
- package/dist/server/firebase-media-io.js.map +1 -1
- package/dist/server/index.d.ts +0 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +0 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/storage-ops.d.ts +39 -39
- package/dist/server/storage-ops.d.ts.map +1 -1
- package/dist/server/storage-ops.js +105 -64
- package/dist/server/storage-ops.js.map +1 -1
- package/package.json +3 -2
- package/dist/server/build-download-url.d.ts +0 -2
- package/dist/server/build-download-url.d.ts.map +0 -1
- package/dist/server/build-download-url.js +0 -20
- package/dist/server/build-download-url.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-pipeline.d.ts","sourceRoot":"","sources":["../src/run-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,qBAAqB,EACrB,mBAAmB,EAEpB,MAAM,gCAAgC,CAAC;AAIxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAK/D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,iBAAiB,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,YAAY,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACpE;AAiBD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC,
|
|
1
|
+
{"version":3,"file":"run-pipeline.d.ts","sourceRoot":"","sources":["../src/run-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,qBAAqB,EACrB,mBAAmB,EAEpB,MAAM,gCAAgC,CAAC;AAIxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAK/D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,iBAAiB,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,YAAY,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACpE;AAiBD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAiL5F"}
|
package/dist/run-pipeline.js
CHANGED
|
@@ -125,26 +125,10 @@ export async function runMediaPipeline(args) {
|
|
|
125
125
|
onProgress?.({ phase: "done", percent: result.ok ? 1 : 0 });
|
|
126
126
|
return result;
|
|
127
127
|
}
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
for (const out of result.outputs) {
|
|
133
|
-
if (signal?.aborted) {
|
|
134
|
-
return { ok: false, mediaType: result.mediaType, error: { code: "processing_canceled", message: "Processing canceled." } };
|
|
135
|
-
}
|
|
136
|
-
onProgress?.({ phase: "persist_outputs", percent: totalOut ? outIdx / totalOut : 0, detail: { key: out.key } });
|
|
137
|
-
if (!out.path)
|
|
138
|
-
continue;
|
|
139
|
-
const persisted = await io.output.writeFromFile(out.path, out.key);
|
|
140
|
-
if (persisted.url)
|
|
141
|
-
out.url = persisted.url;
|
|
142
|
-
if (persisted.path)
|
|
143
|
-
out.path = persisted.path;
|
|
144
|
-
outIdx += 1;
|
|
145
|
-
}
|
|
146
|
-
onProgress?.({ phase: "persist_outputs", percent: 1 });
|
|
147
|
-
// POST moderation (processed outputs)
|
|
128
|
+
// POST moderation (processed outputs) — runs on the LOCAL output paths
|
|
129
|
+
// BEFORE any final persistence, so rejected output never reaches the
|
|
130
|
+
// final store (R2 / emulator). Ordering is load-bearing — see
|
|
131
|
+
// ttt-prod docs/design/media-assets-and-protected-serving.md.
|
|
148
132
|
let mOut;
|
|
149
133
|
if (moderation?.moderateOutput) {
|
|
150
134
|
onProgress?.({ phase: "moderation_output", percent: 0 });
|
|
@@ -169,6 +153,25 @@ export async function runMediaPipeline(args) {
|
|
|
169
153
|
};
|
|
170
154
|
}
|
|
171
155
|
}
|
|
156
|
+
// Persist outputs — only after input AND output moderation both passed.
|
|
157
|
+
onProgress?.({ phase: "persist_outputs", percent: 0 });
|
|
158
|
+
const totalOut = result.outputs.length;
|
|
159
|
+
let outIdx = 0;
|
|
160
|
+
for (const out of result.outputs) {
|
|
161
|
+
if (signal?.aborted) {
|
|
162
|
+
return { ok: false, mediaType: result.mediaType, error: { code: "processing_canceled", message: "Processing canceled." } };
|
|
163
|
+
}
|
|
164
|
+
onProgress?.({ phase: "persist_outputs", percent: totalOut ? outIdx / totalOut : 0, detail: { key: out.key } });
|
|
165
|
+
if (!out.path)
|
|
166
|
+
continue;
|
|
167
|
+
const persisted = await io.output.writeFromFile(out.path, out.key);
|
|
168
|
+
if (persisted.url)
|
|
169
|
+
out.url = persisted.url;
|
|
170
|
+
if (persisted.path)
|
|
171
|
+
out.path = persisted.path;
|
|
172
|
+
outIdx += 1;
|
|
173
|
+
}
|
|
174
|
+
onProgress?.({ phase: "persist_outputs", percent: 1 });
|
|
172
175
|
const merged = mergeModeration(mIn ?? undefined, mOut ?? undefined);
|
|
173
176
|
if (merged)
|
|
174
177
|
result.moderation = merged;
|
package/dist/run-pipeline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-pipeline.js","sourceRoot":"","sources":["../src/run-pipeline.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"run-pipeline.js","sourceRoot":"","sources":["../src/run-pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { MediaIO } from "../io/types.js";
|
|
2
|
-
|
|
2
|
+
import type { MediaObjectStore } from "./storage-ops.js";
|
|
3
|
+
export interface CreateObjectStoreMediaIOArgs {
|
|
4
|
+
/** Firebase Storage staging path of the uploaded source file. */
|
|
3
5
|
inputStoragePath: string;
|
|
4
|
-
|
|
6
|
+
/** Store receiving the processed outputs (R2 or emulator). */
|
|
7
|
+
outputStore: MediaObjectStore;
|
|
8
|
+
/** Key prefix for outputs, e.g. `mediaAssets/{mediaAssetId}`. */
|
|
9
|
+
outputKeyPrefix: string;
|
|
10
|
+
/** Optional mime hint for the input. */
|
|
11
|
+
inputMime?: string;
|
|
5
12
|
}
|
|
6
|
-
export declare function
|
|
13
|
+
export declare function createObjectStoreMediaIO(args: CreateObjectStoreMediaIOArgs): MediaIO;
|
|
7
14
|
//# sourceMappingURL=firebase-media-io.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firebase-media-io.d.ts","sourceRoot":"","sources":["../../src/server/firebase-media-io.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"firebase-media-io.d.ts","sourceRoot":"","sources":["../../src/server/firebase-media-io.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,MAAM,WAAW,4BAA4B;IAC3C,iEAAiE;IACjE,gBAAgB,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,WAAW,EAAE,gBAAgB,CAAC;IAC9B,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,4BAA4B,GAAG,OAAO,CAkBpF"}
|
|
@@ -1,27 +1,23 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// MediaIO adapter: reads the staged upload from Firebase Storage, writes
|
|
2
|
+
// processed outputs through a MediaObjectStore (R2 in deployed envs, the
|
|
3
|
+
// Storage emulator locally). Output keys are extension-less:
|
|
4
|
+
// `${outputKeyPrefix}/${outputKey}` — meaning lives in the asset registry,
|
|
5
|
+
// not the key. Writes return the object key; no URLs anywhere.
|
|
5
6
|
import { getStorage } from "firebase-admin/storage";
|
|
6
|
-
|
|
7
|
-
export function createFirebaseMediaIO(args) {
|
|
7
|
+
export function createObjectStoreMediaIO(args) {
|
|
8
8
|
const bucket = getStorage().bucket();
|
|
9
9
|
return {
|
|
10
10
|
input: {
|
|
11
|
+
mime: args.inputMime,
|
|
11
12
|
async readToFile(localPath) {
|
|
12
13
|
await bucket.file(args.inputStoragePath).download({ destination: localPath });
|
|
13
14
|
},
|
|
14
15
|
},
|
|
15
16
|
output: {
|
|
16
17
|
async writeFromFile(localPath, outputKey) {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
bucket,
|
|
21
|
-
localPath,
|
|
22
|
-
destinationPath,
|
|
23
|
-
});
|
|
24
|
-
return { url: result.url, path: result.path };
|
|
18
|
+
const key = `${args.outputKeyPrefix}/${outputKey}`;
|
|
19
|
+
const result = await args.outputStore.putFile({ localPath, key });
|
|
20
|
+
return { path: result.key };
|
|
25
21
|
},
|
|
26
22
|
},
|
|
27
23
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firebase-media-io.js","sourceRoot":"","sources":["../../src/server/firebase-media-io.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"firebase-media-io.js","sourceRoot":"","sources":["../../src/server/firebase-media-io.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,yEAAyE;AACzE,6DAA6D;AAC7D,2EAA2E;AAC3E,+DAA+D;AAE/D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAepD,MAAM,UAAU,wBAAwB,CAAC,IAAkC;IACzE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,MAAM,EAAE,CAAC;IAErC,OAAO;QACL,KAAK,EAAE;YACL,IAAI,EAAE,IAAI,CAAC,SAAS;YACpB,KAAK,CAAC,UAAU,CAAC,SAAiB;gBAChC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC;YAChF,CAAC;SACF;QACD,MAAM,EAAE;YACN,KAAK,CAAC,aAAa,CAAC,SAAiB,EAAE,SAAiB;gBACtD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,eAAe,IAAI,SAAS,EAAE,CAAC;gBACnD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;gBAClE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;YAC9B,CAAC;SACF;KACF,CAAC;AACJ,CAAC"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC"}
|
package/dist/server/index.js
CHANGED
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC"}
|
|
@@ -1,45 +1,45 @@
|
|
|
1
1
|
import type { getStorage } from "firebase-admin/storage";
|
|
2
2
|
/** Firebase Admin Storage Bucket — inferred to avoid @google-cloud/storage dep. */
|
|
3
3
|
export type Bucket = ReturnType<ReturnType<typeof getStorage>["bucket"]>;
|
|
4
|
-
export interface
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
export interface ObjectWriteResult {
|
|
5
|
+
key: string;
|
|
6
|
+
sizeBytes: number;
|
|
7
|
+
contentType: string;
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* location. Use when the file is already in place and only needs a token
|
|
39
|
-
* (e.g., content-moderation rejection where the file was renamed earlier).
|
|
40
|
-
*/
|
|
41
|
-
export declare function attachDownloadToken(args: {
|
|
9
|
+
export interface MediaObjectStore {
|
|
10
|
+
/** Upload a local file to the store at `key`. */
|
|
11
|
+
putFile(args: {
|
|
12
|
+
localPath: string;
|
|
13
|
+
key: string;
|
|
14
|
+
contentType?: string;
|
|
15
|
+
}): Promise<ObjectWriteResult>;
|
|
16
|
+
/** Server-side copy within the store. */
|
|
17
|
+
copy(args: {
|
|
18
|
+
fromKey: string;
|
|
19
|
+
toKey: string;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
key: string;
|
|
22
|
+
}>;
|
|
23
|
+
/** Delete an object. Missing objects are a no-op, not an error. */
|
|
24
|
+
delete(key: string): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export interface CreateR2ObjectStoreArgs {
|
|
27
|
+
accountId: string;
|
|
28
|
+
accessKeyId: string;
|
|
29
|
+
secretAccessKey: string;
|
|
30
|
+
bucket: string;
|
|
31
|
+
/** Override for tests; defaults to the real R2 S3 endpoint. */
|
|
32
|
+
endpoint?: string;
|
|
33
|
+
/** Override fetch for tests. */
|
|
34
|
+
fetchImpl?: typeof fetch;
|
|
35
|
+
}
|
|
36
|
+
export declare function createR2ObjectStore(args: CreateR2ObjectStoreArgs): MediaObjectStore;
|
|
37
|
+
export interface CreateEmulatorObjectStoreArgs {
|
|
42
38
|
bucket: Bucket;
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
/** Fixed token attached to every object so deterministic emulator URLs work
|
|
40
|
+
* (token URLs bypass rules in the emulator). Pass the app's constant. */
|
|
41
|
+
downloadToken: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function createFirebaseEmulatorObjectStore(args: CreateEmulatorObjectStoreArgs): MediaObjectStore;
|
|
44
|
+
export declare function getMimeFromExt(ext: string): string;
|
|
45
45
|
//# sourceMappingURL=storage-ops.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-ops.d.ts","sourceRoot":"","sources":["../../src/server/storage-ops.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"storage-ops.d.ts","sourceRoot":"","sources":["../../src/server/storage-ops.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,mFAAmF;AACnF,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEzE,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,OAAO,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACpG,yCAAyC;IACzC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzE,mEAAmE;IACnE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAQD,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,uBAAuB,GAAG,gBAAgB,CA0DnF;AAcD,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf;6EACyE;IACzE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,6BAA6B,GAAG,gBAAgB,CAkCvG;AAQD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAsBlD"}
|
|
@@ -1,81 +1,122 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// Final-media object-store chokepoint — every final-media write/copy/delete in
|
|
2
|
+
// the system goes through a MediaObjectStore. Inline storage-provider calls for
|
|
3
|
+
// FINAL media are forbidden outside this module. (Upload STAGING stays plain
|
|
4
|
+
// Firebase Storage and is not this module's concern.)
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
metadata: { firebaseStorageDownloadTokens: token },
|
|
27
|
-
},
|
|
6
|
+
// There are two implementations:
|
|
7
|
+
// - R2 (S3-compatible) for deployed environments — the only place final user
|
|
8
|
+
// media lives in prod/dev; bytes are served exclusively by the gateway Worker.
|
|
9
|
+
// - Firebase Storage emulator for local dev/tests — objects get a FIXED
|
|
10
|
+
// caller-supplied download token so deterministic emulator URLs work with
|
|
11
|
+
// zero Cloudflare involvement. Production NEVER mints download tokens.
|
|
12
|
+
//
|
|
13
|
+
// Writes return `{ key }` — never URLs. Display URLs are built at render time
|
|
14
|
+
// from asset refs (see ttt-core media-asset-url).
|
|
15
|
+
import { createReadStream } from "node:fs";
|
|
16
|
+
import { stat } from "node:fs/promises";
|
|
17
|
+
import { AwsClient } from "aws4fetch";
|
|
18
|
+
const DEFAULT_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
19
|
+
export function createR2ObjectStore(args) {
|
|
20
|
+
const endpoint = (args.endpoint ?? `https://${args.accountId}.r2.cloudflarestorage.com`).replace(/\/$/, "");
|
|
21
|
+
const client = new AwsClient({
|
|
22
|
+
accessKeyId: args.accessKeyId,
|
|
23
|
+
secretAccessKey: args.secretAccessKey,
|
|
24
|
+
service: "s3",
|
|
25
|
+
region: "auto",
|
|
28
26
|
});
|
|
27
|
+
const doFetch = args.fetchImpl ?? fetch;
|
|
28
|
+
const objectUrl = (key) => `${endpoint}/${args.bucket}/${key.split("/").map(encodeURIComponent).join("/")}`;
|
|
29
|
+
async function signedFetch(url, init) {
|
|
30
|
+
const signed = await client.sign(url, { ...init, aws: { allHeaders: true } });
|
|
31
|
+
return doFetch(signed, { ...(init.body ? { duplex: "half" } : {}) });
|
|
32
|
+
}
|
|
29
33
|
return {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
async putFile({ localPath, key, contentType }) {
|
|
35
|
+
const { size } = await stat(localPath);
|
|
36
|
+
const ct = contentType ?? getMimeFromExt(extractExt(localPath));
|
|
37
|
+
const res = await signedFetch(objectUrl(key), {
|
|
38
|
+
method: "PUT",
|
|
39
|
+
headers: {
|
|
40
|
+
"content-type": ct,
|
|
41
|
+
"content-length": String(size),
|
|
42
|
+
"cache-control": DEFAULT_CACHE_CONTROL,
|
|
43
|
+
},
|
|
44
|
+
body: createReadStream(localPath),
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
throw new Error(`R2 putFile failed for ${key}: ${res.status} ${await safeText(res)}`);
|
|
48
|
+
}
|
|
49
|
+
return { key, sizeBytes: size, contentType: ct };
|
|
50
|
+
},
|
|
51
|
+
async copy({ fromKey, toKey }) {
|
|
52
|
+
const res = await signedFetch(objectUrl(toKey), {
|
|
53
|
+
method: "PUT",
|
|
54
|
+
headers: {
|
|
55
|
+
"x-amz-copy-source": `/${args.bucket}/${fromKey.split("/").map(encodeURIComponent).join("/")}`,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
throw new Error(`R2 copy failed ${fromKey} -> ${toKey}: ${res.status} ${await safeText(res)}`);
|
|
60
|
+
}
|
|
61
|
+
return { key: toKey };
|
|
62
|
+
},
|
|
63
|
+
async delete(key) {
|
|
64
|
+
const res = await signedFetch(objectUrl(key), { method: "DELETE" });
|
|
65
|
+
// S3 DELETE returns 204 even for missing keys; anything else non-ok is real.
|
|
66
|
+
if (!res.ok && res.status !== 404) {
|
|
67
|
+
throw new Error(`R2 delete failed for ${key}: ${res.status} ${await safeText(res)}`);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
33
70
|
};
|
|
34
71
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* emulator-aware download URL.
|
|
39
|
-
*
|
|
40
|
-
* Use this for any "Storage → Storage" relocation flow.
|
|
41
|
-
*/
|
|
42
|
-
export async function relocateStorageFile(args) {
|
|
43
|
-
const token = randomUUID();
|
|
44
|
-
if (args.mode === "move") {
|
|
45
|
-
await args.bucket.file(args.fromPath).move(args.toPath);
|
|
72
|
+
async function safeText(res) {
|
|
73
|
+
try {
|
|
74
|
+
return (await res.text()).slice(0, 500);
|
|
46
75
|
}
|
|
47
|
-
|
|
48
|
-
|
|
76
|
+
catch {
|
|
77
|
+
return "<no body>";
|
|
49
78
|
}
|
|
50
|
-
await args.bucket.file(args.toPath).setMetadata({
|
|
51
|
-
metadata: { firebaseStorageDownloadTokens: token },
|
|
52
|
-
});
|
|
53
|
-
return {
|
|
54
|
-
url: buildFirebaseDownloadUrl(args.bucket.name, args.toPath, token),
|
|
55
|
-
path: args.toPath,
|
|
56
|
-
token,
|
|
57
|
-
};
|
|
58
79
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
* location. Use when the file is already in place and only needs a token
|
|
62
|
-
* (e.g., content-moderation rejection where the file was renamed earlier).
|
|
63
|
-
*/
|
|
64
|
-
export async function attachDownloadToken(args) {
|
|
65
|
-
const token = randomUUID();
|
|
66
|
-
await args.bucket.file(args.storagePath).setMetadata({
|
|
67
|
-
metadata: { firebaseStorageDownloadTokens: token },
|
|
68
|
-
});
|
|
80
|
+
export function createFirebaseEmulatorObjectStore(args) {
|
|
81
|
+
const { bucket, downloadToken } = args;
|
|
69
82
|
return {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
83
|
+
async putFile({ localPath, key, contentType }) {
|
|
84
|
+
const { size } = await stat(localPath);
|
|
85
|
+
const ct = contentType ?? getMimeFromExt(extractExt(localPath));
|
|
86
|
+
await bucket.upload(localPath, {
|
|
87
|
+
destination: key,
|
|
88
|
+
metadata: {
|
|
89
|
+
contentType: ct,
|
|
90
|
+
cacheControl: DEFAULT_CACHE_CONTROL,
|
|
91
|
+
metadata: { firebaseStorageDownloadTokens: downloadToken },
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
return { key, sizeBytes: size, contentType: ct };
|
|
95
|
+
},
|
|
96
|
+
async copy({ fromKey, toKey }) {
|
|
97
|
+
await bucket.file(fromKey).copy(bucket.file(toKey));
|
|
98
|
+
await bucket.file(toKey).setMetadata({
|
|
99
|
+
metadata: { firebaseStorageDownloadTokens: downloadToken },
|
|
100
|
+
});
|
|
101
|
+
return { key: toKey };
|
|
102
|
+
},
|
|
103
|
+
async delete(key) {
|
|
104
|
+
try {
|
|
105
|
+
await bucket.file(key).delete();
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
if (e?.code === 404 || /No such object/i.test(String(e?.message)))
|
|
109
|
+
return;
|
|
110
|
+
throw e;
|
|
111
|
+
}
|
|
112
|
+
},
|
|
73
113
|
};
|
|
74
114
|
}
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
75
116
|
function extractExt(localPath) {
|
|
76
117
|
return localPath.split(".").pop() || "";
|
|
77
118
|
}
|
|
78
|
-
function getMimeFromExt(ext) {
|
|
119
|
+
export function getMimeFromExt(ext) {
|
|
79
120
|
switch (ext.toLowerCase()) {
|
|
80
121
|
case "jpg":
|
|
81
122
|
case "jpeg":
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-ops.js","sourceRoot":"","sources":["../../src/server/storage-ops.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"storage-ops.js","sourceRoot":"","sources":["../../src/server/storage-ops.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttt-productions/media-processing-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/ttt-productions/ttt-packages.git",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"prepublishOnly": "npm run clean && npm run build"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ttt-productions/media-schemas": "^0.
|
|
34
|
+
"@ttt-productions/media-schemas": "^0.11.0",
|
|
35
|
+
"aws4fetch": "^1.0.20",
|
|
35
36
|
"sharp": "^0.34.1"
|
|
36
37
|
},
|
|
37
38
|
"peerDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-download-url.d.ts","sourceRoot":"","sources":["../../src/server/build-download-url.ts"],"names":[],"mappings":"AAYA,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GACpB,MAAM,CAOR"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// Single source of truth for Firebase Storage download URL construction.
|
|
2
|
-
//
|
|
3
|
-
// Emulator awareness: when `FIREBASE_STORAGE_EMULATOR_HOST` is set (the
|
|
4
|
-
// Firebase Admin SDK auto-sets this when the Storage emulator is running),
|
|
5
|
-
// constructed URLs target the local emulator. Otherwise they target the
|
|
6
|
-
// production Firebase Storage CDN. This is critical for E2E tests — without
|
|
7
|
-
// it, browsers running against the emulator fetch production URLs and 404.
|
|
8
|
-
//
|
|
9
|
-
// Every download URL produced anywhere in the system MUST come from this
|
|
10
|
-
// function. Inline construction (`https://firebasestorage.googleapis.com/...`)
|
|
11
|
-
// is forbidden — it bypasses the emulator branch and breaks E2E tests.
|
|
12
|
-
export function buildFirebaseDownloadUrl(bucketName, storagePath, downloadToken) {
|
|
13
|
-
const encodedPath = encodeURIComponent(storagePath);
|
|
14
|
-
const emulatorHost = process.env.FIREBASE_STORAGE_EMULATOR_HOST;
|
|
15
|
-
const baseUrl = emulatorHost
|
|
16
|
-
? `http://${emulatorHost}`
|
|
17
|
-
: "https://firebasestorage.googleapis.com";
|
|
18
|
-
return `${baseUrl}/v0/b/${bucketName}/o/${encodedPath}?alt=media&token=${downloadToken}`;
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=build-download-url.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-download-url.js","sourceRoot":"","sources":["../../src/server/build-download-url.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,wEAAwE;AACxE,2EAA2E;AAC3E,wEAAwE;AACxE,4EAA4E;AAC5E,2EAA2E;AAC3E,EAAE;AACF,yEAAyE;AACzE,+EAA+E;AAC/E,uEAAuE;AAEvE,MAAM,UAAU,wBAAwB,CACtC,UAAkB,EAClB,WAAmB,EACnB,aAAqB;IAErB,MAAM,WAAW,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC;IAChE,MAAM,OAAO,GAAG,YAAY;QAC1B,CAAC,CAAC,UAAU,YAAY,EAAE;QAC1B,CAAC,CAAC,wCAAwC,CAAC;IAC7C,OAAO,GAAG,OAAO,SAAS,UAAU,MAAM,WAAW,oBAAoB,aAAa,EAAE,CAAC;AAC3F,CAAC"}
|