@maas/payload-plugin-media-cloud 0.0.10 → 0.0.14

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.
Files changed (54) hide show
  1. package/dist/adapter/handleDelete.mjs +31 -24
  2. package/dist/adapter/handleDelete.mjs.map +1 -1
  3. package/dist/adapter/handleUpload.mjs +1 -1
  4. package/dist/adapter/handleUpload.mjs.map +1 -1
  5. package/dist/collections/mediaCollection.d.mts +1 -0
  6. package/dist/collections/mediaCollection.mjs +8 -2
  7. package/dist/collections/mediaCollection.mjs.map +1 -1
  8. package/dist/components/upload-handler/upload-handler.mjs +31 -33
  9. package/dist/components/upload-handler/upload-handler.mjs.map +1 -1
  10. package/dist/components/upload-manager/upload-manager.d.mts +4 -14
  11. package/dist/components/upload-manager/upload-manager.mjs +35 -36
  12. package/dist/components/upload-manager/upload-manager.mjs.map +1 -1
  13. package/dist/endpoints/muxAssetHandler.mjs +13 -9
  14. package/dist/endpoints/muxAssetHandler.mjs.map +1 -1
  15. package/dist/endpoints/muxCreateUploadHandler.mjs +1 -1
  16. package/dist/endpoints/muxCreateUploadHandler.mjs.map +1 -1
  17. package/dist/endpoints/muxWebhookHandler.mjs +46 -20
  18. package/dist/endpoints/muxWebhookHandler.mjs.map +1 -1
  19. package/dist/endpoints/tusPostProcessorHandler.mjs +7 -9
  20. package/dist/endpoints/tusPostProcessorHandler.mjs.map +1 -1
  21. package/dist/error-handler/dist/index.mjs +84 -64
  22. package/dist/error-handler/dist/index.mjs.map +1 -1
  23. package/dist/hooks/useErrorHandler.d.mts +2 -172
  24. package/dist/hooks/useErrorHandler.mjs +4 -9
  25. package/dist/hooks/useErrorHandler.mjs.map +1 -1
  26. package/dist/hooks/useMediaCloudEmitter.d.mts +26 -0
  27. package/dist/hooks/{useEmitter.mjs → useMediaCloudEmitter.mjs} +4 -4
  28. package/dist/hooks/useMediaCloudEmitter.mjs.map +1 -0
  29. package/dist/index.d.mts +2 -2
  30. package/dist/plugin.d.mts +2 -3
  31. package/dist/plugin.mjs +25 -172
  32. package/dist/plugin.mjs.map +1 -1
  33. package/dist/tus/stores/s3/index.d.mts +15 -0
  34. package/dist/tus/stores/s3/index.mjs +30 -0
  35. package/dist/tus/stores/s3/index.mjs.map +1 -0
  36. package/dist/tus/stores/s3/parts-manager.mjs +1 -1
  37. package/dist/tus/stores/s3/parts-manager.mjs.map +1 -1
  38. package/dist/tus/stores/s3/s3-store.mjs +1 -1
  39. package/dist/tus/stores/s3/s3-store.mjs.map +1 -1
  40. package/dist/types/errors.d.mts +24 -44
  41. package/dist/types/errors.mjs +25 -16
  42. package/dist/types/errors.mjs.map +1 -1
  43. package/dist/types/index.d.mts +30 -2
  44. package/dist/utils/file.mjs +2 -2
  45. package/dist/utils/file.mjs.map +1 -1
  46. package/dist/utils/mux.d.mts +39 -0
  47. package/dist/utils/mux.mjs +56 -0
  48. package/dist/utils/mux.mjs.map +1 -0
  49. package/dist/utils/tus.d.mts +56 -0
  50. package/dist/utils/tus.mjs +85 -0
  51. package/dist/utils/tus.mjs.map +1 -0
  52. package/package.json +20 -15
  53. package/dist/hooks/useEmitter.d.mts +0 -47
  54. package/dist/hooks/useEmitter.mjs.map +0 -1
@@ -4,28 +4,6 @@ import { useErrorHandler } from "../hooks/useErrorHandler.mjs";
4
4
  //#region src/adapter/handleDelete.ts
5
5
  const { logError } = useErrorHandler();
6
6
  /**
7
- * Creates a handle delete function for processing file deletions from both Mux and S3 storage
8
- * @param args - The arguments for creating the delete handler
9
- * @param args.s3Store - The S3Store instance for S3 file operations
10
- * @param args.getMuxClient - Function that returns a Mux client instance
11
- * @returns A HandleDelete function that processes deletion requests based on storage type
12
- */
13
- function getHandleDelete(args) {
14
- const { s3Store, getMuxClient } = args;
15
- return async ({ doc, req }) => {
16
- if (req?.method !== "DELETE" || !doc) return;
17
- const media = doc;
18
- if (media?.storage === "mux" && media.mux?.uploadId) await deleteMuxAsset({
19
- getMuxClient,
20
- uploadId: media.mux.uploadId
21
- });
22
- else if (media.filename) await deleteS3File({
23
- s3Store,
24
- filename: media.filename
25
- });
26
- };
27
- }
28
- /**
29
7
  * Deletes a Mux asset by upload ID
30
8
  * @param args - The arguments for deleting the Mux asset
31
9
  * @param args.getMuxClient - Function that returns a Mux client instance
@@ -42,7 +20,7 @@ async function deleteMuxAsset(args) {
42
20
  await mux.video.assets.delete(asset.id);
43
21
  }
44
22
  } catch (_error) {
45
- logError(MediaCloudErrors.MUX_ASSET_DELETE_ERROR);
23
+ logError(MediaCloudErrors.MUX_ASSET_DELETE_ERROR.message);
46
24
  }
47
25
  }
48
26
  /**
@@ -61,9 +39,38 @@ async function deleteS3File(args) {
61
39
  Delete: { Objects: [{ Key: filename }, { Key: `${filename}.info` }] }
62
40
  });
63
41
  } catch (_error) {
64
- logError(MediaCloudErrors.S3_DELETE_ERROR);
42
+ logError(MediaCloudErrors.S3_DELETE_ERROR.message);
65
43
  }
66
44
  }
45
+ /**
46
+ * Creates a handle delete function for processing file deletions from both Mux and S3 storage
47
+ * @param args - The arguments for creating the delete handler
48
+ * @param args.s3Store - The S3Store instance for S3 file operations
49
+ * @param args.getMuxClient - Function that returns a Mux client instance
50
+ * @returns A HandleDelete function that processes deletion requests based on storage type
51
+ */
52
+ function getHandleDelete(args) {
53
+ const { s3Store, getMuxClient } = args;
54
+ return async ({ doc, req }) => {
55
+ if (req?.method !== "DELETE" || !doc) return;
56
+ const media = doc;
57
+ switch (media?.storage) {
58
+ case "mux":
59
+ if (media.mux?.uploadId) await deleteMuxAsset({
60
+ getMuxClient,
61
+ uploadId: media.mux.uploadId
62
+ });
63
+ break;
64
+ case "s3":
65
+ if (media.filename) await deleteS3File({
66
+ s3Store,
67
+ filename: media.filename
68
+ });
69
+ break;
70
+ default: logError(MediaCloudErrors.UNKNOWN_STORAGE_TYPE.message);
71
+ }
72
+ };
73
+ }
67
74
 
68
75
  //#endregion
69
76
  export { getHandleDelete };
