@downcity/plugins 1.0.56 → 1.0.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/BuiltinPlugins.d.ts.map +1 -1
- package/bin/BuiltinPlugins.js +0 -4
- package/bin/BuiltinPlugins.js.map +1 -1
- package/bin/asr/Plugin.d.ts +67 -7
- package/bin/asr/Plugin.d.ts.map +1 -1
- package/bin/asr/Plugin.js +229 -461
- package/bin/asr/Plugin.js.map +1 -1
- package/bin/asr/types/AsrPlugin.d.ts +114 -0
- package/bin/asr/types/AsrPlugin.d.ts.map +1 -0
- package/bin/asr/types/AsrPlugin.js +10 -0
- package/bin/asr/types/AsrPlugin.js.map +1 -0
- package/bin/image/ImagePlugin.d.ts +1 -1
- package/bin/image/ImagePlugin.d.ts.map +1 -1
- package/bin/image/ImagePlugin.js +23 -5
- package/bin/image/ImagePlugin.js.map +1 -1
- package/bin/index.d.ts +2 -0
- package/bin/index.d.ts.map +1 -1
- package/bin/tts/Plugin.d.ts +53 -6
- package/bin/tts/Plugin.d.ts.map +1 -1
- package/bin/tts/Plugin.js +197 -474
- package/bin/tts/Plugin.js.map +1 -1
- package/bin/tts/types/TtsPlugin.d.ts +63 -102
- package/bin/tts/types/TtsPlugin.d.ts.map +1 -1
- package/bin/tts/types/TtsPlugin.js +4 -3
- package/bin/tts/types/TtsPlugin.js.map +1 -1
- package/bin/web/PROMPT.d.ts +1 -1
- package/bin/web/PROMPT.d.ts.map +1 -1
- package/bin/web/PROMPT.js +1 -1
- package/bin/web/PROMPT.js.map +1 -1
- package/bin/web/Plugin.d.ts +66 -5
- package/bin/web/Plugin.d.ts.map +1 -1
- package/bin/web/Plugin.js +126 -450
- package/bin/web/Plugin.js.map +1 -1
- package/bin/web/WebPromptAssets.d.ts +1 -9
- package/bin/web/WebPromptAssets.d.ts.map +1 -1
- package/bin/web/WebPromptAssets.js +1 -11
- package/bin/web/WebPromptAssets.js.map +1 -1
- package/bin/web/runtime/Install.d.ts +19 -0
- package/bin/web/runtime/Install.d.ts.map +1 -0
- package/bin/web/runtime/Install.js +178 -0
- package/bin/web/runtime/Install.js.map +1 -0
- package/bin/web/types/WebPlugin.d.ts +37 -109
- package/bin/web/types/WebPlugin.d.ts.map +1 -1
- package/bin/web/types/WebPlugin.js +5 -7
- package/bin/web/types/WebPlugin.js.map +1 -1
- package/package.json +2 -2
- package/src/BuiltinPlugins.ts +0 -4
- package/src/asr/Plugin.ts +264 -483
- package/src/asr/types/AsrPlugin.ts +118 -0
- package/src/image/ImagePlugin.ts +23 -5
- package/src/index.ts +12 -0
- package/src/tts/Plugin.ts +225 -492
- package/src/tts/types/TtsPlugin.ts +67 -102
- package/src/web/PROMPT.ts +1 -1
- package/src/web/PROMPT.ts.txt +32 -6
- package/src/web/Plugin.ts +119 -453
- package/src/web/WebPromptAssets.ts +1 -13
- package/src/web/runtime/Install.ts +241 -0
- package/src/web/types/WebPlugin.ts +37 -113
- package/bin/asr/Config.d.ts +0 -43
- package/bin/asr/Config.d.ts.map +0 -1
- package/bin/asr/Config.js +0 -107
- package/bin/asr/Config.js.map +0 -1
- package/bin/asr/Dependency.d.ts +0 -77
- package/bin/asr/Dependency.d.ts.map +0 -1
- package/bin/asr/Dependency.js +0 -238
- package/bin/asr/Dependency.js.map +0 -1
- package/bin/asr/InboundAugment.d.ts +0 -17
- package/bin/asr/InboundAugment.d.ts.map +0 -1
- package/bin/asr/InboundAugment.js +0 -47
- package/bin/asr/InboundAugment.js.map +0 -1
- package/bin/asr/ModelCatalog.d.ts +0 -29
- package/bin/asr/ModelCatalog.d.ts.map +0 -1
- package/bin/asr/ModelCatalog.js +0 -25
- package/bin/asr/ModelCatalog.js.map +0 -1
- package/bin/tts/Dependency.d.ts +0 -90
- package/bin/tts/Dependency.d.ts.map +0 -1
- package/bin/tts/Dependency.js +0 -344
- package/bin/tts/Dependency.js.map +0 -1
- package/bin/tts/PluginSupport.d.ts +0 -25
- package/bin/tts/PluginSupport.d.ts.map +0 -1
- package/bin/tts/PluginSupport.js +0 -72
- package/bin/tts/PluginSupport.js.map +0 -1
- package/bin/tts/runtime/Catalog.d.ts +0 -21
- package/bin/tts/runtime/Catalog.d.ts.map +0 -1
- package/bin/tts/runtime/Catalog.js +0 -90
- package/bin/tts/runtime/Catalog.js.map +0 -1
- package/bin/tts/runtime/DependencyInstaller.d.ts +0 -143
- package/bin/tts/runtime/DependencyInstaller.d.ts.map +0 -1
- package/bin/tts/runtime/DependencyInstaller.js +0 -261
- package/bin/tts/runtime/DependencyInstaller.js.map +0 -1
- package/bin/tts/runtime/Installer.d.ts +0 -89
- package/bin/tts/runtime/Installer.d.ts.map +0 -1
- package/bin/tts/runtime/Installer.js +0 -188
- package/bin/tts/runtime/Installer.js.map +0 -1
- package/bin/tts/runtime/Paths.d.ts +0 -20
- package/bin/tts/runtime/Paths.d.ts.map +0 -1
- package/bin/tts/runtime/Paths.js +0 -32
- package/bin/tts/runtime/Paths.js.map +0 -1
- package/bin/tts/runtime/Synthesizer.d.ts +0 -44
- package/bin/tts/runtime/Synthesizer.d.ts.map +0 -1
- package/bin/tts/runtime/Synthesizer.js +0 -363
- package/bin/tts/runtime/Synthesizer.js.map +0 -1
- package/bin/tts/types/Tts.d.ts +0 -91
- package/bin/tts/types/Tts.d.ts.map +0 -1
- package/bin/tts/types/Tts.js +0 -9
- package/bin/tts/types/Tts.js.map +0 -1
- package/bin/voice/Config.d.ts +0 -43
- package/bin/voice/Config.d.ts.map +0 -1
- package/bin/voice/Config.js +0 -104
- package/bin/voice/Config.js.map +0 -1
- package/bin/voice/Dependency.d.ts +0 -77
- package/bin/voice/Dependency.d.ts.map +0 -1
- package/bin/voice/Dependency.js +0 -237
- package/bin/voice/Dependency.js.map +0 -1
- package/bin/voice/InboundAugment.d.ts +0 -17
- package/bin/voice/InboundAugment.d.ts.map +0 -1
- package/bin/voice/InboundAugment.js +0 -47
- package/bin/voice/InboundAugment.js.map +0 -1
- package/bin/voice/ModelCatalog.d.ts +0 -29
- package/bin/voice/ModelCatalog.d.ts.map +0 -1
- package/bin/voice/ModelCatalog.js +0 -25
- package/bin/voice/ModelCatalog.js.map +0 -1
- package/bin/voice/runtime/Catalog.d.ts +0 -18
- package/bin/voice/runtime/Catalog.d.ts.map +0 -1
- package/bin/voice/runtime/Catalog.js +0 -61
- package/bin/voice/runtime/Catalog.js.map +0 -1
- package/bin/voice/runtime/DependencyInstaller.d.ts +0 -145
- package/bin/voice/runtime/DependencyInstaller.d.ts.map +0 -1
- package/bin/voice/runtime/DependencyInstaller.js +0 -309
- package/bin/voice/runtime/DependencyInstaller.js.map +0 -1
- package/bin/voice/runtime/Installer.d.ts +0 -94
- package/bin/voice/runtime/Installer.d.ts.map +0 -1
- package/bin/voice/runtime/Installer.js +0 -200
- package/bin/voice/runtime/Installer.js.map +0 -1
- package/bin/voice/runtime/Paths.d.ts +0 -8
- package/bin/voice/runtime/Paths.d.ts.map +0 -1
- package/bin/voice/runtime/Paths.js +0 -26
- package/bin/voice/runtime/Paths.js.map +0 -1
- package/bin/voice/runtime/Transcriber.d.ts +0 -57
- package/bin/voice/runtime/Transcriber.d.ts.map +0 -1
- package/bin/voice/runtime/Transcriber.js +0 -329
- package/bin/voice/runtime/Transcriber.js.map +0 -1
- package/bin/voice/types/Voice.d.ts +0 -58
- package/bin/voice/types/Voice.d.ts.map +0 -1
- package/bin/voice/types/Voice.js +0 -9
- package/bin/voice/types/Voice.js.map +0 -1
- package/bin/voice/types/VoicePlugin.d.ts +0 -190
- package/bin/voice/types/VoicePlugin.d.ts.map +0 -1
- package/bin/voice/types/VoicePlugin.js +0 -9
- package/bin/voice/types/VoicePlugin.js.map +0 -1
- package/bin/web/Dependency.d.ts +0 -10
- package/bin/web/Dependency.d.ts.map +0 -1
- package/bin/web/Dependency.js +0 -10
- package/bin/web/Dependency.js.map +0 -1
- package/bin/web/PROMPT.agent-browser.d.ts +0 -7
- package/bin/web/PROMPT.agent-browser.d.ts.map +0 -1
- package/bin/web/PROMPT.agent-browser.js +0 -8
- package/bin/web/PROMPT.agent-browser.js.map +0 -1
- package/bin/web/PROMPT.web-access.d.ts +0 -7
- package/bin/web/PROMPT.web-access.d.ts.map +0 -1
- package/bin/web/PROMPT.web-access.js +0 -8
- package/bin/web/PROMPT.web-access.js.map +0 -1
- package/bin/web/runtime/Config.d.ts +0 -21
- package/bin/web/runtime/Config.d.ts.map +0 -1
- package/bin/web/runtime/Config.js +0 -79
- package/bin/web/runtime/Config.js.map +0 -1
- package/bin/web/runtime/Source.d.ts +0 -29
- package/bin/web/runtime/Source.d.ts.map +0 -1
- package/bin/web/runtime/Source.js +0 -209
- package/bin/web/runtime/Source.js.map +0 -1
- package/src/asr/Config.ts +0 -138
- package/src/asr/Dependency.ts +0 -336
- package/src/asr/InboundAugment.ts +0 -59
- package/src/asr/ModelCatalog.ts +0 -43
- package/src/tts/Dependency.ts +0 -473
- package/src/tts/PluginSupport.ts +0 -85
- package/src/tts/runtime/Catalog.ts +0 -97
- package/src/tts/runtime/DependencyInstaller.ts +0 -436
- package/src/tts/runtime/Installer.ts +0 -297
- package/src/tts/runtime/Paths.ts +0 -39
- package/src/tts/runtime/Synthesizer.ts +0 -480
- package/src/tts/types/Tts.ts +0 -99
- package/src/voice/Config.ts +0 -135
- package/src/voice/Dependency.ts +0 -329
- package/src/voice/InboundAugment.ts +0 -59
- package/src/voice/ModelCatalog.ts +0 -43
- package/src/voice/runtime/Catalog.ts +0 -68
- package/src/voice/runtime/DependencyInstaller.ts +0 -505
- package/src/voice/runtime/Installer.ts +0 -324
- package/src/voice/runtime/Paths.ts +0 -26
- package/src/voice/runtime/Transcriber.ts +0 -467
- package/src/voice/types/Voice.ts +0 -68
- package/src/voice/types/VoicePlugin.ts +0 -194
- package/src/web/Dependency.ts +0 -17
- package/src/web/PROMPT.agent-browser.ts +0 -9
- package/src/web/PROMPT.agent-browser.ts.txt +0 -17
- package/src/web/PROMPT.web-access.ts +0 -9
- package/src/web/PROMPT.web-access.ts.txt +0 -13
- package/src/web/runtime/Config.ts +0 -105
- package/src/web/runtime/Source.ts +0 -257
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import fsExtra from "fs-extra";
|
|
4
|
-
import { Readable } from "node:stream";
|
|
5
|
-
import { pipeline } from "node:stream/promises";
|
|
6
|
-
import type { VoiceModelCatalogItem, VoiceModelId } from "@/voice/types/Voice.js";
|
|
7
|
-
|
|
8
|
-
const HUGGINGFACE_HOST = "https://huggingface.co";
|
|
9
|
-
|
|
10
|
-
type HuggingFaceModelApiResponse = {
|
|
11
|
-
siblings?: Array<{
|
|
12
|
-
rfilename?: string;
|
|
13
|
-
}>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Voice 模型安装结果。
|
|
18
|
-
*/
|
|
19
|
-
export interface VoiceModelInstallResult {
|
|
20
|
-
/**
|
|
21
|
-
* 模型目录(绝对路径)。
|
|
22
|
-
*/
|
|
23
|
-
modelDir: string;
|
|
24
|
-
/**
|
|
25
|
-
* 本次下载文件数。
|
|
26
|
-
*/
|
|
27
|
-
downloadedFiles: number;
|
|
28
|
-
/**
|
|
29
|
-
* 本次跳过文件数(已存在且未强制覆盖)。
|
|
30
|
-
*/
|
|
31
|
-
skippedFiles: number;
|
|
32
|
-
/**
|
|
33
|
-
* 本次实际下载的文件路径(相对模型目录)。
|
|
34
|
-
*/
|
|
35
|
-
downloadedFilePaths: string[];
|
|
36
|
-
/**
|
|
37
|
-
* 本次跳过的文件路径(相对模型目录)。
|
|
38
|
-
*/
|
|
39
|
-
skippedFilePaths: string[];
|
|
40
|
-
/**
|
|
41
|
-
* 安装源仓库 ID。
|
|
42
|
-
*/
|
|
43
|
-
repoId: string;
|
|
44
|
-
/**
|
|
45
|
-
* 安装源 revision。
|
|
46
|
-
*/
|
|
47
|
-
revision: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Voice 模型安装进度事件。
|
|
52
|
-
*/
|
|
53
|
-
export interface VoiceModelInstallProgressEvent {
|
|
54
|
-
/**
|
|
55
|
-
* 当前阶段。
|
|
56
|
-
*/
|
|
57
|
-
stage:
|
|
58
|
-
| "discover"
|
|
59
|
-
| "skip"
|
|
60
|
-
| "download_start"
|
|
61
|
-
| "download_done"
|
|
62
|
-
| "manifest";
|
|
63
|
-
/**
|
|
64
|
-
* 文件路径(相对模型目录,可选)。
|
|
65
|
-
*/
|
|
66
|
-
filePath?: string;
|
|
67
|
-
/**
|
|
68
|
-
* 文件总数(可选)。
|
|
69
|
-
*/
|
|
70
|
-
totalFiles?: number;
|
|
71
|
-
/**
|
|
72
|
-
* 当前文件序号(从 1 开始,可选)。
|
|
73
|
-
*/
|
|
74
|
-
index?: number;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* 判断模型在本地是否已安装。
|
|
79
|
-
*
|
|
80
|
-
* 关键点(中文)
|
|
81
|
-
* - 优先识别安装清单 `downcity.voice.install.json`。
|
|
82
|
-
* - 若无清单但目录非空,也视为已存在模型,避免重复下载。
|
|
83
|
-
*/
|
|
84
|
-
export async function detectLocalVoiceModelInstallState(input: {
|
|
85
|
-
modelId: VoiceModelId;
|
|
86
|
-
modelsRootDir: string;
|
|
87
|
-
}): Promise<{
|
|
88
|
-
/**
|
|
89
|
-
* 模型目录(绝对路径)。
|
|
90
|
-
*/
|
|
91
|
-
modelDir: string;
|
|
92
|
-
/**
|
|
93
|
-
* 是否判断为“已安装”。
|
|
94
|
-
*/
|
|
95
|
-
installed: boolean;
|
|
96
|
-
/**
|
|
97
|
-
* 已安装来源。
|
|
98
|
-
*/
|
|
99
|
-
source?: "manifest" | "directory";
|
|
100
|
-
}> {
|
|
101
|
-
const modelDir = path.resolve(input.modelsRootDir, input.modelId);
|
|
102
|
-
const manifestPath = path.join(modelDir, "downcity.voice.install.json");
|
|
103
|
-
const hasManifest = await fsExtra.pathExists(manifestPath);
|
|
104
|
-
if (hasManifest) {
|
|
105
|
-
return {
|
|
106
|
-
modelDir,
|
|
107
|
-
installed: true,
|
|
108
|
-
source: "manifest",
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const hasModelDir = await fsExtra.pathExists(modelDir);
|
|
113
|
-
if (!hasModelDir) {
|
|
114
|
-
return {
|
|
115
|
-
modelDir,
|
|
116
|
-
installed: false,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const entries = await fsExtra.readdir(modelDir).catch(() => []);
|
|
121
|
-
const hasPersistedFiles = entries.some(
|
|
122
|
-
(entry) => !String(entry).endsWith(".downloading"),
|
|
123
|
-
);
|
|
124
|
-
if (!hasPersistedFiles) {
|
|
125
|
-
return {
|
|
126
|
-
modelDir,
|
|
127
|
-
installed: false,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
modelDir,
|
|
133
|
-
installed: true,
|
|
134
|
-
source: "directory",
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function encodePathSegments(input: string): string {
|
|
139
|
-
return input
|
|
140
|
-
.split("/")
|
|
141
|
-
.map((segment) => encodeURIComponent(segment))
|
|
142
|
-
.join("/");
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function buildAuthHeaders(token?: string): Record<string, string> | undefined {
|
|
146
|
-
const t = String(token || "").trim();
|
|
147
|
-
if (!t) return undefined;
|
|
148
|
-
return { Authorization: `Bearer ${t}` };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function resolveSafeTargetPath(baseDir: string, relativePath: string): string {
|
|
152
|
-
const normalized = relativePath.replace(/\\/g, "/");
|
|
153
|
-
const target = path.resolve(baseDir, normalized);
|
|
154
|
-
const base = path.resolve(baseDir);
|
|
155
|
-
if (target !== base && !target.startsWith(`${base}${path.sep}`)) {
|
|
156
|
-
throw new Error(`Unsafe file path from HuggingFace: ${relativePath}`);
|
|
157
|
-
}
|
|
158
|
-
return target;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async function readModelFiles(input: {
|
|
162
|
-
repoId: string;
|
|
163
|
-
hfToken?: string;
|
|
164
|
-
}): Promise<string[]> {
|
|
165
|
-
const repoUrlPath = encodePathSegments(input.repoId);
|
|
166
|
-
const metadataUrl = `${HUGGINGFACE_HOST}/api/models/${repoUrlPath}`;
|
|
167
|
-
const response = await fetch(metadataUrl, {
|
|
168
|
-
headers: {
|
|
169
|
-
Accept: "application/json",
|
|
170
|
-
...(buildAuthHeaders(input.hfToken) || {}),
|
|
171
|
-
},
|
|
172
|
-
});
|
|
173
|
-
if (!response.ok) {
|
|
174
|
-
const responseText = await response.text().catch(() => "");
|
|
175
|
-
throw new Error(
|
|
176
|
-
`Failed to fetch model metadata (${input.repoId}): HTTP ${response.status}${responseText ? ` ${responseText.slice(0, 300)}` : ""}`,
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
const payload = (await response.json()) as HuggingFaceModelApiResponse;
|
|
180
|
-
const files = (Array.isArray(payload.siblings) ? payload.siblings : [])
|
|
181
|
-
.map((item) => String(item?.rfilename || "").trim())
|
|
182
|
-
.filter(Boolean)
|
|
183
|
-
.filter((item) => item !== ".gitattributes");
|
|
184
|
-
if (files.length === 0) {
|
|
185
|
-
throw new Error(`No downloadable files found in repo: ${input.repoId}`);
|
|
186
|
-
}
|
|
187
|
-
return files;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async function downloadModelFile(input: {
|
|
191
|
-
repoId: string;
|
|
192
|
-
revision: string;
|
|
193
|
-
filePath: string;
|
|
194
|
-
targetPath: string;
|
|
195
|
-
hfToken?: string;
|
|
196
|
-
}): Promise<void> {
|
|
197
|
-
const repoPath = encodePathSegments(input.repoId);
|
|
198
|
-
const revision = encodeURIComponent(input.revision);
|
|
199
|
-
const filePath = encodePathSegments(input.filePath);
|
|
200
|
-
const url = `${HUGGINGFACE_HOST}/${repoPath}/resolve/${revision}/${filePath}?download=1`;
|
|
201
|
-
const response = await fetch(url, {
|
|
202
|
-
headers: {
|
|
203
|
-
...(buildAuthHeaders(input.hfToken) || {}),
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
if (!response.ok) {
|
|
207
|
-
const responseText = await response.text().catch(() => "");
|
|
208
|
-
throw new Error(
|
|
209
|
-
`Failed to download ${input.filePath}: HTTP ${response.status}${responseText ? ` ${responseText.slice(0, 300)}` : ""}`,
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
if (!response.body) {
|
|
213
|
-
throw new Error(`Empty response body for file: ${input.filePath}`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const tempPath = `${input.targetPath}.downloading`;
|
|
217
|
-
await fsExtra.ensureDir(path.dirname(input.targetPath));
|
|
218
|
-
try {
|
|
219
|
-
const body = Readable.fromWeb(
|
|
220
|
-
response.body as unknown as globalThis.ReadableStream<Uint8Array>,
|
|
221
|
-
);
|
|
222
|
-
await pipeline(body, fs.createWriteStream(tempPath));
|
|
223
|
-
await fsExtra.move(tempPath, input.targetPath, { overwrite: true });
|
|
224
|
-
} catch (error) {
|
|
225
|
-
await fsExtra.remove(tempPath).catch(() => undefined);
|
|
226
|
-
throw error;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* 安装指定 voice 模型到本地目录。
|
|
232
|
-
*
|
|
233
|
-
* 关键点(中文)
|
|
234
|
-
* - 使用 HTTP 文件级下载,不依赖 git-lfs。
|
|
235
|
-
* - 默认复用已有文件;`force=true` 时覆盖重下。
|
|
236
|
-
*/
|
|
237
|
-
export async function installVoiceModelFromHuggingFace(input: {
|
|
238
|
-
model: VoiceModelCatalogItem;
|
|
239
|
-
modelsRootDir: string;
|
|
240
|
-
force?: boolean;
|
|
241
|
-
hfToken?: string;
|
|
242
|
-
onProgress?: (event: VoiceModelInstallProgressEvent) => void;
|
|
243
|
-
}): Promise<VoiceModelInstallResult> {
|
|
244
|
-
const modelDir = path.resolve(input.modelsRootDir, input.model.id);
|
|
245
|
-
await fsExtra.ensureDir(modelDir);
|
|
246
|
-
|
|
247
|
-
const files = await readModelFiles({
|
|
248
|
-
repoId: input.model.huggingfaceRepo,
|
|
249
|
-
hfToken: input.hfToken,
|
|
250
|
-
});
|
|
251
|
-
input.onProgress?.({
|
|
252
|
-
stage: "discover",
|
|
253
|
-
totalFiles: files.length,
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
let downloadedFiles = 0;
|
|
257
|
-
let skippedFiles = 0;
|
|
258
|
-
const downloadedFilePaths: string[] = [];
|
|
259
|
-
const skippedFilePaths: string[] = [];
|
|
260
|
-
for (let i = 0; i < files.length; i += 1) {
|
|
261
|
-
const filePath = files[i];
|
|
262
|
-
const targetPath = resolveSafeTargetPath(modelDir, filePath);
|
|
263
|
-
const exists = await fsExtra.pathExists(targetPath);
|
|
264
|
-
if (exists && input.force !== true) {
|
|
265
|
-
skippedFiles += 1;
|
|
266
|
-
skippedFilePaths.push(filePath);
|
|
267
|
-
input.onProgress?.({
|
|
268
|
-
stage: "skip",
|
|
269
|
-
filePath,
|
|
270
|
-
index: i + 1,
|
|
271
|
-
totalFiles: files.length,
|
|
272
|
-
});
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
input.onProgress?.({
|
|
276
|
-
stage: "download_start",
|
|
277
|
-
filePath,
|
|
278
|
-
index: i + 1,
|
|
279
|
-
totalFiles: files.length,
|
|
280
|
-
});
|
|
281
|
-
await downloadModelFile({
|
|
282
|
-
repoId: input.model.huggingfaceRepo,
|
|
283
|
-
revision: input.model.revision,
|
|
284
|
-
filePath,
|
|
285
|
-
targetPath,
|
|
286
|
-
hfToken: input.hfToken,
|
|
287
|
-
});
|
|
288
|
-
downloadedFiles += 1;
|
|
289
|
-
downloadedFilePaths.push(filePath);
|
|
290
|
-
input.onProgress?.({
|
|
291
|
-
stage: "download_done",
|
|
292
|
-
filePath,
|
|
293
|
-
index: i + 1,
|
|
294
|
-
totalFiles: files.length,
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
await fsExtra.writeJson(
|
|
299
|
-
path.join(modelDir, "downcity.voice.install.json"),
|
|
300
|
-
{
|
|
301
|
-
modelId: input.model.id,
|
|
302
|
-
label: input.model.label,
|
|
303
|
-
repoId: input.model.huggingfaceRepo,
|
|
304
|
-
revision: input.model.revision,
|
|
305
|
-
installedAt: new Date().toISOString(),
|
|
306
|
-
downloadedFiles,
|
|
307
|
-
skippedFiles,
|
|
308
|
-
},
|
|
309
|
-
{ spaces: 2 },
|
|
310
|
-
);
|
|
311
|
-
input.onProgress?.({
|
|
312
|
-
stage: "manifest",
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
return {
|
|
316
|
-
modelDir,
|
|
317
|
-
downloadedFiles,
|
|
318
|
-
skippedFiles,
|
|
319
|
-
downloadedFilePaths,
|
|
320
|
-
skippedFilePaths,
|
|
321
|
-
repoId: input.model.huggingfaceRepo,
|
|
322
|
-
revision: input.model.revision,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
|
|
4
|
-
function expandHomePath(inputPath: string): string {
|
|
5
|
-
const raw = String(inputPath || "").trim();
|
|
6
|
-
if (!raw) return raw;
|
|
7
|
-
if (raw === "~") return os.homedir();
|
|
8
|
-
if (raw.startsWith("~/")) {
|
|
9
|
-
return path.join(os.homedir(), raw.slice(2));
|
|
10
|
-
}
|
|
11
|
-
return raw;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 将相对路径解析为绝对路径;空值时回退默认目录。
|
|
16
|
-
*/
|
|
17
|
-
export function resolveVoiceModelsRootDir(input: {
|
|
18
|
-
projectRoot: string;
|
|
19
|
-
modelsDir?: string;
|
|
20
|
-
}): string {
|
|
21
|
-
const fallback = path.join(os.homedir(), ".downcity", "models", "voice");
|
|
22
|
-
const raw = expandHomePath(String(input.modelsDir || fallback).trim());
|
|
23
|
-
if (!raw) return path.resolve(fallback);
|
|
24
|
-
if (path.isAbsolute(raw)) return path.resolve(raw);
|
|
25
|
-
return path.resolve(input.projectRoot, raw);
|
|
26
|
-
}
|