mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-09-24 04:53:31 +08:00
新增图片操作分类,支持调整大小、旋转、添加水印、裁剪、格式转换、图片信息、png2ico
This commit is contained in:
@@ -8,6 +8,7 @@ const quickcomposer = {
|
||||
math: require("./quickcomposer/math"),
|
||||
ui: require("./quickcomposer/ui"),
|
||||
audio: require("./quickcomposer/audio"),
|
||||
image: require("./quickcomposer/image"),
|
||||
};
|
||||
|
||||
module.exports = quickcomposer;
|
||||
|
492
plugin/lib/quickcomposer/image/image.js
Normal file
492
plugin/lib/quickcomposer/image/image.js
Normal file
@@ -0,0 +1,492 @@
|
||||
const fs = require("fs");
|
||||
const exif = require("exif-reader");
|
||||
|
||||
/**
|
||||
* 加载图片
|
||||
* @param {string} file 图片文件路径
|
||||
* @returns {Promise<HTMLImageElement>} 图片元素
|
||||
*/
|
||||
async function loadImage(file) {
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(`图片文件不存在: ${file}`);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = reject;
|
||||
img.src = `file://${file}`;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取EXIF信息
|
||||
* @param {string} file 图片文件路径
|
||||
* @returns {Promise<Object>} EXIF信息
|
||||
*/
|
||||
async function readExif(file) {
|
||||
try {
|
||||
const buffer = fs.readFileSync(file);
|
||||
|
||||
// 检查是否是JPEG文件
|
||||
if (buffer[0] !== 0xff || buffer[1] !== 0xd8) {
|
||||
console.warn("不是JPEG文件");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 查找APP1段
|
||||
let offset = 2;
|
||||
while (offset < buffer.length) {
|
||||
if (buffer[offset] === 0xff && buffer[offset + 1] === 0xe1) {
|
||||
// 获取段长度
|
||||
const segmentLength = buffer[offset + 2] * 256 + buffer[offset + 3];
|
||||
|
||||
// 提取EXIF数据
|
||||
const exifData = buffer.slice(offset + 4, offset + 2 + segmentLength);
|
||||
|
||||
// 检查是否是EXIF数据
|
||||
if (exifData.slice(0, 6).toString() === "Exif\0\0") {
|
||||
try {
|
||||
// 提取实际的EXIF数据(跳过Exif\0\0)
|
||||
const metadata = exif(exifData.slice(6));
|
||||
return metadata;
|
||||
} catch (e) {
|
||||
console.warn("解析EXIF数据失败:", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
|
||||
console.warn("未找到EXIF数据");
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.warn("读取EXIF信息失败:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片颜色信息
|
||||
* @param {HTMLImageElement} img 图片元素
|
||||
* @returns {Object} 颜色信息
|
||||
*/
|
||||
function getColorInfo(img) {
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const data = imageData.data;
|
||||
let r = 0,
|
||||
g = 0,
|
||||
b = 0,
|
||||
a = 0;
|
||||
|
||||
// 计算平均颜色
|
||||
for (let i = 0; i < data.length; i += 4) {
|
||||
r += data[i];
|
||||
g += data[i + 1];
|
||||
b += data[i + 2];
|
||||
a += data[i + 3];
|
||||
}
|
||||
|
||||
const pixels = data.length / 4;
|
||||
return {
|
||||
averageColor: {
|
||||
r: Math.round(r / pixels),
|
||||
g: Math.round(g / pixels),
|
||||
b: Math.round(b / pixels),
|
||||
a: Math.round(a / pixels) / 255,
|
||||
},
|
||||
isTransparent: Math.round(a / pixels) < 255,
|
||||
hasAlphaChannel: true, // Canvas总是包含alpha通道
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析图片信息
|
||||
* @param {string} file 图片文件路径
|
||||
* @returns {Promise<Object>} 图片信息
|
||||
*/
|
||||
async function analyze(file) {
|
||||
const img = await loadImage(file);
|
||||
const stats = fs.statSync(file);
|
||||
const ext = file.split(".").pop().toLowerCase();
|
||||
const exifData = await readExif(file);
|
||||
const colorInfo = getColorInfo(img);
|
||||
|
||||
// 计算图片大小的可读格式
|
||||
const formatSize = (bytes) => {
|
||||
if (bytes < 1024) return bytes + " B";
|
||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + " KB";
|
||||
return (bytes / (1024 * 1024)).toFixed(2) + " MB";
|
||||
};
|
||||
|
||||
// 获取图片类型
|
||||
const getImageType = (ext) => {
|
||||
const types = {
|
||||
jpg: "JPEG图片",
|
||||
jpeg: "JPEG图片",
|
||||
png: "PNG图片",
|
||||
webp: "WebP图片",
|
||||
gif: "GIF图片",
|
||||
bmp: "BMP图片",
|
||||
};
|
||||
return types[ext] || "未知类型";
|
||||
};
|
||||
|
||||
// 格式化EXIF信息
|
||||
const formatExif = (exif) => {
|
||||
if (!exif) return null;
|
||||
return {
|
||||
相机信息: {
|
||||
制造商: exif.Image?.Make,
|
||||
型号: exif.Image?.Model,
|
||||
软件: exif.Image?.Software,
|
||||
方向: exif.Image?.Orientation,
|
||||
分辨率: {
|
||||
X: exif.Image?.XResolution,
|
||||
Y: exif.Image?.YResolution,
|
||||
单位: exif.Image?.ResolutionUnit,
|
||||
},
|
||||
},
|
||||
拍摄信息: {
|
||||
拍摄时间: exif.Photo?.DateTimeOriginal,
|
||||
曝光时间: exif.Photo?.ExposureTime,
|
||||
光圈值: exif.Photo?.FNumber,
|
||||
ISO感光度: exif.Photo?.ISOSpeedRatings,
|
||||
焦距: exif.Photo?.FocalLength,
|
||||
焦距35mm: exif.Photo?.FocalLengthIn35mmFilm,
|
||||
闪光灯: exif.Photo?.Flash,
|
||||
白平衡: exif.Photo?.WhiteBalance,
|
||||
曝光程序: exif.Photo?.ExposureProgram,
|
||||
曝光补偿: exif.Photo?.ExposureBiasValue,
|
||||
测光模式: exif.Photo?.MeteringMode,
|
||||
亮度值: exif.Photo?.BrightnessValue,
|
||||
场景类型: exif.Photo?.SceneCaptureType,
|
||||
镜头信息: {
|
||||
制造商: exif.Photo?.LensMake,
|
||||
型号: exif.Photo?.LensModel,
|
||||
},
|
||||
},
|
||||
GPS信息: exif.GPSInfo
|
||||
? {
|
||||
纬度: {
|
||||
参考: exif.GPSInfo.GPSLatitudeRef,
|
||||
值: exif.GPSInfo.GPSLatitude,
|
||||
},
|
||||
经度: {
|
||||
参考: exif.GPSInfo.GPSLongitudeRef,
|
||||
值: exif.GPSInfo.GPSLongitude,
|
||||
},
|
||||
海拔: {
|
||||
参考: exif.GPSInfo.GPSAltitudeRef,
|
||||
值: exif.GPSInfo.GPSAltitude,
|
||||
},
|
||||
时间戳: exif.GPSInfo.GPSTimeStamp,
|
||||
日期戳: exif.GPSInfo.GPSDateStamp,
|
||||
方向: {
|
||||
参考: exif.GPSInfo.GPSImgDirectionRef,
|
||||
值: exif.GPSInfo.GPSImgDirection,
|
||||
},
|
||||
}
|
||||
: null,
|
||||
缩略图信息: exif.Thumbnail
|
||||
? {
|
||||
宽度: exif.Thumbnail.ImageWidth,
|
||||
高度: exif.Thumbnail.ImageLength,
|
||||
压缩: exif.Thumbnail.Compression,
|
||||
方向: exif.Thumbnail.Orientation,
|
||||
分辨率: {
|
||||
X: exif.Thumbnail.XResolution,
|
||||
Y: exif.Thumbnail.YResolution,
|
||||
单位: exif.Thumbnail.ResolutionUnit,
|
||||
},
|
||||
}
|
||||
: null,
|
||||
其他信息: {
|
||||
描述: exif.Image?.ImageDescription,
|
||||
作者: exif.Image?.Artist,
|
||||
版权: exif.Image?.Copyright,
|
||||
创建时间: exif.Photo?.DateTimeDigitized,
|
||||
修改时间: exif.Image?.DateTime,
|
||||
色彩空间: exif.Photo?.ColorSpace,
|
||||
图像处理: exif.Photo?.CustomRendered,
|
||||
对比度: exif.Photo?.Contrast,
|
||||
饱和度: exif.Photo?.Saturation,
|
||||
锐度: exif.Photo?.Sharpness,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
// 基本信息
|
||||
width: img.width, // 宽度(像素)
|
||||
height: img.height, // 高度(像素)
|
||||
aspectRatio: img.width / img.height, // 宽高比
|
||||
resolution: img.width * img.height, // 分辨率(总像素)
|
||||
|
||||
// 文件信息
|
||||
type: getImageType(ext), // 图片类型
|
||||
format: ext.toUpperCase(), // 文件格式
|
||||
size: formatSize(stats.size), // 文件大小
|
||||
bytes: stats.size, // 字节数
|
||||
|
||||
// 时间信息
|
||||
createTime: stats.birthtime, // 创建时间
|
||||
modifyTime: stats.mtime, // 修改时间
|
||||
accessTime: stats.atime, // 访问时间
|
||||
|
||||
// 路径信息
|
||||
path: file, // 完整路径
|
||||
filename: file.split("/").pop(), // 文件名
|
||||
|
||||
// 颜色信息
|
||||
colorInfo: {
|
||||
averageColor: colorInfo.averageColor, // 平均颜色
|
||||
isTransparent: colorInfo.isTransparent, // 是否包含透明
|
||||
hasAlphaChannel: colorInfo.hasAlphaChannel, // 是否有Alpha通道
|
||||
},
|
||||
|
||||
// EXIF信息
|
||||
exif: formatExif(exifData),
|
||||
rawExif: exifData, // 原始EXIF数据
|
||||
|
||||
// 其他信息
|
||||
naturalWidth: img.naturalWidth, // 原始宽度
|
||||
naturalHeight: img.naturalHeight, // 原始高度
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整图片大小
|
||||
* @param {string} inputFile 输入文件路径
|
||||
* @param {string} outputFile 输出文件路径
|
||||
* @param {number} width 宽度
|
||||
* @param {number} height 高度
|
||||
* @param {boolean} keepAspectRatio 保持宽高比
|
||||
* @param {number} quality 图片质量 (0-1)
|
||||
*/
|
||||
async function resize(
|
||||
inputFile,
|
||||
outputFile,
|
||||
width = null,
|
||||
height = null,
|
||||
keepAspectRatio = true,
|
||||
quality = 0.92
|
||||
) {
|
||||
const img = await loadImage(inputFile);
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
if (keepAspectRatio) {
|
||||
if (width && !height) {
|
||||
height = width * (img.height / img.width);
|
||||
} else if (height && !width) {
|
||||
width = height * (img.width / img.height);
|
||||
}
|
||||
}
|
||||
|
||||
canvas.width = width || img.width;
|
||||
canvas.height = height || img.height;
|
||||
|
||||
// 绘制调整大小后的图片
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
// 获取图片数据
|
||||
const format = outputFile.split(".").pop() || "jpeg";
|
||||
const dataURL = canvas.toDataURL(`image/${format}`, quality);
|
||||
|
||||
// 保存文件
|
||||
const base64Data = dataURL.replace(/^data:image\/\w+;base64,/, "");
|
||||
fs.writeFileSync(outputFile, Buffer.from(base64Data, "base64"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 旋转图片
|
||||
* @param {string} inputFile 输入文件路径
|
||||
* @param {string} outputFile 输出文件路径
|
||||
* @param {number} angle 旋转角度
|
||||
* @param {number} quality 图片质量
|
||||
*/
|
||||
async function rotate(inputFile, outputFile, angle = 90, quality = 0.92) {
|
||||
const img = await loadImage(inputFile);
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const angleRad = (angle * Math.PI) / 180;
|
||||
const sin = Math.abs(Math.sin(angleRad));
|
||||
const cos = Math.abs(Math.cos(angleRad));
|
||||
|
||||
// 计算旋转后的画布大小
|
||||
canvas.width = img.width * cos + img.height * sin;
|
||||
canvas.height = img.width * sin + img.height * cos;
|
||||
|
||||
// 移动到画布中心并旋转
|
||||
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||
ctx.rotate(angleRad);
|
||||
ctx.drawImage(img, -img.width / 2, -img.height / 2);
|
||||
|
||||
// 保存文件
|
||||
const format = outputFile.split(".").pop() || "jpeg";
|
||||
const dataURL = canvas.toDataURL(`image/${format}`, quality);
|
||||
|
||||
const base64Data = dataURL.replace(/^data:image\/\w+;base64,/, "");
|
||||
fs.writeFileSync(outputFile, Buffer.from(base64Data, "base64"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 裁剪图片
|
||||
* @param {string} inputFile 输入文件路径
|
||||
* @param {string} outputFile 输出文件路径
|
||||
* @param {number} x 起始X坐标
|
||||
* @param {number} y 起始Y坐标
|
||||
* @param {number} width 裁剪宽度
|
||||
* @param {number} height 裁剪高度
|
||||
* @param {number} quality 图片质量
|
||||
*/
|
||||
async function crop(
|
||||
inputFile,
|
||||
outputFile,
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = null,
|
||||
height = null,
|
||||
quality = 0.92
|
||||
) {
|
||||
const img = await loadImage(inputFile);
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
width = width || img.width;
|
||||
height = height || img.height;
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
// 绘制裁剪区域
|
||||
ctx.drawImage(img, x, y, width, height, 0, 0, width, height);
|
||||
|
||||
// 保存文件
|
||||
const format = outputFile.split(".").pop() || "jpeg";
|
||||
const dataURL = canvas.toDataURL(`image/${format}`, quality);
|
||||
|
||||
const base64Data = dataURL.replace(/^data:image\/\w+;base64,/, "");
|
||||
fs.writeFileSync(outputFile, Buffer.from(base64Data, "base64"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加水印
|
||||
* @param {string} inputFile 输入文件路径
|
||||
* @param {string} outputFile 输出文件路径
|
||||
* @param {string} text 水印文字
|
||||
* @param {string} font 字体设置
|
||||
* @param {string} color 文字颜色
|
||||
* @param {string} position 位置(topLeft/topRight/bottomLeft/bottomRight/center)
|
||||
* @param {number} margin 边距
|
||||
* @param {number} opacity 不透明度
|
||||
* @param {number} quality 图片质量
|
||||
*/
|
||||
async function watermark(
|
||||
inputFile,
|
||||
outputFile,
|
||||
text = "水印文字",
|
||||
font = "24px Arial",
|
||||
color = "rgba(255, 255, 255, 0.5)",
|
||||
position = "bottomRight",
|
||||
margin = 20,
|
||||
opacity = 0.5,
|
||||
quality = 0.92
|
||||
) {
|
||||
const img = await loadImage(inputFile);
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
|
||||
// 绘制原图
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
// 设置水印样式
|
||||
ctx.font = font;
|
||||
ctx.fillStyle = color;
|
||||
ctx.globalAlpha = opacity;
|
||||
|
||||
const metrics = ctx.measureText(text);
|
||||
const textWidth = metrics.width;
|
||||
const textHeight = parseInt(ctx.font); // 近似值
|
||||
|
||||
// 计算水印位置
|
||||
let x, y;
|
||||
switch (position) {
|
||||
case "topLeft":
|
||||
x = margin;
|
||||
y = margin + textHeight;
|
||||
break;
|
||||
case "topRight":
|
||||
x = canvas.width - textWidth - margin;
|
||||
y = margin + textHeight;
|
||||
break;
|
||||
case "bottomLeft":
|
||||
x = margin;
|
||||
y = canvas.height - margin;
|
||||
break;
|
||||
case "bottomRight":
|
||||
x = canvas.width - textWidth - margin;
|
||||
y = canvas.height - margin;
|
||||
break;
|
||||
case "center":
|
||||
default:
|
||||
x = (canvas.width - textWidth) / 2;
|
||||
y = (canvas.height + textHeight) / 2;
|
||||
}
|
||||
|
||||
// 绘制水印
|
||||
ctx.fillText(text, x, y);
|
||||
|
||||
// 保存文件
|
||||
const format = outputFile.split(".").pop() || "jpeg";
|
||||
const dataURL = canvas.toDataURL(`image/${format}`, quality);
|
||||
|
||||
const base64Data = dataURL.replace(/^data:image\/\w+;base64,/, "");
|
||||
fs.writeFileSync(outputFile, Buffer.from(base64Data, "base64"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换图片格式
|
||||
* @param {string} inputFile 输入文件路径
|
||||
* @param {string} outputFile 输出文件路径
|
||||
* @param {string} format 输出格式
|
||||
* @param {number} quality 图片质量
|
||||
*/
|
||||
async function convert(inputFile, outputFile, format = "jpeg", quality = 0.92) {
|
||||
const img = await loadImage(inputFile);
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
|
||||
// 绘制图片
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
// 保存为新格式
|
||||
const dataURL = canvas.toDataURL(`image/${format}`, quality);
|
||||
|
||||
const base64Data = dataURL.replace(/^data:image\/\w+;base64,/, "");
|
||||
fs.writeFileSync(outputFile, Buffer.from(base64Data, "base64"));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
analyze,
|
||||
resize,
|
||||
rotate,
|
||||
crop,
|
||||
watermark,
|
||||
convert,
|
||||
};
|
7
plugin/lib/quickcomposer/image/index.js
Normal file
7
plugin/lib/quickcomposer/image/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const image = require("./image");
|
||||
const png2icon = require("./png2icon");
|
||||
|
||||
module.exports = {
|
||||
...image,
|
||||
...png2icon,
|
||||
};
|
36
plugin/lib/quickcomposer/image/png2icon.js
Normal file
36
plugin/lib/quickcomposer/image/png2icon.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const png2icons = require("png2icons");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const pngToIcon = (input, outputDir, type = "ico") => {
|
||||
if (input instanceof Array) {
|
||||
input.forEach((input) => {
|
||||
pngToIcon(input, outputDir, type);
|
||||
});
|
||||
return;
|
||||
}
|
||||
let icon, outputFile, basename;
|
||||
console.log(input);
|
||||
if (input.startsWith("data:image/png;base64,")) {
|
||||
input = Buffer.from(input.split(",")[1], "base64");
|
||||
basename = new Date().getTime().toString();
|
||||
} else {
|
||||
basename = path.basename(input, ".png");
|
||||
input = fs.readFileSync(input);
|
||||
}
|
||||
if (type == "ico") {
|
||||
icon = png2icons.createICO(input, png2icons.BICUBIC, 0, false);
|
||||
outputFile = path.join(outputDir, basename + ".ico");
|
||||
} else {
|
||||
icon = png2icons.createICNS(input, png2icons.BILINEAR, 0);
|
||||
outputFile = path.join(outputDir, basename + ".icns");
|
||||
}
|
||||
if (!icon) return;
|
||||
fs.writeFile(outputFile, icon, (err) => {
|
||||
if (err) throw err;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
pngToIcon,
|
||||
};
|
27
plugin/package-lock.json
generated
27
plugin/package-lock.json
generated
@@ -7,8 +7,10 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"crypto-js": "^4.2.0",
|
||||
"exif-reader": "^2.0.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"node-forge": "^1.3.1",
|
||||
"png2icons": "^2.0.1",
|
||||
"ses": "^1.10.0",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"tree-kill": "^1.2.2"
|
||||
@@ -64,6 +66,12 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/exif-reader": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/exif-reader/-/exif-reader-2.0.1.tgz",
|
||||
"integrity": "sha512-gCQ/86RiAWSjeSlalj1G99IC6XnxbwkvB91HLqhh8somj/YBtC/2xuplvyjDjlfO7NsmYREPPElu/Syuy/H52g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
@@ -145,6 +153,15 @@
|
||||
"node": ">= 6.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/png2icons": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/png2icons/-/png2icons-2.0.1.tgz",
|
||||
"integrity": "sha512-GDEQJr8OG4e6JMp7mABtXFSEpgJa1CCpbQiAR+EjhkHJHnUL9zPPtbOrjsMD8gUbikgv3j7x404b0YJsV3aVFA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"png2icons": "png2icons-cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
@@ -222,6 +239,11 @@
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
||||
},
|
||||
"exif-reader": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/exif-reader/-/exif-reader-2.0.1.tgz",
|
||||
"integrity": "sha512-gCQ/86RiAWSjeSlalj1G99IC6XnxbwkvB91HLqhh8somj/YBtC/2xuplvyjDjlfO7NsmYREPPElu/Syuy/H52g=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
@@ -268,6 +290,11 @@
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="
|
||||
},
|
||||
"png2icons": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/png2icons/-/png2icons-2.0.1.tgz",
|
||||
"integrity": "sha512-GDEQJr8OG4e6JMp7mABtXFSEpgJa1CCpbQiAR+EjhkHJHnUL9zPPtbOrjsMD8gUbikgv3j7x404b0YJsV3aVFA=="
|
||||
},
|
||||
"proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
|
@@ -2,8 +2,10 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"crypto-js": "^4.2.0",
|
||||
"exif-reader": "^2.0.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"node-forge": "^1.3.1",
|
||||
"png2icons": "^2.0.1",
|
||||
"ses": "^1.10.0",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"tree-kill": "^1.2.2"
|
||||
|
Reference in New Issue
Block a user