@@ -1 +1 @@
1
- {"version":3,"file":"handleDelete.mjs","names":[],"sources":["../../src/adapter/handleDelete.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport type { HandleDelete } from '@payloadcms/plugin-cloud-storage/types'\nimport type { Document } from 'payload'\nimport type { Mux } from '@mux/mux-node'\nimport type { S3Store } from '../tus/stores/s3/s3-store'\n\ninterface HandleDeleteArgs {\n s3Store: S3Store\n getMuxClient: () => Mux\n}\n\ninterface DeleteMuxAssetArgs {\n getMuxClient: () => Mux\n uploadId: string\n}\n\ninterface DeleteS3FileArgs {\n s3Store: S3Store\n filename: string\n}\n\nconst { logError } = useErrorHandler()\n\n/**\n * Creates a handle delete function for processing file deletions from both Mux and S3 storage\n * @param args - The arguments for creating the delete handler\n * @param args.s3Store - The S3Store instance for S3 file operations\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @returns A HandleDelete function that processes deletion requests based on storage type\n */\nexport function getHandleDelete(args: HandleDeleteArgs): HandleDelete {\n const { s3Store, getMuxClient } = args\n return async ({ doc, req }) => {\n if (req?.method !== 'DELETE' || !doc) {\n return\n }\n\n const media = doc as Document\n\n if (media?.storage === 'mux' && media.mux?.uploadId) {\n await deleteMuxAsset({ getMuxClient, uploadId: media.mux.uploadId })\n } else if (media.filename) {\n await deleteS3File({ s3Store, filename: media.filename })\n }\n }\n}\n\n/**\n * Deletes a Mux asset by upload ID\n * @param args - The arguments for deleting the Mux asset\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @param args.uploadId - The upload ID of the Mux asset to delete\n * @returns Promise that resolves when the asset is deleted or rejects on error\n */\nasync function deleteMuxAsset(args: DeleteMuxAssetArgs): Promise<void> {\n const { getMuxClient, uploadId } = args\n\n try {\n const mux = getMuxClient()\n const assets = await mux.video.assets.list({ upload_id: uploadId })\n if (assets.data.length > 0) {\n const asset = assets.data[0]\n await mux.video.assets.delete(asset.id)\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_ASSET_DELETE_ERROR)\n }\n}\n\n/**\n * Deletes a file from S3 storage including its metadata info file\n * @param args - The arguments for deleting the S3 file\n * @param args.s3Store - The S3Store instance for S3 operations\n * @param args.filename - The filename of the file to delete from S3\n * @returns Promise that resolves when the file is deleted or rejects on error\n */\nasync function deleteS3File(args: DeleteS3FileArgs): Promise<void> {\n const { s3Store, filename } = args\n\n try {\n const { client, bucket } = s3Store\n await client.deleteObjects({\n Bucket: bucket,\n Delete: {\n Objects: [{ Key: filename }, { Key: `${filename}.info` }],\n },\n })\n } catch (_error) {\n logError(MediaCloudErrors.S3_DELETE_ERROR)\n }\n}\n"],"mappings":";;;;AAuBA,MAAM,EAAE,aAAa,iBAAiB;;;;;;;;AAStC,SAAgB,gBAAgB,MAAsC;CACpE,MAAM,EAAE,SAAS,iBAAiB;AAClC,QAAO,OAAO,EAAE,KAAK,UAAU;AAC7B,MAAI,KAAK,WAAW,YAAY,CAAC,IAC/B;EAGF,MAAM,QAAQ;AAEd,MAAI,OAAO,YAAY,SAAS,MAAM,KAAK,SACzC,OAAM,eAAe;GAAE;GAAc,UAAU,MAAM,IAAI;GAAU,CAAC;WAC3D,MAAM,SACf,OAAM,aAAa;GAAE;GAAS,UAAU,MAAM;GAAU,CAAC;;;;;;;;;;AAY/D,eAAe,eAAe,MAAyC;CACrE,MAAM,EAAE,cAAc,aAAa;AAEnC,KAAI;EACF,MAAM,MAAM,cAAc;EAC1B,MAAM,SAAS,MAAM,IAAI,MAAM,OAAO,KAAK,EAAE,WAAW,UAAU,CAAC;AACnE,MAAI,OAAO,KAAK,SAAS,GAAG;GAC1B,MAAM,QAAQ,OAAO,KAAK;AAC1B,SAAM,IAAI,MAAM,OAAO,OAAO,MAAM,GAAG;;UAElC,QAAQ;AACf,WAAS,iBAAiB,uBAAuB;;;;;;;;;;AAWrD,eAAe,aAAa,MAAuC;CACjE,MAAM,EAAE,SAAS,aAAa;AAE9B,KAAI;EACF,MAAM,EAAE,QAAQ,WAAW;AAC3B,QAAM,OAAO,cAAc;GACzB,QAAQ;GACR,QAAQ,EACN,SAAS,CAAC,EAAE,KAAK,UAAU,EAAE,EAAE,KAAK,GAAG,SAAS,QAAQ,CAAC,EAC1D;GACF,CAAC;UACK,QAAQ;AACf,WAAS,iBAAiB,gBAAgB"}
1
+ {"version":3,"file":"handleDelete.mjs","names":[],"sources":["../../src/adapter/handleDelete.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport type { HandleDelete } from '@payloadcms/plugin-cloud-storage/types'\nimport type { Document } from 'payload'\nimport type { Mux } from '@mux/mux-node'\nimport type { S3Store } from '../tus/stores/s3/s3-store'\n\ninterface HandleDeleteArgs {\n s3Store: S3Store\n getMuxClient: () => Mux\n}\n\ninterface DeleteMuxAssetArgs {\n getMuxClient: () => Mux\n uploadId: string\n}\n\ninterface DeleteS3FileArgs {\n s3Store: S3Store\n filename: string\n}\n\nconst { logError } = useErrorHandler()\n\n/**\n * Deletes a Mux asset by upload ID\n * @param args - The arguments for deleting the Mux asset\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @param args.uploadId - The upload ID of the Mux asset to delete\n * @returns Promise that resolves when the asset is deleted or rejects on error\n */\nasync function deleteMuxAsset(args: DeleteMuxAssetArgs): Promise<void> {\n const { getMuxClient, uploadId } = args\n\n try {\n const mux = getMuxClient()\n const assets = await mux.video.assets.list({ upload_id: uploadId })\n if (assets.data.length > 0) {\n const asset = assets.data[0]\n await mux.video.assets.delete(asset.id)\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_ASSET_DELETE_ERROR.message)\n }\n}\n\n/**\n * Deletes a file from S3 storage including its metadata info file\n * @param args - The arguments for deleting the S3 file\n * @param args.s3Store - The S3Store instance for S3 operations\n * @param args.filename - The filename of the file to delete from S3\n * @returns Promise that resolves when the file is deleted or rejects on error\n */\nasync function deleteS3File(args: DeleteS3FileArgs): Promise<void> {\n const { s3Store, filename } = args\n\n try {\n const { client, bucket } = s3Store\n await client.deleteObjects({\n Bucket: bucket,\n Delete: {\n Objects: [{ Key: filename }, { Key: `${filename}.info` }],\n },\n })\n } catch (_error) {\n logError(MediaCloudErrors.S3_DELETE_ERROR.message)\n }\n}\n\n/**\n * Creates a handle delete function for processing file deletions from both Mux and S3 storage\n * @param args - The arguments for creating the delete handler\n * @param args.s3Store - The S3Store instance for S3 file operations\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @returns A HandleDelete function that processes deletion requests based on storage type\n */\nexport function getHandleDelete(args: HandleDeleteArgs): HandleDelete {\n const { s3Store, getMuxClient } = args\n return async ({ doc, req }) => {\n if (req?.method !== 'DELETE' || !doc) {\n return\n }\n\n const media = doc as Document\n\n switch (media?.storage) {\n case 'mux':\n if (media.mux?.uploadId) {\n await deleteMuxAsset({ getMuxClient, uploadId: media.mux.uploadId })\n }\n break\n case 's3':\n if (media.filename) {\n await deleteS3File({ s3Store, filename: media.filename })\n }\n break\n default:\n logError(MediaCloudErrors.UNKNOWN_STORAGE_TYPE.message)\n }\n }\n}\n"],"mappings":";;;;AAuBA,MAAM,EAAE,aAAa,iBAAiB;;;;;;;;AAStC,eAAe,eAAe,MAAyC;CACrE,MAAM,EAAE,cAAc,aAAa;AAEnC,KAAI;EACF,MAAM,MAAM,cAAc;EAC1B,MAAM,SAAS,MAAM,IAAI,MAAM,OAAO,KAAK,EAAE,WAAW,UAAU,CAAC;AACnE,MAAI,OAAO,KAAK,SAAS,GAAG;GAC1B,MAAM,QAAQ,OAAO,KAAK;AAC1B,SAAM,IAAI,MAAM,OAAO,OAAO,MAAM,GAAG;;UAElC,QAAQ;AACf,WAAS,iBAAiB,uBAAuB,QAAQ;;;;;;;;;;AAW7D,eAAe,aAAa,MAAuC;CACjE,MAAM,EAAE,SAAS,aAAa;AAE9B,KAAI;EACF,MAAM,EAAE,QAAQ,WAAW;AAC3B,QAAM,OAAO,cAAc;GACzB,QAAQ;GACR,QAAQ,EACN,SAAS,CAAC,EAAE,KAAK,UAAU,EAAE,EAAE,KAAK,GAAG,SAAS,QAAQ,CAAC,EAC1D;GACF,CAAC;UACK,QAAQ;AACf,WAAS,iBAAiB,gBAAgB,QAAQ;;;;;;;;;;AAWtD,SAAgB,gBAAgB,MAAsC;CACpE,MAAM,EAAE,SAAS,iBAAiB;AAClC,QAAO,OAAO,EAAE,KAAK,UAAU;AAC7B,MAAI,KAAK,WAAW,YAAY,CAAC,IAC/B;EAGF,MAAM,QAAQ;AAEd,UAAQ,OAAO,SAAf;GACE,KAAK;AACH,QAAI,MAAM,KAAK,SACb,OAAM,eAAe;KAAE;KAAc,UAAU,MAAM,IAAI;KAAU,CAAC;AAEtE;GACF,KAAK;AACH,QAAI,MAAM,SACR,OAAM,aAAa;KAAE;KAAS,UAAU,MAAM;KAAU,CAAC;AAE3D;GACF,QACE,UAAS,iBAAiB,qBAAqB,QAAQ"}
@@ -12,7 +12,7 @@ function getHandleUpload() {
12
12
  case "mux":
13
13
  data.storage = "mux";
14
14
  data.mux = {
15
- uploadId: ctx.uploadId || "",
15
+ uploadId: ctx.uploadId ?? "",
16
16
  status: "preparing"
17
17
  };
18
18
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"handleUpload.mjs","names":[],"sources":["../../src/adapter/handleUpload.ts"],"sourcesContent":["import type { HandleUpload } from '@payloadcms/plugin-cloud-storage/types'\n\ninterface ClientUploadContext {\n storage: 'mux' | 's3'\n uploadId?: string\n mimeType?: string\n}\n\n/**\n * Creates a handle upload function for processing file uploads to both Mux and S3 storage\n * @returns A HandleUpload function that processes client upload context and updates document data\n */\nexport function getHandleUpload(): HandleUpload {\n return async function ({ clientUploadContext, data }) {\n if (!clientUploadContext) {\n return data\n }\n\n const ctx = clientUploadContext as ClientUploadContext\n data.mimeType = ctx.mimeType\n\n switch (ctx.storage) {\n case 'mux':\n data.storage = 'mux'\n data.mux = {\n uploadId: ctx.uploadId || '',\n status: 'preparing',\n }\n break\n default:\n data.storage = 's3'\n break\n }\n\n return data\n }\n}\n"],"mappings":";;;;;AAYA,SAAgB,kBAAgC;AAC9C,QAAO,eAAgB,EAAE,qBAAqB,QAAQ;AACpD,MAAI,CAAC,oBACH,QAAO;EAGT,MAAM,MAAM;AACZ,OAAK,WAAW,IAAI;AAEpB,UAAQ,IAAI,SAAZ;GACE,KAAK;AACH,SAAK,UAAU;AACf,SAAK,MAAM;KACT,UAAU,IAAI,YAAY;KAC1B,QAAQ;KACT;AACD;GACF;AACE,SAAK,UAAU;AACf;;AAGJ,SAAO"}
1
+ {"version":3,"file":"handleUpload.mjs","names":[],"sources":["../../src/adapter/handleUpload.ts"],"sourcesContent":["import type { HandleUpload } from '@payloadcms/plugin-cloud-storage/types'\n\ninterface ClientUploadContext {\n storage: 'mux' | 's3'\n uploadId?: string\n mimeType?: string\n}\n\n/**\n * Creates a handle upload function for processing file uploads to both Mux and S3 storage\n * @returns A HandleUpload function that processes client upload context and updates document data\n */\nexport function getHandleUpload(): HandleUpload {\n return async function ({ clientUploadContext, data }) {\n if (!clientUploadContext) {\n return data\n }\n\n const ctx = clientUploadContext as ClientUploadContext\n data.mimeType = ctx.mimeType\n\n switch (ctx.storage) {\n case 'mux':\n data.storage = 'mux'\n data.mux = {\n uploadId: ctx.uploadId ?? '',\n status: 'preparing',\n }\n break\n default:\n data.storage = 's3'\n break\n }\n\n return data\n }\n}\n"],"mappings":";;;;;AAYA,SAAgB,kBAAgC;AAC9C,QAAO,eAAgB,EAAE,qBAAqB,QAAQ;AACpD,MAAI,CAAC,oBACH,QAAO;EAGT,MAAM,MAAM;AACZ,OAAK,WAAW,IAAI;AAEpB,UAAQ,IAAI,SAAZ;GACE,KAAK;AACH,SAAK,UAAU;AACf,SAAK,MAAM;KACT,UAAU,IAAI,YAAY;KAC1B,QAAQ;KACT;AACD;GACF;AACE,SAAK,UAAU;AACf;;AAGJ,SAAO"}
@@ -4,6 +4,7 @@ import { CollectionConfig } from "payload";
4
4
  //#region src/collections/mediaCollection.d.ts
