summd 0.1.6 → 0.1.8
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 +4 -2
- package/dist/url-to-md.js +40 -33
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from 'fs';
|
|
3
|
-
import { extname } from 'path';
|
|
3
|
+
import { extname, dirname, join } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
4
5
|
import { createInterface } from 'readline';
|
|
5
6
|
import { program } from 'commander';
|
|
7
|
+
const _pkg = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../package.json'), 'utf8'));
|
|
6
8
|
import { saveKey } from './config.js';
|
|
7
9
|
import * as api from './api.js';
|
|
8
10
|
import { urlToMarkdown } from './url-to-md.js';
|
|
@@ -47,7 +49,7 @@ function formatDate(iso) {
|
|
|
47
49
|
program
|
|
48
50
|
.name('summd')
|
|
49
51
|
.description('Sum to anything.')
|
|
50
|
-
.version(
|
|
52
|
+
.version(_pkg.version)
|
|
51
53
|
.option('--key <token>', 'API key for this request')
|
|
52
54
|
.option('--base <url>', 'Override API base URL');
|
|
53
55
|
// ── summd key ─────────────────────────────────────────────────────────────────
|
package/dist/url-to-md.js
CHANGED
|
@@ -78,54 +78,61 @@ async function fetchYouTubeTitle(url, videoId) {
|
|
|
78
78
|
catch { }
|
|
79
79
|
return `YouTube: ${videoId}`;
|
|
80
80
|
}
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
const
|
|
84
|
-
// yt-dlp —
|
|
81
|
+
// Browser cookie sources — tried in order, first one that succeeds is used.
|
|
82
|
+
// Cookies bypass bot-detection and geo-blocks; required on most IPs.
|
|
83
|
+
const BROWSERS = ['chrome', 'chromium', 'firefox', 'safari', 'edge'];
|
|
84
|
+
// yt-dlp — uses browser cookies by default; falls back to no-cookies for public videos.
|
|
85
|
+
// --sub-langs all: accept whatever subtitle language the video provides, no preference.
|
|
85
86
|
async function ytDlpTranscript(url, videoId) {
|
|
86
87
|
const dir = tmpdir();
|
|
87
88
|
const outTemplate = join(dir, videoId);
|
|
88
|
-
const run = (
|
|
89
|
+
const run = (cookieArgs) => execFileAsync('yt-dlp', [
|
|
89
90
|
'--write-subs',
|
|
90
91
|
'--write-auto-subs',
|
|
91
|
-
'--sub-langs',
|
|
92
|
+
'--sub-langs', 'all', // accept any language — no hardcoded preference
|
|
92
93
|
'--sub-format', 'json3',
|
|
93
94
|
'--skip-download',
|
|
94
95
|
'--quiet',
|
|
95
96
|
'--no-progress',
|
|
96
|
-
...
|
|
97
|
+
...cookieArgs,
|
|
97
98
|
'-o', outTemplate,
|
|
98
99
|
url,
|
|
99
100
|
], { timeout: 30_000 });
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
// Try with browser cookies first (handles bot-detection, auth, geo-block).
|
|
102
|
+
// Fall back to no-cookies only if no browser is available.
|
|
103
|
+
let ran = false;
|
|
104
|
+
for (const browser of BROWSERS) {
|
|
105
|
+
try {
|
|
106
|
+
await run(['--cookies-from-browser', browser]);
|
|
107
|
+
ran = true;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
const err = e;
|
|
112
|
+
if (err.code === 'ENOENT')
|
|
113
|
+
return { transcript: null, reason: 'not-installed' };
|
|
114
|
+
const stderr = err.stderr ?? '';
|
|
115
|
+
// Cookie access failed (browser not installed / locked DB) — try next browser
|
|
116
|
+
if (stderr.includes('browser') || stderr.includes('cookie') || stderr.includes('Could not find'))
|
|
117
|
+
continue;
|
|
118
|
+
// yt-dlp ran but failed for another reason (video unavailable, no captions…)
|
|
119
|
+
ran = true;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
102
122
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
for (const browser of browsers) {
|
|
114
|
-
try {
|
|
115
|
-
await run('--cookies-from-browser', browser);
|
|
116
|
-
authed = true;
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
if (!authed)
|
|
124
|
-
return { transcript: null, reason: 'no-transcript' };
|
|
123
|
+
// No browser found with usable cookies — try without (works for fully public videos)
|
|
124
|
+
if (!ran) {
|
|
125
|
+
try {
|
|
126
|
+
await run([]);
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
const err = e;
|
|
130
|
+
if (err.code === 'ENOENT')
|
|
131
|
+
return { transcript: null, reason: 'not-installed' };
|
|
132
|
+
return { transcript: null, reason: 'no-transcript' };
|
|
125
133
|
}
|
|
126
|
-
// Other errors (geo-block, video unavailable): fall through to file check
|
|
127
134
|
}
|
|
128
|
-
//
|
|
135
|
+
// Read the first valid output file; clean up all temp files regardless
|
|
129
136
|
try {
|
|
130
137
|
const files = readdirSync(dir).filter(f => f.startsWith(videoId) && f.endsWith('.json3'));
|
|
131
138
|
for (const file of files) {
|