From 6cdf4cbcd6a0d51bf25a682aca1f72515569ec55 Mon Sep 17 00:00:00 2001 From: yizhi <946185759@qq.com> Date: Thu, 20 Mar 2025 18:24:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=8B=E8=BD=BD=E5=B7=A5?= =?UTF-8?q?=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 14 ++++++ cxx/mnn/node.cc | 3 ++ cxx/node.cc | 3 ++ cxx/ort/node.cc | 3 ++ src/{ => backend}/config.ts | 2 +- src/backend/download.ts | 95 +++++++++++++++++++++++++++++++++++++ src/backend/main.ts | 3 +- src/backend/mnn/session.ts | 2 +- src/backend/ort/session.ts | 2 +- src/main.ts | 2 +- src/test.ts | 8 ++-- thirdpart/cmake-js-util.js | 3 +- 12 files changed, 131 insertions(+), 9 deletions(-) rename src/{ => backend}/config.ts (87%) create mode 100644 src/backend/download.ts diff --git a/CMakeLists.txt b/CMakeLists.txt index cc4324b..2fb5399 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,20 @@ if(CMAKE_JS_RESULT EQUAL 0) ) endif() + # ReleaseVersion + if(CMAKE_JS_RESULT EQUAL 0) + execute_process( + COMMAND node ${CMAKE_SOURCE_DIR}/thirdpart/cmake-js-util.js --release + RESULT_VARIABLE CMAKE_JS_RESULT + OUTPUT_VARIABLE RELEASE_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(CMAKE_JS_RESULT EQUAL 0) + message(STATUS "RELEASE_VERSION: ${RELEASE_VERSION}") + add_compile_definitions(RELEASE_VERSION="${RELEASE_VERSION}") + endif() + endif() + # NAPI if(CMAKE_JS_RESULT EQUAL 0) execute_process( diff --git a/cxx/mnn/node.cc b/cxx/mnn/node.cc index 0caf19a..c2697eb 100644 --- a/cxx/mnn/node.cc +++ b/cxx/mnn/node.cc @@ -237,6 +237,9 @@ void InstallMNNAPI(Napi::Env env, Napi::Object exports) #if defined(USE_MNN) && !defined(BUILD_MAIN_WORD) static Object Init(Env env, Object exports) { +#ifdef RELEASE_VERSION + exports.Set("__release__", Napi::String::New(env, RELEASE_VERSION)); +#endif InstallMNNAPI(env, exports); return exports; } diff --git a/cxx/node.cc b/cxx/node.cc index 31cf39c..a167caa 100644 --- a/cxx/node.cc +++ b/cxx/node.cc @@ -7,6 +7,9 @@ using namespace Napi; #if defined(BUILD_MAIN_WORD) Object Init(Env env, Object exports) { +#ifdef RELEASE_VERSION + exports.Set("__release__", Napi::String::New(env, RELEASE_VERSION)); +#endif // OnnxRuntime #ifdef USE_ONNXRUNTIME InstallOrtAPI(env, exports); diff --git a/cxx/ort/node.cc b/cxx/ort/node.cc index 2bd4ac7..9959bde 100644 --- a/cxx/ort/node.cc +++ b/cxx/ort/node.cc @@ -308,6 +308,9 @@ void InstallOrtAPI(Napi::Env env, Napi::Object exports) #if defined(USE_ONNXRUNTIME) && !defined(BUILD_MAIN_WORD) static Object Init(Env env, Object exports) { +#ifdef RELEASE_VERSION + exports.Set("__release__", Napi::String::New(env, RELEASE_VERSION)); +#endif InstallOrtAPI(env, exports); return exports; } diff --git a/src/config.ts b/src/backend/config.ts similarity index 87% rename from src/config.ts rename to src/backend/config.ts index f5934cf..7702983 100644 --- a/src/config.ts +++ b/src/backend/config.ts @@ -1,6 +1,6 @@ import path from "path"; -const defaultAddonDir = path.join(__dirname, "../build") +const defaultAddonDir = path.join(__dirname, "../../build") const aiConfig = { "MNN_ADDON_FILE": path.join(defaultAddonDir, "mnn.node"), diff --git a/src/backend/download.ts b/src/backend/download.ts new file mode 100644 index 0000000..43a3b69 --- /dev/null +++ b/src/backend/download.ts @@ -0,0 +1,95 @@ +import os from "os"; +import fs from "fs"; +import path from "path"; +import { getConfig } from "./config"; + + +const URLS = { + GITHUB: "https://github.com/kangkang520/node-addons/releases/download/ai{{version}}/{{backend}}_{{platform}}_{{arch}}.node", + XXXXX: `http://git.urnas.cn:5200/yizhi-js-lib/ai-box/releases/download/{{version}}/{{backend}}_{{platform}}_{{arch}}.node`, +} + +function releaseVersion() { return require("../../package.json").releaseVersion } + +function getURL(backend: "ort" | "mnn", template: string) { + + let platform = ""; + let arch = ""; + switch (os.platform()) { + case "win32": + platform = "windows"; + break; + case "linux": + platform = "linux"; + break; + case "darwin": + platform = "macos"; + break + default: + throw new Error(`Unsupported platform: ${os.platform()}, Please compile the addon yourself.`); + } + switch (os.arch()) { + case "x64": + arch = "x64"; + break; + case "arm64": + arch = "arm64"; + break; + default: + throw new Error(`Unsupported architecture: ${os.arch()}, Please compile the addon yourself.`); + } + + return template.replaceAll("{{backend}}", backend).replaceAll("{{version}}", releaseVersion()).replaceAll("{{platform}}", platform).replaceAll("{{arch}}", arch); +} + +async function getStream(backend: "mnn" | "ort") { + for (const [name, url] of Object.entries(URLS)) { + try { + return await fetch(getURL(backend, url)).then(res => { + if (res.status != 200) throw new Error("Failed to download addon."); + return res.blob().then(b => b.stream()); + }) + } catch (e) { } + } + throw new Error("Failed to download addon."); +} + +export async function downloadBackend(backend: "ort" | "mnn", savename?: string) { + const backendConfigNameDict = { ort: "ORT_ADDON_FILE" as const, mnn: "MNN_ADDON_FILE" as const }; + const defaultAddon = path.resolve(process.cwd(), getConfig(backendConfigNameDict[backend])); + + const saveName = savename ? path.resolve(path.dirname(defaultAddon), savename) : defaultAddon; + if (fs.existsSync(saveName)) { + try { + const addon = require(saveName); + if (addon.__release__ === releaseVersion()) return saveName; + //清除缓存 + delete require.cache[saveName]; + } catch (err) { } + } + + await fs.promises.mkdir(path.dirname(saveName), { recursive: true }); + + const stream = await getStream(backend); + const cacheFile = await new Promise((resolve, reject) => { + const cacheFile = path.join(os.tmpdir(), Date.now() + ".cv.node"); + let fsStream!: ReturnType; + stream.pipeTo(new WritableStream({ + start(controller) { + fsStream = fs.createWriteStream(cacheFile); + }, + async write(chunk, controller) { + await new Promise((resolve, reject) => fsStream.write(chunk, err => err ? reject(err) : resolve())); + }, + close() { + fsStream.end(); + resolve(cacheFile); + }, + abort() { } + })).catch(reject); + }); + if (fs.existsSync(saveName)) await fs.promises.rm(saveName, { recursive: true, force: true }); + await fs.promises.cp(cacheFile, saveName); + await fs.promises.rm(cacheFile); + return saveName; +} \ No newline at end of file diff --git a/src/backend/main.ts b/src/backend/main.ts index 33481a0..fe2079d 100644 --- a/src/backend/main.ts +++ b/src/backend/main.ts @@ -1,3 +1,4 @@ export { SessionNodeInfo, DataTypeString, DataType, SessionNodeData, SessionRunInputOption, SessionRunOutput, CommonSession } from "./common"; export * as ort from "./ort"; -export * as mnn from "./mnn"; \ No newline at end of file +export * as mnn from "./mnn"; +export { downloadBackend } from "./download"; \ No newline at end of file diff --git a/src/backend/mnn/session.ts b/src/backend/mnn/session.ts index c21fa96..0113427 100644 --- a/src/backend/mnn/session.ts +++ b/src/backend/mnn/session.ts @@ -1,4 +1,4 @@ -import { getConfig } from "../../config"; +import { getConfig } from "../config"; import { CommonSession, dataTypeFrom, isTypedArray, SessionNodeData, SessionNodeInfo, SessionRunInputOption, SessionRunOutput } from "../common"; export class MNNSession extends CommonSession { diff --git a/src/backend/ort/session.ts b/src/backend/ort/session.ts index 5f04ab6..864cbb6 100644 --- a/src/backend/ort/session.ts +++ b/src/backend/ort/session.ts @@ -1,4 +1,4 @@ -import { getConfig } from "../../config"; +import { getConfig } from "../config"; import { CommonSession, dataTypeFrom, isTypedArray, SessionNodeData, SessionNodeInfo, SessionRunInputOption, SessionRunOutput } from "../common"; export class OrtSession extends CommonSession { diff --git a/src/main.ts b/src/main.ts index b12ad4a..3a3d5ce 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,3 @@ export { deploy } from "./deploy"; export { backend } from "./backend"; -export { setConfig as config } from "./config"; +export { setConfig as config } from "./backend/config"; diff --git a/src/test.ts b/src/test.ts index 06ef8f5..cf7754a 100644 --- a/src/test.ts +++ b/src/test.ts @@ -4,6 +4,7 @@ import { deploy } from "./deploy"; import { faceidTestData } from "./test_data/faceid"; import path from "path"; import crypto from "crypto"; +import ai from "."; cv.config("ADDON_PATH", path.join(__dirname, "../build/cv.node")); @@ -152,9 +153,10 @@ async function testFaceAlign() { } async function test() { - await testGenderTest(); - await testFaceID(); - await testFaceAlign(); + await ai.backend.downloadBackend("ort"); + // await testGenderTest(); + // await testFaceID(); + // await testFaceAlign(); } test().catch(err => { diff --git a/thirdpart/cmake-js-util.js b/thirdpart/cmake-js-util.js index b8cfb92..ec0a15c 100644 --- a/thirdpart/cmake-js-util.js +++ b/thirdpart/cmake-js-util.js @@ -15,4 +15,5 @@ function runCmakeJS(args) { if (args.includes("--include")) runCmakeJS(["print-cmakejs-include"]); else if (args.includes("--src")) runCmakeJS(["print-cmakejs-src"]); else if (args.includes("--lib")) runCmakeJS(["print-cmakejs-lib"]); -else if (args.includes("--napi")) console.log(require("node-addon-api").include.replace(/^"/, "").replace(/"$/, "")); \ No newline at end of file +else if (args.includes("--napi")) console.log(require("node-addon-api").include.replace(/^"/, "").replace(/"$/, "")); +else if (args.includes("--release")) console.log(require("../package.json").releaseVersion); \ No newline at end of file