vidpipe 1.3.5 → 1.3.6

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/dist/cli.js CHANGED
@@ -1440,6 +1440,50 @@ init_environment();
1440
1440
  init_paths();
1441
1441
  init_fileSystem();
1442
1442
  init_configLogger();
1443
+
1444
+ // src/L0-pure/types/index.ts
1445
+ var Platform = /* @__PURE__ */ ((Platform2) => {
1446
+ Platform2["TikTok"] = "tiktok";
1447
+ Platform2["YouTube"] = "youtube";
1448
+ Platform2["Instagram"] = "instagram";
1449
+ Platform2["LinkedIn"] = "linkedin";
1450
+ Platform2["X"] = "x";
1451
+ return Platform2;
1452
+ })(Platform || {});
1453
+ var PLATFORM_CHAR_LIMITS = {
1454
+ tiktok: 2200,
1455
+ youtube: 5e3,
1456
+ instagram: 2200,
1457
+ linkedin: 3e3,
1458
+ twitter: 280
1459
+ };
1460
+ function toLatePlatform(platform) {
1461
+ return platform === "x" /* X */ ? "twitter" : platform;
1462
+ }
1463
+ function fromLatePlatform(latePlatform) {
1464
+ const normalized = normalizePlatformString(latePlatform);
1465
+ if (normalized === "twitter") {
1466
+ return "x" /* X */;
1467
+ }
1468
+ const platformValues = Object.values(Platform);
1469
+ if (platformValues.includes(normalized)) {
1470
+ return normalized;
1471
+ }
1472
+ throw new Error(`Unsupported platform from Late API: ${latePlatform}`);
1473
+ }
1474
+ function normalizePlatformString(raw) {
1475
+ const lower = raw.toLowerCase().trim();
1476
+ if (lower === "x" || lower === "x (twitter)" || lower === "x/twitter") {
1477
+ return "twitter";
1478
+ }
1479
+ return lower;
1480
+ }
1481
+ var SUPPORTED_VIDEO_EXTENSIONS = [".mp4", ".webm"];
1482
+ function isSupportedVideoExtension(ext) {
1483
+ return SUPPORTED_VIDEO_EXTENSIONS.includes(ext.toLowerCase());
1484
+ }
1485
+
1486
+ // src/L7-app/fileWatcher.ts
1443
1487
  var FileWatcher = class _FileWatcher extends EventEmitter {
1444
1488
  watchFolder;
1445
1489
  watcher = null;
@@ -1469,8 +1513,8 @@ var FileWatcher = class _FileWatcher extends EventEmitter {
1469
1513
  }
1470
1514
  }
