@hed-hog/lms 0.0.364 → 0.0.365

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 (218) hide show
  1. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +1 -0
  2. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -1
  3. package/dist/bitcode-wallet/bitcode-wallet.service.js +22 -3
  4. package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -1
  5. package/dist/course/course-export-scorm12-worker.service.d.ts +21 -0
  6. package/dist/course/course-export-scorm12-worker.service.d.ts.map +1 -0
  7. package/dist/course/course-export-scorm12-worker.service.js +109 -0
  8. package/dist/course/course-export-scorm12-worker.service.js.map +1 -0
  9. package/dist/course/course-export-scorm12.service.d.ts +42 -0
  10. package/dist/course/course-export-scorm12.service.d.ts.map +1 -0
  11. package/dist/course/course-export-scorm12.service.js +628 -0
  12. package/dist/course/course-export-scorm12.service.js.map +1 -0
  13. package/dist/course/course-export.service.d.ts +84 -0
  14. package/dist/course/course-export.service.d.ts.map +1 -0
  15. package/dist/course/course-export.service.js +237 -0
  16. package/dist/course/course-export.service.js.map +1 -0
  17. package/dist/course/course-structure.controller.d.ts +17 -9
  18. package/dist/course/course-structure.controller.d.ts.map +1 -1
  19. package/dist/course/course-structure.controller.js +17 -4
  20. package/dist/course/course-structure.controller.js.map +1 -1
  21. package/dist/course/course-structure.service.d.ts +12 -4
  22. package/dist/course/course-structure.service.d.ts.map +1 -1
  23. package/dist/course/course-structure.service.js +98 -23
  24. package/dist/course/course-structure.service.js.map +1 -1
  25. package/dist/course/course-video-hls.service.d.ts +57 -0
  26. package/dist/course/course-video-hls.service.d.ts.map +1 -0
  27. package/dist/course/course-video-hls.service.js +767 -0
  28. package/dist/course/course-video-hls.service.js.map +1 -0
  29. package/dist/course/course.controller.d.ts +45 -13
  30. package/dist/course/course.controller.d.ts.map +1 -1
  31. package/dist/course/course.controller.js +40 -26
  32. package/dist/course/course.controller.js.map +1 -1
  33. package/dist/course/course.mcp-tools.js +1 -1
  34. package/dist/course/course.mcp-tools.js.map +1 -1
  35. package/dist/course/course.module.d.ts.map +1 -1
  36. package/dist/course/course.module.js +11 -0
  37. package/dist/course/course.module.js.map +1 -1
  38. package/dist/course/course.service.d.ts +6 -9
  39. package/dist/course/course.service.d.ts.map +1 -1
  40. package/dist/course/course.service.js +57 -48
  41. package/dist/course/course.service.js.map +1 -1
  42. package/dist/course/dto/cleanup-course-storage.dto.d.ts +1 -1
  43. package/dist/course/dto/cleanup-course-storage.dto.d.ts.map +1 -1
  44. package/dist/course/dto/cleanup-course-storage.dto.js +1 -0
  45. package/dist/course/dto/cleanup-course-storage.dto.js.map +1 -1
  46. package/dist/course/dto/cleanup-upload-history.dto.d.ts +1 -1
  47. package/dist/course/dto/cleanup-upload-history.dto.d.ts.map +1 -1
  48. package/dist/course/dto/cleanup-upload-history.dto.js +1 -1
  49. package/dist/course/dto/cleanup-upload-history.dto.js.map +1 -1
  50. package/dist/course/dto/create-course-bulk-job.dto.d.ts +2 -1
  51. package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -1
  52. package/dist/course/dto/create-course-bulk-job.dto.js +6 -1
  53. package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -1
  54. package/dist/course/dto/create-course-export.dto.d.ts +14 -0
  55. package/dist/course/dto/create-course-export.dto.d.ts.map +1 -0
  56. package/dist/course/dto/create-course-export.dto.js +71 -0
  57. package/dist/course/dto/create-course-export.dto.js.map +1 -0
  58. package/dist/course/dto/create-course-structure-lesson.dto.d.ts +2 -2
  59. package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
  60. package/dist/course/dto/create-course-structure-lesson.dto.js +3 -2
  61. package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
  62. package/dist/course/lms-bulk-upload-automation.service.d.ts +16 -1
  63. package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -1
  64. package/dist/course/lms-bulk-upload-automation.service.js +102 -8
  65. package/dist/course/lms-bulk-upload-automation.service.js.map +1 -1
  66. package/dist/course/lms-bulk-upload-infra.service.d.ts +1 -0
  67. package/dist/course/lms-bulk-upload-infra.service.d.ts.map +1 -1
  68. package/dist/course/lms-bulk-upload-infra.service.js +32 -8
  69. package/dist/course/lms-bulk-upload-infra.service.js.map +1 -1
  70. package/dist/course/lms-bulk-upload.controller.d.ts +30 -3
  71. package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
  72. package/dist/course/lms-bulk-upload.controller.js +43 -2
  73. package/dist/course/lms-bulk-upload.controller.js.map +1 -1
  74. package/dist/course/lms-bulk-upload.service.d.ts +11 -0
  75. package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
  76. package/dist/course/lms-bulk-upload.service.js +59 -6
  77. package/dist/course/lms-bulk-upload.service.js.map +1 -1
  78. package/dist/course/lms-setting.controller.d.ts +2 -1
  79. package/dist/course/lms-setting.controller.d.ts.map +1 -1
  80. package/dist/course/lms-setting.controller.js +4 -2
  81. package/dist/course/lms-setting.controller.js.map +1 -1
  82. package/dist/course/scorm12-schemas.d.ts +4 -0
  83. package/dist/course/scorm12-schemas.d.ts.map +1 -0
  84. package/dist/course/scorm12-schemas.js +9 -0
  85. package/dist/course/scorm12-schemas.js.map +1 -0
  86. package/dist/enterprise/training/training-student.service.d.ts +51 -0
  87. package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
  88. package/dist/enterprise/training/training-student.service.js +217 -4
  89. package/dist/enterprise/training/training-student.service.js.map +1 -1
  90. package/dist/evaluation/evaluation.service.d.ts +18 -0
  91. package/dist/evaluation/evaluation.service.d.ts.map +1 -1
  92. package/dist/evaluation/evaluation.service.js +125 -0
  93. package/dist/evaluation/evaluation.service.js.map +1 -1
  94. package/dist/exam/dto/create-standalone-question.dto.d.ts +12 -0
  95. package/dist/exam/dto/create-standalone-question.dto.d.ts.map +1 -0
  96. package/dist/exam/dto/create-standalone-question.dto.js +70 -0
  97. package/dist/exam/dto/create-standalone-question.dto.js.map +1 -0
  98. package/dist/exam/exam.module.d.ts.map +1 -1
  99. package/dist/exam/exam.module.js +2 -1
  100. package/dist/exam/exam.module.js.map +1 -1
  101. package/dist/exam/exam.service.d.ts +21 -0
  102. package/dist/exam/exam.service.d.ts.map +1 -1
  103. package/dist/exam/exam.service.js +80 -0
  104. package/dist/exam/exam.service.js.map +1 -1
  105. package/dist/exam/question.controller.d.ts +27 -0
  106. package/dist/exam/question.controller.d.ts.map +1 -0
  107. package/dist/exam/question.controller.js +53 -0
  108. package/dist/exam/question.controller.js.map +1 -0
  109. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +4 -0
  110. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -1
  111. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +161 -25
  112. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -1
  113. package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -1
  114. package/dist/lms-commerce-access.subscriber.d.ts +11 -0
  115. package/dist/lms-commerce-access.subscriber.d.ts.map +1 -0
  116. package/dist/lms-commerce-access.subscriber.js +74 -0
  117. package/dist/lms-commerce-access.subscriber.js.map +1 -0
  118. package/dist/lms.module.d.ts.map +1 -1
  119. package/dist/lms.module.js +6 -5
  120. package/dist/lms.module.js.map +1 -1
  121. package/dist/platforma/platforma-video.service.d.ts +39 -0
  122. package/dist/platforma/platforma-video.service.d.ts.map +1 -0
  123. package/dist/platforma/platforma-video.service.js +301 -0
  124. package/dist/platforma/platforma-video.service.js.map +1 -0
  125. package/dist/platforma/platforma.controller.d.ts +95 -1
  126. package/dist/platforma/platforma.controller.d.ts.map +1 -1
  127. package/dist/platforma/platforma.controller.js +160 -2
  128. package/dist/platforma/platforma.controller.js.map +1 -1
  129. package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts +5 -0
  130. package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts.map +1 -0
  131. package/dist/student-xp/dto/grant-skill-card-xp.dto.js +26 -0
  132. package/dist/student-xp/dto/grant-skill-card-xp.dto.js.map +1 -0
  133. package/dist/student-xp/student-xp.controller.d.ts +15 -0
  134. package/dist/student-xp/student-xp.controller.d.ts.map +1 -1
  135. package/dist/student-xp/student-xp.controller.js +24 -0
  136. package/dist/student-xp/student-xp.controller.js.map +1 -1
  137. package/dist/student-xp/student-xp.service.d.ts +16 -0
  138. package/dist/student-xp/student-xp.service.d.ts.map +1 -1
  139. package/dist/student-xp/student-xp.service.js +51 -1
  140. package/dist/student-xp/student-xp.service.js.map +1 -1
  141. package/hedhog/data/evaluation_topic.yaml +17 -0
  142. package/hedhog/data/menu.yaml +0 -17
  143. package/hedhog/data/queue_definition.yaml +48 -0
  144. package/hedhog/data/route.yaml +94 -124
  145. package/hedhog/data/setting_group.yaml +19 -19
  146. package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +337 -41
  147. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +69 -4
  148. package/hedhog/frontend/app/courses/[id]/structure/_components/course-export-sheet.tsx.ejs +420 -0
  149. package/hedhog/frontend/app/courses/[id]/structure/_components/course-exports-tab.tsx.ejs +308 -0
  150. package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +17 -15
  151. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +51 -63
  152. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-panel.tsx.ejs +8 -3
  153. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +31 -8
  154. package/hedhog/frontend/app/courses/[id]/structure/_components/drag-overlay.tsx.ejs +16 -9
  155. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +201 -401
  156. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +378 -690
  157. package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +1 -2
  158. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +3 -9
  159. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +1 -1
  160. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +6 -10
  161. package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +49 -0
  162. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +4 -3
  163. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +0 -1
  164. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-exports.ts.ejs +106 -0
  165. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +28 -1
  166. package/hedhog/frontend/app/courses/[id]/structure/_data/use-lms-settings-query.ts.ejs +0 -2
  167. package/hedhog/frontend/app/courses/page.tsx.ejs +45 -0
  168. package/hedhog/frontend/messages/en.json +26 -28
  169. package/hedhog/frontend/messages/pt.json +26 -28
  170. package/hedhog/table/course_export.yaml +62 -0
  171. package/package.json +13 -9
  172. package/src/bitcode-wallet/bitcode-wallet.service.ts +43 -4
  173. package/src/course/course-export-scorm12-worker.service.ts +124 -0
  174. package/src/course/course-export-scorm12.service.ts +668 -0
  175. package/src/course/course-export.service.ts +280 -0
  176. package/src/course/course-structure.controller.ts +14 -2
  177. package/src/course/course-structure.service.ts +100 -7
  178. package/src/course/course-video-hls.service.ts +946 -0
  179. package/src/course/course.controller.ts +33 -19
  180. package/src/course/course.mcp-tools.ts +1 -1
  181. package/src/course/course.module.ts +11 -0
  182. package/src/course/course.service.ts +73 -60
  183. package/src/course/dto/cleanup-course-storage.dto.ts +1 -0
  184. package/src/course/dto/cleanup-upload-history.dto.ts +1 -1
  185. package/src/course/dto/create-course-bulk-job.dto.ts +7 -3
  186. package/src/course/dto/create-course-export.dto.ts +56 -0
  187. package/src/course/dto/create-course-structure-lesson.dto.ts +4 -3
  188. package/src/course/lms-bulk-upload-automation.service.ts +153 -6
  189. package/src/course/lms-bulk-upload-infra.service.ts +39 -6
  190. package/src/course/lms-bulk-upload.controller.ts +32 -2
  191. package/src/course/lms-bulk-upload.service.ts +70 -7
  192. package/src/course/lms-setting.controller.ts +4 -2
  193. package/src/course/scorm12-schemas.ts +9 -0
  194. package/src/enterprise/training/training-student.service.ts +221 -2
  195. package/src/evaluation/evaluation.service.ts +123 -0
  196. package/src/exam/dto/create-standalone-question.dto.ts +66 -0
  197. package/src/exam/exam.module.ts +2 -1
  198. package/src/exam/exam.service.ts +86 -0
  199. package/src/exam/question.controller.ts +28 -0
  200. package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +205 -31
  201. package/src/lms-commerce-access.subscriber.ts +88 -0
  202. package/src/lms.module.ts +6 -5
  203. package/src/platforma/platforma-video.service.ts +346 -0
  204. package/src/platforma/platforma.controller.ts +95 -1
  205. package/src/platforma/platforma.service.ts +268 -268
  206. package/src/student-xp/dto/grant-skill-card-xp.dto.ts +10 -0
  207. package/src/student-xp/student-xp.controller.ts +18 -2
  208. package/src/student-xp/student-xp.service.ts +84 -2
  209. package/hedhog/data/video_resolution_profile.yaml +0 -7
  210. package/hedhog/frontend/app/video-resolution-profiles/page.tsx.ejs +0 -607
  211. package/hedhog/table/course_video_resolution_profile.yaml +0 -22
  212. package/hedhog/table/video_resolution_profile.yaml +0 -18
  213. package/src/video-resolution-profile/dto/create-video-resolution-profile.dto.ts +0 -16
  214. package/src/video-resolution-profile/dto/update-video-resolution-profile.dto.ts +0 -16
  215. package/src/video-resolution-profile/video-resolution-profile.controller.ts +0 -62
  216. package/src/video-resolution-profile/video-resolution-profile.mcp-tools.ts +0 -128
  217. package/src/video-resolution-profile/video-resolution-profile.module.ts +0 -13
  218. 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(private readonly courseService: CourseService) {}
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|exercicio' },
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' },
@@ -9,10 +9,15 @@ import { CourseOperationsController } from './course-operations.controller';
9
9
  import { CourseStructureController } from './course-structure.controller';
