koztv-blog-tools 1.2.3 → 1.2.5

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/index.js CHANGED
@@ -497,37 +497,44 @@ async function exportTelegramChannel(options) {
497
497
  const postMediaDir = path.join(mediaDir, paddedId);
498
498
  const mediaFiles = [];
499
499
  if (downloadMedia && message.media) {
500
- fs.mkdirSync(postMediaDir, { recursive: true });
501
- try {
502
- const buffer = await client.downloadMedia(message.media, {});
503
- if (buffer) {
504
- let ext = ".bin";
505
- if (message.media instanceof import_telegram.Api.MessageMediaPhoto) {
506
- ext = ".jpg";
507
- } else if (message.media instanceof import_telegram.Api.MessageMediaDocument) {
508
- const doc = message.media.document;
509
- if (doc instanceof import_telegram.Api.Document) {
510
- const mimeExt = doc.mimeType?.split("/")[1];
511
- if (mimeExt) {
512
- ext = "." + mimeExt.replace("jpeg", "jpg");
513
- }
514
- for (const attr of doc.attributes) {
515
- if (attr instanceof import_telegram.Api.DocumentAttributeVideo) {
516
- ext = ".mp4";
500
+ const existingFiles = fs.existsSync(postMediaDir) ? fs.readdirSync(postMediaDir).filter((f) => f.startsWith("media.")) : [];
501
+ if (existingFiles.length > 0) {
502
+ for (const f of existingFiles) {
503
+ mediaFiles.push(`media/${paddedId}/${f}`);
504
+ }
505
+ } else {
506
+ fs.mkdirSync(postMediaDir, { recursive: true });
507
+ try {
508
+ const buffer = await client.downloadMedia(message.media, {});
509
+ if (buffer) {
510
+ let ext = ".bin";
511
+ if (message.media instanceof import_telegram.Api.MessageMediaPhoto) {
512
+ ext = ".jpg";
513
+ } else if (message.media instanceof import_telegram.Api.MessageMediaDocument) {
514
+ const doc = message.media.document;
515
+ if (doc instanceof import_telegram.Api.Document) {
516
+ const mimeExt = doc.mimeType?.split("/")[1];
517
+ if (mimeExt) {
518
+ ext = "." + mimeExt.replace("jpeg", "jpg");
517
519
  }
518
- if (attr instanceof import_telegram.Api.DocumentAttributeFilename) {
519
- ext = path.extname(attr.fileName) || ext;
520
+ for (const attr of doc.attributes) {
521
+ if (attr instanceof import_telegram.Api.DocumentAttributeVideo) {
522
+ ext = ".mp4";
523
+ }
524
+ if (attr instanceof import_telegram.Api.DocumentAttributeFilename) {
525
+ ext = path.extname(attr.fileName) || ext;
526
+ }
520
527
  }
521
528
  }
522
529
  }
530
+ const mediaFileName = `media${ext}`;
531
+ const mediaPath = path.join(postMediaDir, mediaFileName);
532
+ fs.writeFileSync(mediaPath, buffer);
533
+ mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
523
534
  }
524
- const mediaFileName = `media${ext}`;
525
- const mediaPath = path.join(postMediaDir, mediaFileName);
526
- fs.writeFileSync(mediaPath, buffer);
527
- mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
535
+ } catch (e) {
536
+ console.error(`Error downloading media for message ${msgId}:`, e);
528
537
  }
529
- } catch (e) {
530
- console.error(`Error downloading media for message ${msgId}:`, e);
531
538
  }
532
539
  }
533
540
  const content = message.message || "";
@@ -664,9 +671,26 @@ async function processPost(post, options, exportDir) {
664
671
  const { title: originalTitle, body: originalBody } = extractTitleAndBody(cleanedContent);
665
672
  const date = post.date.toISOString().split("T")[0];
666
673
  const languages = [];
674
+ const translationExists = (lang) => {
675
+ const langDir = path2.join(outputDir, lang);
676
+ if (!fs2.existsSync(langDir)) return false;
677
+ try {
678
+ for (const slug of fs2.readdirSync(langDir)) {
679
+ const indexPath = path2.join(langDir, slug, "index.md");
680
+ if (fs2.existsSync(indexPath)) {
681
+ const content = fs2.readFileSync(indexPath, "utf-8");
682
+ if (content.includes(`original_link: "${post.link}"`)) {
683
+ return true;
684
+ }
685
+ }
686
+ }
687
+ } catch {
688
+ }
689
+ return false;
690
+ };
667
691
  if (translate && translate.targetLangs.length > 0) {
668
692
  const sourceLang = translate.sourceLang || "ru";
669
- if (translate.keepOriginal) {
693
+ if (translate.keepOriginal && !translationExists(sourceLang)) {
670
694
  languages.push({
671
695
  lang: sourceLang,
672
696
  title: originalTitle,
@@ -676,6 +700,10 @@ async function processPost(post, options, exportDir) {
676
700
  }
677
701
  for (const targetLang of translate.targetLangs) {
678
702
  if (targetLang === sourceLang) continue;
703
+ if (translationExists(targetLang)) {
704
+ onProgress?.(` ${targetLang}: already exists, skipping`);
705
+ continue;
706
+ }
679
707
  onProgress?.(` Translating to ${targetLang}...`);
680
708
  const translateOpts = {
681
709
  apiKey: translate.apiKey,
@@ -804,10 +832,6 @@ async function exportAndTranslate(options) {
804
832
  let skipped = 0;
805
833
  for (const post of exportResult.posts) {
806
834
  const postId = `${post.channelUsername}-${post.msgId}`;
807
- if (processedLog[postId]) {
808
- skipped++;
809
- continue;
810
- }
811
835
  if (!post.content && !post.hasMedia) {
812
836
  skipped++;
813
837
  continue;
@@ -818,8 +842,9 @@ Processing: ${postId}`);
818
842
  const created = await processPost(post, options, exportDir);
819
843
  if (created.length > 0) {
820
844
  processed++;
845
+ const existing = processedLog[postId]?.posts || [];
821
846
  processedLog[postId] = {
822
- posts: created,
847
+ posts: [.../* @__PURE__ */ new Set([...existing, ...created])],
823
848
  date: post.date.toISOString(),
824
849
  processedAt: (/* @__PURE__ */ new Date()).toISOString()
825
850
  };
package/dist/index.mjs CHANGED
@@ -440,37 +440,44 @@ async function exportTelegramChannel(options) {
440
440
  const postMediaDir = path.join(mediaDir, paddedId);
441
441
  const mediaFiles = [];
442
442
  if (downloadMedia && message.media) {
443
- fs.mkdirSync(postMediaDir, { recursive: true });
444
- try {
445
- const buffer = await client.downloadMedia(message.media, {});
446
- if (buffer) {
447
- let ext = ".bin";
448
- if (message.media instanceof Api.MessageMediaPhoto) {
449
- ext = ".jpg";
450
- } else if (message.media instanceof Api.MessageMediaDocument) {
451
- const doc = message.media.document;
452
- if (doc instanceof Api.Document) {
453
- const mimeExt = doc.mimeType?.split("/")[1];
454
- if (mimeExt) {
455
- ext = "." + mimeExt.replace("jpeg", "jpg");
456
- }
457
- for (const attr of doc.attributes) {
458
- if (attr instanceof Api.DocumentAttributeVideo) {
459
- ext = ".mp4";
443
+ const existingFiles = fs.existsSync(postMediaDir) ? fs.readdirSync(postMediaDir).filter((f) => f.startsWith("media.")) : [];
444
+ if (existingFiles.length > 0) {
445
+ for (const f of existingFiles) {
446
+ mediaFiles.push(`media/${paddedId}/${f}`);
447
+ }
448
+ } else {
449
+ fs.mkdirSync(postMediaDir, { recursive: true });
450
+ try {
451
+ const buffer = await client.downloadMedia(message.media, {});
452
+ if (buffer) {
453
+ let ext = ".bin";
454
+ if (message.media instanceof Api.MessageMediaPhoto) {
455
+ ext = ".jpg";
456
+ } else if (message.media instanceof Api.MessageMediaDocument) {
457
+ const doc = message.media.document;
458
+ if (doc instanceof Api.Document) {
459
+ const mimeExt = doc.mimeType?.split("/")[1];
460
+ if (mimeExt) {
461
+ ext = "." + mimeExt.replace("jpeg", "jpg");
460
462
  }
461
- if (attr instanceof Api.DocumentAttributeFilename) {
462
- ext = path.extname(attr.fileName) || ext;
463
+ for (const attr of doc.attributes) {
464
+ if (attr instanceof Api.DocumentAttributeVideo) {
465
+ ext = ".mp4";
466
+ }
467
+ if (attr instanceof Api.DocumentAttributeFilename) {
468
+ ext = path.extname(attr.fileName) || ext;
469
+ }
463
470
  }
464
471
  }
465
472
  }
473
+ const mediaFileName = `media${ext}`;
474
+ const mediaPath = path.join(postMediaDir, mediaFileName);
475
+ fs.writeFileSync(mediaPath, buffer);
476
+ mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
466
477
  }
467
- const mediaFileName = `media${ext}`;
468
- const mediaPath = path.join(postMediaDir, mediaFileName);
469
- fs.writeFileSync(mediaPath, buffer);
470
- mediaFiles.push(`media/${paddedId}/${mediaFileName}`);
478
+ } catch (e) {
479
+ console.error(`Error downloading media for message ${msgId}:`, e);
471
480
  }
472
- } catch (e) {
473
- console.error(`Error downloading media for message ${msgId}:`, e);
474
481
  }
475
482
  }
476
483
  const content = message.message || "";
@@ -607,9 +614,26 @@ async function processPost(post, options, exportDir) {
607
614
  const { title: originalTitle, body: originalBody } = extractTitleAndBody(cleanedContent);
608
615
  const date = post.date.toISOString().split("T")[0];
609
616
  const languages = [];
617
+ const translationExists = (lang) => {
618
+ const langDir = path2.join(outputDir, lang);
619
+ if (!fs2.existsSync(langDir)) return false;
620
+ try {
621
+ for (const slug of fs2.readdirSync(langDir)) {
622
+ const indexPath = path2.join(langDir, slug, "index.md");
623
+ if (fs2.existsSync(indexPath)) {
624
+ const content = fs2.readFileSync(indexPath, "utf-8");
625
+ if (content.includes(`original_link: "${post.link}"`)) {
626
+ return true;
627
+ }
628
+ }
629
+ }
630
+ } catch {
631
+ }
632
+ return false;
633
+ };
610
634
  if (translate && translate.targetLangs.length > 0) {
611
635
  const sourceLang = translate.sourceLang || "ru";
612
- if (translate.keepOriginal) {
636
+ if (translate.keepOriginal && !translationExists(sourceLang)) {
613
637
  languages.push({
614
638
  lang: sourceLang,
615
639
  title: originalTitle,
@@ -619,6 +643,10 @@ async function processPost(post, options, exportDir) {
619
643
  }
620
644
  for (const targetLang of translate.targetLangs) {
621
645
  if (targetLang === sourceLang) continue;
646
+ if (translationExists(targetLang)) {
647
+ onProgress?.(` ${targetLang}: already exists, skipping`);
648
+ continue;
649
+ }
622
650
  onProgress?.(` Translating to ${targetLang}...`);
623
651
  const translateOpts = {
624
652
  apiKey: translate.apiKey,
@@ -747,10 +775,6 @@ async function exportAndTranslate(options) {
747
775
  let skipped = 0;
748
776
  for (const post of exportResult.posts) {
749
777
  const postId = `${post.channelUsername}-${post.msgId}`;
750
- if (processedLog[postId]) {
751
- skipped++;
752
- continue;
753
- }
754
778
  if (!post.content && !post.hasMedia) {
755
779
  skipped++;
756
780
  continue;
@@ -761,8 +785,9 @@ Processing: ${postId}`);
761
785
  const created = await processPost(post, options, exportDir);
762
786
  if (created.length > 0) {
763
787
  processed++;
788
+ const existing = processedLog[postId]?.posts || [];
764
789
  processedLog[postId] = {
765
- posts: created,
790
+ posts: [.../* @__PURE__ */ new Set([...existing, ...created])],
766
791
  date: post.date.toISOString(),
767
792
  processedAt: (/* @__PURE__ */ new Date()).toISOString()
768
793
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koztv-blog-tools",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "Shared utilities for Telegram-based blog sites",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",