@hed-hog/lms 0.0.365 → 0.0.366

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 (113) hide show
  1. package/dist/class-group/class-group.controller.d.ts +1 -0
  2. package/dist/class-group/class-group.controller.d.ts.map +1 -1
  3. package/dist/class-group/class-group.service.d.ts +1 -0
  4. package/dist/class-group/class-group.service.d.ts.map +1 -1
  5. package/dist/course/course-structure.controller.d.ts +4 -2
  6. package/dist/course/course-structure.controller.d.ts.map +1 -1
  7. package/dist/course/course-structure.controller.js +6 -3
  8. package/dist/course/course-structure.controller.js.map +1 -1
  9. package/dist/course/course-video-agent-pipeline.service.d.ts +70 -0
  10. package/dist/course/course-video-agent-pipeline.service.d.ts.map +1 -0
  11. package/dist/course/course-video-agent-pipeline.service.js +398 -0
  12. package/dist/course/course-video-agent-pipeline.service.js.map +1 -0
  13. package/dist/course/course-video-hls.service.d.ts +14 -0
  14. package/dist/course/course-video-hls.service.d.ts.map +1 -1
  15. package/dist/course/course-video-hls.service.js +25 -8
  16. package/dist/course/course-video-hls.service.js.map +1 -1
  17. package/dist/course/course.controller.d.ts +2 -0
  18. package/dist/course/course.controller.d.ts.map +1 -1
  19. package/dist/course/course.module.d.ts.map +1 -1
  20. package/dist/course/course.module.js +5 -0
  21. package/dist/course/course.module.js.map +1 -1
  22. package/dist/course/course.service.d.ts +2 -0
  23. package/dist/course/course.service.d.ts.map +1 -1
  24. package/dist/course/course.service.js +36 -2
  25. package/dist/course/course.service.js.map +1 -1
  26. package/dist/course/ffmpeg.util.d.ts +10 -0
  27. package/dist/course/ffmpeg.util.d.ts.map +1 -0
  28. package/dist/course/ffmpeg.util.js +79 -0
  29. package/dist/course/ffmpeg.util.js.map +1 -0
  30. package/dist/course/lms-bulk-upload-automation.service.d.ts +3 -1
  31. package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -1
  32. package/dist/course/lms-bulk-upload-automation.service.js +7 -3
  33. package/dist/course/lms-bulk-upload-automation.service.js.map +1 -1
  34. package/dist/enterprise/training/training-admin.controller.d.ts +2 -0
  35. package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
  36. package/dist/enterprise/training/training-admin.service.d.ts +2 -0
  37. package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
  38. package/dist/lms.module.d.ts.map +1 -1
  39. package/dist/lms.module.js +10 -0
  40. package/dist/lms.module.js.map +1 -1
  41. package/dist/platforma/dto/heartbeat.dto.d.ts +9 -0
  42. package/dist/platforma/dto/heartbeat.dto.d.ts.map +1 -0
  43. package/dist/platforma/dto/heartbeat.dto.js +50 -0
  44. package/dist/platforma/dto/heartbeat.dto.js.map +1 -0
  45. package/dist/platforma/handlers/emit-certificate.handler.d.ts +27 -0
  46. package/dist/platforma/handlers/emit-certificate.handler.d.ts.map +1 -0
  47. package/dist/platforma/handlers/emit-certificate.handler.js +117 -0
  48. package/dist/platforma/handlers/emit-certificate.handler.js.map +1 -0
  49. package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts +31 -0
  50. package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts.map +1 -0
  51. package/dist/platforma/handlers/lesson-heartbeat.handler.js +281 -0
  52. package/dist/platforma/handlers/lesson-heartbeat.handler.js.map +1 -0
  53. package/dist/platforma/platforma-heartbeat.service.d.ts +10 -0
  54. package/dist/platforma/platforma-heartbeat.service.d.ts.map +1 -0
  55. package/dist/platforma/platforma-heartbeat.service.js +50 -0
  56. package/dist/platforma/platforma-heartbeat.service.js.map +1 -0
  57. package/dist/platforma/platforma-performance.service.d.ts +121 -0
  58. package/dist/platforma/platforma-performance.service.d.ts.map +1 -0
  59. package/dist/platforma/platforma-performance.service.js +500 -0
  60. package/dist/platforma/platforma-performance.service.js.map +1 -0
  61. package/dist/platforma/platforma-search.service.d.ts +21 -0
  62. package/dist/platforma/platforma-search.service.d.ts.map +1 -0
  63. package/dist/platforma/platforma-search.service.js +64 -0
  64. package/dist/platforma/platforma-search.service.js.map +1 -0
  65. package/dist/platforma/platforma.controller.d.ts +115 -1
  66. package/dist/platforma/platforma.controller.d.ts.map +1 -1
  67. package/dist/platforma/platforma.controller.js +50 -2
  68. package/dist/platforma/platforma.controller.js.map +1 -1
  69. package/dist/realtime/lms-realtime.controller.d.ts +2 -0
  70. package/dist/realtime/lms-realtime.controller.d.ts.map +1 -1
  71. package/dist/realtime/lms-realtime.controller.js +31 -0
  72. package/dist/realtime/lms-realtime.controller.js.map +1 -1
  73. package/dist/realtime/lms-realtime.service.d.ts +1 -1
  74. package/dist/realtime/lms-realtime.service.d.ts.map +1 -1
  75. package/dist/realtime/lms-realtime.service.js.map +1 -1
  76. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +182 -29
  77. package/hedhog/frontend/app/classes/_components/classes-calendar-view.tsx.ejs +277 -0
  78. package/hedhog/frontend/app/classes/page.tsx.ejs +127 -20
  79. package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +141 -30
  80. package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +13 -13
  81. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +11 -23
  82. package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +1 -8
  83. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +2 -0
  84. package/hedhog/frontend/app/courses/page.tsx.ejs +40 -9
  85. package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +6 -0
  86. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-calendar-tab.tsx.ejs +264 -0
  87. package/hedhog/frontend/app/enterprise/page.tsx.ejs +104 -47
  88. package/hedhog/frontend/app/exams/page.tsx.ejs +38 -4
  89. package/hedhog/frontend/app/instructors/page.tsx.ejs +87 -46
  90. package/hedhog/frontend/app/paths/page.tsx.ejs +38 -4
  91. package/hedhog/frontend/app/training/page.tsx.ejs +38 -4
  92. package/hedhog/frontend/messages/en.json +18 -0
  93. package/hedhog/frontend/messages/pt.json +21 -1
  94. package/hedhog/table/course_enrollment.yaml +3 -0
  95. package/hedhog/table/lesson_view_event.yaml +66 -0
  96. package/package.json +9 -8
  97. package/src/course/course-structure.controller.ts +3 -1
  98. package/src/course/course-video-agent-pipeline.service.ts +471 -0
  99. package/src/course/course-video-hls.service.ts +30 -10
  100. package/src/course/course.module.ts +5 -0
  101. package/src/course/course.service.ts +46 -1
  102. package/src/course/ffmpeg.util.ts +65 -0
  103. package/src/course/lms-bulk-upload-automation.service.ts +4 -1
  104. package/src/lms.module.ts +10 -0
  105. package/src/platforma/dto/heartbeat.dto.ts +30 -0
  106. package/src/platforma/handlers/emit-certificate.handler.ts +117 -0
  107. package/src/platforma/handlers/lesson-heartbeat.handler.ts +343 -0
  108. package/src/platforma/platforma-heartbeat.service.ts +33 -0
  109. package/src/platforma/platforma-performance.service.ts +606 -0
  110. package/src/platforma/platforma-search.service.ts +48 -0
  111. package/src/platforma/platforma.controller.ts +42 -0
  112. package/src/realtime/lms-realtime.controller.ts +27 -1
  113. package/src/realtime/lms-realtime.service.ts +2 -1