10
10
  import { CourseStructureService } from './course-structure.service';
11
11
  import { CourseVideoConversionService } from './course-video-conversion.service';
12
+ import { CourseVideoHlsService } from './course-video-hls.service';
13
+ import { CourseExportScorm12Service } from './course-export-scorm12.service';
14
+ import { CourseExportScorm12WorkerService } from './course-export-scorm12-worker.service';
15
+ import { CourseExportService } from './course-export.service';
12
16
  import { CourseController } from './course.controller';
13
17
  import { LmsCoursesMcpTools } from './course.mcp-tools';
14
18
  import { LmsBulkUploadInfraService } from './lms-bulk-upload-infra.service';
15
19
  import { CourseService } from './course.service';
20
+ import { LmsBulkUploadAutomationService } from './lms-bulk-upload-automation.service';
16
21
  import { LmsBulkUploadController } from './lms-bulk-upload.controller';
17
22
  import { LmsBulkUploadService } from './lms-bulk-upload.service';
18
23
  import { LmsOperationsTaskSubscriber } from './lms-operations-task.subscriber';
@@ -34,10 +39,15 @@ import { LmsSettingController } from './lms-setting.controller';
34
39
  LmsBulkUploadController,
35
40
  ],
36
41
  providers: [
42
+ CourseExportService,
43
+ CourseExportScorm12Service,
44
+ CourseExportScorm12WorkerService,
37
45
  CourseOperationsIntegrationService,
38
46
  CourseService,
39
47
  CourseStructureService,
40
48
  CourseVideoConversionService,
49
+ CourseVideoHlsService,
50
+ LmsBulkUploadAutomationService,
41
51
  LmsBulkUploadInfraService,
42
52
  LmsBulkUploadService,
43
53
  LmsCoursesMcpTools,
@@ -47,6 +57,7 @@ import { LmsSettingController } from './lms-setting.controller';
47
57
  forwardRef(() => CourseService),
48
58
  forwardRef(() => CourseStructureService),
49
59
  forwardRef(() => CourseVideoConversionService),
60
+ forwardRef(() => CourseVideoHlsService),
50
61
  ],
