@komponent/unifi-protect-lib
Version:
Node library for connecting to Ubiquiti Unifi Protect controllers and listen for events
87 lines • 4.19 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = __importDefault(require("axios"));
const url_1 = require("url");
class UnifiImageHandler {
constructor(log, apiClient) {
this.log = log;
this.apiClient = apiClient;
this.snapshotCache = {};
}
async getSnapshot(request) {
const params = new url_1.URLSearchParams({ force: "true" });
if (!request || !request.camera) {
return null;
}
// If we aren't connected, we're done.
if (request.camera.state !== "CONNECTED") {
this.log.error("%s: Unable to retrieve a snapshot: the camera is offline or unavailable.", request.camera.name);
return null;
}
// If we have details of the snapshot request, use it to request the right size.
if (request.width && request.height) {
params.append("width", request.width.toString());
params.append("height", request.height.toString());
}
const snapshotUrl = `${this.apiClient.camerasUrl}/${request.camera.id}/snapshot`;
// Request the image from the controller.
const response = await this.apiClient.fetch(`${snapshotUrl}?${params.toString()}`, { method: "GET", responseType: "arraybuffer" });
// Occasional snapshot failures will happen. The controller isn't always able to generate them if
// it's already generating one, or it's requested too quickly after the last one.
if (!response || response.status != 200) {
// See if we have an image cached that we can use instead.
const cachedSnapshot = this.getCachedSnapshot(request.camera.mac);
if (cachedSnapshot) {
this.log.error("%s: Unable to retrieve a snapshot. Using the most recent cached snapshot instead.", request.camera.name);
return cachedSnapshot;
}
this.log.error("%s: Unable to retrieve a snapshot. %s", request.camera.name, response
? `${response.status.toString()} - ${response.statusText}.`
: "No response from API");
return null;
}
try {
// Retrieve the image.
const buffer = Buffer.from(response.data, "base64");
this.snapshotCache[request.camera.mac] = {
image: buffer,
time: Date.now(),
};
return buffer;
}
catch (error) {
if (axios_1.default.isAxiosError(error)) {
let cachedSnapshot;
switch (error.code) {
case "ERR_STREAM_PREMATURE_CLOSE":
cachedSnapshot = this.getCachedSnapshot(request.camera.mac);
if (cachedSnapshot) {
this.log.error("%s: Unable to retrieve a snapshot. Using a cached snapshot instead.", request.camera.name);
return cachedSnapshot;
}
this.log.error("%s: Unable to retrieve a snapshot: the Protect controller closed the connection prematurely.", request.camera.name);
return null;
default:
this.log.error("%s: Unknown error: %s", request.camera.name, error.message);
return null;
}
}
this.log.error("%s: An error occurred while making a snapshot request: %s.", request.camera.name, error);
return null;
}
}
getCachedSnapshot(cameraMac) {
// If we have an image from the last few seconds, we can use it. Otherwise, we're done.
if (!this.snapshotCache[cameraMac] ||
Date.now() - this.snapshotCache[cameraMac].time > 60 * 1000) {
delete this.snapshotCache[cameraMac];
return null;
}
return this.snapshotCache[cameraMac].image;
}
}
exports.default = UnifiImageHandler;
//# sourceMappingURL=UnifiImageHandler.js.map