@@ -0,0 +1,398 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ var CourseVideoAgentPipelineService_1;
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.CourseVideoAgentPipelineService = exports.LMS_TRANSCRIPTION_SAVE_JOB = exports.LMS_AUDIO_TRANSCRIBE_CHUNKS_JOB = exports.LMS_AUDIO_SPLIT_JOB = exports.LMS_AUDIO_EXTRACT_JOB = exports.VIDEO_PROCESSING_AGENT_SLUG = void 0;
20
+ const api_prisma_1 = require("@hed-hog/api-prisma");
21
+ const core_1 = require("@hed-hog/core");
22
+ const queue_1 = require("@hed-hog/queue");
23
+ const agent_1 = require("@hed-hog/agent");
24
+ const common_1 = require("@nestjs/common");
25
+ const axios_1 = __importDefault(require("axios"));
26
+ const child_process_1 = require("child_process");
27
+ const fs_1 = require("fs");
28
+ const os_1 = require("os");
29
+ const path_1 = require("path");
30
+ const util_1 = require("util");
31
+ const course_video_hls_service_1 = require("./course-video-hls.service");
32
+ const ffmpeg_util_1 = require("./ffmpeg.util");
33
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
34
+ exports.VIDEO_PROCESSING_AGENT_SLUG = 'lms-video-processing';
35
+ exports.LMS_AUDIO_EXTRACT_JOB = 'lms.audio.extract';
36
+ exports.LMS_AUDIO_SPLIT_JOB = 'lms.audio.split';
37
+ exports.LMS_AUDIO_TRANSCRIBE_CHUNKS_JOB = 'lms.audio.transcribe.chunks';
38
+ exports.LMS_TRANSCRIPTION_SAVE_JOB = 'lms.transcription.save';
39
+ const CHUNK_SECONDS = 60;
40
+ /**
41
+ * Decomposed, queue-backed steps of the LMS video transcription pipeline, orchestrated by the
42
+ * seeded `lms-video-processing` agent flow via `tool.queue_dispatch` (dispatch-and-wait).
43
+ *
44
+ * Each step is its own job so it is visible both in the queue dashboard and the agent run
45
+ * timeline. Intermediates are persisted (audio + chunk + transcript files) and only small
46
+ * file-id references travel through the agent state between steps. The existing monolithic
47
+ * `lms.audio.transcribe` job and legacy paths are left untouched.
48
+ */
49
+ let CourseVideoAgentPipelineService = CourseVideoAgentPipelineService_1 = class CourseVideoAgentPipelineService {
50
+ constructor(prisma, fileService, settingService, registry, dbQueue, agentRuntime, hlsService) {
51
+ this.prisma = prisma;
52
+ this.fileService = fileService;
53
+ this.settingService = settingService;
54
+ this.registry = registry;
55
+ this.dbQueue = dbQueue;
56
+ this.agentRuntime = agentRuntime;
57
+ this.hlsService = hlsService;
58
+ this.logger = new common_1.Logger(CourseVideoAgentPipelineService_1.name);
59
+ }
60
+ get db() {
61
+ return this.prisma;
62
+ }
63
+ onModuleInit() {
64
+ for (const type of [
65
+ exports.LMS_AUDIO_EXTRACT_JOB,
66
+ exports.LMS_AUDIO_SPLIT_JOB,
67
+ exports.LMS_AUDIO_TRANSCRIBE_CHUNKS_JOB,
68
+ exports.LMS_TRANSCRIPTION_SAVE_JOB,
69
+ ]) {
70
+ this.registry.register(type, this);
71
+ }
72
+ this.logger.log(`Registered handlers for ${exports.LMS_AUDIO_EXTRACT_JOB}, ${exports.LMS_AUDIO_SPLIT_JOB}, ${exports.LMS_AUDIO_TRANSCRIBE_CHUNKS_JOB}, ${exports.LMS_TRANSCRIPTION_SAVE_JOB}`);
73
+ }
74
+ // ───────────────────────────── orchestration entry ─────────────────────────────
75
+ /**
76
+ * Validates the lesson/original, registers the `video_original` file, and starts the seeded
77
+ * `lms-video-processing` agent run (async). Falls back to the legacy monolithic HLS pipeline
78
+ * when the agent flow is not seeded yet, so the feature degrades gracefully during rollout.
79
+ */
80
+ async startProcessing(params) {
81
+ var _a, _b, _c;
82
+ const agent = await this.db.agent.findUnique({
83
+ where: { slug: exports.VIDEO_PROCESSING_AGENT_SLUG },
84
+ select: { id: true, status: true },
85
+ });
86
+ if (!agent || agent.status === 'archived') {
87
+ this.logger.warn(`Agent "${exports.VIDEO_PROCESSING_AGENT_SLUG}" unavailable — falling back to legacy enqueueHls.`);
88
+ const legacy = await this.hlsService.enqueueHls(params);
89
+ return { agentRunId: null, status: legacy.status };
90
+ }
91
+ await this.hlsService.prepareLessonForProcessing({
92
+ courseId: params.courseId,
93
+ sessionId: params.sessionId,
94
+ lessonId: params.lessonId,
95
+ originalFileId: params.originalFileId,
96
+ });
97
+ const settings = await this.settingService.getSettingValues([
98
+ 'lms-audio-transcription-enabled',
99
+ ]);
100
+ const transcriptionEnabled = settings['lms-audio-transcription-enabled'] !== false;
101
+ const course = await this.db.course.findUnique({
102
+ where: { id: params.courseId },
103
+ include: { locale: true },
104
+ });
105
+ const localeCode = (_b = (_a = course === null || course === void 0 ? void 0 : course.locale) === null || _a === void 0 ? void 0 : _a.code) !== null && _b !== void 0 ? _b : 'pt';
106
+ const run = await this.agentRuntime.startManualRun(agent.id, {
107
+ course_id: params.courseId,
108
+ session_id: params.sessionId,
109
+ lesson_id: params.lessonId,
110
+ original_file_id: params.originalFileId,
111
+ user_id: params.userId,
112
+ transcription_enabled: transcriptionEnabled,
113
+ locale_code: localeCode,
114
+ }, params.userId);
115
+ this.logger.log(`Started agent run ${run === null || run === void 0 ? void 0 : run.id} for lesson ${params.lessonId} (transcription=${transcriptionEnabled}).`);
116
+ return { agentRunId: (_c = run === null || run === void 0 ? void 0 : run.id) !== null && _c !== void 0 ? _c : null, status: 'started' };
117
+ }
118
+ // ───────────────────────────── job dispatch ─────────────────────────────
119
+ async handle(job) {
120
+ switch (job.type) {
121
+ case exports.LMS_AUDIO_EXTRACT_JOB:
122
+ return this.handleExtract(job);
123
+ case exports.LMS_AUDIO_SPLIT_JOB:
124
+ return this.handleSplit(job);
125
+ case exports.LMS_AUDIO_TRANSCRIBE_CHUNKS_JOB:
126
+ return this.handleTranscribeChunks(job);
127
+ case exports.LMS_TRANSCRIPTION_SAVE_JOB:
128
+ return this.handleSave(job);
129
+ default:
130
+ throw new queue_1.NonRetryableError(`Unsupported job type "${job.type}"`);
131
+ }
132
+ }
133
+ /** Step: extract a 16 kHz mono mp3 from the original video and persist it as lesson_audio. */
134
+ async handleExtract(job) {
135
+ var _a, _b, _c, _d, _e;
136
+ const courseId = Number((_a = job.payload) === null || _a === void 0 ? void 0 : _a.courseId);
137
+ const lessonId = Number((_b = job.payload) === null || _b === void 0 ? void 0 : _b.lessonId);
138
+ const originalFileId = Number((_c = job.payload) === null || _c === void 0 ? void 0 : _c.originalFileId);
139
+ if (!lessonId || !originalFileId) {
140
+ throw new queue_1.NonRetryableError('lms.audio.extract: lessonId and originalFileId are required.');
141
+ }
142
+ const workDir = await fs_1.promises.mkdtemp((0, path_1.join)((0, os_1.tmpdir)(), `lms-extract-${job.id}-`));
143
+ const inputPath = (0, path_1.join)(workDir, `original-${originalFileId}`);
144
+ const mp3Path = (0, path_1.join)(workDir, `lesson_${lessonId}_audio.mp3`);
145
+ try {
146
+ await this.fileService.downloadToPath(originalFileId, inputPath);
147
+ await execFileAsync((0, ffmpeg_util_1.getFfmpegCommand)(), ['-y', '-i', inputPath, '-vn', '-ar', '16000', '-ac', '1', '-c:a', 'libmp3lame', '-b:a', '32k', mp3Path], { maxBuffer: 1024 * 1024 * 20, windowsHide: true });
148
+ const course = await this.db.course.findUnique({
149
+ where: { id: courseId },
150
+ select: { locale_id: true },
151
+ });
152
+ const uploaded = await this.fileService.uploadFromPath('lms/lessons/audio', mp3Path, {
153
+ originalname: (0, path_1.basename)(mp3Path),
154
+ mimetype: 'audio/mp3',
155
+ });
156
+ const defaultLocale = await this.db.locale.findFirst({
157
+ where: { OR: [{ code: 'pt-BR' }, { code: 'pt' }] },
158
+ select: { id: true },
159
+ orderBy: { id: 'asc' },
160
+ });
161
+ const resolvedLocaleId = (_e = (_d = course === null || course === void 0 ? void 0 : course.locale_id) !== null && _d !== void 0 ? _d : defaultLocale === null || defaultLocale === void 0 ? void 0 : defaultLocale.id) !== null && _e !== void 0 ? _e : null;
162
+ const existing = await this.db.course_lesson_file.findFirst({
163
+ where: { course_lesson_id: lessonId, type: 'lesson_audio' },
164
+ select: { id: true },
165
+ });
166
+ if (existing) {
167
+ await this.db.course_lesson_file.update({
168
+ where: { id: existing.id },
169
+ data: { file_id: uploaded.id, locale_id: resolvedLocaleId, title: 'Audio Original.mp3', type: 'lesson_audio', is_public: false },
170
+ });
171
+ }
172
+ else {
173
+ await this.db.course_lesson_file.create({
174
+ data: { course_lesson_id: lessonId, file_id: uploaded.id, title: 'Audio Original.mp3', type: 'lesson_audio', is_public: false, locale_id: resolvedLocaleId },
175
+ });
176
+ }
177
+ this.logger.log(`[extract job=${job.id}] lesson ${lessonId} audio fileId=${uploaded.id}`);
178
+ return { audioFileId: uploaded.id };
179
+ }
180
+ finally {
181
+ await fs_1.promises.rm(workDir, { recursive: true, force: true }).catch(() => undefined);
182
+ }
183
+ }
184
+ /** Step: split the lesson audio into 60s chunks, uploaded to a staging prefix. */
185
+ async handleSplit(job) {
186
+ var _a, _b;
187
+ const lessonId = Number((_a = job.payload) === null || _a === void 0 ? void 0 : _a.lessonId);
188
+ const audioFileId = Number((_b = job.payload) === null || _b === void 0 ? void 0 : _b.audioFileId);
189
+ if (!lessonId || !audioFileId) {
190
+ throw new queue_1.NonRetryableError('lms.audio.split: lessonId and audioFileId are required.');
191
+ }
192
+ const workDir = await fs_1.promises.mkdtemp((0, path_1.join)((0, os_1.tmpdir)(), `lms-split-${job.id}-`));
193
+ const sourceAudioPath = (0, path_1.join)(workDir, `audio_${lessonId}.source`);
194
+ const normalizedAudioPath = (0, path_1.join)(workDir, `audio_${lessonId}.normalized.mp3`);
195
+ try {
196
+ await this.fileService.downloadToPath(audioFileId, sourceAudioPath);
197
+ await execFileAsync((0, ffmpeg_util_1.getFfmpegCommand)(), ['-y', '-i', sourceAudioPath, '-ar', '16000', '-ac', '1', '-c:a', 'libmp3lame', '-b:a', '32k', normalizedAudioPath], { maxBuffer: 1024 * 1024 * 20, windowsHide: true });
198
+ const chunkPattern = (0, path_1.join)(workDir, 'chunk_%04d.mp3');
199
+ await execFileAsync((0, ffmpeg_util_1.getFfmpegCommand)(), ['-y', '-i', normalizedAudioPath, '-f', 'segment', '-segment_time', String(CHUNK_SECONDS), '-ar', '16000', '-ac', '1', '-c:a', 'libmp3lame', '-b:a', '32k', chunkPattern], { maxBuffer: 1024 * 1024 * 20, windowsHide: true });
200
+ const chunkNames = (await fs_1.promises.readdir(workDir))
201
+ .filter((n) => n.startsWith('chunk_') && n.endsWith('.mp3'))
202
+ .sort((a, b) => a.localeCompare(b));
203
+ const chunkFileIds = [];
204
+ for (const name of chunkNames) {
205
+ const uploaded = await this.fileService.uploadFromPath(`lms/lessons/transcribe-chunks/${lessonId}`, (0, path_1.join)(workDir, name), { originalname: name, mimetype: 'audio/mpeg' });
206
+ chunkFileIds.push(uploaded.id);
207
+ }
208
+ this.logger.log(`[split job=${job.id}] lesson ${lessonId} → ${chunkFileIds.length} chunk(s)`);
209
+ return { chunkFileIds, chunkCount: chunkFileIds.length };
210
+ }
211
+ finally {
212
+ await fs_1.promises.rm(workDir, { recursive: true, force: true }).catch(() => undefined);
213
+ }
214
+ }
215
+ /** Step: transcribe each chunk via OpenAI Whisper and persist the merged transcript JSON. */
216
+ async handleTranscribeChunks(job) {
217
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
218
+ const courseId = Number((_a = job.payload) === null || _a === void 0 ? void 0 : _a.courseId);
219
+ const lessonId = Number((_b = job.payload) === null || _b === void 0 ? void 0 : _b.lessonId);
220
+ const chunkFileIds = this.parseIdArray((_c = job.payload) === null || _c === void 0 ? void 0 : _c.chunkFileIds);
221
+ if (!lessonId || chunkFileIds.length === 0) {
222
+ throw new queue_1.NonRetryableError('lms.audio.transcribe.chunks: lessonId and chunkFileIds are required.');
223
+ }
224
+ const apiKey = await this.resolveOpenAiKey();
225
+ const course = await this.db.course.findUnique({
226
+ where: { id: courseId },
227
+ include: { locale: true },
228
+ });
229
+ const language = (_e = (_d = course === null || course === void 0 ? void 0 : course.locale) === null || _d === void 0 ? void 0 : _d.code) !== null && _e !== void 0 ? _e : 'pt';
230
+ const workDir = await fs_1.promises.mkdtemp((0, path_1.join)((0, os_1.tmpdir)(), `lms-transcribe-${job.id}-`));
231
+ try {
232
+ const segments = [];
233
+ for (let i = 0; i < chunkFileIds.length; i += 1) {
234
+ const chunkPath = (0, path_1.join)(workDir, `chunk_${i}.mp3`);
235
+ await this.fileService.downloadToPath(chunkFileIds[i], chunkPath);
236
+ const offsetSeconds = i * CHUNK_SECONDS;
237
+ const buffer = await fs_1.promises.readFile(chunkPath);
238
+ const formData = new FormData();
239
+ formData.append('file', new Blob([buffer], { type: 'audio/mpeg' }), `chunk_${i}.mp3`);
240
+ formData.append('model', 'whisper-1');
241
+ formData.append('response_format', 'verbose_json');
242
+ formData.append('language', language);
243
+ const headers = { Authorization: `Bearer ${apiKey}` };
244
+ const maybeHeaders = (_g = (_f = formData).getHeaders) === null || _g === void 0 ? void 0 : _g.call(_f);
245
+ if (maybeHeaders && typeof maybeHeaders === 'object')
246
+ Object.assign(headers, maybeHeaders);
247
+ const response = await axios_1.default.post('https://api.openai.com/v1/audio/transcriptions', formData, { headers });
248
+ for (const segment of (_j = (_h = response.data) === null || _h === void 0 ? void 0 : _h.segments) !== null && _j !== void 0 ? _j : []) {
249
+ const text = String((_k = segment === null || segment === void 0 ? void 0 : segment.text) !== null && _k !== void 0 ? _k : '').trim();
250
+ if (!text)
251
+ continue;
252
+ segments.push({
253
+ course_lesson_id: lessonId,
254
+ start_seconds: offsetSeconds + Number((_l = segment === null || segment === void 0 ? void 0 : segment.start) !== null && _l !== void 0 ? _l : 0),
255
+ end_seconds: offsetSeconds + Number((_m = segment === null || segment === void 0 ? void 0 : segment.end) !== null && _m !== void 0 ? _m : 0),
256
+ text,
257
+ });
258
+ }
259
+ }
260
+ const transcriptPath = (0, path_1.join)(workDir, `transcript_${lessonId}.json`);
261
+ await fs_1.promises.writeFile(transcriptPath, JSON.stringify({ lessonId, segments }), 'utf8');
262
+ const uploaded = await this.fileService.uploadFromPath(`lms/lessons/transcripts/${lessonId}`, transcriptPath, { originalname: `transcript_${lessonId}.json`, mimetype: 'application/json' });
263
+ this.logger.log(`[transcribe job=${job.id}] lesson ${lessonId} → ${segments.length} segment(s)`);
264
+ return { transcriptFileId: uploaded.id, segmentCount: segments.length };
265
+ }
266
+ finally {
267
+ await fs_1.promises.rm(workDir, { recursive: true, force: true }).catch(() => undefined);
268
+ }
269
+ }
270
+ /** Step: join + persist the transcript segments and trigger XP calculation. */
271
+ async handleSave(job) {
272
+ var _a, _b, _c;
273
+ const lessonId = Number((_a = job.payload) === null || _a === void 0 ? void 0 : _a.lessonId);
274
+ const transcriptFileId = Number((_b = job.payload) === null || _b === void 0 ? void 0 : _b.transcriptFileId);
275
+ const userId = Number((_c = job.payload) === null || _c === void 0 ? void 0 : _c.userId) || 0;
276
+ if (!lessonId || !transcriptFileId) {
277
+ throw new queue_1.NonRetryableError('lms.transcription.save: lessonId and transcriptFileId are required.');
278
+ }
279
+ const workDir = await fs_1.promises.mkdtemp((0, path_1.join)((0, os_1.tmpdir)(), `lms-tsave-${job.id}-`));
280
+ try {
281
+ const transcriptPath = (0, path_1.join)(workDir, 'transcript.json');
282
+ await this.fileService.downloadToPath(transcriptFileId, transcriptPath);
283
+ const parsed = JSON.parse(await fs_1.promises.readFile(transcriptPath, 'utf8'));
284
+ const segments = Array.isArray(parsed === null || parsed === void 0 ? void 0 : parsed.segments)
285
+ ? parsed.segments
286
+ : [];
287
+ await this.prisma.$transaction([
288
+ this.db.course_lesson_transcription_segment.deleteMany({
289
+ where: { course_lesson_id: lessonId },
290
+ }),
291
+ this.db.course_lesson_transcription_segment.createMany({
292
+ data: segments.map((s) => {
293
+ var _a, _b, _c;
294
+ return ({
295
+ course_lesson_id: lessonId,
296
+ start_seconds: Number((_a = s === null || s === void 0 ? void 0 : s.start_seconds) !== null && _a !== void 0 ? _a : 0),
297
+ end_seconds: Number((_b = s === null || s === void 0 ? void 0 : s.end_seconds) !== null && _b !== void 0 ? _b : 0),
298
+ text: String((_c = s === null || s === void 0 ? void 0 : s.text) !== null && _c !== void 0 ? _c : ''),
299
+ });
300
+ }),
301
+ }),
302
+ ]);
303
+ this.logger.log(`[save job=${job.id}] lesson ${lessonId} saved ${segments.length} segment(s)`);
304
+ await this.triggerXpCalculation(lessonId, userId);
305
+ return { saved: segments.length };
306
+ }
307
+ finally {
308
+ await fs_1.promises.rm(workDir, { recursive: true, force: true }).catch(() => undefined);
309
+ }
310
+ }
311
+ // ───────────────────────────── helpers ─────────────────────────────
312
+ parseIdArray(raw) {
313
+ let arr = raw;
314
+ if (typeof raw === 'string') {
315
+ try {
316
+ arr = JSON.parse(raw);
317
+ }
318
+ catch (_a) {
319
+ arr = [];
320
+ }
321
+ }
322
+ if (!Array.isArray(arr))
323
+ return [];
324
+ return arr.map((x) => Number(x)).filter((n) => Number.isFinite(n) && n > 0);
325
+ }
326
+ async resolveOpenAiKey() {
327
+ const settings = await this.settingService.getSettingValues(['ai-openai-profile-id']);
328
+ const profileId = Number(settings['ai-openai-profile-id']);
329
+ let apiKey = '';
330
+ if (profileId) {
331
+ const profile = await this.db.integration_profile.findUnique({
332
+ where: { id: profileId },
333
+ include: { integration_provider: { select: { slug: true } } },
334
+ });
335
+ if (profile) {
336
+ apiKey = (0, core_1.buildAiConfigFromIntegration)(profile.integration_provider.slug, profile.config).apiKey;
337
+ }
338
+ }
339
+ if (!apiKey) {
340
+ throw new queue_1.NonRetryableError('Transcrição de áudio requer um perfil OpenAI configurado (Settings → LMS → ai-openai-profile-id).');
341
+ }
342
+ return apiKey;
343
+ }
344
+ async triggerXpCalculation(lessonId, userId) {
345
+ try {
346
+ const existingMap = await this.db.lesson_xp_map.findUnique({
347
+ where: { course_lesson_id: lessonId },
348
+ select: { id: true },
349
+ });
350
+ let mapId;
351
+ if (existingMap) {
352
+ mapId = existingMap.id;
353
+ await this.prisma.$executeRawUnsafe(`UPDATE lesson_xp_map
354
+ SET status = 'processing'::lesson_xp_map_status_d4e5f6a7b8_enum,
355
+ processing_error = NULL,
356
+ updated_at = NOW()
357
+ WHERE id = $1`, mapId);
358
+ }
359
+ else {
360
+ const rows = await this.prisma.$queryRawUnsafe(`INSERT INTO lesson_xp_map (course_lesson_id, version, total_xp, status, created_at, updated_at)
361
+ VALUES ($1, 1, 0, 'processing'::lesson_xp_map_status_d4e5f6a7b8_enum, NOW(), NOW())
362
+ RETURNING id`, lessonId);
363
+ mapId = rows[0].id;
364
+ }
365
+ await this.dbQueue.enqueue({
366
+ type: 'lms.lesson.xp.calculate',
367
+ queueName: 'lms.lesson.xp.calculate',
368
+ payload: { lessonId, mapId, userId },
369
+ maxAttempts: 3,
370
+ sourceModule: 'lms',
371
+ sourceEntity: 'course_lesson',
372
+ sourceEntityId: String(lessonId),
373
+ });
374
+ }
375
+ catch (err) {
376
+ this.logger.warn(`Failed to enqueue XP calculation for lesson ${lessonId}: ${err instanceof Error ? err.message : String(err)}`);
377
+ }
378
+ }
379
+ };
380
+ exports.CourseVideoAgentPipelineService = CourseVideoAgentPipelineService;
381
+ exports.CourseVideoAgentPipelineService = CourseVideoAgentPipelineService = CourseVideoAgentPipelineService_1 = __decorate([
382
+ (0, common_1.Injectable)(),
383
+ __param(0, (0, common_1.Inject)((0, common_1.forwardRef)(() => api_prisma_1.PrismaService))),
384
+ __param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.FileService))),
385
+ __param(2, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.SettingService))),
386
+ __param(3, (0, common_1.Inject)((0, common_1.forwardRef)(() => queue_1.QueueHandlerRegistry))),
387
+ __param(4, (0, common_1.Inject)((0, common_1.forwardRef)(() => queue_1.DatabaseQueueProvider))),
388
+ __param(5, (0, common_1.Inject)((0, common_1.forwardRef)(() => agent_1.AgentRuntimeService))),
389
+ __param(6, (0, common_1.Inject)((0, common_1.forwardRef)(() => course_video_hls_service_1.CourseVideoHlsService))),
390
+ __metadata("design:paramtypes", [api_prisma_1.PrismaService,
391
+ core_1.FileService,
392
+ core_1.SettingService,
393
+ queue_1.QueueHandlerRegistry,
394
+ queue_1.DatabaseQueueProvider,
395
+ agent_1.AgentRuntimeService,
396
+ course_video_hls_service_1.CourseVideoHlsService])
397
+ ], CourseVideoAgentPipelineService);
398
+ //# sourceMappingURL=course-video-agent-pipeline.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"course-video-agent-pipeline.service.js","sourceRoot":"","sources":["../../src/course/course-video-agent-pipeline.service.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,20 @@ export declare class CourseVideoHlsService implements OnModuleInit, IJobHandler
13
13
  private readonly logger;
