k2-im 0.2.14 → 0.2.15

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.
@@ -5557,7 +5557,7 @@ interface ImMediaTasksApi {
5557
5557
  *
5558
5558
  * 返回的对象可以**原样**作为 `localPreviewMetadata` 交给 `sendImage` / `sendVideo` /
5559
5559
  * `sendMediaAlbum`:发送链路会复用可用元信息;合集项超出其可用消息空间时重新生成封面。
5560
- * posterData 使用共享自适应发送规格(视频 JPEG,长边最多 480px,目标 8 KiB、硬上限 24 KiB);includeDisplayPoster 可在同一次解码中额外生成
5560
+ * posterData 使用共享自适应发送规格(视频 JPEG,长边最多 960px,目标 8 KiB、硬上限 24 KiB);includeDisplayPoster 可在同一次解码中额外生成
5561
5561
  * 仅供页面显示的高清 displayPoster(长边最多 960),不得绕过发送校验直接用它替换 posterData。
5562
5562
  *
5563
5563
  * @param input.kind 按文件类型选解码路径;`'video'` 才会抽首帧与读时长。
@@ -5591,7 +5591,8 @@ interface ImMediaTasksApi {
5591
5591
  }) => Promise<Blob | null>;
5592
5592
  /**
5593
5593
  * 为本地图片生成仅供胶片条/相册格显示的缩略图,不上传、不写缓存、不改变发送文件与元信息。
5594
- * JPEG 原文件按签名识别并生成 JPEG 显示缩略图,最长边最多 256px 且不放大,与发送预读共用账号的图片解码预算。
5594
+ * JPEG 原文件按签名识别并生成显示图,最长边最多 1440px、不放大,质量 0.9、最多 512 KiB。
5595
+ * 显示图与发送缩略图使用独立字节预算,解码仍共用账号的并发限制。
5595
5596
  * 其它格式直接返回原 Blob,保留透明与动画;宿主负责返回 Blob 的 object URL 生命周期。
5596
5597
  * 生成失败返回 null 并记录诊断;取消、账号失效和未装配能力保持明确错误。
5597
5598
  * @param input.file 原始本地图片;返回的显示资源不得代替它传入发送命令。
@@ -5668,7 +5668,7 @@ interface ImMediaTasksApi {
5668
5668
  *
5669
5669
  * 返回的对象可以**原样**作为 `localPreviewMetadata` 交给 `sendImage` / `sendVideo` /
5670
5670
  * `sendMediaAlbum`:发送链路会复用可用元信息;合集项超出其可用消息空间时重新生成封面。
5671
- * posterData 使用共享自适应发送规格(视频 JPEG,长边最多 480px,目标 8 KiB、硬上限 24 KiB);includeDisplayPoster 可在同一次解码中额外生成
5671
+ * posterData 使用共享自适应发送规格(视频 JPEG,长边最多 960px,目标 8 KiB、硬上限 24 KiB);includeDisplayPoster 可在同一次解码中额外生成
5672
5672
  * 仅供页面显示的高清 displayPoster(长边最多 960),不得绕过发送校验直接用它替换 posterData。
5673
5673
  *
5674
5674
  * @param input.kind 按文件类型选解码路径;`'video'` 才会抽首帧与读时长。
@@ -5702,7 +5702,8 @@ interface ImMediaTasksApi {
5702
5702
  }) => Promise<Blob | null>;
5703
5703
  /**
5704
5704
  * 为本地图片生成仅供胶片条/相册格显示的缩略图,不上传、不写缓存、不改变发送文件与元信息。
5705
- * JPEG 原文件按签名识别并生成 JPEG 显示缩略图,最长边最多 256px 且不放大,与发送预读共用账号的图片解码预算。
5705
+ * JPEG 原文件按签名识别并生成显示图,最长边最多 1440px、不放大,质量 0.9、最多 512 KiB。
5706
+ * 显示图与发送缩略图使用独立字节预算,解码仍共用账号的并发限制。
5706
5707
  * 其它格式直接返回原 Blob,保留透明与动画;宿主负责返回 Blob 的 object URL 生命周期。
5707
5708
  * 生成失败返回 null 并记录诊断;取消、账号失效和未装配能力保持明确错误。
5708
5709
  * @param input.file 原始本地图片;返回的显示资源不得代替它传入发送命令。
package/dist/index.d.ts CHANGED
@@ -6240,7 +6240,7 @@ interface ImMediaTasksApi {
6240
6240
  *
6241
6241
  * 返回的对象可以**原样**作为 `localPreviewMetadata` 交给 `sendImage` / `sendVideo` /
6242
6242
  * `sendMediaAlbum`:发送链路会复用可用元信息;合集项超出其可用消息空间时重新生成封面。
6243
- * posterData 使用共享自适应发送规格(视频 JPEG,长边最多 480px,目标 8 KiB、硬上限 24 KiB);includeDisplayPoster 可在同一次解码中额外生成
6243
+ * posterData 使用共享自适应发送规格(视频 JPEG,长边最多 960px,目标 8 KiB、硬上限 24 KiB);includeDisplayPoster 可在同一次解码中额外生成
6244
6244
  * 仅供页面显示的高清 displayPoster(长边最多 960),不得绕过发送校验直接用它替换 posterData。
6245
6245
  *
6246
6246
  * @param input.kind 按文件类型选解码路径;`'video'` 才会抽首帧与读时长。
@@ -6274,7 +6274,8 @@ interface ImMediaTasksApi {
6274
6274
  }) => Promise<Blob | null>;
6275
6275
  /**
6276
6276
  * 为本地图片生成仅供胶片条/相册格显示的缩略图,不上传、不写缓存、不改变发送文件与元信息。
6277
- * JPEG 原文件按签名识别并生成 JPEG 显示缩略图,最长边最多 256px 且不放大,与发送预读共用账号的图片解码预算。
6277
+ * JPEG 原文件按签名识别并生成显示图,最长边最多 1440px、不放大,质量 0.9、最多 512 KiB。
6278
+ * 显示图与发送缩略图使用独立字节预算,解码仍共用账号的并发限制。
6278
6279
  * 其它格式直接返回原 Blob,保留透明与动画;宿主负责返回 Blob 的 object URL 生命周期。
6279
6280
  * 生成失败返回 null 并记录诊断;取消、账号失效和未装配能力保持明确错误。
6280
6281
  * @param input.file 原始本地图片;返回的显示资源不得代替它传入发送命令。
@@ -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{resolveMediaThumbnailPolicy as a}from"../../../im-core/src/media-thumbnail-policy.js";const n=a();function o(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 s(t){if(t?.aborted)throw new DOMException("媒体预览准备已取消","AbortError")}const h=16777216;function c(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>h){const t=Math.sqrt(h/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 d(t,e){s(e);const i=await new Promise(e=>t.toBlob(e,"image/png"));if(s(e),null===i)throw new Error("媒体预览准备失败:Canvas 编码返回空结果");return await r(await i.arrayBuffer(),e)}async function l(t,e,i){s(i);const r=await new Promise(i=>t.toBlob(i,"image/jpeg",e));if(s(i),null===r)throw new Error("媒体预览准备失败:Canvas 编码返回空结果");return await r.arrayBuffer()}function f(t,e){if(!Number.isInteger(t)||t<=0)throw new RangeError(`${e}必须是正整数`)}function m(t,e){if(!Number.isFinite(t)||t<=0||t>1)throw new RangeError(`${e}必须大于 0 且不超过 1`)}function u(t){const e="video"===t.kind?"视频封面":"图片缩略图",i=t.maxSize??n.maxSize,r=t.maxBytes??n.maxBytes,a=t.targetBytes??Math.min(n.targetBytes,r),o=t.quality??Math.max(.72,t.minQuality??0),s=t.minQuality??Math.min(o,.55);if(f(i,`${e}最大边长`),f(a,`${e}目标字节数`),f(r,`${e}最大字节数`),m(o,`${e}初始质量`),m(s,`${e}最低质量`),a>r)throw new RangeError(`${e}目标字节数不能超过最大字节数`);if(s>o)throw new RangeError(`${e}最低质量不能高于初始质量`);return{label:e,maxSize:i,targetBytes:a,maxBytes:r,initialQuality:o,minQuality:s}}class w extends Error{constructor(){super(...arguments),e(this,"name","ThumbnailByteLimitError")}}async function g(t,e,i,r,a){const{label:n,maxSize:o,targetBytes:h,maxBytes:f,initialQuality:m,minQuality:g}=u(r),y="video"===r.kind;s(a);const v=document.createElement("canvas");try{const u=v.getContext("2d");if(null===u)throw new Error("媒体预览准备失败:无法创建 Canvas 2D 上下文");let b;const M=e=>{s(a),v.width=e.width,v.height=e.height,void 0!==b&&(u.fillStyle=b,u.fillRect(0,0,e.width,e.height)),u.drawImage(t,0,0,e.width,e.height)},p=c(e,i,o);let x=p;const E=Math.min(o<=320?240:320,Math.max(x.width,x.height));M(x);const L=y?void 0:u.getImageData(0,0,x.width,x.height).data;let B,P=!1;if(void 0!==L)for(let t=3;t<L.length;t+=4)if(L[t]<255){P=!0;break}if(P){let t;for(let r=0;r<4;r+=1){const n=await d(v,a);if((void 0===t||n.byteLength<t.byteLength)&&(t=n),n.byteLength<=h)return n;const o=Math.max(x.width,x.height);if(o<=E||3===r)break;const s=Math.min(.8,Math.max(.35,.9*Math.sqrt(h/n.byteLength)));x=c(e,i,Math.max(E,Math.floor(o*s))),M(x)}if(void 0!==t&&t.byteLength<=f)return t;b=function(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)}`}(L,p.width),x=p,M(x)}const R=[.../* @__PURE__ */new Set([m,Math.max(g,m-.12),g])];for(const t of R){const e=await l(v,t,a);if((void 0===B||e.byteLength<B.byteLength)&&(B=e),e.byteLength<=h)return e}for(let t=0;t<3;t+=1){const t=Math.max(x.width,x.height);if(t<=E||void 0===B)break;const r=Math.sqrt(h/B.byteLength);x=c(e,i,Math.max(E,Math.floor(t*Math.min(.9,Math.max(.5,r))))),M(x);for(const t of[m,g]){const e=await l(v,t,a);if(e.byteLength<B.byteLength&&(B=e),e.byteLength<=h)return e}}if(void 0!==B&&B.byteLength<=f)return B;const j=Math.min(o<=320?160:240,Math.max(x.width,x.height)),D=r.minQuality??Math.min(m,.45);for(let t=0;t<2&&void 0!==B;t+=1){const r=Math.max(x.width,x.height),n=1===t?j:Math.max(j,Math.floor(r*Math.min(.9,.95*Math.sqrt(f/B.byteLength))));x=c(e,i,n),M(x);for(const t of/* @__PURE__ */new Set([g,D])){const e=await l(v,t,a);if(e.byteLength<B.byteLength&&(B=e),e.byteLength<=f)return e}if(n<=j)break}throw new w(`${n}生成失败:编码结果超过 ${f} 字节硬上限`)}finally{v.width=0,v.height=0}}function y(t,e,i,r){return s(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 v(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=o(e),a=o(1);let n=!1;return{async prepare(t,e){if(n)throw new Error("媒体预览执行器已释放");const o="video"===t.kind,h=await(o?a:r).acquire(e);try{const r=o?await async function(t,e){const i=URL.createObjectURL(t.file),r=document.createElement("video");r.muted=!0,r.preload="metadata",r.playsInline=!0;try{await y(r,"loadedmetadata",e,()=>{r.src=i,r.load()});const a=r.videoWidth,n=r.videoHeight;if(a<=0||n<=0)throw new Error("视频预览准备失败:视频尺寸无效");const o=Number.isFinite(r.duration)&&r.duration>0?r.duration:void 0,s=void 0===t.atTime?void 0===o?0:Math.min(1,o/2):Math.max(0,Math.min(t.atTime,o??t.atTime));s>0&&await y(r,"seeked",e,()=>{r.currentTime=s}),await y(r,"loadeddata",e,()=>{r.preload="auto"});const h=void 0===o?void 0:o;let c,d;try{c=await g(r,a,n,t,e)}catch(t){if(e?.aborted||t instanceof RangeError)throw t}if(t.includeDisplayPoster)try{d=await g(r,a,n,{...t,maxSize:960,targetBytes:163840,maxBytes:262144,quality:.9,minQuality:.8},e)}catch(t){if(e?.aborted||t instanceof RangeError)throw t}return{width:a,height:n,...void 0===h?{}:{duration:h},...void 0===c?{}:{posterData:c},...void 0===d?{}:{displayPosterData:d}}}finally{r.pause(),r.removeAttribute("src"),r.load(),URL.revokeObjectURL(i)}}(t,e):await async function(t,e){s(e);const{maxSize:r,targetBytes:a,maxBytes:n}=u(t),o=await i(t.file);function h(e,i){return"image/jpeg"===t.file.type&&t.file.size<=a&&Math.max(e,i)<=Math.min(r,480)}async function d(i,a){const o="image/jpeg"===t.file.type&&Math.max(a.width,a.height)<=r&&t.file.size<=n;try{const e=await i;if(!o||t.file.size>e.byteLength)return e}catch(t){if(!(o&&t instanceof w))throw t}return s(e),await t.file.arrayBuffer()}if(s(e),void 0!==o){if(!1===t.includePoster)return o;if(h(o.width,o.height))return{...o,posterData:await t.file.arrayBuffer()}}const l=void 0===o?void 0:c(o.width,o.height,r);let f;try{f=void 0===l?await createImageBitmap(t.file):await createImageBitmap(t.file,{resizeWidth:l.width,resizeHeight:l.height,resizeQuality:"high"})}catch(t){if(e?.aborted)throw t}if(void 0!==f)try{s(e);const i=o??{width:f.width,height:f.height};return!1===t.includePoster?i:void 0===o&&h(f.width,f.height)?{...i,posterData:await t.file.arrayBuffer()}:{...i,posterData:await d(g(f,f.width,f.height,t,e),i)}}finally{f.close()}const m=URL.createObjectURL(t.file),y=new Image;try{await new Promise((t,i)=>{let r,a;const n=()=>{void 0!==r&&clearTimeout(r),y.onload=null,y.onerror=null,e?.removeEventListener("abort",a)};a=()=>{n(),i(new DOMException("媒体预览准备已取消","AbortError"))},y.onload=()=>{n(),t()},y.onerror=()=>{n(),i(new Error("图片预览准备失败:Image 解码错误"))},r=setTimeout(()=>{n(),i(new Error("图片预览准备失败:Image 解码超时"))},15e3),e?.addEventListener("abort",a,{once:!0}),y.src=m});const i=o??{width:y.naturalWidth,height:y.naturalHeight};return!1===t.includePoster?i:h(y.naturalWidth,y.naturalHeight)?{...i,posterData:await t.file.arrayBuffer()}:{...i,posterData:await d(g(y,y.naturalWidth,y.naturalHeight,t,e),i)}}finally{y.removeAttribute("src"),URL.revokeObjectURL(m)}}(t,e);return s(e),r}finally{h()}},dispose(){n||(n=!0,r.dispose(),a.dispose())}}}export{v 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{resolveMediaThumbnailPolicy as a}from"../../../im-core/src/media-thumbnail-policy.js";const n=a();function o(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 s(t){if(t?.aborted)throw new DOMException("媒体预览准备已取消","AbortError")}const h=16777216;function c(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>h){const t=Math.sqrt(h/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 d(t,e){s(e);const i=await new Promise(e=>t.toBlob(e,"image/png"));if(s(e),null===i)throw new Error("媒体预览准备失败:Canvas 编码返回空结果");return await r(await i.arrayBuffer(),e)}async function l(t,e,i){s(i);const r=await new Promise(i=>t.toBlob(i,"image/jpeg",e));if(s(i),null===r)throw new Error("媒体预览准备失败:Canvas 编码返回空结果");return await r.arrayBuffer()}function f(t,e){if(!Number.isInteger(t)||t<=0)throw new RangeError(`${e}必须是正整数`)}function m(t,e){if(!Number.isFinite(t)||t<=0||t>1)throw new RangeError(`${e}必须大于 0 且不超过 1`)}function u(t){const e="video"===t.kind?"视频封面":"图片缩略图",i=t.maxSize??n.maxSize,r=t.maxBytes??n.maxBytes,a=t.targetBytes??Math.min(n.targetBytes,r),o=t.quality??Math.max(.72,t.minQuality??0),s=t.minQuality??Math.min(o,.55);if(f(i,`${e}最大边长`),f(a,`${e}目标字节数`),f(r,`${e}最大字节数`),m(o,`${e}初始质量`),m(s,`${e}最低质量`),a>r)throw new RangeError(`${e}目标字节数不能超过最大字节数`);if(s>o)throw new RangeError(`${e}最低质量不能高于初始质量`);return{label:e,maxSize:i,targetBytes:a,maxBytes:r,initialQuality:o,minQuality:s}}class g extends Error{constructor(){super(...arguments),e(this,"name","ThumbnailByteLimitError")}}async function w(t,e,i,r,a){const{label:n,maxSize:o,targetBytes:h,maxBytes:f,initialQuality:m,minQuality:w}=u(r),y="video"===r.kind;s(a);const v=document.createElement("canvas");try{const u=v.getContext("2d");if(null===u)throw new Error("媒体预览准备失败:无法创建 Canvas 2D 上下文");let b;const M=e=>{s(a),v.width=e.width,v.height=e.height,void 0!==b&&(u.fillStyle=b,u.fillRect(0,0,e.width,e.height)),u.drawImage(t,0,0,e.width,e.height)},p=c(e,i,o);let x=p;const E=Math.min(o<=640?240:320,Math.max(x.width,x.height));M(x);const L=y?void 0:u.getImageData(0,0,x.width,x.height).data;let B,P,R=!1;if(void 0!==L)for(let t=3;t<L.length;t+=4)if(L[t]<255){R=!0;break}if(R){let t;for(let r=0;r<4;r+=1){const n=await d(v,a);if((void 0===t||n.byteLength<t.byteLength)&&(t=n),n.byteLength<=f)return n;const o=Math.max(x.width,x.height);if(o<=E||3===r)break;const s=Math.min(.8,Math.max(.35,.9*Math.sqrt(f/n.byteLength)));x=c(e,i,Math.max(E,Math.floor(o*s))),M(x)}if(void 0!==t&&t.byteLength<=f)return t;b=function(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)}`}(L,p.width),x=p,M(x)}const j=[.../* @__PURE__ */new Set([m,Math.max(w,m-.12),w])];for(const t of j){const e=await l(v,t,a);if((void 0===B||e.byteLength<B.byteLength)&&(B=e),void 0===P&&e.byteLength<=f&&(P=e),e.byteLength<=h)return e}if(void 0!==P)return P;for(let t=0;t<3;t+=1){const t=Math.max(x.width,x.height);if(t<=E||void 0===B)break;const r=Math.sqrt(f/B.byteLength);x=c(e,i,Math.max(E,Math.floor(t*Math.min(.9,Math.max(.5,r))))),M(x);for(const t of/* @__PURE__ */new Set([m,w])){const e=await l(v,t,a);if(e.byteLength<B.byteLength&&(B=e),void 0===P&&e.byteLength<=f&&(P=e),e.byteLength<=h)return e}if(void 0!==P)return P}if(void 0!==B&&B.byteLength<=f)return B;const D=Math.min(o<=640?160:240,Math.max(x.width,x.height)),A=r.minQuality??Math.min(m,.45);for(let t=0;t<2&&void 0!==B;t+=1){const r=Math.max(x.width,x.height),n=1===t?D:Math.max(D,Math.floor(r*Math.min(.9,.95*Math.sqrt(f/B.byteLength))));x=c(e,i,n),M(x);for(const t of/* @__PURE__ */new Set([w,A])){const e=await l(v,t,a);if(e.byteLength<B.byteLength&&(B=e),e.byteLength<=f)return e}if(n<=D)break}throw new g(`${n}生成失败:编码结果超过 ${f} 字节硬上限`)}finally{v.width=0,v.height=0}}function y(t,e,i,r){return s(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 v(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=o(e),a=o(1);let n=!1;return{async prepare(t,e){if(n)throw new Error("媒体预览执行器已释放");const o="video"===t.kind,h=await(o?a:r).acquire(e);try{const r=o?await async function(t,e){const i=URL.createObjectURL(t.file),r=document.createElement("video");r.muted=!0,r.preload="metadata",r.playsInline=!0;try{await y(r,"loadedmetadata",e,()=>{r.src=i,r.load()});const a=r.videoWidth,n=r.videoHeight;if(a<=0||n<=0)throw new Error("视频预览准备失败:视频尺寸无效");const o=Number.isFinite(r.duration)&&r.duration>0?r.duration:void 0,s=void 0===t.atTime?void 0===o?0:Math.min(1,o/2):Math.max(0,Math.min(t.atTime,o??t.atTime));s>0&&await y(r,"seeked",e,()=>{r.currentTime=s}),await y(r,"loadeddata",e,()=>{r.preload="auto"});const h=void 0===o?void 0:o;let c,d;try{c=await w(r,a,n,t,e)}catch(t){if(e?.aborted||t instanceof RangeError)throw t}if(t.includeDisplayPoster)try{d=await w(r,a,n,{...t,maxSize:960,targetBytes:163840,maxBytes:262144,quality:.9,minQuality:.8},e)}catch(t){if(e?.aborted||t instanceof RangeError)throw t}return{width:a,height:n,...void 0===h?{}:{duration:h},...void 0===c?{}:{posterData:c},...void 0===d?{}:{displayPosterData:d}}}finally{r.pause(),r.removeAttribute("src"),r.load(),URL.revokeObjectURL(i)}}(t,e):await async function(t,e){s(e);const{maxSize:r,targetBytes:a,maxBytes:n}=u(t),o=await i(t.file);function h(e,i){return"image/jpeg"===t.file.type&&t.file.size<=a&&Math.max(e,i)<=r}async function d(i,a){const o="image/jpeg"===t.file.type&&Math.max(a.width,a.height)<=r&&t.file.size<=n;try{const e=await i;if(!o||t.file.size>e.byteLength)return e}catch(t){if(!(o&&t instanceof g))throw t}return s(e),await t.file.arrayBuffer()}if(s(e),void 0!==o){if(!1===t.includePoster)return o;if(h(o.width,o.height))return{...o,posterData:await t.file.arrayBuffer()}}const l=void 0===o?void 0:c(o.width,o.height,r);let f;try{f=void 0===l?await createImageBitmap(t.file):await createImageBitmap(t.file,{resizeWidth:l.width,resizeHeight:l.height,resizeQuality:"high"})}catch(t){if(e?.aborted)throw t}if(void 0!==f)try{s(e);const i=o??{width:f.width,height:f.height};return!1===t.includePoster?i:void 0===o&&h(f.width,f.height)?{...i,posterData:await t.file.arrayBuffer()}:{...i,posterData:await d(w(f,f.width,f.height,t,e),i)}}finally{f.close()}const m=URL.createObjectURL(t.file),y=new Image;try{await new Promise((t,i)=>{let r,a;const n=()=>{void 0!==r&&clearTimeout(r),y.onload=null,y.onerror=null,e?.removeEventListener("abort",a)};a=()=>{n(),i(new DOMException("媒体预览准备已取消","AbortError"))},y.onload=()=>{n(),t()},y.onerror=()=>{n(),i(new Error("图片预览准备失败:Image 解码错误"))},r=setTimeout(()=>{n(),i(new Error("图片预览准备失败:Image 解码超时"))},15e3),e?.addEventListener("abort",a,{once:!0}),y.src=m});const i=o??{width:y.naturalWidth,height:y.naturalHeight};return!1===t.includePoster?i:h(y.naturalWidth,y.naturalHeight)?{...i,posterData:await t.file.arrayBuffer()}:{...i,posterData:await d(w(y,y.naturalWidth,y.naturalHeight,t,e),i)}}finally{y.removeAttribute("src"),URL.revokeObjectURL(m)}}(t,e);return s(e),r}finally{h()}},dispose(){n||(n=!0,r.dispose(),a.dispose())}}}export{v as createMediaPreviewPreparationExecutor};
@@ -1 +1 @@
1
- import{MEDIA_ALBUM_MAX_ITEMS as t,MEDIA_ALBUM_CONTENT_MAX_BYTES as o}from"./mapping/media-album-body.js";const r=Math.floor(3*(o-32768)/4);function a(o=1){if(!Number.isInteger(o)||o<1||o>t)throw new RangeError(`媒体缩略图项数必须是 1 到 ${t} 的整数`);const r=1===o?1:o+1;return{maxSize:o<=2?480:320,targetBytes:Math.min(8192,3*Math.floor(49152/r/3)),maxBytes:Math.min(o<=2?24576:16384,3*Math.floor(73728/r/3))}}export{r as MEDIA_THUMBNAIL_MAX_BYTES,a as resolveMediaThumbnailPolicy};
1
+ import{MEDIA_ALBUM_MAX_ITEMS as t,MEDIA_ALBUM_CONTENT_MAX_BYTES as o}from"./mapping/media-album-body.js";const r=Math.floor(3*(o-32768)/4);function a(o=1){if(!Number.isInteger(o)||o<1||o>t)throw new RangeError(`媒体缩略图项数必须是 1 到 ${t} 的整数`);const r=1===o?1:o+1;return{maxSize:o<=2?960:640,targetBytes:Math.min(8192,3*Math.floor(49152/r/3)),maxBytes:Math.min(o<=2?24576:16384,3*Math.floor(73728/r/3))}}export{r as MEDIA_THUMBNAIL_MAX_BYTES,a as resolveMediaThumbnailPolicy};
@@ -1 +1 @@
1
- function e(e,t){return{prepareLocalMediaPreview:async i=>{const a=await e.prepare({operationId:i.operationId,generation:t(),kind:i.kind,file:i.file,includePoster:"video"===i.kind,..."video"===i.kind&&i.includeDisplayPoster?{includeDisplayPoster:!0}:{}},i.signal);return"video"===i.kind?{width:a.width,height:a.height,...void 0===a.duration?{}:{duration:a.duration},...void 0===a.posterData?{}:{posterData:new Uint8Array(a.posterData)},...void 0===a.displayPosterData?{}:{displayPosterData:new Uint8Array(a.displayPosterData)}}:{width:a.width,height:a.height}},prepareLocalVideoPoster:async i=>{const a=await e.prepare({operationId:i.operationId,generation:t(),kind:"video",file:i.file,includePoster:!0,maxSize:960,targetBytes:163840,maxBytes:262144,quality:.9,minQuality:.8},i.signal);return{width:a.width,height:a.height,...void 0===a.posterData?{}:{posterData:new Uint8Array(a.posterData)}}},prepareLocalImageThumbnail:async i=>{const a=await e.prepare({operationId:i.operationId,generation:t(),kind:"image",file:i.file,includePoster:!0,maxSize:256,targetBytes:262144,maxBytes:262144,quality:.9,minQuality:.9},i.signal);return{width:a.width,height:a.height,...void 0===a.posterData?{}:{posterData:new Uint8Array(a.posterData)}}}}}export{e as createLocalMediaPreviewPreparation};
1
+ function e(e,t){return{prepareLocalMediaPreview:async i=>{const a=await e.prepare({operationId:i.operationId,generation:t(),kind:i.kind,file:i.file,includePoster:"video"===i.kind,..."video"===i.kind&&i.includeDisplayPoster?{includeDisplayPoster:!0}:{}},i.signal);return"video"===i.kind?{width:a.width,height:a.height,...void 0===a.duration?{}:{duration:a.duration},...void 0===a.posterData?{}:{posterData:new Uint8Array(a.posterData)},...void 0===a.displayPosterData?{}:{displayPosterData:new Uint8Array(a.displayPosterData)}}:{width:a.width,height:a.height}},prepareLocalVideoPoster:async i=>{const a=await e.prepare({operationId:i.operationId,generation:t(),kind:"video",file:i.file,includePoster:!0,maxSize:960,targetBytes:163840,maxBytes:262144,quality:.9,minQuality:.8},i.signal);return{width:a.width,height:a.height,...void 0===a.posterData?{}:{posterData:new Uint8Array(a.posterData)}}},prepareLocalImageThumbnail:async i=>{const a=await e.prepare({operationId:i.operationId,generation:t(),kind:"image",file:i.file,includePoster:!0,maxSize:1440,targetBytes:524288,maxBytes:524288,quality:.9,minQuality:.9},i.signal);return{width:a.width,height:a.height,...void 0===a.posterData?{}:{posterData:new Uint8Array(a.posterData)}}}}}export{e as createLocalMediaPreviewPreparation};
package/dist/sdk.d.ts CHANGED
@@ -5269,7 +5269,7 @@ interface ImMediaTasksApi {
5269
5269
  *
5270
5270
  * 返回的对象可以**原样**作为 `localPreviewMetadata` 交给 `sendImage` / `sendVideo` /
5271
5271
  * `sendMediaAlbum`:发送链路会复用可用元信息;合集项超出其可用消息空间时重新生成封面。
5272
- * posterData 使用共享自适应发送规格(视频 JPEG,长边最多 480px,目标 8 KiB、硬上限 24 KiB);includeDisplayPoster 可在同一次解码中额外生成
5272
+ * posterData 使用共享自适应发送规格(视频 JPEG,长边最多 960px,目标 8 KiB、硬上限 24 KiB);includeDisplayPoster 可在同一次解码中额外生成
5273
5273
  * 仅供页面显示的高清 displayPoster(长边最多 960),不得绕过发送校验直接用它替换 posterData。
5274
5274
  *
5275
5275
  * @param input.kind 按文件类型选解码路径;`'video'` 才会抽首帧与读时长。
@@ -5303,7 +5303,8 @@ interface ImMediaTasksApi {
5303
5303
  }) => Promise<Blob | null>;
5304
5304
  /**
5305
5305
  * 为本地图片生成仅供胶片条/相册格显示的缩略图,不上传、不写缓存、不改变发送文件与元信息。
5306
- * JPEG 原文件按签名识别并生成 JPEG 显示缩略图,最长边最多 256px 且不放大,与发送预读共用账号的图片解码预算。
5306
+ * JPEG 原文件按签名识别并生成显示图,最长边最多 1440px、不放大,质量 0.9、最多 512 KiB。
5307
+ * 显示图与发送缩略图使用独立字节预算,解码仍共用账号的并发限制。
5307
5308
  * 其它格式直接返回原 Blob,保留透明与动画;宿主负责返回 Blob 的 object URL 生命周期。
5308
5309
  * 生成失败返回 null 并记录诊断;取消、账号失效和未装配能力保持明确错误。
5309
5310
  * @param input.file 原始本地图片;返回的显示资源不得代替它传入发送命令。
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "k2-im",
3
3
  "type": "module",
4
- "version": "0.2.14",
4
+ "version": "0.2.15",
5
5
  "browserslist": {
6
6
  "js": [
7
7
  "chrome >= 93",