koztv-blog-tools 1.3.1 → 1.3.3

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/node.d.mts CHANGED
@@ -27,6 +27,8 @@ interface TelegramExportOptions {
27
27
  downloadMedia?: boolean;
28
28
  /** Skip posts that already have markdown files */
29
29
  skipExisting?: boolean;
30
+ /** Compress large videos and skip files over 95MB (requires ffmpeg) */
31
+ compressMedia?: boolean;
30
32
  /** Number of concurrent media downloads */
31
33
  mediaWorkers?: number;
32
34
  /** Callback for progress updates */
package/dist/node.d.ts CHANGED
@@ -27,6 +27,8 @@ interface TelegramExportOptions {
27
27
  downloadMedia?: boolean;
28
28
  /** Skip posts that already have markdown files */
29
29
  skipExisting?: boolean;
30
+ /** Compress large videos and skip files over 95MB (requires ffmpeg) */
31
+ compressMedia?: boolean;
30
32
  /** Number of concurrent media downloads */
31
33
  mediaWorkers?: number;
32
34
  /** Callback for progress updates */
package/dist/node.js CHANGED
@@ -375,6 +375,30 @@ var import_sessions = require("telegram/sessions/index.js");
375
375
  var fs = __toESM(require("fs"));
376
376
  var path = __toESM(require("path"));
377
377
  var readline = __toESM(require("readline"));
378
+ var import_child_process = require("child_process");
379
+ var MAX_FILE_SIZE = 95 * 1024 * 1024;
380
+ var COMPRESS_THRESHOLD = 50 * 1024 * 1024;
381
+ function hasFFmpeg() {
382
+ try {
383
+ (0, import_child_process.execSync)("ffmpeg -version", { stdio: "ignore" });
384
+ return true;
385
+ } catch {
386
+ return false;
387
+ }
388
+ }
389
+ function compressVideo(inputPath, outputPath) {
390
+ try {
391
+ (0, import_child_process.execSync)(
392
+ `ffmpeg -i "${inputPath}" -vf "scale='min(1280,iw)':'-2'" -c:v libx264 -crf 28 -preset fast -c:a aac -b:a 128k -y "${outputPath}"`,
393
+ { stdio: "ignore", timeout: 3e5 }
394
+ // 5 min timeout
395
+ );
396
+ const stats = fs.statSync(outputPath);
397
+ return stats.size <= MAX_FILE_SIZE;
398
+ } catch {
399
+ return false;
400
+ }
401
+ }
378
402
  function entitiesToMarkdown(text, entities) {
379
403
  if (!entities || entities.length === 0) return text;
380
404
  const mergedEntities = [];
@@ -515,6 +539,7 @@ async function exportTelegramChannel(options) {
515
539
  until,
516
540
  downloadMedia = true,
517
541
  skipExisting = false,
542
+ compressMedia = false,
518
543
  mediaWorkers = 3,
519
544
  onProgress,
520
545
  onPhoneNumber = () => defaultReadline("Phone number: "),
@@ -655,7 +680,43 @@ async function exportTelegramChannel(options) {
655
680
  const mediaFileName = `media${ext}`;
656
681
  const mediaPath = path.join(postMediaDir, mediaFileName);
657
682
  fs.writeFileSync(mediaPath, buffer);
658
- mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
683
+ if (compressMedia) {
684
+ const stats = fs.statSync(mediaPath);
685
+ const isVideo = [".mp4", ".mov", ".avi", ".webm", ".m4v"].includes(ext.toLowerCase());
686
+ if (stats.size > MAX_FILE_SIZE) {
687
+ if (isVideo && hasFFmpeg()) {
688
+ const compressedPath = path.join(postMediaDir, `media_compressed${ext}`);
689
+ if (compressVideo(mediaPath, compressedPath)) {
690
+ fs.unlinkSync(mediaPath);
691
+ fs.renameSync(compressedPath, mediaPath);
692
+ mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
693
+ } else {
694
+ fs.unlinkSync(mediaPath);
695
+ if (fs.existsSync(compressedPath)) fs.unlinkSync(compressedPath);
696
+ console.warn(`Skipping large media for message ${msgId} (>${MAX_FILE_SIZE / 1024 / 1024}MB)`);
697
+ }
698
+ } else {
699
+ fs.unlinkSync(mediaPath);
700
+ console.warn(`Skipping large media for message ${msgId} (>${MAX_FILE_SIZE / 1024 / 1024}MB)`);
701
+ }
702
+ } else if (stats.size > COMPRESS_THRESHOLD && isVideo && hasFFmpeg()) {
703
+ const compressedPath = path.join(postMediaDir, `media_compressed${ext}`);
704
+ if (compressVideo(mediaPath, compressedPath)) {
705
+ const compressedStats = fs.statSync(compressedPath);
706
+ if (compressedStats.size < stats.size) {
707
+ fs.unlinkSync(mediaPath);
708
+ fs.renameSync(compressedPath, mediaPath);
709
+ } else {
710
+ fs.unlinkSync(compressedPath);
711
+ }
712
+ }
713
+ mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
714
+ } else {
715
+ mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
716
+ }
717
+ } else {
718
+ mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
719
+ }
659
720
  }
660
721
  } catch (e) {
661
722
  console.error(`Error downloading media for message ${msgId}:`, e);
package/dist/node.mjs CHANGED
@@ -25,6 +25,30 @@ import { StringSession } from "telegram/sessions/index.js";
25
25
  import * as fs from "fs";
26
26
  import * as path from "path";
27
27
  import * as readline from "readline";
28
+ import { execSync } from "child_process";
29
+ var MAX_FILE_SIZE = 95 * 1024 * 1024;
30
+ var COMPRESS_THRESHOLD = 50 * 1024 * 1024;
31
+ function hasFFmpeg() {
32
+ try {
33
+ execSync("ffmpeg -version", { stdio: "ignore" });
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+ function compressVideo(inputPath, outputPath) {
40
+ try {
41
+ execSync(
42
+ `ffmpeg -i "${inputPath}" -vf "scale='min(1280,iw)':'-2'" -c:v libx264 -crf 28 -preset fast -c:a aac -b:a 128k -y "${outputPath}"`,
43
+ { stdio: "ignore", timeout: 3e5 }
44
+ // 5 min timeout
45
+ );
46
+ const stats = fs.statSync(outputPath);
47
+ return stats.size <= MAX_FILE_SIZE;
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
28
52
  function entitiesToMarkdown(text, entities) {
29
53
  if (!entities || entities.length === 0) return text;
30
54
  const mergedEntities = [];
@@ -165,6 +189,7 @@ async function exportTelegramChannel(options) {
165
189
  until,
166
190
  downloadMedia = true,
167
191
  skipExisting = false,
192
+ compressMedia = false,
168
193
  mediaWorkers = 3,
169
194
  onProgress,
170
195
  onPhoneNumber = () => defaultReadline("Phone number: "),
@@ -305,7 +330,43 @@ async function exportTelegramChannel(options) {
305
330
  const mediaFileName = `media${ext}`;
306
331
  const mediaPath = path.join(postMediaDir, mediaFileName);
307
332
  fs.writeFileSync(mediaPath, buffer);
308
- mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
333
+ if (compressMedia) {
334
+ const stats = fs.statSync(mediaPath);
335
+ const isVideo = [".mp4", ".mov", ".avi", ".webm", ".m4v"].includes(ext.toLowerCase());
336
+ if (stats.size > MAX_FILE_SIZE) {
337
+ if (isVideo && hasFFmpeg()) {
338
+ const compressedPath = path.join(postMediaDir, `media_compressed${ext}`);
339
+ if (compressVideo(mediaPath, compressedPath)) {
340
+ fs.unlinkSync(mediaPath);
341
+ fs.renameSync(compressedPath, mediaPath);
342
+ mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
343
+ } else {
344
+ fs.unlinkSync(mediaPath);
345
+ if (fs.existsSync(compressedPath)) fs.unlinkSync(compressedPath);
346
+ console.warn(`Skipping large media for message ${msgId} (>${MAX_FILE_SIZE / 1024 / 1024}MB)`);
347
+ }
348
+ } else {
349
+ fs.unlinkSync(mediaPath);
350
+ console.warn(`Skipping large media for message ${msgId} (>${MAX_FILE_SIZE / 1024 / 1024}MB)`);
351
+ }
352
+ } else if (stats.size > COMPRESS_THRESHOLD && isVideo && hasFFmpeg()) {
353
+ const compressedPath = path.join(postMediaDir, `media_compressed${ext}`);
354
+ if (compressVideo(mediaPath, compressedPath)) {
355
+ const compressedStats = fs.statSync(compressedPath);
356
+ if (compressedStats.size < stats.size) {
357
+ fs.unlinkSync(mediaPath);
358
+ fs.renameSync(compressedPath, mediaPath);
359
+ } else {
360
+ fs.unlinkSync(compressedPath);
361
+ }
362
+ }
363
+ mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
364
+ } else {
365
+ mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
366
+ }
367
+ } else {
368
+ mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
369
+ }
309
370
  }
310
371
  } catch (e) {
311
372
  console.error(`Error downloading media for message ${msgId}:`, e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koztv-blog-tools",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Shared utilities for Telegram-based blog sites",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",