5
5
  interface GetMediaCollectionArgs {
6
6
  s3Store: S3Store;
7
+ baseCollection?: CollectionConfig;
7
8
  }
8
9
  /**
9
10
  * Creates a media collection configuration for Payload CMS
@@ -5,8 +5,8 @@
5
5
  * @returns A configured Payload collection for media files
6
6
  */
7
7
  function getMediaCollection(args) {
8
- const { s3Store } = args;
9
- return {
8
+ const { s3Store, baseCollection } = args;
9
+ const config = {
10
10
  slug: "media",
11
11
  access: {
12
12
  read: () => true,
@@ -156,6 +156,12 @@ function getMediaCollection(args) {
156
156
  }
157
157
  ]
158
158
  };
159
+ if (!baseCollection) return config;
160
+ return {
161
+ ...baseCollection,
162
+ ...config,
163
+ fields: [...baseCollection.fields || [], ...config.fields]
164
+ };
159
165
  }
160
166
 
161
167
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"mediaCollection.mjs","names":[],"sources":["../../src/collections/mediaCollection.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\nimport type { Document } from 'payload'\nimport type { S3Store } from './../tus/stores/s3/s3-store'\n\ninterface GetMediaCollectionArgs {\n s3Store: S3Store\n}\n\n/**\n * Creates a media collection configuration for Payload CMS\n * @param args - Arguments including the S3Store instance\n * @returns A configured Payload collection for media files\n */\nexport function getMediaCollection(\n args: GetMediaCollectionArgs\n): CollectionConfig {\n const { s3Store } = args\n\n return {\n slug: 'media',\n access: {\n read: () => true,\n delete: () => true,\n },\n admin: {\n group: 'Media Cloud',\n pagination: {\n defaultLimit: 50,\n },\n },\n upload: {\n crop: false,\n displayPreview: true,\n hideRemoveFile: true,\n adminThumbnail({ doc }: { doc: Document }) {\n if (doc?.storage === 'mux' && doc?.mux?.playbackId) {\n return `https://image.mux.com/${doc.mux.playbackId}/thumbnail.jpg?width=200&height=200&fit_mode=pad`\n } else if (doc?.storage === 's3') {\n // @TODO: Make configurable\n const url = s3Store.getUrl(doc.filename)\n return `https://wsrv.nl/?url=${url}?width=200&height=200&default=1`\n }\n return null\n },\n },\n fields: [\n {\n name: 'alt',\n label: 'Alternative Text',\n type: 'text',\n },\n {\n name: 'caption',\n label: 'Caption',\n type: 'text',\n },\n {\n name: 'copyright',\n label: 'Copyright',\n type: 'text',\n },\n {\n type: 'row',\n fields: [\n {\n name: 'width',\n label: 'Width',\n type: 'text',\n admin: {\n readOnly: true,\n hidden: false,\n width: '50%',\n },\n },\n {\n name: 'height',\n label: 'Height',\n type: 'number',\n admin: {\n readOnly: true,\n hidden: false,\n width: '50%',\n },\n },\n ],\n },\n {\n name: 'storage',\n label: 'Storage',\n type: 'select',\n options: [\n {\n label: 'Mux',\n value: 'mux',\n },\n {\n label: 'S3',\n value: 's3',\n },\n ],\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'mux',\n label: 'Mux',\n type: 'group',\n admin: {\n disableListColumn: true,\n disableBulkEdit: true,\n disableListFilter: true,\n readOnly: true,\n condition: (data) => {\n return data.storage === 'mux'\n },\n },\n fields: [\n {\n name: 'preview',\n type: 'ui',\n admin: {\n condition: (data) => {\n return data.storage === 'mux'\n },\n disableListColumn: true,\n components: {\n Field: '@maas/payload-plugin-media-cloud/components#MuxPreview',\n },\n },\n },\n {\n name: 'status',\n label: 'Status',\n type: 'text',\n },\n {\n name: 'uploadId',\n label: 'Upload ID',\n type: 'text',\n },\n {\n name: 'assetId',\n label: 'Asset ID',\n type: 'text',\n },\n {\n name: 'playbackId',\n label: 'Playback ID',\n type: 'text',\n },\n {\n name: 'aspectRatio',\n label: 'Aspect Ratio',\n type: 'text',\n },\n {\n name: 'duration',\n label: 'Duration',\n type: 'number',\n },\n {\n name: 'tracks',\n label: 'Tracks',\n type: 'json',\n },\n {\n name: 'maxResolutionTier',\n label: 'Max Resolution Tier',\n type: 'text',\n },\n {\n name: 'videoQuality',\n label: 'Video Quality',\n type: 'text',\n },\n {\n name: 'staticRenditions',\n label: 'Static Renditions',\n type: 'json',\n },\n ],\n },\n ],\n }\n}\n"],"mappings":";;;;;;AAaA,SAAgB,mBACd,MACkB;CAClB,MAAM,EAAE,YAAY;AAEpB,QAAO;EACL,MAAM;EACN,QAAQ;GACN,YAAY;GACZ,cAAc;GACf;EACD,OAAO;GACL,OAAO;GACP,YAAY,EACV,cAAc,IACf;GACF;EACD,QAAQ;GACN,MAAM;GACN,gBAAgB;GAChB,gBAAgB;GAChB,eAAe,EAAE,OAA0B;AACzC,QAAI,KAAK,YAAY,SAAS,KAAK,KAAK,WACtC,QAAO,yBAAyB,IAAI,IAAI,WAAW;aAC1C,KAAK,YAAY,KAG1B,QAAO,wBADK,QAAQ,OAAO,IAAI,SAAS,CACL;AAErC,WAAO;;GAEV;EACD,QAAQ;GACN;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACP;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACP;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACP;GACD;IACE,MAAM;IACN,QAAQ,CACN;KACE,MAAM;KACN,OAAO;KACP,MAAM;KACN,OAAO;MACL,UAAU;MACV,QAAQ;MACR,OAAO;MACR;KACF,EACD;KACE,MAAM;KACN,OAAO;KACP,MAAM;KACN,OAAO;MACL,UAAU;MACV,QAAQ;MACR,OAAO;MACR;KACF,CACF;IACF;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACN,SAAS,CACP;KACE,OAAO;KACP,OAAO;KACR,EACD;KACE,OAAO;KACP,OAAO;KACR,CACF;IACD,OAAO,EACL,UAAU,MACX;IACF;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACN,OAAO;KACL,mBAAmB;KACnB,iBAAiB;KACjB,mBAAmB;KACnB,UAAU;KACV,YAAY,SAAS;AACnB,aAAO,KAAK,YAAY;;KAE3B;IACD,QAAQ;KACN;MACE,MAAM;MACN,MAAM;MACN,OAAO;OACL,YAAY,SAAS;AACnB,eAAO,KAAK,YAAY;;OAE1B,mBAAmB;OACnB,YAAY,EACV,OAAO,0DACR;OACF;MACF;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACF;IACF;GACF;EACF"}
1
+ {"version":3,"file":"mediaCollection.mjs","names":["config: CollectionConfig"],"sources":["../../src/collections/mediaCollection.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\nimport type { Document } from 'payload'\nimport type { S3Store } from './../tus/stores/s3/s3-store'\n\ninterface GetMediaCollectionArgs {\n s3Store: S3Store\n baseCollection?: CollectionConfig\n}\n\n/**\n * Creates a media collection configuration for Payload CMS\n * @param args - Arguments including the S3Store instance\n * @returns A configured Payload collection for media files\n */\nexport function getMediaCollection(\n args: GetMediaCollectionArgs\n): CollectionConfig {\n const { s3Store, baseCollection } = args\n\n const config: CollectionConfig = {\n slug: 'media',\n access: {\n read: () => true,\n delete: () => true,\n },\n admin: {\n group: 'Media Cloud',\n pagination: {\n defaultLimit: 50,\n },\n },\n upload: {\n crop: false,\n displayPreview: true,\n hideRemoveFile: true,\n adminThumbnail({ doc }: { doc: Document }) {\n if (doc?.storage === 'mux' && doc?.mux?.playbackId) {\n return `https://image.mux.com/${doc.mux.playbackId}/thumbnail.jpg?width=200&height=200&fit_mode=pad`\n } else if (doc?.storage === 's3') {\n // @TODO: Make configurable\n const url = s3Store.getUrl(doc.filename)\n return `https://wsrv.nl/?url=${url}?width=200&height=200&default=1`\n }\n return null\n },\n },\n fields: [\n {\n name: 'alt',\n label: 'Alternative Text',\n type: 'text',\n },\n {\n name: 'caption',\n label: 'Caption',\n type: 'text',\n },\n {\n name: 'copyright',\n label: 'Copyright',\n type: 'text',\n },\n {\n type: 'row',\n fields: [\n {\n name: 'width',\n label: 'Width',\n type: 'text',\n admin: {\n readOnly: true,\n hidden: false,\n width: '50%',\n },\n },\n {\n name: 'height',\n label: 'Height',\n type: 'number',\n admin: {\n readOnly: true,\n hidden: false,\n width: '50%',\n },\n },\n ],\n },\n {\n name: 'storage',\n label: 'Storage',\n type: 'select',\n options: [\n {\n label: 'Mux',\n value: 'mux',\n },\n {\n label: 'S3',\n value: 's3',\n },\n ],\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'mux',\n label: 'Mux',\n type: 'group',\n admin: {\n disableListColumn: true,\n disableBulkEdit: true,\n disableListFilter: true,\n readOnly: true,\n condition: (data) => {\n return data.storage === 'mux'\n },\n },\n fields: [\n {\n name: 'preview',\n type: 'ui',\n admin: {\n condition: (data) => {\n return data.storage === 'mux'\n },\n disableListColumn: true,\n components: {\n Field: '@maas/payload-plugin-media-cloud/components#MuxPreview',\n },\n },\n },\n {\n name: 'status',\n label: 'Status',\n type: 'text',\n },\n {\n name: 'uploadId',\n label: 'Upload ID',\n type: 'text',\n },\n {\n name: 'assetId',\n label: 'Asset ID',\n type: 'text',\n },\n {\n name: 'playbackId',\n label: 'Playback ID',\n type: 'text',\n },\n {\n name: 'aspectRatio',\n label: 'Aspect Ratio',\n type: 'text',\n },\n {\n name: 'duration',\n label: 'Duration',\n type: 'number',\n },\n {\n name: 'tracks',\n label: 'Tracks',\n type: 'json',\n },\n {\n name: 'maxResolutionTier',\n label: 'Max Resolution Tier',\n type: 'text',\n },\n {\n name: 'videoQuality',\n label: 'Video Quality',\n type: 'text',\n },\n {\n name: 'staticRenditions',\n label: 'Static Renditions',\n type: 'json',\n },\n ],\n },\n ],\n }\n\n // Simple merge: if baseCollection exists, spread it first, then our config overrides\n if (!baseCollection) {\n return config\n }\n\n return {\n ...baseCollection,\n ...config,\n fields: [...(baseCollection.fields || []), ...config.fields],\n }\n}\n"],"mappings":";;;;;;AAcA,SAAgB,mBACd,MACkB;CAClB,MAAM,EAAE,SAAS,mBAAmB;CAEpC,MAAMA,SAA2B;EAC/B,MAAM;EACN,QAAQ;GACN,YAAY;GACZ,cAAc;GACf;EACD,OAAO;GACL,OAAO;GACP,YAAY,EACV,cAAc,IACf;GACF;EACD,QAAQ;GACN,MAAM;GACN,gBAAgB;GAChB,gBAAgB;GAChB,eAAe,EAAE,OAA0B;AACzC,QAAI,KAAK,YAAY,SAAS,KAAK,KAAK,WACtC,QAAO,yBAAyB,IAAI,IAAI,WAAW;aAC1C,KAAK,YAAY,KAG1B,QAAO,wBADK,QAAQ,OAAO,IAAI,SAAS,CACL;AAErC,WAAO;;GAEV;EACD,QAAQ;GACN;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACP;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACP;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACP;GACD;IACE,MAAM;IACN,QAAQ,CACN;KACE,MAAM;KACN,OAAO;KACP,MAAM;KACN,OAAO;MACL,UAAU;MACV,QAAQ;MACR,OAAO;MACR;KACF,EACD;KACE,MAAM;KACN,OAAO;KACP,MAAM;KACN,OAAO;MACL,UAAU;MACV,QAAQ;MACR,OAAO;MACR;KACF,CACF;IACF;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACN,SAAS,CACP;KACE,OAAO;KACP,OAAO;KACR,EACD;KACE,OAAO;KACP,OAAO;KACR,CACF;IACD,OAAO,EACL,UAAU,MACX;IACF;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACN,OAAO;KACL,mBAAmB;KACnB,iBAAiB;KACjB,mBAAmB;KACnB,UAAU;KACV,YAAY,SAAS;AACnB,aAAO,KAAK,YAAY;;KAE3B;IACD,QAAQ;KACN;MACE,MAAM;MACN,MAAM;MACN,OAAO;OACL,YAAY,SAAS;AACnB,eAAO,KAAK,YAAY;;OAE1B,mBAAmB;OACnB,YAAY,EACV,OAAO,0DACR;OACF;MACF;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACP;KACF;IACF;GACF;EACF;AAGD,KAAI,CAAC,eACH,QAAO;AAGT,QAAO;EACL,GAAG;EACH,GAAG;EACH,QAAQ,CAAC,GAAI,eAAe,UAAU,EAAE,EAAG,GAAG,OAAO,OAAO;EAC7D"}
@@ -3,7 +3,7 @@
3
3
  import { MediaCloudErrors } from "../../types/errors.mjs";
