Merge pull request #102 from ente-io/ffmpeg-static
add support to run FFmpeg locally
This commit is contained in:
commit
d6d5d961be
|
@ -39,6 +39,11 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"asarUnpack": [
|
||||
"node_modules/ffmpeg-static/bin/${os}/${arch}/ffmpeg",
|
||||
"node_modules/ffmpeg-static/index.js",
|
||||
"node_modules/ffmpeg-static/package.json"
|
||||
],
|
||||
"files": [
|
||||
"app/**/*",
|
||||
{
|
||||
|
@ -69,6 +74,7 @@
|
|||
"devDependencies": {
|
||||
"@sentry/cli": "^1.68.0",
|
||||
"@types/auto-launch": "^5.0.2",
|
||||
"@types/ffmpeg-static": "^3.0.1",
|
||||
"@types/get-folder-size": "^2.0.0",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/semver-compare": "^1.0.1",
|
||||
|
@ -91,12 +97,14 @@
|
|||
"@sentry/electron": "^2.5.1",
|
||||
"@types/node": "^14.14.37",
|
||||
"@types/promise-fs": "^2.1.1",
|
||||
"any-shell-escape": "^0.1.1",
|
||||
"auto-launch": "^5.0.5",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron-log": "^4.3.5",
|
||||
"electron-reload": "^2.0.0-alpha.1",
|
||||
"electron-store": "^8.0.1",
|
||||
"electron-updater": "^4.3.8",
|
||||
"ffmpeg-static": "^5.1.0",
|
||||
"get-folder-size": "^2.0.1",
|
||||
"next-electron-server": "file:./thirdparty/next-electron-server",
|
||||
"node-fetch": "^2.6.7",
|
||||
|
|
40
src/api/ffmpeg.ts
Normal file
40
src/api/ffmpeg.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { ipcRenderer } from 'electron';
|
||||
import { logError } from '../services/logging';
|
||||
import { ElectronFile } from '../types';
|
||||
|
||||
export async function runFFmpegCmd(
|
||||
cmd: string[],
|
||||
inputFile: File | ElectronFile,
|
||||
outputFileName: string
|
||||
) {
|
||||
let inputFilePath = null;
|
||||
let createdTempInputFile = null;
|
||||
try {
|
||||
if (!inputFile.path) {
|
||||
const inputFileData = new Uint8Array(await inputFile.arrayBuffer());
|
||||
inputFilePath = await ipcRenderer.invoke(
|
||||
'write-temp-file',
|
||||
inputFileData,
|
||||
inputFile.name
|
||||
);
|
||||
createdTempInputFile = true;
|
||||
} else {
|
||||
inputFilePath = inputFile.path;
|
||||
}
|
||||
const outputFileData = await ipcRenderer.invoke(
|
||||
'run-ffmpeg-cmd',
|
||||
cmd,
|
||||
inputFilePath,
|
||||
outputFileName
|
||||
);
|
||||
return new File([outputFileData], outputFileName);
|
||||
} finally {
|
||||
if (createdTempInputFile) {
|
||||
try {
|
||||
await ipcRenderer.invoke('remove-temp-file', inputFilePath);
|
||||
} catch (e) {
|
||||
logError(e, 'failed to deleteTempFile');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,6 +50,7 @@ import { fixHotReloadNext12 } from './utils/preload';
|
|||
import { isFolder, getDirFiles } from './api/fs';
|
||||
import { convertHEIC } from './api/heicConvert';
|
||||
import { setupLogging } from './utils/logging';
|
||||
import { runFFmpegCmd } from './api/ffmpeg';
|
||||
|
||||
fixHotReloadNext12();
|
||||
setupLogging();
|
||||
|
@ -100,4 +101,5 @@ windowObject['ElectronAPIs'] = {
|
|||
skipAppVersion,
|
||||
getSentryUserID,
|
||||
getAppVersion,
|
||||
runFFmpegCmd,
|
||||
};
|
||||
|
|
70
src/services/ffmpeg.ts
Normal file
70
src/services/ffmpeg.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
import pathToFfmpeg from 'ffmpeg-static';
|
||||
const shellescape = require('any-shell-escape');
|
||||
import util from 'util';
|
||||
import log from 'electron-log';
|
||||
import { readFile, rmSync, writeFile } from 'promise-fs';
|
||||
import { logErrorSentry } from './sentry';
|
||||
import { generateTempFilePath, getTempDirPath } from '../utils/temp';
|
||||
|
||||
const execAsync = util.promisify(require('child_process').exec);
|
||||
|
||||
export const INPUT_PATH_PLACEHOLDER = 'INPUT';
|
||||
export const FFMPEG_PLACEHOLDER = 'FFMPEG';
|
||||
export const OUTPUT_PATH_PLACEHOLDER = 'OUTPUT';
|
||||
|
||||
function getFFmpegStaticPath() {
|
||||
return pathToFfmpeg.replace('app.asar', 'app.asar.unpacked');
|
||||
}
|
||||
|
||||
export async function runFFmpegCmd(
|
||||
cmd: string[],
|
||||
inputFilePath: string,
|
||||
outputFileName: string
|
||||
) {
|
||||
let tempOutputFilePath: string;
|
||||
try {
|
||||
tempOutputFilePath = await generateTempFilePath(outputFileName);
|
||||
|
||||
cmd = cmd.map((cmdPart) => {
|
||||
if (cmdPart === FFMPEG_PLACEHOLDER) {
|
||||
return getFFmpegStaticPath();
|
||||
} else if (cmdPart === INPUT_PATH_PLACEHOLDER) {
|
||||
return inputFilePath;
|
||||
} else if (cmdPart === OUTPUT_PATH_PLACEHOLDER) {
|
||||
return tempOutputFilePath;
|
||||
} else {
|
||||
return cmdPart;
|
||||
}
|
||||
});
|
||||
cmd = shellescape(cmd);
|
||||
log.info('cmd', cmd);
|
||||
await execAsync(cmd);
|
||||
return new Uint8Array(await readFile(tempOutputFilePath));
|
||||
} catch (e) {
|
||||
logErrorSentry(e, 'ffmpeg run command error');
|
||||
throw e;
|
||||
} finally {
|
||||
try {
|
||||
rmSync(tempOutputFilePath);
|
||||
} catch (e) {
|
||||
logErrorSentry(e, 'failed to remove tempOutputFile');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function writeTempFile(fileStream: Uint8Array, fileName: string) {
|
||||
const tempFilePath = await generateTempFilePath(fileName);
|
||||
await writeFile(tempFilePath, fileStream);
|
||||
return tempFilePath;
|
||||
}
|
||||
|
||||
export async function deleteTempFile(tempFilePath: string) {
|
||||
const tempDirPath = await getTempDirPath();
|
||||
if (!tempFilePath.startsWith(tempDirPath)) {
|
||||
logErrorSentry(
|
||||
Error('not a temp file'),
|
||||
'tried to delete a non temp file'
|
||||
);
|
||||
}
|
||||
rmSync(tempFilePath);
|
||||
}
|
|
@ -1,46 +1,27 @@
|
|||
import { exec, ExecException } from 'child_process';
|
||||
import { app } from 'electron';
|
||||
import { existsSync, rmSync } from 'fs';
|
||||
import path from 'path';
|
||||
import { mkdir, readFile, writeFile } from 'promise-fs';
|
||||
import { generateRandomName } from '../utils/common';
|
||||
import util from 'util';
|
||||
import { exec } from 'child_process';
|
||||
|
||||
import { rmSync } from 'fs';
|
||||
import { readFile, writeFile } from 'promise-fs';
|
||||
import { generateTempFilePath } from '../utils/temp';
|
||||
import { logErrorSentry } from './sentry';
|
||||
|
||||
const asyncExec = util.promisify(exec);
|
||||
|
||||
export async function convertHEIC(
|
||||
heicFileData: Uint8Array
|
||||
): Promise<Uint8Array> {
|
||||
let tempInputFilePath: string;
|
||||
let tempOutputFilePath: string;
|
||||
try {
|
||||
const tempDir = path.join(app.getPath('temp'), 'ente');
|
||||
if (!existsSync(tempDir)) {
|
||||
await mkdir(tempDir);
|
||||
}
|
||||
const tempName = generateRandomName(10);
|
||||
|
||||
tempInputFilePath = path.join(tempDir, tempName + '.heic');
|
||||
tempOutputFilePath = path.join(tempDir, tempName + '.jpeg');
|
||||
tempInputFilePath = await generateTempFilePath('.heic');
|
||||
tempOutputFilePath = await generateTempFilePath('.jpeg');
|
||||
|
||||
await writeFile(tempInputFilePath, heicFileData);
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
exec(
|
||||
`sips -s format jpeg ${tempInputFilePath} --out ${tempOutputFilePath}`,
|
||||
(
|
||||
error: ExecException | null,
|
||||
stdout: string,
|
||||
stderr: string
|
||||
) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else if (stderr) {
|
||||
reject(stderr);
|
||||
} else {
|
||||
resolve(stdout);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
await asyncExec(
|
||||
`sips -s format jpeg ${tempInputFilePath} --out ${tempOutputFilePath}`
|
||||
);
|
||||
const convertedFileData = new Uint8Array(
|
||||
await readFile(tempOutputFilePath)
|
||||
);
|
||||
|
|
|
@ -1,17 +1,2 @@
|
|||
import { app } from 'electron';
|
||||
export const isDev = !app.isPackaged;
|
||||
|
||||
const characters =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
export function generateRandomName(length: number) {
|
||||
let result = '';
|
||||
|
||||
const charactersLength = characters.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(
|
||||
Math.floor(Math.random() * charactersLength)
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@ import {
|
|||
skipAppVersion,
|
||||
updateAndRestart,
|
||||
} from '../services/appUpdater';
|
||||
import {
|
||||
deleteTempFile,
|
||||
runFFmpegCmd,
|
||||
writeTempFile,
|
||||
} from '../services/ffmpeg';
|
||||
|
||||
export default function setupIpcComs(
|
||||
tray: Tray,
|
||||
|
@ -125,4 +130,20 @@ export default function setupIpcComs(
|
|||
ipcMain.handle('get-app-version', () => {
|
||||
return getAppVersion();
|
||||
});
|
||||
|
||||
ipcMain.handle(
|
||||
'run-ffmpeg-cmd',
|
||||
(_, cmd, inputFilePath, outputFileName) => {
|
||||
return runFFmpegCmd(cmd, inputFilePath, outputFileName);
|
||||
}
|
||||
);
|
||||
ipcMain.handle(
|
||||
'write-temp-file',
|
||||
(_, fileStream: Uint8Array, fileName: string) => {
|
||||
return writeTempFile(fileStream, fileName);
|
||||
}
|
||||
);
|
||||
ipcMain.handle('remove-temp-file', (_, tempFilePath: string) => {
|
||||
return deleteTempFile(tempFilePath);
|
||||
});
|
||||
}
|
||||
|
|
38
src/utils/temp.ts
Normal file
38
src/utils/temp.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { app } from 'electron';
|
||||
import path from 'path';
|
||||
import { existsSync, mkdir } from 'promise-fs';
|
||||
|
||||
const ENTE_TEMP_DIRECTORY = 'ente';
|
||||
|
||||
const CHARACTERS =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
export async function getTempDirPath() {
|
||||
const tempDirPath = path.join(app.getPath('temp'), ENTE_TEMP_DIRECTORY);
|
||||
if (!existsSync(tempDirPath)) {
|
||||
await mkdir(tempDirPath);
|
||||
}
|
||||
return tempDirPath;
|
||||
}
|
||||
|
||||
function generateTempName(length: number) {
|
||||
let result = '';
|
||||
|
||||
const charactersLength = CHARACTERS.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += CHARACTERS.charAt(
|
||||
Math.floor(Math.random() * charactersLength)
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function generateTempFilePath(formatSuffix: string) {
|
||||
const tempDirPath = await getTempDirPath();
|
||||
const namePrefix = generateTempName(10);
|
||||
const tempFilePath = path.join(
|
||||
tempDirPath,
|
||||
namePrefix + '-' + formatSuffix
|
||||
);
|
||||
return tempFilePath;
|
||||
}
|
2
ui
2
ui
|
@ -1 +1 @@
|
|||
Subproject commit c746bf89353c8dd303778214d16434faeffce556
|
||||
Subproject commit 6320f0295d811570adad53a32ce94beb912fa9f9
|
79
yarn.lock
79
yarn.lock
|
@ -35,6 +35,16 @@
|
|||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@derhuerst/http-basic@^8.2.0":
|
||||
version "8.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@derhuerst/http-basic/-/http-basic-8.2.4.tgz#d021ebb8f65d54bea681ae6f4a8733ce89e7f59b"
|
||||
integrity sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==
|
||||
dependencies:
|
||||
caseless "^0.12.0"
|
||||
concat-stream "^2.0.0"
|
||||
http-response-object "^3.0.1"
|
||||
parse-cache-control "^1.0.1"
|
||||
|
||||
"@develar/schema-utils@~2.6.5":
|
||||
version "2.6.5"
|
||||
resolved "https://registry.yarnpkg.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz#3ece22c5838402419a6e0425f85742b961d9b6c6"
|
||||
|
@ -277,6 +287,11 @@
|
|||
dependencies:
|
||||
"@types/ms" "*"
|
||||
|
||||
"@types/ffmpeg-static@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/ffmpeg-static/-/ffmpeg-static-3.0.1.tgz#1003f003624bcd2f569b56185a62dcbacd935c39"
|
||||
integrity sha512-hEJdQMv/g1olk9qTiWqh23BfbKsDKE6Tc7DilNJWF1MgZsU9fYOPKrgQ448vfT7aP2Yt5re9vgJDVv9TXEoTyQ==
|
||||
|
||||
"@types/fs-extra@^9.0.11":
|
||||
version "9.0.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45"
|
||||
|
@ -325,6 +340,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.3.tgz#463fc47f13ec0688a33aec75d078a0541a447199"
|
||||
integrity sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ==
|
||||
|
||||
"@types/node@^10.0.3":
|
||||
version "10.17.60"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
|
||||
integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==
|
||||
|
||||
"@types/node@^14.14.37", "@types/node@^14.6.2":
|
||||
version "14.18.21"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.21.tgz#0155ee46f6be28b2ff0342ca1a9b9fd4468bef41"
|
||||
|
@ -567,6 +587,11 @@ ansi-styles@^6.0.0:
|
|||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3"
|
||||
integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==
|
||||
|
||||
any-shell-escape@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/any-shell-escape/-/any-shell-escape-0.1.1.tgz#d55ab972244c71a9a5e1ab0879f30bf110806959"
|
||||
integrity sha512-36j4l5HVkboyRhIWgtMh1I9i8LTdFqVwDEHy1cp+QioJyKgAUG40X0W8s7jakWRta/Sjvm8mUG1fU6Tj8mWagQ==
|
||||
|
||||
anymatch@~3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
|
||||
|
@ -900,7 +925,7 @@ camelcase@^6.2.0:
|
|||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
caseless@~0.12.0:
|
||||
caseless@^0.12.0, caseless@~0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==
|
||||
|
@ -1089,6 +1114,16 @@ concat-stream@^1.6.2:
|
|||
readable-stream "^2.2.2"
|
||||
typedarray "^0.0.6"
|
||||
|
||||
concat-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1"
|
||||
integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^3.0.2"
|
||||
typedarray "^0.0.6"
|
||||
|
||||
concurrently@^7.0.0:
|
||||
version "7.2.2"
|
||||
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-7.2.2.tgz#4ad4a4dfd3945f668d727379de2a29502e6a531c"
|
||||
|
@ -1778,6 +1813,16 @@ fd-slicer@~1.1.0:
|
|||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
ffmpeg-static@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ffmpeg-static/-/ffmpeg-static-5.1.0.tgz#133500f4566570c5a0e96795152b0526d8c936ad"
|
||||
integrity sha512-eEWOiGdbf7HKPeJI5PoJ0oCwkL0hckL2JdS4JOuB/gUETppwkEpq8nF0+e6VEQnDCo/iuoipbTUsn9QJmtpNkg==
|
||||
dependencies:
|
||||
"@derhuerst/http-basic" "^8.2.0"
|
||||
env-paths "^2.2.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
progress "^2.0.3"
|
||||
|
||||
file-entry-cache@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
||||
|
@ -2169,6 +2214,13 @@ http-proxy-agent@^5.0.0:
|
|||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
http-response-object@^3.0.1:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810"
|
||||
integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==
|
||||
dependencies:
|
||||
"@types/node" "^10.0.3"
|
||||
|
||||
http-signature@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
||||
|
@ -2995,6 +3047,11 @@ parent-module@^1.0.0:
|
|||
dependencies:
|
||||
callsites "^3.0.0"
|
||||
|
||||
parse-cache-control@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e"
|
||||
integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==
|
||||
|
||||
parse-json@^5.0.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
|
||||
|
@ -3228,6 +3285,15 @@ readable-stream@^2.0.6, readable-stream@^2.2.2:
|
|||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^3.0.2:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
string_decoder "^1.1.1"
|
||||
util-deprecate "^1.0.1"
|
||||
|
||||
readable-stream@~1.1.9:
|
||||
version "1.1.14"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
|
||||
|
@ -3372,7 +3438,7 @@ rxjs@^7.0.0, rxjs@^7.5.5:
|
|||
dependencies:
|
||||
tslib "^2.1.0"
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
@ -3616,6 +3682,13 @@ string-width@^5.0.0:
|
|||
emoji-regex "^9.2.2"
|
||||
strip-ansi "^7.0.1"
|
||||
|
||||
string_decoder@^1.1.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
string_decoder@~0.10.x:
|
||||
version "0.10.31"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
|
||||
|
@ -3963,7 +4036,7 @@ utf8-byte-length@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
|
||||
integrity sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==
|
||||
|
||||
util-deprecate@~1.0.1:
|
||||
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
|
Loading…
Reference in a new issue