@sigmaott/base-library-next 2.2.5 → 2.2.7

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 (63) hide show
  1. package/.npmrc +3 -0
  2. package/locales/en.yaml +289 -289
  3. package/locales/vi.yaml +294 -294
  4. package/nuxt.config.ts +18 -18
  5. package/package.json +32 -33
  6. package/public/routes.json +33 -33
  7. package/src/api/axios.ts +3 -3
  8. package/src/api/index.ts +86 -86
  9. package/src/api-client-library/.openapi-generator/FILES +20 -20
  10. package/src/api-client-library/.openapi-generator-ignore +23 -23
  11. package/src/api-client-library/api/health-api.ts +119 -119
  12. package/src/api-client-library/api/presets-api.ts +599 -599
  13. package/src/api-client-library/api/profiles-api.ts +676 -676
  14. package/src/api-client-library/api.ts +20 -20
  15. package/src/api-client-library/base.ts +72 -72
  16. package/src/api-client-library/common.ts +150 -150
  17. package/src/api-client-library/configuration.ts +101 -101
  18. package/src/api-client-library/git_push.sh +57 -57
  19. package/src/api-client-library/index.ts +18 -18
  20. package/src/api-client-library/models/create-preset-dto.ts +223 -223
  21. package/src/api-client-library/models/create-profile-dto.ts +45 -45
  22. package/src/api-client-library/models/health-controller-get-health200-response-info-value.ts +32 -32
  23. package/src/api-client-library/models/health-controller-get-health200-response.ts +51 -51
  24. package/src/api-client-library/models/health-controller-get-health503-response.ts +51 -51
  25. package/src/api-client-library/models/index.ts +7 -7
  26. package/src/api-client-library/models/update-preset-dto.ts +223 -223
  27. package/src/api-client-library/models/update-profile-dto.ts +45 -45
  28. package/src/components/MediaSelection.vue +40 -40
  29. package/src/components/PresetModify.vue +154 -154
  30. package/src/components/PresetTable.vue +114 -114
  31. package/src/components/ProfileAllList.vue +137 -137
  32. package/src/components/ProfileFormModal.vue +79 -79
  33. package/src/components/ProfileModify.vue +152 -152
  34. package/src/components/ProfileTable.vue +68 -68
  35. package/src/components/WatermarkDraggableItem.vue +88 -88
  36. package/src/components/channel/ConfigWatermarkItem.vue +239 -239
  37. package/src/components/channel/WatermarkPreview.vue +19 -19
  38. package/src/components/common/Vue3DraggableResizable/Container.vue +71 -71
  39. package/src/components/common/Vue3DraggableResizable/index.vue +1327 -1327
  40. package/src/components/common/Vue3DraggableResizable/utils/dom.js +63 -63
  41. package/src/components/common/Vue3DraggableResizable/utils/fns.js +37 -37
  42. package/src/components/common/VueDraggableResizable/dom.js +63 -63
  43. package/src/components/common/VueDraggableResizable/fns.js +37 -37
  44. package/src/components/common/VueDraggableResizable/index.vue +958 -958
  45. package/src/components/preset/ConfigItem.vue +956 -956
  46. package/src/components/profile/ConfigItem.vue +765 -765
  47. package/src/components/profile/TableColumns.vue +137 -137
  48. package/src/components/shared/AudioInfoViewer.vue +101 -101
  49. package/src/components/shared/MediaInfoViewer.vue +249 -249
  50. package/src/components/shared/MediaInfoViewerSmall.vue +111 -105
  51. package/src/components/shared/PopoverProfile.vue +17 -17
  52. package/src/components/shared/VideoInfoViewer.vue +136 -136
  53. package/src/components/shared/fileSizeFilter.ts +26 -26
  54. package/src/composables/preset.ts +141 -141
  55. package/src/public/build-time.json +1 -1
  56. package/src/public/favicon.svg +15 -15
  57. package/src/public/logo.svg +9 -9
  58. package/src/public/routes.json +86 -86
  59. package/src/utils/common.ts +175 -175
  60. package/src/utils/config.ts +19 -19
  61. package/src/utils/preset.ts +353 -353
  62. package/src/utils/profile.ts +30 -30
  63. package/tsconfig.json +3 -3