4
4
  import { useErrorHandler } from "../../hooks/useErrorHandler.mjs";
5
5
  import { getFileType, isVideo, sanitizeFilename } from "../../utils/file.mjs";
6
- import { emitter } from "../../hooks/useEmitter.mjs";
6
+ import { useMediaCloudEmitter } from "../../hooks/useMediaCloudEmitter.mjs";
7
7
  import * as upchunk from "@mux/upchunk";
8
8
  import * as tus from "tus-js-client";
9
9
  import { toast } from "@payloadcms/ui";
@@ -11,6 +11,7 @@ import { createClientUploadHandler } from "@payloadcms/plugin-cloud-storage/clie
11
11
 
12
12
  //#region src/components/upload-handler/upload-handler.tsx
13
13
  const { logError, throwError } = useErrorHandler();
14
+ const emitter = useMediaCloudEmitter();
14
15
  const MUX_CHUNK_SIZE = 30720;
15
16
  const TUS_CHUNK_SIZE = 1024 * 1024;
16
17
  const TUS_RETRY_DELAYS = [
@@ -24,9 +25,9 @@ const TUS_RETRY_DELAYS = [
24
25
  * @param uploadUrl - The upload URL to parse
25
26
  * @returns The extracted upload ID or empty string if parsing fails
26
27
  */
27
- function parseUploadId(uploadUrl) {
28
+ function parseFilename(uploadUrl) {
28
29
  if (!uploadUrl) {
29
- logError(MediaCloudErrors.UPLOAD_NO_URL);
30
+ logError(MediaCloudErrors.UPLOAD_NO_URL.message);
30
31
  return "";
31
32
  }
32
33
  return new URL(uploadUrl).pathname.split("/").pop() || "";
@@ -39,11 +40,9 @@ function parseUploadId(uploadUrl) {
39
40
  async function muxUpload(args) {
40
41
  const { file, serverURL, apiRoute, mimeType, updateFilename } = args;
41
42
  const filename = sanitizeFilename(file.name);
42
- const getUploadUrlEndpoint = `${serverURL}${apiRoute}/mux/upload`;
43
- const getAssetEndpoint = `${serverURL}${apiRoute}/mux/asset`;
44
43
  updateFilename(filename);
45
44
  try {
46
- const { url, uploadId } = await (await fetch(getUploadUrlEndpoint, {
45
+ const { url, uploadId } = await (await fetch(`${serverURL}${apiRoute}/mux/upload`, {
47
46
  body: JSON.stringify({
48
47
  filename,
49
48
  mimeType
@@ -57,33 +56,34 @@ async function muxUpload(args) {
57
56
  file,
58
57
  chunkSize: MUX_CHUNK_SIZE
59
58
  });
60
- emitter.emit("add-upload", {
61
- id: uploadId,
59
+ emitter.emit("addUpload", {
62
60
  filename,
61
+ uploadId,
63
62
  polling: false,
64
- pollingUrl: getAssetEndpoint
63
+ pollingUrl: `${serverURL}${apiRoute}/mux/asset`
65
64
  });
66
65
  uploader.on("error", function() {
67
- logError(MediaCloudErrors.MUX_UPLOAD_ERROR);
66
+ logError(MediaCloudErrors.MUX_UPLOAD_ERROR.message);
68
67
  toast.error("Video upload failed");
69
- emitter.emit("remove-upload", { id: uploadId });
68
+ emitter.emit("removeUpload", { uploadId });
70
69
  });
71
70
  uploader.on("progress", function(progress) {
72
- emitter.emit("update-upload", {
73
- id: uploadId,
71
+ emitter.emit("updateUpload", {
72
+ filename,
74
73
  progress: progress.detail
75
74
  });
76
75
  });
77
76
  uploader.on("success", function() {
78
- emitter.emit("upload-completed", { id: uploadId });
77
+ emitter.emit("uploadComplete", { filename });
79
78
  });
80
79
  return {
80
+ filename,
81
81
  uploadId,
82
82
  mimeType,
83
83
  storage: "mux"
84
84
  };
85
85
  } catch (_error) {
86
- logError(MediaCloudErrors.MUX_DIRECT_UPLOAD_ERROR);
86
+ logError(MediaCloudErrors.MUX_DIRECT_UPLOAD_ERROR.message);
87
87
  toast.error("Video upload failed");
88
88
  return null;
89
89
  }
@@ -112,32 +112,29 @@ async function tusUpload(args) {
112
112
  contentLength: filesize
113
113
  },
114
114
  onError: function() {
115
- logError(MediaCloudErrors.TUS_UPLOAD_ERROR);
115
+ logError(MediaCloudErrors.TUS_UPLOAD_ERROR.message);
116
116
  toast.error("File upload failed");
117
117
  resolve(null);
118
118
  },
119
119
  onProgress: function(bytesUploaded, bytesTotal) {
120
120
  const percentage = Math.round(bytesUploaded / bytesTotal * 100);
121
- const uploadId = parseUploadId(upload?.url);
122
- emitter.emit("update-upload", {
123
- id: uploadId,
121
+ const filename$1 = parseFilename(upload?.url);
122
+ emitter.emit("updateUpload", {
123
+ filename: filename$1,
124
124
  progress: percentage
125
125
  });
126
126
  },
127
127
  onSuccess: function() {
128
- const uploadId = parseUploadId(upload?.url);
129
- emitter.emit("upload-completed", { id: uploadId });
130
- fetch(`${serverURL}${apiRoute}/uploads/${uploadId}/process`);
128
+ const filename$1 = parseFilename(upload?.url);
129
+ emitter.emit("uploadComplete", { filename: filename$1 });
130
+ fetch(`${serverURL}${apiRoute}/uploads/${filename$1}/process`);
131
131
  },
132
132
  onUploadUrlAvailable: function() {
133
- const uploadId = parseUploadId(upload?.url);
134
- updateFilename(uploadId);
135
- emitter.emit("add-upload", {
136
- id: uploadId,
137
- filename
138
- });
133
+ const filename$1 = parseFilename(upload?.url);
134
+ updateFilename(filename$1);
135
+ emitter.emit("addUpload", { filename: filename$1 });
139
136
  resolve({
140
- uploadId,
137
+ filename: filename$1,
141
138
  mimeType,
142
139
  storage: "s3"
143
140
  });
@@ -149,7 +146,7 @@ async function tusUpload(args) {
149
146
  const UploadHandler = createClientUploadHandler({ handler: async function(args) {
150
147
  const { serverURL, apiRoute, file, updateFilename } = args;
151
148
  try {
152
- const mimeType = await getFileType(file);
149
+ const mimeType = await getFileType(file) || file.type || "application/octet-stream";
153
150
  if (!mimeType) {
154
151
  throwError(MediaCloudErrors.FILE_TYPE_UNKNOWN);
155
152
  return null;
@@ -164,9 +161,10 @@ const UploadHandler = createClientUploadHandler({ handler: async function(args)
164
161
  };
165
162
  if (isVideoFile) return await muxUpload(uploadArgs);
166
163
  else return await tusUpload(uploadArgs);
167
- } catch (_error) {
168
- logError(MediaCloudErrors.UPLOAD_HANDLER_ERROR);
169
- toast.error("Upload failed");
164
+ } catch (error) {
165
+ console.error("[PLUGIN-MEDIA-CLOUD] Upload handler detailed error:", error);
166
+ logError(MediaCloudErrors.UPLOAD_HANDLER_ERROR.message);
167
+ toast.error(`Upload failed: ${error instanceof Error ? error.message : "Unknown error"}`);
170
168
  return null;
171
169
  }
172
170
  } });
@@ -1 +1 @@
1
- {"version":3,"file":"upload-handler.mjs","names":["uploadArgs: UploadArgs"],"sources":["../../../src/components/upload-handler/upload-handler.tsx"],"sourcesContent":["'use client'\n\nimport * as upchunk from '@mux/upchunk'\nimport * as tus from 'tus-js-client'\n\nimport { toast } from '@payloadcms/ui'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { MediaCloudErrors } from '../../types/errors'\nimport { emitter } from '../../hooks/useEmitter'\nimport { useErrorHandler } from '../../hooks/useErrorHandler'\nimport { isVideo, getFileType, sanitizeFilename } from '../../utils/file'\n\ninterface UploadArgs {\n serverURL: string\n apiRoute: string\n file: File\n mimeType: string\n updateFilename: (filename: string) => void\n}\n\ninterface UploadResult {\n uploadId: string\n mimeType: string\n storage: 'mux' | 's3'\n}\n\ninterface MuxCreateUploadResponse {\n url: string\n uploadId: string\n}\n\nconst { logError, throwError } = useErrorHandler()\n\nconst MUX_CHUNK_SIZE = 30720\nconst TUS_CHUNK_SIZE = 1024 * 1024\nconst TUS_RETRY_DELAYS = [0, 1000, 2000, 5000]\n\n/**\n * Utility function to parse upload ID from URL\n * @param uploadUrl - The upload URL to parse\n * @returns The extracted upload ID or empty string if parsing fails\n */\nfunction parseUploadId(uploadUrl?: string | null): string {\n if (!uploadUrl) {\n logError(MediaCloudErrors.UPLOAD_NO_URL)\n return ''\n }\n const url = new URL(uploadUrl)\n return url.pathname.split('/').pop() || ''\n}\n\n/**\n * Handles Mux video upload with progress tracking\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function muxUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { file, serverURL, apiRoute, mimeType, updateFilename } = args\n\n const filename = sanitizeFilename(file.name)\n const getUploadUrlEndpoint = `${serverURL}${apiRoute}/mux/upload`\n const getAssetEndpoint = `${serverURL}${apiRoute}/mux/asset`\n\n updateFilename(filename)\n\n try {\n // Request upload URL from Mux\n const response = await fetch(getUploadUrlEndpoint, {\n body: JSON.stringify({ filename, mimeType }),\n credentials: 'include',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n\n const { url, uploadId } = (await response.json()) as MuxCreateUploadResponse\n\n // Create upchunk uploader\n const uploader = await upchunk.createUpload({\n endpoint: url,\n file,\n chunkSize: MUX_CHUNK_SIZE,\n })\n\n // Add upload to tracker\n emitter.emit('add-upload', {\n id: uploadId,\n filename,\n polling: false,\n pollingUrl: getAssetEndpoint,\n })\n\n // Set up event handlers\n uploader.on('error', function () {\n logError(MediaCloudErrors.MUX_UPLOAD_ERROR)\n toast.error('Video upload failed')\n emitter.emit('remove-upload', { id: uploadId })\n })\n\n uploader.on('progress', function (progress) {\n emitter.emit('update-upload', {\n id: uploadId,\n progress: progress.detail,\n })\n })\n\n uploader.on('success', function () {\n emitter.emit('upload-completed', { id: uploadId })\n })\n\n return {\n uploadId,\n mimeType,\n storage: 'mux',\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_DIRECT_UPLOAD_ERROR)\n toast.error('Video upload failed')\n return null\n }\n}\n\n/**\n * Handles TUS file upload with resumable capabilities\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function tusUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { apiRoute, serverURL, file, mimeType, updateFilename } = args\n\n const filename = file.name\n const filetype = file.type\n const filesize = file.size.toString()\n\n return new Promise((resolve) => {\n const upload = new tus.Upload(file, {\n endpoint: `${serverURL}${apiRoute}/uploads`,\n retryDelays: TUS_RETRY_DELAYS,\n chunkSize: TUS_CHUNK_SIZE,\n metadata: {\n filename,\n filetype,\n filesize,\n contentType: filetype,\n contentDisposition: 'inline',\n contentLength: filesize,\n },\n onError: function () {\n logError(MediaCloudErrors.TUS_UPLOAD_ERROR)\n toast.error('File upload failed')\n resolve(null)\n },\n onProgress: function (bytesUploaded, bytesTotal) {\n const percentage = Math.round((bytesUploaded / bytesTotal) * 100)\n const uploadId = parseUploadId(upload?.url)\n emitter.emit('update-upload', {\n id: uploadId,\n progress: percentage,\n })\n },\n onSuccess: function () {\n const uploadId = parseUploadId(upload?.url)\n emitter.emit('upload-completed', { id: uploadId })\n\n // Trigger post upload processing\n fetch(`${serverURL}${apiRoute}/uploads/${uploadId}/process`)\n },\n onUploadUrlAvailable: function () {\n const uploadId = parseUploadId(upload?.url)\n updateFilename(uploadId)\n emitter.emit('add-upload', { id: uploadId, filename })\n resolve({\n uploadId,\n mimeType,\n storage: 's3',\n })\n },\n })\n\n upload.start()\n })\n}\n\nexport const UploadHandler = createClientUploadHandler({\n handler: async function (args) {\n const { serverURL, apiRoute, file, updateFilename } = args\n\n try {\n const mimeType = await getFileType(file)\n\n if (!mimeType) {\n throwError(MediaCloudErrors.FILE_TYPE_UNKNOWN)\n return null\n }\n\n const isVideoFile = await isVideo(file)\n const uploadArgs: UploadArgs = {\n file,\n serverURL,\n apiRoute,\n mimeType,\n updateFilename,\n }\n\n if (isVideoFile) {\n return await muxUpload(uploadArgs)\n } else {\n return await tusUpload(uploadArgs)\n }\n } catch (_error) {\n logError(MediaCloudErrors.UPLOAD_HANDLER_ERROR)\n toast.error('Upload failed')\n return null\n }\n },\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"upload-handler.mjs","names":["filename","uploadArgs: UploadArgs"],"sources":["../../../src/components/upload-handler/upload-handler.tsx"],"sourcesContent":["'use client'\n\nimport * as upchunk from '@mux/upchunk'\nimport * as tus from 'tus-js-client'\n\nimport { toast } from '@payloadcms/ui'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { MediaCloudErrors } from '../../types/errors'\nimport { useMediaCloudEmitter } from '../../hooks/useMediaCloudEmitter'\nimport { useErrorHandler } from '../../hooks/useErrorHandler'\nimport { isVideo, getFileType, sanitizeFilename } from '../../utils/file'\n\ninterface UploadArgs {\n serverURL: string\n apiRoute: string\n file: File\n mimeType: string\n updateFilename: (filename: string) => void\n}\n\ninterface UploadResult {\n filename: string\n uploadId?: string\n mimeType: string\n storage: 'mux' | 's3'\n}\n\ninterface MuxCreateUploadResponse {\n url: string\n uploadId: string\n filename: string\n}\n\nconst { logError, throwError } = useErrorHandler()\nconst emitter = useMediaCloudEmitter()\n\nconst MUX_CHUNK_SIZE = 30720\nconst TUS_CHUNK_SIZE = 1024 * 1024\nconst TUS_RETRY_DELAYS = [0, 1000, 2000, 5000]\n\n/**\n * Utility function to parse upload ID from URL\n * @param uploadUrl - The upload URL to parse\n * @returns The extracted upload ID or empty string if parsing fails\n */\nfunction parseFilename(uploadUrl?: string | null): string {\n if (!uploadUrl) {\n logError(MediaCloudErrors.UPLOAD_NO_URL.message)\n return ''\n }\n const url = new URL(uploadUrl)\n return url.pathname.split('/').pop() || ''\n}\n\n/**\n * Handles Mux video upload with progress tracking\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function muxUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { file, serverURL, apiRoute, mimeType, updateFilename } = args\n\n const filename = sanitizeFilename(file.name)\n updateFilename(filename)\n\n try {\n // Request upload URL from Mux\n const response = await fetch(`${serverURL}${apiRoute}/mux/upload`, {\n body: JSON.stringify({ filename, mimeType }),\n credentials: 'include',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n\n const { url, uploadId } = (await response.json()) as MuxCreateUploadResponse\n\n // Create upchunk uploader\n const uploader = await upchunk.createUpload({\n endpoint: url,\n file,\n chunkSize: MUX_CHUNK_SIZE,\n })\n\n // Add upload to tracker\n emitter.emit('addUpload', {\n filename,\n uploadId,\n polling: false,\n pollingUrl: `${serverURL}${apiRoute}/mux/asset`,\n })\n\n // Set up event handlers\n uploader.on('error', function () {\n logError(MediaCloudErrors.MUX_UPLOAD_ERROR.message)\n toast.error('Video upload failed')\n emitter.emit('removeUpload', { uploadId })\n })\n\n uploader.on('progress', function (progress) {\n emitter.emit('updateUpload', {\n filename,\n progress: progress.detail,\n })\n })\n\n uploader.on('success', function () {\n emitter.emit('uploadComplete', { filename })\n })\n\n // Update collection entry\n // with filename, uploadId, mimeType, and storage\n return {\n filename,\n uploadId,\n mimeType,\n storage: 'mux',\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_DIRECT_UPLOAD_ERROR.message)\n toast.error('Video upload failed')\n return null\n }\n}\n\n/**\n * Handles TUS file upload with resumable capabilities\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function tusUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { apiRoute, serverURL, file, mimeType, updateFilename } = args\n\n const filename = file.name\n const filetype = file.type\n const filesize = file.size.toString()\n\n return new Promise((resolve) => {\n const upload = new tus.Upload(file, {\n endpoint: `${serverURL}${apiRoute}/uploads`,\n retryDelays: TUS_RETRY_DELAYS,\n chunkSize: TUS_CHUNK_SIZE,\n metadata: {\n filename,\n filetype,\n filesize,\n contentType: filetype,\n contentDisposition: 'inline',\n contentLength: filesize,\n },\n onError: function () {\n logError(MediaCloudErrors.TUS_UPLOAD_ERROR.message)\n toast.error('File upload failed')\n resolve(null)\n },\n onProgress: function (bytesUploaded, bytesTotal) {\n const percentage = Math.round((bytesUploaded / bytesTotal) * 100)\n const filename = parseFilename(upload?.url)\n emitter.emit('updateUpload', {\n filename,\n progress: percentage,\n })\n },\n onSuccess: function () {\n const filename = parseFilename(upload?.url)\n emitter.emit('uploadComplete', { filename })\n\n // Trigger post upload processing\n fetch(`${serverURL}${apiRoute}/uploads/${filename}/process`)\n },\n onUploadUrlAvailable: function () {\n const filename = parseFilename(upload?.url)\n updateFilename(filename)\n emitter.emit('addUpload', { filename })\n\n // Update collection entry\n // with filename, mimeType, and storage\n resolve({\n filename,\n mimeType,\n storage: 's3',\n })\n },\n })\n\n upload.start()\n })\n}\n\nexport const UploadHandler = createClientUploadHandler({\n handler: async function (args) {\n const { serverURL, apiRoute, file, updateFilename } = args\n\n try {\n const mimeType =\n (await getFileType(file)) || file.type || 'application/octet-stream'\n\n if (!mimeType) {\n throwError(MediaCloudErrors.FILE_TYPE_UNKNOWN)\n return null\n }\n\n const isVideoFile = await isVideo(file)\n const uploadArgs: UploadArgs = {\n file,\n serverURL,\n apiRoute,\n mimeType,\n updateFilename,\n }\n\n if (isVideoFile) {\n return await muxUpload(uploadArgs)\n } else {\n return await tusUpload(uploadArgs)\n }\n } catch (error) {\n console.error(\n '[PLUGIN-MEDIA-CLOUD] Upload handler detailed error:',\n error\n )\n logError(MediaCloudErrors.UPLOAD_HANDLER_ERROR.message)\n toast.error(\n `Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n )\n return null\n }\n },\n})\n"],"mappings":"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"}
@@ -1,11 +1,12 @@
1
+ import { MediaCloudEmitterEvents } from "../../types/index.mjs";
1
2
  import React from "react";
2
3
  import "./upload-manager.css";
3
4
 
4
5
  //#region src/components/upload-manager/upload-manager.d.ts
5
6
  interface Upload {
6
- id: string;
7
7
  filename: string;
8
8
  progress: number;
9
+ uploadId?: string;
9
10
  error?: string;
10
11
  polling?: boolean;
11
12
  pollingUrl?: string;
@@ -14,23 +15,12 @@ interface Upload {
14
15
  interface UploadManagerContextType {
15
16
  showUploadManager?: boolean;
16
17
  activeUploads: Upload[];
17
- addUpload: (args: AddUploadArgs) => void;
18
- updateUpload: (args: UpdateUploadArgs) => void;
18
+ addUpload: (args: MediaCloudEmitterEvents['addUpload']) => void;
19
+ updateUpload: (args: MediaCloudEmitterEvents['updateUpload']) => void;
19
20
  }
20
21
  interface UploadManagerProviderArgs {
21
22
  children: React.ReactNode;
22
23
  }
23
- interface AddUploadArgs {
24
- id: string;
25
- filename: string;
26
- polling?: boolean;
27
- pollingUrl?: string;
28
- }
29
- interface UpdateUploadArgs {
30
- id: string;
31
- progress: number;
32
- polling?: boolean;
33
- }
34
24
  /**
35
25
  * Provider component for upload management context
36
26
  * @param args - Arguments including children to wrap
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { MediaCloudErrors } from "../../types/errors.mjs";
4
4
  import { useErrorHandler } from "../../hooks/useErrorHandler.mjs";
5
- import { useEmitter } from "../../hooks/useEmitter.mjs";
5
+ import { useMediaCloudEmitter } from "../../hooks/useMediaCloudEmitter.mjs";
6
6
  import { createContext, use, useCallback, useEffect, useRef, useState } from "react";
7
7
  import { Button } from "@payloadcms/ui";
8
8
  import "./upload-manager.css";
@@ -24,6 +24,7 @@ function UploadManagerProvider(args) {
24
24
  const [showUploadManager, setShowUploadManager] = useState(false);
25
25
  const [activeUploads, setActiveUploads] = useState([]);
26
26
  const [activeTab, setActiveTab] = useState("uploading");
27
+ const emitter = useMediaCloudEmitter();
27
28
  const activeTabRef = useRef(activeTab);
28
29
  const activeUploadsRef = useRef(activeUploads);
29
30
  const { logError } = useErrorHandler();
@@ -33,7 +34,6 @@ function UploadManagerProvider(args) {
33
34
  useEffect(() => {
34
35
  activeUploadsRef.current = activeUploads;
35
36
  }, [activeUploads]);
36
- const emitter = useEmitter();
37
37
  const checkAutoSwitchToCompleted = useCallback((uploads) => {
38
38
  const hasActiveUploads = uploads.some((upload) => upload.status === "uploading");
39
39
  const hasProcessingUploads = uploads.some((upload) => upload.status === "processing");
@@ -45,13 +45,13 @@ function UploadManagerProvider(args) {
45
45
  if (pollingUploads.length === 0) return;
46
46
  const pollAssets = async () => {
47
47
  for (const pollingUpload of pollingUploads) try {
48
- const response = await fetch(`${pollingUpload.pollingUrl}?upload_id=${pollingUpload.id}`, {
48
+ const response = await fetch(`${pollingUpload.pollingUrl}?upload_id=${pollingUpload.uploadId}`, {
49
49
  method: "GET",
50
50
  credentials: "include"
51
51
  });
52
52
  if (response.ok) {
53
53
  if ((await response.json()).ready) setActiveUploads((prev) => {
54
- const updatedUploads = prev.map((upload) => upload.id === pollingUpload.id ? {
54
+ const updatedUploads = prev.map((upload) => upload.uploadId === pollingUpload.uploadId ? {
55
55
  ...upload,
56
56
  polling: false,
57
57
  progress: 100,
@@ -62,7 +62,7 @@ function UploadManagerProvider(args) {
62
62
  });
63
63
  }
64
64
  } catch (_error) {
65
- logError(MediaCloudErrors.UPLOAD_POLLING_ERROR);
65
+ logError(MediaCloudErrors.UPLOAD_POLLING_ERROR.message);
66
66
  }
67
67
  };
68
68
  const intervalId = setInterval(pollAssets, 2e3);
@@ -73,26 +73,26 @@ function UploadManagerProvider(args) {
73
73
  logError
74
74
  ]);
75
75
  /**
76
- * Handles the 'upload-errored' event
76
+ * Handles the 'uploadError' event
77
77
  * @param event - The upload error event
78
78
  */
79
79
  const onUploadError = useCallback((event) => {
80
- const { id, error } = event;
81
- logError(MediaCloudErrors.TUS_UPLOAD_ERROR);
82
- setActiveUploads((prev) => prev.map((upload) => upload.id === id ? {
80
+ const { filename, error } = event;
81
+ logError(MediaCloudErrors.TUS_UPLOAD_ERROR.message);
82
+ setActiveUploads((prev) => prev.map((upload) => upload.filename === filename ? {
83
83
  ...upload,
84
84
  error,
85
85
  status: "completed"
86
86
  } : upload));
87
87
  }, [logError]);
88
88
  /**
89
- * Handles the 'add-upload' event
89
+ * Handles the 'addUpload' event
90
90
  * @param event - The add upload event
91
91
  */
92
92
  const onAddUpload = useCallback((event) => {
93
93
  const upload = {
94
- id: event.id,
95
94
  filename: event.filename,
95
+ uploadId: event.uploadId,
96
96
  progress: 0,
97
97
  polling: event.polling,
98
98
  pollingUrl: event.pollingUrl,
@@ -103,12 +103,12 @@ function UploadManagerProvider(args) {
103
103
  if (activeTabRef.current !== "uploading") setActiveTab("uploading");
104
104
  }, []);
105
105
  /**
106
- * Handles the 'update-upload' event
106
+ * Handles the 'updateUpload' event
107
107
  * @param event - The update upload event
108
108
  */
109
109
  const onUpdateUpload = useCallback((event) => {
110
- const { id, progress, polling } = event;
111
- setActiveUploads((prev) => prev.map((upload) => upload.id === id ? {
110
+ const { filename, progress, polling } = event;
111
+ setActiveUploads((prev) => prev.map((upload) => upload.filename === filename ? {
112
112
  ...upload,
113
113
  progress,
114
114
  ...polling !== void 0 && { polling },
@@ -116,25 +116,25 @@ function UploadManagerProvider(args) {
116
116
  } : upload));
117
117
  }, []);
118
118
  /**
119
- * Handles the 'remove-upload' event
119
+ * Handles the 'removeUpload' event
120
120
  * @param event - The remove upload event
121
121
  */
122
122
  const onRemoveUpload = useCallback((event) => {
123
- setActiveUploads((prev) => prev.filter((upload) => upload.id !== event.id));
123
+ setActiveUploads((prev) => prev.filter((upload) => upload.filename !== event.filename));
124
124
  }, []);
125
125
  /**
126
- * Handles the 'upload-completed' event
126
+ * Handles the 'uploadComplete' event
127
127
  * @param event - The upload completed event
128
128
  */
129
- const onUploadCompleted = useCallback((event) => {
130
- if (activeUploadsRef.current.find((upload) => upload.id === event.id)?.pollingUrl) setActiveUploads((prev) => prev.map((upload) => upload.id === event.id ? {
129
+ const onUploadComplete = useCallback((event) => {
130
+ if (activeUploadsRef.current.find((upload) => upload.filename === event.filename)?.pollingUrl) setActiveUploads((prev) => prev.map((upload) => upload.filename === event.filename ? {
131
131
  ...upload,
132
132
  polling: true,
133
133
  progress: 100,
134
134
  status: "processing"
135
135
  } : upload));
136
136
  else setActiveUploads((prev) => {
137
- const updatedUploads = prev.map((upload) => upload.id === event.id ? {
137
+ const updatedUploads = prev.map((upload) => upload.filename === event.filename ? {
138
138
  ...upload,
139
139
  progress: 100,
140
140
  status: "completed"
@@ -144,17 +144,17 @@ function UploadManagerProvider(args) {
144
144
  });
145
145
  }, [checkAutoSwitchToCompleted]);
146
146
  useEffect(() => {
147
- emitter.on("add-upload", onAddUpload);
148
- emitter.on("update-upload", onUpdateUpload);
149
- emitter.on("remove-upload", onRemoveUpload);
150
- emitter.on("upload-errored", onUploadError);
151
- emitter.on("upload-completed", onUploadCompleted);
147
+ emitter.on("addUpload", onAddUpload);
148
+ emitter.on("updateUpload", onUpdateUpload);
149
+ emitter.on("removeUpload", onRemoveUpload);
150
+ emitter.on("uploadError", onUploadError);
151
+ emitter.on("uploadComplete", onUploadComplete);
152
152
  return () => {
153
- emitter.off("add-upload", onAddUpload);
154
- emitter.off("update-upload", onUpdateUpload);
155
- emitter.off("remove-upload", onRemoveUpload);
156
- emitter.off("upload-errored", onUploadError);
157
- emitter.off("upload-completed", onUploadCompleted);
153
+ emitter.off("addUpload", onAddUpload);
154
+ emitter.off("updateUpload", onUpdateUpload);
155
+ emitter.off("removeUpload", onRemoveUpload);
156
+ emitter.off("uploadError", onUploadError);
157
+ emitter.off("uploadComplete", onUploadComplete);
158
158
  };
159
159
  }, [
160
160
  emitter,
@@ -162,16 +162,15 @@ function UploadManagerProvider(args) {
162
162
  onUpdateUpload,
163
163
  onRemoveUpload,
164
164
  onUploadError,
165
- onUploadCompleted
165
+ onUploadComplete
166
166
  ]);
167
167
  /**
168
168
  * Adds a new upload to the manager
169
169
  * @param args - The upload arguments
170
170
  */
171
171
  const addUpload = useCallback((args$1) => {
172
- const { id, filename, polling = false, pollingUrl } = args$1;
172
+ const { filename, polling = false, pollingUrl } = args$1;
173
173
  const upload = {
174
- id,
175
174
  filename,
176
175
  progress: 0,
177
176
  polling,
@@ -185,8 +184,8 @@ function UploadManagerProvider(args) {
185
184
  * @param args - The update arguments
186
185
  */
187
186
  const updateUpload = useCallback((args$1) => {
188
- const { id, progress, polling } = args$1;
189
- setActiveUploads((prev) => prev.map((upload) => upload.id === id ? {
187
+ const { filename, progress, polling } = args$1;
188
+ setActiveUploads((prev) => prev.map((upload) => upload.filename === filename ? {
190
189
  ...upload,
191
190
  progress,
192
191
  ...polling !== void 0 && { polling },
@@ -204,7 +203,7 @@ function UploadManagerProvider(args) {
204
203
  function renderUploadList(args$1) {
205
204
  const { uploads } = args$1;
206
205
  return <ul>
207
- {uploads.map((upload) => <li key={upload.id} data-status={upload.status}>
206
+ {uploads.map((upload) => <li key={upload.filename} data-status={upload.status}>
208
207
  <div className="upload-info">
209
208
  <span className="upload-filename">{upload.filename}</span>
210
209
  <span className="upload-meta">