From e6cb41168f77d7b194b814d1866e70e38dca659f Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Wed, 3 Apr 2024 08:57:20 -0700 Subject: [PATCH] snapshot: better error reporting --- plugins/snapshot/package-lock.json | 4 +- plugins/snapshot/package.json | 2 +- plugins/snapshot/src/image-converter.ts | 2 +- plugins/snapshot/src/main.ts | 2 + plugins/snapshot/src/parse-dims.ts | 57 +++++++++++++++++-------- 5 files changed, 46 insertions(+), 21 deletions(-) diff --git a/plugins/snapshot/package-lock.json b/plugins/snapshot/package-lock.json index e94d9583e5..91ec82941b 100644 --- a/plugins/snapshot/package-lock.json +++ b/plugins/snapshot/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/snapshot", - "version": "0.2.43", + "version": "0.2.44", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/snapshot", - "version": "0.2.43", + "version": "0.2.44", "dependencies": { "@types/node": "^20.10.6", "sharp": "^0.33.1", diff --git a/plugins/snapshot/package.json b/plugins/snapshot/package.json index 81ebf9b5e8..ebdd07c1ed 100644 --- a/plugins/snapshot/package.json +++ b/plugins/snapshot/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/snapshot", - "version": "0.2.43", + "version": "0.2.44", "description": "Snapshot Plugin for Scrypted", "scripts": { "scrypted-setup-project": "scrypted-setup-project", diff --git a/plugins/snapshot/src/image-converter.ts b/plugins/snapshot/src/image-converter.ts index 34a0d6f767..0b742925c7 100644 --- a/plugins/snapshot/src/image-converter.ts +++ b/plugins/snapshot/src/image-converter.ts @@ -19,6 +19,6 @@ export class ImageConverter extends ScryptedDeviceBase implements BufferConverte const op = parseImageOp(mime.parameters); const ffmpegInput = JSON.parse(data.toString()) as FFmpegInput; - return processImageOp(ffmpegInput, op, parseFloat(mime.parameters.get('time')), options?.sourceId, this.plugin.debugConsole); + return processImageOp(ffmpegInput, op, parseFloat(mime.parameters.get('time')), options?.sourceId, !!this.plugin.debugConsole); } } diff --git a/plugins/snapshot/src/main.ts b/plugins/snapshot/src/main.ts index 6a34548b6e..3a1b1bbec9 100644 --- a/plugins/snapshot/src/main.ts +++ b/plugins/snapshot/src/main.ts @@ -203,6 +203,7 @@ class SnapshotMixin extends SettingsMixinDeviceBase implements Camera { const takePicture = await preparePrebufferSnapshot() if (!takePicture) throw e; + this.console.error('Snapshot failed, falling back to prebuffer', e); return takePicture(); } @@ -505,6 +506,7 @@ class SnapshotMixin extends SettingsMixinDeviceBase implements Camera { return this.progressPicture.promise; } else { + this.console.error('Snapshot failed', e); this.errorPicture = singletonPromise(this.errorPicture, () => this.createTextErrorImage('Snapshot Failed')); return this.errorPicture.promise; diff --git a/plugins/snapshot/src/parse-dims.ts b/plugins/snapshot/src/parse-dims.ts index 9f5a2fac82..f67b92c5a2 100644 --- a/plugins/snapshot/src/parse-dims.ts +++ b/plugins/snapshot/src/parse-dims.ts @@ -1,4 +1,6 @@ import sdk, { FFmpegInput, RecordingStreamThumbnailOptions } from '@scrypted/sdk'; +import { Console } from 'console'; +import { PassThrough } from 'stream'; import url from 'url'; import type { MIMETypeParameters } from 'whatwg-mimetype'; import { FFmpegImageFilterOptions, ffmpegFilterImage, ffmpegFilterImageBuffer } from './ffmpeg-image-filter'; @@ -71,7 +73,7 @@ export function toImageOp(options: RecordingStreamThumbnailOptions) { return ret; } -export async function processImageOp(input: string | FFmpegInput | Buffer, op: ImageOp, time: number, sourceId: string, debugConsole: Console): Promise { +export async function processImageOp(input: string | FFmpegInput | Buffer, op: ImageOp, time: number, sourceId: string, debug?: boolean): Promise { const { crop, resize } = op; const { width, height, fractional } = resize || {}; const { left, top, right, bottom, fractional: cropFractional } = crop || {}; @@ -120,8 +122,19 @@ export async function processImageOp(input: string | FFmpegInput | Buffer, op: I } } + const out = new PassThrough(); + let console = new Console(out, out); + const printConsole = () => { + if (!console) + return; + console = undefined; + const data = out.read().toString(); + const deviceConsole = sdk.deviceManager.getMixinConsole(sourceId); + deviceConsole.log(data); + } + const ffmpegOpts: FFmpegImageFilterOptions = { - console: debugConsole, + console, ffmpegPath: await sdk.mediaManager.getFFmpegPath(), resize: width === undefined && height === undefined ? undefined @@ -143,22 +156,32 @@ export async function processImageOp(input: string | FFmpegInput | Buffer, op: I time, }; - if (Buffer.isBuffer(input)) { - return ffmpegFilterImageBuffer(input, ffmpegOpts); - } + try { + if (Buffer.isBuffer(input)) { + return await ffmpegFilterImageBuffer(input, ffmpegOpts); + } - const ffmpegInput: FFmpegInput = typeof input !== 'string' - ? input - : { - inputArguments: [ - '-i', input, - ] - }; + const ffmpegInput: FFmpegInput = typeof input !== 'string' + ? input + : { + inputArguments: [ + '-i', input, + ] + }; - const args = [ - ...ffmpegInput.inputArguments, - ...(ffmpegInput.h264EncoderArguments || []), - ]; + const args = [ + ...ffmpegInput.inputArguments, + ...(ffmpegInput.h264EncoderArguments || []), + ]; - return ffmpegFilterImage(args, ffmpegOpts); + return await ffmpegFilterImage(args, ffmpegOpts); + } + catch (e) { + printConsole(); + throw e; + } + finally { + if (debug) + printConsole(); + } }