vibeaudio 0.8.1 → 0.8.2
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/package.json +1 -1
- package/src/player.js +33 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeaudio",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Procedural focus music while your AI coding tools (Claude Code, Codex, Cursor, Grok, Gemini, Copilot) think — a different arrangement per project.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"vibeaudio": "bin/vibeaudio.js",
|
package/src/player.js
CHANGED
|
@@ -294,9 +294,21 @@ function seedDir(seed) {
|
|
|
294
294
|
return path.join(CACHE_DIR, `s${seed >>> 0}`);
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
-
/**
|
|
297
|
+
/**
|
|
298
|
+
* Keep a small working set of projects cached; regenerating costs ~100ms.
|
|
299
|
+
*
|
|
300
|
+
* A directory touched within PRUNE_MIN_AGE_MS is left alone whoever owns it.
|
|
301
|
+
* Another process may be part-way through a render there - it creates the
|
|
302
|
+
* directory, then writes its .tmp into it - and deleting it underneath that
|
|
303
|
+
* write throws ENOENT on the playback path, which is the one thing audio is
|
|
304
|
+
* never allowed to do. Time is the only signal we have across processes;
|
|
305
|
+
* skipping only our own directory would not have helped the other one.
|
|
306
|
+
*/
|
|
307
|
+
const PRUNE_MIN_AGE_MS = 10_000;
|
|
308
|
+
|
|
298
309
|
function pruneSeedDirs(keep = 3) {
|
|
299
310
|
try {
|
|
311
|
+
const now = Date.now();
|
|
300
312
|
const dirs = fs
|
|
301
313
|
.readdirSync(CACHE_DIR, { withFileTypes: true })
|
|
302
314
|
.filter((e) => e.isDirectory() && /^s\d+$/.test(e.name))
|
|
@@ -307,6 +319,7 @@ function pruneSeedDirs(keep = 3) {
|
|
|
307
319
|
.sort((a, b) => b.mtime - a.mtime);
|
|
308
320
|
|
|
309
321
|
for (const stale of dirs.slice(keep)) {
|
|
322
|
+
if (now - stale.mtime < PRUNE_MIN_AGE_MS) continue;
|
|
310
323
|
fs.rmSync(stale.full, { recursive: true, force: true });
|
|
311
324
|
}
|
|
312
325
|
} catch (e) {
|
|
@@ -549,6 +562,23 @@ function writeCacheFileAtomic(filePath, buffer) {
|
|
|
549
562
|
} catch (e) {
|
|
550
563
|
fs.rmSync(tmp, { force: true });
|
|
551
564
|
|
|
565
|
+
// The seed directory can be pruned by another process between our mkdir
|
|
566
|
+
// and this write. The mtime floor in pruneSeedDirs makes that rare rather
|
|
567
|
+
// than impossible, so recreate the directory and try once more - a second
|
|
568
|
+
// ENOENT would mean a prune landed inside a window of microseconds.
|
|
569
|
+
if (e.code === "ENOENT") {
|
|
570
|
+
try {
|
|
571
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
572
|
+
fs.writeFileSync(tmp, buffer);
|
|
573
|
+
fs.renameSync(tmp, filePath);
|
|
574
|
+
return;
|
|
575
|
+
} catch (retryErr) {
|
|
576
|
+
fs.rmSync(tmp, { force: true });
|
|
577
|
+
if (fs.existsSync(filePath)) return;
|
|
578
|
+
throw retryErr;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
552
582
|
// POSIX rename replaces the destination silently. Windows does not: it
|
|
553
583
|
// fails with EPERM/EACCES when another process holds the destination
|
|
554
584
|
// open - which is precisely the concurrent case this function exists to
|
|
@@ -789,6 +819,8 @@ class AudioPlayer {
|
|
|
789
819
|
module.exports = {
|
|
790
820
|
AudioPlayer,
|
|
791
821
|
getAudioPath,
|
|
822
|
+
pruneSeedDirs,
|
|
823
|
+
writeCacheFileAtomic,
|
|
792
824
|
getChimePath,
|
|
793
825
|
clearCache,
|
|
794
826
|
detectPlayer,
|