@adonis-agora/media 0.8.1 → 0.9.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/CHANGELOG.md +121 -0
- package/README.md +206 -0
- package/dist/providers/media_provider.d.ts +3 -13
- package/dist/providers/media_provider.d.ts.map +1 -1
- package/dist/providers/media_provider.js +151 -4
- package/dist/providers/media_provider.js.map +1 -1
- package/dist/src/content_type.d.ts +6 -1
- package/dist/src/content_type.d.ts.map +1 -1
- package/dist/src/content_type.js +102 -7
- package/dist/src/content_type.js.map +1 -1
- package/dist/src/define_config.d.ts +88 -5
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js +7 -2
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/direct_upload.d.ts +177 -0
- package/dist/src/direct_upload.d.ts.map +1 -0
- package/dist/src/direct_upload.js +299 -0
- package/dist/src/direct_upload.js.map +1 -0
- package/dist/src/direct_upload_handler.d.ts +83 -0
- package/dist/src/direct_upload_handler.d.ts.map +1 -0
- package/dist/src/direct_upload_handler.js +180 -0
- package/dist/src/direct_upload_handler.js.map +1 -0
- package/dist/src/disks/factory.d.ts +11 -3
- package/dist/src/disks/factory.d.ts.map +1 -1
- package/dist/src/disks/factory.js +5 -3
- package/dist/src/disks/factory.js.map +1 -1
- package/dist/src/disks/s3.d.ts +10 -0
- package/dist/src/disks/s3.d.ts.map +1 -1
- package/dist/src/disks/s3.js +96 -22
- package/dist/src/disks/s3.js.map +1 -1
- package/dist/src/disks/sigv4.d.ts +58 -0
- package/dist/src/disks/sigv4.d.ts.map +1 -0
- package/dist/src/disks/sigv4.js +87 -0
- package/dist/src/disks/sigv4.js.map +1 -0
- package/dist/src/disks/xml_fallback.d.ts +3 -3
- package/dist/src/disks/xml_fallback.js +3 -3
- package/dist/src/errors.d.ts +104 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +145 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/hls/delivery.d.ts +112 -0
- package/dist/src/hls/delivery.d.ts.map +1 -0
- package/dist/src/hls/delivery.js +118 -0
- package/dist/src/hls/delivery.js.map +1 -0
- package/dist/src/hls/playlist.d.ts +67 -0
- package/dist/src/hls/playlist.d.ts.map +1 -0
- package/dist/src/hls/playlist.js +126 -0
- package/dist/src/hls/playlist.js.map +1 -0
- package/dist/src/index.d.ts +18 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +13 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/media_collection.d.ts +8 -1
- package/dist/src/media_collection.d.ts.map +1 -1
- package/dist/src/media_collection.js +23 -1
- package/dist/src/media_collection.js.map +1 -1
- package/dist/src/media_library.d.ts +32 -2
- package/dist/src/media_library.d.ts.map +1 -1
- package/dist/src/media_library.js +218 -11
- package/dist/src/media_library.js.map +1 -1
- package/dist/src/media_manager.d.ts +37 -2
- package/dist/src/media_manager.d.ts.map +1 -1
- package/dist/src/media_manager.js +54 -1
- package/dist/src/media_manager.js.map +1 -1
- package/dist/src/media_record.d.ts +22 -2
- package/dist/src/media_record.d.ts.map +1 -1
- package/dist/src/services/booted_app.d.ts +9 -0
- package/dist/src/services/booted_app.d.ts.map +1 -0
- package/dist/src/services/booted_app.js +33 -0
- package/dist/src/services/booted_app.js.map +1 -0
- package/dist/src/services/main.d.ts +12 -0
- package/dist/src/services/main.d.ts.map +1 -1
- package/dist/src/services/main.js +15 -3
- package/dist/src/services/main.js.map +1 -1
- package/dist/src/single_file_store.d.ts +4 -0
- package/dist/src/single_file_store.d.ts.map +1 -1
- package/dist/src/single_file_store.js +8 -4
- package/dist/src/single_file_store.js.map +1 -1
- package/dist/src/testing/fake_transformer.d.ts +32 -0
- package/dist/src/testing/fake_transformer.d.ts.map +1 -0
- package/dist/src/testing/fake_transformer.js +32 -0
- package/dist/src/testing/fake_transformer.js.map +1 -0
- package/dist/src/testing/index.d.ts +2 -0
- package/dist/src/testing/index.d.ts.map +1 -1
- package/dist/src/testing/index.js +1 -0
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/transformer.d.ts +111 -0
- package/dist/src/transformer.d.ts.map +1 -0
- package/dist/src/transformer.js +2 -0
- package/dist/src/transformer.js.map +1 -0
- package/dist/src/transformers/factory.d.ts +30 -0
- package/dist/src/transformers/factory.d.ts.map +1 -0
- package/dist/src/transformers/factory.js +34 -0
- package/dist/src/transformers/factory.js.map +1 -0
- package/dist/src/transformers/hls.d.ts +110 -0
- package/dist/src/transformers/hls.d.ts.map +1 -0
- package/dist/src/transformers/hls.js +111 -0
- package/dist/src/transformers/hls.js.map +1 -0
- package/dist/src/transformers/mediabunny.d.ts +5 -0
- package/dist/src/transformers/mediabunny.d.ts.map +1 -0
- package/dist/src/transformers/mediabunny.js +150 -0
- package/dist/src/transformers/mediabunny.js.map +1 -0
- package/dist/src/transformers/mediabunny_loader.d.ts +15 -0
- package/dist/src/transformers/mediabunny_loader.d.ts.map +1 -0
- package/dist/src/transformers/mediabunny_loader.js +21 -0
- package/dist/src/transformers/mediabunny_loader.js.map +1 -0
- package/dist/src/transformers/probe.d.ts +56 -0
- package/dist/src/transformers/probe.d.ts.map +1 -0
- package/dist/src/transformers/probe.js +32 -0
- package/dist/src/transformers/probe.js.map +1 -0
- package/dist/src/transformers/source_file.d.ts +20 -0
- package/dist/src/transformers/source_file.d.ts.map +1 -0
- package/dist/src/transformers/source_file.js +30 -0
- package/dist/src/transformers/source_file.js.map +1 -0
- package/dist/stubs/config/media.stub +12 -0
- package/package.json +11 -7
- package/LICENSE +0 -21
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playlist.js","sourceRoot":"","sources":["../../../src/hls/playlist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,2FAA2F;AAC3F,MAAM,CAAC,MAAM,yBAAyB,GAAG,+BAA+B,CAAC;AAEzE,yCAAyC;AACzC,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAyBrD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAe,EACf,OAAuB;IAEvB,yEAAyE;IACzE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE;YAAE,SAAS;QAEhD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC5E,SAAS,CAAC,2BAA2B;QACvC,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,GAAG,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;YAAE,SAAS;QAChD,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnE,kFAAkF;QAClF,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAE3C,kEAAkE;AAClE,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,OAAuB;IACjE,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAEzE,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACrD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAW,CAAC;QAC/B,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,GAAG,KAAK,EAAE,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YACrC,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;YACxE,MAAM,IAAI,QAAQ,WAAW,GAAG,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC;QACD,QAAQ,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC3C,CAAC;IACD,OAAO,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,kBAAkB;IACzD,OAAO,CAAC,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAW,CAAC;IAChD,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,GAAW;IAC9D,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAW,CAAC;IAChD,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,OAAO,IAAI,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACnD,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG;YAAE,SAAS;QAChD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACvC,QAAQ,CAAC,GAAG,EAAE,CAAC;YACf,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,yBAAyB,CAAC;IAC9D,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,wBAAwB,CAAC;IAC3D,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,WAAW,CAAC;IACzE,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,WAAW,CAAC;IAC/C,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,YAAY,CAAC;IAChD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,UAAU,CAAC;IAC9C,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,17 +1,32 @@
|
|
|
1
|
-
export { defineConfig, stores, processors, disks, uploadSessions } from './define_config.js';
|
|
2
|
-
export type { MediaConfig, MediaUploadsConfig, MediaResumableConfig, MediaDeliveryConfig, StoreContext, StoreFactory, LucidStoreConfig, ImageProcessorFactory, DiskFactory, S3DiskConfig, S3Credentials, UploadMode, UploadSessionStoreContext, UploadSessionStoreFactory, LucidUploadSessionStoreConfig, } from './define_config.js';
|
|
1
|
+
export { defineConfig, stores, processors, disks, uploadSessions, transformers, } from './define_config.js';
|
|
2
|
+
export type { InferConversions, InferTransformers, MediaConfig, MediaUploadsConfig, MediaResumableConfig, MediaDirectUploadConfig, MediaDeliveryConfig, StoreContext, StoreFactory, LucidStoreConfig, ImageProcessorFactory, DiskFactory, S3DiskConfig, S3Credentials, UploadMode, UploadSessionStoreContext, UploadSessionStoreFactory, LucidUploadSessionStoreConfig, } from './define_config.js';
|
|
3
3
|
export { MediaManager } from './media_manager.js';
|
|
4
4
|
export type { MediaManagerOptions } from './media_manager.js';
|
|
5
5
|
export { UploadManager } from './upload_manager.js';
|
|
6
6
|
export type { UploadManagerOptions, InitiateDirectUploadInput, DirectUploadCreated, PresignPartInput, CompleteDirectUploadInput, AbortDirectUploadInput, ProxyUploadInput, } from './upload_manager.js';
|
|
7
7
|
export { resolveUploadMode } from './upload_mode.js';
|
|
8
8
|
export type { ResolvedUploadMode, UploadModeLevels } from './upload_mode.js';
|
|
9
|
+
export { DirectUploadManager, DEFAULT_DIRECT_PART_SIZE, MIN_DIRECT_PART_SIZE, MAX_DIRECT_PARTS, } from './direct_upload.js';
|
|
10
|
+
export type { DirectUploadManagerOptions, DirectUploadInitiateInput, DirectUploadCreatedSession, DirectUploadStatus, DirectUploadPartUrl, } from './direct_upload.js';
|
|
11
|
+
export { DirectUploadHandler } from './direct_upload_handler.js';
|
|
12
|
+
export type { DirectUploadHandlerOptions, DirectUploadRequest, DirectUploadResponse, } from './direct_upload_handler.js';
|
|
13
|
+
export { presignS3Url } from './disks/sigv4.js';
|
|
14
|
+
export type { PresignS3UrlInput, SigV4Credentials } from './disks/sigv4.js';
|
|
9
15
|
export { ResumableUploadManager } from './resumable_upload.js';
|
|
10
16
|
export type { ResumableUploadManagerOptions, UploadSession, UploadSessionStore, UploadSessionListFilter, CreateUploadInput, } from './resumable_upload.js';
|
|
11
17
|
export { TusUploadHandler, parseTusMetadata, TUS_VERSION } from './tus.js';
|
|
12
18
|
export type { TusRequest, TusResponse, TusUploadHandlerOptions } from './tus.js';
|
|
13
19
|
export { MediaDeliveryHandler, resolveDeliveryMode, DEFAULT_DELIVERY_SIGNED_TTL_SECONDS, } from './delivery.js';
|
|
14
20
|
export type { DeliveryMode, ResolvedDeliveryMode, DeliveryResult, DeliveryRequest, MediaDeliveryHandlerOptions, } from './delivery.js';
|
|
21
|
+
export type { Transformer, TransformerContext, TransformerWriteOptions, TransformResult, } from './transformer.js';
|
|
22
|
+
export { HlsTransformer, HLS_ENTRY_PLAYLIST, resolveWebCodecsSupport } from './transformers/hls.js';
|
|
23
|
+
export type { HlsTransformerOptions, HlsRemuxEngine, HlsRemuxRequest, HlsRemuxSummary, WebCodecsProvider, WebCodecsSupport, } from './transformers/hls.js';
|
|
24
|
+
export { MetadataProbeTransformer } from './transformers/probe.js';
|
|
25
|
+
export type { MetadataProbeTransformerOptions, MediaProbeEngine, MediaProbeSummary, } from './transformers/probe.js';
|
|
26
|
+
export { rewriteHlsPlaylist, isRelativeUri, classifyUri, resolvePackagePath, hlsArtifactContentType, HLS_PLAYLIST_CONTENT_TYPE, HLS_SEGMENT_CONTENT_TYPE, } from './hls/playlist.js';
|
|
27
|
+
export type { HlsUriRef, HlsUriRewriter } from './hls/playlist.js';
|
|
28
|
+
export { HlsDeliveryHandler } from './hls/delivery.js';
|
|
29
|
+
export type { HlsDeliveryHandlerOptions, HlsDeliveryRequest, HlsDeliveryResult, HlsFileRef, HlsUrlBuilder, } from './hls/delivery.js';
|
|
15
30
|
export { detectMimeType, isDetectableMimeType, isClosedSignatureWhitelist, verifyContentAgainstWhitelist, SIGNATURE_HEAD_BYTES, } from './content_type.js';
|
|
16
31
|
export type { ContentVerdict } from './content_type.js';
|
|
17
32
|
export { createDriveBackedResolver } from './disks/drive.js';
|
|
@@ -32,6 +47,6 @@ export type { MediaCollectionConfig } from './media_collection.js';
|
|
|
32
47
|
export type { ConversionPreset, ConversionResult, ImageProcessor } from './image_processor.js';
|
|
33
48
|
export type { Disk, DiskResolver, DiskWriteOptions, DiskMetaData, SignedUrlOptions, MultipartPart, MultipartUploadDisk, ExtendedDisk, DiskCapabilities, DiskStat, CopyOptions, ListOptions, ListEntry, ListResult, } from './types.js';
|
|
34
49
|
export type { MediaDiagnosticEvent, MediaDiagnosticPayloads, AttachPayload, DeletePayload, ConversionPayload, AttachmentCreatePayload, AttachmentDeletePayload, UploadStartPayload, UploadProgressPayload, UploadCompletePayload, UploadAbortPayload, } from './diagnostics.js';
|
|
35
|
-
export { MimeNotAllowedError, ContentTypeMismatchError, ContentSignatureUnrecognizedError, DriveNotReadyError, MediaNotFoundError, MediaObjectMissingError, ConversionNotDefinedError, ImageProcessorMissingError, VariantNotFoundError, StoreNotConfiguredError, UploadNotSupportedError, UploadSessionNotFoundError, UploadOffsetConflictError, UploadSessionExpiredError, UploadSessionStoreNotConfiguredError, ResumableUploadsNotConfiguredError, } from './errors.js';
|
|
50
|
+
export { MimeNotAllowedError, ContentTypeMismatchError, ContentSignatureUnrecognizedError, DriveNotReadyError, MediaNotFoundError, MediaObjectMissingError, ConversionNotDefinedError, ConversionArtifactMissingError, TransformerNotDefinedError, TransformNotReadyError, TransformerConflictError, TransformerRuntimeMissingError, TransformerOutputError, HlsSourceUnsupportedError, ImageProcessorMissingError, VariantNotFoundError, StoreNotConfiguredError, UploadNotSupportedError, UploadSessionNotFoundError, UploadOffsetConflictError, UploadSessionExpiredError, UploadSessionStoreNotConfiguredError, ResumableUploadsNotConfiguredError, DirectUploadsNotConfiguredError, UploadPartSizeError, UploadPartOutOfRangeError, UploadPartsIncompleteError, } from './errors.js';
|
|
36
51
|
export { configure } from '../configure.js';
|
|
37
52
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/src/index.js
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
// Config idiom
|
|
2
|
-
export { defineConfig, stores, processors, disks, uploadSessions } from './define_config.js';
|
|
2
|
+
export { defineConfig, stores, processors, disks, uploadSessions, transformers, } from './define_config.js';
|
|
3
3
|
// Manager + layers
|
|
4
4
|
export { MediaManager } from './media_manager.js';
|
|
5
5
|
// Direct-S3 upload modes (proxy + direct multipart)
|
|
6
6
|
export { UploadManager } from './upload_manager.js';
|
|
7
7
|
export { resolveUploadMode } from './upload_mode.js';
|
|
8
|
+
// Session-backed direct uploads (browser→S3 multipart with a persisted, resumable session)
|
|
9
|
+
export { DirectUploadManager, DEFAULT_DIRECT_PART_SIZE, MIN_DIRECT_PART_SIZE, MAX_DIRECT_PARTS, } from './direct_upload.js';
|
|
10
|
+
export { DirectUploadHandler } from './direct_upload_handler.js';
|
|
11
|
+
// Hand-rolled SigV4 query presigner (what the S3 disk signs part/GET URLs with; exported for
|
|
12
|
+
// wiring presigned URLs outside the bundled disk)
|
|
13
|
+
export { presignS3Url } from './disks/sigv4.js';
|
|
8
14
|
// Resumable (TUS) uploads
|
|
9
15
|
export { ResumableUploadManager } from './resumable_upload.js';
|
|
10
16
|
export { TusUploadHandler, parseTusMetadata, TUS_VERSION } from './tus.js';
|
|
11
17
|
// Configurable delivery (the read-side counterpart to the upload modes)
|
|
12
18
|
export { MediaDeliveryHandler, resolveDeliveryMode, DEFAULT_DELIVERY_SIGNED_TTL_SECONDS, } from './delivery.js';
|
|
19
|
+
export { HlsTransformer, HLS_ENTRY_PLAYLIST, resolveWebCodecsSupport } from './transformers/hls.js';
|
|
20
|
+
export { MetadataProbeTransformer } from './transformers/probe.js';
|
|
21
|
+
// HLS delivery (playlist rewriting + the framework-agnostic package handler)
|
|
22
|
+
export { rewriteHlsPlaylist, isRelativeUri, classifyUri, resolvePackagePath, hlsArtifactContentType, HLS_PLAYLIST_CONTENT_TYPE, HLS_SEGMENT_CONTENT_TYPE, } from './hls/playlist.js';
|
|
23
|
+
export { HlsDeliveryHandler } from './hls/delivery.js';
|
|
13
24
|
// Real-content validation (magic-byte signatures behind `acceptsMimeTypes`)
|
|
14
25
|
export { detectMimeType, isDetectableMimeType, isClosedSignatureWhitelist, verifyContentAgainstWhitelist, SIGNATURE_HEAD_BYTES, } from './content_type.js';
|
|
15
26
|
// Drive-backed disk resolution (the provider's resolver; exported for wiring media outside AdonisJS)
|
|
@@ -22,7 +33,7 @@ export { StorageManager } from './storage_manager.js';
|
|
|
22
33
|
export { DEFAULT_MEDIA_LIST_LIMIT, MAX_MEDIA_LIST_LIMIT, encodeMediaCursor, decodeMediaCursor, clampMediaListLimit, } from './media_store.js';
|
|
23
34
|
export { MediaCollectionRegistry } from './media_collection.js';
|
|
24
35
|
// Errors
|
|
25
|
-
export { MimeNotAllowedError, ContentTypeMismatchError, ContentSignatureUnrecognizedError, DriveNotReadyError, MediaNotFoundError, MediaObjectMissingError, ConversionNotDefinedError, ImageProcessorMissingError, VariantNotFoundError, StoreNotConfiguredError, UploadNotSupportedError, UploadSessionNotFoundError, UploadOffsetConflictError, UploadSessionExpiredError, UploadSessionStoreNotConfiguredError, ResumableUploadsNotConfiguredError, } from './errors.js';
|
|
36
|
+
export { MimeNotAllowedError, ContentTypeMismatchError, ContentSignatureUnrecognizedError, DriveNotReadyError, MediaNotFoundError, MediaObjectMissingError, ConversionNotDefinedError, ConversionArtifactMissingError, TransformerNotDefinedError, TransformNotReadyError, TransformerConflictError, TransformerRuntimeMissingError, TransformerOutputError, HlsSourceUnsupportedError, ImageProcessorMissingError, VariantNotFoundError, StoreNotConfiguredError, UploadNotSupportedError, UploadSessionNotFoundError, UploadOffsetConflictError, UploadSessionExpiredError, UploadSessionStoreNotConfiguredError, ResumableUploadsNotConfiguredError, DirectUploadsNotConfiguredError, UploadPartSizeError, UploadPartOutOfRangeError, UploadPartsIncompleteError, } from './errors.js';
|
|
26
37
|
// `node ace configure` reaches the hook through this entry point, not through the package's
|
|
27
38
|
// `./configure` export path.
|
|
28
39
|
export { configure } from '../configure.js';
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,OAAO,EACL,YAAY,EACZ,MAAM,EACN,UAAU,EACV,KAAK,EACL,cAAc,EACd,YAAY,GACb,MAAM,oBAAoB,CAAC;AAsB5B,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,oDAAoD;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAUpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,2FAA2F;AAC3F,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAOjE,6FAA6F;AAC7F,kDAAkD;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,0BAA0B;AAC1B,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAQ/D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE3E,wEAAwE;AACxE,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,mCAAmC,GACpC,MAAM,eAAe,CAAC;AAgBvB,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AASpG,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAOnE,6EAA6E;AAC7E,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,sBAAsB,EACtB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AASvD,4EAA4E;AAC5E,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,0BAA0B,EAC1B,6BAA6B,EAC7B,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAG3B,qGAAqG;AACrG,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAO7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AASlD,OAAO,EACL,UAAU,EACV,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AASzB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAMtD,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAmChE,SAAS;AACT,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,iCAAiC,EACjC,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,8BAA8B,EAC9B,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,8BAA8B,EAC9B,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,oCAAoC,EACpC,kCAAkC,EAClC,+BAA+B,EAC/B,mBAAmB,EACnB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AAErB,4FAA4F;AAC5F,6BAA6B;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ConversionPreset } from './image_processor.js';
|
|
2
|
+
import type { Transformer } from './transformer.js';
|
|
2
3
|
export interface MediaCollectionConfig {
|
|
3
4
|
name: string;
|
|
4
5
|
/** Single-file collection: attaching replaces any existing media. */
|
|
@@ -9,10 +10,16 @@ export interface MediaCollectionConfig {
|
|
|
9
10
|
acceptsMimeTypes?: string[];
|
|
10
11
|
/** Image conversion presets available for this collection. */
|
|
11
12
|
conversions?: ConversionPreset[];
|
|
13
|
+
/**
|
|
14
|
+
* Content transformers available for this collection (e.g. `transformers.hls()`). Each produces
|
|
15
|
+
* a named entry in `record.conversions`, so transformer names and preset names share one
|
|
16
|
+
* namespace — a collision throws {@link TransformerConflictError} at construction.
|
|
17
|
+
*/
|
|
18
|
+
transformers?: Transformer[];
|
|
12
19
|
}
|
|
13
20
|
export declare class MediaCollectionRegistry {
|
|
14
21
|
private readonly map;
|
|
15
|
-
constructor(collections?: MediaCollectionConfig[]);
|
|
22
|
+
constructor(collections?: readonly MediaCollectionConfig[]);
|
|
16
23
|
/** Registered config for a collection, or a permissive default. */
|
|
17
24
|
get(name: string): MediaCollectionConfig;
|
|
18
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media_collection.d.ts","sourceRoot":"","sources":["../../src/media_collection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"media_collection.d.ts","sourceRoot":"","sources":["../../src/media_collection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC;;;;OAIG;IACH,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;CAC9B;AAED,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA4C;gBAEpD,WAAW,GAAE,SAAS,qBAAqB,EAAO;IAO9D,mEAAmE;IACnE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB;CAGzC"}
|
|
@@ -1,12 +1,34 @@
|
|
|
1
|
+
import { TransformerConflictError } from './errors.js';
|
|
1
2
|
export class MediaCollectionRegistry {
|
|
2
3
|
map = new Map();
|
|
3
4
|
constructor(collections = []) {
|
|
4
|
-
for (const c of collections)
|
|
5
|
+
for (const c of collections) {
|
|
6
|
+
assertDistinctDerivativeNames(c);
|
|
5
7
|
this.map.set(c.name, c);
|
|
8
|
+
}
|
|
6
9
|
}
|
|
7
10
|
/** Registered config for a collection, or a permissive default. */
|
|
8
11
|
get(name) {
|
|
9
12
|
return this.map.get(name) ?? { name };
|
|
10
13
|
}
|
|
11
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Presets and transformers both persist under `record.conversions[name]`, so within one collection
|
|
17
|
+
* every derivative name must be unique across BOTH lists. Checked eagerly at registry construction
|
|
18
|
+
* — a collision is a config bug, and failing at boot beats failing on the first attach.
|
|
19
|
+
*/
|
|
20
|
+
function assertDistinctDerivativeNames(collection) {
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
for (const preset of collection.conversions ?? []) {
|
|
23
|
+
if (seen.has(preset.name))
|
|
24
|
+
throw new TransformerConflictError(collection.name, preset.name);
|
|
25
|
+
seen.add(preset.name);
|
|
26
|
+
}
|
|
27
|
+
for (const transformer of collection.transformers ?? []) {
|
|
28
|
+
if (seen.has(transformer.name)) {
|
|
29
|
+
throw new TransformerConflictError(collection.name, transformer.name);
|
|
30
|
+
}
|
|
31
|
+
seen.add(transformer.name);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
12
34
|
//# sourceMappingURL=media_collection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media_collection.js","sourceRoot":"","sources":["../../src/media_collection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"media_collection.js","sourceRoot":"","sources":["../../src/media_collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAsBvD,MAAM,OAAO,uBAAuB;IACjB,GAAG,GAAG,IAAI,GAAG,EAAiC,CAAC;IAEhE,YAAY,cAAgD,EAAE;QAC5D,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5B,6BAA6B,CAAC,CAAC,CAAC,CAAC;YACjC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;IACxC,CAAC;CACF;AAED;;;;GAIG;AACH,SAAS,6BAA6B,CAAC,UAAiC;IACtE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,MAAM,IAAI,UAAU,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;QAClD,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,wBAAwB,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5F,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IACD,KAAK,MAAM,WAAW,IAAI,UAAU,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC;QACxD,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,wBAAwB,CAAC,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;QACxE,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC"}
|
|
@@ -9,7 +9,7 @@ import type { SignedUrlOptions } from './types.js';
|
|
|
9
9
|
export interface MediaLibraryOptions {
|
|
10
10
|
storage: StorageManager;
|
|
11
11
|
store: MediaStore;
|
|
12
|
-
collections?: MediaCollectionConfig[];
|
|
12
|
+
collections?: readonly MediaCollectionConfig[];
|
|
13
13
|
/** Engine for image conversions. Required only if collections define conversions. */
|
|
14
14
|
imageProcessor?: ImageProcessor;
|
|
15
15
|
/** Emit `agora:media:*` diagnostics events (default true). */
|
|
@@ -105,7 +105,13 @@ export interface OwnerMediaBinding {
|
|
|
105
105
|
}
|
|
106
106
|
export declare class MediaLibrary {
|
|
107
107
|
#private;
|
|
108
|
-
|
|
108
|
+
/**
|
|
109
|
+
* The storage façade the library resolves disks through. Public (read-only) so delivery
|
|
110
|
+
* composition — {@link HlsDeliveryHandler} serving individual package artifacts, custom handlers
|
|
111
|
+
* doing the same — can reach the disk of a record it already holds without re-plumbing a
|
|
112
|
+
* resolver. Writes still belong to the library's own methods.
|
|
113
|
+
*/
|
|
114
|
+
readonly storage: StorageManager;
|
|
109
115
|
private readonly store;
|
|
110
116
|
/**
|
|
111
117
|
* Registered collection configs. Public because `acceptsMimeTypes` must have exactly one source
|
|
@@ -145,6 +151,30 @@ export declare class MediaLibrary {
|
|
|
145
151
|
attachExisting(input: AttachExistingInput): Promise<MediaRecord>;
|
|
146
152
|
/** Generate (if absent) and persist a named conversion; returns the updated record. Lazy entry point. */
|
|
147
153
|
ensureConversion(id: string, conversionName: string): Promise<MediaRecord>;
|
|
154
|
+
/**
|
|
155
|
+
* Run a named {@link Transformer} of the record's collection and persist its output as
|
|
156
|
+
* `record.conversions[name]`. **Idempotent**: an already-generated conversion returns the record
|
|
157
|
+
* untouched, so a retried job skips straight through — the library assumes no queue system, it
|
|
158
|
+
* only guarantees this call is safe to repeat.
|
|
159
|
+
*
|
|
160
|
+
* ```ts
|
|
161
|
+
* // app job (the app owns the queue, retries and any locking):
|
|
162
|
+
* await media.library.transform(payload.mediaId, 'hls')
|
|
163
|
+
* ```
|
|
164
|
+
*
|
|
165
|
+
* Deliberately explicit: unlike image presets, a transformer conversion is never generated
|
|
166
|
+
* lazily inside `url()`/`deliver()` — reads on a missing one throw {@link TransformNotReadyError}
|
|
167
|
+
* instead of stalling a request behind a remux. Transformers marked `eager: true` are the
|
|
168
|
+
* exception and run inside attach (see {@link Transformer}).
|
|
169
|
+
*
|
|
170
|
+
* Two concurrent calls for the same media+name both run and the second save wins; the artifacts
|
|
171
|
+
* are deterministic per key, so the result is identical — wasteful, not corrupting. Serialize
|
|
172
|
+
* with your own lock if the double work matters.
|
|
173
|
+
*
|
|
174
|
+
* A mid-flight failure sweeps whatever artifacts were already written (best-effort) and
|
|
175
|
+
* re-throws, so no conversion entry is ever persisted for a half-written package.
|
|
176
|
+
*/
|
|
177
|
+
transform(id: string, transformerName: string): Promise<MediaRecord>;
|
|
148
178
|
list(ownerType: string, ownerId: string | number, collection?: string): Promise<MediaRecord[]>;
|
|
149
179
|
find(id: string): Promise<MediaRecord | null>;
|
|
150
180
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media_library.d.ts","sourceRoot":"","sources":["../../src/media_library.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,cAAc,EAEpB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"media_library.d.ts","sourceRoot":"","sources":["../../src/media_library.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,10 +3,16 @@ import { SIGNATURE_HEAD_BYTES, verifyContentAgainstWhitelist } from './content_t
|
|
|
3
3
|
import { asBuffer, peekContents, peekStream, toBytes } from './contents.js';
|
|
4
4
|
import { DEFAULT_DELIVERY_SIGNED_TTL_SECONDS, resolveDeliveryMode, } from './delivery.js';
|
|
5
5
|
import { publishMedia } from './diagnostics.js';
|
|
6
|
-
import { ContentSignatureUnrecognizedError, ContentTypeMismatchError, ConversionNotDefinedError, ImageProcessorMissingError, MediaNotFoundError, MediaObjectMissingError, MimeNotAllowedError, UploadNotSupportedError, } from './errors.js';
|
|
6
|
+
import { ContentSignatureUnrecognizedError, ContentTypeMismatchError, ConversionArtifactMissingError, ConversionNotDefinedError, ImageProcessorMissingError, MediaNotFoundError, MediaObjectMissingError, MimeNotAllowedError, TransformNotReadyError, TransformerNotDefinedError, TransformerOutputError, UploadNotSupportedError, } from './errors.js';
|
|
7
7
|
import { isExtendedDisk } from './extended_disk.js';
|
|
8
8
|
import { MediaCollectionRegistry } from './media_collection.js';
|
|
9
9
|
export class MediaLibrary {
|
|
10
|
+
/**
|
|
11
|
+
* The storage façade the library resolves disks through. Public (read-only) so delivery
|
|
12
|
+
* composition — {@link HlsDeliveryHandler} serving individual package artifacts, custom handlers
|
|
13
|
+
* doing the same — can reach the disk of a record it already holds without re-plumbing a
|
|
14
|
+
* resolver. Writes still belong to the library's own methods.
|
|
15
|
+
*/
|
|
10
16
|
storage;
|
|
11
17
|
store;
|
|
12
18
|
/**
|
|
@@ -220,14 +226,22 @@ export class MediaLibrary {
|
|
|
220
226
|
await this.#safeDelete(disk, path);
|
|
221
227
|
throw error;
|
|
222
228
|
}
|
|
223
|
-
// Eager presets are generated synchronously on attach; lazy ones on first `url()`.
|
|
224
|
-
//
|
|
225
|
-
|
|
229
|
+
// Eager presets are generated synchronously on attach; lazy ones on first `url()`. Eager
|
|
230
|
+
// transformers run right after them — the hook that makes "TUS drops the bytes, transformers
|
|
231
|
+
// derive from them" automatic, since `attachExisting` (and so `completeUploadToLibrary`)
|
|
232
|
+
// funnels through this same commit. Both run BEFORE `previous` is dropped: a derivation that
|
|
233
|
+
// throws must not leave the owner with nothing.
|
|
234
|
+
const eagerPresets = (config.conversions ?? []).filter((p) => p.eager);
|
|
235
|
+
const eagerTransformers = (config.transformers ?? []).filter((t) => t.eager === true);
|
|
226
236
|
let current = saved;
|
|
227
|
-
if (
|
|
237
|
+
if (eagerPresets.length > 0 || eagerTransformers.length > 0) {
|
|
228
238
|
try {
|
|
229
|
-
for (const preset of
|
|
239
|
+
for (const preset of eagerPresets) {
|
|
230
240
|
current = await this.ensureConversion(saved.id, preset.name);
|
|
241
|
+
}
|
|
242
|
+
for (const transformer of eagerTransformers) {
|
|
243
|
+
current = await this.#runTransformer(current, transformer);
|
|
244
|
+
}
|
|
231
245
|
}
|
|
232
246
|
catch (error) {
|
|
233
247
|
// The new media can't be rendered, so roll it back whole — bytes, any conversions already
|
|
@@ -235,7 +249,7 @@ export class MediaLibrary {
|
|
|
235
249
|
if (input.ownsObject)
|
|
236
250
|
await this.#safeDelete(disk, path);
|
|
237
251
|
for (const variant of Object.values(current.conversions)) {
|
|
238
|
-
await this.#
|
|
252
|
+
await this.#deleteConversionArtifacts(variant, { safe: true });
|
|
239
253
|
}
|
|
240
254
|
await this.store.delete(saved.id);
|
|
241
255
|
throw error;
|
|
@@ -257,7 +271,7 @@ export class MediaLibrary {
|
|
|
257
271
|
if (sharesRow) {
|
|
258
272
|
await this.#safeDelete(record.disk, record.path);
|
|
259
273
|
for (const variant of Object.values(record.conversions)) {
|
|
260
|
-
await this.#
|
|
274
|
+
await this.#deleteConversionArtifacts(variant, { safe: true });
|
|
261
275
|
}
|
|
262
276
|
continue;
|
|
263
277
|
}
|
|
@@ -282,9 +296,16 @@ export class MediaLibrary {
|
|
|
282
296
|
throw new MediaNotFoundError(id);
|
|
283
297
|
if (record.conversions[conversionName])
|
|
284
298
|
return record;
|
|
285
|
-
const
|
|
286
|
-
|
|
299
|
+
const config = this.collections.get(record.collection);
|
|
300
|
+
const preset = (config.conversions ?? []).find((p) => p.name === conversionName);
|
|
301
|
+
if (!preset) {
|
|
302
|
+
// The name belongs to a transformer, not a preset: it IS defined, just not generated — and a
|
|
303
|
+
// read path never generates a transform (assumed heavy). Point the caller at `transform()`.
|
|
304
|
+
if ((config.transformers ?? []).some((t) => t.name === conversionName)) {
|
|
305
|
+
throw new TransformNotReadyError(id, conversionName);
|
|
306
|
+
}
|
|
287
307
|
throw new ConversionNotDefinedError(record.collection, conversionName);
|
|
308
|
+
}
|
|
288
309
|
if (!this.imageProcessor)
|
|
289
310
|
throw new ImageProcessorMissingError();
|
|
290
311
|
const original = asBuffer(await this.storage.disk(record.disk).getBytes(record.path));
|
|
@@ -305,6 +326,129 @@ export class MediaLibrary {
|
|
|
305
326
|
this.emit('conversion', { id, conversion: conversionName, path: conversionPath });
|
|
306
327
|
return updated;
|
|
307
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Run a named {@link Transformer} of the record's collection and persist its output as
|
|
331
|
+
* `record.conversions[name]`. **Idempotent**: an already-generated conversion returns the record
|
|
332
|
+
* untouched, so a retried job skips straight through — the library assumes no queue system, it
|
|
333
|
+
* only guarantees this call is safe to repeat.
|
|
334
|
+
*
|
|
335
|
+
* ```ts
|
|
336
|
+
* // app job (the app owns the queue, retries and any locking):
|
|
337
|
+
* await media.library.transform(payload.mediaId, 'hls')
|
|
338
|
+
* ```
|
|
339
|
+
*
|
|
340
|
+
* Deliberately explicit: unlike image presets, a transformer conversion is never generated
|
|
341
|
+
* lazily inside `url()`/`deliver()` — reads on a missing one throw {@link TransformNotReadyError}
|
|
342
|
+
* instead of stalling a request behind a remux. Transformers marked `eager: true` are the
|
|
343
|
+
* exception and run inside attach (see {@link Transformer}).
|
|
344
|
+
*
|
|
345
|
+
* Two concurrent calls for the same media+name both run and the second save wins; the artifacts
|
|
346
|
+
* are deterministic per key, so the result is identical — wasteful, not corrupting. Serialize
|
|
347
|
+
* with your own lock if the double work matters.
|
|
348
|
+
*
|
|
349
|
+
* A mid-flight failure sweeps whatever artifacts were already written (best-effort) and
|
|
350
|
+
* re-throws, so no conversion entry is ever persisted for a half-written package.
|
|
351
|
+
*/
|
|
352
|
+
async transform(id, transformerName) {
|
|
353
|
+
const record = await this.store.find(id);
|
|
354
|
+
if (!record)
|
|
355
|
+
throw new MediaNotFoundError(id);
|
|
356
|
+
if (record.conversions[transformerName])
|
|
357
|
+
return record;
|
|
358
|
+
const config = this.collections.get(record.collection);
|
|
359
|
+
const transformer = (config.transformers ?? []).find((t) => t.name === transformerName);
|
|
360
|
+
if (!transformer)
|
|
361
|
+
throw new TransformerNotDefinedError(record.collection, transformerName);
|
|
362
|
+
return this.#runTransformer(record, transformer);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Execute one transformer against one record: build the sandboxed {@link TransformerContext},
|
|
366
|
+
* validate the {@link TransformResult} contract, persist the conversion entry, emit
|
|
367
|
+
* `conversion`. The context's `write` is the only artifact path — it pins every write under the
|
|
368
|
+
* conversion prefix and records it, which is what makes the output deletable and servable
|
|
369
|
+
* without trusting the transformer's bookkeeping.
|
|
370
|
+
*/
|
|
371
|
+
async #runTransformer(record, transformer) {
|
|
372
|
+
const diskName = record.disk;
|
|
373
|
+
const disk = this.storage.disk(diskName);
|
|
374
|
+
const dir = record.path.slice(0, record.path.lastIndexOf('/'));
|
|
375
|
+
const outputPrefix = `${dir}/conversions/${transformer.name}/`;
|
|
376
|
+
const written = [];
|
|
377
|
+
const write = async (relativePath, contents, options = {}) => {
|
|
378
|
+
const relative = assertSafeArtifactPath(transformer.name, relativePath);
|
|
379
|
+
const key = `${outputPrefix}${relative}`;
|
|
380
|
+
const writeOptions = {
|
|
381
|
+
...(options.contentType !== undefined ? { contentType: options.contentType } : {}),
|
|
382
|
+
};
|
|
383
|
+
if (contents instanceof Uint8Array) {
|
|
384
|
+
await disk.put(key, contents, writeOptions);
|
|
385
|
+
}
|
|
386
|
+
else if (options.contentLength !== undefined && typeof disk.putStream === 'function') {
|
|
387
|
+
// A sized stream goes straight through — a segment file never has to fit in memory twice.
|
|
388
|
+
await disk.putStream(key, contents, {
|
|
389
|
+
...writeOptions,
|
|
390
|
+
contentLength: options.contentLength,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
await disk.put(key, await toBytes(contents), writeOptions);
|
|
395
|
+
}
|
|
396
|
+
if (!written.includes(relative))
|
|
397
|
+
written.push(relative);
|
|
398
|
+
};
|
|
399
|
+
const context = {
|
|
400
|
+
record,
|
|
401
|
+
disk,
|
|
402
|
+
diskName,
|
|
403
|
+
storage: this.storage,
|
|
404
|
+
getBytes: () => disk.getBytes(record.path),
|
|
405
|
+
getStream: () => disk.getStream(record.path),
|
|
406
|
+
outputPrefix,
|
|
407
|
+
write,
|
|
408
|
+
};
|
|
409
|
+
let result;
|
|
410
|
+
try {
|
|
411
|
+
result = await transformer.transform(context);
|
|
412
|
+
if (result.entry !== undefined && !written.includes(result.entry)) {
|
|
413
|
+
throw new TransformerOutputError(transformer.name, `entry "${result.entry}" was never written through context.write`);
|
|
414
|
+
}
|
|
415
|
+
if (result.entry === undefined && written.length > 0) {
|
|
416
|
+
throw new TransformerOutputError(transformer.name, `it wrote ${written.length} artifact(s) but declared no entry`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
// Sweep the partial output (best-effort) so a retry starts clean and a failed transform
|
|
421
|
+
// never leaks storage; the conversion entry was never persisted, so `transform()` will
|
|
422
|
+
// simply run again.
|
|
423
|
+
for (const relative of written) {
|
|
424
|
+
await this.#safeDelete(diskName, `${outputPrefix}${relative}`);
|
|
425
|
+
}
|
|
426
|
+
throw error;
|
|
427
|
+
}
|
|
428
|
+
const conversion = result.entry !== undefined
|
|
429
|
+
? {
|
|
430
|
+
path: `${outputPrefix}${result.entry}`,
|
|
431
|
+
disk: diskName,
|
|
432
|
+
prefix: outputPrefix,
|
|
433
|
+
files: [...written],
|
|
434
|
+
...(result.meta !== undefined ? { meta: result.meta } : {}),
|
|
435
|
+
}
|
|
436
|
+
: { ...(result.meta !== undefined ? { meta: result.meta } : {}) };
|
|
437
|
+
// Re-read before saving: the transform may have taken minutes, and `save` persists the whole
|
|
438
|
+
// record — merging over a fresh row keeps conversions generated meanwhile.
|
|
439
|
+
const fresh = (await this.store.find(record.id)) ?? record;
|
|
440
|
+
const updated = await this.store.save({
|
|
441
|
+
...fresh,
|
|
442
|
+
conversions: { ...fresh.conversions, [transformer.name]: conversion },
|
|
443
|
+
updatedAt: this.now(),
|
|
444
|
+
});
|
|
445
|
+
this.emit('conversion', {
|
|
446
|
+
id: record.id,
|
|
447
|
+
conversion: transformer.name,
|
|
448
|
+
path: conversion.path ?? '',
|
|
449
|
+
});
|
|
450
|
+
return updated;
|
|
451
|
+
}
|
|
308
452
|
list(ownerType, ownerId, collection) {
|
|
309
453
|
return this.store.listByOwner(ownerType, String(ownerId), collection);
|
|
310
454
|
}
|
|
@@ -333,7 +477,7 @@ export class MediaLibrary {
|
|
|
333
477
|
async #deleteRecord(record) {
|
|
334
478
|
await this.storage.disk(record.disk).delete(record.path);
|
|
335
479
|
for (const conversion of Object.values(record.conversions)) {
|
|
336
|
-
await this
|
|
480
|
+
await this.#deleteConversionArtifacts(conversion, { safe: false });
|
|
337
481
|
}
|
|
338
482
|
await this.store.delete(record.id);
|
|
339
483
|
this.emit('delete', {
|
|
@@ -351,6 +495,48 @@ export class MediaLibrary {
|
|
|
351
495
|
// swallow: this is compensation for a failed write; the original error is what matters
|
|
352
496
|
}
|
|
353
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* Delete every storage key a conversion entry owns: the single artifact of an image conversion,
|
|
500
|
+
* every listed file of a multi-artifact (transformer) conversion, nothing for a metadata-only
|
|
501
|
+
* one. Batched through `deleteMany` on capable disks — an HLS package is hundreds of segments,
|
|
502
|
+
* and one batched call beats hundreds of round-trips. `safe: true` swallows failures
|
|
503
|
+
* (compensation paths must never mask the original error).
|
|
504
|
+
*/
|
|
505
|
+
async #deleteConversionArtifacts(variant, options) {
|
|
506
|
+
let diskName;
|
|
507
|
+
let keys;
|
|
508
|
+
if (variant.prefix !== undefined && variant.files !== undefined && variant.disk !== undefined) {
|
|
509
|
+
diskName = variant.disk;
|
|
510
|
+
keys = variant.files.map((file) => `${variant.prefix}${file}`);
|
|
511
|
+
}
|
|
512
|
+
else if (variant.path !== undefined && variant.disk !== undefined) {
|
|
513
|
+
diskName = variant.disk;
|
|
514
|
+
keys = [variant.path];
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
return; // metadata-only conversion: nothing on any disk
|
|
518
|
+
}
|
|
519
|
+
const disk = this.storage.disk(diskName);
|
|
520
|
+
if (isExtendedDisk(disk)) {
|
|
521
|
+
try {
|
|
522
|
+
await disk.deleteMany(keys);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
catch (error) {
|
|
526
|
+
if (!options.safe)
|
|
527
|
+
throw error;
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
for (const key of keys) {
|
|
532
|
+
if (options.safe) {
|
|
533
|
+
await this.#safeDelete(diskName, key);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
await disk.delete(key);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
354
540
|
/**
|
|
355
541
|
* Resolve a media id (optionally a named conversion of it) to the concrete object on a disk that
|
|
356
542
|
* every read path — {@link url}, {@link signedUrl}, {@link deliver} — then acts on. Requesting a
|
|
@@ -362,6 +548,10 @@ export class MediaLibrary {
|
|
|
362
548
|
const variant = record.conversions[conversion];
|
|
363
549
|
if (!variant)
|
|
364
550
|
throw new MediaNotFoundError(`${id}#${conversion}`);
|
|
551
|
+
// A metadata-only transform (a probe) HAS a conversion entry but no file behind it.
|
|
552
|
+
if (variant.path === undefined || variant.disk === undefined) {
|
|
553
|
+
throw new ConversionArtifactMissingError(id, conversion);
|
|
554
|
+
}
|
|
365
555
|
return {
|
|
366
556
|
record,
|
|
367
557
|
disk: variant.disk,
|
|
@@ -439,4 +629,21 @@ function stripExtension(fileName) {
|
|
|
439
629
|
const dot = fileName.lastIndexOf('.');
|
|
440
630
|
return dot > 0 ? fileName.slice(0, dot) : fileName;
|
|
441
631
|
}
|
|
632
|
+
/**
|
|
633
|
+
* Validate an artifact path a transformer hands to `context.write`: relative, normalized, and
|
|
634
|
+
* incapable of escaping the conversion prefix. Throws {@link TransformerOutputError} — a traversal
|
|
635
|
+
* attempt is a transformer bug (or a transformer echoing hostile input), never something to
|
|
636
|
+
* silently normalize into place.
|
|
637
|
+
*/
|
|
638
|
+
function assertSafeArtifactPath(transformer, relativePath) {
|
|
639
|
+
const normalized = relativePath.replace(/\\/g, '/');
|
|
640
|
+
if (normalized === '' || normalized.startsWith('/') || normalized.endsWith('/')) {
|
|
641
|
+
throw new TransformerOutputError(transformer, `artifact path "${relativePath}" must be a relative file path`);
|
|
642
|
+
}
|
|
643
|
+
const segments = normalized.split('/');
|
|
644
|
+
if (segments.some((segment) => segment === '' || segment === '.' || segment === '..')) {
|
|
645
|
+
throw new TransformerOutputError(transformer, `artifact path "${relativePath}" must not contain empty, "." or ".." segments`);
|
|
646
|
+
}
|
|
647
|
+
return normalized;
|
|
648
|
+
}
|
|
442
649
|
//# sourceMappingURL=media_library.js.map
|