socialdatax-skills 0.2.22 → 0.2.23
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/README.md +1 -1
- package/cli.mjs +185 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ Common search phrases for this skill package:
|
|
|
73
73
|
- Sensitive Words Check MCP server name: `sensitive-check`
|
|
74
74
|
- Sensitive Words Check future MCP registry name: `com.socialdatax/sensitive-check`
|
|
75
75
|
- Unified MCP registry name: none; this package installs skills and calls explicit platform services.
|
|
76
|
-
- Current public capability version: `0.2.
|
|
76
|
+
- Current public capability version: `0.2.23`
|
|
77
77
|
|
|
78
78
|
## Direct CLI
|
|
79
79
|
|
package/cli.mjs
CHANGED
|
@@ -17,7 +17,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
|
|
19
19
|
const PACKAGE_NAME = "socialdatax-skills";
|
|
20
|
-
const PACKAGE_VERSION = "0.2.
|
|
20
|
+
const PACKAGE_VERSION = "0.2.23";
|
|
21
21
|
const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
|
|
22
22
|
const LOG_PREFIX = `[${PACKAGE_NAME}]`;
|
|
23
23
|
const MIN_NODE_VERSION = "20.18.1";
|
|
@@ -29,6 +29,37 @@ const MEDIA_DOWNLOAD_TIMEOUT_MS = 10 * 60 * 1000;
|
|
|
29
29
|
const MEDIA_DOWNLOAD_MAX_BYTES = 2 * 1024 * 1024 * 1024;
|
|
30
30
|
const MEDIA_DECRYPT_CHUNK_SIZE = 64 * 1024;
|
|
31
31
|
const MEDIA_DOWNLOAD_MAX_REDIRECTS = 5;
|
|
32
|
+
const MP4_BOX_HEADER_SIZE = 8;
|
|
33
|
+
const MP4_EXTENDED_BOX_HEADER_SIZE = 16;
|
|
34
|
+
const MP4_MAX_BOX_SCAN_DEPTH = 16;
|
|
35
|
+
const MP4_FULL_BOX_HEADER_SIZE = 4;
|
|
36
|
+
const MP4_STSD_ENTRY_COUNT_SIZE = 4;
|
|
37
|
+
const MP4_VISUAL_SAMPLE_ENTRY_HEADER_SIZE = 78;
|
|
38
|
+
const MP4_VVC_NAL_LENGTH_SIZE_BYTE_OFFSET = 4;
|
|
39
|
+
const MP4_CONTAINER_BOX_TYPES = new Set([
|
|
40
|
+
"dinf",
|
|
41
|
+
"edts",
|
|
42
|
+
"mdia",
|
|
43
|
+
"meta",
|
|
44
|
+
"minf",
|
|
45
|
+
"moof",
|
|
46
|
+
"moov",
|
|
47
|
+
"mfra",
|
|
48
|
+
"stbl",
|
|
49
|
+
"traf",
|
|
50
|
+
"trak",
|
|
51
|
+
"udta",
|
|
52
|
+
]);
|
|
53
|
+
const MP4_VISUAL_SAMPLE_ENTRY_TYPES = new Set([
|
|
54
|
+
"av01",
|
|
55
|
+
"avc1",
|
|
56
|
+
"avc3",
|
|
57
|
+
"encv",
|
|
58
|
+
"hev1",
|
|
59
|
+
"hvc1",
|
|
60
|
+
"vvc1",
|
|
61
|
+
"vvi1",
|
|
62
|
+
]);
|
|
32
63
|
// Keep this local helper scoped to WeChat media host trees; require k for the decrypt key.
|
|
33
64
|
const WECHAT_MEDIA_DOWNLOAD_HOST_SUFFIXES = [
|
|
34
65
|
".wxapp.tc.qq.com",
|
|
@@ -2936,6 +2967,7 @@ export async function decryptWechatMediaCommand(
|
|
|
2936
2967
|
encryptedLength,
|
|
2937
2968
|
decodeKey,
|
|
2938
2969
|
});
|
|
2970
|
+
await patchWechatVvcMp4NalLengthMetadata(tempPath);
|
|
2939
2971
|
decrypted = true;
|
|
2940
2972
|
}
|
|
2941
2973
|
await rename(tempPath, outputPath);
|
|
@@ -3185,6 +3217,158 @@ async function decryptWechatMediaFilePrefix(
|
|
|
3185
3217
|
}
|
|
3186
3218
|
}
|
|
3187
3219
|
|
|
3220
|
+
async function patchWechatVvcMp4NalLengthMetadata(filePath) {
|
|
3221
|
+
const file = await openFile(filePath, "r+");
|
|
3222
|
+
try {
|
|
3223
|
+
const stats = await file.stat();
|
|
3224
|
+
await patchMp4ChildBoxes(file, 0, stats.size, 0);
|
|
3225
|
+
} finally {
|
|
3226
|
+
await file.close();
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
async function patchMp4ChildBoxes(file, startOffset, endOffset, depth) {
|
|
3231
|
+
if (
|
|
3232
|
+
depth > MP4_MAX_BOX_SCAN_DEPTH ||
|
|
3233
|
+
startOffset < 0 ||
|
|
3234
|
+
endOffset - startOffset < MP4_BOX_HEADER_SIZE
|
|
3235
|
+
) {
|
|
3236
|
+
return;
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
let offset = startOffset;
|
|
3240
|
+
while (offset + MP4_BOX_HEADER_SIZE <= endOffset) {
|
|
3241
|
+
const box = await readMp4BoxHeader(file, offset, endOffset);
|
|
3242
|
+
if (!box) {
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3245
|
+
if (box.type === "vvcC") {
|
|
3246
|
+
await patchVvcConfigurationNalLengthSize(file, box);
|
|
3247
|
+
} else {
|
|
3248
|
+
const childRange = mp4ChildBoxRange(box);
|
|
3249
|
+
if (childRange) {
|
|
3250
|
+
await patchMp4ChildBoxes(
|
|
3251
|
+
file,
|
|
3252
|
+
childRange.startOffset,
|
|
3253
|
+
childRange.endOffset,
|
|
3254
|
+
depth + 1
|
|
3255
|
+
);
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
offset = box.endOffset;
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3262
|
+
async function readMp4BoxHeader(file, offset, parentEndOffset) {
|
|
3263
|
+
const baseHeader = Buffer.allocUnsafe(MP4_BOX_HEADER_SIZE);
|
|
3264
|
+
const baseRead = await file.read(
|
|
3265
|
+
baseHeader,
|
|
3266
|
+
0,
|
|
3267
|
+
MP4_BOX_HEADER_SIZE,
|
|
3268
|
+
offset
|
|
3269
|
+
);
|
|
3270
|
+
if (baseRead.bytesRead !== MP4_BOX_HEADER_SIZE) {
|
|
3271
|
+
return undefined;
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
const size32 = baseHeader.readUInt32BE(0);
|
|
3275
|
+
const type = baseHeader.toString("ascii", 4, 8);
|
|
3276
|
+
let headerSize = MP4_BOX_HEADER_SIZE;
|
|
3277
|
+
let size;
|
|
3278
|
+
if (size32 === 0) {
|
|
3279
|
+
size = parentEndOffset - offset;
|
|
3280
|
+
} else if (size32 === 1) {
|
|
3281
|
+
if (offset + MP4_EXTENDED_BOX_HEADER_SIZE > parentEndOffset) {
|
|
3282
|
+
return undefined;
|
|
3283
|
+
}
|
|
3284
|
+
const extendedSizeBuffer = Buffer.allocUnsafe(8);
|
|
3285
|
+
const extendedRead = await file.read(
|
|
3286
|
+
extendedSizeBuffer,
|
|
3287
|
+
0,
|
|
3288
|
+
8,
|
|
3289
|
+
offset + MP4_BOX_HEADER_SIZE
|
|
3290
|
+
);
|
|
3291
|
+
if (extendedRead.bytesRead !== 8) {
|
|
3292
|
+
return undefined;
|
|
3293
|
+
}
|
|
3294
|
+
const extendedSize = extendedSizeBuffer.readBigUInt64BE(0);
|
|
3295
|
+
if (extendedSize > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
3296
|
+
return undefined;
|
|
3297
|
+
}
|
|
3298
|
+
size = Number(extendedSize);
|
|
3299
|
+
headerSize = MP4_EXTENDED_BOX_HEADER_SIZE;
|
|
3300
|
+
} else {
|
|
3301
|
+
size = size32;
|
|
3302
|
+
}
|
|
3303
|
+
|
|
3304
|
+
if (
|
|
3305
|
+
size < headerSize ||
|
|
3306
|
+
offset + size > parentEndOffset ||
|
|
3307
|
+
!isMp4BoxType(type)
|
|
3308
|
+
) {
|
|
3309
|
+
return undefined;
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
return {
|
|
3313
|
+
type,
|
|
3314
|
+
startOffset: offset,
|
|
3315
|
+
headerSize,
|
|
3316
|
+
payloadOffset: offset + headerSize,
|
|
3317
|
+
endOffset: offset + size,
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3321
|
+
function isMp4BoxType(type) {
|
|
3322
|
+
return /^[\x20-\x7e]{4}$/.test(type);
|
|
3323
|
+
}
|
|
3324
|
+
|
|
3325
|
+
function mp4ChildBoxRange(box) {
|
|
3326
|
+
if (box.type === "stsd") {
|
|
3327
|
+
const childStart =
|
|
3328
|
+
box.payloadOffset + MP4_FULL_BOX_HEADER_SIZE + MP4_STSD_ENTRY_COUNT_SIZE;
|
|
3329
|
+
return childStart < box.endOffset
|
|
3330
|
+
? { startOffset: childStart, endOffset: box.endOffset }
|
|
3331
|
+
: undefined;
|
|
3332
|
+
}
|
|
3333
|
+
if (box.type === "meta") {
|
|
3334
|
+
const childStart = box.payloadOffset + MP4_FULL_BOX_HEADER_SIZE;
|
|
3335
|
+
return childStart < box.endOffset
|
|
3336
|
+
? { startOffset: childStart, endOffset: box.endOffset }
|
|
3337
|
+
: undefined;
|
|
3338
|
+
}
|
|
3339
|
+
if (MP4_VISUAL_SAMPLE_ENTRY_TYPES.has(box.type)) {
|
|
3340
|
+
const childStart = box.payloadOffset + MP4_VISUAL_SAMPLE_ENTRY_HEADER_SIZE;
|
|
3341
|
+
return childStart < box.endOffset
|
|
3342
|
+
? { startOffset: childStart, endOffset: box.endOffset }
|
|
3343
|
+
: undefined;
|
|
3344
|
+
}
|
|
3345
|
+
if (MP4_CONTAINER_BOX_TYPES.has(box.type)) {
|
|
3346
|
+
return box.payloadOffset < box.endOffset
|
|
3347
|
+
? { startOffset: box.payloadOffset, endOffset: box.endOffset }
|
|
3348
|
+
: undefined;
|
|
3349
|
+
}
|
|
3350
|
+
return undefined;
|
|
3351
|
+
}
|
|
3352
|
+
|
|
3353
|
+
async function patchVvcConfigurationNalLengthSize(file, box) {
|
|
3354
|
+
const targetOffset =
|
|
3355
|
+
box.payloadOffset + MP4_VVC_NAL_LENGTH_SIZE_BYTE_OFFSET;
|
|
3356
|
+
if (targetOffset >= box.endOffset) {
|
|
3357
|
+
return;
|
|
3358
|
+
}
|
|
3359
|
+
const buffer = Buffer.allocUnsafe(1);
|
|
3360
|
+
const { bytesRead } = await file.read(buffer, 0, 1, targetOffset);
|
|
3361
|
+
if (bytesRead !== 1) {
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3364
|
+
const patched = (buffer[0] & 0xf8) | 0x07;
|
|
3365
|
+
if (patched === buffer[0]) {
|
|
3366
|
+
return;
|
|
3367
|
+
}
|
|
3368
|
+
buffer[0] = patched;
|
|
3369
|
+
await writeAllToFile(file, buffer, targetOffset);
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3188
3372
|
function isAbortLikeError(error) {
|
|
3189
3373
|
return error?.name === "AbortError" || error?.name === "TimeoutError";
|
|
3190
3374
|
}
|
package/package.json
CHANGED