k2-im 0.2.16 → 0.2.17

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 (29) hide show
  1. package/dist/chat-kit-pinia.d.ts +55 -11
  2. package/dist/chat-kit-vue.d.ts +5 -5
  3. package/dist/chat-kit.d.ts +101 -11
  4. package/dist/index.d.ts +104 -13
  5. package/dist/modules/im-browser/src/http/cm-http-adapter.js +1 -1
  6. package/dist/modules/im-browser/src/http/request-timeout.js +1 -0
  7. package/dist/modules/im-browser/src/http/requests/core-json-request.js +1 -1
  8. package/dist/modules/im-browser/src/http/requests/get-conversation-background-presets.js +1 -1
  9. package/dist/modules/im-browser/src/http/requests/pull-offline-channel.js +1 -1
  10. package/dist/modules/im-browser/src/media/media-download-request.js +1 -0
  11. package/dist/modules/im-browser/src/media/media-downloader.js +1 -1
  12. package/dist/modules/im-browser/src/media/media-preview-preparation-executor.js +1 -1
  13. package/dist/modules/im-browser/src/media/media-transfer-policy.js +1 -1
  14. package/dist/modules/im-browser/src/storage/indexed-db-storage-adapter.js +1 -1
  15. package/dist/modules/im-chat-kit/src/runtime/chat-runtime.js +1 -1
  16. package/dist/modules/im-chat-kit/src/runtime/media-object-url-registry.js +1 -1
  17. package/dist/modules/im-chat-kit/src/services/media.service.js +1 -1
  18. package/dist/modules/im-chat-kit/src/utils/media-transfer-presentation.js +1 -1
  19. package/dist/modules/im-core/src/mapping/group-offline-mapper.js +1 -1
  20. package/dist/modules/im-core/src/message-flow/media/media-sender.js +1 -1
  21. package/dist/modules/im-sdk/src/create-im-client.js +1 -1
  22. package/dist/modules/im-sdk/src/product/conversation-handle.js +1 -1
  23. package/dist/modules/im-sdk/src/product/media.js +1 -1
  24. package/dist/modules/im-sdk/src/runtime/history-page-size.js +1 -0
  25. package/dist/modules/im-sdk/src/runtime/local-media-preview.js +1 -1
  26. package/dist/modules/im-sdk/src/runtime/recent-history-fetcher.js +1 -1
  27. package/dist/modules/im-sdk/src/runtime/session-runtime.js +1 -1
  28. package/dist/sdk.d.ts +49 -10
  29. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -1981,7 +1981,7 @@ interface ImHttpPort {
1981
1981
  }
1982
1982
 
1983
1983
  /** 可持久化的媒体资产类型;未注册类型必须跳过缓存,不能回退到其它类型。 */
1984
- type MediaAssetKind = 'image' | 'video' | 'file';
1984
+ type MediaAssetKind = 'image' | 'image-display' | 'video-poster' | 'video' | 'file';
1985
1985
  /** 持久媒体资产的稳定键。resourceVersion 由远端资源标识派生,避免同消息替换资源后误命中旧缓存。 */
