@hed-hog/lms 0.0.364 → 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.
- package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +1 -0
- package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -1
- package/dist/bitcode-wallet/bitcode-wallet.service.js +22 -3
- package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -1
- package/dist/class-group/class-group.controller.d.ts +1 -0
- package/dist/class-group/class-group.controller.d.ts.map +1 -1
- package/dist/class-group/class-group.service.d.ts +1 -0
- package/dist/class-group/class-group.service.d.ts.map +1 -1
- package/dist/course/course-export-scorm12-worker.service.d.ts +21 -0
- package/dist/course/course-export-scorm12-worker.service.d.ts.map +1 -0
- package/dist/course/course-export-scorm12-worker.service.js +109 -0
- package/dist/course/course-export-scorm12-worker.service.js.map +1 -0
- package/dist/course/course-export-scorm12.service.d.ts +42 -0
- package/dist/course/course-export-scorm12.service.d.ts.map +1 -0
- package/dist/course/course-export-scorm12.service.js +628 -0
- package/dist/course/course-export-scorm12.service.js.map +1 -0
- package/dist/course/course-export.service.d.ts +84 -0
- package/dist/course/course-export.service.d.ts.map +1 -0
- package/dist/course/course-export.service.js +237 -0
- package/dist/course/course-export.service.js.map +1 -0
- package/dist/course/course-structure.controller.d.ts +20 -10
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.controller.js +20 -4
- package/dist/course/course-structure.controller.js.map +1 -1
- package/dist/course/course-structure.service.d.ts +12 -4
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +98 -23
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course-video-agent-pipeline.service.d.ts +70 -0
- package/dist/course/course-video-agent-pipeline.service.d.ts.map +1 -0
- package/dist/course/course-video-agent-pipeline.service.js +398 -0
- package/dist/course/course-video-agent-pipeline.service.js.map +1 -0
- package/dist/course/course-video-hls.service.d.ts +71 -0
- package/dist/course/course-video-hls.service.d.ts.map +1 -0
- package/dist/course/course-video-hls.service.js +784 -0
- package/dist/course/course-video-hls.service.js.map +1 -0
- package/dist/course/course.controller.d.ts +47 -13
- package/dist/course/course.controller.d.ts.map +1 -1
- package/dist/course/course.controller.js +40 -26
- package/dist/course/course.controller.js.map +1 -1
- package/dist/course/course.mcp-tools.js +1 -1
- package/dist/course/course.mcp-tools.js.map +1 -1
- package/dist/course/course.module.d.ts.map +1 -1
- package/dist/course/course.module.js +16 -0
- package/dist/course/course.module.js.map +1 -1
- package/dist/course/course.service.d.ts +8 -9
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +93 -50
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/cleanup-course-storage.dto.d.ts +1 -1
- package/dist/course/dto/cleanup-course-storage.dto.d.ts.map +1 -1
- package/dist/course/dto/cleanup-course-storage.dto.js +1 -0
- package/dist/course/dto/cleanup-course-storage.dto.js.map +1 -1
- package/dist/course/dto/cleanup-upload-history.dto.d.ts +1 -1
- package/dist/course/dto/cleanup-upload-history.dto.d.ts.map +1 -1
- package/dist/course/dto/cleanup-upload-history.dto.js +1 -1
- package/dist/course/dto/cleanup-upload-history.dto.js.map +1 -1
- package/dist/course/dto/create-course-bulk-job.dto.d.ts +2 -1
- package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-bulk-job.dto.js +6 -1
- package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -1
- package/dist/course/dto/create-course-export.dto.d.ts +14 -0
- package/dist/course/dto/create-course-export.dto.d.ts.map +1 -0
- package/dist/course/dto/create-course-export.dto.js +71 -0
- package/dist/course/dto/create-course-export.dto.js.map +1 -0
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts +2 -2
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js +3 -2
- package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
- package/dist/course/ffmpeg.util.d.ts +10 -0
- package/dist/course/ffmpeg.util.d.ts.map +1 -0
- package/dist/course/ffmpeg.util.js +79 -0
- package/dist/course/ffmpeg.util.js.map +1 -0
- package/dist/course/lms-bulk-upload-automation.service.d.ts +18 -1
- package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload-automation.service.js +106 -8
- package/dist/course/lms-bulk-upload-automation.service.js.map +1 -1
- package/dist/course/lms-bulk-upload-infra.service.d.ts +1 -0
- package/dist/course/lms-bulk-upload-infra.service.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload-infra.service.js +32 -8
- package/dist/course/lms-bulk-upload-infra.service.js.map +1 -1
- package/dist/course/lms-bulk-upload.controller.d.ts +30 -3
- package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload.controller.js +43 -2
- package/dist/course/lms-bulk-upload.controller.js.map +1 -1
- package/dist/course/lms-bulk-upload.service.d.ts +11 -0
- package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload.service.js +59 -6
- package/dist/course/lms-bulk-upload.service.js.map +1 -1
- package/dist/course/lms-setting.controller.d.ts +2 -1
- package/dist/course/lms-setting.controller.d.ts.map +1 -1
- package/dist/course/lms-setting.controller.js +4 -2
- package/dist/course/lms-setting.controller.js.map +1 -1
- package/dist/course/scorm12-schemas.d.ts +4 -0
- package/dist/course/scorm12-schemas.d.ts.map +1 -0
- package/dist/course/scorm12-schemas.js +9 -0
- package/dist/course/scorm12-schemas.js.map +1 -0
- package/dist/enterprise/training/training-admin.controller.d.ts +2 -0
- package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
- package/dist/enterprise/training/training-admin.service.d.ts +2 -0
- package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.service.d.ts +51 -0
- package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.service.js +217 -4
- package/dist/enterprise/training/training-student.service.js.map +1 -1
- package/dist/evaluation/evaluation.service.d.ts +18 -0
- package/dist/evaluation/evaluation.service.d.ts.map +1 -1
- package/dist/evaluation/evaluation.service.js +125 -0
- package/dist/evaluation/evaluation.service.js.map +1 -1
- package/dist/exam/dto/create-standalone-question.dto.d.ts +12 -0
- package/dist/exam/dto/create-standalone-question.dto.d.ts.map +1 -0
- package/dist/exam/dto/create-standalone-question.dto.js +70 -0
- package/dist/exam/dto/create-standalone-question.dto.js.map +1 -0
- package/dist/exam/exam.module.d.ts.map +1 -1
- package/dist/exam/exam.module.js +2 -1
- package/dist/exam/exam.module.js.map +1 -1
- package/dist/exam/exam.service.d.ts +21 -0
- package/dist/exam/exam.service.d.ts.map +1 -1
- package/dist/exam/exam.service.js +80 -0
- package/dist/exam/exam.service.js.map +1 -1
- package/dist/exam/question.controller.d.ts +27 -0
- package/dist/exam/question.controller.d.ts.map +1 -0
- package/dist/exam/question.controller.js +53 -0
- package/dist/exam/question.controller.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +4 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -1
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +161 -25
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -1
- package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -1
- package/dist/lms-commerce-access.subscriber.d.ts +11 -0
- package/dist/lms-commerce-access.subscriber.d.ts.map +1 -0
- package/dist/lms-commerce-access.subscriber.js +74 -0
- package/dist/lms-commerce-access.subscriber.js.map +1 -0
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +16 -5
- package/dist/lms.module.js.map +1 -1
- package/dist/platforma/dto/heartbeat.dto.d.ts +9 -0
- package/dist/platforma/dto/heartbeat.dto.d.ts.map +1 -0
- package/dist/platforma/dto/heartbeat.dto.js +50 -0
- package/dist/platforma/dto/heartbeat.dto.js.map +1 -0
- package/dist/platforma/handlers/emit-certificate.handler.d.ts +27 -0
- package/dist/platforma/handlers/emit-certificate.handler.d.ts.map +1 -0
- package/dist/platforma/handlers/emit-certificate.handler.js +117 -0
- package/dist/platforma/handlers/emit-certificate.handler.js.map +1 -0
- package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts +31 -0
- package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts.map +1 -0
- package/dist/platforma/handlers/lesson-heartbeat.handler.js +281 -0
- package/dist/platforma/handlers/lesson-heartbeat.handler.js.map +1 -0
- package/dist/platforma/platforma-heartbeat.service.d.ts +10 -0
- package/dist/platforma/platforma-heartbeat.service.d.ts.map +1 -0
- package/dist/platforma/platforma-heartbeat.service.js +50 -0
- package/dist/platforma/platforma-heartbeat.service.js.map +1 -0
- package/dist/platforma/platforma-performance.service.d.ts +121 -0
- package/dist/platforma/platforma-performance.service.d.ts.map +1 -0
- package/dist/platforma/platforma-performance.service.js +500 -0
- package/dist/platforma/platforma-performance.service.js.map +1 -0
- package/dist/platforma/platforma-search.service.d.ts +21 -0
- package/dist/platforma/platforma-search.service.d.ts.map +1 -0
- package/dist/platforma/platforma-search.service.js +64 -0
- package/dist/platforma/platforma-search.service.js.map +1 -0
- package/dist/platforma/platforma-video.service.d.ts +39 -0
- package/dist/platforma/platforma-video.service.d.ts.map +1 -0
- package/dist/platforma/platforma-video.service.js +301 -0
- package/dist/platforma/platforma-video.service.js.map +1 -0
- package/dist/platforma/platforma.controller.d.ts +209 -1
- package/dist/platforma/platforma.controller.d.ts.map +1 -1
- package/dist/platforma/platforma.controller.js +208 -2
- package/dist/platforma/platforma.controller.js.map +1 -1
- package/dist/realtime/lms-realtime.controller.d.ts +2 -0
- package/dist/realtime/lms-realtime.controller.d.ts.map +1 -1
- package/dist/realtime/lms-realtime.controller.js +31 -0
- package/dist/realtime/lms-realtime.controller.js.map +1 -1
- package/dist/realtime/lms-realtime.service.d.ts +1 -1
- package/dist/realtime/lms-realtime.service.d.ts.map +1 -1
- package/dist/realtime/lms-realtime.service.js.map +1 -1
- package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts +5 -0
- package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts.map +1 -0
- package/dist/student-xp/dto/grant-skill-card-xp.dto.js +26 -0
- package/dist/student-xp/dto/grant-skill-card-xp.dto.js.map +1 -0
- package/dist/student-xp/student-xp.controller.d.ts +15 -0
- package/dist/student-xp/student-xp.controller.d.ts.map +1 -1
- package/dist/student-xp/student-xp.controller.js +24 -0
- package/dist/student-xp/student-xp.controller.js.map +1 -1
- package/dist/student-xp/student-xp.service.d.ts +16 -0
- package/dist/student-xp/student-xp.service.d.ts.map +1 -1
- package/dist/student-xp/student-xp.service.js +51 -1
- package/dist/student-xp/student-xp.service.js.map +1 -1
- package/hedhog/data/evaluation_topic.yaml +17 -0
- package/hedhog/data/menu.yaml +0 -17
- package/hedhog/data/queue_definition.yaml +48 -0
- package/hedhog/data/route.yaml +94 -124
- package/hedhog/data/setting_group.yaml +19 -19
- package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +337 -41
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +182 -29
- package/hedhog/frontend/app/classes/_components/classes-calendar-view.tsx.ejs +277 -0
- package/hedhog/frontend/app/classes/page.tsx.ejs +127 -20
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +69 -4
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-export-sheet.tsx.ejs +420 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-exports-tab.tsx.ejs +308 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +158 -45
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +13 -13
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +11 -23
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +51 -63
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-panel.tsx.ejs +8 -3
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +31 -8
- package/hedhog/frontend/app/courses/[id]/structure/_components/drag-overlay.tsx.ejs +16 -9
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +201 -401
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +378 -690
- package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +1 -2
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +3 -9
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +1 -8
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +6 -10
- package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +49 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +4 -3
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +2 -1
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-exports.ts.ejs +106 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +28 -1
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-lms-settings-query.ts.ejs +0 -2
- package/hedhog/frontend/app/courses/page.tsx.ejs +85 -9
- package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +6 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-classes-calendar-tab.tsx.ejs +264 -0
- package/hedhog/frontend/app/enterprise/page.tsx.ejs +104 -47
- package/hedhog/frontend/app/exams/page.tsx.ejs +38 -4
- package/hedhog/frontend/app/instructors/page.tsx.ejs +87 -46
- package/hedhog/frontend/app/paths/page.tsx.ejs +38 -4
- package/hedhog/frontend/app/training/page.tsx.ejs +38 -4
- package/hedhog/frontend/messages/en.json +44 -28
- package/hedhog/frontend/messages/pt.json +47 -29
- package/hedhog/table/course_enrollment.yaml +3 -0
- package/hedhog/table/course_export.yaml +62 -0
- package/hedhog/table/lesson_view_event.yaml +66 -0
- package/package.json +14 -9
- package/src/bitcode-wallet/bitcode-wallet.service.ts +43 -4
- package/src/course/course-export-scorm12-worker.service.ts +124 -0
- package/src/course/course-export-scorm12.service.ts +668 -0
- package/src/course/course-export.service.ts +280 -0
- package/src/course/course-structure.controller.ts +16 -2
- package/src/course/course-structure.service.ts +100 -7
- package/src/course/course-video-agent-pipeline.service.ts +471 -0
- package/src/course/course-video-hls.service.ts +966 -0
- package/src/course/course.controller.ts +33 -19
- package/src/course/course.mcp-tools.ts +1 -1
- package/src/course/course.module.ts +16 -0
- package/src/course/course.service.ts +119 -61
- package/src/course/dto/cleanup-course-storage.dto.ts +1 -0
- package/src/course/dto/cleanup-upload-history.dto.ts +1 -1
- package/src/course/dto/create-course-bulk-job.dto.ts +7 -3
- package/src/course/dto/create-course-export.dto.ts +56 -0
- package/src/course/dto/create-course-structure-lesson.dto.ts +4 -3
- package/src/course/ffmpeg.util.ts +65 -0
- package/src/course/lms-bulk-upload-automation.service.ts +156 -6
- package/src/course/lms-bulk-upload-infra.service.ts +39 -6
- package/src/course/lms-bulk-upload.controller.ts +32 -2
- package/src/course/lms-bulk-upload.service.ts +70 -7
- package/src/course/lms-setting.controller.ts +4 -2
- package/src/course/scorm12-schemas.ts +9 -0
- package/src/enterprise/training/training-student.service.ts +221 -2
- package/src/evaluation/evaluation.service.ts +123 -0
- package/src/exam/dto/create-standalone-question.dto.ts +66 -0
- package/src/exam/exam.module.ts +2 -1
- package/src/exam/exam.service.ts +86 -0
- package/src/exam/question.controller.ts +28 -0
- package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +205 -31
- package/src/lms-commerce-access.subscriber.ts +88 -0
- package/src/lms.module.ts +16 -5
- package/src/platforma/dto/heartbeat.dto.ts +30 -0
- package/src/platforma/handlers/emit-certificate.handler.ts +117 -0
- package/src/platforma/handlers/lesson-heartbeat.handler.ts +343 -0
- package/src/platforma/platforma-heartbeat.service.ts +33 -0
- package/src/platforma/platforma-performance.service.ts +606 -0
- package/src/platforma/platforma-search.service.ts +48 -0
- package/src/platforma/platforma-video.service.ts +346 -0
- package/src/platforma/platforma.controller.ts +137 -1
- package/src/platforma/platforma.service.ts +268 -268
- package/src/realtime/lms-realtime.controller.ts +27 -1
- package/src/realtime/lms-realtime.service.ts +2 -1
- package/src/student-xp/dto/grant-skill-card-xp.dto.ts +10 -0
- package/src/student-xp/student-xp.controller.ts +18 -2
- package/src/student-xp/student-xp.service.ts +84 -2
- package/hedhog/data/video_resolution_profile.yaml +0 -7
- package/hedhog/frontend/app/video-resolution-profiles/page.tsx.ejs +0 -607
- package/hedhog/table/course_video_resolution_profile.yaml +0 -22
- package/hedhog/table/video_resolution_profile.yaml +0 -18
- package/src/video-resolution-profile/dto/create-video-resolution-profile.dto.ts +0 -16
- package/src/video-resolution-profile/dto/update-video-resolution-profile.dto.ts +0 -16
- package/src/video-resolution-profile/video-resolution-profile.controller.ts +0 -62
- package/src/video-resolution-profile/video-resolution-profile.mcp-tools.ts +0 -128
- package/src/video-resolution-profile/video-resolution-profile.module.ts +0 -13
- package/src/video-resolution-profile/video-resolution-profile.service.ts +0 -117
|
@@ -5,23 +5,26 @@ import {
|
|
|
5
5
|
Controller,
|
|
6
6
|
Delete,
|
|
7
7
|
Get,
|
|
8
|
-
HttpCode,
|
|
9
|
-
HttpStatus,
|
|
10
8
|
Param,
|
|
11
9
|
ParseIntPipe,
|
|
12
10
|
Patch,
|
|
13
11
|
Post,
|
|
14
12
|
Query,
|
|
15
13
|
} from '@nestjs/common';
|
|
14
|
+
import { CourseExportService } from './course-export.service';
|
|
16
15
|
import { CourseService } from './course.service';
|
|
17
|
-
import { CreateCourseDto } from './dto/create-course.dto';
|
|
18
16
|
import { CleanupCourseStorageDto } from './dto/cleanup-course-storage.dto';
|
|
17
|
+
import { CreateCourseDto } from './dto/create-course.dto';
|
|
18
|
+
import { CreateCourseExportDto } from './dto/create-course-export.dto';
|
|
19
19
|
import { UpdateCourseDto } from './dto/update-course.dto';
|
|
20
20
|
|
|
21
21
|
@Role()
|
|
22
22
|
@Controller('lms/courses')
|
|
23
23
|
export class CourseController {
|
|
24
|
-
constructor(
|
|
24
|
+
constructor(
|
|
25
|
+
private readonly courseService: CourseService,
|
|
26
|
+
private readonly courseExportService: CourseExportService,
|
|
27
|
+
) {}
|
|
25
28
|
|
|
26
29
|
@Get()
|
|
27
30
|
list(
|
|
@@ -83,6 +86,32 @@ export class CourseController {
|
|
|
83
86
|
);
|
|
84
87
|
}
|
|
85
88
|
|
|
89
|
+
@Get(':id/exports')
|
|
90
|
+
listExports(@Param('id', ParseIntPipe) id: number) {
|
|
91
|
+
return this.courseExportService.listExports(id);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@Post(':id/exports')
|
|
95
|
+
createExport(
|
|
96
|
+
@Param('id', ParseIntPipe) id: number,
|
|
97
|
+
@Body() dto: CreateCourseExportDto,
|
|
98
|
+
@User() user: any,
|
|
99
|
+
) {
|
|
100
|
+
return this.courseExportService.enqueueScorm12Export(
|
|
101
|
+
id,
|
|
102
|
+
Number(user?.id || 0),
|
|
103
|
+
dto,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@Delete(':id/exports/:exportId')
|
|
108
|
+
deleteExport(
|
|
109
|
+
@Param('id', ParseIntPipe) id: number,
|
|
110
|
+
@Param('exportId', ParseIntPipe) exportId: number,
|
|
111
|
+
) {
|
|
112
|
+
return this.courseExportService.deleteExport(id, exportId);
|
|
113
|
+
}
|
|
114
|
+
|
|
86
115
|
@Get(':id')
|
|
87
116
|
getById(@Param('id', ParseIntPipe) id: number) {
|
|
88
117
|
return this.courseService.getById(id);
|
|
@@ -106,19 +135,4 @@ export class CourseController {
|
|
|
106
135
|
return this.courseService.remove(id, Number(user?.id || 0) || null);
|
|
107
136
|
}
|
|
108
137
|
|
|
109
|
-
@Get(':courseId/video-resolution-profiles')
|
|
110
|
-
getCourseVideoProfiles(
|
|
111
|
-
@Param('courseId', ParseIntPipe) courseId: number,
|
|
112
|
-
) {
|
|
113
|
-
return this.courseService.getCourseVideoProfiles(courseId);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
@Post(':courseId/video-resolution-profiles/sync')
|
|
117
|
-
@HttpCode(HttpStatus.OK)
|
|
118
|
-
syncCourseVideoProfiles(
|
|
119
|
-
@Param('courseId', ParseIntPipe) courseId: number,
|
|
120
|
-
@Body() body: { profileIds: number[] },
|
|
121
|
-
) {
|
|
122
|
-
return this.courseService.syncCourseVideoProfiles(courseId, body.profileIds ?? []);
|
|
123
|
-
}
|
|
124
138
|
}
|
|
@@ -244,7 +244,7 @@ export class LmsCoursesMcpTools {
|
|
|
244
244
|
courseId: { type: 'number', description: 'Course ID' },
|
|
245
245
|
sessionId: { type: 'number', description: 'Session ID' },
|
|
246
246
|
titulo: { type: 'string', description: 'Lesson title (required, max 255)' },
|
|
247
|
-
tipo: { type: 'string', description: 'Lesson type (required): video|questao|post
|
|
247
|
+
tipo: { type: 'string', description: 'Lesson type (required): video|questao|post' },
|
|
248
248
|
descricaoPublica: { type: 'string', description: 'Public description' },
|
|
249
249
|
descricaoPrivada: { type: 'string', description: 'Private description' },
|
|
250
250
|
duracao: { type: 'number', description: 'Duration in seconds' },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AgentModule } from '@hed-hog/agent';
|
|
1
2
|
import { PrismaModule } from '@hed-hog/api-prisma';
|
|
2
3
|
import { CoreModule } from '@hed-hog/core';
|
|
3
4
|
import { QueueModule } from '@hed-hog/queue';
|
|
@@ -8,11 +9,17 @@ import { CourseOperationsIntegrationService } from './course-operations-integrat
|
|
|
8
9
|
import { CourseOperationsController } from './course-operations.controller';
|
|
9
10
|
import { CourseStructureController } from './course-structure.controller';
|
|
10
11
|
import { CourseStructureService } from './course-structure.service';
|
|
12
|
+
import { CourseVideoAgentPipelineService } from './course-video-agent-pipeline.service';
|
|
11
13
|
import { CourseVideoConversionService } from './course-video-conversion.service';
|
|
14
|
+
import { CourseVideoHlsService } from './course-video-hls.service';
|
|
15
|
+
import { CourseExportScorm12Service } from './course-export-scorm12.service';
|
|
16
|
+
import { CourseExportScorm12WorkerService } from './course-export-scorm12-worker.service';
|
|
17
|
+
import { CourseExportService } from './course-export.service';
|
|
12
18
|
import { CourseController } from './course.controller';
|
|
13
19
|
import { LmsCoursesMcpTools } from './course.mcp-tools';
|
|
14
20
|
import { LmsBulkUploadInfraService } from './lms-bulk-upload-infra.service';
|
|
15
21
|
import { CourseService } from './course.service';
|
|
22
|
+
import { LmsBulkUploadAutomationService } from './lms-bulk-upload-automation.service';
|
|
16
23
|
import { LmsBulkUploadController } from './lms-bulk-upload.controller';
|
|
17
24
|
import { LmsBulkUploadService } from './lms-bulk-upload.service';
|
|
18
25
|
import { LmsOperationsTaskSubscriber } from './lms-operations-task.subscriber';
|
|
@@ -24,6 +31,7 @@ import { LmsSettingController } from './lms-setting.controller';
|
|
|
24
31
|
forwardRef(() => InstructorModule),
|
|
25
32
|
forwardRef(() => CoreModule),
|
|
26
33
|
forwardRef(() => QueueModule),
|
|
34
|
+
forwardRef(() => AgentModule),
|
|
27
35
|
],
|
|
28
36
|
controllers: [
|
|
29
37
|
CourseController,
|
|
@@ -34,10 +42,16 @@ import { LmsSettingController } from './lms-setting.controller';
|
|
|
34
42
|
LmsBulkUploadController,
|
|
35
43
|
],
|
|
36
44
|
providers: [
|
|
45
|
+
CourseExportService,
|
|
46
|
+
CourseExportScorm12Service,
|
|
47
|
+
CourseExportScorm12WorkerService,
|
|
37
48
|
CourseOperationsIntegrationService,
|
|
38
49
|
CourseService,
|
|
39
50
|
CourseStructureService,
|
|
51
|
+
CourseVideoAgentPipelineService,
|
|
40
52
|
CourseVideoConversionService,
|
|
53
|
+
CourseVideoHlsService,
|
|
54
|
+
LmsBulkUploadAutomationService,
|
|
41
55
|
LmsBulkUploadInfraService,
|
|
42
56
|
LmsBulkUploadService,
|
|
43
57
|
LmsCoursesMcpTools,
|
|
@@ -47,6 +61,8 @@ import { LmsSettingController } from './lms-setting.controller';
|
|
|
47
61
|
forwardRef(() => CourseService),
|
|
48
62
|
forwardRef(() => CourseStructureService),
|
|
49
63
|
forwardRef(() => CourseVideoConversionService),
|
|
64
|
+
forwardRef(() => CourseVideoHlsService),
|
|
65
|
+
CourseVideoAgentPipelineService,
|
|
50
66
|
],
|
|
51
67
|
})
|
|
52
68
|
export class CourseModule {}
|
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
} from '@nestjs/common';
|
|
17
17
|
import { CourseOperationsIntegrationService } from './course-operations-integration.service';
|
|
18
18
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
COURSE_STORAGE_CLEANUP_CATEGORIES,
|
|
20
|
+
type CourseStorageCleanupCategory,
|
|
21
21
|
} from './dto/cleanup-course-storage.dto';
|
|
22
22
|
import { CreateCourseDto } from './dto/create-course.dto';
|
|
23
23
|
import { UpdateCourseDto } from './dto/update-course.dto';
|
|
@@ -480,10 +480,12 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
480
480
|
]);
|
|
481
481
|
|
|
482
482
|
const courseIds = courses.map((course) => course.id);
|
|
483
|
-
const [extrasById, projectLinksById] =
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
483
|
+
const [extrasById, projectLinksById, coursesWithRunningJobs] =
|
|
484
|
+
await Promise.all([
|
|
485
|
+
this.getCourseExtras(courseIds),
|
|
486
|
+
this.operationsIntegration.getCourseProjectLinks(courseIds),
|
|
487
|
+
this.getCoursesWithRunningJobs(courseIds),
|
|
488
|
+
]);
|
|
487
489
|
|
|
488
490
|
return {
|
|
489
491
|
total,
|
|
@@ -496,6 +498,8 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
496
498
|
undefined,
|
|
497
499
|
extrasById.get(c.id),
|
|
498
500
|
projectLinksById.get(c.id),
|
|
501
|
+
undefined,
|
|
502
|
+
coursesWithRunningJobs.has(c.id),
|
|
499
503
|
),
|
|
500
504
|
),
|
|
501
505
|
};
|
|
@@ -1169,6 +1173,7 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1169
1173
|
course_image: 'Imagens do curso',
|
|
1170
1174
|
course_file: 'Arquivos do curso',
|
|
1171
1175
|
other_lesson_file: 'Outros arquivos',
|
|
1176
|
+
course_export: 'Arquivos de exportação',
|
|
1172
1177
|
};
|
|
1173
1178
|
|
|
1174
1179
|
return labels[category];
|
|
@@ -1265,6 +1270,30 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1265
1270
|
return;
|
|
1266
1271
|
}
|
|
1267
1272
|
|
|
1273
|
+
if (category === 'course_export') {
|
|
1274
|
+
const exports = await this.prisma.course_export.findMany({
|
|
1275
|
+
where: {
|
|
1276
|
+
course_id: courseId,
|
|
1277
|
+
file_id: { not: null },
|
|
1278
|
+
status: { in: ['completed', 'failed'] },
|
|
1279
|
+
},
|
|
1280
|
+
select: { id: true, file_id: true },
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
const fileIds = exports
|
|
1284
|
+
.map((e) => e.file_id)
|
|
1285
|
+
.filter((id): id is number => id != null);
|
|
1286
|
+
|
|
1287
|
+
await this.prisma.course_export.deleteMany({
|
|
1288
|
+
where: { id: { in: exports.map((e) => e.id) } },
|
|
1289
|
+
});
|
|
1290
|
+
|
|
1291
|
+
if (fileIds.length > 0) {
|
|
1292
|
+
await this.fileService.delete('pt', { ids: fileIds }).catch(() => undefined);
|
|
1293
|
+
}
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1268
1297
|
await this.prisma.course_lesson_file.deleteMany({
|
|
1269
1298
|
where: {
|
|
1270
1299
|
course_lesson: { course_module: { course_id: courseId } },
|
|
@@ -1315,6 +1344,15 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1315
1344
|
INNER JOIN course_lesson cl ON cl.id = clvf.course_lesson_id
|
|
1316
1345
|
INNER JOIN course_module cm ON cm.id = cl.course_module_id
|
|
1317
1346
|
WHERE cm.course_id = ${courseId}
|
|
1347
|
+
|
|
1348
|
+
UNION ALL
|
|
1349
|
+
|
|
1350
|
+
SELECT ce.file_id, f.size AS file_size, 'course_export' AS storage_category
|
|
1351
|
+
FROM course_export ce
|
|
1352
|
+
INNER JOIN file f ON f.id = ce.file_id
|
|
1353
|
+
WHERE ce.course_id = ${courseId}
|
|
1354
|
+
AND ce.status = 'completed'
|
|
1355
|
+
AND ce.file_id IS NOT NULL
|
|
1318
1356
|
) AS files
|
|
1319
1357
|
WHERE files.file_id IS NOT NULL
|
|
1320
1358
|
`;
|
|
@@ -1463,6 +1501,7 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1463
1501
|
} | null;
|
|
1464
1502
|
} | null;
|
|
1465
1503
|
}>,
|
|
1504
|
+
hasRunningJobs = false,
|
|
1466
1505
|
) {
|
|
1467
1506
|
const resolvedName = extras?.name ?? c.name ?? null;
|
|
1468
1507
|
const resolvedTitle = this.normalizeOptionalText(c.title) ?? c.slug;
|
|
@@ -1549,6 +1588,7 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1549
1588
|
(c.certificate_template_id
|
|
1550
1589
|
? String(c.certificate_template_id)
|
|
1551
1590
|
: null),
|
|
1591
|
+
hasRunningJobs,
|
|
1552
1592
|
progressByModule: metrics?.progressByModule ?? [],
|
|
1553
1593
|
instructors: Array.from(
|
|
1554
1594
|
new Map(
|
|
@@ -1624,6 +1664,25 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1624
1664
|
return extrasById;
|
|
1625
1665
|
}
|
|
1626
1666
|
|
|
1667
|
+
private async getCoursesWithRunningJobs(
|
|
1668
|
+
courseIds: number[],
|
|
1669
|
+
): Promise<Set<number>> {
|
|
1670
|
+
if (courseIds.length === 0) return new Set();
|
|
1671
|
+
|
|
1672
|
+
const rows = (await this.prisma.$queryRawUnsafe(`
|
|
1673
|
+
SELECT DISTINCT cm.course_id
|
|
1674
|
+
FROM queue_job qj
|
|
1675
|
+
INNER JOIN course_lesson cl ON cl.id = CAST(qj.source_entity_id AS INTEGER)
|
|
1676
|
+
INNER JOIN course_module cm ON cm.id = cl.course_module_id
|
|
1677
|
+
WHERE qj.source_entity = 'course_lesson'
|
|
1678
|
+
AND qj.source_module = 'lms'
|
|
1679
|
+
AND qj.status IN ('pending', 'scheduled', 'processing', 'retrying')
|
|
1680
|
+
AND cm.course_id IN (${courseIds.join(',')})
|
|
1681
|
+
`)) as Array<{ course_id: number }>;
|
|
1682
|
+
|
|
1683
|
+
return new Set(rows.map((r) => Number(r.course_id)));
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1627
1686
|
private async persistCourseExtras(
|
|
1628
1687
|
id: number,
|
|
1629
1688
|
data: PersistCourseExtrasInput,
|
|
@@ -1653,7 +1712,8 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1653
1712
|
}
|
|
1654
1713
|
|
|
1655
1714
|
if (data.offeringType !== undefined) {
|
|
1656
|
-
|
|
1715
|
+
values.push(data.offeringType);
|
|
1716
|
+
setClauses.push(`offering_type = $${values.length}::course_offering_type_8af4d2739d_enum`);
|
|
1657
1717
|
}
|
|
1658
1718
|
|
|
1659
1719
|
if (setClauses.length === 0) {
|
|
@@ -1670,8 +1730,8 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1670
1730
|
`,
|
|
1671
1731
|
...values,
|
|
1672
1732
|
);
|
|
1673
|
-
} catch {
|
|
1674
|
-
|
|
1733
|
+
} catch (err) {
|
|
1734
|
+
console.error('[persistCourseExtras] failed:', err);
|
|
1675
1735
|
}
|
|
1676
1736
|
}
|
|
1677
1737
|
|
|
@@ -1836,53 +1896,6 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1836
1896
|
});
|
|
1837
1897
|
}
|
|
1838
1898
|
|
|
1839
|
-
async getCourseVideoProfiles(courseId: number) {
|
|
1840
|
-
const rows = await (this.prisma as any).course_video_resolution_profile.findMany({
|
|
1841
|
-
where: { course_id: courseId },
|
|
1842
|
-
include: { video_resolution_profile: true },
|
|
1843
|
-
orderBy: { video_resolution_profile: { name: 'asc' } },
|
|
1844
|
-
});
|
|
1845
|
-
|
|
1846
|
-
return (rows as any[]).map((row) => ({
|
|
1847
|
-
id: row.video_resolution_profile.id,
|
|
1848
|
-
name: row.video_resolution_profile.name,
|
|
1849
|
-
ffmpeg_params: row.video_resolution_profile.ffmpeg_params,
|
|
1850
|
-
status: row.video_resolution_profile.status,
|
|
1851
|
-
}));
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
async syncCourseVideoProfiles(courseId: number, profileIds: number[]) {
|
|
1855
|
-
const linkClient = (this.prisma as any).course_video_resolution_profile;
|
|
1856
|
-
|
|
1857
|
-
await this.prisma.$transaction(async (tx: any) => {
|
|
1858
|
-
await tx.course_video_resolution_profile.deleteMany({
|
|
1859
|
-
where: { course_id: courseId },
|
|
1860
|
-
});
|
|
1861
|
-
|
|
1862
|
-
if (profileIds.length > 0) {
|
|
1863
|
-
await tx.course_video_resolution_profile.createMany({
|
|
1864
|
-
data: profileIds.map((id) => ({
|
|
1865
|
-
course_id: courseId,
|
|
1866
|
-
video_resolution_profile_id: id,
|
|
1867
|
-
})),
|
|
1868
|
-
});
|
|
1869
|
-
}
|
|
1870
|
-
});
|
|
1871
|
-
|
|
1872
|
-
return linkClient.findMany({
|
|
1873
|
-
where: { course_id: courseId },
|
|
1874
|
-
include: { video_resolution_profile: true },
|
|
1875
|
-
orderBy: { video_resolution_profile: { name: 'asc' } },
|
|
1876
|
-
}).then((rows: any[]) =>
|
|
1877
|
-
rows.map((row: any) => ({
|
|
1878
|
-
id: row.video_resolution_profile.id,
|
|
1879
|
-
name: row.video_resolution_profile.name,
|
|
1880
|
-
ffmpeg_params: row.video_resolution_profile.ffmpeg_params,
|
|
1881
|
-
status: row.video_resolution_profile.status,
|
|
1882
|
-
})),
|
|
1883
|
-
);
|
|
1884
|
-
}
|
|
1885
|
-
|
|
1886
1899
|
async getCourseContentOverview(courseId: number) {
|
|
1887
1900
|
const [
|
|
1888
1901
|
moduleCount,
|
|
@@ -1892,6 +1905,7 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1892
1905
|
extractedImageCount,
|
|
1893
1906
|
resourceFileCount,
|
|
1894
1907
|
storageRows,
|
|
1908
|
+
videoFileRows,
|
|
1895
1909
|
] = await Promise.all([
|
|
1896
1910
|
this.prisma.course_module.count({ where: { course_id: courseId } }),
|
|
1897
1911
|
this.prisma.course_lesson.findMany({
|
|
@@ -1917,6 +1931,17 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1917
1931
|
},
|
|
1918
1932
|
}),
|
|
1919
1933
|
this.getCourseStorageRows(courseId),
|
|
1934
|
+
this.prisma.course_lesson_file.findMany({
|
|
1935
|
+
where: {
|
|
1936
|
+
course_lesson: { course_module: { course_id: courseId } },
|
|
1937
|
+
OR: [
|
|
1938
|
+
{ type: 'video_original' },
|
|
1939
|
+
{ type: { startsWith: 'video_profile:' } },
|
|
1940
|
+
],
|
|
1941
|
+
file_id: { not: null },
|
|
1942
|
+
},
|
|
1943
|
+
select: { course_lesson_id: true, type: true },
|
|
1944
|
+
}),
|
|
1920
1945
|
]);
|
|
1921
1946
|
|
|
1922
1947
|
const transcriptionLessonIds = new Set(
|
|
@@ -1924,10 +1949,23 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1924
1949
|
);
|
|
1925
1950
|
const xpLessonIds = new Set(xpMapRows.map((r) => r.course_lesson_id));
|
|
1926
1951
|
|
|
1927
|
-
const
|
|
1952
|
+
const videoOriginalLessonIds = new Set(
|
|
1953
|
+
videoFileRows
|
|
1954
|
+
.filter((r) => r.type === 'video_original')
|
|
1955
|
+
.map((r) => r.course_lesson_id),
|
|
1956
|
+
);
|
|
1957
|
+
const videoProfileLessonIds = new Set(
|
|
1958
|
+
videoFileRows
|
|
1959
|
+
.filter((r) => r.type?.startsWith('video_profile:'))
|
|
1960
|
+
.map((r) => r.course_lesson_id),
|
|
1961
|
+
);
|
|
1962
|
+
|
|
1963
|
+
const lessonsByType = { video: 0, questao: 0, post: 0 };
|
|
1928
1964
|
let publishedLessonCount = 0;
|
|
1929
1965
|
let videoWithTranscription = 0;
|
|
1930
1966
|
let videoWithXp = 0;
|
|
1967
|
+
let videoWithVideo = 0;
|
|
1968
|
+
let videoWithProcessedVideo = 0;
|
|
1931
1969
|
|
|
1932
1970
|
const categoryOrder = [
|
|
1933
1971
|
'video_original',
|
|
@@ -1939,6 +1977,7 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1939
1977
|
'course_image',
|
|
1940
1978
|
'course_file',
|
|
1941
1979
|
'other_lesson_file',
|
|
1980
|
+
'course_export',
|
|
1942
1981
|
];
|
|
1943
1982
|
|
|
1944
1983
|
const uniqueStorageFiles = new Map<number, number>();
|
|
@@ -1995,8 +2034,9 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
1995
2034
|
if (lesson.published) publishedLessonCount++;
|
|
1996
2035
|
|
|
1997
2036
|
let sourceType: string | undefined;
|
|
2037
|
+
let parsed: Record<string, unknown> | null = null;
|
|
1998
2038
|
try {
|
|
1999
|
-
|
|
2039
|
+
parsed = lesson.content
|
|
2000
2040
|
? (JSON.parse(lesson.content as string) as Record<string, unknown>)
|
|
2001
2041
|
: null;
|
|
2002
2042
|
sourceType =
|
|
@@ -2007,12 +2047,11 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
2007
2047
|
// ignore malformed content
|
|
2008
2048
|
}
|
|
2009
2049
|
|
|
2010
|
-
let uiType: 'video' | 'questao' | 'post'
|
|
2050
|
+
let uiType: 'video' | 'questao' | 'post';
|
|
2011
2051
|
if (
|
|
2012
2052
|
sourceType === 'video' ||
|
|
2013
2053
|
sourceType === 'questao' ||
|
|
2014
|
-
sourceType === 'post'
|
|
2015
|
-
sourceType === 'exercicio'
|
|
2054
|
+
sourceType === 'post'
|
|
2016
2055
|
) {
|
|
2017
2056
|
uiType = sourceType;
|
|
2018
2057
|
} else if (lesson.type === 'video') {
|
|
@@ -2028,6 +2067,23 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
2028
2067
|
if (uiType === 'video') {
|
|
2029
2068
|
if (transcriptionLessonIds.has(lesson.id)) videoWithTranscription++;
|
|
2030
2069
|
if (xpLessonIds.has(lesson.id)) videoWithXp++;
|
|
2070
|
+
|
|
2071
|
+
const videoUrl =
|
|
2072
|
+
typeof parsed?.videoUrl === 'string' ? parsed.videoUrl : '';
|
|
2073
|
+
const videoProvedor =
|
|
2074
|
+
typeof parsed?.videoProvedor === 'string'
|
|
2075
|
+
? parsed.videoProvedor
|
|
2076
|
+
: 'file_storage';
|
|
2077
|
+
|
|
2078
|
+
const hasVideo =
|
|
2079
|
+
videoUrl.length > 0 || videoOriginalLessonIds.has(lesson.id);
|
|
2080
|
+
const isProcessed =
|
|
2081
|
+
videoProvedor !== 'file_storage'
|
|
2082
|
+
? videoUrl.length > 0
|
|
2083
|
+
: videoProfileLessonIds.has(lesson.id);
|
|
2084
|
+
|
|
2085
|
+
if (hasVideo) videoWithVideo++;
|
|
2086
|
+
if (isProcessed) videoWithProcessedVideo++;
|
|
2031
2087
|
}
|
|
2032
2088
|
}
|
|
2033
2089
|
|
|
@@ -2042,6 +2098,8 @@ export class CourseService implements OnModuleInit, IJobHandler {
|
|
|
2042
2098
|
lessonCount: lessonsByType.video,
|
|
2043
2099
|
withTranscription: videoWithTranscription,
|
|
2044
2100
|
withXp: videoWithXp,
|
|
2101
|
+
withVideo: videoWithVideo,
|
|
2102
|
+
withProcessedVideo: videoWithProcessedVideo,
|
|
2045
2103
|
},
|
|
2046
2104
|
media: { extractedImageCount },
|
|
2047
2105
|
resources: { fileCount: resourceFileCount },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ArrayNotEmpty, IsArray, IsIn, IsOptional } from 'class-validator';
|
|
2
2
|
|
|
3
|
-
export const BULK_UPLOAD_CLEANUP_STATUSES = ['done', 'error', 'cancelled'] as const;
|
|
3
|
+
export const BULK_UPLOAD_CLEANUP_STATUSES = ['received', 'done', 'error', 'cancelled'] as const;
|
|
4
4
|
export const BULK_UPLOAD_CLEANUP_WINDOWS = [
|
|
5
5
|
'last_hour',
|
|
6
6
|
'last_day',
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { IsIn } from 'class-validator';
|
|
1
|
+
import { IsBoolean, IsIn, IsOptional } from 'class-validator';
|
|
2
2
|
|
|
3
3
|
export class CreateCourseBulkJobDto {
|
|
4
|
-
@IsIn(['transcription', 'xp_recalculation'])
|
|
5
|
-
jobType: 'transcription' | 'xp_recalculation';
|
|
4
|
+
@IsIn(['transcription', 'xp_recalculation', 'video_processing'])
|
|
5
|
+
jobType: 'transcription' | 'xp_recalculation' | 'video_processing';
|
|
6
|
+
|
|
7
|
+
@IsBoolean()
|
|
8
|
+
@IsOptional()
|
|
9
|
+
reprocessAlreadyProcessed?: boolean;
|
|
6
10
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Type } from 'class-transformer';
|
|
2
|
+
import {
|
|
3
|
+
IsIn,
|
|
4
|
+
IsInt,
|
|
5
|
+
IsOptional,
|
|
6
|
+
IsString,
|
|
7
|
+
Matches,
|
|
8
|
+
Max,
|
|
9
|
+
Min,
|
|
10
|
+
ValidateNested,
|
|
11
|
+
} from 'class-validator';
|
|
12
|
+
|
|
13
|
+
export class ScormVisualSettingsDto {
|
|
14
|
+
@IsOptional()
|
|
15
|
+
@IsString()
|
|
16
|
+
@Matches(/^#[0-9a-fA-F]{6}$/, {
|
|
17
|
+
message: 'primaryColor deve ser uma cor hex válida (ex: #2563eb)',
|
|
18
|
+
})
|
|
19
|
+
primaryColor?: string;
|
|
20
|
+
|
|
21
|
+
@IsOptional()
|
|
22
|
+
@IsIn(['system', 'humanist', 'serif', 'mono'])
|
|
23
|
+
fontFamily?: string;
|
|
24
|
+
|
|
25
|
+
@IsOptional()
|
|
26
|
+
@IsIn(['sm', 'md', 'lg'])
|
|
27
|
+
fontSize?: string;
|
|
28
|
+
|
|
29
|
+
@IsOptional()
|
|
30
|
+
@IsInt()
|
|
31
|
+
@Min(200)
|
|
32
|
+
@Max(420)
|
|
33
|
+
sidebarWidth?: number;
|
|
34
|
+
|
|
35
|
+
@IsOptional()
|
|
36
|
+
@IsIn(['left', 'right'])
|
|
37
|
+
sidebarPosition?: string;
|
|
38
|
+
|
|
39
|
+
@IsOptional()
|
|
40
|
+
@IsIn(['bar', 'pie'])
|
|
41
|
+
progressStyle?: string;
|
|
42
|
+
|
|
43
|
+
@IsOptional()
|
|
44
|
+
@IsIn(['light', 'dark'])
|
|
45
|
+
sidebarTheme?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class CreateCourseExportDto {
|
|
49
|
+
@IsIn(['scorm_1_2'])
|
|
50
|
+
format: 'scorm_1_2';
|
|
51
|
+
|
|
52
|
+
@IsOptional()
|
|
53
|
+
@ValidateNested()
|
|
54
|
+
@Type(() => ScormVisualSettingsDto)
|
|
55
|
+
visualSettings?: ScormVisualSettingsDto;
|
|
56
|
+
}
|
|
@@ -18,10 +18,11 @@ const COURSE_LESSON_FILE_TYPES = [
|
|
|
18
18
|
'student_download',
|
|
19
19
|
'supplementary_material',
|
|
20
20
|
'video_original',
|
|
21
|
+
'video_hls',
|
|
21
22
|
] as const;
|
|
22
23
|
|
|
23
24
|
const COURSE_LESSON_FILE_TYPE_REGEX =
|
|
24
|
-
/^(lesson_audio|student_download|supplementary_material|video_original|video_profile:\d+)$/;
|
|
25
|
+
/^(lesson_audio|student_download|supplementary_material|video_original|video_hls|video_profile:\d+)$/;
|
|
25
26
|
|
|
26
27
|
export type CourseLessonFileType =
|
|
27
28
|
| (typeof COURSE_LESSON_FILE_TYPES)[number]
|
|
@@ -74,8 +75,8 @@ export class CreateCourseStructureLessonDto {
|
|
|
74
75
|
@IsOptional()
|
|
75
76
|
descricaoPrivada?: string;
|
|
76
77
|
|
|
77
|
-
@IsEnum(['video', 'questao', 'post'
|
|
78
|
-
tipo: 'video' | 'questao' | 'post'
|
|
78
|
+
@IsEnum(['video', 'questao', 'post'])
|
|
79
|
+
tipo: 'video' | 'questao' | 'post';
|
|
79
80
|
|
|
80
81
|
@IsInt()
|
|
81
82
|
@Min(0)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the ffmpeg/ffprobe binaries across environments: explicit env override first,
|
|
6
|
+
* then a WinGet (Gyan.FFmpeg) lookup on Windows, finally the bare command on PATH.
|
|
7
|
+
* Mirrors the resolution baked into CourseVideoHlsService so the decomposed pipeline jobs
|
|
8
|
+
* (extract / split) behave identically.
|
|
9
|
+
*/
|
|
10
|
+
export function getFfmpegCommand(): string {
|
|
11
|
+
const fromEnv = process.env.FFMPEG_PATH?.trim();
|
|
12
|
+
if (fromEnv) return fromEnv;
|
|
13
|
+
if (process.platform === 'win32') {
|
|
14
|
+
const found = findWindowsBinary('ffmpeg');
|
|
15
|
+
if (found) return found;
|
|
16
|
+
}
|
|
17
|
+
return 'ffmpeg';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getFfprobeCommand(): string {
|
|
21
|
+
const fromEnv = process.env.FFPROBE_PATH?.trim();
|
|
22
|
+
if (fromEnv) return fromEnv;
|
|
23
|
+
const ffmpegEnv = process.env.FFMPEG_PATH?.trim();
|
|
24
|
+
if (ffmpegEnv) {
|
|
25
|
+
const candidate = ffmpegEnv.replace(/ffmpeg(\.exe)?$/i, (_, ext) => `ffprobe${ext ?? ''}`);
|
|
26
|
+
if (existsSync(candidate)) return candidate;
|
|
27
|
+
}
|
|
28
|
+
if (process.platform === 'win32') {
|
|
29
|
+
const found = findWindowsBinary('ffprobe');
|
|
30
|
+
if (found) return found;
|
|
31
|
+
}
|
|
32
|
+
return 'ffprobe';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function findWindowsBinary(name: 'ffmpeg' | 'ffprobe'): string | null {
|
|
36
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
37
|
+
if (!localAppData) return null;
|
|
38
|
+
const packagesRoot = join(localAppData, 'Microsoft', 'WinGet', 'Packages');
|
|
39
|
+
try {
|
|
40
|
+
const packageDirs = readdirSync(packagesRoot, { withFileTypes: true })
|
|
41
|
+
.filter((e) => e.isDirectory() && e.name.startsWith('Gyan.FFmpeg'))
|
|
42
|
+
.map((e) => join(packagesRoot, e.name))
|
|
43
|
+
.sort((a, b) => b.localeCompare(a));
|
|
44
|
+
|
|
45
|
+
for (const dir of packageDirs) {
|
|
46
|
+
const direct = join(dir, 'bin', `${name}.exe`);
|
|
47
|
+
if (existsSync(direct)) return direct;
|
|
48
|
+
try {
|
|
49
|
+
const versionDirs = readdirSync(dir, { withFileTypes: true })
|
|
50
|
+
.filter((e) => e.isDirectory() && e.name.toLowerCase().startsWith('ffmpeg-'))
|
|
51
|
+
.map((e) => join(dir, e.name))
|
|
52
|
+
.sort((a, b) => b.localeCompare(a));
|
|
53
|
+
for (const vd of versionDirs) {
|
|
54
|
+
const candidate = join(vd, 'bin', `${name}.exe`);
|
|
55
|
+
if (existsSync(candidate)) return candidate;
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
/* skip */
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
/* skip */
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|