@hed-hog/lms 0.0.350 → 0.0.351

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 (160) hide show
  1. package/dist/certificate/certificate.controller.d.ts +2 -2
  2. package/dist/certificate/certificate.controller.d.ts.map +1 -1
  3. package/dist/certificate/certificate.controller.js +8 -6
  4. package/dist/certificate/certificate.controller.js.map +1 -1
  5. package/dist/certificate/certificate.service.d.ts +5 -2
  6. package/dist/certificate/certificate.service.d.ts.map +1 -1
  7. package/dist/certificate/certificate.service.js +70 -6
  8. package/dist/certificate/certificate.service.js.map +1 -1
  9. package/dist/course/course-structure.controller.d.ts +24 -10
  10. package/dist/course/course-structure.controller.d.ts.map +1 -1
  11. package/dist/course/course-structure.controller.js +23 -2
  12. package/dist/course/course-structure.controller.js.map +1 -1
  13. package/dist/course/course-structure.service.d.ts +16 -8
  14. package/dist/course/course-structure.service.d.ts.map +1 -1
  15. package/dist/course/course-structure.service.js +61 -30
  16. package/dist/course/course-structure.service.js.map +1 -1
  17. package/dist/course/course-video-conversion.service.d.ts +37 -0
  18. package/dist/course/course-video-conversion.service.d.ts.map +1 -0
  19. package/dist/course/course-video-conversion.service.js +308 -0
  20. package/dist/course/course-video-conversion.service.js.map +1 -0
  21. package/dist/course/course.controller.d.ts +17 -0
  22. package/dist/course/course.controller.d.ts.map +1 -1
  23. package/dist/course/course.controller.js +23 -0
  24. package/dist/course/course.controller.js.map +1 -1
  25. package/dist/course/course.module.d.ts.map +1 -1
  26. package/dist/course/course.module.js +15 -2
  27. package/dist/course/course.module.js.map +1 -1
  28. package/dist/course/course.service.d.ts +15 -0
  29. package/dist/course/course.service.d.ts.map +1 -1
  30. package/dist/course/course.service.js +103 -49
  31. package/dist/course/course.service.js.map +1 -1
  32. package/dist/course/dto/create-course-structure-lesson.dto.d.ts +5 -1
  33. package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
  34. package/dist/course/dto/create-course-structure-lesson.dto.js +16 -2
  35. package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
  36. package/dist/course/dto/create-course.dto.d.ts +1 -0
  37. package/dist/course/dto/create-course.dto.d.ts.map +1 -1
  38. package/dist/course/dto/create-course.dto.js +9 -0
  39. package/dist/course/dto/create-course.dto.js.map +1 -1
  40. package/dist/enterprise/enterprise.controller.d.ts +3 -3
  41. package/dist/enterprise/enterprise.controller.d.ts.map +1 -1
  42. package/dist/enterprise/enterprise.controller.js +0 -1
  43. package/dist/enterprise/enterprise.controller.js.map +1 -1
  44. package/dist/enterprise/enterprise.service.d.ts +3 -3
  45. package/dist/evaluation/evaluation.service.d.ts.map +1 -1
  46. package/dist/evaluation/evaluation.service.js +9 -2
  47. package/dist/evaluation/evaluation.service.js.map +1 -1
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +1 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/lms.module.d.ts.map +1 -1
  53. package/dist/lms.module.js +3 -0
  54. package/dist/lms.module.js.map +1 -1
  55. package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.d.ts +6 -0
  56. package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.d.ts.map +1 -0
  57. package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.js +33 -0
  58. package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.js.map +1 -0
  59. package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.d.ts +6 -0
  60. package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.d.ts.map +1 -0
  61. package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.js +33 -0
  62. package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.js.map +1 -0
  63. package/dist/video-resolution-profile/video-resolution-profile.controller.d.ts +38 -0
  64. package/dist/video-resolution-profile/video-resolution-profile.controller.d.ts.map +1 -0
  65. package/dist/video-resolution-profile/video-resolution-profile.controller.js +89 -0
  66. package/dist/video-resolution-profile/video-resolution-profile.controller.js.map +1 -0
  67. package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.d.ts +26 -0
  68. package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.d.ts.map +1 -0
  69. package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.js +160 -0
  70. package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.js.map +1 -0
  71. package/dist/video-resolution-profile/video-resolution-profile.module.d.ts +3 -0
  72. package/dist/video-resolution-profile/video-resolution-profile.module.d.ts.map +1 -0
  73. package/dist/video-resolution-profile/video-resolution-profile.module.js +26 -0
  74. package/dist/video-resolution-profile/video-resolution-profile.module.js.map +1 -0
  75. package/dist/video-resolution-profile/video-resolution-profile.service.d.ts +45 -0
  76. package/dist/video-resolution-profile/video-resolution-profile.service.d.ts.map +1 -0
  77. package/dist/video-resolution-profile/video-resolution-profile.service.js +117 -0
  78. package/dist/video-resolution-profile/video-resolution-profile.service.js.map +1 -0
  79. package/hedhog/data/menu.yaml +17 -0
  80. package/hedhog/data/route.yaml +133 -0
  81. package/hedhog/data/video_resolution_profile.yaml +7 -0
  82. package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +269 -324
  83. package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +124 -70
  84. package/hedhog/frontend/app/_components/create-lms-instructor-sheet.tsx.ejs +7 -4
  85. package/hedhog/frontend/app/_components/create-lms-person-sheet.tsx.ejs +2 -2
  86. package/hedhog/frontend/app/_components/create-lms-student-person-sheet.tsx.ejs +2 -2
  87. package/hedhog/frontend/app/_lib/editor/templateSerializer.ts.ejs +34 -4
  88. package/hedhog/frontend/app/_lib/editor/types.ts.ejs +28 -3
  89. package/hedhog/frontend/app/achievements/page.tsx.ejs +9 -3
  90. package/hedhog/frontend/app/bitcodes/page.tsx.ejs +9 -3
  91. package/hedhog/frontend/app/certificates/issued/page.tsx.ejs +7 -3
  92. package/hedhog/frontend/app/certificates/models/CanvasStage.tsx.ejs +29 -8
  93. package/hedhog/frontend/app/certificates/models/LeftPanel.tsx.ejs +14 -0
  94. package/hedhog/frontend/app/certificates/models/RightPanel.tsx.ejs +194 -9
  95. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +15 -5
  96. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +9 -5
  97. package/hedhog/frontend/app/classes/page.tsx.ejs +73 -47
  98. package/hedhog/frontend/app/courses/[id]/_components/CourseCertificateCard.tsx.ejs +19 -9
  99. package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +24 -1
  100. package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +1 -1
  101. package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +1 -1
  102. package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +28 -16
  103. package/hedhog/frontend/app/courses/[id]/_components/CourseSectionCard.tsx.ejs +11 -6
  104. package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +7 -4
  105. package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -0
  106. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +24 -87
  107. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +892 -411
  108. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +1004 -293
  109. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +11 -11
  110. package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +62 -52
  111. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +2 -0
  112. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +19 -6
  113. package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +86 -1
  114. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +3 -0
  115. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +1 -0
  116. package/hedhog/frontend/app/courses/page.tsx.ejs +112 -89
  117. package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +1 -1
  118. package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +10 -3
  119. package/hedhog/frontend/app/enterprise/_components/enterprise-detail-sheet.tsx.ejs +8 -4
  120. package/hedhog/frontend/app/enterprise/_components/enterprise-person-edit-sheet.tsx.ejs +2 -2
  121. package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +10 -4
  122. package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +10 -3
  123. package/hedhog/frontend/app/enterprise/_components/enterprise-user-create-sheet.tsx.ejs +10 -3
  124. package/hedhog/frontend/app/evaluations/_components/evaluation-topic-form-sheet.tsx.ejs +10 -3
  125. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +23 -9
  126. package/hedhog/frontend/app/exams/page.tsx.ejs +14 -6
  127. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +9 -3
  128. package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +190 -17
  129. package/hedhog/frontend/app/layout.tsx.ejs +5 -1
  130. package/hedhog/frontend/app/paths/page.tsx.ejs +13 -5
  131. package/hedhog/frontend/app/reports/evaluations/page.tsx.ejs +10 -10
  132. package/hedhog/frontend/app/training/page.tsx.ejs +13 -5
  133. package/hedhog/frontend/app/video-resolution-profiles/page.tsx.ejs +607 -0
  134. package/hedhog/frontend/messages/en.json +250 -9
  135. package/hedhog/frontend/messages/pt.json +250 -9
  136. package/hedhog/table/course.yaml +4 -0
  137. package/hedhog/table/course_lesson_file.yaml +8 -0
  138. package/hedhog/table/course_video_resolution_profile.yaml +22 -0
  139. package/hedhog/table/video_resolution_profile.yaml +18 -0
  140. package/package.json +7 -6
  141. package/src/certificate/certificate.controller.ts +19 -14
  142. package/src/certificate/certificate.service.ts +106 -11
  143. package/src/course/course-structure.controller.ts +24 -2
  144. package/src/course/course-structure.service.ts +21 -4
  145. package/src/course/course-video-conversion.service.ts +415 -0
  146. package/src/course/course.controller.ts +18 -0
  147. package/src/course/course.module.ts +15 -2
  148. package/src/course/course.service.ts +72 -2
  149. package/src/course/dto/create-course-structure-lesson.dto.ts +13 -2
  150. package/src/course/dto/create-course.dto.ts +8 -0
  151. package/src/enterprise/enterprise.controller.ts +0 -1
  152. package/src/evaluation/evaluation.service.ts +9 -2
  153. package/src/index.ts +1 -0
  154. package/src/lms.module.ts +3 -0
  155. package/src/video-resolution-profile/dto/create-video-resolution-profile.dto.ts +16 -0
  156. package/src/video-resolution-profile/dto/update-video-resolution-profile.dto.ts +16 -0
  157. package/src/video-resolution-profile/video-resolution-profile.controller.ts +62 -0
  158. package/src/video-resolution-profile/video-resolution-profile.mcp-tools.ts +128 -0
  159. package/src/video-resolution-profile/video-resolution-profile.module.ts +13 -0
  160. package/src/video-resolution-profile/video-resolution-profile.service.ts +117 -0
@@ -201,7 +201,17 @@ export class CourseService {
201
201
  orderBy: [{ is_primary: 'desc' }, { order: 'asc' }],
202
202
  },
203
203
  _count: {
204
- select: { course_enrollment: true, course_class_group: true },
204
+ select: {
205
+ course_enrollment: true,
206
+ course_class_group: true,
207
+ course_module: true,
208
+ course_file: true,
209
+ },
210
+ },
211
+ course_module: {
212
+ select: {
213
+ _count: { select: { course_lesson: true } },
214
+ },
205
215
  },
206
216
  },
207
217
  }),
@@ -425,6 +435,7 @@ export class CourseService {
425
435
  }),
426
436
  level: this.normalizeLevel(data.level) ?? 'beginner',
427
437
  status: this.normalizeStatus(data.status) ?? 'draft',
438
+ offering_type: (this.normalizeOfferingType(data.offeringType) ?? 'on_demand') as any,
428
439
  ...(data.requirements !== undefined && {
429
440
  requirements: data.requirements,
430
441
  }),
@@ -455,6 +466,7 @@ export class CourseService {
455
466
  ...(data.secondaryContrastColor !== undefined && {
456
467
  secondary_contrast_color: data.secondaryContrastColor,
457
468
  }),
469
+ ...(data.code !== undefined && { code: data.code }),
458
470
  ...(categories.length > 0 && {
459
471
  course_category: {
460
472
  create: categories.map((item) => ({ category_id: item.id })),
@@ -627,6 +639,10 @@ export class CourseService {
627
639
  ...(data.status !== undefined && {
628
640
  status: this.normalizeStatus(data.status),
629
641
  }),
642
+ ...(data.offeringType !== undefined &&
643
+ this.normalizeOfferingType(data.offeringType) !== undefined && {
644
+ offering_type: this.normalizeOfferingType(data.offeringType) as any,
645
+ }),
630
646
  ...(data.requirements !== undefined && {
631
647
  requirements: data.requirements,
632
648
  }),
@@ -657,6 +673,7 @@ export class CourseService {
657
673
  ...(data.secondaryContrastColor !== undefined && {
658
674
  secondary_contrast_color: data.secondaryContrastColor,
659
675
  }),
676
+ ...(data.code !== undefined && { code: data.code }),
660
677
  ...(categories.length > 0 && {
661
678
  course_category: {
662
679
  create: categories.map((item) => ({ category_id: item.id })),
@@ -811,6 +828,7 @@ export class CourseService {
811
828
  id: c.id,
812
829
  name: resolvedName ?? '',
813
830
  slug: c.slug,
831
+ code: c.code ?? null,
814
832
  title: resolvedTitle,
815
833
  description: c.description ?? '',
816
834
  level: c.level,
@@ -860,7 +878,12 @@ export class CourseService {
860
878
  enrollmentCount: c._count?.course_enrollment ?? 0,
861
879
  moduleCount: c._count?.course_module ?? 0,
862
880
  classCount,
863
- lessonCount: metrics?.lessonCount ?? 0,
881
+ lessonCount:
882
+ (c.course_module as { _count: { course_lesson: number } }[] | undefined)
883
+ ?.reduce((sum: number, m: any) => sum + (m._count?.course_lesson ?? 0), 0) ??
884
+ metrics?.lessonCount ??
885
+ 0,
886
+ resourceCount: c._count?.course_file ?? 0,
864
887
  sessionCount: metrics?.sessionCount ?? 0,
865
888
  averageCompletion: Math.round(metrics?.averageCompletion ?? 0),
866
889
  certificatesIssued:
@@ -1154,4 +1177,51 @@ export class CourseService {
1154
1177
  select: { id: true },
1155
1178
  });
1156
1179
  }
1180
+
1181
+ async getCourseVideoProfiles(courseId: number) {
1182
+ const rows = await (this.prisma as any).course_video_resolution_profile.findMany({
1183
+ where: { course_id: courseId },
1184
+ include: { video_resolution_profile: true },
1185
+ orderBy: { video_resolution_profile: { name: 'asc' } },
1186
+ });
1187
+
1188
+ return (rows as any[]).map((row) => ({
1189
+ id: row.video_resolution_profile.id,
1190
+ name: row.video_resolution_profile.name,
1191
+ ffmpeg_params: row.video_resolution_profile.ffmpeg_params,
1192
+ status: row.video_resolution_profile.status,
1193
+ }));
1194
+ }
1195
+
1196
+ async syncCourseVideoProfiles(courseId: number, profileIds: number[]) {
1197
+ const linkClient = (this.prisma as any).course_video_resolution_profile;
1198
+
1199
+ await this.prisma.$transaction(async (tx: any) => {
1200
+ await tx.course_video_resolution_profile.deleteMany({
1201
+ where: { course_id: courseId },
1202
+ });
1203
+
1204
+ if (profileIds.length > 0) {
1205
+ await tx.course_video_resolution_profile.createMany({
1206
+ data: profileIds.map((id) => ({
1207
+ course_id: courseId,
1208
+ video_resolution_profile_id: id,
1209
+ })),
1210
+ });
1211
+ }
1212
+ });
1213
+
1214
+ return linkClient.findMany({
1215
+ where: { course_id: courseId },
1216
+ include: { video_resolution_profile: true },
1217
+ orderBy: { video_resolution_profile: { name: 'asc' } },
1218
+ }).then((rows: any[]) =>
1219
+ rows.map((row: any) => ({
1220
+ id: row.video_resolution_profile.id,
1221
+ name: row.video_resolution_profile.name,
1222
+ ffmpeg_params: row.video_resolution_profile.ffmpeg_params,
1223
+ status: row.video_resolution_profile.status,
1224
+ })),
1225
+ );
1226
+ }
1157
1227
  }
@@ -32,6 +32,12 @@ class CourseLessonResourceDto {
32
32
  publico?: boolean;
33
33
  }
34
34
 
35
+ export class CreateLessonVideoConversionDto {
36
+ @IsInt()
37
+ @Min(1)
38
+ originalFileId: number;
39
+ }
40
+
35
41
  export class CreateCourseStructureLessonDto {
36
42
  @IsString()
37
43
  @IsNotEmpty()
@@ -60,9 +66,9 @@ export class CreateCourseStructureLessonDto {
60
66
  @IsOptional()
61
67
  duracao: number = 0;
62
68
 
63
- @IsEnum(['youtube', 'vimeo', 'bunny', 'custom'])
69
+ @IsEnum(['youtube', 'vimeo', 'file_storage', 'bunny', 'custom'])
64
70
  @IsOptional()
65
- videoProvedor?: 'youtube' | 'vimeo' | 'bunny' | 'custom';
71
+ videoProvedor?: 'youtube' | 'vimeo' | 'file_storage' | 'bunny' | 'custom';
66
72
 
67
73
  @IsString()
68
74
  @IsOptional()
@@ -76,6 +82,11 @@ export class CreateCourseStructureLessonDto {
76
82
  @IsOptional()
77
83
  transcricao?: string;
78
84
 
85
+ @IsInt()
86
+ @Min(1)
87
+ @IsOptional()
88
+ videoConversionJobId?: number;
89
+
79
90
  @IsInt()
80
91
  @Min(1)
81
92
  @IsOptional()
@@ -123,4 +123,12 @@ export class CreateCourseDto {
123
123
  @IsInt()
124
124
  @IsOptional()
125
125
  operationsProjectId?: number | null;
126
+
127
+ @IsString()
128
+ @MaxLength(32)
129
+ @Matches(/^[A-Z0-9]{2,}$/, {
130
+ message: 'code deve conter apenas letras maiúsculas e números, mínimo 2 caracteres',
131
+ })
132
+ @IsOptional()
133
+ code?: string;
126
134
  }
@@ -118,7 +118,6 @@ export class EnterpriseController {
118
118
  return this.enterpriseService.getCrmOptions();
119
119
  }
120
120
 
121
- @Public()
122
121
  @Get('auth/profiles')
123
122
  async getAuthProfiles(@UserOptional() user: any) {
124
123
  if (!user?.id) return { profiles: [] };
@@ -114,7 +114,14 @@ export class EvaluationService {
114
114
  sessionWhere.course_class_group_id = params.classId;
115
115
  }
116
116
  if (params.instructorId) {
117
- sessionWhere.course_class_group = { instructor_id: params.instructorId };
117
+ sessionWhere.OR = [
118
+ { course_class_group: { instructor_id: params.instructorId } },
119
+ {
120
+ course_class_session_instructor: {
121
+ some: { instructor_id: params.instructorId },
122
+ },
123
+ },
124
+ ];
118
125
  }
119
126
  if (Object.keys(sessionWhere).length > 0) {
120
127
  topicWhere.course_class_session = sessionWhere;
@@ -252,7 +259,7 @@ export class EvaluationService {
252
259
  }
253
260
  if (group.instructor?.person) {
254
261
  instructorName = group.instructor.person.name;
255
- instructorId = group.instructor.person.id;
262
+ instructorId = group.instructor.id;
256
263
  }
257
264
  }
258
265
  } else if (targetType === 'question' && topic.question) {
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ export * from './course-lesson-note/course-lesson-note.module';
12
12
  export * from './course-lesson-note/course-lesson-note.service';
13
13
  export * from './course/course-structure.controller';
14
14
  export * from './course/course-structure.service';
15
+ export * from './course/course-video-conversion.service';
15
16
  export * from './course/course.controller';
16
17
  export * from './course/course.module';
17
18
  export * from './course/course.service';
package/src/lms.module.ts CHANGED
@@ -19,6 +19,7 @@ import { InstructorModule } from './instructor/instructor.module';
19
19
  import { LmsRealtimeModule } from './realtime/lms-realtime.module';
20
20
  import { LmsReportsModule } from './reports/reports.module';
21
21
  import { TrainingModule } from './training/training.module';
22
+ import { VideoResolutionProfileModule } from './video-resolution-profile/video-resolution-profile.module';
22
23
 
23
24
  @Module({
24
25
  imports: [
@@ -42,6 +43,7 @@ import { TrainingModule } from './training/training.module';
42
43
  forwardRef(() => LmsRealtimeModule),
43
44
  forwardRef(() => LmsReportsModule),
44
45
  forwardRef(() => TrainingModule),
46
+ forwardRef(() => VideoResolutionProfileModule),
45
47
  ],
46
48
  exports: [
47
49
  forwardRef(() => AchievementModule),
@@ -60,6 +62,7 @@ import { TrainingModule } from './training/training.module';
60
62
  forwardRef(() => LmsRealtimeModule),
61
63
  forwardRef(() => LmsReportsModule),
62
64
  forwardRef(() => TrainingModule),
65
+ forwardRef(() => VideoResolutionProfileModule),
63
66
  ],
64
67
  })
65
68
  export class LmsModule {}
@@ -0,0 +1,16 @@
1
+ import { IsEnum, IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator';
2
+
3
+ export class CreateVideoResolutionProfileDto {
4
+ @IsString()
5
+ @IsNotEmpty()
6
+ @MaxLength(100)
7
+ name: string;
8
+
9
+ @IsString()
10
+ @IsNotEmpty()
11
+ ffmpeg_params: string;
12
+
13
+ @IsOptional()
14
+ @IsEnum(['active', 'inactive'])
15
+ status?: 'active' | 'inactive';
16
+ }
@@ -0,0 +1,16 @@
1
+ import { IsEnum, IsOptional, IsString, MaxLength } from 'class-validator';
2
+
3
+ export class UpdateVideoResolutionProfileDto {
4
+ @IsOptional()
5
+ @IsString()
6
+ @MaxLength(100)
7
+ name?: string;
8
+
9
+ @IsOptional()
10
+ @IsString()
11
+ ffmpeg_params?: string;
12
+
13
+ @IsOptional()
14
+ @IsEnum(['active', 'inactive'])
15
+ status?: 'active' | 'inactive';
16
+ }
@@ -0,0 +1,62 @@
1
+ import { Role } from '@hed-hog/api';
2
+ import {
3
+ Body,
4
+ Controller,
5
+ Delete,
6
+ Get,
7
+ HttpCode,
8
+ HttpStatus,
9
+ Param,
10
+ ParseIntPipe,
11
+ Patch,
12
+ Post,
13
+ Query,
14
+ } from '@nestjs/common';
15
+ import { CreateVideoResolutionProfileDto } from './dto/create-video-resolution-profile.dto';
16
+ import { UpdateVideoResolutionProfileDto } from './dto/update-video-resolution-profile.dto';
17
+ import { VideoResolutionProfileService } from './video-resolution-profile.service';
18
+
19
+ @Role()
20
+ @Controller('lms/video-resolution-profiles')
21
+ export class VideoResolutionProfileController {
22
+ constructor(
23
+ private readonly videoResolutionProfileService: VideoResolutionProfileService,
24
+ ) {}
25
+
26
+ @Get()
27
+ list(
28
+ @Query('page') page?: string,
29
+ @Query('pageSize') pageSize?: string,
30
+ @Query('search') search?: string,
31
+ ) {
32
+ return this.videoResolutionProfileService.list({
33
+ page: page ? Number(page) : 1,
34
+ pageSize: pageSize ? Number(pageSize) : 20,
35
+ search,
36
+ });
37
+ }
38
+
39
+ @Get('all')
40
+ getAll() {
41
+ return this.videoResolutionProfileService.getAll();
42
+ }
43
+
44
+ @Post()
45
+ create(@Body() dto: CreateVideoResolutionProfileDto) {
46
+ return this.videoResolutionProfileService.create(dto);
47
+ }
48
+
49
+ @Patch(':id')
50
+ update(
51
+ @Param('id', ParseIntPipe) id: number,
52
+ @Body() dto: UpdateVideoResolutionProfileDto,
53
+ ) {
54
+ return this.videoResolutionProfileService.update(id, dto);
55
+ }
56
+
57
+ @Delete(':id')
58
+ @HttpCode(HttpStatus.NO_CONTENT)
59
+ remove(@Param('id', ParseIntPipe) id: number) {
60
+ return this.videoResolutionProfileService.delete(id);
61
+ }
62
+ }
@@ -0,0 +1,128 @@
1
+ import { McpContext, McpTool } from '@hed-hog/core';
2
+ import { Injectable } from '@nestjs/common';
3
+ import { CreateVideoResolutionProfileDto } from './dto/create-video-resolution-profile.dto';
4
+ import { UpdateVideoResolutionProfileDto } from './dto/update-video-resolution-profile.dto';
5
+ import { VideoResolutionProfileService } from './video-resolution-profile.service';
6
+
7
+ @Injectable()
8
+ export class VideoResolutionProfileMcpTools {
9
+ constructor(
10
+ private readonly videoResolutionProfileService: VideoResolutionProfileService,
11
+ ) {}
12
+
13
+ @McpTool({
14
+ name: 'lms.video-resolution-profiles.list',
15
+ description: 'Lists video resolution profiles with optional pagination and search.',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {
19
+ page: { type: 'number', description: 'Page number (default: 1)' },
20
+ pageSize: { type: 'number', description: 'Items per page (default: 20)' },
21
+ search: { type: 'string', description: 'Search term by profile name' },
22
+ },
23
+ },
24
+ readOnly: true,
25
+ })
26
+ async listProfiles(
27
+ args: { page?: number; pageSize?: number; search?: string },
28
+ _context: McpContext,
29
+ ): Promise<any> {
30
+ return this.videoResolutionProfileService.list(args);
31
+ }
32
+
33
+ @McpTool({
34
+ name: 'lms.video-resolution-profiles.list-active',
35
+ description: 'Lists all active video resolution profiles.',
36
+ inputSchema: {
37
+ type: 'object',
38
+ properties: {},
39
+ },
40
+ readOnly: true,
41
+ })
42
+ async listActiveProfiles(
43
+ _args: Record<string, never>,
44
+ _context: McpContext,
45
+ ): Promise<any> {
46
+ return this.videoResolutionProfileService.getAll();
47
+ }
48
+
49
+ @McpTool({
50
+ name: 'lms.video-resolution-profiles.create',
51
+ description: 'Creates a new video resolution profile.',
52
+ inputSchema: {
53
+ type: 'object',
54
+ properties: {
55
+ name: { type: 'string', description: 'Profile name (required, max 100 chars)' },
56
+ ffmpeg_params: { type: 'string', description: 'FFmpeg params string (required)' },
57
+ status: { type: 'string', description: 'Status (active|inactive)' },
58
+ },
59
+ required: ['name', 'ffmpeg_params'],
60
+ },
61
+ })
62
+ async createProfile(
63
+ args: CreateVideoResolutionProfileDto,
64
+ _context: McpContext,
65
+ ): Promise<any> {
66
+ return this.videoResolutionProfileService.create(args);
67
+ }
68
+
69
+ @McpTool({
70
+ name: 'lms.video-resolution-profiles.update',
71
+ description: 'Updates an existing video resolution profile.',
72
+ inputSchema: {
73
+ type: 'object',
74
+ properties: {
75
+ id: { type: 'number', description: 'Profile ID' },
76
+ name: { type: 'string', description: 'Profile name (max 100 chars)' },
77
+ ffmpeg_params: { type: 'string', description: 'FFmpeg params string' },
78
+ status: { type: 'string', description: 'Status (active|inactive)' },
79
+ },
80
+ required: ['id'],
81
+ },
82
+ })
83
+ async updateProfile(
84
+ args: { id: number } & UpdateVideoResolutionProfileDto,
85
+ _context: McpContext,
86
+ ): Promise<any> {
87
+ const { id, ...dto } = args;
88
+ return this.videoResolutionProfileService.update(id, dto);
89
+ }
90
+
91
+ @McpTool({
92
+ name: 'lms.video-resolution-profiles.delete',
93
+ description: 'Removes a video resolution profile by ID.',
94
+ inputSchema: {
95
+ type: 'object',
96
+ properties: {
97
+ id: { type: 'number', description: 'Profile ID' },
98
+ },
99
+ required: ['id'],
100
+ },
101
+ })
102
+ async deleteProfile(
103
+ args: { id: number },
104
+ _context: McpContext,
105
+ ): Promise<any> {
106
+ return this.videoResolutionProfileService.delete(args.id);
107
+ }
108
+
109
+ @McpTool({
110
+ name: 'lms.video-resolution-profiles.toggle-status',
111
+ description: 'Toggles profile status between active and inactive.',
112
+ inputSchema: {
113
+ type: 'object',
114
+ properties: {
115
+ id: { type: 'number', description: 'Profile ID' },
116
+ status: { type: 'string', description: 'Target status (active|inactive)' },
117
+ },
118
+ required: ['id', 'status'],
119
+ },
120
+ })
121
+ async toggleProfileStatus(
122
+ args: { id: number; status: 'active' | 'inactive' },
123
+ _context: McpContext,
124
+ ): Promise<any> {
125
+ const { id, status } = args;
126
+ return this.videoResolutionProfileService.update(id, { status });
127
+ }
128
+ }
@@ -0,0 +1,13 @@
1
+ import { PrismaModule } from '@hed-hog/api-prisma';
2
+ import { forwardRef, Module } from '@nestjs/common';
3
+ import { VideoResolutionProfileController } from './video-resolution-profile.controller';
4
+ import { VideoResolutionProfileMcpTools } from './video-resolution-profile.mcp-tools';
5
+ import { VideoResolutionProfileService } from './video-resolution-profile.service';
6
+
7
+ @Module({
8
+ imports: [forwardRef(() => PrismaModule)],
9
+ controllers: [VideoResolutionProfileController],
10
+ providers: [VideoResolutionProfileService, VideoResolutionProfileMcpTools],
11
+ exports: [forwardRef(() => VideoResolutionProfileService)],
12
+ })
13
+ export class VideoResolutionProfileModule {}
@@ -0,0 +1,117 @@
1
+ import { PrismaService } from '@hed-hog/api-prisma';
2
+ import { Injectable, NotFoundException } from '@nestjs/common';
3
+ import { CreateVideoResolutionProfileDto } from './dto/create-video-resolution-profile.dto';
4
+ import { UpdateVideoResolutionProfileDto } from './dto/update-video-resolution-profile.dto';
5
+
6
+ @Injectable()
7
+ export class VideoResolutionProfileService {
8
+ constructor(private readonly prisma: PrismaService) {}
9
+
10
+ private get profileClient() {
11
+ return (this.prisma as any).video_resolution_profile;
12
+ }
13
+
14
+ async list(params: { page?: number; pageSize?: number; search?: string }) {
15
+ const page = Math.max(Number(params.page) || 1, 1);
16
+ const pageSize = Math.max(Number(params.pageSize) || 20, 1);
17
+ const skip = (page - 1) * pageSize;
18
+ const search = params.search?.trim();
19
+
20
+ const where: any = {};
21
+ if (search) {
22
+ where.name = { contains: search, mode: 'insensitive' };
23
+ }
24
+
25
+ const [rows, total] = await Promise.all([
26
+ this.profileClient.findMany({
27
+ where,
28
+ skip,
29
+ take: pageSize,
30
+ orderBy: { name: 'asc' },
31
+ }),
32
+ this.profileClient.count({ where }),
33
+ ]);
34
+
35
+ return {
36
+ data: (rows as any[]).map((row) => this.mapRow(row)),
37
+ total,
38
+ page,
39
+ pageSize,
40
+ };
41
+ }
42
+
43
+ async getAll() {
44
+ const rows = await this.profileClient.findMany({
45
+ where: { status: 'active' },
46
+ orderBy: { name: 'asc' },
47
+ });
48
+
49
+ return (rows as any[]).map((row) => this.mapRow(row));
50
+ }
51
+
52
+ async create(dto: CreateVideoResolutionProfileDto) {
53
+ const profile = await this.profileClient.create({
54
+ data: {
55
+ name: dto.name,
56
+ ffmpeg_params: dto.ffmpeg_params,
57
+ status: dto.status ?? 'active',
58
+ },
59
+ });
60
+
61
+ return this.getProfileById(profile.id);
62
+ }
63
+
64
+ async update(id: number, dto: UpdateVideoResolutionProfileDto) {
65
+ const profile = await this.profileClient.findUnique({
66
+ where: { id },
67
+ select: { id: true },
68
+ });
69
+
70
+ if (!profile) {
71
+ throw new NotFoundException('Video resolution profile not found');
72
+ }
73
+
74
+ const data: any = {};
75
+ if (dto.name !== undefined) data.name = dto.name;
76
+ if (dto.ffmpeg_params !== undefined) data.ffmpeg_params = dto.ffmpeg_params;
77
+ if (dto.status !== undefined) data.status = dto.status;
78
+
79
+ if (Object.keys(data).length > 0) {
80
+ await this.profileClient.update({ where: { id }, data });
81
+ }
82
+
83
+ return this.getProfileById(id);
84
+ }
85
+
86
+ async delete(id: number) {
87
+ const profile = await this.profileClient.findUnique({
88
+ where: { id },
89
+ select: { id: true },
90
+ });
91
+
92
+ if (!profile) {
93
+ throw new NotFoundException('Video resolution profile not found');
94
+ }
95
+
96
+ await this.profileClient.delete({ where: { id } });
97
+ }
98
+
99
+ private async getProfileById(id: number) {
100
+ const row = await this.profileClient.findUnique({ where: { id } });
101
+
102
+ if (!row) {
103
+ throw new NotFoundException('Video resolution profile not found');
104
+ }
105
+
106
+ return this.mapRow(row);
107
+ }
108
+
109
+ private mapRow(row: any) {
110
+ return {
111
+ id: row.id,
112
+ name: row.name,
113
+ ffmpeg_params: row.ffmpeg_params,
114
+ status: row.status,
115
+ };
116
+ }
117
+ }