Spaces:
Build error
Build error
File size: 6,765 Bytes
f316cce | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | "use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AltTextService = void 0;
const civkit_1 = require("civkit");
const tsyringe_1 = require("tsyringe");
const logger_1 = require("./logger");
const canvas_1 = require("./canvas");
const common_iminterrogate_1 = require("../shared/services/common-iminterrogate");
const img_alt_1 = require("../db/img-alt");
const async_context_1 = require("./async-context");
const md5Hasher = new civkit_1.HashManager('md5', 'hex');
let AltTextService = class AltTextService extends civkit_1.AsyncService {
constructor(globalLogger, imageInterrogator, canvasService, asyncLocalContext) {
super(...arguments);
this.globalLogger = globalLogger;
this.imageInterrogator = imageInterrogator;
this.canvasService = canvasService;
this.asyncLocalContext = asyncLocalContext;
this.altsToIgnore = 'image,img,photo,picture,pic,alt,figure,fig'.split(',');
this.logger = this.globalLogger.child({ service: this.constructor.name });
}
async init() {
await this.dependencyReady();
this.emit('ready');
}
async caption(url) {
try {
const img = await this.canvasService.loadImage(url);
const contentTypeHint = Reflect.get(img, 'contentType');
if (Math.min(img.naturalHeight, img.naturalWidth) <= 1) {
return `A ${img.naturalWidth}x${img.naturalHeight} image, likely be a tacker probe`;
}
if (Math.min(img.naturalHeight, img.naturalWidth) < 64) {
return `A ${img.naturalWidth}x${img.naturalHeight} small image, likely a logo, icon or avatar`;
}
const resized = this.canvasService.fitImageToSquareBox(img, 1024);
const exported = await this.canvasService.canvasToBuffer(resized, 'image/png');
const svgHint = contentTypeHint.includes('svg') ? `Beware this image is a SVG rendered on a gray background, the gray background is not part of the image.\n\n` : '';
const svgSystemHint = contentTypeHint.includes('svg') ? ` Sometimes the system renders SVG on a gray background. When this happens, you must not include the gray background in the description.` : '';
const r = await this.imageInterrogator.interrogate('vertex-gemini-2.0-flash', {
image: exported,
prompt: `${svgHint}Give a concise image caption descriptive sentence in third person. Start directly with the description.`,
system: `You are BLIP2, an image caption model. You will generate Alt Text (in web pages) for any image for a11y purposes. You must not start with "This image is sth...", instead, start direly with "sth..."${svgSystemHint}`,
});
return r.replaceAll(/[\n\"]|(\.\s*$)/g, '').trim();
}
catch (err) {
throw new civkit_1.AssertionFailureError({ message: `Could not generate alt text for url ${url}`, cause: err });
}
}
async getAltText(imgBrief) {
if (!imgBrief.src) {
return undefined;
}
if (imgBrief.alt && !this.altsToIgnore.includes(imgBrief.alt.trim().toLowerCase())) {
return imgBrief.alt;
}
const digest = md5Hasher.hash(imgBrief.src);
const shortDigest = Buffer.from(digest, 'hex').toString('base64url');
let dims = [];
do {
if (imgBrief.loaded) {
if (imgBrief.naturalWidth && imgBrief.naturalHeight) {
if (Math.min(imgBrief.naturalWidth, imgBrief.naturalHeight) < 64) {
dims = [imgBrief.naturalWidth, imgBrief.naturalHeight];
break;
}
}
}
if (imgBrief.width && imgBrief.height) {
if (Math.min(imgBrief.width, imgBrief.height) < 64) {
dims = [imgBrief.width, imgBrief.height];
break;
}
}
} while (false);
if (Math.min(...dims) <= 1) {
return `A ${dims[0]}x${dims[1]} image, likely be a tacker probe`;
}
if (Math.min(...dims) < 64) {
return `A ${dims[0]}x${dims[1]} small image, likely a logo, icon or avatar`;
}
const existing = await img_alt_1.ImgAlt.fromFirestore(shortDigest);
if (existing) {
return existing.generatedAlt || existing.originalAlt || '';
}
let generatedCaption = '';
try {
generatedCaption = await this.caption(imgBrief.src);
}
catch (err) {
this.logger.warn(`Unable to generate alt text for ${imgBrief.src}`, { err });
}
if (this.asyncLocalContext.ctx.DNT) {
// Don't cache alt text if DNT is set
return generatedCaption;
}
// Don't try again until the next day
const expireMixin = generatedCaption ? {} : { expireAt: new Date(Date.now() + 1000 * 3600 * 24) };
await img_alt_1.ImgAlt.COLLECTION.doc(shortDigest).set({
_id: shortDigest,
src: imgBrief.src || '',
width: imgBrief.naturalWidth || 0,
height: imgBrief.naturalHeight || 0,
urlDigest: digest,
originalAlt: imgBrief.alt || '',
generatedAlt: generatedCaption || '',
createdAt: new Date(),
...expireMixin
}, { merge: true });
return generatedCaption;
}
};
exports.AltTextService = AltTextService;
exports.AltTextService = AltTextService = __decorate([
(0, tsyringe_1.singleton)(),
__metadata("design:paramtypes", [logger_1.GlobalLogger, typeof (_a = typeof common_iminterrogate_1.ImageInterrogationManager !== "undefined" && common_iminterrogate_1.ImageInterrogationManager) === "function" ? _a : Object, canvas_1.CanvasService,
async_context_1.AsyncLocalContext])
], AltTextService);
;
//# sourceMappingURL=alt-text.js.map |