@ttt-productions/media-processing-core 0.0.4 → 0.0.5
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/server/build-download-url.d.ts +2 -0
- package/dist/server/build-download-url.d.ts.map +1 -0
- package/dist/server/build-download-url.js +20 -0
- package/dist/server/build-download-url.js.map +1 -0
- package/dist/server/firebase-media-io.d.ts.map +1 -1
- package/dist/server/firebase-media-io.js +11 -46
- package/dist/server/firebase-media-io.js.map +1 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/storage-ops.d.ts +47 -0
- package/dist/server/storage-ops.d.ts.map +1 -0
- package/dist/server/storage-ops.js +103 -0
- package/dist/server/storage-ops.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -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":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG9C,MAAM,WAAW,yBAAyB;IACxC,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAwB9E"}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
// Firebase Storage MediaIO adapter for the media-processing pipeline.
|
|
2
|
-
//
|
|
2
|
+
// Public signature is unchanged from earlier ttt-prod implementations,
|
|
3
|
+
// so existing pipeline consumers don't need to update call shapes — only
|
|
4
|
+
// the import path moves to `@ttt-productions/media-processing-core/server`.
|
|
3
5
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// auto-sets it when the Storage emulator is running), URLs target the
|
|
7
|
-
// local emulator; otherwise they target the production Firebase Storage
|
|
8
|
-
// CDN. This is critical for E2E tests — without it, the browser fetches
|
|
9
|
-
// production URLs that 404 because the file lives in the local emulator.
|
|
6
|
+
// Internals delegate to `uploadFileToStorage` so URL construction and
|
|
7
|
+
// token attachment have a single source of truth.
|
|
10
8
|
import { getStorage } from "firebase-admin/storage";
|
|
11
|
-
import {
|
|
9
|
+
import { uploadFileToStorage } from "./storage-ops.js";
|
|
12
10
|
export function createFirebaseMediaIO(args) {
|
|
13
11
|
const bucket = getStorage().bucket();
|
|
14
12
|
return {
|
|
@@ -21,47 +19,14 @@ export function createFirebaseMediaIO(args) {
|
|
|
21
19
|
async writeFromFile(localPath, outputKey) {
|
|
22
20
|
const ext = localPath.split(".").pop() || "";
|
|
23
21
|
const destinationPath = `${args.outputStorageBasePath}/${outputKey}.${ext}`;
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
contentType: getMimeFromExt(ext),
|
|
29
|
-
cacheControl: "public, max-age=31536000",
|
|
30
|
-
metadata: { firebaseStorageDownloadTokens: downloadToken },
|
|
31
|
-
},
|
|
22
|
+
const result = await uploadFileToStorage({
|
|
23
|
+
bucket,
|
|
24
|
+
localPath,
|
|
25
|
+
destinationPath,
|
|
32
26
|
});
|
|
33
|
-
|
|
34
|
-
const emulatorHost = process.env.FIREBASE_STORAGE_EMULATOR_HOST;
|
|
35
|
-
const baseUrl = emulatorHost
|
|
36
|
-
? `http://${emulatorHost}`
|
|
37
|
-
: "https://firebasestorage.googleapis.com";
|
|
38
|
-
const url = `${baseUrl}/v0/b/${bucket.name}/o/${encodedPath}?alt=media&token=${downloadToken}`;
|
|
39
|
-
return { url, path: destinationPath };
|
|
27
|
+
return { url: result.url, path: result.path };
|
|
40
28
|
},
|
|
41
29
|
},
|
|
42
30
|
};
|
|
43
31
|
}
|
|
44
|
-
function getMimeFromExt(ext) {
|
|
45
|
-
switch (ext.toLowerCase()) {
|
|
46
|
-
case "jpg":
|
|
47
|
-
case "jpeg":
|
|
48
|
-
return "image/jpeg";
|
|
49
|
-
case "png":
|
|
50
|
-
return "image/png";
|
|
51
|
-
case "webp":
|
|
52
|
-
return "image/webp";
|
|
53
|
-
case "mp4":
|
|
54
|
-
return "video/mp4";
|
|
55
|
-
case "webm":
|
|
56
|
-
return "video/webm";
|
|
57
|
-
case "m4a":
|
|
58
|
-
return "audio/mp4";
|
|
59
|
-
case "aac":
|
|
60
|
-
return "audio/aac";
|
|
61
|
-
case "mp3":
|
|
62
|
-
return "audio/mpeg";
|
|
63
|
-
default:
|
|
64
|
-
return "application/octet-stream";
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
32
|
//# sourceMappingURL=firebase-media-io.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firebase-media-io.js","sourceRoot":"","sources":["../../src/server/firebase-media-io.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,
|
|
1
|
+
{"version":3,"file":"firebase-media-io.js","sourceRoot":"","sources":["../../src/server/firebase-media-io.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,uEAAuE;AACvE,yEAAyE;AACzE,4EAA4E;AAC5E,EAAE;AACF,sEAAsE;AACtE,kDAAkD;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAOvD,MAAM,UAAU,qBAAqB,CAAC,IAA+B;IACnE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,MAAM,EAAE,CAAC;IAErC,OAAO;QACL,KAAK,EAAE;YACL,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,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;gBAC7C,MAAM,eAAe,GAAG,GAAG,IAAI,CAAC,qBAAqB,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;gBAE5E,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC;oBACvC,MAAM;oBACN,SAAS;oBACT,eAAe;iBAChB,CAAC,CAAC;gBAEH,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;YAChD,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,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,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,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { getStorage } from "firebase-admin/storage";
|
|
2
|
+
/** Firebase Admin Storage Bucket — inferred to avoid @google-cloud/storage dep. */
|
|
3
|
+
export type Bucket = ReturnType<ReturnType<typeof getStorage>["bucket"]>;
|
|
4
|
+
export interface StorageWriteResult {
|
|
5
|
+
url: string;
|
|
6
|
+
path: string;
|
|
7
|
+
token: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Upload a local file to Storage. Generates a download token, attaches it
|
|
11
|
+
* to the file's metadata, and returns the emulator-aware download URL.
|
|
12
|
+
*
|
|
13
|
+
* Use this for: pipeline outputs (sharp, ffmpeg) and any other "local file
|
|
14
|
+
* → Storage" flow.
|
|
15
|
+
*/
|
|
16
|
+
export declare function uploadFileToStorage(args: {
|
|
17
|
+
bucket: Bucket;
|
|
18
|
+
localPath: string;
|
|
19
|
+
destinationPath: string;
|
|
20
|
+
contentType?: string;
|
|
21
|
+
cacheControl?: string;
|
|
22
|
+
}): Promise<StorageWriteResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Move or copy a file already in Storage to a new location. Generates a
|
|
25
|
+
* fresh download token at the destination, attaches it, and returns the
|
|
26
|
+
* emulator-aware download URL.
|
|
27
|
+
*
|
|
28
|
+
* Use this for: library publish (copies from project to library), streetz
|
|
29
|
+
* non-media file moves, job-posting / job-reply / chat-attachment
|
|
30
|
+
* relocations, and any other "Storage → Storage" flow.
|
|
31
|
+
*/
|
|
32
|
+
export declare function relocateStorageFile(args: {
|
|
33
|
+
bucket: Bucket;
|
|
34
|
+
fromPath: string;
|
|
35
|
+
toPath: string;
|
|
36
|
+
mode: "move" | "copy";
|
|
37
|
+
}): Promise<StorageWriteResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Attach a fresh download token to a file already at its final Storage
|
|
40
|
+
* location. Use when the file is already in place and only needs a token
|
|
41
|
+
* (e.g., content-moderation rejection where the file was renamed earlier).
|
|
42
|
+
*/
|
|
43
|
+
export declare function attachDownloadToken(args: {
|
|
44
|
+
bucket: Bucket;
|
|
45
|
+
storagePath: string;
|
|
46
|
+
}): Promise<StorageWriteResult>;
|
|
47
|
+
//# sourceMappingURL=storage-ops.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-ops.d.ts","sourceRoot":"","sources":["../../src/server/storage-ops.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,mFAAmF;AACnF,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEzE,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAID;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAmB9B;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAkB9B;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAY9B"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Storage write operations — chokepoint for every file-storage side-effect
|
|
2
|
+
// in the system. Inline `bucket.upload()`, `bucket.file().move()`,
|
|
3
|
+
// `bucket.file().copy()`, and `setMetadata({ firebaseStorageDownloadTokens })`
|
|
4
|
+
// calls are forbidden outside this module.
|
|
5
|
+
//
|
|
6
|
+
// Every operation generates a fresh download token, attaches it to the file's
|
|
7
|
+
// metadata, and returns a `StorageWriteResult` with the emulator-aware URL.
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
import { buildFirebaseDownloadUrl } from "./build-download-url.js";
|
|
10
|
+
const DEFAULT_CACHE_CONTROL = "public, max-age=31536000";
|
|
11
|
+
/**
|
|
12
|
+
* Upload a local file to Storage. Generates a download token, attaches it
|
|
13
|
+
* to the file's metadata, and returns the emulator-aware download URL.
|
|
14
|
+
*
|
|
15
|
+
* Use this for: pipeline outputs (sharp, ffmpeg) and any other "local file
|
|
16
|
+
* → Storage" flow.
|
|
17
|
+
*/
|
|
18
|
+
export async function uploadFileToStorage(args) {
|
|
19
|
+
const token = randomUUID();
|
|
20
|
+
const contentType = args.contentType ?? getMimeFromExt(extractExt(args.localPath));
|
|
21
|
+
await args.bucket.upload(args.localPath, {
|
|
22
|
+
destination: args.destinationPath,
|
|
23
|
+
metadata: {
|
|
24
|
+
contentType,
|
|
25
|
+
cacheControl: args.cacheControl ?? DEFAULT_CACHE_CONTROL,
|
|
26
|
+
metadata: { firebaseStorageDownloadTokens: token },
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
url: buildFirebaseDownloadUrl(args.bucket.name, args.destinationPath, token),
|
|
31
|
+
path: args.destinationPath,
|
|
32
|
+
token,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Move or copy a file already in Storage to a new location. Generates a
|
|
37
|
+
* fresh download token at the destination, attaches it, and returns the
|
|
38
|
+
* emulator-aware download URL.
|
|
39
|
+
*
|
|
40
|
+
* Use this for: library publish (copies from project to library), streetz
|
|
41
|
+
* non-media file moves, job-posting / job-reply / chat-attachment
|
|
42
|
+
* relocations, and any other "Storage → Storage" flow.
|
|
43
|
+
*/
|
|
44
|
+
export async function relocateStorageFile(args) {
|
|
45
|
+
const token = randomUUID();
|
|
46
|
+
if (args.mode === "move") {
|
|
47
|
+
await args.bucket.file(args.fromPath).move(args.toPath);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
await args.bucket.file(args.fromPath).copy(args.bucket.file(args.toPath));
|
|
51
|
+
}
|
|
52
|
+
await args.bucket.file(args.toPath).setMetadata({
|
|
53
|
+
metadata: { firebaseStorageDownloadTokens: token },
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
url: buildFirebaseDownloadUrl(args.bucket.name, args.toPath, token),
|
|
57
|
+
path: args.toPath,
|
|
58
|
+
token,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Attach a fresh download token to a file already at its final Storage
|
|
63
|
+
* location. Use when the file is already in place and only needs a token
|
|
64
|
+
* (e.g., content-moderation rejection where the file was renamed earlier).
|
|
65
|
+
*/
|
|
66
|
+
export async function attachDownloadToken(args) {
|
|
67
|
+
const token = randomUUID();
|
|
68
|
+
await args.bucket.file(args.storagePath).setMetadata({
|
|
69
|
+
metadata: { firebaseStorageDownloadTokens: token },
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
url: buildFirebaseDownloadUrl(args.bucket.name, args.storagePath, token),
|
|
73
|
+
path: args.storagePath,
|
|
74
|
+
token,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function extractExt(localPath) {
|
|
78
|
+
return localPath.split(".").pop() || "";
|
|
79
|
+
}
|
|
80
|
+
function getMimeFromExt(ext) {
|
|
81
|
+
switch (ext.toLowerCase()) {
|
|
82
|
+
case "jpg":
|
|
83
|
+
case "jpeg":
|
|
84
|
+
return "image/jpeg";
|
|
85
|
+
case "png":
|
|
86
|
+
return "image/png";
|
|
87
|
+
case "webp":
|
|
88
|
+
return "image/webp";
|
|
89
|
+
case "mp4":
|
|
90
|
+
return "video/mp4";
|
|
91
|
+
case "webm":
|
|
92
|
+
return "video/webm";
|
|
93
|
+
case "m4a":
|
|
94
|
+
return "audio/mp4";
|
|
95
|
+
case "aac":
|
|
96
|
+
return "audio/aac";
|
|
97
|
+
case "mp3":
|
|
98
|
+
return "audio/mpeg";
|
|
99
|
+
default:
|
|
100
|
+
return "application/octet-stream";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=storage-ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-ops.js","sourceRoot":"","sources":["../../src/server/storage-ops.ts"],"names":[],"mappings":"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"}
|