@tacone/prosey 0.4.0 → 0.6.0
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/bin/prosey +75 -12
- package/package.json +1 -1
- package/src/default-config.toml +17 -1
- package/src/html.ts +2 -1
- package/src/index.ts +75 -11
package/bin/prosey
CHANGED
|
@@ -103244,6 +103244,7 @@ blockquote {
|
|
|
103244
103244
|
blockquote:first-child { margin-top: 0; }
|
|
103245
103245
|
blockquote:last-child { margin-bottom: 0; }
|
|
103246
103246
|
* { transition: all 0.3s; }
|
|
103247
|
+
#theme-btn, #theme-btn:focus, #theme-btn:active, #theme-btn:hover { box-shadow: none !important; }
|
|
103247
103248
|
#theme-btn:focus, #theme-btn:active { outline: none !important; }
|
|
103248
103249
|
#theme-btn:hover, #theme-btn:focus, #theme-btn:active { opacity: 1 !important; }
|
|
103249
103250
|
img[alt="Prosey"] { filter: grayscale(100%); }
|
|
@@ -103259,7 +103260,7 @@ img[alt="Prosey"]:hover, img[alt="Prosey"]:active, img[alt="Prosey"]:focus { fil
|
|
|
103259
103260
|
${body}
|
|
103260
103261
|
</main>
|
|
103261
103262
|
<script>
|
|
103262
|
-
(function(){var b=document.getElementById('theme-btn'),
|
|
103263
|
+
(function(){var b=document.getElementById('theme-btn'),icons={auto:'\uD83D\uDCA1',light:'☀️',dark:'\uD83C\uDF19'};function apply(m){var t=m;if(t==='auto')t=window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light';document.documentElement.setAttribute('data-theme',t);localStorage.setItem('prosey-theme',m);b.textContent=icons[m]}b.addEventListener('click',function(){var m=localStorage.getItem('prosey-theme')||'auto',d=window.matchMedia('(prefers-color-scheme:dark)').matches;apply(d?{auto:'light',light:'dark',dark:'auto'}[m]:{auto:'dark',dark:'light',light:'auto'}[m])});apply(localStorage.getItem('prosey-theme')||'auto')})();
|
|
103263
103264
|
</script>
|
|
103264
103265
|
</body>
|
|
103265
103266
|
</html>`;
|
|
@@ -103290,7 +103291,7 @@ function openInBrowser(htmlPath) {
|
|
|
103290
103291
|
// package.json
|
|
103291
103292
|
var package_default = {
|
|
103292
103293
|
name: "@tacone/prosey",
|
|
103293
|
-
version: "0.
|
|
103294
|
+
version: "0.6.0",
|
|
103294
103295
|
description: "Download YouTube video transcripts from the CLI",
|
|
103295
103296
|
module: "src/index.ts",
|
|
103296
103297
|
type: "module",
|
|
@@ -122283,10 +122284,12 @@ try {
|
|
|
122283
122284
|
const dir2 = cacheDir(videoId, cacheOpts2);
|
|
122284
122285
|
let segments2 = null;
|
|
122285
122286
|
let summary = null;
|
|
122287
|
+
let cachedInfo = null;
|
|
122286
122288
|
startTimer();
|
|
122287
122289
|
if (!noCache) {
|
|
122288
122290
|
const cachedSegments = await readCache(dir2, "transcript.json");
|
|
122289
122291
|
const cachedSummary = await readCache(dir2, "summary.md");
|
|
122292
|
+
cachedInfo = await readCache(dir2, "info.json");
|
|
122290
122293
|
if (cachedSegments && cachedSummary) {
|
|
122291
122294
|
info("Transcript cached");
|
|
122292
122295
|
debug("Cache hit:", dir2);
|
|
@@ -122298,23 +122301,83 @@ try {
|
|
|
122298
122301
|
} else {
|
|
122299
122302
|
debug("Cache skipped (--no-cache)");
|
|
122300
122303
|
}
|
|
122301
|
-
if (!segments2) {
|
|
122302
|
-
info("Fetching transcript...");
|
|
122303
|
-
segments2 = lang ? await fetchTranscript(videoId, { lang }) : await fetchTranscript(videoId);
|
|
122304
|
-
info(`Transcript: ${segments2.length} segments`);
|
|
122305
|
-
await writeCache(dir2, "transcript.json", JSON.stringify(segments2));
|
|
122306
|
-
debug("Cache written: transcript.json");
|
|
122307
|
-
}
|
|
122308
122304
|
const prompt2 = resolveSummarizePrompt(config) ?? "";
|
|
122309
122305
|
if (!prompt2) {
|
|
122310
122306
|
console.error("Error: no prompt configured. Set a prompt in the [summarize] section of your config.");
|
|
122311
122307
|
exitProcess(1);
|
|
122312
122308
|
}
|
|
122313
|
-
|
|
122309
|
+
let structuredContent;
|
|
122310
|
+
if (!segments2) {
|
|
122311
|
+
info("Fetching transcript...");
|
|
122312
|
+
const opts = lang ? { lang, videoDetails: true } : { videoDetails: true };
|
|
122313
|
+
const result = await fetchTranscript(videoId, opts);
|
|
122314
|
+
segments2 = result.segments;
|
|
122315
|
+
const infoJson = JSON.stringify({
|
|
122316
|
+
title: result.videoDetails.title,
|
|
122317
|
+
channel: result.videoDetails.author,
|
|
122318
|
+
description: result.videoDetails.description
|
|
122319
|
+
});
|
|
122320
|
+
cachedInfo = infoJson;
|
|
122321
|
+
const chapterValue = formatChaptersAsJson(extractChapters(result.videoDetails.description));
|
|
122322
|
+
const truncatedInfo = JSON.stringify({
|
|
122323
|
+
title: result.videoDetails.title,
|
|
122324
|
+
channel: result.videoDetails.author,
|
|
122325
|
+
description: result.videoDetails.description.slice(0, 1000)
|
|
122326
|
+
});
|
|
122327
|
+
const transcriptText = toText(segments2, !noDecode);
|
|
122328
|
+
structuredContent = `INFO:
|
|
122329
|
+
${truncatedInfo}
|
|
122330
|
+
|
|
122331
|
+
TEXT:
|
|
122332
|
+
${transcriptText}`;
|
|
122333
|
+
if (dryRun) {
|
|
122334
|
+
await outputText(`${prompt2}
|
|
122335
|
+
|
|
122336
|
+
${structuredContent}
|
|
122337
|
+
`);
|
|
122338
|
+
exitProcess(0);
|
|
122339
|
+
}
|
|
122340
|
+
info(`Transcript: ${segments2.length} segments`);
|
|
122341
|
+
await writeCache(dir2, "transcript.json", JSON.stringify(segments2));
|
|
122342
|
+
await writeCache(dir2, "info.json", infoJson);
|
|
122343
|
+
await writeCache(dir2, "chapters.json", chapterValue);
|
|
122344
|
+
debug("Cache written: transcript.json, info.json, chapters.json");
|
|
122345
|
+
} else {
|
|
122346
|
+
let chapterValue;
|
|
122347
|
+
if (!cachedInfo) {
|
|
122348
|
+
debug("Cache missing info.json, re-fetching video details");
|
|
122349
|
+
const fallbackOpts = lang ? { lang, videoDetails: true } : { videoDetails: true };
|
|
122350
|
+
const fallbackResult = await fetchTranscript(videoId, fallbackOpts);
|
|
122351
|
+
cachedInfo = JSON.stringify({
|
|
122352
|
+
title: fallbackResult.videoDetails.title,
|
|
122353
|
+
channel: fallbackResult.videoDetails.author,
|
|
122354
|
+
description: fallbackResult.videoDetails.description
|
|
122355
|
+
});
|
|
122356
|
+
await writeCache(dir2, "info.json", cachedInfo);
|
|
122357
|
+
chapterValue = formatChaptersAsJson(extractChapters(fallbackResult.videoDetails.description));
|
|
122358
|
+
await writeCache(dir2, "chapters.json", chapterValue);
|
|
122359
|
+
debug("Cache written: info.json, chapters.json");
|
|
122360
|
+
} else {
|
|
122361
|
+
const cachedChapters = await readCache(dir2, "chapters.json");
|
|
122362
|
+
chapterValue = cachedChapters ?? "not available";
|
|
122363
|
+
}
|
|
122364
|
+
const transcriptText = toText(segments2, !noDecode);
|
|
122365
|
+
const cachedInfoObj = JSON.parse(cachedInfo);
|
|
122366
|
+
const truncatedInfo = JSON.stringify({
|
|
122367
|
+
title: cachedInfoObj.title,
|
|
122368
|
+
channel: cachedInfoObj.channel,
|
|
122369
|
+
description: cachedInfoObj.description.slice(0, 1000)
|
|
122370
|
+
});
|
|
122371
|
+
structuredContent = `INFO:
|
|
122372
|
+
${truncatedInfo}
|
|
122373
|
+
|
|
122374
|
+
TEXT:
|
|
122375
|
+
${transcriptText}`;
|
|
122376
|
+
}
|
|
122314
122377
|
if (dryRun) {
|
|
122315
122378
|
await outputText(`${prompt2}
|
|
122316
122379
|
|
|
122317
|
-
${
|
|
122380
|
+
${structuredContent}
|
|
122318
122381
|
`);
|
|
122319
122382
|
exitProcess(0);
|
|
122320
122383
|
}
|
|
@@ -122323,7 +122386,7 @@ ${transcriptText}
|
|
|
122323
122386
|
summary = await summarize({
|
|
122324
122387
|
prompt: prompt2,
|
|
122325
122388
|
command: sumCmd,
|
|
122326
|
-
transcript:
|
|
122389
|
+
transcript: structuredContent,
|
|
122327
122390
|
cwd: dir2
|
|
122328
122391
|
});
|
|
122329
122392
|
info("Summary ready");
|
package/package.json
CHANGED
package/src/default-config.toml
CHANGED
|
@@ -29,7 +29,23 @@ command = "OPENCODE_PERMISSION='{\"read\":\"allow\",\"write\":\"deny\",\"edit\":
|
|
|
29
29
|
# Customize this to change how transcripts are summarized.
|
|
30
30
|
|
|
31
31
|
prompt = """
|
|
32
|
-
Write a comprehensive summary of the following transcription
|
|
32
|
+
Write a comprehensive summary of the following video transcription:
|
|
33
|
+
|
|
34
|
+
Guidelines:
|
|
35
|
+
- use the INFO section data to correct spellings in the text (e.g. Pi vs Pie)
|
|
36
|
+
- use the INFO title value as the main title (h1 header)
|
|
37
|
+
- use the TIMESTAMPS only to understand the structure, don't output time markers
|
|
38
|
+
- content length: aim to be the same range as the original text or more.
|
|
39
|
+
- section titles should be h2 headers
|
|
40
|
+
|
|
41
|
+
Content considerations:
|
|
42
|
+
|
|
43
|
+
- findings should be presented in lists or table
|
|
44
|
+
- flows should be presented in ordered lists
|
|
45
|
+
- elaborations are better in prose
|
|
46
|
+
- when content mixes types within a section, lead with prose and break out lists only for discrete items
|
|
47
|
+
|
|
48
|
+
Only output the summary text without adding anything.
|
|
33
49
|
"""
|
|
34
50
|
|
|
35
51
|
# Command override for summarize. Uncomment to use a different command
|
package/src/html.ts
CHANGED
|
@@ -91,6 +91,7 @@ blockquote {
|
|
|
91
91
|
blockquote:first-child { margin-top: 0; }
|
|
92
92
|
blockquote:last-child { margin-bottom: 0; }
|
|
93
93
|
* { transition: all 0.3s; }
|
|
94
|
+
#theme-btn, #theme-btn:focus, #theme-btn:active, #theme-btn:hover { box-shadow: none !important; }
|
|
94
95
|
#theme-btn:focus, #theme-btn:active { outline: none !important; }
|
|
95
96
|
#theme-btn:hover, #theme-btn:focus, #theme-btn:active { opacity: 1 !important; }
|
|
96
97
|
img[alt="Prosey"] { filter: grayscale(100%); }
|
|
@@ -106,7 +107,7 @@ img[alt="Prosey"]:hover, img[alt="Prosey"]:active, img[alt="Prosey"]:focus { fil
|
|
|
106
107
|
${body}
|
|
107
108
|
</main>
|
|
108
109
|
<script>
|
|
109
|
-
(function(){var b=document.getElementById('theme-btn'),
|
|
110
|
+
(function(){var b=document.getElementById('theme-btn'),icons={auto:'\u{1F4A1}',light:'\u2600\uFE0F',dark:'\u{1F319}'};function apply(m){var t=m;if(t==='auto')t=window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light';document.documentElement.setAttribute('data-theme',t);localStorage.setItem('prosey-theme',m);b.textContent=icons[m]}b.addEventListener('click',function(){var m=localStorage.getItem('prosey-theme')||'auto',d=window.matchMedia('(prefers-color-scheme:dark)').matches;apply(d?{auto:'light',light:'dark',dark:'auto'}[m]:{auto:'dark',dark:'light',light:'auto'}[m])});apply(localStorage.getItem('prosey-theme')||'auto')})();
|
|
110
111
|
</script>
|
|
111
112
|
</body>
|
|
112
113
|
</html>`;
|
package/src/index.ts
CHANGED
|
@@ -443,12 +443,14 @@ try {
|
|
|
443
443
|
const dir = cacheDir(videoId, cacheOpts);
|
|
444
444
|
let segments: TranscriptSegment[] | null = null;
|
|
445
445
|
let summary: string | null = null;
|
|
446
|
+
let cachedInfo: string | null = null;
|
|
446
447
|
|
|
447
448
|
startTimer();
|
|
448
449
|
|
|
449
450
|
if (!noCache) {
|
|
450
451
|
const cachedSegments = await readCache(dir, "transcript.json");
|
|
451
452
|
const cachedSummary = await readCache(dir, "summary.md");
|
|
453
|
+
cachedInfo = await readCache(dir, "info.json");
|
|
452
454
|
if (cachedSegments && cachedSummary) {
|
|
453
455
|
info("Transcript cached");
|
|
454
456
|
debug("Cache hit:", dir);
|
|
@@ -461,14 +463,6 @@ try {
|
|
|
461
463
|
debug("Cache skipped (--no-cache)");
|
|
462
464
|
}
|
|
463
465
|
|
|
464
|
-
if (!segments) {
|
|
465
|
-
info("Fetching transcript...");
|
|
466
|
-
segments = lang ? await fetchTranscript(videoId, { lang }) : await fetchTranscript(videoId);
|
|
467
|
-
info(`Transcript: ${segments.length} segments`);
|
|
468
|
-
await writeCache(dir, "transcript.json", JSON.stringify(segments));
|
|
469
|
-
debug("Cache written: transcript.json");
|
|
470
|
-
}
|
|
471
|
-
|
|
472
466
|
const prompt = resolveSummarizePrompt(config) ?? "";
|
|
473
467
|
if (!prompt) {
|
|
474
468
|
console.error(
|
|
@@ -476,10 +470,80 @@ try {
|
|
|
476
470
|
);
|
|
477
471
|
exitProcess(1);
|
|
478
472
|
}
|
|
479
|
-
|
|
473
|
+
|
|
474
|
+
let structuredContent: string;
|
|
475
|
+
|
|
476
|
+
if (!segments) {
|
|
477
|
+
info("Fetching transcript...");
|
|
478
|
+
const opts = lang ? { lang, videoDetails: true as const } : { videoDetails: true as const };
|
|
479
|
+
const result = (await fetchTranscript(videoId, opts)) as {
|
|
480
|
+
videoDetails: VideoDetails;
|
|
481
|
+
segments: TranscriptSegment[];
|
|
482
|
+
};
|
|
483
|
+
segments = result.segments;
|
|
484
|
+
const infoJson = JSON.stringify({
|
|
485
|
+
title: result.videoDetails.title,
|
|
486
|
+
channel: result.videoDetails.author,
|
|
487
|
+
description: result.videoDetails.description,
|
|
488
|
+
});
|
|
489
|
+
cachedInfo = infoJson;
|
|
490
|
+
const chapterValue = formatChaptersAsJson(extractChapters(result.videoDetails.description));
|
|
491
|
+
const truncatedInfo = JSON.stringify({
|
|
492
|
+
title: result.videoDetails.title,
|
|
493
|
+
channel: result.videoDetails.author,
|
|
494
|
+
description: result.videoDetails.description.slice(0, 1000),
|
|
495
|
+
});
|
|
496
|
+
const transcriptText = toText(segments, !noDecode);
|
|
497
|
+
structuredContent = `INFO:\n${truncatedInfo}\n\nTEXT:\n${transcriptText}`;
|
|
498
|
+
|
|
499
|
+
if (dryRun) {
|
|
500
|
+
await outputText(`${prompt}\n\n${structuredContent}\n`);
|
|
501
|
+
exitProcess(0);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
info(`Transcript: ${segments.length} segments`);
|
|
505
|
+
await writeCache(dir, "transcript.json", JSON.stringify(segments));
|
|
506
|
+
await writeCache(dir, "info.json", infoJson);
|
|
507
|
+
await writeCache(dir, "chapters.json", chapterValue);
|
|
508
|
+
debug("Cache written: transcript.json, info.json, chapters.json");
|
|
509
|
+
} else {
|
|
510
|
+
let chapterValue: string;
|
|
511
|
+
if (!cachedInfo) {
|
|
512
|
+
debug("Cache missing info.json, re-fetching video details");
|
|
513
|
+
const fallbackOpts = lang
|
|
514
|
+
? { lang, videoDetails: true as const }
|
|
515
|
+
: { videoDetails: true as const };
|
|
516
|
+
const fallbackResult = (await fetchTranscript(videoId, fallbackOpts)) as {
|
|
517
|
+
videoDetails: VideoDetails;
|
|
518
|
+
segments: TranscriptSegment[];
|
|
519
|
+
};
|
|
520
|
+
cachedInfo = JSON.stringify({
|
|
521
|
+
title: fallbackResult.videoDetails.title,
|
|
522
|
+
channel: fallbackResult.videoDetails.author,
|
|
523
|
+
description: fallbackResult.videoDetails.description,
|
|
524
|
+
});
|
|
525
|
+
await writeCache(dir, "info.json", cachedInfo);
|
|
526
|
+
chapterValue = formatChaptersAsJson(
|
|
527
|
+
extractChapters(fallbackResult.videoDetails.description),
|
|
528
|
+
);
|
|
529
|
+
await writeCache(dir, "chapters.json", chapterValue);
|
|
530
|
+
debug("Cache written: info.json, chapters.json");
|
|
531
|
+
} else {
|
|
532
|
+
const cachedChapters = await readCache(dir, "chapters.json");
|
|
533
|
+
chapterValue = cachedChapters ?? "not available";
|
|
534
|
+
}
|
|
535
|
+
const transcriptText = toText(segments, !noDecode);
|
|
536
|
+
const cachedInfoObj = JSON.parse(cachedInfo);
|
|
537
|
+
const truncatedInfo = JSON.stringify({
|
|
538
|
+
title: cachedInfoObj.title,
|
|
539
|
+
channel: cachedInfoObj.channel,
|
|
540
|
+
description: cachedInfoObj.description.slice(0, 1000),
|
|
541
|
+
});
|
|
542
|
+
structuredContent = `INFO:\n${truncatedInfo}\n\nTEXT:\n${transcriptText}`;
|
|
543
|
+
}
|
|
480
544
|
|
|
481
545
|
if (dryRun) {
|
|
482
|
-
await outputText(`${prompt}\n\n${
|
|
546
|
+
await outputText(`${prompt}\n\n${structuredContent}\n`);
|
|
483
547
|
exitProcess(0);
|
|
484
548
|
}
|
|
485
549
|
|
|
@@ -488,7 +552,7 @@ try {
|
|
|
488
552
|
summary = await summarize({
|
|
489
553
|
prompt,
|
|
490
554
|
command: sumCmd,
|
|
491
|
-
transcript:
|
|
555
|
+
transcript: structuredContent,
|
|
492
556
|
cwd: dir,
|
|
493
557
|
});
|
|
494
558
|
info("Summary ready");
|