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.
Files changed (64) hide show
  1. package/lib/globals/aiModels/enums.d.ts +1 -0
  2. package/lib/globals/aiModels/enums.d.ts.map +1 -1
  3. package/lib/globals/aiModels/enums.js +1 -0
  4. package/lib/globals/aiModels/providers/google.d.ts.map +1 -1
  5. package/lib/globals/aiModels/providers/google.js +26 -4
  6. package/lib/globals/aiModels/providers/kling.d.ts.map +1 -1
  7. package/lib/globals/aiModels/providers/kling.js +11 -3
  8. package/lib/globals/aiModels/types.d.ts +36 -0
  9. package/lib/globals/aiModels/types.d.ts.map +1 -1
  10. package/lib/globals/ttsModels/index.d.ts +2 -0
  11. package/lib/globals/ttsModels/index.d.ts.map +1 -1
  12. package/lib/globals/ttsModels/index.js +3 -1
  13. package/lib/globals/ttsModels/providers/elevenlabs.d.ts.map +1 -1
  14. package/lib/globals/ttsModels/providers/elevenlabs.js +16 -0
  15. package/lib/globals/ttsModels/types.d.ts +16 -0
  16. package/lib/globals/ttsModels/types.d.ts.map +1 -1
  17. package/lib/globals/types.d.ts +9 -2
  18. package/lib/globals/types.d.ts.map +1 -1
  19. package/lib/globals/types.js +7 -0
  20. package/lib/models/video.model.d.ts +2 -0
  21. package/lib/models/video.model.d.ts.map +1 -1
  22. package/lib/services/aiGen/aiGenFactory.service.d.ts.map +1 -1
  23. package/lib/services/aiGen/aiGenFactory.service.js +1 -0
  24. package/lib/services/aiGen/providers/alibaba/alibaba.d.ts +1 -1
  25. package/lib/services/aiGen/providers/alibaba/alibaba.d.ts.map +1 -1
  26. package/lib/services/aiGen/providers/alibaba/alibaba.js +8 -2
  27. package/lib/services/aiGen/providers/azure/azure.service.d.ts +1 -1
  28. package/lib/services/aiGen/providers/azure/azure.service.d.ts.map +1 -1
  29. package/lib/services/aiGen/providers/azure/azure.service.js +7 -2
  30. package/lib/services/aiGen/providers/bytedance/bytedance.service.js +3 -3
  31. package/lib/services/aiGen/providers/google/google.service.d.ts +1 -1
  32. package/lib/services/aiGen/providers/google/google.service.d.ts.map +1 -1
  33. package/lib/services/aiGen/providers/google/google.service.js +13 -4
  34. package/lib/services/aiGen/providers/kling/kling.service.d.ts +1 -1
  35. package/lib/services/aiGen/providers/kling/kling.service.d.ts.map +1 -1
  36. package/lib/services/aiGen/providers/kling/kling.service.js +51 -13
  37. package/lib/services/aiGen/providers/minimax/minimax.service.d.ts +1 -1
  38. package/lib/services/aiGen/providers/minimax/minimax.service.d.ts.map +1 -1
  39. package/lib/services/aiGen/providers/minimax/minimax.service.js +7 -2
  40. package/lib/services/aiGen/providers/openai/openai.service.d.ts +1 -1
  41. package/lib/services/aiGen/providers/openai/openai.service.d.ts.map +1 -1
  42. package/lib/services/aiGen/providers/openai/openai.service.js +7 -2
  43. package/lib/services/aiGen/providers/pixverse/pixverse.service.js +1 -1
  44. package/lib/services/aiGen/providers/runway/runway.service.d.ts +1 -1
  45. package/lib/services/aiGen/providers/runway/runway.service.d.ts.map +1 -1
  46. package/lib/services/aiGen/providers/runway/runway.service.js +8 -2
  47. package/lib/services/tts/providers/elevenlabs.service.d.ts +3 -1
  48. package/lib/services/tts/providers/elevenlabs.service.d.ts.map +1 -1
  49. package/lib/services/tts/providers/elevenlabs.service.js +11 -1
  50. package/lib/services/tts/ttsFactory.service.d.ts +1 -1
  51. package/lib/services/tts/ttsFactory.service.d.ts.map +1 -1
  52. package/lib/services/tts/ttsFactory.service.js +1 -1
  53. package/lib/services/tts/types.d.ts +19 -0
  54. package/lib/services/tts/types.d.ts.map +1 -1
  55. package/lib/utils/helpers.d.ts +13 -2
  56. package/lib/utils/helpers.d.ts.map +1 -1
  57. package/lib/utils/helpers.js +40 -16
  58. package/lib/utils/ttsUtils.d.ts +15 -0
  59. package/lib/utils/ttsUtils.d.ts.map +1 -1
  60. package/lib/utils/ttsUtils.js +41 -0
  61. package/package.json +1 -1
  62. package/lib/models/editorState.model.d.ts +0 -9
  63. package/lib/models/editorState.model.d.ts.map +0 -1
  64. 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
- const isRetryable = isTimeout || isNetworkError;
105
- if (!isRetryable || attempt === maxAttempts)
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
- logger_1.logger.warn("Kling request failed with transient error — retrying", {
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, 1000));
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: `${result.code}: ${result.message}`,
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: `${result.code}: ${result.message}`,
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
- // Avatar: per-second pricing based on mode
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
- return (0, helpers_2.getCreditsFromCost)(cost ?? 1);
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;AASlB,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,GACb,EAAE,iBAAiB,GAAG,MAAM;CAO9B"}
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
- return (0, helpers_2.getCreditsFromCost)(cost ?? 0.5); // default to 10 cents if unknown // log error as well
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":"AAcA,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,GACb,EAAE,iBAAiB,GAAG,MAAM;CAK9B"}
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
- return (0, utils_1.getCreditsFromCost)(cost ?? 2);
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":"AAMA,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,EAAE,iBAAiB,GAAG,MAAM;CAIrE"}
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
- return (0, helpers_1.getCreditsFromCost)(modelConfig.cost?.perSecond ?? duration);
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":"AAEA,OAAO,EAAE,sBAAsB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAExE,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;CA+BtD"}
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: params.languageCode,
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?: TtsProvider): BaseTtsProviderService;
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,GAAE,WAA0B,GAAG,sBAAsB,CAOlG"}
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 = "elevenlabs") {
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;CACzD"}
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"}
@@ -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,EAEnB,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,KAAG,MAajD,CAAC;AAEF,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,CAclE,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"}
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"}
@@ -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 margin = 0.2; // 20% margin
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 totalCost = cost * (1 + margin);
58
- const rawCredits = totalCost / costPerCredit;
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
- const res = await axios_1.default.post("https://us-central1-translation-service-3931b.cloudfunctions.net/detectLang", {
80
- text,
81
- }, {
82
- headers: {
83
- "internal-key": process.env.TRANSLATION_SERVICE_KEY || "",
84
- },
85
- });
86
- return res.data;
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) {
@@ -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"}
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "vidspotai-shared",
3
- "version": "1.0.38",
3
+ "version": "1.0.40",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "exports": {
@@ -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"}
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });