karin-plugin-kkk 2.24.0 → 2.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [2.25.1](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.25.0...v2.25.1) (2026-03-11)
6
+
7
+
8
+ ### 🧰 其他更新
9
+
10
+ * 注释 ([d78adef](https://github.com/ikenxuan/karin-plugin-kkk/commit/d78adef61be62a7486a84a4be18e8b3f40ae1b7f))
11
+
12
+
13
+ ### ⚙️ 配置变更
14
+
15
+ * 添加 Live Photo 静态图兼容系统配置 ([a8c5137](https://github.com/ikenxuan/karin-plugin-kkk/commit/a8c51374a697c111a0813e9f0eb939cdb9285597))
16
+
17
+ ## [2.25.0](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.24.0...v2.25.0) (2026-03-11)
18
+
19
+
20
+ ### ✨ 新功能
21
+
22
+ * **core:** 解析动图作品时,实验性将合并转发消息中的静态图片魔改成 Google Motion 标准的 Live Photo ([5e789e3](https://github.com/ikenxuan/karin-plugin-kkk/commit/5e789e351eebfaca84e26c326ddf7c16e253a20f))
23
+
24
+
25
+ ### 🐛 错误修复
26
+
27
+ * 取消注释 ([921bf95](https://github.com/ikenxuan/karin-plugin-kkk/commit/921bf95ad05d8ac17a2540ac9aa3014beed3b6e5))
28
+
5
29
  ## [2.24.0](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.23.2...v2.24.0) (2026-03-10)
6
30
 
7
31
 
@@ -47,6 +47,10 @@ multiPageRender: true
47
47
  # 分页渲染时,每页的高度,经测试最佳每页高度为12000px,默认12000px
48
48
  multiPageHeight: 12000
49
49
 
50
+ # 当解析到作品/动态包含 Live Photo 时,合并转发消息里发送的 Live Photo 静态图兼容系统
51
+ # 可选值:'google'、'xiaomi'、'oppo'、'huawei_honor'
52
+ livePhotoSystem: google
53
+
50
54
  # 扫码登录时使用的地址类型,可选值:'lan'(局域网IP)、'external'(外部地址)
51
55
  qrLoginAddrType: lan
52
56
 
package/lib/apps/admin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { C as biLogin, E as task, T as removeOldFiles, w as dylogin } from "../core_chunk/main-1eljaHiz.js";
2
+ import { C as biLogin, E as task, T as removeOldFiles, w as dylogin } from "../core_chunk/main-BY6eDfoV.js";
3
3
  import "../core_chunk/vendor-DxfKHvj-.js";
4
4
  import "../core_chunk/template-DekmxKd7.js";
5
5
  export { biLogin, dylogin, removeOldFiles, task };
package/lib/apps/help.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { S as version, x as help } from "../core_chunk/main-1eljaHiz.js";
2
+ import { S as version, x as help } from "../core_chunk/main-BY6eDfoV.js";
3
3
  import "../core_chunk/vendor-DxfKHvj-.js";
4
4
  import "../core_chunk/template-DekmxKd7.js";
5
5
  export { help, version };
package/lib/apps/push.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { _ as forcePush, b as testDouyinPush, f as bilibiliPush, g as douyinPushList, h as douyinPush, m as changeBotID, p as bilibiliPushList, v as setbiliPush, y as setdyPush } from "../core_chunk/main-1eljaHiz.js";
2
+ import { _ as forcePush, b as testDouyinPush, f as bilibiliPush, g as douyinPushList, h as douyinPush, m as changeBotID, p as bilibiliPushList, v as setbiliPush, y as setdyPush } from "../core_chunk/main-BY6eDfoV.js";
3
3
  import "../core_chunk/vendor-DxfKHvj-.js";
4
4
  import "../core_chunk/template-DekmxKd7.js";
5
5
  export { bilibiliPush, bilibiliPushList, changeBotID, douyinPush, douyinPushList, forcePush, setbiliPush, setdyPush, testDouyinPush };
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { d as qrLogin } from "../core_chunk/main-1eljaHiz.js";
2
+ import { d as qrLogin } from "../core_chunk/main-BY6eDfoV.js";
3
3
  import "../core_chunk/vendor-DxfKHvj-.js";
4
4
  import "../core_chunk/template-DekmxKd7.js";
5
5
  export { qrLogin };
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { l as globalStatistics, u as groupStatistics } from "../core_chunk/main-1eljaHiz.js";
2
+ import { l as globalStatistics, u as groupStatistics } from "../core_chunk/main-BY6eDfoV.js";
3
3
  import "../core_chunk/vendor-DxfKHvj-.js";
4
4
  import "../core_chunk/template-DekmxKd7.js";
5
5
  export { globalStatistics, groupStatistics };
package/lib/apps/tools.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { a as douyinAPP, c as xiaohongshuAPP, i as bilibiliAPP, o as kuaishouAPP, s as prefix } from "../core_chunk/main-1eljaHiz.js";
2
+ import { a as douyinAPP, c as xiaohongshuAPP, i as bilibiliAPP, o as kuaishouAPP, s as prefix } from "../core_chunk/main-BY6eDfoV.js";
3
3
  import "../core_chunk/vendor-DxfKHvj-.js";
4
4
  import "../core_chunk/template-DekmxKd7.js";
5
5
  export { bilibiliAPP, douyinAPP, kuaishouAPP, prefix, xiaohongshuAPP };
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { n as kkkUpdateCommand, r as update, t as kkkUpdate } from "../core_chunk/main-1eljaHiz.js";
2
+ import { n as kkkUpdateCommand, r as update, t as kkkUpdate } from "../core_chunk/main-BY6eDfoV.js";
3
3
  import "../core_chunk/vendor-DxfKHvj-.js";
4
4
  import "../core_chunk/template-DekmxKd7.js";
5
5
  export { kkkUpdate, kkkUpdateCommand, update };
@@ -1,10 +1,10 @@
1
1
  {
2
- "version": "2.24.0",
3
- "buildTime": "2026-03-10T21:34:33.429Z",
4
- "buildTimestamp": 1773178473429,
2
+ "version": "2.25.1",
3
+ "buildTime": "2026-03-11T02:39:23.980Z",
4
+ "buildTimestamp": 1773196763981,
5
5
  "name": "karin-plugin-kkk",
6
6
  "description": "Karin 的「抖音」「B 站」视频解析/动态推送插件",
7
7
  "homepage": "https://github.com/ikenxuan/karin-plugin-kkk",
8
- "commitHash": "2d382ab276694e9bed2c1b785e937e32934380eb",
9
- "shortCommitHash": "2d382ab2"
8
+ "commitHash": "81f623e7cf8c1ee0af14ba3c417709705d54512c",
9
+ "shortCommitHash": "81f623e7"
10
10
  }
@@ -7111,6 +7111,124 @@ var init_EmojiReaction = __esmMin(() => {
7111
7111
  }
7112
7112
  };
7113
7113
  });
7114
+ var xmpHeaderBuffer, oppoExifHex, xiaomiExifHex, huaweiHonorLiveIdFallback, isJpegBuffer, getJpegDimensions, hasExifApp1, buildExifSegment, getSystemExifHex, resolveMotionPhotoSystem, buildMotionPhotoXmp, injectXmpToJpeg, readOrConvertToJpeg, buildGoogleMotionPhoto;
7115
+ var init_MotionPhoto = __esmMin(async () => {
7116
+ await init_Common();
7117
+ await init_Config();
7118
+ xmpHeaderBuffer = Buffer.from("http://ns.adobe.com/xap/1.0/\0", "utf8");
7119
+ oppoExifHex = "FFE100724578696600004D4D002A0000000800040100000400000001000005A001010004000000010000043C87690004000000010000003E011200030000000100000000000000000002928600020000000E0000005C920800040000000100000000000000006F706C75735F3833383836303800";
7120
+ xiaomiExifHex = "FFE1007E4578696600004D4D002A0000000800040100000400000001000005A001010004000000010000043C01120003000000010000000087690004000000010000003E000000000003889700010000000101000000920800040000000100000000928600020000000E00000068000000006F706C75735F3833383836303800";
7121
+ huaweiHonorLiveIdFallback = 1915884;
7122
+ isJpegBuffer = (fileBuffer) => fileBuffer.length > 2 && fileBuffer[0] === 255 && fileBuffer[1] === 216;
7123
+ getJpegDimensions = (jpegBuffer) => {
7124
+ let offset = 2;
7125
+ while (offset + 9 < jpegBuffer.length) {
7126
+ if (jpegBuffer[offset] !== 255) {
7127
+ offset += 1;
7128
+ continue;
7129
+ }
7130
+ const marker = jpegBuffer[offset + 1];
7131
+ if (marker === 216 || marker === 217 || marker === 1 || marker >= 208 && marker <= 215) {
7132
+ offset += 2;
7133
+ continue;
7134
+ }
7135
+ if (offset + 3 >= jpegBuffer.length) return null;
7136
+ const segmentLength = jpegBuffer.readUInt16BE(offset + 2);
7137
+ if (segmentLength < 2 || offset + 2 + segmentLength > jpegBuffer.length) return null;
7138
+ if ((marker >= 192 && marker <= 195 || marker >= 197 && marker <= 199 || marker >= 201 && marker <= 203 || marker >= 205 && marker <= 207) && segmentLength >= 7) {
7139
+ const height = jpegBuffer.readUInt16BE(offset + 5);
7140
+ const width = jpegBuffer.readUInt16BE(offset + 7);
7141
+ if (width > 0 && height > 0) return {
7142
+ width,
7143
+ height
7144
+ };
7145
+ return null;
7146
+ }
7147
+ offset += 2 + segmentLength;
7148
+ }
7149
+ return null;
7150
+ };
7151
+ hasExifApp1 = (jpegBuffer) => jpegBuffer.includes(Buffer.from("Exif\0\0", "binary"));
7152
+ buildExifSegment = (hex, width, height) => {
7153
+ const exifBuffer = Buffer.from(hex, "hex");
7154
+ exifBuffer[28] = width >> 24 & 255;
7155
+ exifBuffer[29] = width >> 16 & 255;
7156
+ exifBuffer[30] = width >> 8 & 255;
7157
+ exifBuffer[31] = width & 255;
7158
+ exifBuffer[40] = height >> 24 & 255;
7159
+ exifBuffer[41] = height >> 16 & 255;
7160
+ exifBuffer[42] = height >> 8 & 255;
7161
+ exifBuffer[43] = height & 255;
7162
+ return exifBuffer;
7163
+ };
7164
+ getSystemExifHex = (system) => {
7165
+ if (system === "oppo") return oppoExifHex;
7166
+ if (system === "xiaomi") return xiaomiExifHex;
7167
+ return null;
7168
+ };
7169
+ resolveMotionPhotoSystem = () => {
7170
+ const system = Config.app.livePhotoSystem;
7171
+ if (system === "google" || system === "xiaomi" || system === "oppo" || system === "huawei_honor") return system;
7172
+ return "google";
7173
+ };
7174
+ buildMotionPhotoXmp = (videoLength, presentationTimestampUs, system) => {
7175
+ if (system === "oppo") return `<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.0-jc003"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:hdrgm="http://ns.adobe.com/hdr-gain-map/1.0/" xmlns:GCamera="http://ns.google.com/photos/1.0/camera/" xmlns:OpCamera="http://ns.oplus.com/photos/1.0/camera/" xmlns:Container="http://ns.google.com/photos/1.0/container/" xmlns:Item="http://ns.google.com/photos/1.0/container/item/" hdrgm:Version="1.0" GCamera:MotionPhoto="1" GCamera:MotionPhotoVersion="1" GCamera:MotionPhotoPresentationTimestampUs="${presentationTimestampUs}" OpCamera:MotionPhotoPrimaryPresentationTimestampUs="${presentationTimestampUs}" OpCamera:MotionPhotoOwner="oplus" OpCamera:OLivePhotoVersion="2" OpCamera:VideoLength="${videoLength}"><Container:Directory><rdf:Seq><rdf:li rdf:parseType="Resource"><Container:Item Item:Mime="image/jpeg" Item:Semantic="Primary" Item:Length="0" Item:Padding="0" /></rdf:li><rdf:li rdf:parseType="Resource"><Container:Item Item:Mime="video/mp4" Item:Semantic="MotionPhoto" Item:Length="${videoLength}" /></rdf:li></rdf:Seq></Container:Directory></rdf:Description></rdf:RDF></x:xmpmeta>`;
7176
+ if (system === "xiaomi") return `<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.0-jc003"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:GCamera="http://ns.google.com/photos/1.0/camera/" xmlns:MiCamera="http://ns.xiaomi.com/photos/1.0/camera/" xmlns:Container="http://ns.google.com/photos/1.0/container/" xmlns:Item="http://ns.google.com/photos/1.0/container/item/" GCamera:MotionPhoto="1" GCamera:MotionPhotoVersion="1" GCamera:MotionPhotoPresentationTimestampUs="${presentationTimestampUs}" GCamera:MicroVideo="1" GCamera:MicroVideoVersion="1" GCamera:MicroVideoOffset="${videoLength}" GCamera:MicroVideoPresentationTimestampUs="${presentationTimestampUs}" MiCamera:XMPMeta="&lt;?xml version=&apos;1.0&apos; encoding=&apos;UTF-8&apos; standalone=&apos;yes&apos; ?&gt;"><Container:Directory><rdf:Seq><rdf:li rdf:parseType="Resource"><Container:Item Item:Mime="image/jpeg" Item:Semantic="Primary" Item:Length="0" Item:Padding="0" /></rdf:li><rdf:li rdf:parseType="Resource"><Container:Item Item:Mime="video/mp4" Item:Semantic="MotionPhoto" Item:Length="${videoLength}" Item:Padding="0" /></rdf:li></rdf:Seq></Container:Directory></rdf:Description></rdf:RDF></x:xmpmeta>`;
7177
+ return `<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.0-jc003"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:GCamera="http://ns.google.com/photos/1.0/camera/" xmlns:Container="http://ns.google.com/photos/1.0/container/" xmlns:Item="http://ns.google.com/photos/1.0/container/item/" GCamera:MotionPhoto="1" GCamera:MotionPhotoVersion="1" GCamera:MotionPhotoPresentationTimestampUs="${presentationTimestampUs}"><Container:Directory><rdf:Seq><rdf:li rdf:parseType="Resource"><Container:Item Item:Mime="image/jpeg" Item:Semantic="Primary" Item:Length="0" Item:Padding="0" /></rdf:li><rdf:li rdf:parseType="Resource"><Container:Item Item:Mime="video/mp4" Item:Semantic="MotionPhoto" Item:Length="${videoLength}" Item:Padding="0" /></rdf:li></rdf:Seq></Container:Directory></rdf:Description></rdf:RDF></x:xmpmeta>`;
7178
+ };
7179
+ injectXmpToJpeg = (jpegBuffer, xmpPacket, system) => {
7180
+ if (!isJpegBuffer(jpegBuffer)) throw new Error("输入图片不是 JPEG 格式");
7181
+ const xmpPayload = Buffer.concat([xmpHeaderBuffer, Buffer.from(xmpPacket, "utf8")]);
7182
+ const app1Length = xmpPayload.length + 2;
7183
+ if (app1Length > 65535) throw new Error("XMP 数据过大,无法写入 JPEG APP1");
7184
+ const app1Segment = Buffer.alloc(4);
7185
+ app1Segment[0] = 255;
7186
+ app1Segment[1] = 225;
7187
+ app1Segment.writeUInt16BE(app1Length, 2);
7188
+ const dimensions = getJpegDimensions(jpegBuffer);
7189
+ const exifHex = getSystemExifHex(system);
7190
+ const exifSegment = !hasExifApp1(jpegBuffer) && dimensions !== null && exifHex !== null ? buildExifSegment(exifHex, dimensions.width, dimensions.height) : null;
7191
+ return Buffer.concat([
7192
+ jpegBuffer.subarray(0, 2),
7193
+ ...exifSegment ? [exifSegment] : [],
7194
+ app1Segment,
7195
+ xmpPayload,
7196
+ jpegBuffer.subarray(2)
7197
+ ]);
7198
+ };
7199
+ readOrConvertToJpeg = async (imagePath) => {
7200
+ const sourceBuffer = fs.readFileSync(imagePath);
7201
+ if (isJpegBuffer(sourceBuffer)) return sourceBuffer;
7202
+ const tempJpegPath = path.join(Common.tempDri.images, `MotionPhoto_${Date.now()}_${Math.random().toString(36).slice(2)}.jpg`);
7203
+ if (!(await ffmpeg(`-y -i "${imagePath}" -frames:v 1 -q:v 2 "${tempJpegPath}"`)).status) throw new Error(`图片转换 JPEG 失败: ${imagePath}`);
7204
+ try {
7205
+ return fs.readFileSync(tempJpegPath);
7206
+ } finally {
7207
+ fs.rmSync(tempJpegPath, { force: true });
7208
+ }
7209
+ };
7210
+ buildGoogleMotionPhoto = async (options) => {
7211
+ const { imagePath, videoPath, outputPath, presentationTimestampUs } = options;
7212
+ try {
7213
+ const system = resolveMotionPhotoSystem();
7214
+ const imageBuffer = await readOrConvertToJpeg(imagePath);
7215
+ const videoBuffer = fs.readFileSync(videoPath);
7216
+ const resolvedPresentationTimestampUs = presentationTimestampUs === void 0 || presentationTimestampUs < 0 ? 0 : presentationTimestampUs;
7217
+ const huaweiHonorFooter = Buffer.from(`v2_f35 409:1000 LIVE_${resolvedPresentationTimestampUs > 0 ? Math.floor(resolvedPresentationTimestampUs) : huaweiHonorLiveIdFallback}`, "utf8");
7218
+ const outputBuffer = system === "huawei_honor" ? Buffer.concat([imageBuffer, huaweiHonorFooter]) : (() => {
7219
+ const jpegWithXmp = injectXmpToJpeg(imageBuffer, buildMotionPhotoXmp(videoBuffer.length, resolvedPresentationTimestampUs, system), system);
7220
+ return Buffer.concat([jpegWithXmp, videoBuffer]);
7221
+ })();
7222
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
7223
+ fs.writeFileSync(outputPath, outputBuffer);
7224
+ logger.debug(`Motion Photo 封面生成成功(${system}): ${outputPath}`);
7225
+ return true;
7226
+ } catch (error) {
7227
+ logger.error("Motion Photo 封面生成失败", error);
7228
+ return false;
7229
+ }
7230
+ };
7231
+ });
7114
7232
  async function fixM4sFile(inputPath, outputPath) {
7115
7233
  const result = await ffmpeg(`-y -i "${inputPath}" -c copy -movflags +faststart "${outputPath}"`);
7116
7234
  if (result.status) logger.debug(`m4s 文件修复成功: ${outputPath}`);
@@ -7123,7 +7241,7 @@ async function getMediaDuration(path$1) {
7123
7241
  }
7124
7242
  async function mergeVideoAudio(videoPath, audioPath, resultPath) {
7125
7243
  const result = await ffmpeg(`-y -i "${videoPath}" -i "${audioPath}" -c copy "${resultPath}"`);
7126
- if (result.status) logger.mark(`视频合成成功: ${resultPath}`);
7244
+ if (result.status) logger.debug(`视频合成成功: ${resultPath}`);
7127
7245
  else logger.error("视频合成失败", result);
7128
7246
  return result.status;
7129
7247
  }
@@ -7131,7 +7249,7 @@ async function compressVideo(options) {
7131
7249
  const { inputPath, outputPath, targetBitrate, maxRate = targetBitrate * 1.5, bufSize = targetBitrate * 2, crf = 35, removeSource = true } = options;
7132
7250
  const result = await ffmpeg(`-y -i "${inputPath}" -b:v ${targetBitrate}k -maxrate ${maxRate}k -bufsize ${bufSize}k -crf ${crf} -preset medium -c:v libx264 -vf "scale='if(gte(iw/ih,16/9),1280,-1)':'if(gte(iw/ih,16/9),-1,720)',scale=ceil(iw/2)*2:ceil(ih/2)*2" "${outputPath}"`);
7133
7251
  if (result.status) {
7134
- logger.mark(`视频压缩成功: ${outputPath}`);
7252
+ logger.debug(`视频压缩成功: ${outputPath}`);
7135
7253
  if (removeSource) Common.removeFile(inputPath);
7136
7254
  } else logger.error(`视频压缩失败: ${inputPath}`, result);
7137
7255
  return result.status;
@@ -7139,6 +7257,7 @@ async function compressVideo(options) {
7139
7257
  var getMediaFrameRate, loopVideoWithTransition;
7140
7258
  var init_FFmpeg = __esmMin(async () => {
7141
7259
  await init_utils$1();
7260
+ await init_MotionPhoto();
7142
7261
  getMediaFrameRate = async (path$1) => {
7143
7262
  const { stdout } = await ffprobe(`-v error -select_streams v:0 -show_entries stream=avg_frame_rate -of default=noprint_wrappers=1:nokey=1 "${path$1}"`);
7144
7263
  const rate = stdout.trim();
@@ -7214,7 +7333,7 @@ var init_FFmpeg = __esmMin(async () => {
7214
7333
  else bgmInputArgs = `-stream_loop ${Math.ceil(totalDuration / baseContext.bgmDuration) + 1} -ss ${bgmStartTime} -i "${bgmPath}"`;
7215
7334
  } else if (bgmNeedLoop) bgmInputArgs = `-stream_loop ${Math.max(0, Math.ceil(totalDuration / baseContext.bgmDuration) - 1)} -i "${bgmPath}"`;
7216
7335
  const result$1 = await ffmpeg(`-y ${inputArgs} ${bgmInputArgs} -filter_complex "${filterComplex};[0:a][${bgmInputIndex}:a]amix=inputs=2:duration=longest:dropout_transition=3[aout]" -map "[outv]" -map "[aout]" -c:v libx264 -c:a aac -b:a 192k -pix_fmt yuv420p -shortest "${outputPath}"`);
7217
- if (result$1.status) logger.mark(`Live Photo 效果视频重放成功: ${outputPath}`);
7336
+ if (result$1.status) logger.debug(`Live Photo 效果视频重放成功: ${outputPath}`);
7218
7337
  else logger.error("Live Photo 效果视频重放失败", result$1);
7219
7338
  if (mergeMode === "continuous") {
7220
7339
  const outputDuration = result$1.status ? await getMediaDuration(outputPath) : totalDuration;
@@ -7230,7 +7349,7 @@ var init_FFmpeg = __esmMin(async () => {
7230
7349
  };
7231
7350
  }
7232
7351
  const result = await ffmpeg(`-y ${inputArgs} -filter_complex "${filterComplex}" -map "[outv]" -c:v libx264 -pix_fmt yuv420p "${outputPath}"`);
7233
- if (result.status) logger.mark(`Live Photo 效果视频重放成功: ${outputPath}`);
7352
+ if (result.status) logger.debug(`Live Photo 效果视频重放成功: ${outputPath}`);
7234
7353
  else logger.error("Live Photo 效果视频重放失败", result);
7235
7354
  return { success: result.status };
7236
7355
  };
@@ -8158,6 +8277,7 @@ var init_utils$1 = __esmMin(async () => {
8158
8277
  init_EmojiReaction();
8159
8278
  init_FFmpeg();
8160
8279
  init_ImageHelper();
8280
+ init_MotionPhoto();
8161
8281
  init_Networks();
8162
8282
  init_QRCodeScanner();
8163
8283
  init_Render();
@@ -11051,6 +11171,35 @@ var init_app_schema = __esmMin(() => {
11051
11171
  error: "请输入一个范围在 1000 到 20000 之间的数字"
11052
11172
  }]
11053
11173
  },
11174
+ {
11175
+ type: "divider",
11176
+ title: "Live Photo 兼容设置"
11177
+ },
11178
+ {
11179
+ key: "livePhotoSystem",
11180
+ type: "radio",
11181
+ label: "Live Photo 静态图兼容系统",
11182
+ description: "当解析到作品/动态包含 Live Photo 时,合并转发里发送的 Live Photo 静态图按所选系统生成",
11183
+ orientation: "horizontal",
11184
+ options: [
11185
+ {
11186
+ label: "Google",
11187
+ value: "google"
11188
+ },
11189
+ {
11190
+ label: "小米(HyperOS)",
11191
+ value: "xiaomi"
11192
+ },
11193
+ {
11194
+ label: "OPPO(ColorOS)",
11195
+ value: "oppo"
11196
+ },
11197
+ {
11198
+ label: "华为/荣耀(HarmonyOS/MagicOS)",
11199
+ value: "huawei_honor"
11200
+ }
11201
+ ]
11202
+ },
11054
11203
  {
11055
11204
  type: "divider",
11056
11205
  title: "API服务配置"
@@ -14431,6 +14580,34 @@ const webConfig = defineConfig({
14431
14580
  error: "请输入一个范围在 1000 到 20000 之间的数字"
14432
14581
  }]
14433
14582
  }),
14583
+ components.divider.create("divider-app-live-photo", {
14584
+ description: "Live Photo 兼容设置",
14585
+ descPosition: 20
14586
+ }),
14587
+ components.radio.group("livePhotoSystem", {
14588
+ label: "Live Photo 静态图兼容系统",
14589
+ description: "当解析到作品/动态包含 Live Photo 时,合并转发里发送的 Live Photo 静态图按所选系统生成",
14590
+ orientation: "horizontal",
14591
+ defaultValue: all.app.livePhotoSystem || "google",
14592
+ radio: [
14593
+ components.radio.create("livePhotoSystem-google", {
14594
+ label: "Google",
14595
+ value: "google"
14596
+ }),
14597
+ components.radio.create("livePhotoSystem-xiaomi", {
14598
+ label: "小米(HyperOS)",
14599
+ value: "xiaomi"
14600
+ }),
14601
+ components.radio.create("livePhotoSystem-oppo", {
14602
+ label: "OPPO(ColorOS)",
14603
+ value: "oppo"
14604
+ }),
14605
+ components.radio.create("livePhotoSystem-huawei-honor", {
14606
+ label: "华为/荣耀(HarmonyOS/MagicOS)",
14607
+ value: "huawei_honor"
14608
+ })
14609
+ ]
14610
+ }),
14434
14611
  components.divider.create("divider-app-api", {
14435
14612
  description: "API服务配置",
14436
14613
  descPosition: 20
@@ -15792,6 +15969,10 @@ var Bilibili = class extends Base {
15792
15969
  headers: BASE_HEADERS,
15793
15970
  filepath: Common.tempDri.images + `Bilibili_static_${Date.now()}_${index}.jpg`
15794
15971
  });
15972
+ if (staticImg.filepath) temp.push({
15973
+ filepath: staticImg.filepath,
15974
+ totalBytes: 0
15975
+ });
15795
15976
  const loopCount = 3;
15796
15977
  if (!staticImg.filepath) {
15797
15978
  await Common.removeFile(livePhoto.filepath, true);
@@ -15808,16 +15989,35 @@ var Bilibili = class extends Base {
15808
15989
  const filePath = Common.tempDri.video + `tmp_${Date.now()}.mp4`;
15809
15990
  fs.renameSync(outputPath, filePath);
15810
15991
  logger.mark(`视频文件重命名完成: ${outputPath.split("/").pop()} -> ${filePath.split("/").pop()}`);
15811
- logger.mark("正在尝试删除缓存文件");
15812
- await Common.removeFile(livePhoto.filepath, true);
15813
15992
  temp.push({
15814
15993
  filepath: filePath,
15815
15994
  totalBytes: 0
15816
15995
  });
15817
15996
  const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
15818
15997
  imgArray.push(segment.video(videoPath));
15819
- const imageUrl = await processImageUrl(img$2.url, title, index);
15820
- imgArray.push(segment.image(imageUrl));
15998
+ let hasPushedMotionPhotoCover = false;
15999
+ if (staticImg.filepath) {
16000
+ const motionPhotoCoverPath = Common.tempDri.images + `MVIMG_${format(/* @__PURE__ */ new Date(), "yyyyMMdd_HHmmss_SSS")}_${index}.jpg`;
16001
+ if (await buildGoogleMotionPhoto({
16002
+ imagePath: staticImg.filepath,
16003
+ videoPath: livePhoto.filepath,
16004
+ outputPath: motionPhotoCoverPath
16005
+ })) {
16006
+ temp.push({
16007
+ filepath: motionPhotoCoverPath,
16008
+ totalBytes: 0
16009
+ });
16010
+ const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
16011
+ imgArray.push(segment.image(motionPhotoCover));
16012
+ hasPushedMotionPhotoCover = true;
16013
+ }
16014
+ }
16015
+ if (!hasPushedMotionPhotoCover) {
16016
+ const imageUrl = await processImageUrl(img$2.url, title, index);
16017
+ imgArray.push(segment.image(imageUrl));
16018
+ }
16019
+ logger.mark("正在尝试删除缓存文件");
16020
+ await Common.removeFile(livePhoto.filepath, true);
15821
16021
  } else await Common.removeFile(livePhoto.filepath, true);
15822
16022
  }
15823
16023
  } else {
@@ -17602,6 +17802,10 @@ var Bilibilipush = class extends Base {
17602
17802
  headers: bilibiliBaseHeaders,
17603
17803
  filepath: Common.tempDri.images + `Bilibili_static_${Date.now()}_${index}.jpg`
17604
17804
  });
17805
+ if (staticImg.filepath) temp.push({
17806
+ filepath: staticImg.filepath,
17807
+ totalBytes: 0
17808
+ });
17605
17809
  const loopCount = 3;
17606
17810
  if (!staticImg.filepath) {
17607
17811
  await Common.removeFile(livePhoto.filepath, true);
@@ -17617,16 +17821,35 @@ var Bilibilipush = class extends Base {
17617
17821
  const filePath = Common.tempDri.video + `tmp_${Date.now()}.mp4`;
17618
17822
  fs.renameSync(outputPath, filePath);
17619
17823
  logger.mark(`视频文件重命名完成: ${outputPath.split("/").pop()} -> ${filePath.split("/").pop()}`);
17620
- logger.mark("正在尝试删除缓存文件");
17621
- await Common.removeFile(staticImg.filepath, true);
17622
17824
  temp.push({
17623
17825
  filepath: filePath,
17624
17826
  totalBytes: 0
17625
17827
  });
17626
17828
  const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
17627
17829
  imgArray.push(segment.video(videoPath));
17628
- const imageUrl = await processImageUrl(imageSrc, title, index);
17629
- imgArray.push(segment.image(imageUrl));
17830
+ let hasPushedMotionPhotoCover = false;
17831
+ if (staticImg.filepath) {
17832
+ const motionPhotoCoverPath = Common.tempDri.images + `MVIMG_${Date.now()}_${index}.jpg`;
17833
+ if (await buildGoogleMotionPhoto({
17834
+ imagePath: staticImg.filepath,
17835
+ videoPath: livePhoto.filepath,
17836
+ outputPath: motionPhotoCoverPath
17837
+ })) {
17838
+ temp.push({
17839
+ filepath: motionPhotoCoverPath,
17840
+ totalBytes: 0
17841
+ });
17842
+ const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
17843
+ imgArray.push(segment.image(motionPhotoCover));
17844
+ hasPushedMotionPhotoCover = true;
17845
+ }
17846
+ }
17847
+ if (!hasPushedMotionPhotoCover) {
17848
+ const imageUrl = await processImageUrl(imageSrc, title, index);
17849
+ imgArray.push(segment.image(imageUrl));
17850
+ }
17851
+ logger.mark("正在尝试删除缓存文件");
17852
+ await Common.removeFile(livePhoto.filepath, true);
17630
17853
  continue;
17631
17854
  }
17632
17855
  await Common.removeFile(livePhoto.filepath, true);
@@ -18498,11 +18721,18 @@ var DouYin = class extends Base {
18498
18721
  const outputPath = Common.tempDri.video + `Douyin_Result_${Date.now()}.mp4`;
18499
18722
  const loopCount = imageItem.clip_type === 4 ? 1 : 3;
18500
18723
  let staticImgPath = "";
18501
- if (imageItem.url_list?.[0]) staticImgPath = (await downloadFile(imageItem.url_list[0], {
18502
- title: `Douyin_static_${Date.now()}_${index}.jpg`,
18503
- headers: this.headers,
18504
- filepath: Common.tempDri.images + `Douyin_static_${Date.now()}_${index}.jpg`
18505
- })).filepath ?? "";
18724
+ if (imageItem.url_list?.[0]) {
18725
+ const staticImg = await downloadFile(imageItem.url_list[0], {
18726
+ title: `Douyin_static_${Date.now()}_${index}.jpg`,
18727
+ headers: this.headers,
18728
+ filepath: Common.tempDri.images + `Douyin_static_${Date.now()}_${index}.jpg`
18729
+ });
18730
+ temp.push({
18731
+ filepath: staticImg.filepath,
18732
+ totalBytes: 0
18733
+ });
18734
+ staticImgPath = staticImg.filepath ?? "";
18735
+ }
18506
18736
  const transitionEnabled = loopCount > 1 && Boolean(staticImgPath);
18507
18737
  const safeStaticPath = staticImgPath || liveimg.filepath;
18508
18738
  const result = await loopVideoWithTransition({
@@ -18521,8 +18751,6 @@ var DouYin = class extends Base {
18521
18751
  const filePath = Common.tempDri.video + `tmp_${Date.now()}.mp4`;
18522
18752
  fs.renameSync(outputPath, filePath);
18523
18753
  logger.mark(`视频文件重命名完成: ${outputPath.split("/").pop()} -> ${filePath.split("/").pop()}`);
18524
- logger.mark("正在尝试删除缓存文件");
18525
- await Common.removeFile(liveimg.filepath, true);
18526
18754
  temp.push({
18527
18755
  filepath: filePath,
18528
18756
  totalBytes: 0
@@ -18530,9 +18758,30 @@ var DouYin = class extends Base {
18530
18758
  const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
18531
18759
  processedImages.push(segment.video(videoPath));
18532
18760
  if (imageItem.clip_type === 5 && imageItem.url_list?.[0]) {
18533
- const imageUrl = await processImageUrl(imageItem.url_list[0], g_title, index);
18534
- processedImages.push(segment.image(imageUrl));
18761
+ let hasPushedMotionPhotoCover = false;
18762
+ if (staticImgPath) {
18763
+ const motionPhotoCoverPath = Common.tempDri.images + `MVIMG_${format(/* @__PURE__ */ new Date(), "yyyyMMdd_HHmmss_SSS")}_${index}.jpg`;
18764
+ if (await buildGoogleMotionPhoto({
18765
+ imagePath: staticImgPath,
18766
+ videoPath: liveimg.filepath,
18767
+ outputPath: motionPhotoCoverPath
18768
+ })) {
18769
+ temp.push({
18770
+ filepath: motionPhotoCoverPath,
18771
+ totalBytes: 0
18772
+ });
18773
+ const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
18774
+ processedImages.push(segment.image(motionPhotoCover));
18775
+ hasPushedMotionPhotoCover = true;
18776
+ }
18777
+ }
18778
+ if (!hasPushedMotionPhotoCover) {
18779
+ const imageUrl = await processImageUrl(imageItem.url_list[0], g_title, index);
18780
+ processedImages.push(segment.image(imageUrl));
18781
+ }
18535
18782
  }
18783
+ logger.mark("正在尝试删除缓存文件");
18784
+ await Common.removeFile(liveimg.filepath, true);
18536
18785
  } else await Common.removeFile(liveimg.filepath, true);
18537
18786
  }
18538
18787
  }
@@ -18612,11 +18861,18 @@ var DouYin = class extends Base {
18612
18861
  const outputPath = Common.tempDri.video + `Douyin_Result_${Date.now()}.mp4`;
18613
18862
  const loopCount = item.clip_type === 4 ? 1 : 3;
18614
18863
  let staticImgPath = "";
18615
- if (item.url_list?.[0]) staticImgPath = (await downloadFile(item.url_list[0], {
18616
- title: `Douyin_static_${Date.now()}_${index}.jpg`,
18617
- headers: this.headers,
18618
- filepath: Common.tempDri.images + `Douyin_static_${Date.now()}_${index}.jpg`
18619
- })).filepath ?? "";
18864
+ if (item.url_list?.[0]) {
18865
+ const staticImg = await downloadFile(item.url_list[0], {
18866
+ title: `Douyin_static_${Date.now()}_${index}.jpg`,
18867
+ headers: this.headers,
18868
+ filepath: Common.tempDri.images + `Douyin_static_${Date.now()}_${index}.jpg`
18869
+ });
18870
+ temp.push({
18871
+ filepath: staticImg.filepath,
18872
+ totalBytes: 0
18873
+ });
18874
+ staticImgPath = staticImg.filepath ?? "";
18875
+ }
18620
18876
  const transitionEnabled = loopCount > 1 && Boolean(staticImgPath);
18621
18877
  const safeStaticPath = staticImgPath || livePhoto.filepath;
18622
18878
  const result = await loopVideoWithTransition({
@@ -18635,8 +18891,6 @@ var DouYin = class extends Base {
18635
18891
  const filePath = Common.tempDri.video + `tmp_${Date.now()}.mp4`;
18636
18892
  fs.renameSync(outputPath, filePath);
18637
18893
  logger.mark(`视频文件重命名完成: ${outputPath.split("/").pop()} -> ${filePath.split("/").pop()}`);
18638
- logger.mark("正在尝试删除缓存文件");
18639
- await Common.removeFile(livePhoto.filepath, true);
18640
18894
  temp.push({
18641
18895
  filepath: filePath,
18642
18896
  totalBytes: 0
@@ -18644,9 +18898,30 @@ var DouYin = class extends Base {
18644
18898
  const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
18645
18899
  images.push(segment.video(videoPath));
18646
18900
  if (item.clip_type === 5 && item.url_list?.[0]) {
18647
- const imageUrl = await processImageUrl(item.url_list[0], g_title, index);
18648
- images.push(segment.image(imageUrl));
18901
+ let hasPushedMotionPhotoCover = false;
18902
+ if (staticImgPath) {
18903
+ const motionPhotoCoverPath = Common.tempDri.images + `MVIMG_${format(/* @__PURE__ */ new Date(), "yyyyMMdd_HHmmss_SSS")}_${index}.jpg`;
18904
+ if (await buildGoogleMotionPhoto({
18905
+ imagePath: staticImgPath,
18906
+ videoPath: livePhoto.filepath,
18907
+ outputPath: motionPhotoCoverPath
18908
+ })) {
18909
+ temp.push({
18910
+ filepath: motionPhotoCoverPath,
18911
+ totalBytes: 0
18912
+ });
18913
+ const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
18914
+ images.push(segment.image(motionPhotoCover));
18915
+ hasPushedMotionPhotoCover = true;
18916
+ }
18917
+ }
18918
+ if (!hasPushedMotionPhotoCover) {
18919
+ const imageUrl = await processImageUrl(item.url_list[0], g_title, index);
18920
+ images.push(segment.image(imageUrl));
18921
+ }
18649
18922
  }
18923
+ logger.mark("正在尝试删除缓存文件");
18924
+ await Common.removeFile(livePhoto.filepath, true);
18650
18925
  } else await Common.removeFile(livePhoto.filepath, true);
18651
18926
  }
18652
18927
  }
@@ -18670,11 +18945,12 @@ var DouYin = class extends Base {
18670
18945
  if (music.play_url.uri === "") music_url = JSON.parse(music.extra).original_song_url;
18671
18946
  else music_url = music.play_url.uri;
18672
18947
  if (!isVideo && Config.app.removeCache === false && music_url !== void 0) try {
18673
- const path$1 = Common.tempDri.images + `${g_title}/BGM.mp3`;
18674
- await new Network({
18675
- url: music_url,
18676
- type: "arraybuffer"
18677
- }).getData().then((data$2) => fs.promises.writeFile(path$1, Buffer.from(data$2)));
18948
+ const title = g_title ?? VideoData.data.aweme_detail.preview_title.substring(0, 50).replace(/[\\/:*?"<>|\r\n]/g, " ");
18949
+ const path$1 = Common.tempDri.images + `${title}.mp3`;
18950
+ await downloadFile(music_url, {
18951
+ title,
18952
+ filepath: path$1
18953
+ });
18678
18954
  } catch (error) {
18679
18955
  console.log(error);
18680
18956
  }
@@ -19739,11 +20015,18 @@ var DouYinpush = class extends Base {
19739
20015
  const outputPath = Common.tempDri.video + `Douyin_Result_${Date.now()}.mp4`;
19740
20016
  const loopCount = item.clip_type === 4 ? 1 : 3;
19741
20017
  let staticImgPath = "";
19742
- if (item.url_list?.[0]) staticImgPath = (await downloadFile(item.url_list[0], {
19743
- title: `Douyin_static_${Date.now()}_${index}.jpg`,
19744
- headers: douyinBaseHeaders,
19745
- filepath: Common.tempDri.images + `Douyin_static_${Date.now()}_${index}.jpg`
19746
- })).filepath ?? "";
20018
+ if (item.url_list?.[0]) {
20019
+ const staticImg = await downloadFile(item.url_list[0], {
20020
+ title: `Douyin_static_${Date.now()}_${index}.jpg`,
20021
+ headers: douyinBaseHeaders,
20022
+ filepath: Common.tempDri.images + `Douyin_static_${Date.now()}_${index}.jpg`
20023
+ });
20024
+ if (staticImg.filepath) temp.push({
20025
+ filepath: staticImg.filepath,
20026
+ totalBytes: 0
20027
+ });
20028
+ staticImgPath = staticImg.filepath ?? "";
20029
+ }
19747
20030
  const transitionEnabled = loopCount > 1 && Boolean(staticImgPath);
19748
20031
  const safeStaticPath = staticImgPath || liveimg.filepath;
19749
20032
  const result = await loopVideoWithTransition({
@@ -19762,8 +20045,6 @@ var DouYinpush = class extends Base {
19762
20045
  const filePath = Common.tempDri.video + `tmp_${Date.now()}.mp4`;
19763
20046
  fs.renameSync(outputPath, filePath);
19764
20047
  logger.mark(`视频文件重命名完成: ${outputPath.split("/").pop()} -> ${filePath.split("/").pop()}`);
19765
- logger.mark("正在尝试删除缓存文件");
19766
- await Common.removeFile(liveimg.filepath, true);
19767
20048
  temp.push({
19768
20049
  filepath: filePath,
19769
20050
  totalBytes: 0
@@ -19771,9 +20052,30 @@ var DouYinpush = class extends Base {
19771
20052
  const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
19772
20053
  images.push(segment.video(videoPath));
19773
20054
  if (item.clip_type === 5 && item.url_list?.[0]) {
19774
- const imageUrl = await processImageUrl(item.url_list[0], Detail_Data.desc, index);
19775
- images.push(segment.image(imageUrl));
20055
+ let hasPushedMotionPhotoCover = false;
20056
+ if (staticImgPath) {
20057
+ const motionPhotoCoverPath = Common.tempDri.images + `MVIMG_${format(/* @__PURE__ */ new Date(), "yyyyMMdd_HHmmss_SSS")}_${index}.jpg`;
20058
+ if (await buildGoogleMotionPhoto({
20059
+ imagePath: staticImgPath,
20060
+ videoPath: liveimg.filepath,
20061
+ outputPath: motionPhotoCoverPath
20062
+ })) {
20063
+ temp.push({
20064
+ filepath: motionPhotoCoverPath,
20065
+ totalBytes: 0
20066
+ });
20067
+ const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
20068
+ images.push(segment.image(motionPhotoCover));
20069
+ hasPushedMotionPhotoCover = true;
20070
+ }
20071
+ }
20072
+ if (!hasPushedMotionPhotoCover) {
20073
+ const imageUrl = await processImageUrl(item.url_list[0], Detail_Data.desc, index);
20074
+ images.push(segment.image(imageUrl));
20075
+ }
19776
20076
  }
20077
+ logger.mark("正在尝试删除缓存文件");
20078
+ await Common.removeFile(liveimg.filepath, true);
19777
20079
  } else await Common.removeFile(liveimg.filepath, true);
19778
20080
  }
19779
20081
  }
@@ -19821,11 +20123,18 @@ var DouYinpush = class extends Base {
19821
20123
  const outputPath = Common.tempDri.video + `Douyin_Result_${Date.now()}.mp4`;
19822
20124
  const loopCount = item.clip_type === 4 ? 1 : 3;
19823
20125
  let staticImgPath = "";
19824
- if (item.url_list?.[0]) staticImgPath = (await downloadFile(item.url_list[0], {
19825
- title: `Douyin_static_${Date.now()}_${index}.jpg`,
19826
- headers: douyinBaseHeaders,
19827
- filepath: Common.tempDri.images + `Douyin_static_${Date.now()}_${index}.jpg`
19828
- })).filepath ?? "";
20126
+ if (item.url_list?.[0]) {
20127
+ const staticImg = await downloadFile(item.url_list[0], {
20128
+ title: `Douyin_static_${Date.now()}_${index}.jpg`,
20129
+ headers: douyinBaseHeaders,
20130
+ filepath: Common.tempDri.images + `Douyin_static_${Date.now()}_${index}.jpg`
20131
+ });
20132
+ if (staticImg.filepath) temp.push({
20133
+ filepath: staticImg.filepath,
20134
+ totalBytes: 0
20135
+ });
20136
+ staticImgPath = staticImg.filepath ?? "";
20137
+ }
19829
20138
  const transitionEnabled = loopCount > 1 && Boolean(staticImgPath);
19830
20139
  const safeStaticPath = staticImgPath || liveimg.filepath;
19831
20140
  const result = await loopVideoWithTransition({
@@ -19844,8 +20153,6 @@ var DouYinpush = class extends Base {
19844
20153
  const filePath = Common.tempDri.video + `tmp_${Date.now()}.mp4`;
19845
20154
  fs.renameSync(outputPath, filePath);
19846
20155
  logger.mark(`视频文件重命名完成: ${outputPath.split("/").pop()} -> ${filePath.split("/").pop()}`);
19847
- logger.mark("正在尝试删除缓存文件");
19848
- await Common.removeFile(liveimg.filepath, true);
19849
20156
  temp.push({
19850
20157
  filepath: filePath,
19851
20158
  totalBytes: 0
@@ -19853,9 +20160,30 @@ var DouYinpush = class extends Base {
19853
20160
  const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
19854
20161
  processedImages.push(segment.video(videoPath));
19855
20162
  if (item.clip_type === 5 && item.url_list?.[0]) {
19856
- const imageUrl = await processImageUrl(item.url_list[0], Detail_Data.desc, index);
19857
- processedImages.push(segment.image(imageUrl));
20163
+ let hasPushedMotionPhotoCover = false;
20164
+ if (staticImgPath) {
20165
+ const motionPhotoCoverPath = Common.tempDri.images + `MVIMG_${format(/* @__PURE__ */ new Date(), "yyyyMMdd_HHmmss_SSS")}_${index}.jpg`;
20166
+ if (await buildGoogleMotionPhoto({
20167
+ imagePath: staticImgPath,
20168
+ videoPath: liveimg.filepath,
20169
+ outputPath: motionPhotoCoverPath
20170
+ })) {
20171
+ temp.push({
20172
+ filepath: motionPhotoCoverPath,
20173
+ totalBytes: 0
20174
+ });
20175
+ const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
20176
+ processedImages.push(segment.image(motionPhotoCover));
20177
+ hasPushedMotionPhotoCover = true;
20178
+ }
20179
+ }
20180
+ if (!hasPushedMotionPhotoCover) {
20181
+ const imageUrl = await processImageUrl(item.url_list[0], Detail_Data.desc, index);
20182
+ processedImages.push(segment.image(imageUrl));
20183
+ }
19858
20184
  }
20185
+ logger.mark("正在尝试删除缓存文件");
20186
+ await Common.removeFile(liveimg.filepath, true);
19859
20187
  } else await Common.removeFile(liveimg.filepath, true);
19860
20188
  }
19861
20189
  }
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import "./core_chunk/main-1eljaHiz.js";
2
+ import "./core_chunk/main-BY6eDfoV.js";
3
3
  import "./core_chunk/vendor-DxfKHvj-.js";
4
4
  import "./core_chunk/template-DekmxKd7.js";
5
5
  export {};
package/lib/root.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import "./core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { A as init_root, k as Root } from "./core_chunk/main-1eljaHiz.js";
2
+ import { A as init_root, k as Root } from "./core_chunk/main-BY6eDfoV.js";
3
3
  init_root();
4
4
  export { Root };
package/lib/web.config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { D as webConfig, O as web_config_default } from "./core_chunk/main-1eljaHiz.js";
2
+ import { D as webConfig, O as web_config_default } from "./core_chunk/main-BY6eDfoV.js";
3
3
  import "./core_chunk/vendor-DxfKHvj-.js";
4
4
  import "./core_chunk/template-DekmxKd7.js";
5
5
  export { web_config_default as default, webConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karin-plugin-kkk",
3
- "version": "2.24.0",
3
+ "version": "2.25.1",
4
4
  "description": "Karin 的「抖音」「B 站」视频解析/动态推送插件",
5
5
  "keywords": [
6
6
  "karin-plugin",