1986
1986
  interface MediaAssetCacheKey {
1987
1987
  kind: MediaAssetKind;
@@ -2030,7 +2030,10 @@ interface MediaAssetCachePort {
2030
2030
  get: (key: MediaAssetCacheKey) => Promise<MediaAssetCacheEntry | null>;
2031
2031
  /** 按输入顺序检查完整资源是否存在且未过期;不读取 Blob、不延长闲置期限,存储故障必须抛出。 */
2032
2032
  hasMany?: (keys: readonly MediaAssetCacheKey[]) => Promise<readonly boolean[]>;
2033
- put: (entry: MediaAssetCacheEntry) => Promise<'stored' | 'skipped'>;
2033
+ /** isCurrent 在排队结束和写事务开始前复核;已失效的生成结果返回 skipped,已开始的事务不撤销。 */
2034
+ put: (entry: MediaAssetCacheEntry, options?: {
2035
+ isCurrent?: () => boolean;
2036
+ }) => Promise<'stored' | 'skipped'>;
2034
2037
  clear: () => Promise<void>;
2035
2038
  /** 删除超过闲置期限的完整持久副本;不撤销已经返回的 Blob 或宿主持有的播放地址。 */
2036
2039
  prune?: () => Promise<void>;
@@ -3473,13 +3476,13 @@ interface MediaTransferAdapterConfig {
3473
3476
  useAccelerateEndpoint?: boolean;
3474
3477
  /**
3475
3478
  * 统一允许 S3 分片上传和 Range 分段下载,默认 false(单次 PUT / 普通 GET)。
3476
- * 显式 true 后仍按 16MiB 阈值及已确认下载大小选择策略,小资源不分片。
3479
+ * 显式 true 后,上传对象达到 8 MiB 使用 Multipart;上传片长与下载段长均为 16 MiB。
3477
3480
  * 开启前各桶须支持分片请求方法,并暴露上传 ETag、下载 Content-Range 与强 ETag。
3478
3481
  * @example { multipartTransfer: true } 同时允许大资源分片上传、下载。
3479
3482
  */
3480
3483
  multipartTransfer?: boolean;
3481
3484
  /**
3482
- * 总开关开启后,是否允许超过分片阈值(16MiB)的上传使用 S3 分片上传。
3485
+ * 总开关开启后,是否允许达到分片阈值(8 MiB)的上传使用 S3 分片上传。
3483
3486
  *
3484
3487
  * 分片相比单次 PUT 需要各物理桶的 CORS 额外放行 POST(Create/CompleteMultipartUpload)、
3485
3488
  * DELETE(AbortMultipartUpload)并 `ExposeHeaders: ETag`;未配齐时分片会直接失败。
@@ -3495,8 +3498,8 @@ interface MediaTransferAdapterConfig {
3495
3498
  /**
3496
3499
  * 媒体发送阶段的并发上限;由 im-core 消费,限制媒体任务数而非分片请求数。
3497
3500
  *
3498
- * 并发按媒体体积分两档:不超过分片阈值(16 MiB)的非视频媒体走小文件档,
3499
- * 缺省 5;视频与超过阈值(或字节数未知)的内容走大文件档,缺省 3。
3501
+ * 并发按媒体体积分两档:小于分片阈值(8 MiB)的非视频媒体走小文件档,
3502
+ * 缺省 5;视频与达到阈值(或字节数未知)的内容走大文件档,缺省 3。
3500
3503
  * 分档只限制媒体任务数;实际 PUT/UploadPart 另受 adapter 的六路数据请求限制。
3501
3504
  * 两档都只接受 1-6 的整数,超出范围由 core 显式报错。
3502
3505
  * - 传一个数字:所有媒体共用这一个并发数,与分档前的语义一致。
@@ -3523,11 +3526,12 @@ interface MediaTransferAdapterConfig {
3523
3526
  * - `multipart-etag-missing`:分片响应读不到 ETag,**桶 CORS 缺 `ExposeHeaders: ETag`**,不是网络问题。
3524
3527
  * - `multipart-abort-failed`:分片失败后的 `AbortMultipartUpload` 清理本身失败,已上传分片可能残留并计费。
3525
3528
  * - `multipart-upload-failed`:分片上传因其它原因失败;`cause` 保留原始错误,消息列出分片对 CORS 的额外要求。
3529
+ * - `download-timeout`:下载连续 30 秒无新数据,或单次 GET 超过 30 分钟。
3526
3530
  * - `download-range-invalid`:分段状态、范围、长度或可读响应头不符合合同。
3527
3531
  * - `download-object-changed`:分段之间的强 ETag 或总长度改变,拒绝混合不同文件版本。
3528
3532
  * - `download-range-failed`:下载返回非成功 HTTP 状态。
3529
3533
  */
3530
- type MediaTransferErrorCode = 'upload-timeout' | 'bucket-unconfigured' | 'credentials-provider-missing' | 'credentials-request-failed' | 'credentials-invalid' | 'upload-source-invalid' | 'upload-size-exceeded' | 'upload-resume-unavailable' | 'multipart-etag-missing' | 'multipart-abort-failed' | 'multipart-upload-failed' | 'download-range-invalid' | 'download-object-changed' | 'download-range-failed';
3534
+ type MediaTransferErrorCode = 'upload-timeout' | 'bucket-unconfigured' | 'credentials-provider-missing' | 'credentials-request-failed' | 'credentials-invalid' | 'upload-source-invalid' | 'upload-size-exceeded' | 'upload-resume-unavailable' | 'multipart-etag-missing' | 'multipart-abort-failed' | 'multipart-upload-failed' | 'download-timeout' | 'download-range-invalid' | 'download-object-changed' | 'download-range-failed';
3531
3535
  /** 媒体传输统一错误:携带稳定业务码 + 可选底层 cause(诊断用,永不携带凭证明文)。 */
3532
3536
  declare class MediaTransferError extends Error {
3533
3537
  readonly code: MediaTransferErrorCode;
@@ -6275,16 +6279,26 @@ interface ImMediaTasksApi {
6275
6279
  includeDisplayPoster?: boolean;
6276
6280
  }) => Promise<(TKind extends 'video' ? LocalVideoPreviewMetadata : ImagePreviewMetadata) | null>;
6277
6281
  /**
6278
- * 从本地视频提取仅供页面展示的高清 JPEG 封面,不上传、不下载、不写缓存。
6282
+ * 从本地视频或已有截图生成气泡封面;传入消息身份时复用独立持久缓存,不下载或上传。
6279
6283
  *
6280
6284
  * 长边最多 960px 且不放大原视频;与发送预读共用同一解码执行器。返回 Blob 由宿主创建和释放
6281
6285
  * object URL,不能作为 localPreviewMetadata 发进消息正文。读取失败返回 null 并记录诊断;
6282
6286
  * 宿主取消抛 AbortError,运行时失效或未装配能力时抛与 prepareLocalPreview 相同的明确错误。
6283
- * @param input.file 本地或已解密的完整视频 Blob。
6287
+ * @param input.file 本地或已解密的完整视频 Blob;已有 poster 时可以不传。
6288
+ * @param input.poster 发送前已经提取的截图,避免重新加载视频抽帧。
6289
+ * @param input.message 可选消息身份,用于持久保存封面。
6290
+ * @param input.expectedResourceUrl 取得源文件前的资源地址,防止换图后的旧结果污染缓存。
6284
6291
  * @param input.signal 页面卸载、换源或切会话时取消这次抽帧。
6285
6292
  */
6286
6293
  prepareLocalVideoPoster: (input: {
6287
- file: Blob;
6294
+ file?: Blob;
6295
+ poster?: Blob;
6296
+ message?: ImMessageRef;
6297
+ expectedResourceUrl?: string;
6298
+ signal?: AbortSignal;
6299
+ }) => Promise<Blob | null>;
6300
+ /** 只读视频气泡封面缓存;不下载、不抽帧,未命中返回 null。 */
6301
+ getCachedVideoPoster: (message: ImMessageRef, options?: {
6288
6302
  signal?: AbortSignal;
6289
6303
  }) => Promise<Blob | null>;
6290
6304
  /**
@@ -6300,6 +6314,34 @@ interface ImMediaTasksApi {
6300
6314
  file: Blob;
6301
6315
  signal?: AbortSignal;
6302
6316
  }) => Promise<Blob | null>;
6317
+ /**
6318
+ * 从本机原图生成独立的静态显示图,供 PC/H5 气泡使用;不下载、不上传、不改原图与消息尺寸。
6319
+ * 传入消息引用时先查独立的显示图缓存;稳定远端资源的结果可持久保存,本地待上传资源只生成。
6320
+ * 所有浏览器可解码格式均处理;最长边不超过 maxSize 且不放大,透明图优先保留透明度。
6321
+ * WebP 质量 0.85,最低 0.82,目标 256 KiB、最多 512 KiB;超过字节预算时可继续缩小,失败返回 null 并记录诊断。
6322
+ * 取消、账号失效、能力缺失和非法参数明确拒绝;返回 Blob 的 object URL 由宿主管理。
6323
+ * @param input.file 原始或已解密的完整图片 Blob,不会被修改。
6324
+ * @param input.maxSize 显示图最长边上限,必须是 1 到 2048 的整数。
6325
+ * @param input.signal 页面退出、资源失效时取消准备。
6326
+ * @param input.message 可选的精确消息引用,用于按消息、合集项、资源版本和尺寸隔离显示图缓存。
6327
+ * @param input.expectedResourceUrl 取得 file 前记录的消息资源地址;与 message 配合使用,不匹配时返回 null,防止旧文件写入新资源缓存。
6328
+ * @example const display = await client.media.prepareLocalImageDisplay({ file: original, maxSize: 1440 })
6329
+ */
6330
+ prepareLocalImageDisplay: (input: {
6331
+ file: Blob;
6332
+ maxSize: number;
6333
+ message?: ImMessageRef;
6334
+ expectedResourceUrl?: string;
6335
+ signal?: AbortSignal;
6336
+ }) => Promise<Blob | null>;
6337
+ /**
6338
+ * 仅读取指定消息和尺寸的持久显示图,不下载原图、不编码;未命中或读取期间资源失效返回 null。
6339
+ * maxSize 必须是 1 到 2048 的整数;取消、账号失效、存储读取失败明确拒绝。
6340
+ */
6341
+ getCachedImageDisplay: (message: ImMessageRef, options: {
6342
+ maxSize: number;
6343
+ signal?: AbortSignal;
6344
+ }) => Promise<Blob | null>;
6303
6345
  }
6304
6346
 
6305
6347
  /**
@@ -7003,7 +7045,7 @@ declare function resolveImEmojiToken(token: string): ImEmojiCatalogEntry | undef
7003
7045
  * 1 小时、按服务端过期时间提前 5 分钟失效)、同桶并发请求复用和失败后重新签发由 SDK 负责,宿主只需提供「打一次接口」的纯函数。
7004
7046
  *
7005
7047
  * 默认单次 PUT/GET;仅 multipartTransfer=true 才允许大资源分片上传、下载。
7006
- * 各 bucket 需在对象存储侧配置 CORS 允许本站 origin 的 PUT + GET;开启分片后,超过 16MiB 的上传另需放行
7048
+ * 各 bucket 需在对象存储侧配置 CORS 允许本站 origin 的 PUT + GET;开启分片后,达到 8 MiB 的上传另需放行
7007
7049
  * POST(Create/CompleteMultipartUpload)、DELETE(AbortMultipartUpload)并 ExposeHeaders: ETag。CORS 未配齐时
7008
7050
  * 下载还需暴露 Content-Range 与强 ETag;multipartTransfer=false 统一关闭分片上传和下载。
7009
7051
  * ⚠ 并发字段只由 SDK core 的媒体编排消费;adapter 会忽略该字段。高内存设备可设置 5-6,低内存设备建议 2-3。
@@ -7691,7 +7733,7 @@ interface ImResourceUploadResult {
7691
7733
  /** 独立资源上传器;不创建消息、Outbox 项或 client.media 任务。 */
7692
7734
  interface ImResourceUploader {
7693
7735
  /**
7694
- * 上传原始文件。默认单次 PUT;multipartTransfer=true 且 >16 MiB 才允许分片,沿用既有凭证刷新策略。
7736
+ * 上传原始文件。默认单次 PUT;multipartTransfer=true 且 ≥8 MiB 才允许分片,沿用既有凭证刷新策略。
7695
7737
  * 失败会清理未完成分片并保留错误,显式重试须再次调用 upload;不会退回旧 HTTP 上传接口。
7696
7738
  * @throws 取消为 AbortError;若取消后的分片清理失败,仍报告 multipart-abort-failed。
7697
7739
  * 配置、凭证、S3 或清理失败直接拒绝;dispose 后不可再使用。
@@ -8973,6 +9015,8 @@ interface ChatMediaMessageRef {
8973
9015
  * 后绑定的一格把先绑定的挤掉并撤销其地址。单媒体消息不传这个字段。
8974
9016
  */
8975
9017
  itemIndex?: number;
9018
+ /** 查询时校验当前正文资源地址;不匹配即未命中,不根据页面历史推断租约身份。 */
9019
+ resourceUrl?: string;
8976
9020
  }
8977
9021
  /**
8978
9022
  * Chat Kit 提供给共享 Store 和业务 Service 的账号运行接口。
@@ -9165,8 +9209,9 @@ interface ChatRuntime {
9165
9209
  * 媒体上传成功并获得远端资源后,结束本地预览的“上传源”职责。
9166
9210
  *
9167
9211
  * 图片和视频保留为可淘汰本地缓存,使发送者短时间内查看无需重新下载;GIF 直接释放。
9212
+ * resourceUrl 指定上传完成后的正文地址,用于绑定源文件身份;后续换地址会移除旧缓存。
9168
9213
  */
9169
- resolveMediaPreviewToRemote: (ref: ChatMediaMessageRef, contentType: 'image' | 'video' | 'sticker', byteSize?: number) => void;
9214
+ resolveMediaPreviewToRemote: (ref: ChatMediaMessageRef, contentType: 'image' | 'video' | 'sticker', byteSize?: number, resourceUrl?: string) => void;
9170
9215
  /** 为指定消息取得本地媒体播放租约;没有本地缓存时返回 `undefined`,调用方应改用远端资源。 */
9171
9216
  acquireMediaBlob: (ref: ChatMediaMessageRef) => MediaObjectUrlLease | undefined;
9172
9217
  /** 只读检查完整本地媒体是否可用,不取得租约、不改变缓存淘汰顺序。 */
@@ -9555,6 +9600,38 @@ type GifFileClassification = 'gif' | 'not-gif' | 'invalid-gif';
9555
9600
  declare function classifyGifFile(file: Blob): Promise<GifFileClassification>;
9556
9601
  declare function isGifFile(file: Blob): Promise<boolean>;
9557
9602
 
9603
+ /** 气泡显示图的像素规格;两端共用,完整原图和消息内嵌缩略图不使用此预算。 */
9604
+ declare function resolveImageDisplayMaxSize(message: ImMessageView, itemIndex?: number): number;
9605
+ /**
9606
+ * 从借用的本机原图生成独立气泡地址。不会撤销输入 URL,也不会触发远端下载。
9607
+ * identity 让 SDK 按完整资源身份复用持久显示缓存;调用方负责撤销返回的 URL。
9608
+ * 预览必须继续使用原图,不能使用此方法的返回值。
9609
+ */
9610
+ declare function prepareImageDisplayObjectUrl(runtime: ChatRuntime, url: string, maxSize: number, signal?: AbortSignal, identity?: {
9611
+ message: ImMessageView;
9612
+ itemIndex?: number;
9613
+ }): Promise<string | null>;
9614
+ /**
9615
+ * 气泡优先读持久显示图;未命中才用已缓存的原图生成,不发起网络下载。
9616
+ * 仅产生一个由调用方拥有的显示 URL,整个过程不创建或持有原图 URL。
9617
+ */
9618
+ declare function loadCachedImageDisplayObjectUrl(runtime: ChatRuntime, message: ImMessageView, itemIndex?: number, signal?: AbortSignal): Promise<string | null>;
9619
+ /**
9620
+ * 自动加载气泡显示图:优先恢复派生缓存或本机原图,均未命中才下载。
9621
+ * 原图地址只在生成期间持有并始终释放;返回的独立显示地址归宿主。
9622
+ * 显示缓存命中即可满足气泡,完整原件已被淘汰时留待用户预览再下载。
9623
+ */
9624
+ declare function loadImageDisplayObjectUrl(runtime: ChatRuntime, message: ImMessageView, hooks: ServiceNoteHooks, options: ImMediaDownloadOptions, itemIndex?: number): Promise<string | null>;
9625
+ /**
9626
+ * 借用本机视频地址或发送前截图生成气泡封面,优先命中持久缓存。
9627
+ * 不触发远端下载;输入地址仍归调用方,返回的封面地址由调用方释放。
9628
+ */
9629
+ declare function prepareVideoPosterObjectUrl(runtime: ChatRuntime, url: string, signal?: AbortSignal, identity?: {
9630
+ message: ImMessageView;
9631
+ itemIndex?: number;
9632
+ }, poster?: Blob): Promise<string | null>;
9633
+ /** 只恢复本机视频封面:先读派生缓存,未命中才读完整视频并抽帧;不触发视频下载。 */
9634
+ declare function loadCachedVideoPosterObjectUrl(runtime: ChatRuntime, message: ImMessageView, itemIndex?: number, signal?: AbortSignal): Promise<string | null>;
9558
9635
  /**
9559
9636
  * 将用户最后预览的媒体项交给 SDK 提高下载优先级,不创建任务或持有页面资源。
9560
9637
  * 在异步查缓存、加载 URL 之前调用,重复打开已有下载也会更新优先级。
@@ -9672,31 +9749,43 @@ declare function loadFileObjectUrl(runtime: ChatRuntime, message: ImMessageView,
9672
9749
  declare const media_service_acquireMediaBlob: typeof acquireMediaBlob;
9673
9750
  declare const media_service_detectImageMime: typeof detectImageMime;
9674
9751
  declare const media_service_detectVideoMime: typeof detectVideoMime;
9752
+ declare const media_service_loadCachedImageDisplayObjectUrl: typeof loadCachedImageDisplayObjectUrl;
9675
9753
  declare const media_service_loadCachedImageObjectUrl: typeof loadCachedImageObjectUrl;
9676
9754
  declare const media_service_loadCachedMediaAlbumItemObjectUrl: typeof loadCachedMediaAlbumItemObjectUrl;
9677
9755
  declare const media_service_loadCachedVideoObjectUrl: typeof loadCachedVideoObjectUrl;
9756
+ declare const media_service_loadCachedVideoPosterObjectUrl: typeof loadCachedVideoPosterObjectUrl;
9678
9757
  declare const media_service_loadFileObjectUrl: typeof loadFileObjectUrl;
9758
+ declare const media_service_loadImageDisplayObjectUrl: typeof loadImageDisplayObjectUrl;
9679
9759
  declare const media_service_loadImageObjectUrl: typeof loadImageObjectUrl;
9680
9760
  declare const media_service_loadMediaAlbumItemObjectUrl: typeof loadMediaAlbumItemObjectUrl;
9681
9761
  declare const media_service_loadVideoObjectUrl: typeof loadVideoObjectUrl;
9682
9762
  declare const media_service_loadVoiceBytes: typeof loadVoiceBytes;
9763
+ declare const media_service_prepareImageDisplayObjectUrl: typeof prepareImageDisplayObjectUrl;
9683
9764
  declare const media_service_prepareLocalMediaPreview: typeof prepareLocalMediaPreview;
9765
+ declare const media_service_prepareVideoPosterObjectUrl: typeof prepareVideoPosterObjectUrl;
9684
9766
  declare const media_service_prioritizeMediaDownload: typeof prioritizeMediaDownload;
9767
+ declare const media_service_resolveImageDisplayMaxSize: typeof resolveImageDisplayMaxSize;
9685
9768
  declare namespace media_service {
9686
9769
  export {
9687
9770
  media_service_acquireMediaBlob as acquireMediaBlob,
9688
9771
  media_service_detectImageMime as detectImageMime,
9689
9772
  media_service_detectVideoMime as detectVideoMime,
9773
+ media_service_loadCachedImageDisplayObjectUrl as loadCachedImageDisplayObjectUrl,
9690
9774
  media_service_loadCachedImageObjectUrl as loadCachedImageObjectUrl,
9691
9775
  media_service_loadCachedMediaAlbumItemObjectUrl as loadCachedMediaAlbumItemObjectUrl,
9692
9776
  media_service_loadCachedVideoObjectUrl as loadCachedVideoObjectUrl,
9777
+ media_service_loadCachedVideoPosterObjectUrl as loadCachedVideoPosterObjectUrl,
9693
9778
  media_service_loadFileObjectUrl as loadFileObjectUrl,
9779
+ media_service_loadImageDisplayObjectUrl as loadImageDisplayObjectUrl,
9694
9780
  media_service_loadImageObjectUrl as loadImageObjectUrl,
9695
9781
  media_service_loadMediaAlbumItemObjectUrl as loadMediaAlbumItemObjectUrl,
9696
9782
  media_service_loadVideoObjectUrl as loadVideoObjectUrl,
9697
9783
  media_service_loadVoiceBytes as loadVoiceBytes,
9784
+ media_service_prepareImageDisplayObjectUrl as prepareImageDisplayObjectUrl,
9698
9785
  media_service_prepareLocalMediaPreview as prepareLocalMediaPreview,
9786
+ media_service_prepareVideoPosterObjectUrl as prepareVideoPosterObjectUrl,
9699
9787
  media_service_prioritizeMediaDownload as prioritizeMediaDownload,
9788
+ media_service_resolveImageDisplayMaxSize as resolveImageDisplayMaxSize,
9700
9789
  };
9701
9790
  }
9702
9791
 
@@ -10913,6 +11002,8 @@ declare function resolveMediaTransferPresentation(input: {
10913
11002
  aggregateFrom?: number;
10914
11003
  /** 宿主确实持有的完整本地资源;单条媒体以 undefined 表示,缩略图不属于完整资源。 */
10915
11004
  availableItemIndices?: readonly (number | undefined)[];
11005
+ /** 仅气泡传入:已有清晰显示图或完整本机资源的项不显示下载胶囊;不能用作预览可用性证据。 */
11006
+ displayAvailableItemIndices?: readonly (number | undefined)[];
10916
11007
  /** SDK 明确查到完整缓存已不存在;历史 completed 任务不能替代当前资源证据。 */
10917
11008
  unavailableItemIndices?: readonly (number | undefined)[];
10918
11009
  /** 当前窗口正在查询的媒体项,不表示未下载。 */
@@ -1 +1 @@
1
- import{ImHttpRequestError as t}from"../../../im-core/src/ports/http-port.js";function e(e){const o=e.fetch??((t,e)=>globalThis.fetch(t,e));return{async postProtobuf({path:r,body:s,headers:a,signal:n}){const i=await e.getBaseUrl();if(!i)throw new Error("CmHttp.postProtobuf:缺少 CM HTTP 基址(getCmHttpBaseUrl 为空)");const p={"Content-Type":"application/octet-stream"},c=e.getClientType?.();void 0!==c&&(p["Client-type"]=String(c)),Object.assign(p,a);const u=await e.getAuth();null!==u&&""!==u.token&&""!==u.userId&&(p.Authorization=`${u.token}:${u.userId}`);const h=await o(`${i}${r}`,{method:"POST",body:s,headers:p,signal:n});if(!h.ok)throw new t({path:r,status:h.status,statusText:h.statusText});const l=await h.arrayBuffer();return new Uint8Array(l)}}}export{e as createCmHttpAdapter};
1
+ import{withRequestTimeout as t,trackResponseActivity as e}from"./request-timeout.js";import{ImHttpRequestError as o}from"../../../im-core/src/ports/http-port.js";function r(r){const s=r.fetch??((t,e)=>globalThis.fetch(t,e));return{async postProtobuf({path:a,body:n,headers:i,signal:c}){const u=await r.getBaseUrl();if(!u)throw new Error("CmHttp.postProtobuf:缺少 CM HTTP 基址(getCmHttpBaseUrl 为空)");const p={"Content-Type":"application/octet-stream"},l=r.getClientType?.();void 0!==l&&(p["Client-type"]=String(l)),Object.assign(p,i);const h=await r.getAuth();return null!==h&&""!==h.token&&""!==h.userId&&(p.Authorization=`${h.token}:${h.userId}`),t({signal:c,idleMs:15e3,timeoutError:()=>new DOMException("CM 请求连续 15 秒没有收到新数据","TimeoutError")},async t=>{const r=await s(`${u}${a}`,{method:"POST",body:n,headers:p,signal:t.signal});if(!r.ok)throw await(r.body?.cancel().catch(()=>{})),new o({path:a,status:r.status,statusText:r.statusText});const i=await e(r,t).arrayBuffer();return new Uint8Array(i)})}}}export{r as createCmHttpAdapter};
@@ -0,0 +1 @@
1
+ async function e(e,t){const r=new AbortController;let o,n,s,a,i=!1;const u=new Promise((e,t)=>{a=t}),l=e=>{i||(i=!0,s=e,r.abort(e),a(e))},c=()=>l(e.signal?.reason??new DOMException("Request canceled","AbortError")),h=()=>{i||(clearTimeout(o),o=setTimeout(()=>l(e.timeoutError()),e.idleMs))};e.signal?.addEventListener("abort",c,{once:!0});try{if(e.signal?.aborted)throw e.signal.reason??new DOMException("Request canceled","AbortError");h(),void 0!==e.totalMs&&(n=setTimeout(()=>l(e.timeoutError()),e.totalMs));const o=Promise.resolve().then(()=>{if(i)throw s;return t({signal:r.signal,touch:h})}),a=await Promise.race([o,u]);if(i)throw s;return a}catch(e){throw r.abort(e),e}finally{i=!0,clearTimeout(o),clearTimeout(n),e.signal?.removeEventListener("abort",c)}}function t(e,t){if(t.touch(),null==e.body)return e;const r=e.body.pipeThrough(new TransformStream({transform(e,r){e.byteLength>0&&t.touch(),r.enqueue(e)}}),{signal:t.signal});return new Response(r,{status:e.status,statusText:e.statusText,headers:e.headers})}export{t as trackResponseActivity,e as withRequestTimeout};
@@ -1 +1 @@
1
- var e=Object.defineProperty,o=(o,r,t)=>((o,r,t)=>r in o?e(o,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[r]=t)(o,"symbol"!=typeof r?r+"":r,t);const r="core",t=200,s=502;class a extends Error{constructor(e,r){super(`core JSON 传输失败:${e||"unknown transport error"}`,void 0===r?void 0:{cause:r}),o(this,"code",502),this.name="CoreJsonTransportError"}}class n extends Error{constructor(e,r,t){super(`${e}失败:code=${r}${t?` ${t}`:""}`),o(this,"businessCode"),o(this,"operation"),this.name="CoreJsonBusinessError",this.businessCode=r,this.operation=e}}async function d(e,o,s,n){const d=void 0!==n&&"responseMode"in n&&"strict"===n.responseMode;let i;try{i=await e(o,{target:r,data:s,...void 0===n?.method?{}:{method:n.method},...void 0===n?.headers?{}:{headers:n.headers},...void 0===n?.prefix?{}:{prefix:n.prefix}})}catch(e){if(d)throw new a(e instanceof Error?e.message:String(e),e);throw e}if(d){const e="string"==typeof i.msg?i.msg:"";if(502===i.code)throw new a(e);return i.code!==t?{ok:!1,code:i.code,message:e,data:i.data}:{ok:!0,code:t,message:e,data:i.data}}return{code:i.code,data:i.code===t&&null!=i.data?i.data:null}}export{t as CORE_JSON_CODE_SUCCESS,r as CORE_JSON_TARGET,s as CORE_JSON_TRANSPORT_FAILURE_CODE,n as CoreJsonBusinessError,a as CoreJsonTransportError,d as requestCoreJson};
1
+ var e=Object.defineProperty,r=(r,o,s)=>((r,o,s)=>o in r?e(r,o,{enumerable:!0,configurable:!0,writable:!0,value:s}):r[o]=s)(r,"symbol"!=typeof o?o+"":o,s);const o="core",s=200,t=502,a=/* @__PURE__ */new Set(["/friend/list","/friend/apply/list_paginated","/user_group/list","/user_radio/list","/user_radio/users"]);class i extends Error{constructor(e,o){super(`core JSON 传输失败:${e||"unknown transport error"}`,void 0===o?void 0:{cause:o}),r(this,"code",502),this.name="CoreJsonTransportError"}}class n extends Error{constructor(e,o,s){super(`${e}失败:code=${o}${s?` ${s}`:""}`),r(this,"businessCode"),r(this,"operation"),this.name="CoreJsonBusinessError",this.businessCode=o,this.operation=e}}async function d(e,r,t,n){const d=void 0!==n&&"responseMode"in n&&"strict"===n.responseMode;let c;try{c=await e(r,{target:o,data:t,timeoutMs:a.has(r)?6e4:15e3,...void 0===n?.method?{}:{method:n.method},...void 0===n?.headers?{}:{headers:n.headers},...void 0===n?.prefix?{}:{prefix:n.prefix}})}catch(e){if(d)throw new i(e instanceof Error?e.message:String(e),e);throw e}if(d){const e="string"==typeof c.msg?c.msg:"";if(502===c.code)throw new i(e);return c.code!==s?{ok:!1,code:c.code,message:e,data:c.data}:{ok:!0,code:s,message:e,data:c.data}}return{code:c.code,data:c.code===s&&null!=c.data?c.data:null}}export{s as CORE_JSON_CODE_SUCCESS,o as CORE_JSON_TARGET,t as CORE_JSON_TRANSPORT_FAILURE_CODE,n as CoreJsonBusinessError,i as CoreJsonTransportError,d as requestCoreJson};
@@ -1 +1 @@
1
- function t(t){if("string"!=typeof t||0===t.length)return!1;try{const r=new URL(t);return"http:"===r.protocol||"https:"===r.protocol}catch{return!1}}async function r(r){const n=await r("/system/background/list",{target:"core",method:"POST",data:{}});if(200!==n.code)throw new Error(`会话背景预置列表请求失败:${n.code}${n.msg?` ${n.msg}`:""}`);if(!Array.isArray(n.data?.lists))throw new Error("会话背景预置列表响应缺少 lists");const e=[],o=/* @__PURE__ */new Set;for(const r of n.data.lists){const n="string"==typeof r.id||"number"==typeof r.id?String(r.id):"";0!==n.length&&1===r.is_enabled&&t(r.img_url)&&(o.has(n)||(o.add(n),e.push({id:n,url:r.img_url,..."string"==typeof r.name&&r.name.length>0?{name:r.name}:{}})))}return e}export{r as getConversationBackgroundPresets};
1
+ function t(t){if("string"!=typeof t||0===t.length)return!1;try{const r=new URL(t);return"http:"===r.protocol||"https:"===r.protocol}catch{return!1}}async function r(r){const e=await r("/system/background/list",{target:"core",method:"POST",timeoutMs:15e3,data:{}});if(200!==e.code)throw new Error(`会话背景预置列表请求失败:${e.code}${e.msg?` ${e.msg}`:""}`);if(!Array.isArray(e.data?.lists))throw new Error("会话背景预置列表响应缺少 lists");const n=[],o=/* @__PURE__ */new Set;for(const r of e.data.lists){const e="string"==typeof r.id||"number"==typeof r.id?String(r.id):"";0!==e.length&&1===r.is_enabled&&t(r.img_url)&&(o.has(e)||(o.add(e),n.push({id:e,url:r.img_url,..."string"==typeof r.name&&r.name.length>0?{name:r.name}:{}})))}return n}export{r as getConversationBackgroundPresets};
@@ -1 +1 @@
1
- import{encodeChannelSessionsRequest as e,decodeChannelSessionsResponse as n,encodeChannelMessagesRequest as o,decodeChannelMessagesResponse as t,decodeChannelMessagesResponseAsync as a,encodeChannelHistoryRequest as s,decodeChannelHistoryResponse as i}from"../../../../im-core/src/mapping/channel-offline-mapper.js";import{throwIfAborted as r}from"../../../../im-core/src/abort.js";import{pullChannelMessagesRespCodec as c}from"../../../../im-protocol/src/protobuf/registry.js";import{compareBySortKey as d}from"../../../../im-core/src/mapping/sort.js";async function l(o){const t=[];for(const a of u(o.channels,20)){const s=e({userId:o.userId,clientType:o.clientType,channels:a}),i=await o.http.postProtobuf({path:"/channel/v1/sessions",body:s}),r={selfId:o.userId,keepRaw:o.keepRaw??!0,expectedChannelIds:a.map(e=>e.channelId),diagnostic:{endpoint:"sessions",surface:"session-last",...void 0===o.operationId?{}:{operationId:o.operationId},clientTypeHeader:o.clientType,clientTypeBody:o.clientType}},c=n(i,r);await f(o,c.sessions.filter(e=>"active"===e.channelState||0===e.raw?.status).map(e=>e.channelId)),t.push(...n(i,{...r,onContentDecodeError:o.onContentDecodeError,onReaction:o.onReaction},o.decryptDeps).sessions)}return t}async function p(e){const n=function(e){if(void 0===e)return 10;if(!Number.isInteger(e)||e<=0)throw new Error("channel offline: channelBatchSize 必须为正整数");return Math.min(e,10)}(e.channelBatchSize),s=e.messageCount??30,i=function(e){if(void 0===e)return 100;if(!Number.isInteger(e)||e<=0)throw new Error("channel offline: maxPagesPerBatch 必须为正整数");return e}(e.maxPagesPerBatch),c=/* @__PURE__ */new Map;for(const d of u(e.pulls,n)){let n=d.map(e=>({...e}));const l=new Map(n.map(e=>[e.channelId,/* @__PURE__ */new Set([e.baseIndex])]));let p=0;for(;n.length>0;){r(e.signal),await(e.beforePage?.()),r(e.signal);const d=e.isChannelCurrent;if(void 0!==d&&(n=n.filter(e=>d(e.channelId))),0===n.length)break;if(p>=i){const e=n.map(({channelId:e,baseIndex:n})=>`${e}@${n}`).join(",");throw new Error(`channel offline 分页超过 maxPagesPerBatch=${i}(active=${e})`)}p+=1;const h=o({userId:e.userId,clientType:e.clientType,pulls:n.map(e=>({channelId:e.channelId,baseIndex:e.baseIndex,direction:0,count:s,contain:0}))});r(e.signal);const I=await e.http.postProtobuf({path:"/channel/v1/offlineMessages",body:h,...void 0===e.signal?{}:{signal:e.signal}});r(e.signal),await f(e,n.map(e=>e.channelId)),r(e.signal);const u={selfId:e.userId,keepRaw:e.keepRaw??!0,expectedChannelIds:n.map(e=>e.channelId),diagnostic:{endpoint:"offlineMessages",surface:"offline-batch",...void 0===e.operationId?{}:{operationId:e.operationId},clientTypeHeader:e.clientType,clientTypeBody:e.clientType},onContentDecodeError:e.onContentDecodeError,onReaction:e.onReaction},g=void 0===e.yieldToEventLoop?t(I,u,e.decryptDeps):await a(I,u,e.decryptDeps,{yieldToEventLoop:e.yieldToEventLoop,signal:e.signal});r(e.signal);const y=[];for(const e of n){const n=g.nextBaseIndexes[e.channelId],o=g.messageCounts[e.channelId]??0;if(void 0===n)continue;const t=l.get(e.channelId);if(t.has(n))throw new Error(`channel offline cursor 循环(channelId=${e.channelId}, baseIndex=${n})`);o<s||(t.add(n),y.push({channelId:e.channelId,baseIndex:n}))}await(e.onPage?.(g.messages,{pulls:n.map(e=>({...e})),nextBaseIndexes:{...g.nextBaseIndexes},messageCounts:{...g.messageCounts}})),r(e.signal);for(const e of g.messages)c.has(e.id)||c.set(e.id,e);n=y}}return[...c.values()].sort(d)}async function h(e){r(e.signal);const n=e.count??30;if(!Number.isInteger(n)||n<=0)throw new Error("channel latest: count 必须为正整数");void 0!==e.decryptDeps&&void 0!==e.prepareKey&&await e.prepareKey(e.channelId);const s=o({userId:e.userId,clientType:e.clientType,pulls:[{channelId:e.channelId,baseIndex:"",direction:1,count:n,contain:0}]});r(e.signal);const i=await e.http.postProtobuf({path:"/channel/v1/offlineMessages",body:s,...void 0===e.signal?{}:{signal:e.signal}});r(e.signal);const d={selfId:e.userId,keepRaw:e.keepRaw??!0,expectedChannelIds:[e.channelId],diagnostic:{endpoint:"offlineMessages",surface:"history-page",...void 0===e.operationId?{}:{operationId:e.operationId},clientTypeHeader:e.clientType,clientTypeBody:e.clientType},onContentDecodeError:e.onContentDecodeError,onReaction:e.onReaction},l=void 0===e.yieldToEventLoop?{...t(i,d,e.decryptDeps),firstBaseIndex:c.decode(i).lsMessage[0]?.sIndex}:await a(i,d,e.decryptDeps,{yieldToEventLoop:e.yieldToEventLoop,signal:e.signal}),p=l.batchSize===n,h=l.firstBaseIndex;if(p&&("string"!=typeof h||""===h.trim()))throw new Error(`channel latest: 满页缺少向旧 cursor(channelId=${e.channelId})`);return{messages:l.messages,hasOlder:p,...p?{nextCursor:h}:{}}}async function I(e){r(e.signal);const n=e.count??30;void 0!==e.decryptDeps&&void 0!==e.prepareKey&&await e.prepareKey(e.channelId);const o=s({userId:e.userId,clientType:e.clientType,channelId:e.channelId,baseIndex:e.baseIndex,count:n,contain:e.contain??0});r(e.signal);const t=await e.http.postProtobuf({path:"/channel/v1/offlineMessages",body:o,...void 0===e.signal?{}:{signal:e.signal}});r(e.signal);return i(t,{selfId:e.userId,channelId:e.channelId,requestedBaseIndex:e.baseIndex,requestedCount:n,keepRaw:e.keepRaw??!0,diagnostic:{endpoint:"history",surface:"history-page",...void 0===e.operationId?{}:{operationId:e.operationId},clientTypeHeader:e.clientType,clientTypeBody:e.clientType},onContentDecodeError:e.onContentDecodeError,onReaction:e.onReaction},e.decryptDeps)}async function f(e,n){if(void 0===e.decryptDeps||void 0===e.prepareKeys)return;const o=[...new Set(n.filter(Boolean))];o.length>0&&await e.prepareKeys(o)}function u(e,n){const o=[];for(let t=0;t<e.length;t+=n)o.push(e.slice(t,t+n));return o}export{I as pullChannelHistory,h as pullChannelLatestPage,p as pullChannelOfflineMessages,l as pullChannelSessions};
1
+ import{encodeChannelSessionsRequest as e,decodeChannelSessionsResponse as n,encodeChannelMessagesRequest as o,decodeChannelMessagesResponse as t,decodeChannelMessagesResponseAsync as a,encodeChannelHistoryRequest as s,decodeChannelHistoryResponse as i}from"../../../../im-core/src/mapping/channel-offline-mapper.js";import{throwIfAborted as r}from"../../../../im-core/src/abort.js";import{pullChannelMessagesRespCodec as c}from"../../../../im-protocol/src/protobuf/registry.js";import{compareBySortKey as d}from"../../../../im-core/src/mapping/sort.js";async function l(o){const t=[];for(const a of u(o.channels,20)){const s=e({userId:o.userId,clientType:o.clientType,channels:a}),i=await o.http.postProtobuf({path:"/channel/v1/sessions",body:s}),r={selfId:o.userId,keepRaw:o.keepRaw??!0,expectedChannelIds:a.map(e=>e.channelId),diagnostic:{endpoint:"sessions",surface:"session-last",...void 0===o.operationId?{}:{operationId:o.operationId},clientTypeHeader:o.clientType,clientTypeBody:o.clientType}},c=n(i,r);await f(o,c.sessions.filter(e=>"active"===e.channelState||0===e.raw?.status).map(e=>e.channelId)),t.push(...n(i,{...r,onContentDecodeError:o.onContentDecodeError,onReaction:o.onReaction},o.decryptDeps).sessions)}return t}async function p(e){const n=function(e){if(void 0===e)return 10;if(!Number.isInteger(e)||e<=0)throw new Error("channel offline: channelBatchSize 必须为正整数");return Math.min(e,10)}(e.channelBatchSize),s=e.messageCount??20,i=function(e){if(void 0===e)return 100;if(!Number.isInteger(e)||e<=0)throw new Error("channel offline: maxPagesPerBatch 必须为正整数");return e}(e.maxPagesPerBatch),c=/* @__PURE__ */new Map;for(const d of u(e.pulls,n)){let n=d.map(e=>({...e}));const l=new Map(n.map(e=>[e.channelId,/* @__PURE__ */new Set([e.baseIndex])]));let p=0;for(;n.length>0;){r(e.signal),await(e.beforePage?.()),r(e.signal);const d=e.isChannelCurrent;if(void 0!==d&&(n=n.filter(e=>d(e.channelId))),0===n.length)break;if(p>=i){const e=n.map(({channelId:e,baseIndex:n})=>`${e}@${n}`).join(",");throw new Error(`channel offline 分页超过 maxPagesPerBatch=${i}(active=${e})`)}p+=1;const h=o({userId:e.userId,clientType:e.clientType,pulls:n.map(e=>({channelId:e.channelId,baseIndex:e.baseIndex,direction:0,count:s,contain:0}))});r(e.signal);const I=await e.http.postProtobuf({path:"/channel/v1/offlineMessages",body:h,...void 0===e.signal?{}:{signal:e.signal}});r(e.signal),await f(e,n.map(e=>e.channelId)),r(e.signal);const u={selfId:e.userId,keepRaw:e.keepRaw??!0,expectedChannelIds:n.map(e=>e.channelId),diagnostic:{endpoint:"offlineMessages",surface:"offline-batch",...void 0===e.operationId?{}:{operationId:e.operationId},clientTypeHeader:e.clientType,clientTypeBody:e.clientType},onContentDecodeError:e.onContentDecodeError,onReaction:e.onReaction},g=void 0===e.yieldToEventLoop?t(I,u,e.decryptDeps):await a(I,u,e.decryptDeps,{yieldToEventLoop:e.yieldToEventLoop,signal:e.signal});r(e.signal);const y=[];for(const e of n){const n=g.nextBaseIndexes[e.channelId],o=g.messageCounts[e.channelId]??0;if(void 0===n)continue;const t=l.get(e.channelId);if(t.has(n))throw new Error(`channel offline cursor 循环(channelId=${e.channelId}, baseIndex=${n})`);o<s||(t.add(n),y.push({channelId:e.channelId,baseIndex:n}))}await(e.onPage?.(g.messages,{pulls:n.map(e=>({...e})),nextBaseIndexes:{...g.nextBaseIndexes},messageCounts:{...g.messageCounts}})),r(e.signal);for(const e of g.messages)c.has(e.id)||c.set(e.id,e);n=y}}return[...c.values()].sort(d)}async function h(e){r(e.signal);const n=e.count??20;if(!Number.isInteger(n)||n<=0)throw new Error("channel latest: count 必须为正整数");void 0!==e.decryptDeps&&void 0!==e.prepareKey&&await e.prepareKey(e.channelId);const s=o({userId:e.userId,clientType:e.clientType,pulls:[{channelId:e.channelId,baseIndex:"",direction:1,count:n,contain:0}]});r(e.signal);const i=await e.http.postProtobuf({path:"/channel/v1/offlineMessages",body:s,...void 0===e.signal?{}:{signal:e.signal}});r(e.signal);const d={selfId:e.userId,keepRaw:e.keepRaw??!0,expectedChannelIds:[e.channelId],diagnostic:{endpoint:"offlineMessages",surface:"history-page",...void 0===e.operationId?{}:{operationId:e.operationId},clientTypeHeader:e.clientType,clientTypeBody:e.clientType},onContentDecodeError:e.onContentDecodeError,onReaction:e.onReaction},l=void 0===e.yieldToEventLoop?{...t(i,d,e.decryptDeps),firstBaseIndex:c.decode(i).lsMessage[0]?.sIndex}:await a(i,d,e.decryptDeps,{yieldToEventLoop:e.yieldToEventLoop,signal:e.signal}),p=l.batchSize===n,h=l.firstBaseIndex;if(p&&("string"!=typeof h||""===h.trim()))throw new Error(`channel latest: 满页缺少向旧 cursor(channelId=${e.channelId})`);return{messages:l.messages,hasOlder:p,...p?{nextCursor:h}:{}}}async function I(e){r(e.signal);const n=e.count??20;void 0!==e.decryptDeps&&void 0!==e.prepareKey&&await e.prepareKey(e.channelId);const o=s({userId:e.userId,clientType:e.clientType,channelId:e.channelId,baseIndex:e.baseIndex,count:n,contain:e.contain??0});r(e.signal);const t=await e.http.postProtobuf({path:"/channel/v1/offlineMessages",body:o,...void 0===e.signal?{}:{signal:e.signal}});r(e.signal);return i(t,{selfId:e.userId,channelId:e.channelId,requestedBaseIndex:e.baseIndex,requestedCount:n,keepRaw:e.keepRaw??!0,diagnostic:{endpoint:"history",surface:"history-page",...void 0===e.operationId?{}:{operationId:e.operationId},clientTypeHeader:e.clientType,clientTypeBody:e.clientType},onContentDecodeError:e.onContentDecodeError,onReaction:e.onReaction},e.decryptDeps)}async function f(e,n){if(void 0===e.decryptDeps||void 0===e.prepareKeys)return;const o=[...new Set(n.filter(Boolean))];o.length>0&&await e.prepareKeys(o)}function u(e,n){const o=[];for(let t=0;t<e.length;t+=n)o.push(e.slice(t,t+n));return o}export{I as pullChannelHistory,h as pullChannelLatestPage,p as pullChannelOfflineMessages,l as pullChannelSessions};
@@ -0,0 +1 @@
1
+ var t=Object.defineProperty,e=(e,r,o)=>((e,r,o)=>r in e?t(e,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[r]=o)(e,"symbol"!=typeof r?r+"":r,o);import{withRequestTimeout as r}from"../http/request-timeout.js";import{MediaTransferError as o}from"./media-transfer-errors.js";class n extends o{constructor(t){super("download-range-failed",`媒体下载失败:HTTP ${t}`),e(this,"status"),this.status=t}}class a extends Error{constructor(t){super("媒体下载网络失败",{cause:t}),this.name="MediaDownloadNetworkError"}}async function s(t){try{return await t()}catch(t){if(t instanceof TypeError||t instanceof Error&&"NetworkError"===t.name)throw new a(t);throw t}}function i(t,e){return r({signal:t,idleMs:3e4,totalMs:18e5,timeoutError:()=>new o("download-timeout","媒体下载连续 30 秒无新数据,或单次请求超过 30 分钟")},e)}function c(t){return t instanceof n?t.status>=500&&t.status<=599:t instanceof o?"download-timeout"===t.code:t instanceof a}async function u(t){if(t?.aborted)throw t.reason??new DOMException("媒体下载已取消","AbortError");await new Promise((e,r)=>{let o;const n=()=>{clearTimeout(o),t?.removeEventListener("abort",n),r(t?.reason??new DOMException("媒体下载已取消","AbortError"))};o=setTimeout(()=>{t?.removeEventListener("abort",n),e()},1e3),t?.addEventListener("abort",n,{once:!0})})}async function f(t,e){for(let r=0;;r++)try{if(t?.aborted)throw t.reason??new DOMException("媒体下载已取消","AbortError");return await e()}catch(e){if(t?.aborted||r>=3||!c(e))throw e instanceof a?e.cause:e;await u(t)}}export{n as MediaDownloadHttpError,a as MediaDownloadNetworkError,s as downloadNetworkOperation,f as retryDownloadRequest,i as timedDownload};
@@ -1 +1 @@
1
- import{createMediaRequestLimiter as e}from"./media-request-limiter.js";import{MediaTransferError as t}from"./media-transfer-errors.js";import{MEDIA_TRANSFER_PART_BYTES as n,MEDIA_TRANSFER_REQUEST_CONCURRENCY as o,MEDIA_DOWNLOAD_PART_CONCURRENCY as r}from"./media-transfer-policy.js";function a(a=!0,i,d=n){if(!Number.isSafeInteger(d)||d<=0)throw new RangeError("下载分段大小必须为正整数");const s=e(o),l=/* @__PURE__ */new WeakMap;async function c(e,n){if(!e.ok)throw new t("download-range-failed",`媒体下载失败:HTTP ${e.status}`);return void 0===n.onProgress||null===e.body?!0===n.preferBlob?await e.blob():new Uint8Array(await e.arrayBuffer()):async function(e,t,n,o=!1){const r=Number(e.headers.get("content-length")??0),a=void 0!==n&&Number.isFinite(n)&&n>0?Math.floor(n):0,i=Number.isFinite(r)&&r>0?r:a;let d=0;const s=e.body.pipeThrough(new TransformStream({transform(e,n){d+=e.byteLength;const o=i>0?Math.min(99,Math.round(d/i*100)):0;t({percent:o,bytesTransferred:d,bytesTotal:i}),n.enqueue(e)}})),l=new Response(s,{headers:e.headers});return o?await l.blob():new Uint8Array(await l.arrayBuffer())}(e,n.onProgress,n.expectedBytes,!0===n.preferBlob)}async function u(e){const n=e.resume?.transport,o="object"==typeof n&&null!==n?l.get(n):void 0;if(void 0!==e.resume&&(void 0===o||o.url!==e.url))throw e.onResumeInvalidated?.(),new t("download-range-invalid","下载断点不属于当前资源或传输实例,请重新下载");const a=new AbortController,u=()=>a.abort(e.signal?.reason);e.signal?.aborted?u():e.signal?.addEventListener("abort",u,{once:!0});const f=a.signal,w=void 0===o?[]:[...o.blobs];let b,g=w.reduce((e,t)=>e+(t?.size??0),0),h=o?.object,y=o?.mimeType??"",m=0,v=!1,p=!1;function B(){if(void 0===e.onResumable||void 0===h||0===g)return;const t=Object.freeze({});l.set(t,Object.freeze({url:e.url,object:Object.freeze({...h}),mimeType:y,blobs:Object.freeze([...w])}));const n=Object.freeze({bytesDownloaded:g,bytesTotal:h.total,transport:t});e.onResumable(n)}function E(){const t=h.total;e.onProgress?.({percent:Math.min(99,Math.round(g/t*100)),bytesTransferred:g,bytesTotal:t})}function M(e){return void 0!==h&&w[e]?.size===Math.min(d,h.total-e*d)}async function x(n,a=!1){const l=a?0:n*d+(w[n]?.size??0),u=a?0:void 0===h?l+d-1:Math.min((n+1)*d-1,h.total-1);return s(f,async()=>{const s=await fetch(e.url,{signal:f,cache:"no-store",headers:{Range:`bytes=${l}-${u}`}});if(void 0===o&&void 0===h&&0===n&&200===s.status)return i?.record("info","sdk","media","download-range-unavailable",()=>({data:{reason:"server-returned-full-response"}})),w[0]=await c(s,{...e,preferBlob:!0}),"single";if(429===s.status||s.status>=500)throw await(s.body?.cancel().catch(()=>{})),new t("download-range-failed",`媒体下载失败:HTTP ${s.status}`);let b;try{b=function(e,n,o,r){const a=/^bytes (\d+)-(\d+)\/(\d+)$/.exec(e.headers.get("content-range")??""),i=e.headers.get("etag")??"",d=e.headers.get("content-length"),s=Number(a?.[1]),l=Number(a?.[2]),c=Number(a?.[3]);if(206!==e.status||null===a||![s,l,c].every(Number.isSafeInteger)||c<=0||s!==n||l!==Math.min(o,c-1)||l<s||!/^"[\x21\x23-\x7E\x80-\xFF]*"$/.test(i)||null!==d&&(!/^\d+$/.test(d)||Number(d)!==l-s+1)||null!==e.headers.get("content-encoding")&&"identity"!==e.headers.get("content-encoding"))throw new t("download-range-invalid","分段下载响应无效:需要 206、正确 Content-Range、强 ETag 和未压缩字节;请检查下载域名的 CORS ExposeHeaders");if(void 0!==r&&(c!==r.total||i!==r.etag))throw new t("download-object-changed","分段下载期间文件版本发生变化,请重新下载");return{end:l,total:c,etag:i}}(s,l,u,h)}catch(e){throw await(s.body?.cancel().catch(()=>{})),e}if(void 0===h&&(h=b,y=s.headers.get("content-type")??"",i?.record("debug","sdk","media","download-range-started",()=>({data:{bytesTotal:b.total,partSizeBytes:d,concurrency:r}}))),a){if(1!==(await s.arrayBuffer()).byteLength)throw new t("download-range-invalid","下载对象校验响应长度无效")}else await async function(e,n,o){const r=e.body?.getReader();if(void 0===r)throw new t("download-range-invalid","分段下载没有响应字节流");const a=()=>{r.cancel().catch(()=>{})};f.addEventListener("abort",a,{once:!0});let i=0;try{for(;;){if(f.aborted)throw new DOMException("媒体下载已取消","AbortError");const e=await r.read();if(f.aborted)throw new DOMException("媒体下载已取消","AbortError");if(e.done)break;if(i+=e.value.byteLength,i>o)throw new t("download-range-invalid","分段下载字节数超过 Content-Range");const a=w[n];w[n]=new Blob(void 0===a?[e.value]:[a,e.value]),g+=e.value.byteLength,B(),E()}if(i!==o)throw new t("download-range-invalid","分段下载字节数与 Content-Range 不一致");E()}finally{f.removeEventListener("abort",a),await r.cancel().catch(()=>{}),r.releaseLock()}}(s,n,b.end-l+1);return"range"},e.getPriority)}try{if(void 0!==h)for(;m<Math.ceil(h.total/d)&&M(m);)m+=1;const e=void 0!==h&&m===Math.ceil(h.total/d),n=await x(m++,e);if("range"===n){const e=Math.ceil(h.total/d),n=Array.from({length:Math.min(r,Math.max(0,e-m))},async()=>{try{for(;!f.aborted&&m<e;){const e=m++;M(e)||await x(e)}}catch(e){(e=>{!p&&e instanceof t&&("download-range-invalid"===e.code||"download-object-changed"===e.code)&&(p=!0,b=e),v||(v=!0,b=e,a.abort())})(e)}});if(await Promise.allSettled(n),v)throw b}if(f.aborted)throw new DOMException("媒体下载已取消","AbortError");const o=[];if("range"===n)for(let e=0;e<Math.ceil(h.total/d);e+=1){if(!M(e))throw new t("download-range-invalid","下载仍缺少分段,不能交付完整文件");o.push(w[e])}return"single"===n?w[0]:new Blob(o,{type:y})}catch(n){throw a.abort(),p||n instanceof t&&("download-range-invalid"===n.code||"download-object-changed"===n.code)?e.onResumeInvalidated?.():B(),i?.record("warn","sdk","media","download-range-failed",()=>({data:{code:n instanceof t?n.code:"download-failed"}})),n}finally{e.signal?.removeEventListener("abort",u)}}return async function(e){if(void 0!==e.resume&&!a)throw e.onResumeInvalidated?.(),new t("download-range-invalid","当前下载器未开启断点下载,请重新下载");if(a&&(void 0!==e.resume||void 0!==e.onResumable||!0===e.preferBlob&&Number.isFinite(e.expectedBytes)&&e.expectedBytes>d)){const t=await u(e);return!0===e.preferBlob?t:new Uint8Array(await t.arrayBuffer())}return s(e.signal,async()=>{if(void 0!==e.onProgress&&"undefined"!=typeof XMLHttpRequest)return function(e){const t=!0===e.preferBlob;return new Promise((n,o)=>{const r=new XMLHttpRequest;let a=!1,i=()=>{};const d=()=>{e.signal?.removeEventListener("abort",i),r.onprogress=null,r.onload=null,r.onerror=null,r.onabort=null},s=e=>{a||(a=!0,d(),e())};i=()=>{r.abort(),s(()=>o(new DOMException("media download canceled","AbortError")))},r.open("GET",e.url,!0),r.responseType=t?"blob":"arraybuffer",r.onprogress=t=>{const n=void 0!==e.expectedBytes&&Number.isFinite(e.expectedBytes)&&e.expectedBytes>0?Math.floor(e.expectedBytes):0,o=t.lengthComputable&&t.total>0?t.total:n,r=o>0?Math.min(99,Math.round(t.loaded/o*100)):0;e.onProgress?.({percent:r,bytesTransferred:t.loaded,bytesTotal:o})},r.onload=()=>{if(r.status<200||r.status>=300)return void s(()=>o(new Error(`media download 失败:HTTP ${r.status}`)));const e=r.response;if(t)return"undefined"!=typeof Blob&&e instanceof Blob?void s(()=>n(e)):void s(()=>o(new Error("media download 失败:XHR 未返回 Blob")));e instanceof ArrayBuffer?s(()=>n(new Uint8Array(e))):s(()=>o(new Error("media download 失败:XHR 未返回 ArrayBuffer")))},r.onerror=()=>s(()=>o(new Error("media download 失败:network"))),r.onabort=()=>s(()=>o(new DOMException("media download canceled","AbortError"))),e.signal?.aborted?i():(e.signal?.addEventListener("abort",i,{once:!0}),r.send())})}(e);return c(await fetch(e.url,void 0===e.signal?{}:{signal:e.signal}),e)},e.getPriority)}}export{a as createMediaDownloader};
1
+ import{trackResponseActivity as e}from"../http/request-timeout.js";import{retryDownloadRequest as t,timedDownload as n,downloadNetworkOperation as o,MediaDownloadHttpError as a,MediaDownloadNetworkError as r}from"./media-download-request.js";import{createMediaRequestLimiter as i}from"./media-request-limiter.js";import{MediaTransferError as s}from"./media-transfer-errors.js";import{MEDIA_TRANSFER_PART_BYTES as d,MEDIA_TRANSFER_REQUEST_CONCURRENCY as l,MEDIA_DOWNLOAD_PART_CONCURRENCY as c}from"./media-transfer-policy.js";function u(u=!0,f,w=d){if(!Number.isSafeInteger(w)||w<=0)throw new RangeError("下载分段大小必须为正整数");const b=i(l),g=/* @__PURE__ */new WeakMap;async function y(e,t){if(!e.ok)throw await(e.body?.cancel().catch(()=>{})),new a(e.status);return void 0===t.onProgress||null===e.body?o(async()=>!0===t.preferBlob?await e.blob():new Uint8Array(await e.arrayBuffer())):async function(e,t,n,a=!1){const r=Number(e.headers.get("content-length")??0),i=void 0!==n&&Number.isFinite(n)&&n>0?Math.floor(n):0,s=Number.isFinite(r)&&r>0?r:i;let d=0;const l=e.body.getReader();let c=!1;const u=async e=>{c=!0;try{await l.cancel(e)}catch{}finally{l.releaseLock()}},f=new ReadableStream({async pull(e){try{const n=await o(()=>l.read());if(c)return;if(n.done)return c=!0,l.releaseLock(),void e.close();d+=n.value.byteLength;const a=s>0?Math.min(99,Math.round(d/s*100)):0;t({percent:a,bytesTransferred:d,bytesTotal:s}),e.enqueue(n.value)}catch(t){e.error(t),await u(t)}},cancel:u}),w=new Response(f,{headers:e.headers});return a?await w.blob():new Uint8Array(await w.arrayBuffer())}(e,t.onProgress,t.expectedBytes,!0===t.preferBlob)}async function h(r){const i=r.resume?.transport,d="object"==typeof i&&null!==i?g.get(i):void 0;if(void 0!==r.resume&&(void 0===d||d.url!==r.url))throw r.onResumeInvalidated?.(),new s("download-range-invalid","下载断点不属于当前资源或传输实例,请重新下载");const l=new AbortController,u=()=>l.abort(r.signal?.reason);r.signal?.aborted?u():r.signal?.addEventListener("abort",u,{once:!0});const h=l.signal,m=void 0===d?[]:[...d.blobs];let v,p=m.reduce((e,t)=>e+(t?.size??0),0),B=d?.object,M=d?.mimeType??"",E=0,x=!1,R=!1;function j(){if(void 0===r.onResumable||void 0===B||0===p)return;const e=Object.freeze({});g.set(e,Object.freeze({url:r.url,object:Object.freeze({...B}),mimeType:M,blobs:Object.freeze([...m])}));const t=Object.freeze({bytesDownloaded:p,bytesTotal:B.total,transport:e});r.onResumable(t)}function L(){const e=B.total;r.onProgress?.({percent:Math.min(99,Math.round(p/e*100)),bytesTransferred:p,bytesTotal:e})}function T(e){return void 0!==B&&m[e]?.size===Math.min(w,B.total-e*w)}async function A(i,l=!1){return t(h,()=>b(h,()=>n(h,async t=>{if(!l&&T(i))return"range";const n=l?0:i*w+(m[i]?.size??0),u=l?0:void 0===B?n+w-1:Math.min((i+1)*w-1,B.total-1),b=e(await o(()=>fetch(r.url,{signal:t.signal,cache:"no-store",headers:{Range:`bytes=${n}-${u}`}})),t);if(void 0===d&&void 0===B&&0===i&&200===b.status)return f?.record("info","sdk","media","download-range-unavailable",()=>({data:{reason:"server-returned-full-response"}})),m[0]=await y(b,{...r,signal:h,preferBlob:!0}),"single";if(429===b.status||b.status>=500)throw await(b.body?.cancel().catch(()=>{})),new a(b.status);let g;try{g=function(e,t,n,o){const a=/^bytes (\d+)-(\d+)\/(\d+)$/.exec(e.headers.get("content-range")??""),r=e.headers.get("etag")??"",i=e.headers.get("content-length"),d=Number(a?.[1]),l=Number(a?.[2]),c=Number(a?.[3]);if(206!==e.status||null===a||![d,l,c].every(Number.isSafeInteger)||c<=0||d!==t||l!==Math.min(n,c-1)||l<d||!/^"[\x21\x23-\x7E\x80-\xFF]*"$/.test(r)||null!==i&&(!/^\d+$/.test(i)||Number(i)!==l-d+1)||null!==e.headers.get("content-encoding")&&"identity"!==e.headers.get("content-encoding"))throw new s("download-range-invalid","分段下载响应无效:需要 206、正确 Content-Range、强 ETag 和未压缩字节;请检查下载域名的 CORS ExposeHeaders");if(void 0!==o&&(c!==o.total||r!==o.etag))throw new s("download-object-changed","分段下载期间文件版本发生变化,请重新下载");return{end:l,total:c,etag:r}}(b,n,u,B)}catch(e){throw await(b.body?.cancel().catch(()=>{})),e}if(void 0===B&&(B=g,M=b.headers.get("content-type")??"",f?.record("debug","sdk","media","download-range-started",()=>({data:{bytesTotal:g.total,partSizeBytes:w,concurrency:c}}))),l){if(1!==(await o(()=>b.arrayBuffer())).byteLength)throw new s("download-range-invalid","下载对象校验响应长度无效")}else await async function(e,t,n){const a=e.body?.getReader();if(void 0===a)throw new s("download-range-invalid","分段下载没有响应字节流");const r=()=>{a.cancel().catch(()=>{})};h.addEventListener("abort",r,{once:!0});let i=0;try{for(;;){if(h.aborted)throw new DOMException("媒体下载已取消","AbortError");const e=await o(()=>a.read());if(h.aborted)throw new DOMException("媒体下载已取消","AbortError");if(e.done)break;if(i+=e.value.byteLength,i>n)throw new s("download-range-invalid","分段下载字节数超过 Content-Range");const r=m[t];m[t]=new Blob(void 0===r?[e.value]:[r,e.value]),p+=e.value.byteLength,j(),L()}if(i!==n)throw new s("download-range-invalid","分段下载字节数与 Content-Range 不一致");L()}finally{h.removeEventListener("abort",r),await a.cancel().catch(()=>{}),a.releaseLock()}}(b,i,g.end-n+1);return"range"}),r.getPriority))}try{if(void 0!==B)for(;E<Math.ceil(B.total/w)&&T(E);)E+=1;const e=void 0!==B&&E===Math.ceil(B.total/w),t=await A(E++,e);if("range"===t){const e=Math.ceil(B.total/w),t=Array.from({length:Math.min(c,Math.max(0,e-E))},async()=>{try{for(;!h.aborted&&E<e;){const e=E++;T(e)||await A(e)}}catch(e){(e=>{!R&&e instanceof s&&("download-range-invalid"===e.code||"download-object-changed"===e.code)&&(R=!0,v=e),x||(x=!0,v=e,l.abort())})(e)}});if(await Promise.allSettled(t),x)throw v}if(h.aborted)throw new DOMException("媒体下载已取消","AbortError");const n=[];if("range"===t)for(let e=0;e<Math.ceil(B.total/w);e+=1){if(!T(e))throw new s("download-range-invalid","下载仍缺少分段,不能交付完整文件");n.push(m[e])}return"single"===t?m[0]:new Blob(n,{type:M})}catch(e){throw l.abort(),R||e instanceof s&&("download-range-invalid"===e.code||"download-object-changed"===e.code)?r.onResumeInvalidated?.():j(),f?.record("warn","sdk","media","download-range-failed",()=>({data:{code:e instanceof s?e.code:"download-failed"}})),e}finally{r.signal?.removeEventListener("abort",u)}}return async function(i){if(void 0!==i.resume&&!u)throw i.onResumeInvalidated?.(),new s("download-range-invalid","当前下载器未开启断点下载,请重新下载");if(u&&(void 0!==i.resume||void 0!==i.onResumable||!0===i.preferBlob&&Number.isFinite(i.expectedBytes)&&i.expectedBytes>w)){const e=await h(i);return!0===i.preferBlob?e:new Uint8Array(await e.arrayBuffer())}return t(i.signal,()=>b(i.signal,()=>n(i.signal,async t=>{const n={...i,signal:t.signal};if(void 0!==i.onProgress&&"undefined"!=typeof XMLHttpRequest)return function(e,t){const n=!0===e.preferBlob;return new Promise((o,i)=>{const s=new XMLHttpRequest;let d=!1,l=()=>{};const c=()=>{e.signal?.removeEventListener("abort",l),s.onprogress=null,s.onload=null,s.onerror=null,s.onabort=null,s.onreadystatechange=null},u=e=>{d||(d=!0,c(),e())};l=()=>{s.abort(),u(()=>i(new DOMException("media download canceled","AbortError")))},s.open("GET",e.url,!0),s.responseType=n?"blob":"arraybuffer";let f=0;s.onreadystatechange=()=>{2===s.readyState&&(t.touch(),s.status>0&&(s.status<200||s.status>=300)&&(u(()=>i(new a(s.status))),s.abort()))},s.onprogress=n=>{n.loaded>f&&(f=n.loaded,t.touch());const o=void 0!==e.expectedBytes&&Number.isFinite(e.expectedBytes)&&e.expectedBytes>0?Math.floor(e.expectedBytes):0,a=n.lengthComputable&&n.total>0?n.total:o,r=a>0?Math.min(99,Math.round(n.loaded/a*100)):0;try{e.onProgress?.({percent:r,bytesTransferred:n.loaded,bytesTotal:a})}catch(e){u(()=>i(e)),s.abort()}},s.onload=()=>{if(s.status<200||s.status>=300)return void u(()=>i(new a(s.status)));const e=s.response;if(n)return"undefined"!=typeof Blob&&e instanceof Blob?void u(()=>o(e)):void u(()=>i(new Error("media download 失败:XHR 未返回 Blob")));e instanceof ArrayBuffer?u(()=>o(new Uint8Array(e))):u(()=>i(new Error("media download 失败:XHR 未返回 ArrayBuffer")))},s.onerror=()=>u(()=>i(new r(new TypeError("media download 失败:network")))),s.onabort=()=>u(()=>i(new DOMException("media download canceled","AbortError"))),e.signal?.aborted?l():(e.signal?.addEventListener("abort",l,{once:!0}),s.send())})}(n,t);const s=await o(()=>fetch(i.url,{signal:t.signal}));return y(e(s,t),n)}),i.getPriority))}}export{u as createMediaDownloader};
@@ -1 +1 @@
1
- var t=Object.defineProperty,e=(e,i,r)=>((e,i,r)=>i in e?t(e,i,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[i]=r)(e,"symbol"!=typeof i?i+"":i,r);import{readImageDimensionsFromHeader as i}from"./image-header-dimensions.js";import{compressCanvasPng as r}from"./png-lossless-compression.js";import{createCanvasWebpEncoder as n}from"./webp-encoder.js";import{resolveMediaThumbnailPolicy as a}from"../../../im-core/src/media-thumbnail-policy.js";const o=a(),s=a(3),h=a(5,4),c={maxSize:960,targetBytes:8192,maxBytes:24576};function d(t){const e={active:0,disposed:!1},i=[];function r(){for(;!e.disposed&&e.active<t&&i.length>0;){const t=i.shift();if(t.signal?.aborted){t.reject(new DOMException("媒体预览准备已取消","AbortError"));continue}void 0!==t.onAbort&&t.signal?.removeEventListener("abort",t.onAbort),e.active+=1;let n=!1;t.resolve(()=>{n||(n=!0,e.active-=1,r())})}}return{acquire:t=>e.disposed?Promise.reject(new Error("媒体预览执行器已释放")):new Promise((e,n)=>{const a={resolve:e,reject:n,...void 0===t?{}:{signal:t}};void 0!==t&&(a.onAbort=()=>{const t=i.indexOf(a);t>=0&&i.splice(t,1),n(new DOMException("媒体预览准备已取消","AbortError"))},t.addEventListener("abort",a.onAbort,{once:!0})),i.push(a),r()}),dispose(){e.disposed=!0;for(const t of i.splice(0))void 0!==t.onAbort&&t.signal?.removeEventListener("abort",t.onAbort),t.reject(new Error("媒体预览执行器已释放"))}}}function l(t){if(t?.aborted)throw new DOMException("媒体预览准备已取消","AbortError")}const m=16777216;function f(t,e,i){const r=Math.min(1,i/Math.max(t,e));return function(t,e){const i=Math.min(1,4096/Math.max(t,e));let r=Math.max(1,Math.round(t*i)),n=Math.max(1,Math.round(e*i));const a=r*n;if(a>m){const t=Math.sqrt(m/a);r=Math.max(1,Math.floor(r*t)),n=Math.max(1,Math.floor(n*t))}return{width:r,height:n}}(Math.max(1,Math.round(t*r)),Math.max(1,Math.round(e*r)))}async function u(t,e){l(e);const i=await new Promise(e=>t.toBlob(e,"image/png"));if(l(e),null===i)throw new Error("媒体预览准备失败:Canvas 编码返回空结果");return await r(await i.arrayBuffer(),e)}async function g(t,e,i){l(i);const r=await new Promise(i=>t.toBlob(i,"image/jpeg",e));if(l(i),null===r)throw new Error("媒体预览准备失败:Canvas 编码返回空结果");return await r.arrayBuffer()}function w(t,e){if(!Number.isInteger(t)||t<=0)throw new RangeError(`${e}必须是正整数`)}function y(t,e){if(!Number.isFinite(t)||t<=0||t>1)throw new RangeError(`${e}必须大于 0 且不超过 1`)}function b(t){const e="webp"===t.posterEncoding,i=e?o:c,r="video"===t.kind?"视频封面":"图片缩略图",n=t.maxSize??i.maxSize,a=t.maxBytes??i.maxBytes,s=t.targetBytes??Math.min(i.targetBytes,a),h=t.quality??Math.max(e?.8:.72,t.minQuality??0),d=t.minQuality??Math.min(h,e?.65:.55);if(w(n,`${r}最大边长`),w(s,`${r}目标字节数`),w(a,`${r}最大字节数`),y(h,`${r}初始质量`),y(d,`${r}最低质量`),s>a)throw new RangeError(`${r}目标字节数不能超过最大字节数`);if(d>h)throw new RangeError(`${r}最低质量不能高于初始质量`);return{label:r,maxSize:n,targetBytes:s,maxBytes:a,initialQuality:h,minQuality:d}}class v extends Error{constructor(){super(...arguments),e(this,"name","ThumbnailByteLimitError")}}function p(t,e){const i=[128,64,192],r=i.map(()=>0),n=4*e;for(let a=0;a<t.length;a+=4){const o=t[a+3];if(0===o)continue;const s=a/4%e;if(!(s>0&&t[a-1]<o||s+1<e&&t[a+7]<o||a>=n&&t[a-n+3]<o||a+n+3<t.length&&t[a+n+3]<o))continue;const h=o/255;for(let e=0;e<i.length;e+=1){const n=i[e],o=Math.max(Math.abs(t[a]-n),Math.abs(t[a+1]-n),Math.abs(t[a+2]-n))*h;r[e]=r[e]+Math.min(64,o)}}let a=0;for(let t=1;t<i.length;t+=1)r[t]>r[a]&&(a=t);return`#${i[a].toString(16).padStart(2,"0").repeat(3)}`}async function M(t,e,i,r,n,a){const{label:o,maxSize:c,targetBytes:d,maxBytes:m,initialQuality:w,minQuality:y}=b(r),M="video"===r.kind,x="webp"===r.posterEncoding,E=x?n:g;l(a);const L=document.createElement("canvas");try{const n=L.getContext("2d");if(null===n)throw new Error("媒体预览准备失败:无法创建 Canvas 2D 上下文");let g;const b=e=>{l(a),L.width=e.width,L.height=e.height,void 0!==g&&(n.fillStyle=g,n.fillRect(0,0,e.width,e.height)),n.drawImage(t,0,0,e.width,e.height)},B=f(e,i,c);let j=B;const P=c<=(x?s.maxSize:640),R=x&&c<=h.maxSize,S=Math.min(R?80:P?240:320,Math.max(j.width,j.height));b(j);const z=M?void 0:n.getImageData(0,0,j.width,j.height).data;let D,A,$=!1;if(void 0!==z)for(let t=3;t<z.length;t+=4)if(z[t]<255){$=!0;break}if($&&x&&(g=p(z,B.width),b(j)),$&&!x){let t;for(let r=0;r<4;r+=1){const n=await u(L,a);if((void 0===t||n.byteLength<t.byteLength)&&(t=n),n.byteLength<=m)return n;const o=Math.max(j.width,j.height);if(o<=S||3===r)break;const s=Math.min(.8,Math.max(.35,.9*Math.sqrt(m/n.byteLength)));j=f(e,i,Math.max(S,Math.floor(o*s))),b(j)}if(void 0!==t&&t.byteLength<=m)return t;g=p(z,B.width),j=B,b(j)}const k=[.../* @__PURE__ */new Set([w,Math.max(y,w-.12),y])];for(const t of k){const e=await E(L,t,a);if((void 0===D||e.byteLength<D.byteLength)&&(D=e),e.byteLength<=m&&(void 0===A||x&&e.byteLength<A.byteLength)&&(A=e),e.byteLength<=d)return e}if(void 0!==A)return A;for(let t=0;t<3;t+=1){const t=Math.max(j.width,j.height);if(t<=S||void 0===D)break;const r=Math.sqrt(m/D.byteLength);j=f(e,i,Math.max(S,Math.floor(t*Math.min(.9,Math.max(.5,r))))),b(j);for(const t of/* @__PURE__ */new Set([w,y])){const e=await E(L,t,a);if(e.byteLength<D.byteLength&&(D=e),e.byteLength<=m&&(void 0===A||x&&e.byteLength<A.byteLength)&&(A=e),e.byteLength<=d)return e}if(void 0!==A)return A}if(void 0!==D&&D.byteLength<=m)return D;const O=Math.min(R?64:P?160:240,Math.max(j.width,j.height)),I=r.minQuality??Math.min(w,x?.55:.45);for(let t=0;t<2&&void 0!==D;t+=1){const r=Math.max(j.width,j.height),n=1===t?O:Math.max(O,Math.floor(r*Math.min(.9,.95*Math.sqrt(m/D.byteLength))));j=f(e,i,n),b(j);for(const t of/* @__PURE__ */new Set([y,I])){const e=await E(L,t,a);if(e.byteLength<D.byteLength&&(D=e),e.byteLength<=m)return e}if(n<=O)break}throw new v(`${o}生成失败:编码结果超过 ${m} 字节硬上限`)}finally{L.width=0,L.height=0}}function x(t,e,i,r){return l(i),new Promise((n,a)=>{let o,s,h,c;const d=()=>{void 0!==o&&clearTimeout(o),t.removeEventListener(e,s),t.removeEventListener("error",h),i?.removeEventListener("abort",c)};s=()=>{("seeked"===e?!t.seeking:t.readyState>=("loadedmetadata"===e?1:2))&&(d(),n())},h=()=>{d(),a(new Error(`视频预览准备失败:${e} 前解码错误`))},c=()=>{d(),a(new DOMException("媒体预览准备已取消","AbortError"))},o=setTimeout(()=>{d(),a(new Error(`视频预览准备失败:${e} 超时`))},15e3),t.addEventListener(e,s),t.addEventListener("error",h,{once:!0}),i?.addEventListener("abort",c,{once:!0});try{r?.(),i?.aborted?c():null!==t.error?h():s()}catch(t){d(),a(t)}})}function E(t={}){const e=t.maxConcurrency??function(){if("undefined"==typeof navigator)return 2;const t=navigator.userAgent;if(/iPad|iPhone|iPod/.test(t)||/Macintosh/.test(t)&&(navigator.maxTouchPoints??0)>1)return 1;const e=navigator.deviceMemory;return void 0!==e&&e<=2?1:2}();if(!Number.isInteger(e)||e<1||e>4)throw new RangeError("媒体预览并发数必须是 1-4 的整数");const r=d(e),a=d(1),o=n();let s=!1;return{async prepare(t,e){if(s)throw new Error("媒体预览执行器已释放");const n="video"===t.kind,h=await(n?a:r).acquire(e);try{const r=n?await async function(t,e,i){const r=URL.createObjectURL(t.file),n=document.createElement("video");n.muted=!0,n.preload="metadata",n.playsInline=!0;try{await x(n,"loadedmetadata",i,()=>{n.src=r,n.load()});const a=n.videoWidth,o=n.videoHeight;if(a<=0||o<=0)throw new Error("视频预览准备失败:视频尺寸无效");const s=Number.isFinite(n.duration)&&n.duration>0?n.duration:void 0,h=void 0===t.atTime?void 0===s?0:Math.min(1,s/2):Math.max(0,Math.min(t.atTime,s??t.atTime));h>0&&await x(n,"seeked",i,()=>{n.currentTime=h}),await x(n,"loadeddata",i,()=>{n.preload="auto"});const c=void 0===s?void 0:s;let d,l;try{d=await M(n,a,o,t,e,i)}catch(t){if(i?.aborted||t instanceof RangeError)throw t}if(t.includeDisplayPoster)try{l=await M(n,a,o,{...t,posterEncoding:"jpeg-or-png",maxSize:960,targetBytes:163840,maxBytes:262144,quality:.9,minQuality:.8},e,i)}catch(t){if(i?.aborted||t instanceof RangeError)throw t}return{width:a,height:o,...void 0===c?{}:{duration:c},...void 0===d?{}:{posterData:d},...void 0===l?{}:{displayPosterData:l}}}finally{n.pause(),n.removeAttribute("src"),n.load(),URL.revokeObjectURL(r)}}(t,o.encode,e):await async function(t,e,r){l(r);const{maxSize:n,targetBytes:a,maxBytes:o}=b(t),s=await i(t.file);function h(e,i){return"webp"!==t.posterEncoding&&"image/jpeg"===t.file.type&&t.file.size<=a&&Math.max(e,i)<=n}async function c(e,i){const a="webp"!==t.posterEncoding&&"image/jpeg"===t.file.type&&Math.max(i.width,i.height)<=n&&t.file.size<=o;try{const i=await e;if(!a||t.file.size>i.byteLength)return i}catch(t){if(!(a&&t instanceof v))throw t}return l(r),await t.file.arrayBuffer()}if(l(r),void 0!==s){if(!1===t.includePoster)return s;if(h(s.width,s.height))return{...s,posterData:await t.file.arrayBuffer()}}const d=void 0===s?void 0:f(s.width,s.height,n);let m;try{m=void 0===d?await createImageBitmap(t.file):await createImageBitmap(t.file,{resizeWidth:d.width,resizeHeight:d.height,resizeQuality:"high"})}catch(t){if(r?.aborted)throw t}if(void 0!==m)try{l(r);const i=s??{width:m.width,height:m.height};return!1===t.includePoster?i:void 0===s&&h(m.width,m.height)?{...i,posterData:await t.file.arrayBuffer()}:{...i,posterData:await c(M(m,m.width,m.height,t,e,r),i)}}finally{m.close()}const u=URL.createObjectURL(t.file),g=new Image;try{await new Promise((t,e)=>{let i,n;const a=()=>{void 0!==i&&clearTimeout(i),g.onload=null,g.onerror=null,r?.removeEventListener("abort",n)};n=()=>{a(),e(new DOMException("媒体预览准备已取消","AbortError"))},g.onload=()=>{a(),t()},g.onerror=()=>{a(),e(new Error("图片预览准备失败:Image 解码错误"))},i=setTimeout(()=>{a(),e(new Error("图片预览准备失败:Image 解码超时"))},15e3),r?.addEventListener("abort",n,{once:!0}),g.src=u});const i=s??{width:g.naturalWidth,height:g.naturalHeight};return!1===t.includePoster?i:h(g.naturalWidth,g.naturalHeight)?{...i,posterData:await t.file.arrayBuffer()}:{...i,posterData:await c(M(g,g.naturalWidth,g.naturalHeight,t,e,r),i)}}finally{g.removeAttribute("src"),URL.revokeObjectURL(u)}}(t,o.encode,e);return l(e),r}finally{h()}},dispose(){s||(s=!0,r.dispose(),a.dispose(),o.dispose())}}}export{E as createMediaPreviewPreparationExecutor};
1
+ var t=Object.defineProperty,e=(e,i,r)=>((e,i,r)=>i in e?t(e,i,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[i]=r)(e,"symbol"!=typeof i?i+"":i,r);import{readImageDimensionsFromHeader as i}from"./image-header-dimensions.js";import{compressCanvasPng as r}from"./png-lossless-compression.js";import{createCanvasWebpEncoder as a}from"./webp-encoder.js";import{resolveMediaThumbnailPolicy as n}from"../../../im-core/src/media-thumbnail-policy.js";const o=n(),s=n(3),h=n(5,4),c={maxSize:960,targetBytes:8192,maxBytes:24576};function d(t){const e={active:0,disposed:!1},i=[];function r(){for(;!e.disposed&&e.active<t&&i.length>0;){const t=i.shift();if(t.signal?.aborted){t.reject(new DOMException("媒体预览准备已取消","AbortError"));continue}void 0!==t.onAbort&&t.signal?.removeEventListener("abort",t.onAbort),e.active+=1;let a=!1;t.resolve(()=>{a||(a=!0,e.active-=1,r())})}}return{acquire:t=>e.disposed?Promise.reject(new Error("媒体预览执行器已释放")):new Promise((e,a)=>{const n={resolve:e,reject:a,...void 0===t?{}:{signal:t}};void 0!==t&&(n.onAbort=()=>{const t=i.indexOf(n);t>=0&&i.splice(t,1),a(new DOMException("媒体预览准备已取消","AbortError"))},t.addEventListener("abort",n.onAbort,{once:!0})),i.push(n),r()}),dispose(){e.disposed=!0;for(const t of i.splice(0))void 0!==t.onAbort&&t.signal?.removeEventListener("abort",t.onAbort),t.reject(new Error("媒体预览执行器已释放"))}}}function l(t){if(t?.aborted)throw new DOMException("媒体预览准备已取消","AbortError")}const m=16777216;function f(t,e,i){const r=Math.min(1,i/Math.max(t,e));return function(t,e){const i=Math.min(1,4096/Math.max(t,e));let r=Math.max(1,Math.round(t*i)),a=Math.max(1,Math.round(e*i));const n=r*a;if(n>m){const t=Math.sqrt(m/n);r=Math.max(1,Math.floor(r*t)),a=Math.max(1,Math.floor(a*t))}return{width:r,height:a}}(Math.max(1,Math.round(t*r)),Math.max(1,Math.round(e*r)))}async function u(t,e){l(e);const i=await new Promise(e=>t.toBlob(e,"image/png"));if(l(e),null===i)throw new Error("媒体预览准备失败:Canvas 编码返回空结果");return await r(await i.arrayBuffer(),e)}async function g(t,e,i){l(i);const r=await new Promise(i=>t.toBlob(i,"image/jpeg",e));if(l(i),null===r)throw new Error("媒体预览准备失败:Canvas 编码返回空结果");return await r.arrayBuffer()}function w(t,e){if(!Number.isInteger(t)||t<=0)throw new RangeError(`${e}必须是正整数`)}function y(t,e){if(!Number.isFinite(t)||t<=0||t>1)throw new RangeError(`${e}必须大于 0 且不超过 1`)}function b(t){const e="webp"===t.posterEncoding,i=e?o:c,r="video"===t.kind?"视频封面":"图片缩略图",a=t.maxSize??i.maxSize,n=t.maxBytes??i.maxBytes,s=t.targetBytes??Math.min(i.targetBytes,n),h=t.quality??Math.max(e?.8:.72,t.minQuality??0),d=t.minQuality??Math.min(h,e?.65:.55);if(w(a,`${r}最大边长`),w(s,`${r}目标字节数`),w(n,`${r}最大字节数`),y(h,`${r}初始质量`),y(d,`${r}最低质量`),s>n)throw new RangeError(`${r}目标字节数不能超过最大字节数`);if(d>h)throw new RangeError(`${r}最低质量不能高于初始质量`);return{label:r,maxSize:a,targetBytes:s,maxBytes:n,initialQuality:h,minQuality:d}}class v extends Error{constructor(){super(...arguments),e(this,"name","ThumbnailByteLimitError")}}function p(t,e){const i=[128,64,192],r=i.map(()=>0),a=4*e;for(let n=0;n<t.length;n+=4){const o=t[n+3];if(0===o)continue;const s=n/4%e;if(!(s>0&&t[n-1]<o||s+1<e&&t[n+7]<o||n>=a&&t[n-a+3]<o||n+a+3<t.length&&t[n+a+3]<o))continue;const h=o/255;for(let e=0;e<i.length;e+=1){const a=i[e],o=Math.max(Math.abs(t[n]-a),Math.abs(t[n+1]-a),Math.abs(t[n+2]-a))*h;r[e]=r[e]+Math.min(64,o)}}let n=0;for(let t=1;t<i.length;t+=1)r[t]>r[n]&&(n=t);return`#${i[n].toString(16).padStart(2,"0").repeat(3)}`}async function M(t,e,i,r,a,n){const{label:o,maxSize:c,targetBytes:d,maxBytes:m,initialQuality:w,minQuality:y}=b(r),M="video"===r.kind,x="webp"===r.posterEncoding,E=x?a:g;l(n);const L=document.createElement("canvas");try{const a=L.getContext("2d");if(null===a)throw new Error("媒体预览准备失败:无法创建 Canvas 2D 上下文");let g;const b=e=>{l(n),L.width=e.width,L.height=e.height,void 0!==g&&(a.fillStyle=g,a.fillRect(0,0,e.width,e.height)),a.drawImage(t,0,0,e.width,e.height)},B=f(e,i,c);let j=B;const P=c<=(x?s.maxSize:640),R=x&&c<=h.maxSize,S=Math.min(R?80:P?240:320,Math.max(j.width,j.height));b(j);const z=M?void 0:a.getImageData(0,0,j.width,j.height).data;let D,A,$=!1;if(void 0!==z)for(let t=3;t<z.length;t+=4)if(z[t]<255){$=!0;break}if($&&x&&!r.preserveTransparency&&(g=p(z,B.width),b(j)),$&&!x){let t;for(let r=0;r<4;r+=1){const a=await u(L,n);if((void 0===t||a.byteLength<t.byteLength)&&(t=a),a.byteLength<=m)return a;const o=Math.max(j.width,j.height);if(o<=S||3===r)break;const s=Math.min(.8,Math.max(.35,.9*Math.sqrt(m/a.byteLength)));j=f(e,i,Math.max(S,Math.floor(o*s))),b(j)}if(void 0!==t&&t.byteLength<=m)return t;g=p(z,B.width),j=B,b(j)}const k=[.../* @__PURE__ */new Set([w,Math.max(y,w-.12),y])];for(const t of k){const e=await E(L,t,n);if((void 0===D||e.byteLength<D.byteLength)&&(D=e),e.byteLength<=m&&(void 0===A||x&&e.byteLength<A.byteLength)&&(A=e),e.byteLength<=d)return e}if(void 0!==A)return A;for(let t=0;t<3;t+=1){const t=Math.max(j.width,j.height);if(t<=S||void 0===D)break;const r=Math.sqrt(m/D.byteLength);j=f(e,i,Math.max(S,Math.floor(t*Math.min(.9,Math.max(.5,r))))),b(j);for(const t of/* @__PURE__ */new Set([w,y])){const e=await E(L,t,n);if(e.byteLength<D.byteLength&&(D=e),e.byteLength<=m&&(void 0===A||x&&e.byteLength<A.byteLength)&&(A=e),e.byteLength<=d)return e}if(void 0!==A)return A}if(void 0!==D&&D.byteLength<=m)return D;const O=Math.min(R?64:P?160:240,Math.max(j.width,j.height)),T=r.minQuality??Math.min(w,x?.55:.45);for(let t=0;t<2&&void 0!==D;t+=1){const r=Math.max(j.width,j.height),a=1===t?O:Math.max(O,Math.floor(r*Math.min(.9,.95*Math.sqrt(m/D.byteLength))));j=f(e,i,a),b(j);for(const t of/* @__PURE__ */new Set([y,T])){const e=await E(L,t,n);if(e.byteLength<D.byteLength&&(D=e),e.byteLength<=m)return e}if(a<=O)break}throw new v(`${o}生成失败:编码结果超过 ${m} 字节硬上限`)}finally{L.width=0,L.height=0}}function x(t,e,i,r){return l(i),new Promise((a,n)=>{let o,s,h,c;const d=()=>{void 0!==o&&clearTimeout(o),t.removeEventListener(e,s),t.removeEventListener("error",h),i?.removeEventListener("abort",c)};s=()=>{("seeked"===e?!t.seeking:t.readyState>=("loadedmetadata"===e?1:2))&&(d(),a())},h=()=>{d(),n(new Error(`视频预览准备失败:${e} 前解码错误`))},c=()=>{d(),n(new DOMException("媒体预览准备已取消","AbortError"))},o=setTimeout(()=>{d(),n(new Error(`视频预览准备失败:${e} 超时`))},15e3),t.addEventListener(e,s),t.addEventListener("error",h,{once:!0}),i?.addEventListener("abort",c,{once:!0});try{r?.(),i?.aborted?c():null!==t.error?h():s()}catch(t){d(),n(t)}})}function E(t={}){const e=t.maxConcurrency??function(){if("undefined"==typeof navigator)return 2;const t=navigator.userAgent;if(/iPad|iPhone|iPod/.test(t)||/Macintosh/.test(t)&&(navigator.maxTouchPoints??0)>1)return 1;const e=navigator.deviceMemory;return void 0!==e&&e<=2?1:2}();if(!Number.isInteger(e)||e<1||e>4)throw new RangeError("媒体预览并发数必须是 1-4 的整数");const r=d(e),n=d(1),o=a();let s=!1;return{async prepare(t,e){if(s)throw new Error("媒体预览执行器已释放");const a="video"===t.kind,h=await(a?n:r).acquire(e);try{const r=a?await async function(t,e,i){const r=URL.createObjectURL(t.file),a=document.createElement("video");a.muted=!0,a.preload="metadata",a.playsInline=!0;try{await x(a,"loadedmetadata",i,()=>{a.src=r,a.load()});const n=a.videoWidth,o=a.videoHeight;if(n<=0||o<=0)throw new Error("视频预览准备失败:视频尺寸无效");const s=Number.isFinite(a.duration)&&a.duration>0?a.duration:void 0,h=void 0===t.atTime?void 0===s?0:Math.min(1,s/2):Math.max(0,Math.min(t.atTime,s??t.atTime));h>0&&await x(a,"seeked",i,()=>{a.currentTime=h}),await x(a,"loadeddata",i,()=>{a.preload="auto"});const c=void 0===s?void 0:s;let d,l;try{d=await M(a,n,o,t,e,i)}catch(t){if(i?.aborted||t instanceof RangeError)throw t}if(t.includeDisplayPoster)try{l=await M(a,n,o,{...t,posterEncoding:"webp",maxSize:960,targetBytes:131072,maxBytes:262144,quality:.85,minQuality:.82},e,i)}catch(t){if(i?.aborted||t instanceof RangeError)throw t}return{width:n,height:o,...void 0===c?{}:{duration:c},...void 0===d?{}:{posterData:d},...void 0===l?{}:{displayPosterData:l}}}finally{a.pause(),a.removeAttribute("src"),a.load(),URL.revokeObjectURL(r)}}(t,o.encode,e):await async function(t,e,r){l(r);const{maxSize:a,targetBytes:n,maxBytes:o}=b(t),s=await i(t.file);function h(e,i){return"webp"!==t.posterEncoding&&"image/jpeg"===t.file.type&&t.file.size<=n&&Math.max(e,i)<=a}async function c(e,i){const n="webp"!==t.posterEncoding&&"image/jpeg"===t.file.type&&Math.max(i.width,i.height)<=a&&t.file.size<=o;try{const i=await e;if(!n||t.file.size>i.byteLength)return i}catch(t){if(!(n&&t instanceof v))throw t}return l(r),await t.file.arrayBuffer()}if(l(r),void 0!==s){if(!1===t.includePoster)return s;if(h(s.width,s.height))return{...s,posterData:await t.file.arrayBuffer()}}const d=void 0===s?void 0:f(s.width,s.height,a);let m;try{m=void 0===d?await createImageBitmap(t.file):await createImageBitmap(t.file,{resizeWidth:d.width,resizeHeight:d.height,resizeQuality:"high"})}catch(t){if(r?.aborted)throw t}if(void 0!==m)try{l(r);const i=s??{width:m.width,height:m.height};return!1===t.includePoster?i:void 0===s&&h(m.width,m.height)?{...i,posterData:await t.file.arrayBuffer()}:{...i,posterData:await c(M(m,m.width,m.height,t,e,r),i)}}finally{m.close()}const u=URL.createObjectURL(t.file),g=new Image;try{await new Promise((t,e)=>{let i,a;const n=()=>{void 0!==i&&clearTimeout(i),g.onload=null,g.onerror=null,r?.removeEventListener("abort",a)};a=()=>{n(),e(new DOMException("媒体预览准备已取消","AbortError"))},g.onload=()=>{n(),t()},g.onerror=()=>{n(),e(new Error("图片预览准备失败:Image 解码错误"))},i=setTimeout(()=>{n(),e(new Error("图片预览准备失败:Image 解码超时"))},15e3),r?.addEventListener("abort",a,{once:!0}),g.src=u});const i=s??{width:g.naturalWidth,height:g.naturalHeight};return!1===t.includePoster?i:h(g.naturalWidth,g.naturalHeight)?{...i,posterData:await t.file.arrayBuffer()}:{...i,posterData:await c(M(g,g.naturalWidth,g.naturalHeight,t,e,r),i)}}finally{g.removeAttribute("src"),URL.revokeObjectURL(u)}}(t,o.encode,e);return l(e),r}finally{h()}},dispose(){s||(s=!0,r.dispose(),n.dispose(),o.dispose())}}}export{E as createMediaPreviewPreparationExecutor};
@@ -1 +1 @@
1
- import{MediaTransferError as e}from"./media-transfer-errors.js";const r=16777216,i=3,t=3,o=6,n=5*1024**3,a=1e4;function d(i,t){if(!Number.isSafeInteger(i)||i<0)throw new e("upload-source-invalid","媒体上传大小必须为非负安全整数字节数");const o=t&&i>r,a=o?Math.ceil(i/r):1;if(!o&&i>n)throw new e("upload-size-exceeded","单次 PUT 最多支持 5 GiB;更大对象需要显式启用并配置分片上传");if(a>1e4)throw new e("upload-size-exceeded","当前 16 MiB 分片策略最多支持 10,000 片(156.25 GiB)");return{multipart:o,partSize:r,partCount:a}}export{a as MAX_MEDIA_UPLOAD_PARTS,n as MAX_SINGLE_PUT_BYTES,t as MEDIA_DOWNLOAD_PART_CONCURRENCY,r as MEDIA_TRANSFER_PART_BYTES,o as MEDIA_TRANSFER_REQUEST_CONCURRENCY,i as MEDIA_UPLOAD_PART_CONCURRENCY,d as resolveMediaUploadPlan};
1
+ import{MediaTransferError as e}from"./media-transfer-errors.js";const r=8388608,i=16777216,t=3,o=3,n=6,a=5*1024**3,d=1e4;function s(t,o){if(!Number.isSafeInteger(t)||t<0)throw new e("upload-source-invalid","媒体上传大小必须为非负安全整数字节数");const n=o&&t>=r,d=n?Math.ceil(t/i):1;if(!n&&t>a)throw new e("upload-size-exceeded","单次 PUT 最多支持 5 GiB;更大对象需要显式启用并配置分片上传");if(d>1e4)throw new e("upload-size-exceeded","当前 16 MiB 分片策略最多支持 10,000 片(156.25 GiB)");return{multipart:n,partSize:i,partCount:d}}export{d as MAX_MEDIA_UPLOAD_PARTS,a as MAX_SINGLE_PUT_BYTES,o as MEDIA_DOWNLOAD_PART_CONCURRENCY,i as MEDIA_TRANSFER_PART_BYTES,n as MEDIA_TRANSFER_REQUEST_CONCURRENCY,r as MEDIA_UPLOAD_MULTIPART_THRESHOLD_BYTES,t as MEDIA_UPLOAD_PART_CONCURRENCY,s as resolveMediaUploadPlan};