14
14
  constructor(prisma: PrismaService, fileService: FileService, settingService: SettingService, notificationService: NotificationService, registry: QueueHandlerRegistry, queueJob: QueueJobService);
15
15
  onModuleInit(): void;
16
+ /**
17
+ * Validates a lesson is an eligible file-storage video and registers the uploaded original
18
+ * as its `video_original` file. Shared by the legacy `enqueueHls` path and the new
19
+ * agent-orchestrated pipeline (CourseVideoAgentPipelineService.startProcessing).
20
+ */
21
+ prepareLessonForProcessing(params: {
22
+ courseId: number;
23
+ sessionId: number;
24
+ lessonId: number;
25
+ originalFileId: number;
26
+ }): Promise<{
27
+ content: Record<string, unknown> | null;
28
+ original: any;
29
+ }>;
16
30
  enqueueHls(params: {
17
31
  userId: number;
18
32
  courseId: number;
@@ -1 +1 @@
1
- {"version":3,"file":"course-video-hls.service.d.ts","sourceRoot":"","sources":["../../src/course/course-video-hls.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAML,YAAY,EAEb,MAAM,gBAAgB,CAAC;AAOxB,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AA8BjD,qBACa,qBAAsB,YAAW,YAAY,EAAE,WAAW;IAKnE,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAE5B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAE/B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAEpC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAEzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAd3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;gBAI9C,MAAM,EAAE,aAAa,EAErB,WAAW,EAAE,WAAW,EAExB,cAAc,EAAE,cAAc,EAE9B,mBAAmB,EAAE,mBAAmB,EAExC,QAAQ,EAAE,oBAAoB,EAE9B,QAAQ,EAAE,eAAe;IAG5C,YAAY;IAKN,UAAU,CAAC,MAAM,EAAE;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;KACxB;;;;IAsGK,MAAM,CAAC,GAAG,EAAE;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,GAAG,OAAO,CAAC,GAAG,CAAC;YAsQF,WAAW;YAqEX,kBAAkB;IA8EhC,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,gBAAgB;YAmBV,gBAAgB;YAehB,oBAAoB;YAmBpB,4BAA4B;YAgE5B,2BAA2B;YAyE3B,gBAAgB;YAyBhB,mBAAmB;IAoBjC,OAAO,CAAC,2BAA2B;YAerB,uBAAuB;IAYrC,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,2BAA2B;IAKnC,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,iBAAiB;CA2B1B"}
1
+ {"version":3,"file":"course-video-hls.service.d.ts","sourceRoot":"","sources":["../../src/course/course-video-hls.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAML,YAAY,EAEb,MAAM,gBAAgB,CAAC;AAOxB,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AA8BjD,qBACa,qBAAsB,YAAW,YAAY,EAAE,WAAW;IAKnE,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAE5B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAE/B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAEpC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAEzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAd3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;gBAI9C,MAAM,EAAE,aAAa,EAErB,WAAW,EAAE,WAAW,EAExB,cAAc,EAAE,cAAc,EAE9B,mBAAmB,EAAE,mBAAmB,EAExC,QAAQ,EAAE,oBAAoB,EAE9B,QAAQ,EAAE,eAAe;IAG5C,YAAY;IAKZ;;;;OAIG;IACG,0BAA0B,CAAC,MAAM,EAAE;QACvC,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,GAAG,CAAA;KAAE,CAAC;IA6CjE,UAAU,CAAC,MAAM,EAAE;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;KACxB;;;;IA+DK,MAAM,CAAC,GAAG,EAAE;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,GAAG,OAAO,CAAC,GAAG,CAAC;YA0QF,WAAW;YAqEX,kBAAkB;IA8EhC,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,gBAAgB;YAmBV,gBAAgB;YAehB,oBAAoB;YAmBpB,4BAA4B;YAgE5B,2BAA2B;YAyE3B,gBAAgB;YAyBhB,mBAAmB;IAoBjC,OAAO,CAAC,2BAA2B;YAerB,uBAAuB;IAYrC,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,2BAA2B;IAKnC,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,iBAAiB;CA2B1B"}
@@ -44,7 +44,12 @@ let CourseVideoHlsService = CourseVideoHlsService_1 = class CourseVideoHlsServic
44
44
  this.registry.register(exports.LMS_VIDEO_HLS_JOB, this);
45
45
  this.logger.log(`Registered handler for "${exports.LMS_VIDEO_HLS_JOB}"`);
46
46
  }
47
- async enqueueHls(params) {
47
+ /**
48
+ * Validates a lesson is an eligible file-storage video and registers the uploaded original
49
+ * as its `video_original` file. Shared by the legacy `enqueueHls` path and the new
50
+ * agent-orchestrated pipeline (CourseVideoAgentPipelineService.startProcessing).
51
+ */
52
+ async prepareLessonForProcessing(params) {
48
53
  var _a, _b, _c;
49
54
  const lesson = await this.prisma.course_lesson.findFirst({
50
55
  where: {
@@ -81,6 +86,10 @@ let CourseVideoHlsService = CourseVideoHlsService_1 = class CourseVideoHlsServic
81
86
  public: false,
82
87
  overwrite: true,
83
88
  });
89
+ return { content, original };
90
+ }
91
+ async enqueueHls(params) {
92
+ const { content } = await this.prepareLessonForProcessing(params);
84
93
  const asyncNotification = await this.notificationService.create({
85
94
  user_id: params.userId,
86
95
  title: 'Geração de HLS em andamento',
@@ -133,10 +142,18 @@ let CourseVideoHlsService = CourseVideoHlsService_1 = class CourseVideoHlsServic
133
142
  return { queueJobId: job.id, status: 'queued' };
134
143
  }
135
144
  async handle(job) {
136
- var _a, _b, _c, _d;
137
- const { courseId, sessionId, lessonId, originalFileId } = job.payload;
138
- const notificationContext = Number.isInteger(Number((_a = job.payload) === null || _a === void 0 ? void 0 : _a.notificationId)) &&
139
- Number.isInteger(Number((_b = job.payload) === null || _b === void 0 ? void 0 : _b.notificationUserId))
145
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
146
+ // Ids are coerced because agent-dispatched payloads template values as strings.
147
+ const courseId = Number((_a = job.payload) === null || _a === void 0 ? void 0 : _a.courseId);
148
+ const sessionId = Number((_b = job.payload) === null || _b === void 0 ? void 0 : _b.sessionId);
149
+ const lessonId = Number((_c = job.payload) === null || _c === void 0 ? void 0 : _c.lessonId);
150
+ const originalFileId = Number((_d = job.payload) === null || _d === void 0 ? void 0 : _d.originalFileId);
151
+ // Set by the agent pipeline: HLS-only, since audio extraction + transcription are
152
+ // dispatched as their own visible steps. Legacy callers omit it (full behaviour).
153
+ const skipAudioTranscription = ((_e = job.payload) === null || _e === void 0 ? void 0 : _e.skipAudioTranscription) === true ||
154
+ ((_f = job.payload) === null || _f === void 0 ? void 0 : _f.skipAudioTranscription) === 'true';
155
+ const notificationContext = Number.isInteger(Number((_g = job.payload) === null || _g === void 0 ? void 0 : _g.notificationId)) &&
156
+ Number.isInteger(Number((_h = job.payload) === null || _h === void 0 ? void 0 : _h.notificationUserId))
140
157
  ? { notificationId: Number(job.payload.notificationId), userId: Number(job.payload.notificationUserId) }
141
158
  : undefined;
142
159
  const emitProgress = async (message, metadata) => {
@@ -147,7 +164,7 @@ let CourseVideoHlsService = CourseVideoHlsService_1 = class CourseVideoHlsServic
147
164
  }
148
165
  this.logger.debug(`[HLS job=${job.id}] payload OK — courseId=${courseId} lessonId=${lessonId} originalFileId=${originalFileId} attempt=${job.attempts}/${job.max_attempts}`);
149
166
  const maxInputBytes = this.getPositiveIntegerEnv('LMS_VIDEO_MAX_INPUT_BYTES');
150
- const ffmpegTimeoutMs = (_c = this.getPositiveIntegerEnv('LMS_VIDEO_FFMPEG_TIMEOUT_MS')) !== null && _c !== void 0 ? _c : 1000 * 60 * 60 * 2;
167
+ const ffmpegTimeoutMs = (_j = this.getPositiveIntegerEnv('LMS_VIDEO_FFMPEG_TIMEOUT_MS')) !== null && _j !== void 0 ? _j : 1000 * 60 * 60 * 2;
151
168
  this.logger.debug(`[HLS job=${job.id}] loading settings...`);
152
169
  const settings = await this.settingService.getSettingValues([
153
170
  'lms-hls-resolutions',
@@ -193,7 +210,7 @@ let CourseVideoHlsService = CourseVideoHlsService_1 = class CourseVideoHlsServic
193
210
  }
194
211
  }
195
212
  catch (err) {
196
- this.logger.warn(`Queue job ${job.id}: failed to probe duration: ${(_d = err === null || err === void 0 ? void 0 : err.message) !== null && _d !== void 0 ? _d : err}`);
213
+ this.logger.warn(`Queue job ${job.id}: failed to probe duration: ${(_k = err === null || err === void 0 ? void 0 : err.message) !== null && _k !== void 0 ? _k : err}`);
197
214
  }
198
215
  this.logger.debug(`[HLS job=${job.id}] probing source height...`);
199
216
  const sourceHeight = await this.probeVideoHeight(inputPath);
@@ -266,7 +283,7 @@ let CourseVideoHlsService = CourseVideoHlsService_1 = class CourseVideoHlsServic
266
283
  else {
267
284
  this.logger.debug(`[HLS job=${job.id}] frame extraction disabled — skipping`);
268
285
  }
269
- if (transcriptionEnabled) {
286
+ if (transcriptionEnabled && !skipAudioTranscription) {
270
287
  this.logger.debug(`[HLS job=${job.id}] starting audio extraction for transcription`);
271
288
  await emitProgress('Extraindo áudio do vídeo...', { phase: 'extract_audio', lessonId });
272
289
  const audioFileId = await this.extractAndUploadLessonAudio({