1471
1515
  async handleDetectedFile(filePath) {
1472
- if (extname(filePath).toLowerCase() !== ".mp4") {
1473
- logger_default.debug(`[watcher] Ignoring non-mp4 file: ${filePath}`);
1516
+ if (!isSupportedVideoExtension(extname(filePath).toLowerCase())) {
1517
+ logger_default.debug(`[watcher] Ignoring unsupported file: ${filePath}`);
1474
1518
  return;
1475
1519
  }
1476
1520
  let fileSize;
@@ -1505,7 +1549,7 @@ var FileWatcher = class _FileWatcher extends EventEmitter {
1505
1549
  throw err;
1506
1550
  }
1507
1551
  for (const file of files) {
1508
- if (extname(file).toLowerCase() === ".mp4") {
1552
+ if (isSupportedVideoExtension(extname(file).toLowerCase())) {
1509
1553
  const filePath = join(this.watchFolder, file);
1510
1554
  this.handleDetectedFile(filePath).catch(
1511
1555
  (err) => logger_default.error(`Error processing ${filePath}: ${err instanceof Error ? err.message : String(err)}`)
@@ -1535,7 +1579,7 @@ var FileWatcher = class _FileWatcher extends EventEmitter {
1535
1579
  });
1536
1580
  this.watcher.on("change", (filePath) => {
1537
1581
  logger_default.debug(`[watcher] 'change' event: ${filePath}`);
1538
- if (extname(filePath).toLowerCase() !== ".mp4") return;
1582
+ if (!isSupportedVideoExtension(extname(filePath).toLowerCase())) return;
1539
1583
  logger_default.info(`Change detected on video file: ${filePath}`);
1540
1584
  this.handleDetectedFile(filePath).catch(
1541
1585
  (err) => logger_default.error(`Error processing ${filePath}: ${err instanceof Error ? err.message : String(err)}`)
@@ -1556,7 +1600,7 @@ var FileWatcher = class _FileWatcher extends EventEmitter {
1556
1600
  this.scanExistingFiles();
1557
1601
  }
1558
1602
  });
1559
- logger_default.info(`Watching for new .mp4 files in: ${this.watchFolder}`);
1603
+ logger_default.info(`Watching for new video files in: ${this.watchFolder}`);
1560
1604
  }
1561
1605
  stop() {
1562
1606
  if (this.watcher) {
@@ -2880,6 +2924,41 @@ async function compositeOverlays(videoPath, overlays, outputPath, videoWidth, vi
2880
2924
  });
2881
2925
  }
2882
2926
 
2927
+ // src/L2-clients/ffmpeg/transcoding.ts
2928
+ init_fileSystem();
2929
+ init_paths();
2930
+ init_configLogger();
2931
+ function transcodeToMp4(inputPath, outputPath) {
2932
+ const outputDir = dirname(outputPath);
2933
+ return new Promise((resolve3, reject) => {
2934
+ ensureDirectory(outputDir).then(() => {
2935
+ logger_default.info(`Transcoding to MP4: ${inputPath} \u2192 ${outputPath}`);
2936
+ createFFmpeg(inputPath).outputOptions([
2937
+ "-c:v",
2938
+ "libx264",
2939
+ "-preset",
2940
+ "ultrafast",
2941
+ "-crf",
2942
+ "23",
2943
+ "-threads",
2944
+ "4",
2945
+ "-c:a",
2946
+ "aac",
2947
+ "-b:a",
2948
+ "128k",
2949
+ "-movflags",
2950
+ "+faststart"
2951
+ ]).output(outputPath).on("end", () => {
2952
+ logger_default.info(`Transcoding complete: ${outputPath}`);
2953
+ resolve3(outputPath);
2954
+ }).on("error", (err) => {
2955
+ logger_default.error(`Transcoding failed: ${err.message}`);
2956
+ reject(new Error(`Transcoding to MP4 failed: ${err.message}`));
2957
+ }).run();
2958
+ }).catch(reject);
2959
+ });
2960
+ }
2961
+
2883
2962
  // src/L3-services/videoOperations/videoOperations.ts
2884
2963
  function ffprobe2(...args) {
2885
2964
  return ffprobe(...args);
@@ -2917,6 +2996,9 @@ function getVideoResolution2(...args) {
2917
2996
  function compositeOverlays2(...args) {
2918
2997
  return compositeOverlays(...args);
2919
2998
  }
2999
+ function transcodeToMp42(...args) {
3000
+ return transcodeToMp4(...args);
3001
+ }
2920
3002
 
2921
3003
  // src/L0-pure/captions/captionGenerator.ts
2922
3004
  function pad(n, width) {
@@ -4367,46 +4449,6 @@ var SocialPostAsset = class extends TextAsset {
4367
4449
  // src/L5-assets/ShortVideoAsset.ts
4368
4450
  init_paths();
4369
4451
  init_fileSystem();
4370
-
4371
- // src/L0-pure/types/index.ts
4372
- var Platform = /* @__PURE__ */ ((Platform2) => {
4373
- Platform2["TikTok"] = "tiktok";
4374
- Platform2["YouTube"] = "youtube";
4375
- Platform2["Instagram"] = "instagram";
4376
- Platform2["LinkedIn"] = "linkedin";
4377
- Platform2["X"] = "x";
4378
- return Platform2;
4379
- })(Platform || {});
4380
- var PLATFORM_CHAR_LIMITS = {
4381
- tiktok: 2200,
4382
- youtube: 5e3,
4383
- instagram: 2200,
4384
- linkedin: 3e3,
4385
- twitter: 280
4386
- };
4387
- function toLatePlatform(platform) {
4388
- return platform === "x" /* X */ ? "twitter" : platform;
4389
- }
4390
- function fromLatePlatform(latePlatform) {
4391
- const normalized = normalizePlatformString(latePlatform);
4392
- if (normalized === "twitter") {
4393
- return "x" /* X */;
4394
- }
4395
- const platformValues = Object.values(Platform);
4396
- if (platformValues.includes(normalized)) {
4397
- return normalized;
4398
- }
4399
- throw new Error(`Unsupported platform from Late API: ${latePlatform}`);
4400
- }
4401
- function normalizePlatformString(raw) {
4402
- const lower = raw.toLowerCase().trim();
4403
- if (lower === "x" || lower === "x (twitter)" || lower === "x/twitter") {
4404
- return "twitter";
4405
- }
4406
- return lower;
4407
- }
4408
-
4409
- // src/L5-assets/ShortVideoAsset.ts
4410
4452
  var ShortVideoAsset = class extends VideoAsset {
4411
4453
  /** Reference to the source video this short was extracted from */
4412
4454
  parent;
@@ -8231,26 +8273,40 @@ var MainVideoAsset = class _MainVideoAsset extends VideoAsset {
8231
8273
  await ensureDirectory(socialPostsDir);
8232
8274
  const destFilename = `${slug}.mp4`;
8233
8275
  const destPath = join(videoDir, destFilename);
8234
- let needsCopy = true;
8276
+ const sourceExt = extname(sourcePath).toLowerCase();
8277
+ const needsTranscode = sourceExt !== ".mp4";
8278
+ let needsIngest = true;
8235
8279
  try {
8236
8280
  const destStats = await getFileStats(destPath);
8237
- const srcStats = await getFileStats(sourcePath);
8238
- if (destStats.size === srcStats.size) {
8239
- logger_default.info(`Video already copied (same size), skipping copy`);
8240
- needsCopy = false;
8281
+ if (needsTranscode) {
8282
+ if (destStats.size > 1024) {
8283
+ logger_default.info(`Transcoded MP4 already exists (${(destStats.size / 1024 / 1024).toFixed(1)} MB), skipping transcode`);
8284
+ needsIngest = false;
8285
+ }
8286
+ } else {
8287
+ const srcStats = await getFileStats(sourcePath);
8288
+ if (destStats.size === srcStats.size) {
8289
+ logger_default.info(`Video already copied (same size), skipping copy`);
8290
+ needsIngest = false;
8291
+ }
8241
8292
  }
8242
8293
  } catch {
8243
8294
  }
8244
- if (needsCopy) {
8245
- await new Promise((resolve3, reject) => {
8246
- const readStream = openReadStream(sourcePath);
8247
- const writeStream = openWriteStream(destPath);
8248
- readStream.on("error", reject);
8249
- writeStream.on("error", reject);
8250
- writeStream.on("finish", resolve3);
8251
- readStream.pipe(writeStream);
8252
- });
8253
- logger_default.info(`Copied video to ${destPath}`);
8295
+ if (needsIngest) {
8296
+ if (needsTranscode) {
8297
+ await transcodeToMp42(sourcePath, destPath);
8298
+ logger_default.info(`Transcoded video to ${destPath}`);
8299
+ } else {
8300
+ await new Promise((resolve3, reject) => {
8301
+ const readStream = openReadStream(sourcePath);
8302
+ const writeStream = openWriteStream(destPath);
8303
+ readStream.on("error", reject);
8304
+ writeStream.on("error", reject);
8305
+ writeStream.on("finish", resolve3);
8306
+ readStream.pipe(writeStream);
8307
+ });
8308
+ logger_default.info(`Copied video to ${destPath}`);
8309
+ }
8254
8310
  }
8255
8311
  const asset = new _MainVideoAsset(sourcePath, videoDir, slug);
8256
8312
  try {