@@ -1,765 +1,765 @@
1
- <script lang="ts" setup>
2
- import type { CreatePresetDto } from '@/api-client-library'
3
-
4
- const emit = defineEmits<{
5
- changeNameModifier: [id: string]
6
- }>()
7
-
8
- const { prop, isPackage, videoCodecs, encoderOptions, isMediaLive, isBlockAddData, mode, isConfigEncoder, idx } = definePropsRefs<{
9
- prop?: string
10
- isPackage?: boolean
11
- videoCodecs?: Record<string, string>
12
- encoderOptions?: any
13
- isMediaLive?: boolean
14
- isBlockAddData?: boolean
15
- mode?: string
16
- isConfigEncoder?: boolean
17
- idx?: number
18
- }>()
19
-
20
- const { activeMedia } = defineModels<{
21
- activeMedia: string
22
- }>()
23
-
24
- const { t } = useI18n()
25
- function getProp(key: string) {
26
- return () => prop.value ? `${prop.value}.${key}` : key
27
- }
28
- const creatingMedia = ref<'video' | 'audio' | 'data'>('video')
29
- const isShowMediaSelection = ref(false)
30
- const [nameValue, nameAttrs] = useElField<string>(getProp('name'), [
31
- {
32
- required: true,
33
- validator: nameValidatorLow(t('profile.profile_name')),
34
- trigger: ['blur', 'change'],
35
- },
36
- ])
37
-
38
- const [presetsValue, presetsAttrs, { add, remove }] = useElFieldArray<CreatePresetDto[]>(getProp('presets'), [
39
- {
40
- required: true,
41
- validator: (rule, value, callback) => {
42
- if (value?.length === 0)
43
- callback(new Error(t('profile.preset_required')))
44
- else
45
- callback()
46
- },
47
- trigger: ['blur', 'change'],
48
- },
49
- ])
50
-
51
- function filterPresetsByType(type: string) {
52
- return presetsValue.value?.filter(preset => preset.type === type) || []
53
- }
54
-
55
- function findIndexById(id: string) {
56
- const index = presetsValue.value?.findIndex(preset => preset.id === id)
57
- return index
58
- }
59
- const videoData = computed(() => {
60
- return filterPresetsByType('video') || []
61
- })
62
- const audioData = computed(() => {
63
- return filterPresetsByType('audio') || []
64
- })
65
- const dataData = computed(() => {
66
- return filterPresetsByType('data') || []
67
- })
68
-
69
- const mediaSelectionTitle = computed(() => {
70
- if (creatingMedia.value === 'video')
71
- return 'Add video'
72
- if (creatingMedia.value === 'audio')
73
- return 'Add audio'
74
- if (creatingMedia.value === 'data')
75
- return 'Add data'
76
- return 'Adding media'
77
- })
78
-
79
- const videos = computed(() => {
80
- const _videos: any = []
81
- for (let i = presetsValue.value.length - 1; i >= 0; i--) {
82
- const tmp = presetsValue.value[i]
83
- if (tmp.type === 'video')
84
- _videos.push(tmp)
85
- }
86
- return _videos
87
- })
88
-
89
- const audios = computed(() => {
90
- const _audios: any = []
91
- for (let i = presetsValue.value.length - 1; i >= 0; i--) {
92
- const tmp = presetsValue.value[i]
93
- if (tmp.type === 'audio')
94
- _audios.push(tmp)
95
- }
96
- return _audios
97
- })
98
-
99
- const data = computed(() => {
100
- const _data: any = []
101
- for (let i = presetsValue.value.length - 1; i >= 0; i--) {
102
- const tmp = presetsValue.value[i]
103
- if (tmp.type === 'data')
104
- _data.push(tmp)
105
- }
106
- return _data
107
- })
108
-
109
- const canAddVideo = computed(() => {
110
- return videos.value.length < 1
111
- })
112
-
113
- const canAddAudio = computed(() => {
114
- return audios.value.length < 10
115
- })
116
-
117
- const canAddData = computed(() => {
118
- return data.value.length < 10
119
- })
120
-
121
- function getTabsNameIdx(name: string): number {
122
- const dataSplit = name.split('-')
123
- const idx = Number(dataSplit[dataSplit.length - 1])
124
- return idx
125
- }
126
-
127
- const activeMediaIdx = computed(() => {
128
- let idx
129
- if (activeMedia.value)
130
- idx = getTabsNameIdx(activeMedia.value)
131
- return idx
132
- })
133
-
134
- function addPreset(type: 'video' | 'audio' | 'data') {
135
- creatingMedia.value = type
136
- isShowMediaSelection.value = true
137
- }
138
-
139
- const activeTab = ref('0')
140
- function activeTabChange(type: string) {
141
- activeTab.value = type === 'video' ? '0' : type === 'audio' ? '1' : '2'
142
- if (type === 'video')
143
- activeMedia.value = `media-${videoData.value.length - 1}`
144
- else if (type === 'audio')
145
- activeMedia.value = `media-${audioData.value.length - 1}`
146
- else
147
- activeMedia.value = `media-${dataData.value.length - 1}`
148
- }
149
-
150
- function addEmptyPreset(type: 'video' | 'audio' | 'data') {
151
- const defaultPreset = {
152
- video: isPackage.value
153
- ? {
154
- name: 'video',
155
- type: 'video',
156
- codec: 'copy',
157
- }
158
- : isConfigEncoder.value
159
- ? {
160
- type: 'video',
161
- name: '',
162
- codec: 'h264',
163
- encoderType: 'open_h264',
164
- preset: 'veryfast',
165
- option: '',
166
- description: '',
167
- bitrate: 4000000,
168
- minrate: undefined,
169
- maxrate: undefined,
170
- sampleRate: undefined,
171
- fps: 25,
172
- width: 1920,
173
- height: 1080,
174
- pixelFormat: undefined,
175
- bframe: undefined,
176
- scaleType: undefined,
177
- cq: undefined,
178
- hdr: undefined,
179
- interlaced: false,
180
- cbr: false,
181
- channel: undefined,
182
- profile: undefined,
183
- volume: undefined,
184
- streamId: '',
185
- streamIndex: undefined,
186
- }
187
- : {
188
- type: 'video',
189
- name: '',
190
- codec: 'h264',
191
- encoderType: undefined,
192
- preset: undefined,
193
- option: '',
194
- description: '',
195
- bitrate: 4000000,
196
- minrate: undefined,
197
- maxrate: undefined,
198
- sampleRate: undefined,
199
- fps: 25,
200
- width: 1920,
201
- height: 1080,
202
- pixelFormat: undefined,
203
- bframe: undefined,
204
- scaleType: undefined,
205
- cq: undefined,
206
- hdr: undefined,
207
- interlaced: false,
208
- cbr: false,
209
- channel: undefined,
210
- profile: undefined,
211
- volume: undefined,
212
- streamId: '',
213
- streamIndex: undefined,
214
- },
215
- audio: isPackage.value
216
- ? {
217
- name: 'audio',
218
- type: 'audio',
219
- codec: 'copy',
220
- }
221
- : {
222
- type: 'audio',
223
- name: '',
224
- codec: 'aac',
225
- audioGroupId: undefined,
226
- defaultAudio: undefined,
227
- option: '',
228
- description: '',
229
- bitrate: 64000,
230
- sampleRate: 44100,
231
- fps: undefined,
232
- width: undefined,
233
- height: undefined,
234
- pixelFormat: undefined,
235
- bframe: undefined,
236
- scaleType: undefined,
237
- cq: undefined,
238
- interlaced: false,
239
- cbr: false,
240
- channel: 2,
241
- profile: undefined,
242
- volume: undefined,
243
- streamId: '',
244
- streamIndex: undefined,
245
- },
246
- data: {
247
- type: 'data',
248
- name: '',
249
- codec: 'copy',
250
- option: '',
251
- description: '',
252
- },
253
- }
254
-
255
- if ((type === 'audio' && canAddAudio.value) || (type === 'data' && canAddData.value) || (type === 'video' && canAddVideo.value)) {
256
- presetsValue.value.push({
257
- name: type === 'video' ? `media-${videoData.value.length + 1}` : type === 'audio' ? `media-${audioData.value.length + 1}` : `media-${dataData.value.length + 1}`,
258
- ...defaultPreset[type],
259
- id: genUUID(),
260
- })
261
- }
262
-
263
- activeTabChange(type)
264
- }
265
-
266
- function serializeAudio(data: any) {
267
- const {
268
- name,
269
- option,
270
- description,
271
- type,
272
- codec,
273
- audioGroupId,
274
- defaultAudio,
275
- bitrate,
276
- sampleRate,
277
- channel,
278
- profile,
279
- volume,
280
- id,
281
- language,
282
- label,
283
- } = data
284
- return emptyStr2Undefined({
285
- id,
286
- name,
287
- option,
288
- description,
289
- type,
290
- codec,
291
- audioGroupId,
292
- defaultAudio,
293
- bitrate,
294
- sampleRate,
295
- channel,
296
- profile,
297
- volume,
298
- language,
299
- label,
300
- })
301
- }
302
-
303
- function serializeVideo(data: any) {
304
- const {
305
- name,
306
- option,
307
- description,
308
- type,
309
- fps,
310
- codec,
311
- encoderType,
312
- preset,
313
- bitrate,
314
- minrate,
315
- maxrate,
316
- width,
317
- height,
318
- pixelFormat,
319
- scaleType,
320
- cq,
321
- hdr,
322
- interlaced,
323
- cbr,
324
- bframe,
325
- id,
326
- } = data
327
- return emptyStr2Undefined({
328
- id,
329
- name,
330
- option,
331
- description,
332
- type,
333
- fps,
334
- codec,
335
- encoderType,
336
- preset,
337
- minrate,
338
- bitrate,
339
- maxrate,
340
- width,
341
- height,
342
- pixelFormat,
343
- scaleType,
344
- cq,
345
- hdr,
346
- interlaced,
347
- cbr,
348
- bframe,
349
- })
350
- }
351
-
352
- function serializeCopy(data: any) {
353
- const {
354
- name,
355
- option,
356
- description,
357
- type,
358
- fps,
359
- codec,
360
- bitrate,
361
- width,
362
- height,
363
- pixelFormat,
364
- scaleType,
365
- cq,
366
- interlaced,
367
- cbr,
368
- id,
369
- } = data
370
- return emptyStr2Undefined({
371
- name,
372
- option,
373
- description,
374
- type,
375
- fps,
376
- codec,
377
- bitrate,
378
- width,
379
- height,
380
- pixelFormat,
381
- scaleType,
382
- cq,
383
- interlaced,
384
- cbr,
385
- id,
386
- })
387
- }
388
-
389
- function showNewActiveMedia(type: string) {
390
- // const activeItem = `media-${presetsValue.value.length - 1}`
391
- // activeMedia.value = activeItem
392
-
393
- activeTab.value = type === 'video' ? '0' : type === 'audio' ? '1' : '2'
394
- if (type === 'video')
395
- activeMedia.value = `media-${videoData.value.length}`
396
- else if (type === 'audio')
397
- activeMedia.value = `media-${audioData.value.length}`
398
- else
399
- activeMedia.value = `media-${dataData.value.length}`
400
- }
401
-
402
- function addMedia(data: any) {
403
- if (!canAddVideo.value && data.type === 'video') {
404
- ElMessage.warning('Can not add video. Only one video')
405
- }
406
- else {
407
- if ((data.type === 'audio' && canAddAudio.value) || (data.type === 'data' && canAddData.value) || (data.type === 'video' && canAddVideo.value))
408
- pushPreset(data)
409
-
410
- activeTabChange(data.type)
411
- isShowMediaSelection.value = false
412
- }
413
- }
414
-
415
- function pushPreset(data?: any) {
416
- let serializeData = data
417
- if (data.type === 'video')
418
- serializeData = serializeVideo(data)
419
- else if (data.type === 'audio')
420
- serializeData = serializeAudio(data)
421
- else if (data.type === 'data')
422
- serializeData = serializeCopy(data)
423
- presetsValue.value.push(serializeData)
424
- showNewActiveMedia(data.type)
425
- }
426
-
427
- function setActiveMedia(type: string) {
428
- const activeItem = type === 'video' ? `media-${videoData.value.length - 1}` : type === 'audio' ? `media-${audioData.value.length - 1}` : `media-${dataData.value.length - 1}`
429
- activeMedia.value = activeItem
430
- }
431
-
432
- function handleDestroyMedia(name: any, type: string) {
433
- ElMessageBox.confirm(t('library_profile.are_you_sure_to_remove_media'), t('library_profile.warning'), {
434
- confirmButtonText: t('library_profile.ok'),
435
- cancelButtonText: t('library_action.cancel'),
436
- type: 'warning',
437
- })
438
- .then(() => {
439
- const presetIndex = getTabsNameIdx(name)
440
- const removeId = type === 'video' ? videoData.value[presetIndex]?.id : type === 'audio' ? audioData.value[presetIndex]?.id : dataData.value[presetIndex]?.id
441
-
442
- remove(removeId)
443
-
444
- if (presetsValue.value.length > 0 && activeMediaIdx.value)
445
- setActiveMedia(type)
446
- })
447
- }
448
-
449
- function showErrorArray(error) {
450
- if (typeof error === 'string')
451
- return error
452
- return undefined
453
- }
454
-
455
- function handleChangeName(name: string) {
456
- emit('changeNameModifier', name)
457
- }
458
-
459
- function tabChange(_tab: string) {
460
- activeMedia.value = 'media-0'
461
- }
462
-
463
- onMounted(() => {
464
- if (mode.value === 'create' || mode.value === 'custom') {
465
- addEmptyPreset('video')
466
- }
467
- })
468
-
469
- const { schemaObj } = useAsyncSchema('/api/transcode/api-docs-json', 'CreateProfileDto')
470
- const isBlockEdit = inject('isBlockEdit')
471
-
472
- const { formErrors, formRef } = useElFormContext()
473
-
474
- const isVideoError = computed(() => {
475
- const _errors = presetsValue.value?.map((_item, _idx) => {
476
- return {
477
- isError: hasError(formErrors?.value?.profiles?.[idx.value]?.presets, [`${_idx}`]) || hasError(formErrors?.value?.presets, [`${_idx}`]),
478
- type: _item?.type,
479
- }
480
- })
481
-
482
- return _errors?.some(item => (item.isError === true && item.type === 'video'))
483
- })
484
-
485
- const isAudioError = computed(() => {
486
- const _errors = presetsValue.value?.map((_item, _idx) => {
487
- return {
488
- isError: hasError(formErrors?.value?.profiles?.[idx.value]?.presets, [`${_idx}`]) || hasError(formErrors?.value?.presets, [`${_idx}`]),
489
- type: _item?.type,
490
- }
491
- })
492
-
493
- return _errors?.some(item => (item.isError === true && item.type === 'audio'))
494
- })
495
- const isDataError = computed(() => {
496
- const _errors = presetsValue.value?.map((_item, _idx) => {
497
- return {
498
- isError: hasError(formErrors?.value?.profiles?.[idx.value]?.presets, [`${_idx}`]) || hasError(formErrors?.value?.presets, [`${_idx}`]),
499
- type: _item?.type,
500
- }
501
- })
502
-
503
- return _errors?.some(item => (item.isError === true && item.type === 'data'))
504
- })
505
- </script>
506
-
507
- <template>
508
- <div class="flex items-center gap-5">
509
- <slot name="name">
510
- <el-form-item class="h-80px max-w-50% [&_.el-form-item\_\_error]:relative" v-bind="nameAttrs">
511
- <template #label>
512
- <SSInformationLabel :label="$t('profile.name')" :info="schemaObj.name" />
513
- </template>
514
- <el-input
515
- v-model.trim="nameValue"
516
- placeholder=""
517
- autocomplete="off"
518
- :minlength="1"
519
- :maxlength="50"
520
- show-word-limit
521
- clearable
522
- @change="handleChangeName"
523
- />
524
- </el-form-item>
525
- <slot name="information" />
526
- </slot>
527
- </div>
528
- <slot v-bind="{ canAddVideo, showNewActiveMedia, canAddAudio, canAddData, addEmptyPreset }" name="action">
529
- <div class="flex gap-2">
530
- <el-form-item>
531
- <el-tooltip :disabled="canAddVideo" :content="t('base_library.can_add_only_1_video')">
532
- <el-dropdown placement="bottom-start" trigger="click">
533
- <el-button type="primary" plain :disabled="!canAddVideo">
534
- <template #icon>
535
- <div class="i-ep:plus" />
536
- </template>
537
- Video
538
- </el-button>
539
- <template #dropdown>
540
- <el-dropdown-menu>
541
- <el-dropdown-item @click="addEmptyPreset('video')">
542
- New Preset
543
- </el-dropdown-item>
544
- <el-dropdown-item v-if="!isBlockEdit" @click="addPreset('video')">
545
- Existing Preset
546
- </el-dropdown-item>
547
- </el-dropdown-menu>
548
- </template>
549
- </el-dropdown>
550
- </el-tooltip>
551
- </el-form-item>
552
- <el-form-item>
553
- <el-tooltip :disabled="canAddAudio" :content="t('base_library.can_only_create_10_audio')">
554
- <el-dropdown placement="bottom-start" trigger="click">
555
- <el-button type="warning" plain :disabled="!canAddAudio">
556
- <template #icon>
557
- <div class="i-ep:plus" />
558
- </template>
559
- Audio
560
- </el-button>
561
- <template #dropdown>
562
- <el-dropdown-menu>
563
- <el-dropdown-item @click="addEmptyPreset('audio')">
564
- New Preset
565
- </el-dropdown-item>
566
- <el-dropdown-item v-if="!isBlockEdit" @click="addPreset('audio')">
567
- Existing Preset
568
- </el-dropdown-item>
569
- </el-dropdown-menu>
570
- </template>
571
- </el-dropdown>
572
- </el-tooltip>
573
- </el-form-item>
574
- <el-form-item>
575
- <el-tooltip :disabled="canAddData" :content="t('base_library.can_only_create_10_data')">
576
- <el-dropdown placement="bottom-start" trigger="click">
577
- <el-button plain :disabled="!canAddData">
578
- <template #icon>
579
- <div class="i-ep:plus" />
580
- </template>
581
- Data
582
- </el-button>
583
- <template #dropdown>
584
- <el-dropdown-menu>
585
- <el-dropdown-item @click="addEmptyPreset('data')">
586
- New Preset
587
- </el-dropdown-item>
588
- <el-dropdown-item v-if="!isBlockEdit" @click="addPreset('data')">
589
- Existing Preset
590
- </el-dropdown-item>
591
- </el-dropdown-menu>
592
- </template>
593
- </el-dropdown>
594
- </el-tooltip>
595
- </el-form-item>
596
- </div>
597
- </slot>
598
-
599
- <el-divider content-position="left" class="my-10px flex items-center [&_.el-divider\_\_text.is-left]:translate-y-0">
600
- <el-form-item v-bind="{ ...presetsAttrs, error: showErrorArray(presetsAttrs.error) }" class="w-200px !m-0 [&_.el-form-item\_\_content]:block">
601
- <!-- {{ $t('library_profile.list_of_media') }} -->
602
- </el-form-item>
603
- </el-divider>
604
- <el-tabs
605
- v-model="activeTab"
606
- tab-position="left"
607
- class="demo-tabs [&_.el-tabs\_\_content]:p-10px"
608
- @tab-change="tabChange"
609
- >
610
- <el-tab-pane label="Video">
611
- <template #label>
612
- <div :class="isVideoError ? 'text-error' : ''">
613
- Video ({{ videoData.length }})
614
- </div>
615
- </template>
616
- <el-tabs
617
- v-show="videoData.length > 0"
618
- v-model="activeMedia"
619
- tab-position="top"
620
- type="border-card"
621
- closable
622
- @tab-remove="(name) => handleDestroyMedia(name, 'video')"
623
- >
624
- <el-tab-pane
625
- v-for="(media, _idx) in videoData"
626
- :key="`media-${_idx}`"
627
- :label="media.name"
628
- :name="`media-${_idx}`"
629
- lazy
630
- class=""
631
- >
632
- <template #label>
633
- <span
634
- class="inline-flex items-center text-primary"
635
- :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${_idx}`]) || hasError(formErrors?.presets, [`${_idx}`]) ? '!text-error' : ''"
636
- >
637
- <el-icon>
638
- <div class="i-ep:video-camera-filled" />
639
- </el-icon>&nbsp;
640
- <span :title="media.name" class="block max-w-75px truncate" :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${_idx}`]) || hasError(formErrors?.presets, [`${findIndexById(media.id)}`]) ? '!text-error' : ''">{{ media.name }}</span>
641
- </span>
642
- </template>
643
- <slot name="preset" :prop="getProp(`presets.[${findIndexById(media.id)}]`)()" :media-type="media.type" />
644
- <PresetConfigItem
645
- :is-config-encoder="isConfigEncoder"
646
- :encoder-options="encoderOptions"
647
- :video-codecs="videoCodecs"
648
- :prop="getProp(`presets.[${findIndexById(media.id)}]`)()"
649
- :is-package="isPackage"
650
- :default-media-type="media.type"
651
- />
652
- </el-tab-pane>
653
- </el-tabs>
654
- </el-tab-pane>
655
- <el-tab-pane label="Audio">
656
- <template #label>
657
- <div :class="isAudioError ? 'text-error' : ''">
658
- Audio ({{ audioData.length }})
659
- </div>
660
- </template>
661
- <el-tabs
662
- v-show="audioData.length > 0"
663
- v-model="activeMedia"
664
- tab-position="top"
665
- type="border-card"
666
- closable
667
- @tab-remove="(name) => handleDestroyMedia(name, 'audio')"
668
- >
669
- <el-tab-pane
670
- v-for="(media, _idx) in audioData"
671
- :key="`media-${_idx}`"
672
- :label="media.name"
673
- :name="`media-${_idx}`"
674
- lazy
675
- class=""
676
- >
677
- <template #label>
678
- <span
679
- class="inline-flex items-center text-warning"
680
- :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${findIndexById(media.id)}`]) || hasError(formErrors?.presets, [`${findIndexById(media.id)}`]) ? '!text-error' : ''"
681
- >
682
- <el-icon>
683
- <div class="i-ep:headset" />
684
- </el-icon>&nbsp;
685
- <span :title="media.name" class="block max-w-75px truncate" :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${findIndexById(media.id)}`]) || hasError(formErrors?.presets, [`${findIndexById(media.id)}`]) ? '!text-error' : ''">{{ media.name }}</span>
686
- </span>
687
- </template>
688
- <slot name="preset" :prop="getProp(`presets.[${findIndexById(media.id)}]`)()" :media-type="media.type" />
689
- <PresetConfigItem
690
- :is-config-encoder="isConfigEncoder"
691
- :video-codecs="videoCodecs"
692
- :prop="getProp(`presets.[${findIndexById(media.id)}]`)()"
693
- :is-package="isPackage"
694
- :default-media-type="media.type"
695
- />
696
- </el-tab-pane>
697
- </el-tabs>
698
- </el-tab-pane>
699
- <el-tab-pane v-if="!isBlockAddData" label="Data">
700
- <template #label>
701
- <div :class="isDataError ? 'text-error' : ''">
702
- Data ({{ dataData.length }})
703
- </div>
704
- </template>
705
- <el-tabs
706
- v-show="dataData.length > 0"
707
- v-model="activeMedia"
708
- tab-position="top"
709
- type="border-card"
710
- closable
711
- @tab-remove="(name) => handleDestroyMedia(name, 'data')"
712
- >
713
- <el-tab-pane
714
- v-for="(media, _idx) in dataData"
715
- :key="`media-${_idx}`"
716
- :label="media.name"
717
- :name="`media-${_idx}`"
718
- lazy
719
- class=""
720
- >
721
- <template #label>
722
- <span
723
- class="inline-flex items-center"
724
- :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${findIndexById(media.id)}`]) || hasError(formErrors?.presets, [`${findIndexById(media.id)}`]) ? '!text-error' : ''"
725
- >
726
- <el-icon class="data-icon border">
727
- <div class="i-ep:menu" />
728
- </el-icon>&nbsp;
729
- <span :title="media.name" class="block max-w-75px truncate" :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${findIndexById(media.id)}`]) || hasError(formErrors?.presets, [`${findIndexById(media.id)}`]) ? '!text-error' : ''">{{ media.name }}</span>
730
- </span>
731
- </template>
732
- <slot name="preset" :prop="getProp(`presets.[${findIndexById(media.id)}]`)()" :media-type="media.type" />
733
- <PresetConfigItem
734
- :is-config-encoder="isConfigEncoder"
735
- :video-codecs="videoCodecs"
736
- :prop="getProp(`presets.[${findIndexById(media.id)}]`)()"
737
- :is-package="isPackage"
738
- :default-media-type="media.type"
739
- />
740
- </el-tab-pane>
741
- </el-tabs>
742
- </el-tab-pane>
743
- </el-tabs>
744
-
745
- <el-dialog
746
- v-model="isShowMediaSelection"
747
- append-to-body
748
- width="85%"
749
- :title="mediaSelectionTitle"
750
- >
751
- <slot
752
- v-if="isMediaLive"
753
- name="mediaselection"
754
- :media-type="creatingMedia"
755
- :exclusion="['profiles']"
756
- :select-item="addMedia"
757
- />
758
- <MediaSelection
759
- v-else
760
- :media-type="creatingMedia"
761
- :exclusion="['profiles']"
762
- @select-item="addMedia"
763
- />
764
- </el-dialog>
765
- </template>
1
+ <script lang="ts" setup>
2
+ import type { CreatePresetDto } from '@/api-client-library'
3
+
4
+ const emit = defineEmits<{
5
+ changeNameModifier: [id: string]
6
+ }>()
7
+
8
+ const { prop, isPackage, videoCodecs, encoderOptions, isMediaLive, isBlockAddData, mode, isConfigEncoder, idx } = definePropsRefs<{
9
+ prop?: string
10
+ isPackage?: boolean
11
+ videoCodecs?: Record<string, string>
12
+ encoderOptions?: any
13
+ isMediaLive?: boolean
14
+ isBlockAddData?: boolean
15
+ mode?: string
16
+ isConfigEncoder?: boolean
17
+ idx?: number
18
+ }>()
19
+
20
+ const { activeMedia } = defineModels<{
21
+ activeMedia: string
22
+ }>()
23
+
24
+ const { t } = useI18n()
25
+ function getProp(key: string) {
26
+ return () => prop.value ? `${prop.value}.${key}` : key
27
+ }
28
+ const creatingMedia = ref<'video' | 'audio' | 'data'>('video')
29
+ const isShowMediaSelection = ref(false)
30
+ const [nameValue, nameAttrs] = useElField<string>(getProp('name'), [
31
+ {
32
+ required: true,
33
+ validator: nameValidatorLow(t('profile.profile_name')),
34
+ trigger: ['blur', 'change'],
35
+ },
36
+ ])
37
+
38
+ const [presetsValue, presetsAttrs, { add, remove }] = useElFieldArray<CreatePresetDto[]>(getProp('presets'), [
39
+ {
40
+ required: true,
41
+ validator: (rule, value, callback) => {
42
+ if (value?.length === 0)
43
+ callback(new Error(t('profile.preset_required')))
44
+ else
45
+ callback()
46
+ },
47
+ trigger: ['blur', 'change'],
48
+ },
49
+ ])
50
+
51
+ function filterPresetsByType(type: string) {
52
+ return presetsValue.value?.filter(preset => preset.type === type) || []
53
+ }
54
+
55
+ function findIndexById(id: string) {
56
+ const index = presetsValue.value?.findIndex(preset => preset.id === id)
57
+ return index
58
+ }
59
+ const videoData = computed(() => {
60
+ return filterPresetsByType('video') || []
61
+ })
62
+ const audioData = computed(() => {
63
+ return filterPresetsByType('audio') || []
64
+ })
65
+ const dataData = computed(() => {
66
+ return filterPresetsByType('data') || []
67
+ })
68
+
69
+ const mediaSelectionTitle = computed(() => {
70
+ if (creatingMedia.value === 'video')
71
+ return 'Add video'
72
+ if (creatingMedia.value === 'audio')
73
+ return 'Add audio'
74
+ if (creatingMedia.value === 'data')
75
+ return 'Add data'
76
+ return 'Adding media'
77
+ })
78
+
79
+ const videos = computed(() => {
80
+ const _videos: any = []
81
+ for (let i = presetsValue.value.length - 1; i >= 0; i--) {
82
+ const tmp = presetsValue.value[i]
83
+ if (tmp.type === 'video')
84
+ _videos.push(tmp)
85
+ }
86
+ return _videos
87
+ })
88
+
89
+ const audios = computed(() => {
90
+ const _audios: any = []
91
+ for (let i = presetsValue.value.length - 1; i >= 0; i--) {
92
+ const tmp = presetsValue.value[i]
93
+ if (tmp.type === 'audio')
94
+ _audios.push(tmp)
95
+ }
96
+ return _audios
97
+ })
98
+
99
+ const data = computed(() => {
100
+ const _data: any = []
101
+ for (let i = presetsValue.value.length - 1; i >= 0; i--) {
102
+ const tmp = presetsValue.value[i]
103
+ if (tmp.type === 'data')
104
+ _data.push(tmp)
105
+ }
106
+ return _data
107
+ })
108
+
109
+ const canAddVideo = computed(() => {
110
+ return videos.value.length < 1
111
+ })
112
+
113
+ const canAddAudio = computed(() => {
114
+ return audios.value.length < 10
115
+ })
116
+
117
+ const canAddData = computed(() => {
118
+ return data.value.length < 10
119
+ })
120
+
121
+ function getTabsNameIdx(name: string): number {
122
+ const dataSplit = name.split('-')
123
+ const idx = Number(dataSplit[dataSplit.length - 1])
124
+ return idx
125
+ }
126
+
127
+ const activeMediaIdx = computed(() => {
128
+ let idx
129
+ if (activeMedia.value)
130
+ idx = getTabsNameIdx(activeMedia.value)
131
+ return idx
132
+ })
133
+
134
+ function addPreset(type: 'video' | 'audio' | 'data') {
135
+ creatingMedia.value = type
136
+ isShowMediaSelection.value = true
137
+ }
138
+
139
+ const activeTab = ref('0')
140
+ function activeTabChange(type: string) {
141
+ activeTab.value = type === 'video' ? '0' : type === 'audio' ? '1' : '2'
142
+ if (type === 'video')
143
+ activeMedia.value = `media-${videoData.value.length - 1}`
144
+ else if (type === 'audio')
145
+ activeMedia.value = `media-${audioData.value.length - 1}`
146
+ else
147
+ activeMedia.value = `media-${dataData.value.length - 1}`
148
+ }
149
+
150
+ function addEmptyPreset(type: 'video' | 'audio' | 'data') {
151
+ const defaultPreset = {
152
+ video: isPackage.value
153
+ ? {
154
+ name: 'video',
155
+ type: 'video',
156
+ codec: 'copy',
157
+ }
158
+ : isConfigEncoder.value
159
+ ? {
160
+ type: 'video',
161
+ name: '',
162
+ codec: 'h264',
163
+ encoderType: 'open_h264',
164
+ preset: 'veryfast',
165
+ option: '',
166
+ description: '',
167
+ bitrate: 4000000,
168
+ minrate: undefined,
169
+ maxrate: undefined,
170
+ sampleRate: undefined,
171
+ fps: 25,
172
+ width: 1920,
173
+ height: 1080,
174
+ pixelFormat: undefined,
175
+ bframe: undefined,
176
+ scaleType: undefined,
177
+ cq: undefined,
178
+ hdr: undefined,
179
+ interlaced: false,
180
+ cbr: false,
181
+ channel: undefined,
182
+ profile: undefined,
183
+ volume: undefined,
184
+ streamId: '',
185
+ streamIndex: undefined,
186
+ }
187
+ : {
188
+ type: 'video',
189
+ name: '',
190
+ codec: 'h264',
191
+ encoderType: undefined,
192
+ preset: undefined,
193
+ option: '',
194
+ description: '',
195
+ bitrate: 4000000,
196
+ minrate: undefined,
197
+ maxrate: undefined,
198
+ sampleRate: undefined,
199
+ fps: 25,
200
+ width: 1920,
201
+ height: 1080,
202
+ pixelFormat: undefined,
203
+ bframe: undefined,
204
+ scaleType: undefined,
205
+ cq: undefined,
206
+ hdr: undefined,
207
+ interlaced: false,
208
+ cbr: false,
209
+ channel: undefined,
210
+ profile: undefined,
211
+ volume: undefined,
212
+ streamId: '',
213
+ streamIndex: undefined,
214
+ },
215
+ audio: isPackage.value
216
+ ? {
217
+ name: 'audio',
218
+ type: 'audio',
219
+ codec: 'copy',
220
+ }
221
+ : {
222
+ type: 'audio',
223
+ name: '',
224
+ codec: 'aac',
225
+ audioGroupId: undefined,
226
+ defaultAudio: undefined,
227
+ option: '',
228
+ description: '',
229
+ bitrate: 64000,
230
+ sampleRate: 44100,
231
+ fps: undefined,
232
+ width: undefined,
233
+ height: undefined,
234
+ pixelFormat: undefined,
235
+ bframe: undefined,
236
+ scaleType: undefined,
237
+ cq: undefined,
238
+ interlaced: false,
239
+ cbr: false,
240
+ channel: 2,
241
+ profile: undefined,
242
+ volume: undefined,
243
+ streamId: '',
244
+ streamIndex: undefined,
245
+ },
246
+ data: {
247
+ type: 'data',
248
+ name: '',
249
+ codec: 'copy',
250
+ option: '',
251
+ description: '',
252
+ },
253
+ }
254
+
255
+ if ((type === 'audio' && canAddAudio.value) || (type === 'data' && canAddData.value) || (type === 'video' && canAddVideo.value)) {
256
+ presetsValue.value.push({
257
+ name: type === 'video' ? `media-${videoData.value.length + 1}` : type === 'audio' ? `media-${audioData.value.length + 1}` : `media-${dataData.value.length + 1}`,
258
+ ...defaultPreset[type],
259
+ id: genUUID(),
260
+ })
261
+ }
262
+
263
+ activeTabChange(type)
264
+ }
265
+
266
+ function serializeAudio(data: any) {
267
+ const {
268
+ name,
269
+ option,
270
+ description,
271
+ type,
272
+ codec,
273
+ audioGroupId,
274
+ defaultAudio,
275
+ bitrate,
276
+ sampleRate,
277
+ channel,
278
+ profile,
279
+ volume,
280
+ id,
281
+ language,
282
+ label,
283
+ } = data
284
+ return emptyStr2Undefined({
285
+ id,
286
+ name,
287
+ option,
288
+ description,
289
+ type,
290
+ codec,
291
+ audioGroupId,
292
+ defaultAudio,
293
+ bitrate,
294
+ sampleRate,
295
+ channel,
296
+ profile,
297
+ volume,
298
+ language,
299
+ label,
300
+ })
301
+ }
302
+
303
+ function serializeVideo(data: any) {
304
+ const {
305
+ name,
306
+ option,
307
+ description,
308
+ type,
309
+ fps,
310
+ codec,
311
+ encoderType,
312
+ preset,
313
+ bitrate,
314
+ minrate,
315
+ maxrate,
316
+ width,
317
+ height,
318
+ pixelFormat,
319
+ scaleType,
320
+ cq,
321
+ hdr,
322
+ interlaced,
323
+ cbr,
324
+ bframe,
325
+ id,
326
+ } = data
327
+ return emptyStr2Undefined({
328
+ id,
329
+ name,
330
+ option,
331
+ description,
332
+ type,
333
+ fps,
334
+ codec,
335
+ encoderType,
336
+ preset,
337
+ minrate,
338
+ bitrate,
339
+ maxrate,
340
+ width,
341
+ height,
342
+ pixelFormat,
343
+ scaleType,
344
+ cq,
345
+ hdr,
346
+ interlaced,
347
+ cbr,
348
+ bframe,
349
+ })
350
+ }
351
+
352
+ function serializeCopy(data: any) {
353
+ const {
354
+ name,
355
+ option,
356
+ description,
357
+ type,
358
+ fps,
359
+ codec,
360
+ bitrate,
361
+ width,
362
+ height,
363
+ pixelFormat,
364
+ scaleType,
365
+ cq,
366
+ interlaced,
367
+ cbr,
368
+ id,
369
+ } = data
370
+ return emptyStr2Undefined({
371
+ name,
372
+ option,
373
+ description,
374
+ type,
375
+ fps,
376
+ codec,
377
+ bitrate,
378
+ width,
379
+ height,
380
+ pixelFormat,
381
+ scaleType,
382
+ cq,
383
+ interlaced,
384
+ cbr,
385
+ id,
386
+ })
387
+ }
388
+
389
+ function showNewActiveMedia(type: string) {
390
+ // const activeItem = `media-${presetsValue.value.length - 1}`
391
+ // activeMedia.value = activeItem
392
+
393
+ activeTab.value = type === 'video' ? '0' : type === 'audio' ? '1' : '2'
394
+ if (type === 'video')
395
+ activeMedia.value = `media-${videoData.value.length}`
396
+ else if (type === 'audio')
397
+ activeMedia.value = `media-${audioData.value.length}`
398
+ else
399
+ activeMedia.value = `media-${dataData.value.length}`
400
+ }
401
+
402
+ function addMedia(data: any) {
403
+ if (!canAddVideo.value && data.type === 'video') {
404
+ ElMessage.warning('Can not add video. Only one video')
405
+ }
406
+ else {
407
+ if ((data.type === 'audio' && canAddAudio.value) || (data.type === 'data' && canAddData.value) || (data.type === 'video' && canAddVideo.value))
408
+ pushPreset(data)
409
+
410
+ activeTabChange(data.type)
411
+ isShowMediaSelection.value = false
412
+ }
413
+ }
414
+
415
+ function pushPreset(data?: any) {
416
+ let serializeData = data
417
+ if (data.type === 'video')
418
+ serializeData = serializeVideo(data)
419
+ else if (data.type === 'audio')
420
+ serializeData = serializeAudio(data)
421
+ else if (data.type === 'data')
422
+ serializeData = serializeCopy(data)
423
+ presetsValue.value.push(serializeData)
424
+ showNewActiveMedia(data.type)
425
+ }
426
+
427
+ function setActiveMedia(type: string) {
428
+ const activeItem = type === 'video' ? `media-${videoData.value.length - 1}` : type === 'audio' ? `media-${audioData.value.length - 1}` : `media-${dataData.value.length - 1}`
429
+ activeMedia.value = activeItem
430
+ }
431
+
432
+ function handleDestroyMedia(name: any, type: string) {
433
+ ElMessageBox.confirm(t('library_profile.are_you_sure_to_remove_media'), t('library_profile.warning'), {
434
+ confirmButtonText: t('library_profile.ok'),
435
+ cancelButtonText: t('library_action.cancel'),
436
+ type: 'warning',
437
+ })
438
+ .then(() => {
439
+ const presetIndex = getTabsNameIdx(name)
440
+ const removeId = type === 'video' ? videoData.value[presetIndex]?.id : type === 'audio' ? audioData.value[presetIndex]?.id : dataData.value[presetIndex]?.id
441
+
442
+ remove(removeId)
443
+
444
+ if (presetsValue.value.length > 0 && activeMediaIdx.value)
445
+ setActiveMedia(type)
446
+ })
447
+ }
448
+
449
+ function showErrorArray(error) {
450
+ if (typeof error === 'string')
451
+ return error
452
+ return undefined
453
+ }
454
+
455
+ function handleChangeName(name: string) {
456
+ emit('changeNameModifier', name)
457
+ }
458
+
459
+ function tabChange(_tab: string) {
460
+ activeMedia.value = 'media-0'
461
+ }
462
+
463
+ onMounted(() => {
464
+ if (mode.value === 'create' || mode.value === 'custom') {
465
+ addEmptyPreset('video')
466
+ }
467
+ })
468
+
469
+ const { schemaObj } = useAsyncSchema('/api/transcode/api-docs-json', 'CreateProfileDto')
470
+ const isBlockEdit = inject('isBlockEdit')
471
+
472
+ const { formErrors, formRef } = useElFormContext()
473
+
474
+ const isVideoError = computed(() => {
475
+ const _errors = presetsValue.value?.map((_item, _idx) => {
476
+ return {
477
+ isError: hasError(formErrors?.value?.profiles?.[idx.value]?.presets, [`${_idx}`]) || hasError(formErrors?.value?.presets, [`${_idx}`]),
478
+ type: _item?.type,
479
+ }
480
+ })
481
+
482
+ return _errors?.some(item => (item.isError === true && item.type === 'video'))
483
+ })
484
+
485
+ const isAudioError = computed(() => {
486
+ const _errors = presetsValue.value?.map((_item, _idx) => {
487
+ return {
488
+ isError: hasError(formErrors?.value?.profiles?.[idx.value]?.presets, [`${_idx}`]) || hasError(formErrors?.value?.presets, [`${_idx}`]),
489
+ type: _item?.type,
490
+ }
491
+ })
492
+
493
+ return _errors?.some(item => (item.isError === true && item.type === 'audio'))
494
+ })
495
+ const isDataError = computed(() => {
496
+ const _errors = presetsValue.value?.map((_item, _idx) => {
497
+ return {
498
+ isError: hasError(formErrors?.value?.profiles?.[idx.value]?.presets, [`${_idx}`]) || hasError(formErrors?.value?.presets, [`${_idx}`]),
499
+ type: _item?.type,
500
+ }
501
+ })
502
+
503
+ return _errors?.some(item => (item.isError === true && item.type === 'data'))
504
+ })
505
+ </script>
506
+
507
+ <template>
508
+ <div class="flex items-center gap-5">
509
+ <slot name="name">
510
+ <el-form-item class="h-80px max-w-50% [&_.el-form-item\_\_error]:relative" v-bind="nameAttrs">
511
+ <template #label>
512
+ <SSInformationLabel :label="$t('profile.name')" :info="schemaObj.name" />
513
+ </template>
514
+ <el-input
515
+ v-model.trim="nameValue"
516
+ placeholder=""
517
+ autocomplete="off"
518
+ :minlength="1"
519
+ :maxlength="50"
520
+ show-word-limit
521
+ clearable
522
+ @change="handleChangeName"
523
+ />
524
+ </el-form-item>
525
+ <slot name="information" />
526
+ </slot>
527
+ </div>
528
+ <slot v-bind="{ canAddVideo, showNewActiveMedia, canAddAudio, canAddData, addEmptyPreset }" name="action">
529
+ <div class="flex gap-2">
530
+ <el-form-item>
531
+ <el-tooltip :disabled="canAddVideo" :content="t('base_library.can_add_only_1_video')">
532
+ <el-dropdown placement="bottom-start" trigger="click">
533
+ <el-button type="primary" plain :disabled="!canAddVideo">
534
+ <template #icon>
535
+ <div class="i-ep:plus" />
536
+ </template>
537
+ Video
538
+ </el-button>
539
+ <template #dropdown>
540
+ <el-dropdown-menu>
541
+ <el-dropdown-item @click="addEmptyPreset('video')">
542
+ New Preset
543
+ </el-dropdown-item>
544
+ <el-dropdown-item v-if="!isBlockEdit" @click="addPreset('video')">
545
+ Existing Preset
546
+ </el-dropdown-item>
547
+ </el-dropdown-menu>
548
+ </template>
549
+ </el-dropdown>
550
+ </el-tooltip>
551
+ </el-form-item>
552
+ <el-form-item>
553
+ <el-tooltip :disabled="canAddAudio" :content="t('base_library.can_only_create_10_audio')">
554
+ <el-dropdown placement="bottom-start" trigger="click">
555
+ <el-button type="warning" plain :disabled="!canAddAudio">
556
+ <template #icon>
557
+ <div class="i-ep:plus" />
558
+ </template>
559
+ Audio
560
+ </el-button>
561
+ <template #dropdown>
562
+ <el-dropdown-menu>
563
+ <el-dropdown-item @click="addEmptyPreset('audio')">
564
+ New Preset
565
+ </el-dropdown-item>
566
+ <el-dropdown-item v-if="!isBlockEdit" @click="addPreset('audio')">
567
+ Existing Preset
568
+ </el-dropdown-item>
569
+ </el-dropdown-menu>
570
+ </template>
571
+ </el-dropdown>
572
+ </el-tooltip>
573
+ </el-form-item>
574
+ <el-form-item>
575
+ <el-tooltip :disabled="canAddData" :content="t('base_library.can_only_create_10_data')">
576
+ <el-dropdown placement="bottom-start" trigger="click">
577
+ <el-button plain :disabled="!canAddData">
578
+ <template #icon>
579
+ <div class="i-ep:plus" />
580
+ </template>
581
+ Data
582
+ </el-button>
583
+ <template #dropdown>
584
+ <el-dropdown-menu>
585
+ <el-dropdown-item @click="addEmptyPreset('data')">
586
+ New Preset
587
+ </el-dropdown-item>
588
+ <el-dropdown-item v-if="!isBlockEdit" @click="addPreset('data')">
589
+ Existing Preset
590
+ </el-dropdown-item>
591
+ </el-dropdown-menu>
592
+ </template>
593
+ </el-dropdown>
594
+ </el-tooltip>
595
+ </el-form-item>
596
+ </div>
597
+ </slot>
598
+
599
+ <el-divider content-position="left" class="my-10px flex items-center [&_.el-divider\_\_text.is-left]:translate-y-0">
600
+ <el-form-item v-bind="{ ...presetsAttrs, error: showErrorArray(presetsAttrs.error) }" class="w-200px !m-0 [&_.el-form-item\_\_content]:block">
601
+ <!-- {{ $t('library_profile.list_of_media') }} -->
602
+ </el-form-item>
603
+ </el-divider>
604
+ <el-tabs
605
+ v-model="activeTab"
606
+ tab-position="left"
607
+ class="demo-tabs [&_.el-tabs\_\_content]:p-10px"
608
+ @tab-change="tabChange"
609
+ >
610
+ <el-tab-pane label="Video">
611
+ <template #label>
612
+ <div :class="isVideoError ? 'text-error' : ''">
613
+ Video ({{ videoData.length }})
614
+ </div>
615
+ </template>
616
+ <el-tabs
617
+ v-show="videoData.length > 0"
618
+ v-model="activeMedia"
619
+ tab-position="top"
620
+ type="border-card"
621
+ closable
622
+ @tab-remove="(name) => handleDestroyMedia(name, 'video')"
623
+ >
624
+ <el-tab-pane
625
+ v-for="(media, _idx) in videoData"
626
+ :key="`media-${_idx}`"
627
+ :label="media.name"
628
+ :name="`media-${_idx}`"
629
+ lazy
630
+ class=""
631
+ >
632
+ <template #label>
633
+ <span
634
+ class="inline-flex items-center text-primary"
635
+ :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${_idx}`]) || hasError(formErrors?.presets, [`${_idx}`]) ? '!text-error' : ''"
636
+ >
637
+ <el-icon>
638
+ <div class="i-ep:video-camera-filled" />
639
+ </el-icon>&nbsp;
640
+ <span :title="media.name" class="block max-w-75px truncate" :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${_idx}`]) || hasError(formErrors?.presets, [`${findIndexById(media.id)}`]) ? '!text-error' : ''">{{ media.name }}</span>
641
+ </span>
642
+ </template>
643
+ <slot name="preset" :prop="getProp(`presets.[${findIndexById(media.id)}]`)()" :media-type="media.type" />
644
+ <PresetConfigItem
645
+ :is-config-encoder="isConfigEncoder"
646
+ :encoder-options="encoderOptions"
647
+ :video-codecs="videoCodecs"
648
+ :prop="getProp(`presets.[${findIndexById(media.id)}]`)()"
649
+ :is-package="isPackage"
650
+ :default-media-type="media.type"
651
+ />
652
+ </el-tab-pane>
653
+ </el-tabs>
654
+ </el-tab-pane>
655
+ <el-tab-pane label="Audio">
656
+ <template #label>
657
+ <div :class="isAudioError ? 'text-error' : ''">
658
+ Audio ({{ audioData.length }})
659
+ </div>
660
+ </template>
661
+ <el-tabs
662
+ v-show="audioData.length > 0"
663
+ v-model="activeMedia"
664
+ tab-position="top"
665
+ type="border-card"
666
+ closable
667
+ @tab-remove="(name) => handleDestroyMedia(name, 'audio')"
668
+ >
669
+ <el-tab-pane
670
+ v-for="(media, _idx) in audioData"
671
+ :key="`media-${_idx}`"
672
+ :label="media.name"
673
+ :name="`media-${_idx}`"
674
+ lazy
675
+ class=""
676
+ >
677
+ <template #label>
678
+ <span
679
+ class="inline-flex items-center text-warning"
680
+ :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${findIndexById(media.id)}`]) || hasError(formErrors?.presets, [`${findIndexById(media.id)}`]) ? '!text-error' : ''"
681
+ >
682
+ <el-icon>
683
+ <div class="i-ep:headset" />
684
+ </el-icon>&nbsp;
685
+ <span :title="media.name" class="block max-w-75px truncate" :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${findIndexById(media.id)}`]) || hasError(formErrors?.presets, [`${findIndexById(media.id)}`]) ? '!text-error' : ''">{{ media.name }}</span>
686
+ </span>
687
+ </template>
688
+ <slot name="preset" :prop="getProp(`presets.[${findIndexById(media.id)}]`)()" :media-type="media.type" />
689
+ <PresetConfigItem
690
+ :is-config-encoder="isConfigEncoder"
691
+ :video-codecs="videoCodecs"
692
+ :prop="getProp(`presets.[${findIndexById(media.id)}]`)()"
693
+ :is-package="isPackage"
694
+ :default-media-type="media.type"
695
+ />
696
+ </el-tab-pane>
697
+ </el-tabs>
698
+ </el-tab-pane>
699
+ <el-tab-pane v-if="!isBlockAddData" label="Data">
700
+ <template #label>
701
+ <div :class="isDataError ? 'text-error' : ''">
702
+ Data ({{ dataData.length }})
703
+ </div>
704
+ </template>
705
+ <el-tabs
706
+ v-show="dataData.length > 0"
707
+ v-model="activeMedia"
708
+ tab-position="top"
709
+ type="border-card"
710
+ closable
711
+ @tab-remove="(name) => handleDestroyMedia(name, 'data')"
712
+ >
713
+ <el-tab-pane
714
+ v-for="(media, _idx) in dataData"
715
+ :key="`media-${_idx}`"
716
+ :label="media.name"
717
+ :name="`media-${_idx}`"
718
+ lazy
719
+ class=""
720
+ >
721
+ <template #label>
722
+ <span
723
+ class="inline-flex items-center"
724
+ :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${findIndexById(media.id)}`]) || hasError(formErrors?.presets, [`${findIndexById(media.id)}`]) ? '!text-error' : ''"
725
+ >
726
+ <el-icon class="data-icon border">
727
+ <div class="i-ep:menu" />
728
+ </el-icon>&nbsp;
729
+ <span :title="media.name" class="block max-w-75px truncate" :class="hasError(formErrors?.profiles?.[idx]?.presets, [`${findIndexById(media.id)}`]) || hasError(formErrors?.presets, [`${findIndexById(media.id)}`]) ? '!text-error' : ''">{{ media.name }}</span>
730
+ </span>
731
+ </template>
732
+ <slot name="preset" :prop="getProp(`presets.[${findIndexById(media.id)}]`)()" :media-type="media.type" />
733
+ <PresetConfigItem
734
+ :is-config-encoder="isConfigEncoder"
735
+ :video-codecs="videoCodecs"
736
+ :prop="getProp(`presets.[${findIndexById(media.id)}]`)()"
737
+ :is-package="isPackage"
738
+ :default-media-type="media.type"
739
+ />
740
+ </el-tab-pane>
741
+ </el-tabs>
742
+ </el-tab-pane>
743
+ </el-tabs>
744
+
745
+ <el-dialog
746
+ v-model="isShowMediaSelection"
747
+ append-to-body
748
+ width="85%"
749
+ :title="mediaSelectionTitle"
750
+ >
751
+ <slot
752
+ v-if="isMediaLive"
753
+ name="mediaselection"
754
+ :media-type="creatingMedia"
755
+ :exclusion="['profiles']"
756
+ :select-item="addMedia"
757
+ />
758
+ <MediaSelection
759
+ v-else
760
+ :media-type="creatingMedia"
761
+ :exclusion="['profiles']"
762
+ @select-item="addMedia"
763
+ />
764
+ </el-dialog>
765
+ </template>