fix(refboard): server posters respect culling budget + fix temp dir leak
1. Server posters no longer auto-load in VideoSprite constructor. loadServerPoster() is now public and called by the culling system only when within MAX_POSTER_VIDEOS budget. Videos outside the budget stay as placeholders regardless of server poster availability. 2. video-utils cleanup() now uses statSync to distinguish files from directories, unlinks files first, then rmdirs. Previously tmpDir was passed through path.dirname() which resolved to /tmp instead of the created temp directory.
This commit is contained in:
+19
-10
@@ -112,18 +112,27 @@ function extractPoster(buffer) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanup(...files) {
|
/**
|
||||||
for (const f of files) {
|
* Clean up temp files and directories.
|
||||||
try { unlinkSync(f); } catch { /* ignore */ }
|
* Pass file paths first, then the temp directory last.
|
||||||
}
|
* Files are unlinked, then the directory is removed.
|
||||||
// Try removing parent dirs (they're temp dirs we created)
|
*/
|
||||||
for (const f of files) {
|
function cleanup(...paths) {
|
||||||
|
const dirs = [];
|
||||||
|
// First pass: unlink files, collect directories
|
||||||
|
for (const p of paths) {
|
||||||
try {
|
try {
|
||||||
const dir = path.dirname(f);
|
const stat = require('fs').statSync(p);
|
||||||
if (dir.includes('refboard-')) {
|
if (stat.isDirectory()) {
|
||||||
require('fs').rmdirSync(dir);
|
dirs.push(p);
|
||||||
|
} else {
|
||||||
|
unlinkSync(p);
|
||||||
}
|
}
|
||||||
} catch { /* ignore — dir may not be empty or already removed */ }
|
} catch { /* already gone */ }
|
||||||
|
}
|
||||||
|
// Second pass: remove directories (now empty)
|
||||||
|
for (const d of dirs) {
|
||||||
|
try { require('fs').rmdirSync(d); } catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -248,9 +248,14 @@ const PixiCanvas = forwardRef<PixiCanvasHandle, PixiCanvasProps>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture posters for nearest subset (only if already initialized)
|
// Load posters for nearest subset within budget
|
||||||
for (const { item, sprite } of videoItems) {
|
for (const { item, sprite } of videoItems) {
|
||||||
if (shouldPoster.has(item.id) && sprite.isInitialized && !sprite.hasPoster) {
|
if (!shouldPoster.has(item.id) || sprite.hasPoster) continue;
|
||||||
|
if (sprite.hasServerPoster) {
|
||||||
|
// Server poster: load as image texture (no <video> needed)
|
||||||
|
sprite.loadServerPoster();
|
||||||
|
} else if (sprite.isInitialized) {
|
||||||
|
// Fallback: client-capture from video element
|
||||||
sprite.capturePoster();
|
sprite.capturePoster();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,11 +81,8 @@ export class VideoSprite extends Container {
|
|||||||
this._drawPlayIcon();
|
this._drawPlayIcon();
|
||||||
this.addChild(this._overlay);
|
this.addChild(this._overlay);
|
||||||
|
|
||||||
// If server poster available, load it immediately as an image texture
|
// NOTE: Server poster is NOT loaded here. The culling system calls
|
||||||
// This is the key optimization: video thumbnail = image, no <video> needed
|
// loadServerPoster() when this video is within the poster budget.
|
||||||
if (this.posterAssetKey && this.textures) {
|
|
||||||
this._loadServerPoster();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isPlaying(): boolean { return this._isPlaying; }
|
get isPlaying(): boolean { return this._isPlaying; }
|
||||||
@@ -95,9 +92,13 @@ export class VideoSprite extends Container {
|
|||||||
/** Expose the underlying HTMLVideoElement for external controls. Null if not initialized. */
|
/** Expose the underlying HTMLVideoElement for external controls. Null if not initialized. */
|
||||||
get videoElement(): HTMLVideoElement | null { return this.videoEl; }
|
get videoElement(): HTMLVideoElement | null { return this.videoEl; }
|
||||||
|
|
||||||
|
/** Whether this video has a server-generated poster available (not yet loaded). */
|
||||||
|
get hasServerPoster(): boolean { return !!this.posterAssetKey && !!this.textures; }
|
||||||
|
|
||||||
// ---- Tier 0.5: Server-generated poster (loaded like an image) -----------
|
// ---- Tier 0.5: Server-generated poster (loaded like an image) -----------
|
||||||
|
|
||||||
private async _loadServerPoster(): Promise<void> {
|
/** Load server poster. Called by culling system when within poster budget. */
|
||||||
|
async loadServerPoster(): Promise<void> {
|
||||||
if (!this.posterAssetKey || !this.textures || this.destroyed) return;
|
if (!this.posterAssetKey || !this.textures || this.destroyed) return;
|
||||||
try {
|
try {
|
||||||
const tex = await this.textures.load(this.posterAssetKey);
|
const tex = await this.textures.load(this.posterAssetKey);
|
||||||
|
|||||||
Reference in New Issue
Block a user