Module split: extract audio I/O helpers to server/audio.js
• getAudioDuration(path) — ffprobe wrapper, returns seconds | null • splitAudioFile(in, dir, secs) — ffmpeg -acodec copy chunking • downloadPodcastAudio(url, dst) — streams HTTP audio to disk Also moved fetchUrl into util.js (alongside the other stateless helpers) — it's a generic HTTP-GET-with-redirects used by RSS parsing and channel discovery, not strictly audio. server/index.js: 2758 → 2694 lines. Smoke tested: server boots; /api/license-status, /api/health, / respond. No behavior change.
This commit is contained in:
+27
-3
@@ -1,6 +1,9 @@
|
||||
// Pure helpers — no module-scoped state, no Express, no I/O effects.
|
||||
// Anything in here is safe to import from any other module without
|
||||
// worrying about ordering or initialization side effects.
|
||||
// Stateless helpers — no module-scoped state, no Express, no
|
||||
// initialization side effects. Anything in here is safe to import from
|
||||
// any other module without worrying about ordering. A few helpers do
|
||||
// I/O (fetchUrl) but only when called.
|
||||
|
||||
import https from "https";
|
||||
|
||||
// ── SSE helper ──────────────────────────────────────────────────────────────
|
||||
// Writes a single Server-Sent Events frame: `event: X\ndata: Y\n\n`.
|
||||
@@ -89,6 +92,27 @@ export function safeText(result) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// ── HTTP GET with redirect following ────────────────────────────────────────
|
||||
// Returns the response body as a string. Follows HTTP redirects up to a
|
||||
// reasonable depth (relies on https module's default behavior plus a one-
|
||||
// level recursion). Used for fetching RSS feeds, channel pages, etc.
|
||||
//
|
||||
// For binary downloads (e.g. podcast audio), use audio.downloadPodcastAudio
|
||||
// — it streams to disk instead of buffering in memory.
|
||||
export function fetchUrl(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
https.get(url, (res) => {
|
||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
return fetchUrl(res.headers.location).then(resolve, reject);
|
||||
}
|
||||
let data = "";
|
||||
res.on("data", (chunk) => (data += chunk));
|
||||
res.on("end", () => resolve(data));
|
||||
res.on("error", reject);
|
||||
}).on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
// ── Retry helper for transient Gemini API errors ────────────────────────────
|
||||
// Retries on 503/429 and on common transient network errors. Linear backoff
|
||||
// (delayMs * attempt). The optional `log` callback receives a one-line
|
||||
|
||||
Reference in New Issue
Block a user