vidspotai-shared 1.0.38 → 1.0.40
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/lib/globals/aiModels/enums.d.ts +1 -0
- package/lib/globals/aiModels/enums.d.ts.map +1 -1
- package/lib/globals/aiModels/enums.js +1 -0
- package/lib/globals/aiModels/providers/google.d.ts.map +1 -1
- package/lib/globals/aiModels/providers/google.js +26 -4
- package/lib/globals/aiModels/providers/kling.d.ts.map +1 -1
- package/lib/globals/aiModels/providers/kling.js +11 -3
- package/lib/globals/aiModels/types.d.ts +36 -0
- package/lib/globals/aiModels/types.d.ts.map +1 -1
- package/lib/globals/ttsModels/index.d.ts +2 -0
- package/lib/globals/ttsModels/index.d.ts.map +1 -1
- package/lib/globals/ttsModels/index.js +3 -1
- package/lib/globals/ttsModels/providers/elevenlabs.d.ts.map +1 -1
- package/lib/globals/ttsModels/providers/elevenlabs.js +16 -0
- package/lib/globals/ttsModels/types.d.ts +16 -0
- package/lib/globals/ttsModels/types.d.ts.map +1 -1
- package/lib/globals/types.d.ts +9 -2
- package/lib/globals/types.d.ts.map +1 -1
- package/lib/globals/types.js +7 -0
- package/lib/models/video.model.d.ts +2 -0
- package/lib/models/video.model.d.ts.map +1 -1
- package/lib/services/aiGen/aiGenFactory.service.d.ts.map +1 -1
- package/lib/services/aiGen/aiGenFactory.service.js +1 -0
- package/lib/services/aiGen/providers/alibaba/alibaba.d.ts +1 -1
- package/lib/services/aiGen/providers/alibaba/alibaba.d.ts.map +1 -1
- package/lib/services/aiGen/providers/alibaba/alibaba.js +8 -2
- package/lib/services/aiGen/providers/azure/azure.service.d.ts +1 -1
- package/lib/services/aiGen/providers/azure/azure.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/azure/azure.service.js +7 -2
- package/lib/services/aiGen/providers/bytedance/bytedance.service.js +3 -3
- package/lib/services/aiGen/providers/google/google.service.d.ts +1 -1
- package/lib/services/aiGen/providers/google/google.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/google.service.js +13 -4
- package/lib/services/aiGen/providers/kling/kling.service.d.ts +1 -1
- package/lib/services/aiGen/providers/kling/kling.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/kling/kling.service.js +51 -13
- package/lib/services/aiGen/providers/minimax/minimax.service.d.ts +1 -1
- package/lib/services/aiGen/providers/minimax/minimax.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/minimax/minimax.service.js +7 -2
- package/lib/services/aiGen/providers/openai/openai.service.d.ts +1 -1
- package/lib/services/aiGen/providers/openai/openai.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/openai/openai.service.js +7 -2
- package/lib/services/aiGen/providers/pixverse/pixverse.service.js +1 -1
- package/lib/services/aiGen/providers/runway/runway.service.d.ts +1 -1
- package/lib/services/aiGen/providers/runway/runway.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/runway/runway.service.js +8 -2
- package/lib/services/tts/providers/elevenlabs.service.d.ts +3 -1
- package/lib/services/tts/providers/elevenlabs.service.d.ts.map +1 -1
- package/lib/services/tts/providers/elevenlabs.service.js +11 -1
- package/lib/services/tts/ttsFactory.service.d.ts +1 -1
- package/lib/services/tts/ttsFactory.service.d.ts.map +1 -1
- package/lib/services/tts/ttsFactory.service.js +1 -1
- package/lib/services/tts/types.d.ts +19 -0
- package/lib/services/tts/types.d.ts.map +1 -1
- package/lib/utils/helpers.d.ts +13 -2
- package/lib/utils/helpers.d.ts.map +1 -1
- package/lib/utils/helpers.js +40 -16
- package/lib/utils/ttsUtils.d.ts +15 -0
- package/lib/utils/ttsUtils.d.ts.map +1 -1
- package/lib/utils/ttsUtils.js +41 -0
- package/package.json +1 -1
- package/lib/models/editorState.model.d.ts +0 -9
- package/lib/models/editorState.model.d.ts.map +0 -1
- package/lib/models/editorState.model.js +0 -2
|
@@ -95,24 +95,57 @@ class KlingService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
95
95
|
return await this.request(endpoint, method, body, timeout);
|
|
96
96
|
}
|
|
97
97
|
catch (err) {
|
|
98
|
+
const httpStatus = err.response?.status;
|
|
99
|
+
const klingCode = err.response?.data?.code;
|
|
98
100
|
const isTimeout = err.code === "ECONNABORTED" || err.message?.includes("timeout");
|
|
99
101
|
const isNetworkError = err.code === "ECONNRESET" ||
|
|
100
102
|
err.code === "ECONNREFUSED" ||
|
|
101
103
|
err.code === "ENOTFOUND" ||
|
|
102
104
|
err.code === "ERR_NETWORK" ||
|
|
103
105
|
err?.name === "AggregateError";
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
// Kling code 1102 = "Account balance not enough".
|
|
107
|
+
// Kling returns HTTP 429 for this, but it is NOT a transient rate-limit —
|
|
108
|
+
// retrying will always fail until the account is topped up.
|
|
109
|
+
const isInsufficientBalance = klingCode === 1102;
|
|
110
|
+
// True rate-limit (HTTP 429 without the balance code) — worth retrying with backoff.
|
|
111
|
+
const isRateLimit = httpStatus === 429 && !isInsufficientBalance;
|
|
112
|
+
const isRetryable = (isTimeout || isNetworkError || isRateLimit) && !isInsufficientBalance;
|
|
113
|
+
// Insufficient balance is a critical ops issue — surface immediately to Slack.
|
|
114
|
+
if (isInsufficientBalance) {
|
|
115
|
+
logger_1.logger.error("Kling account balance too low — top up required to continue video generation", {
|
|
116
|
+
endpoint,
|
|
117
|
+
klingCode,
|
|
118
|
+
klingMessage: err.response?.data?.message,
|
|
119
|
+
httpStatus,
|
|
120
|
+
});
|
|
106
121
|
throw err;
|
|
107
|
-
|
|
122
|
+
}
|
|
123
|
+
if (!isRetryable || attempt === maxAttempts) {
|
|
124
|
+
logger_1.logger.error("Kling API request failed (non-retryable or max attempts reached)", {
|
|
125
|
+
endpoint,
|
|
126
|
+
attempt,
|
|
127
|
+
maxAttempts,
|
|
128
|
+
httpStatus,
|
|
129
|
+
responseData: err.response?.data,
|
|
130
|
+
errorCode: err.code,
|
|
131
|
+
errorName: err?.name,
|
|
132
|
+
errorMessage: err.message,
|
|
133
|
+
});
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
136
|
+
const backoffMs = isRateLimit ? 10000 : 1000;
|
|
137
|
+
logger_1.logger.warn("Kling request failed — retrying", {
|
|
108
138
|
endpoint,
|
|
109
139
|
attempt,
|
|
110
140
|
maxAttempts,
|
|
111
141
|
timeoutMs: timeout,
|
|
142
|
+
httpStatus,
|
|
143
|
+
responseData: err.response?.data,
|
|
112
144
|
errorCode: err.code,
|
|
113
145
|
errorName: err?.name,
|
|
146
|
+
backoffMs,
|
|
114
147
|
});
|
|
115
|
-
await new Promise((r) => setTimeout(r,
|
|
148
|
+
await new Promise((r) => setTimeout(r, backoffMs));
|
|
116
149
|
}
|
|
117
150
|
}
|
|
118
151
|
throw new Error("Unreachable");
|
|
@@ -163,7 +196,7 @@ class KlingService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
163
196
|
if (result.data.task_status === "failed") {
|
|
164
197
|
return {
|
|
165
198
|
status: types_1.EVideoSceneStatus.FAILED,
|
|
166
|
-
errorMessage:
|
|
199
|
+
errorMessage: result.data.task_status_msg || result.message || "Kling task failed (no reason provided)",
|
|
167
200
|
};
|
|
168
201
|
}
|
|
169
202
|
return { status: types_1.EVideoSceneStatus.PENDING };
|
|
@@ -171,7 +204,7 @@ class KlingService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
171
204
|
async checkAvatarVideoStatus({ task, outputFilename, outputFilePath = "videos", }) {
|
|
172
205
|
const startTime = Date.now();
|
|
173
206
|
const result = await this.requestWithTimeoutRetry(`/v1/videos/avatar/image2video/${task}`, "GET");
|
|
174
|
-
logger_1.logger.info("Kling checkAvatarVideoStatus polled", { task, durationMs: Date.now() - startTime, status: result?.data?.task_status });
|
|
207
|
+
logger_1.logger.info("Kling checkAvatarVideoStatus polled", { task, durationMs: Date.now() - startTime, status: result?.data?.task_status, statusMsg: result?.data?.task_status_msg || undefined });
|
|
175
208
|
if (!result) {
|
|
176
209
|
return {
|
|
177
210
|
status: types_1.EVideoSceneStatus.FAILED,
|
|
@@ -206,7 +239,7 @@ class KlingService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
206
239
|
if (result.data.task_status === "failed") {
|
|
207
240
|
return {
|
|
208
241
|
status: types_1.EVideoSceneStatus.FAILED,
|
|
209
|
-
errorMessage:
|
|
242
|
+
errorMessage: result.data.task_status_msg || result.message || "Kling avatar task failed (no reason provided)",
|
|
210
243
|
};
|
|
211
244
|
}
|
|
212
245
|
return { status: types_1.EVideoSceneStatus.PENDING };
|
|
@@ -223,17 +256,22 @@ class KlingService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
223
256
|
};
|
|
224
257
|
return jsonwebtoken_1.default.sign(payload, process.env.KLING_SECRET_KEY, options);
|
|
225
258
|
}
|
|
226
|
-
getCreditUsed({ modelKey, mode = types_2.EVideoMode.PROFESSIONAL, duration = 5, }) {
|
|
259
|
+
getCreditUsed({ modelKey, mode = types_2.EVideoMode.PROFESSIONAL, duration = 5, multiClip = false, }) {
|
|
227
260
|
const modelConfig = aiModels_1.aiModelConfigs[modelKey];
|
|
228
|
-
|
|
261
|
+
const applyFloor = !multiClip;
|
|
262
|
+
// Avatar: per-second pricing based on mode (cost.perMode[mode].perSecond)
|
|
229
263
|
if (modelKey === "kling-avatar") {
|
|
230
|
-
const perSecond = modelConfig?.cost?.[mode]?.perSecond
|
|
231
|
-
?? modelConfig?.cost?.["std"]?.perSecond
|
|
264
|
+
const perSecond = modelConfig?.cost?.perMode?.[mode]?.perSecond
|
|
265
|
+
?? modelConfig?.cost?.perMode?.["std"]?.perSecond
|
|
232
266
|
?? 0.014;
|
|
233
|
-
return (0, helpers_2.getCreditsFromCost)(perSecond * duration);
|
|
267
|
+
return (0, helpers_2.getCreditsFromCost)(perSecond * duration, applyFloor);
|
|
234
268
|
}
|
|
235
269
|
const cost = modelConfig?.cost?.table?.[mode]?.[duration];
|
|
236
|
-
|
|
270
|
+
if (cost === undefined || cost === null) {
|
|
271
|
+
logger_1.logger.warn(`Kling getCreditUsed: no cost entry for modelKey="${modelKey}" mode="${mode}" duration=${duration} — returning fallback`, { modelKey, mode, duration });
|
|
272
|
+
return (0, helpers_2.getCreditsFromCost)(1.0, applyFloor);
|
|
273
|
+
}
|
|
274
|
+
return (0, helpers_2.getCreditsFromCost)(cost, applyFloor);
|
|
237
275
|
}
|
|
238
276
|
}
|
|
239
277
|
exports.KlingService = KlingService;
|
|
@@ -7,6 +7,6 @@ export declare class MinimaxService extends BaseAiGenProviderService {
|
|
|
7
7
|
private request;
|
|
8
8
|
generateVideo(params: VideoGenerationParams): Promise<VideoGenerationResult>;
|
|
9
9
|
checkVideoStatus({ task, outputFilename, outputFilePath, }: VideoStatusParams): Promise<VideoStatusResult>;
|
|
10
|
-
getCreditUsed({ modelKey, resolution, duration, }: CreditUsageParams): number;
|
|
10
|
+
getCreditUsed({ modelKey, resolution, duration, multiClip, }: CreditUsageParams): number;
|
|
11
11
|
}
|
|
12
12
|
//# sourceMappingURL=minimax.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"minimax.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/minimax/minimax.service.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"minimax.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/minimax/minimax.service.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAUlB,qBAAa,cAAe,SAAQ,wBAAwB;IAC1D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IAEpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;YAUlB,OAAO;IAiCf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA4B3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA6EjD,aAAa,CAAC,EACZ,QAAQ,EACR,UAAmB,EACnB,QAAY,EACZ,SAAiB,GAClB,EAAE,iBAAiB,GAAG,MAAM;CAW9B"}
|
|
@@ -11,6 +11,7 @@ const types_1 = require("../../../../globals/types");
|
|
|
11
11
|
const helpers_1 = require("../../helpers");
|
|
12
12
|
const baseAiGenProvider_service_1 = require("../baseAiGenProvider.service");
|
|
13
13
|
const helpers_2 = require("../../../../utils/helpers");
|
|
14
|
+
const logger_1 = require("../../../../utils/logger");
|
|
14
15
|
class MinimaxService extends baseAiGenProvider_service_1.BaseAiGenProviderService {
|
|
15
16
|
constructor() {
|
|
16
17
|
super();
|
|
@@ -108,11 +109,15 @@ class MinimaxService extends baseAiGenProvider_service_1.BaseAiGenProviderServic
|
|
|
108
109
|
// rest status are pending forms
|
|
109
110
|
return { status: types_1.EVideoSceneStatus.PENDING };
|
|
110
111
|
}
|
|
111
|
-
getCreditUsed({ modelKey, resolution = "512P", duration = 6, }) {
|
|
112
|
+
getCreditUsed({ modelKey, resolution = "512P", duration = 6, multiClip = false, }) {
|
|
112
113
|
const modelConfig = aiModels_1.aiModelConfigs[modelKey];
|
|
113
114
|
const cost = modelConfig.cost?.fixed ??
|
|
114
115
|
modelConfig.cost?.table?.[resolution]?.[duration];
|
|
115
|
-
|
|
116
|
+
if (cost === undefined || cost === null) {
|
|
117
|
+
logger_1.logger.warn(`Minimax getCreditUsed: no cost entry for modelKey="${modelKey}" resolution="${resolution}" duration=${duration} — returning fallback`, { modelKey, resolution, duration });
|
|
118
|
+
return (0, helpers_2.getCreditsFromCost)(1.0, !multiClip);
|
|
119
|
+
}
|
|
120
|
+
return (0, helpers_2.getCreditsFromCost)(cost, !multiClip);
|
|
116
121
|
}
|
|
117
122
|
}
|
|
118
123
|
exports.MinimaxService = MinimaxService;
|
|
@@ -7,6 +7,6 @@ export declare class OpenaiService extends BaseAiGenProviderService {
|
|
|
7
7
|
generateVideo(params: VideoGenerationParams): Promise<VideoGenerationResult>;
|
|
8
8
|
checkVideoStatus({ task, outputFilename, outputFilePath, }: VideoStatusParams): Promise<VideoStatusResult>;
|
|
9
9
|
generateText(params: TextGenerationParams): Promise<TextGenerationResult>;
|
|
10
|
-
getCreditUsed({ modelKey, resolution, duration, }: CreditUsageParams): number;
|
|
10
|
+
getCreditUsed({ modelKey, resolution, duration, multiClip, }: CreditUsageParams): number;
|
|
11
11
|
}
|
|
12
12
|
//# sourceMappingURL=openai.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/openai/openai.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"openai.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/openai/openai.service.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAElB,qBAAa,aAAc,SAAQ,wBAAwB;IACzD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,MAAM,CAAS;;IAQjB,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAoB3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAsC3C,YAAY,CAChB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,oBAAoB,CAAC;IA2ChC,aAAa,CAAC,EACZ,QAAQ,EACR,UAAuB,EACvB,QAAY,EACZ,SAAiB,GAClB,EAAE,iBAAiB,GAAG,MAAM;CAS9B"}
|
|
@@ -10,6 +10,7 @@ const aiModels_1 = require("../../../../globals/aiModels");
|
|
|
10
10
|
const types_1 = require("../../../../globals/types");
|
|
11
11
|
const firebase_1 = require("../../../../libs/firebase");
|
|
12
12
|
const utils_1 = require("../../../../utils");
|
|
13
|
+
const logger_1 = require("../../../../utils/logger");
|
|
13
14
|
const helpers_1 = require("../../helpers");
|
|
14
15
|
const baseAiGenProvider_service_1 = require("../baseAiGenProvider.service");
|
|
15
16
|
class OpenaiService extends baseAiGenProvider_service_1.BaseAiGenProviderService {
|
|
@@ -102,10 +103,14 @@ class OpenaiService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
102
103
|
text,
|
|
103
104
|
};
|
|
104
105
|
}
|
|
105
|
-
getCreditUsed({ modelKey, resolution = "720x1280", duration = 5, }) {
|
|
106
|
+
getCreditUsed({ modelKey, resolution = "720x1280", duration = 5, multiClip = false, }) {
|
|
106
107
|
const modelConfig = aiModels_1.aiModelConfigs[modelKey];
|
|
107
108
|
const cost = modelConfig?.cost?.table?.[resolution]?.[duration];
|
|
108
|
-
|
|
109
|
+
if (cost === undefined || cost === null) {
|
|
110
|
+
logger_1.logger.warn(`OpenAI getCreditUsed: no cost entry for modelKey="${modelKey}" resolution="${resolution}" duration=${duration} — returning fallback`, { modelKey, resolution, duration });
|
|
111
|
+
return (0, utils_1.getCreditsFromCost)(1.0, !multiClip);
|
|
112
|
+
}
|
|
113
|
+
return (0, utils_1.getCreditsFromCost)(cost, !multiClip);
|
|
109
114
|
}
|
|
110
115
|
}
|
|
111
116
|
exports.OpenaiService = OpenaiService;
|
|
@@ -177,7 +177,7 @@ class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderServi
|
|
|
177
177
|
logger_1.logger.warn(`Unsupported combination for PixVerse credit calc: ${JSON.stringify(params)}`);
|
|
178
178
|
return 10;
|
|
179
179
|
}
|
|
180
|
-
return (0, helpers_2.getCreditsFromCost)(cost);
|
|
180
|
+
return (0, helpers_2.getCreditsFromCost)(cost, !multiClip);
|
|
181
181
|
}
|
|
182
182
|
catch (err) {
|
|
183
183
|
logger_1.logger.error(`PixVerse credit calculation failed`, err);
|
|
@@ -7,6 +7,6 @@ export declare class RunwayService extends BaseAiGenProviderService {
|
|
|
7
7
|
private request;
|
|
8
8
|
generateVideo(params: VideoGenerationParams): Promise<VideoGenerationResult>;
|
|
9
9
|
checkVideoStatus({ task, outputFilename, outputFilePath, }: VideoStatusParams): Promise<VideoStatusResult>;
|
|
10
|
-
getCreditUsed({ modelKey, duration }: CreditUsageParams): number;
|
|
10
|
+
getCreditUsed({ modelKey, duration, multiClip }: CreditUsageParams): number;
|
|
11
11
|
}
|
|
12
12
|
//# sourceMappingURL=runway.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runway.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/runway/runway.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runway.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/runway/runway.service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAGlB,qBAAa,aAAc,SAAQ,wBAAwB;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;YAUlB,OAAO;IAgCf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAqD3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA4DjD,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAY,EAAE,SAAiB,EAAE,EAAE,iBAAiB,GAAG,MAAM;CASxF"}
|
|
@@ -9,6 +9,7 @@ const aiModels_1 = require("../../../../globals/aiModels");
|
|
|
9
9
|
const types_1 = require("../../../../globals/types");
|
|
10
10
|
const firebase_1 = require("../../../../libs/firebase");
|
|
11
11
|
const helpers_1 = require("../../../../utils/helpers");
|
|
12
|
+
const logger_1 = require("../../../../utils/logger");
|
|
12
13
|
const helpers_2 = require("../../helpers");
|
|
13
14
|
const baseAiGenProvider_service_1 = require("../baseAiGenProvider.service");
|
|
14
15
|
class RunwayService extends baseAiGenProvider_service_1.BaseAiGenProviderService {
|
|
@@ -126,9 +127,14 @@ class RunwayService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
126
127
|
// Still processing
|
|
127
128
|
return { status: types_1.EVideoSceneStatus.PENDING };
|
|
128
129
|
}
|
|
129
|
-
getCreditUsed({ modelKey, duration = 5 }) {
|
|
130
|
+
getCreditUsed({ modelKey, duration = 5, multiClip = false }) {
|
|
130
131
|
const modelConfig = aiModels_1.aiModelConfigs[modelKey];
|
|
131
|
-
|
|
132
|
+
const perSecond = modelConfig.cost?.perSecond;
|
|
133
|
+
if (perSecond === undefined || perSecond === null) {
|
|
134
|
+
logger_1.logger.warn(`Runway getCreditUsed: no perSecond cost config for modelKey="${modelKey}" — returning fallback`, { modelKey, duration });
|
|
135
|
+
return (0, helpers_1.getCreditsFromCost)(1.0, !multiClip);
|
|
136
|
+
}
|
|
137
|
+
return (0, helpers_1.getCreditsFromCost)(perSecond * duration, !multiClip);
|
|
132
138
|
}
|
|
133
139
|
}
|
|
134
140
|
exports.RunwayService = RunwayService;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { BaseTtsProviderService, TtsParams, TtsResult } from "../types";
|
|
1
|
+
import { BaseTtsProviderService, ITtsVoiceOption, TtsParams, TtsResult } from "../types";
|
|
2
2
|
export declare class ElevenLabsService extends BaseTtsProviderService {
|
|
3
3
|
private readonly baseUrl;
|
|
4
4
|
private readonly apiKey;
|
|
5
5
|
constructor();
|
|
6
6
|
generate(params: TtsParams): Promise<TtsResult>;
|
|
7
|
+
getVoices(): ITtsVoiceOption[];
|
|
8
|
+
mapLanguageCode(locale: string): string | undefined;
|
|
7
9
|
}
|
|
8
10
|
//# sourceMappingURL=elevenlabs.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elevenlabs.service.d.ts","sourceRoot":"","sources":["../../../../src/services/tts/providers/elevenlabs.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"elevenlabs.service.d.ts","sourceRoot":"","sources":["../../../../src/services/tts/providers/elevenlabs.service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEzF,qBAAa,iBAAkB,SAAQ,sBAAsB;IAC3D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAE1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAW1B,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAmCrD,SAAS,IAAI,eAAe,EAAE;IAI9B,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAGpD"}
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.ElevenLabsService = void 0;
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const elevenlabs_1 = require("../../../globals/ttsModels/providers/elevenlabs");
|
|
9
|
+
const voices_1 = require("../../../globals/ttsModels/voices");
|
|
9
10
|
const types_1 = require("../types");
|
|
10
11
|
class ElevenLabsService extends types_1.BaseTtsProviderService {
|
|
11
12
|
constructor() {
|
|
@@ -19,10 +20,13 @@ class ElevenLabsService extends types_1.BaseTtsProviderService {
|
|
|
19
20
|
}
|
|
20
21
|
async generate(params) {
|
|
21
22
|
const voiceId = params.voiceId ?? elevenlabs_1.elevenlabsConfig.defaultVoiceId;
|
|
23
|
+
const languageCode = params.languageCode
|
|
24
|
+
? this.mapLanguageCode(params.languageCode)
|
|
25
|
+
: undefined;
|
|
22
26
|
const response = await axios_1.default.post(`${this.baseUrl}/text-to-speech/${voiceId}`, {
|
|
23
27
|
text: params.text,
|
|
24
28
|
model_id: elevenlabs_1.elevenlabsConfig.modelId,
|
|
25
|
-
language_code:
|
|
29
|
+
language_code: languageCode,
|
|
26
30
|
voice_settings: {
|
|
27
31
|
stability: 0.5,
|
|
28
32
|
similarity_boost: 0.75,
|
|
@@ -42,5 +46,11 @@ class ElevenLabsService extends types_1.BaseTtsProviderService {
|
|
|
42
46
|
extension: "mp3",
|
|
43
47
|
};
|
|
44
48
|
}
|
|
49
|
+
getVoices() {
|
|
50
|
+
return voices_1.ELEVENLABS_VOICES;
|
|
51
|
+
}
|
|
52
|
+
mapLanguageCode(locale) {
|
|
53
|
+
return voices_1.LANG_TO_ELEVENLABS_CODE[locale] ?? locale;
|
|
54
|
+
}
|
|
45
55
|
}
|
|
46
56
|
exports.ElevenLabsService = ElevenLabsService;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { TtsProvider } from "../../globals/ttsModels/types";
|
|
2
2
|
import { BaseTtsProviderService } from "./types";
|
|
3
|
-
export declare function getTtsProviderService(provider
|
|
3
|
+
export declare function getTtsProviderService(provider: TtsProvider): BaseTtsProviderService;
|
|
4
4
|
//# sourceMappingURL=ttsFactory.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ttsFactory.service.d.ts","sourceRoot":"","sources":["../../../src/services/tts/ttsFactory.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAE5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEjD,wBAAgB,qBAAqB,CAAC,QAAQ,
|
|
1
|
+
{"version":3,"file":"ttsFactory.service.d.ts","sourceRoot":"","sources":["../../../src/services/tts/ttsFactory.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAE5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEjD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,WAAW,GAAG,sBAAsB,CAOnF"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getTtsProviderService = getTtsProviderService;
|
|
4
4
|
const elevenlabs_service_1 = require("./providers/elevenlabs.service");
|
|
5
|
-
function getTtsProviderService(provider
|
|
5
|
+
function getTtsProviderService(provider) {
|
|
6
6
|
switch (provider) {
|
|
7
7
|
case "elevenlabs":
|
|
8
8
|
return new elevenlabs_service_1.ElevenLabsService();
|
|
@@ -8,7 +8,26 @@ export interface TtsResult {
|
|
|
8
8
|
mimeType: string;
|
|
9
9
|
extension: string;
|
|
10
10
|
}
|
|
11
|
+
export interface ITtsVoiceOption {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
gender?: "male" | "female";
|
|
15
|
+
accent?: string;
|
|
16
|
+
/** Public preview audio URL hosted by the provider (optional — not all providers offer this). */
|
|
17
|
+
previewUrl?: string;
|
|
18
|
+
}
|
|
11
19
|
export declare abstract class BaseTtsProviderService {
|
|
12
20
|
abstract generate(params: TtsParams): Promise<TtsResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the list of voices available for this provider.
|
|
23
|
+
* Used by controllers for voice validation and the /voices endpoint.
|
|
24
|
+
*/
|
|
25
|
+
abstract getVoices(): ITtsVoiceOption[];
|
|
26
|
+
/**
|
|
27
|
+
* Maps a standard locale code (e.g. "en", "fr-FR") to the provider's
|
|
28
|
+
* expected language code format. Returns undefined if no mapping needed.
|
|
29
|
+
* Each provider implements its own mapping table internally.
|
|
30
|
+
*/
|
|
31
|
+
abstract mapLanguageCode(locale: string): string | undefined;
|
|
13
32
|
}
|
|
14
33
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/tts/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,8BAAsB,sBAAsB;IAC1C,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/tts/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,8BAAsB,sBAAsB;IAC1C,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAExD;;;OAGG;IACH,QAAQ,CAAC,SAAS,IAAI,eAAe,EAAE;IAEvC;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAC7D"}
|
package/lib/utils/helpers.d.ts
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import { ELANGUAGE_CODE, ERENEWAL_FREQUENCY, ESUBSCRIPTION_PLANS, IDetectLang } from "../globals/types";
|
|
1
|
+
import { ELANGUAGE_CODE, ERENEWAL_FREQUENCY, ESUBSCRIPTION_PLANS, EVideoDurationType, IDetectLang } from "../globals/types";
|
|
2
2
|
import z from "zod";
|
|
3
3
|
import { ETextGenModels } from "../globals";
|
|
4
4
|
export declare const getPlanTypeById: (priceId: string) => ESUBSCRIPTION_PLANS | null;
|
|
5
5
|
export declare const getFreqById: (priceId: string) => ERENEWAL_FREQUENCY | null;
|
|
6
6
|
export declare const getPriceIdByType: (type: ESUBSCRIPTION_PLANS, freq?: ERENEWAL_FREQUENCY) => string;
|
|
7
|
-
export declare const getCreditsFromCost: (cost: number) => number;
|
|
7
|
+
export declare const getCreditsFromCost: (cost: number, applyFloor?: boolean) => number;
|
|
8
|
+
/**
|
|
9
|
+
* Discount factors applied to the total clip cost for multi-clip video types.
|
|
10
|
+
* Implements economies-of-scale pricing: higher volume → lower per-clip margin.
|
|
11
|
+
*
|
|
12
|
+
* SHORT: 1.00 → 2.0x effective markup (no discount)
|
|
13
|
+
* MEDIUM: 0.85 → ~1.7x effective markup (15% bulk discount)
|
|
14
|
+
* LONG: 0.75 → ~1.5x effective markup (25% bulk discount)
|
|
15
|
+
* VERY_LONG: 0.65 → ~1.3x effective markup (35% bulk discount)
|
|
16
|
+
*/
|
|
17
|
+
export declare const MULTI_CLIP_DISCOUNT_FACTORS: Partial<Record<EVideoDurationType, number>>;
|
|
18
|
+
export declare function getMultiClipDiscountFactor(durationType: EVideoDurationType): number;
|
|
8
19
|
export declare const getTranslationWrapper: (text: string, from: ELANGUAGE_CODE, to?: ELANGUAGE_CODE, version?: "v1" | "v2") => Promise<string>;
|
|
9
20
|
export declare const detectLang: (text: string) => Promise<IDetectLang>;
|
|
10
21
|
export declare function waitForFile(path: string, timeout?: number, interval?: number): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAElB,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAE1B,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,KACd,mBAAmB,GAAG,IASxB,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,SAAS,MAAM,KAAG,kBAAkB,GAAG,IASlE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,MAAM,mBAAmB,EACzB,OAAM,kBAA+C,WAgBtD,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,oBAAiB,KAAG,MASpE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAInF,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,kBAAkB,GAAG,MAAM,CAEnF;AAED,eAAO,MAAM,qBAAqB,GAChC,MAAM,MAAM,EACZ,MAAM,cAAc,EACpB,KAAI,cAAkC,EACtC,UAAS,IAAI,GAAG,IAAW,KAC1B,OAAO,CAAC,MAAM,CAmBhB,CAAC;AAEF,eAAO,MAAM,UAAU,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,WAAW,CAqBlE,CAAC;AAEF,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,SAAQ,EACf,QAAQ,SAAM,oBAQf;AAGD,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,SAAS,EAAE,CAAC,CAAC;IACb,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAGxB,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AA+CrB,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAatE"}
|
package/lib/utils/helpers.js
CHANGED
|
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.detectLang = exports.getTranslationWrapper = exports.getCreditsFromCost = exports.getPriceIdByType = exports.getFreqById = exports.getPlanTypeById = void 0;
|
|
6
|
+
exports.detectLang = exports.getTranslationWrapper = exports.MULTI_CLIP_DISCOUNT_FACTORS = exports.getCreditsFromCost = exports.getPriceIdByType = exports.getFreqById = exports.getPlanTypeById = void 0;
|
|
7
|
+
exports.getMultiClipDiscountFactor = getMultiClipDiscountFactor;
|
|
7
8
|
exports.waitForFile = waitForFile;
|
|
8
9
|
exports.generateAndValidate = generateAndValidate;
|
|
9
10
|
exports.getVideoDurationRangeText = getVideoDurationRangeText;
|
|
@@ -48,18 +49,32 @@ const getPriceIdByType = (type, freq = types_1.ERENEWAL_FREQUENCY.MONTHLY) => {
|
|
|
48
49
|
: planDetails?.monthly?.id;
|
|
49
50
|
};
|
|
50
51
|
exports.getPriceIdByType = getPriceIdByType;
|
|
51
|
-
const getCreditsFromCost = (cost) => {
|
|
52
|
-
const
|
|
53
|
-
const costPerCredit = 0.1; // multiple
|
|
52
|
+
const getCreditsFromCost = (cost, applyFloor = true) => {
|
|
53
|
+
const costPerCredit = 0.1;
|
|
54
54
|
const minCredits = 10;
|
|
55
55
|
if (cost <= 0)
|
|
56
56
|
return 0;
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const credits = Math.ceil(rawCredits);
|
|
60
|
-
return Math.max(credits, minCredits);
|
|
57
|
+
const credits = Math.ceil(cost / costPerCredit);
|
|
58
|
+
return applyFloor ? Math.max(credits, minCredits) : credits;
|
|
61
59
|
};
|
|
62
60
|
exports.getCreditsFromCost = getCreditsFromCost;
|
|
61
|
+
/**
|
|
62
|
+
* Discount factors applied to the total clip cost for multi-clip video types.
|
|
63
|
+
* Implements economies-of-scale pricing: higher volume → lower per-clip margin.
|
|
64
|
+
*
|
|
65
|
+
* SHORT: 1.00 → 2.0x effective markup (no discount)
|
|
66
|
+
* MEDIUM: 0.85 → ~1.7x effective markup (15% bulk discount)
|
|
67
|
+
* LONG: 0.75 → ~1.5x effective markup (25% bulk discount)
|
|
68
|
+
* VERY_LONG: 0.65 → ~1.3x effective markup (35% bulk discount)
|
|
69
|
+
*/
|
|
70
|
+
exports.MULTI_CLIP_DISCOUNT_FACTORS = {
|
|
71
|
+
[types_1.EVideoDurationType.MEDIUM]: 0.85,
|
|
72
|
+
[types_1.EVideoDurationType.LONG]: 0.75,
|
|
73
|
+
[types_1.EVideoDurationType.VERY_LONG]: 0.65,
|
|
74
|
+
};
|
|
75
|
+
function getMultiClipDiscountFactor(durationType) {
|
|
76
|
+
return exports.MULTI_CLIP_DISCOUNT_FACTORS[durationType] ?? 1.0;
|
|
77
|
+
}
|
|
63
78
|
const getTranslationWrapper = async (text, from, to = types_1.ELANGUAGE_CODE.en, version = "v2") => {
|
|
64
79
|
try {
|
|
65
80
|
const res = await axios_1.default.post("https://us-central1-translation-service-3931b.cloudfunctions.net/translate", { text, from, to, version }, {
|
|
@@ -76,14 +91,23 @@ const getTranslationWrapper = async (text, from, to = types_1.ELANGUAGE_CODE.en,
|
|
|
76
91
|
};
|
|
77
92
|
exports.getTranslationWrapper = getTranslationWrapper;
|
|
78
93
|
const detectLang = async (text) => {
|
|
79
|
-
|
|
80
|
-
text,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
94
|
+
try {
|
|
95
|
+
const res = await axios_1.default.post("https://us-central1-translation-service-3931b.cloudfunctions.net/detectLang", { text }, {
|
|
96
|
+
headers: { "internal-key": process.env.TRANSLATION_SERVICE_KEY || "" },
|
|
97
|
+
timeout: 8000,
|
|
98
|
+
});
|
|
99
|
+
return res.data;
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
// External service failure — default to English so video generation is not blocked
|
|
103
|
+
console.warn("detectLang failed, defaulting to English:", err?.message ?? err);
|
|
104
|
+
return {
|
|
105
|
+
reliable: false,
|
|
106
|
+
textBytes: 0,
|
|
107
|
+
languages: [{ name: "English", code: types_1.ELANGUAGE_CODE.en, percent: 100, score: 0 }],
|
|
108
|
+
chunks: [],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
87
111
|
};
|
|
88
112
|
exports.detectLang = detectLang;
|
|
89
113
|
async function waitForFile(path, timeout = 10000, interval = 300) {
|
package/lib/utils/ttsUtils.d.ts
CHANGED
|
@@ -8,4 +8,19 @@
|
|
|
8
8
|
* @param wordsPerSecond - Speaking rate from the TTS provider config
|
|
9
9
|
*/
|
|
10
10
|
export declare function estimateTtsDurationSeconds(text: string, wordsPerSecond: number): number;
|
|
11
|
+
/**
|
|
12
|
+
* Splits a long TTS text into chunks whose estimated spoken duration is at or
|
|
13
|
+
* below `maxDurationSeconds`.
|
|
14
|
+
*
|
|
15
|
+
* Splitting happens at sentence-boundary punctuation (., !, ?, newlines) so
|
|
16
|
+
* the generated audio sounds natural. Each chunk is also capped at `maxCharsPerRequest`
|
|
17
|
+
* (the provider's per-call character limit from ITtsProviderConfig).
|
|
18
|
+
*
|
|
19
|
+
* @param text - The full TTS input text
|
|
20
|
+
* @param wordsPerSecond - Speaking rate from the TTS provider config
|
|
21
|
+
* @param maxDurationSeconds - Maximum spoken duration per chunk (should include safety buffer)
|
|
22
|
+
* @param maxCharsPerRequest - Provider's per-call character limit from ITtsProviderConfig
|
|
23
|
+
* @returns Array of text chunks, each safe to send in a single TTS API call
|
|
24
|
+
*/
|
|
25
|
+
export declare function splitTtsTextByDuration(text: string, wordsPerSecond: number, maxDurationSeconds: number, maxCharsPerRequest: number): string[];
|
|
11
26
|
//# sourceMappingURL=ttsUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ttsUtils.d.ts","sourceRoot":"","sources":["../../src/utils/ttsUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAGvF"}
|
|
1
|
+
{"version":3,"file":"ttsUtils.d.ts","sourceRoot":"","sources":["../../src/utils/ttsUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAGvF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,MAAM,EACtB,kBAAkB,EAAE,MAAM,EAC1B,kBAAkB,EAAE,MAAM,GACzB,MAAM,EAAE,CA8BV"}
|
package/lib/utils/ttsUtils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.estimateTtsDurationSeconds = estimateTtsDurationSeconds;
|
|
4
|
+
exports.splitTtsTextByDuration = splitTtsTextByDuration;
|
|
4
5
|
/**
|
|
5
6
|
* Estimates the spoken duration of a TTS text in seconds.
|
|
6
7
|
*
|
|
@@ -14,3 +15,43 @@ function estimateTtsDurationSeconds(text, wordsPerSecond) {
|
|
|
14
15
|
const words = text.trim().split(/\s+/).length;
|
|
15
16
|
return Math.ceil(words / wordsPerSecond);
|
|
16
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Splits a long TTS text into chunks whose estimated spoken duration is at or
|
|
20
|
+
* below `maxDurationSeconds`.
|
|
21
|
+
*
|
|
22
|
+
* Splitting happens at sentence-boundary punctuation (., !, ?, newlines) so
|
|
23
|
+
* the generated audio sounds natural. Each chunk is also capped at `maxCharsPerRequest`
|
|
24
|
+
* (the provider's per-call character limit from ITtsProviderConfig).
|
|
25
|
+
*
|
|
26
|
+
* @param text - The full TTS input text
|
|
27
|
+
* @param wordsPerSecond - Speaking rate from the TTS provider config
|
|
28
|
+
* @param maxDurationSeconds - Maximum spoken duration per chunk (should include safety buffer)
|
|
29
|
+
* @param maxCharsPerRequest - Provider's per-call character limit from ITtsProviderConfig
|
|
30
|
+
* @returns Array of text chunks, each safe to send in a single TTS API call
|
|
31
|
+
*/
|
|
32
|
+
function splitTtsTextByDuration(text, wordsPerSecond, maxDurationSeconds, maxCharsPerRequest) {
|
|
33
|
+
const maxWords = Math.floor(maxDurationSeconds * wordsPerSecond);
|
|
34
|
+
// Split at sentence boundaries, keeping the delimiter attached to the sentence
|
|
35
|
+
const sentences = text.match(/[^.!?\n]+[.!?\n]?/g) ?? [text];
|
|
36
|
+
const chunks = [];
|
|
37
|
+
let currentChunk = "";
|
|
38
|
+
let currentWords = 0;
|
|
39
|
+
for (const sentence of sentences) {
|
|
40
|
+
const sentenceWords = sentence.trim().split(/\s+/).length;
|
|
41
|
+
const wouldExceedDuration = currentWords + sentenceWords > maxWords;
|
|
42
|
+
const wouldExceedChars = currentChunk.length + sentence.length > maxCharsPerRequest;
|
|
43
|
+
if (currentChunk && (wouldExceedDuration || wouldExceedChars)) {
|
|
44
|
+
chunks.push(currentChunk.trim());
|
|
45
|
+
currentChunk = sentence;
|
|
46
|
+
currentWords = sentenceWords;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
currentChunk += sentence;
|
|
50
|
+
currentWords += sentenceWords;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (currentChunk.trim()) {
|
|
54
|
+
chunks.push(currentChunk.trim());
|
|
55
|
+
}
|
|
56
|
+
return chunks.filter(Boolean);
|
|
57
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { FieldValue } from "firebase-admin/firestore";
|
|
2
|
-
export interface IEditorStateModel {
|
|
3
|
-
videoJobId: string;
|
|
4
|
-
userId: string;
|
|
5
|
-
design: Record<string, any>;
|
|
6
|
-
createdAt: FieldValue;
|
|
7
|
-
updatedAt: FieldValue;
|
|
8
|
-
}
|
|
9
|
-
//# sourceMappingURL=editorState.model.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"editorState.model.d.ts","sourceRoot":"","sources":["../../src/models/editorState.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,SAAS,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC;CACvB"}
|