51
62
  })
52
63
  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
- COURSE_STORAGE_CLEANUP_CATEGORIES,
20
- type CourseStorageCleanupCategory,
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] = await Promise.all([
484
- this.getCourseExtras(courseIds),
485
- this.operationsIntegration.getCourseProjectLinks(courseIds),
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
- pushSetClause('offering_type', data.offeringType);
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
- // Some environments may still be behind the current course schema.
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,
@@ -1924,7 +1937,7 @@ export class CourseService implements OnModuleInit, IJobHandler {
1924
1937
  );
1925
1938
  const xpLessonIds = new Set(xpMapRows.map((r) => r.course_lesson_id));
1926
1939
 
1927
- const lessonsByType = { video: 0, questao: 0, post: 0, exercicio: 0 };
1940
+ const lessonsByType = { video: 0, questao: 0, post: 0 };
1928
1941
  let publishedLessonCount = 0;
1929
1942
  let videoWithTranscription = 0;
1930
1943
  let videoWithXp = 0;
@@ -1939,6 +1952,7 @@ export class CourseService implements OnModuleInit, IJobHandler {
1939
1952
  'course_image',
1940
1953
  'course_file',
1941
1954
  'other_lesson_file',
1955
+ 'course_export',
1942
1956
  ];
1943
1957
 
1944
1958
  const uniqueStorageFiles = new Map<number, number>();
@@ -2007,12 +2021,11 @@ export class CourseService implements OnModuleInit, IJobHandler {
2007
2021
  // ignore malformed content
2008
2022
  }
2009
2023
 
2010
- let uiType: 'video' | 'questao' | 'post' | 'exercicio';
2024
+ let uiType: 'video' | 'questao' | 'post';
2011
2025
  if (
2012
2026
  sourceType === 'video' ||
2013
2027
  sourceType === 'questao' ||
2014
- sourceType === 'post' ||
2015
- sourceType === 'exercicio'
2028
+ sourceType === 'post'
2016
2029
  ) {
2017
2030
  uiType = sourceType;
2018
2031
  } else if (lesson.type === 'video') {
@@ -10,6 +10,7 @@ export const COURSE_STORAGE_CLEANUP_CATEGORIES = [
10
10
  'course_image',
11
11
  'course_file',
12
12
  'other_lesson_file',
13
+ 'course_export',
13
14
  ] as const;
14
15
 
15
16
  export type CourseStorageCleanupCategory =
@@ -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', 'exercicio'])
78
- tipo: 'video' | 'questao' | 'post' | 'exercicio';
78
+ @IsEnum(['video', 'questao', 'post'])
79
+ tipo: 'video' | 'questao' | 'post';
79
80
 
80
81
  @IsInt()
81
82
  @Min(0)