nuxt-media-library 0.0.44

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 (164) hide show
  1. package/README.md +243 -0
  2. package/dist/module.d.mts +31 -0
  3. package/dist/module.d.ts +31 -0
  4. package/dist/module.json +9 -0
  5. package/dist/module.mjs +118 -0
  6. package/dist/runtime/components/MLAssetManager/AssetActivity.d.vue.ts +6 -0
  7. package/dist/runtime/components/MLAssetManager/AssetActivity.vue +13 -0
  8. package/dist/runtime/components/MLAssetManager/AssetActivity.vue.d.ts +6 -0
  9. package/dist/runtime/components/MLAssetManager/AssetCard.d.vue.ts +14 -0
  10. package/dist/runtime/components/MLAssetManager/AssetCard.vue +100 -0
  11. package/dist/runtime/components/MLAssetManager/AssetCard.vue.d.ts +14 -0
  12. package/dist/runtime/components/MLAssetManager/AssetDetail.d.vue.ts +14 -0
  13. package/dist/runtime/components/MLAssetManager/AssetDetail.vue +156 -0
  14. package/dist/runtime/components/MLAssetManager/AssetDetail.vue.d.ts +14 -0
  15. package/dist/runtime/components/MLAssetManager/AssetList.d.vue.ts +15 -0
  16. package/dist/runtime/components/MLAssetManager/AssetList.vue +41 -0
  17. package/dist/runtime/components/MLAssetManager/AssetList.vue.d.ts +15 -0
  18. package/dist/runtime/components/MLAssetManager/AssetOverview.d.vue.ts +8 -0
  19. package/dist/runtime/components/MLAssetManager/AssetOverview.vue +321 -0
  20. package/dist/runtime/components/MLAssetManager/AssetOverview.vue.d.ts +8 -0
  21. package/dist/runtime/components/MLAssetManager/AssetPreview.d.vue.ts +10 -0
  22. package/dist/runtime/components/MLAssetManager/AssetPreview.vue +18 -0
  23. package/dist/runtime/components/MLAssetManager/AssetPreview.vue.d.ts +10 -0
  24. package/dist/runtime/components/MLAssetManager/AssetPreviewAudio.d.vue.ts +9 -0
  25. package/dist/runtime/components/MLAssetManager/AssetPreviewAudio.vue +21 -0
  26. package/dist/runtime/components/MLAssetManager/AssetPreviewAudio.vue.d.ts +9 -0
  27. package/dist/runtime/components/MLAssetManager/AssetPreviewDocument.d.vue.ts +9 -0
  28. package/dist/runtime/components/MLAssetManager/AssetPreviewDocument.vue +39 -0
  29. package/dist/runtime/components/MLAssetManager/AssetPreviewDocument.vue.d.ts +9 -0
  30. package/dist/runtime/components/MLAssetManager/AssetPreviewFallback.d.vue.ts +7 -0
  31. package/dist/runtime/components/MLAssetManager/AssetPreviewFallback.vue +41 -0
  32. package/dist/runtime/components/MLAssetManager/AssetPreviewFallback.vue.d.ts +7 -0
  33. package/dist/runtime/components/MLAssetManager/AssetPreviewImage.d.vue.ts +9 -0
  34. package/dist/runtime/components/MLAssetManager/AssetPreviewImage.vue +30 -0
  35. package/dist/runtime/components/MLAssetManager/AssetPreviewImage.vue.d.ts +9 -0
  36. package/dist/runtime/components/MLAssetManager/AssetPreviewSidebar.d.vue.ts +15 -0
  37. package/dist/runtime/components/MLAssetManager/AssetPreviewSidebar.vue +69 -0
  38. package/dist/runtime/components/MLAssetManager/AssetPreviewSidebar.vue.d.ts +15 -0
  39. package/dist/runtime/components/MLAssetManager/AssetPreviewVideo.d.vue.ts +9 -0
  40. package/dist/runtime/components/MLAssetManager/AssetPreviewVideo.vue +148 -0
  41. package/dist/runtime/components/MLAssetManager/AssetPreviewVideo.vue.d.ts +9 -0
  42. package/dist/runtime/components/MLAssetManager/AssetRenditions.d.vue.ts +8 -0
  43. package/dist/runtime/components/MLAssetManager/AssetRenditions.vue +148 -0
  44. package/dist/runtime/components/MLAssetManager/AssetRenditions.vue.d.ts +8 -0
  45. package/dist/runtime/components/MLAssetManager/Processing.d.vue.ts +8 -0
  46. package/dist/runtime/components/MLAssetManager/Processing.vue +84 -0
  47. package/dist/runtime/components/MLAssetManager/Processing.vue.d.ts +8 -0
  48. package/dist/runtime/components/MLAssetManager/SlotCard.d.vue.ts +17 -0
  49. package/dist/runtime/components/MLAssetManager/SlotCard.vue +94 -0
  50. package/dist/runtime/components/MLAssetManager/SlotCard.vue.d.ts +17 -0
  51. package/dist/runtime/components/MLAssetManager/Widget.d.vue.ts +14 -0
  52. package/dist/runtime/components/MLAssetManager/Widget.vue +328 -0
  53. package/dist/runtime/components/MLAssetManager/Widget.vue.d.ts +14 -0
  54. package/dist/runtime/components/MLPreviewProvider.d.vue.ts +31 -0
  55. package/dist/runtime/components/MLPreviewProvider.vue +51 -0
  56. package/dist/runtime/components/MLPreviewProvider.vue.d.ts +31 -0
  57. package/dist/runtime/components/MLProcessing/Forms/ConfigureStepModal.d.vue.ts +13 -0
  58. package/dist/runtime/components/MLProcessing/Forms/ConfigureStepModal.vue +47 -0
  59. package/dist/runtime/components/MLProcessing/Forms/ConfigureStepModal.vue.d.ts +13 -0
  60. package/dist/runtime/components/MLProcessing/Forms/FramePicker.d.vue.ts +11 -0
  61. package/dist/runtime/components/MLProcessing/Forms/FramePicker.vue +162 -0
  62. package/dist/runtime/components/MLProcessing/Forms/FramePicker.vue.d.ts +11 -0
  63. package/dist/runtime/components/MLProcessing/Forms/StepOptionsForm.d.vue.ts +14 -0
  64. package/dist/runtime/components/MLProcessing/Forms/StepOptionsForm.vue +113 -0
  65. package/dist/runtime/components/MLProcessing/Forms/StepOptionsForm.vue.d.ts +14 -0
  66. package/dist/runtime/components/MLProcessing/Pipeline.d.vue.ts +15 -0
  67. package/dist/runtime/components/MLProcessing/Pipeline.vue +178 -0
  68. package/dist/runtime/components/MLProcessing/Pipeline.vue.d.ts +15 -0
  69. package/dist/runtime/components/MLProcessing/PipelineStepItem.d.vue.ts +20 -0
  70. package/dist/runtime/components/MLProcessing/PipelineStepItem.vue +309 -0
  71. package/dist/runtime/components/MLProcessing/PipelineStepItem.vue.d.ts +20 -0
  72. package/dist/runtime/components/MLProcessing/ProgressBar.d.vue.ts +9 -0
  73. package/dist/runtime/components/MLProcessing/ProgressBar.vue +27 -0
  74. package/dist/runtime/components/MLProcessing/ProgressBar.vue.d.ts +9 -0
  75. package/dist/runtime/components/MLProcessing/StepLayout.d.vue.ts +31 -0
  76. package/dist/runtime/components/MLProcessing/StepLayout.vue +36 -0
  77. package/dist/runtime/components/MLProcessing/StepLayout.vue.d.ts +31 -0
  78. package/dist/runtime/components/MLProcessing/Stepper.d.vue.ts +16 -0
  79. package/dist/runtime/components/MLProcessing/Stepper.vue +87 -0
  80. package/dist/runtime/components/MLProcessing/Stepper.vue.d.ts +16 -0
  81. package/dist/runtime/components/MLProcessing/StepperItem.d.vue.ts +16 -0
  82. package/dist/runtime/components/MLProcessing/StepperItem.vue +150 -0
  83. package/dist/runtime/components/MLProcessing/StepperItem.vue.d.ts +16 -0
  84. package/dist/runtime/components/MLProcessing/TopBar.d.vue.ts +21 -0
  85. package/dist/runtime/components/MLProcessing/TopBar.vue +46 -0
  86. package/dist/runtime/components/MLProcessing/TopBar.vue.d.ts +21 -0
  87. package/dist/runtime/components/MLUploader/ButtonUploader.d.vue.ts +55 -0
  88. package/dist/runtime/components/MLUploader/ButtonUploader.vue +211 -0
  89. package/dist/runtime/components/MLUploader/ButtonUploader.vue.d.ts +55 -0
  90. package/dist/runtime/components/MLUploader/Item.d.vue.ts +34 -0
  91. package/dist/runtime/components/MLUploader/Item.vue +242 -0
  92. package/dist/runtime/components/MLUploader/Item.vue.d.ts +34 -0
  93. package/dist/runtime/components/MLUploader/PolicyPopover.d.vue.ts +7 -0
  94. package/dist/runtime/components/MLUploader/PolicyPopover.vue +160 -0
  95. package/dist/runtime/components/MLUploader/PolicyPopover.vue.d.ts +7 -0
  96. package/dist/runtime/components/MLUploader/Widget.d.vue.ts +44 -0
  97. package/dist/runtime/components/MLUploader/Widget.vue +251 -0
  98. package/dist/runtime/components/MLUploader/Widget.vue.d.ts +44 -0
  99. package/dist/runtime/composables/resourcePolicy/mimeUtils.d.ts +4 -0
  100. package/dist/runtime/composables/resourcePolicy/mimeUtils.js +71 -0
  101. package/dist/runtime/composables/resourcePolicy/useMLResourcePolicy.d.ts +17 -0
  102. package/dist/runtime/composables/resourcePolicy/useMLResourcePolicy.js +150 -0
  103. package/dist/runtime/composables/useElapsedTimer.d.ts +13 -0
  104. package/dist/runtime/composables/useElapsedTimer.js +46 -0
  105. package/dist/runtime/composables/useMLAssetLibrary.d.ts +31 -0
  106. package/dist/runtime/composables/useMLAssetLibrary.js +53 -0
  107. package/dist/runtime/composables/useMLAssetManager.d.ts +30 -0
  108. package/dist/runtime/composables/useMLAssetManager.js +229 -0
  109. package/dist/runtime/composables/useMLAssetManagerPreview.d.ts +28 -0
  110. package/dist/runtime/composables/useMLAssetManagerPreview.js +232 -0
  111. package/dist/runtime/composables/useMLAssetPreview.d.ts +23 -0
  112. package/dist/runtime/composables/useMLAssetPreview.js +153 -0
  113. package/dist/runtime/composables/useMLChunkUploader.d.ts +9 -0
  114. package/dist/runtime/composables/useMLChunkUploader.js +22 -0
  115. package/dist/runtime/composables/useMLProcessingPipeline.d.ts +38 -0
  116. package/dist/runtime/composables/useMLProcessingPipeline.js +258 -0
  117. package/dist/runtime/composables/useMLProcessingStatus.d.ts +34 -0
  118. package/dist/runtime/composables/useMLProcessingStatus.js +100 -0
  119. package/dist/runtime/composables/useMLProcessingStepper.d.ts +32 -0
  120. package/dist/runtime/composables/useMLProcessingStepper.js +280 -0
  121. package/dist/runtime/composables/useMLProcessingStrategies.d.ts +7 -0
  122. package/dist/runtime/composables/useMLProcessingStrategies.js +47 -0
  123. package/dist/runtime/composables/useMLRepeater.d.ts +5 -0
  124. package/dist/runtime/composables/useMLRepeater.js +11 -0
  125. package/dist/runtime/css/index.css +1 -0
  126. package/dist/runtime/index.d.ts +13 -0
  127. package/dist/runtime/index.js +13 -0
  128. package/dist/runtime/plugins/asset-direcetive-client.d.ts +2 -0
  129. package/dist/runtime/plugins/asset-direcetive-client.js +64 -0
  130. package/dist/runtime/plugins/config.d.ts +13 -0
  131. package/dist/runtime/plugins/config.js +23 -0
  132. package/dist/runtime/schemas/resource-policy.schema.d.ts +607 -0
  133. package/dist/runtime/schemas/resource-policy.schema.js +117 -0
  134. package/dist/runtime/server/assets-proxy.d.ts +2 -0
  135. package/dist/runtime/server/assets-proxy.js +81 -0
  136. package/dist/runtime/server/create-ml-proxy.d.ts +47 -0
  137. package/dist/runtime/server/create-ml-proxy.js +232 -0
  138. package/dist/runtime/server/delivery-proxy.d.ts +2 -0
  139. package/dist/runtime/server/delivery-proxy.js +74 -0
  140. package/dist/runtime/server/index.d.ts +2 -0
  141. package/dist/runtime/server/index.js +1 -0
  142. package/dist/runtime/server/processing-proxy.d.ts +2 -0
  143. package/dist/runtime/server/processing-proxy.js +55 -0
  144. package/dist/runtime/server/resource-policy-proxy.d.ts +2 -0
  145. package/dist/runtime/server/resource-policy-proxy.js +80 -0
  146. package/dist/runtime/server/tsconfig.json +0 -0
  147. package/dist/runtime/server/uploads-proxy.d.ts +2 -0
  148. package/dist/runtime/server/uploads-proxy.js +99 -0
  149. package/dist/runtime/types/asset.d.ts +174 -0
  150. package/dist/runtime/types/asset.js +7 -0
  151. package/dist/runtime/types/preview.d.ts +20 -0
  152. package/dist/runtime/types/preview.js +0 -0
  153. package/dist/runtime/types/resourcePolicy.d.ts +77 -0
  154. package/dist/runtime/types/resourcePolicy.js +25 -0
  155. package/dist/runtime/types/uploader.d.ts +71 -0
  156. package/dist/runtime/types/uploader.js +0 -0
  157. package/dist/runtime/utils/uploader/checksum.d.ts +21 -0
  158. package/dist/runtime/utils/uploader/checksum.js +23 -0
  159. package/dist/runtime/utils/uploader/chunking.d.ts +8 -0
  160. package/dist/runtime/utils/uploader/chunking.js +21 -0
  161. package/dist/runtime/utils/uploader/uploader.d.ts +20 -0
  162. package/dist/runtime/utils/uploader/uploader.js +206 -0
  163. package/dist/types.d.mts +11 -0
  164. package/package.json +81 -0
@@ -0,0 +1,9 @@
1
+ import type { ChunkUploaderConfig, UploadFileOptions, UploadResult, UploadSessionResponse } from '../types/uploader.js';
2
+ export declare function useMLChunkUploader(config?: ChunkUploaderConfig): {
3
+ config: any;
4
+ abort: () => void;
5
+ createSession: (payload: any) => Promise<UploadSessionResponse>;
6
+ getSession: (id: string) => Promise<UploadSessionResponse>;
7
+ abortSession: (id: string) => Promise<void>;
8
+ uploadFile: (file: File, options: UploadFileOptions) => Promise<UploadResult>;
9
+ };
@@ -0,0 +1,22 @@
1
+ import { getCurrentInstance, inject } from "vue";
2
+ import { ChunkUploader } from "../utils/uploader/uploader.js";
3
+ export function useMLChunkUploader(config) {
4
+ const instance = getCurrentInstance();
5
+ const injected = inject("chunkUploader", null);
6
+ const globalCfg = instance?.appContext.config.globalProperties?.$chunkUploader;
7
+ const globalFallback = globalThis?.MediaLibraryConfig?.chunkUploader ?? globalThis?.MediaLibraryChunkUploader;
8
+ const cfg = config ?? injected ?? globalCfg ?? globalFallback;
9
+ if (!cfg) {
10
+ throw new Error("ChunkUploader config missing. Pass a config or provide it via app.provide().");
11
+ }
12
+ const normalizedCfg = cfg.proxyUploads ? { ...cfg, baseURL: "" } : cfg;
13
+ const uploader = new ChunkUploader(normalizedCfg);
14
+ return {
15
+ config: cfg,
16
+ abort: () => uploader.abort(),
17
+ createSession: (payload) => uploader.createSession(payload),
18
+ getSession: (id) => uploader.getSession(id),
19
+ abortSession: (id) => uploader.abortSession(id),
20
+ uploadFile: (file, options) => uploader.uploadFile(file, options)
21
+ };
22
+ }
@@ -0,0 +1,38 @@
1
+ import { type ComputedRef, type MaybeRef, type Ref, type ShallowRef } from 'vue';
2
+ import type { ProcessingEvent, ProcessingStream } from './useMLProcessingStatus.js';
3
+ import type { ProcessingTask } from '../types/asset.js';
4
+ export interface UseMLProcessingPipelineReturn {
5
+ tasks: Ref<ProcessingTask[]>;
6
+ loading: Ref<boolean>;
7
+ stream: ShallowRef<ProcessingStream | null>;
8
+ taskEventMap: ComputedRef<Map<string, ProcessingEvent>>;
9
+ pipelineStatus: ComputedRef<'queued' | 'running' | 'succeeded' | 'failed' | 'awaiting_input'>;
10
+ pipelineElapsed: Ref<string>;
11
+ completedCount: ComputedRef<number>;
12
+ totalSteps: ComputedRef<number>;
13
+ hasFailedSteps: ComputedRef<boolean>;
14
+ hasPendingInputSteps: ComputedRef<boolean>;
15
+ progressPercent: ComputedRef<number>;
16
+ expandedSteps: Ref<Set<string>>;
17
+ pipelineSchema: Ref<Record<string, Record<string, any>>>;
18
+ toggleStep: (taskId: string) => void;
19
+ fetchTasks: () => Promise<void>;
20
+ configureTask: (taskId: string, options: Record<string, any>) => Promise<void>;
21
+ triggerProcessing: (stepOptions?: Record<string, Record<string, any>>) => Promise<void>;
22
+ /** Retries a single failed task. Throws with the backend message on 400 (budget exhausted, wrong status, etc.). */
23
+ retryTask: (taskId: string) => Promise<void>;
24
+ /** Returns the upstream task currently blocking `taskId` (e.g. a `pending_input` step). Null if nothing upstream is blocking. */
25
+ getBlocker: (taskId: string) => ProcessingTask | null;
26
+ /** Tasks split by pipeline: the asset's own pipeline + one group per rendition. */
27
+ groupedTasks: ComputedRef<{
28
+ assetTasks: ProcessingTask[];
29
+ renditionGroups: {
30
+ renditionId: string;
31
+ tasks: ProcessingTask[];
32
+ }[];
33
+ }>;
34
+ /** First step currently waiting on user input. Drives the AWAITING_INPUT banner. */
35
+ firstPendingInputTask: ComputedRef<ProcessingTask | null>;
36
+ cleanup: () => void;
37
+ }
38
+ export declare function useMLProcessingPipeline(assetId: MaybeRef<string>, assetStatus?: MaybeRef<string | null | undefined>): UseMLProcessingPipelineReturn;
@@ -0,0 +1,258 @@
1
+ import {
2
+ computed,
3
+ onUnmounted,
4
+ ref,
5
+ shallowRef,
6
+ toValue,
7
+ watch
8
+ } from "vue";
9
+ import { $fetch } from "ofetch";
10
+ import { useMLProcessingStatus } from "./useMLProcessingStatus.js";
11
+ import { RENDITION_PIPELINE, useMLProcessingStrategies } from "./useMLProcessingStrategies.js";
12
+ import { useElapsedTimer } from "./useElapsedTimer.js";
13
+ export function useMLProcessingPipeline(assetId, assetStatus) {
14
+ const tasks = ref([]);
15
+ const loading = ref(false);
16
+ const stream = shallowRef(null);
17
+ const expandedSteps = ref(/* @__PURE__ */ new Set());
18
+ const pipelineSchema = ref({});
19
+ const { track, closeAll } = useMLProcessingStatus();
20
+ const { getPipeline } = useMLProcessingStrategies();
21
+ async function fetchTasks() {
22
+ const id = toValue(assetId);
23
+ if (!id) return;
24
+ loading.value = true;
25
+ try {
26
+ const result = await $fetch(
27
+ `/api/media-library/manage/processing/assets/${encodeURIComponent(id)}/tasks`
28
+ );
29
+ const raw = Array.isArray(result) ? result : result.items ?? [];
30
+ tasks.value = sortByPipeline(raw);
31
+ if (!Array.isArray(result) && result.pipeline_steps_schema) {
32
+ pipelineSchema.value = result.pipeline_steps_schema;
33
+ }
34
+ } catch {
35
+ tasks.value = [];
36
+ } finally {
37
+ loading.value = false;
38
+ }
39
+ }
40
+ function sortByPipeline(raw, explicitPipeline) {
41
+ if (raw.length < 2) return raw;
42
+ let pipeline = explicitPipeline ?? [];
43
+ if (!pipeline.length) {
44
+ const taskTypes = raw.map((t) => t.task_type);
45
+ const allKinds = [
46
+ "image",
47
+ "image_batch",
48
+ "video",
49
+ "audio",
50
+ "document",
51
+ "archive",
52
+ "font",
53
+ "model_3d",
54
+ "web_app",
55
+ "other"
56
+ ];
57
+ let bestPipeline = [];
58
+ for (const kind of allKinds) {
59
+ const candidate = getPipeline(kind);
60
+ if (taskTypes.every((t) => candidate.includes(t)) && candidate.length >= bestPipeline.length) {
61
+ bestPipeline = candidate;
62
+ }
63
+ }
64
+ pipeline = bestPipeline;
65
+ }
66
+ if (!pipeline.length) return raw;
67
+ return [...raw].sort((a, b) => {
68
+ const ai = pipeline.indexOf(a.task_type);
69
+ const bi = pipeline.indexOf(b.task_type);
70
+ return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
71
+ });
72
+ }
73
+ async function configureTask(taskId, options) {
74
+ await $fetch(
75
+ `/api/media-library/manage/processing/tasks/${encodeURIComponent(taskId)}/configure`,
76
+ { method: "POST", body: { options } }
77
+ );
78
+ await fetchTasks();
79
+ openStream();
80
+ }
81
+ async function retryTask(taskId) {
82
+ await $fetch(
83
+ `/api/media-library/manage/processing/tasks/${encodeURIComponent(taskId)}/retry`,
84
+ { method: "POST" }
85
+ );
86
+ await fetchTasks();
87
+ openStream();
88
+ }
89
+ async function triggerProcessing(stepOptions) {
90
+ const id = toValue(assetId);
91
+ if (!id) return;
92
+ await $fetch(
93
+ `/api/media-library/manage/processing/assets/${encodeURIComponent(id)}/process`,
94
+ { method: "POST", body: stepOptions ? { stepOptions } : {} }
95
+ );
96
+ await fetchTasks();
97
+ openStream();
98
+ }
99
+ function openStream() {
100
+ const id = toValue(assetId);
101
+ if (!id) return;
102
+ if (stream.value) stream.value.close();
103
+ stream.value = track(id);
104
+ }
105
+ function closeStream() {
106
+ if (stream.value) {
107
+ stream.value.close();
108
+ stream.value = null;
109
+ }
110
+ }
111
+ const taskEventMap = computed(() => {
112
+ return stream.value?.taskEvents.value ?? /* @__PURE__ */ new Map();
113
+ });
114
+ const completedCount = computed(
115
+ () => tasks.value.filter((t) => effectiveStatus(t) === "succeeded").length
116
+ );
117
+ const totalSteps = computed(() => tasks.value.length);
118
+ const hasFailedSteps = computed(
119
+ () => tasks.value.some((t) => effectiveStatus(t) === "failed")
120
+ );
121
+ const hasPendingInputSteps = computed(
122
+ () => tasks.value.some((t) => effectiveStatus(t) === "pending_input")
123
+ );
124
+ const pipelineStatus = computed(() => {
125
+ const asset = toValue(assetStatus)?.toLowerCase();
126
+ if (asset === "ready") return "succeeded";
127
+ if (asset === "failed") return "failed";
128
+ if (asset === "awaiting_input") return "awaiting_input";
129
+ if (!tasks.value.length) return asset === "processing" ? "running" : "queued";
130
+ if (hasFailedSteps.value) return "failed";
131
+ if (completedCount.value === totalSteps.value) return "succeeded";
132
+ const hasRunning = tasks.value.some((t) => {
133
+ const sse = taskEventMap.value.get(t.id);
134
+ const status = (sse?.status ?? t.status)?.toLowerCase();
135
+ return status === "running";
136
+ });
137
+ if (hasRunning) return "running";
138
+ if (hasPendingInputSteps.value) return "awaiting_input";
139
+ if (completedCount.value > 0) return "running";
140
+ return "queued";
141
+ });
142
+ const progressPercent = computed(() => {
143
+ if (!totalSteps.value) return 0;
144
+ return Math.round(completedCount.value / totalSteps.value * 100);
145
+ });
146
+ const pipelineStartedAt = computed(() => {
147
+ const first = tasks.value.find((t) => t.started_at);
148
+ return first?.started_at ?? null;
149
+ });
150
+ const pipelineIsRunning = computed(
151
+ () => pipelineStatus.value === "running"
152
+ );
153
+ const { elapsed: pipelineElapsed } = useElapsedTimer(pipelineStartedAt, pipelineIsRunning);
154
+ function effectiveStatus(t) {
155
+ const sse = taskEventMap.value.get(t.id);
156
+ return (sse?.status ?? t.status ?? "").toLowerCase();
157
+ }
158
+ const groupedTasks = computed(() => {
159
+ const assetTasks = [];
160
+ const byRendition = /* @__PURE__ */ new Map();
161
+ for (const t of tasks.value) {
162
+ const rid = t.rendition_id ?? t.renditionId ?? null;
163
+ if (!rid) {
164
+ assetTasks.push(t);
165
+ } else {
166
+ const list = byRendition.get(rid) ?? [];
167
+ list.push(t);
168
+ byRendition.set(rid, list);
169
+ }
170
+ }
171
+ const renditionGroups = Array.from(byRendition.entries()).map(([renditionId, list]) => ({
172
+ renditionId,
173
+ tasks: sortByPipeline(list, RENDITION_PIPELINE)
174
+ }));
175
+ return { assetTasks, renditionGroups };
176
+ });
177
+ const firstPendingInputTask = computed(
178
+ () => tasks.value.find((t) => effectiveStatus(t) === "pending_input") ?? null
179
+ );
180
+ function getBlocker(taskId) {
181
+ const target = tasks.value.find((t) => t.id === taskId);
182
+ if (!target) return null;
183
+ const targetRid = target.rendition_id ?? target.renditionId ?? null;
184
+ const groups = groupedTasks.value;
185
+ const group = targetRid ? groups.renditionGroups.find((g) => g.renditionId === targetRid)?.tasks ?? [] : groups.assetTasks;
186
+ const idx = group.findIndex((t) => t.id === taskId);
187
+ if (idx <= 0) return null;
188
+ for (let i = idx - 1; i >= 0; i--) {
189
+ const status = effectiveStatus(group[i]);
190
+ if (status === "succeeded" || status === "" && group[i].metadata_payload?.skipped) continue;
191
+ return group[i];
192
+ }
193
+ return null;
194
+ }
195
+ function toggleStep(taskId) {
196
+ const next = new Set(expandedSteps.value);
197
+ if (next.has(taskId)) next.delete(taskId);
198
+ else next.add(taskId);
199
+ expandedSteps.value = next;
200
+ }
201
+ watch(tasks, (list) => {
202
+ const next = new Set(expandedSteps.value);
203
+ for (const t of list) {
204
+ if (effectiveStatus(t) === "failed") next.add(t.id);
205
+ }
206
+ expandedSteps.value = next;
207
+ });
208
+ watch(() => toValue(assetId), (id) => {
209
+ tasks.value = [];
210
+ expandedSteps.value = /* @__PURE__ */ new Set();
211
+ closeStream();
212
+ if (id) {
213
+ fetchTasks();
214
+ }
215
+ }, { immediate: true });
216
+ let refetchTimer = null;
217
+ watch(taskEventMap, () => {
218
+ if (refetchTimer) clearTimeout(refetchTimer);
219
+ refetchTimer = setTimeout(() => {
220
+ fetchTasks();
221
+ }, 500);
222
+ }, { deep: true });
223
+ watch(pipelineStatus, (status) => {
224
+ if (status === "running" || status === "queued") {
225
+ if (!stream.value) openStream();
226
+ }
227
+ }, { immediate: true });
228
+ function cleanup() {
229
+ closeStream();
230
+ closeAll();
231
+ if (refetchTimer) clearTimeout(refetchTimer);
232
+ }
233
+ onUnmounted(cleanup);
234
+ return {
235
+ tasks,
236
+ loading,
237
+ stream,
238
+ taskEventMap,
239
+ pipelineStatus,
240
+ pipelineElapsed,
241
+ completedCount,
242
+ totalSteps,
243
+ hasFailedSteps,
244
+ hasPendingInputSteps,
245
+ progressPercent,
246
+ expandedSteps,
247
+ pipelineSchema,
248
+ toggleStep,
249
+ fetchTasks,
250
+ configureTask,
251
+ triggerProcessing,
252
+ retryTask,
253
+ getBlocker,
254
+ groupedTasks,
255
+ firstPendingInputTask,
256
+ cleanup
257
+ };
258
+ }
@@ -0,0 +1,34 @@
1
+ import { type Ref } from 'vue';
2
+ export interface ProcessingEvent {
3
+ assetId: string;
4
+ taskId?: string;
5
+ strategy?: string;
6
+ status: string;
7
+ step?: number | null;
8
+ totalSteps?: number | null;
9
+ error?: string | null;
10
+ metadata?: Record<string, any> | null;
11
+ timestamp?: string;
12
+ /** Pipeline-level fields */
13
+ completedSteps?: string[];
14
+ }
15
+ export interface ProcessingStream {
16
+ status: Ref<ProcessingEvent | null>;
17
+ isProcessing: Ref<boolean>;
18
+ isComplete: Ref<boolean>;
19
+ isFailed: Ref<boolean>;
20
+ error: Ref<boolean>;
21
+ progress: Ref<number>;
22
+ /** Per-task events from SSE for instant visual feedback */
23
+ taskEvents: Ref<Map<string, ProcessingEvent>>;
24
+ close: () => void;
25
+ }
26
+ /**
27
+ * Single tenant-wide SSE connection that multiplexes events to per-asset streams.
28
+ *
29
+ * Tracks both aggregate pipeline progress and per-task state for instant UI updates.
30
+ */
31
+ export declare function useMLProcessingStatus(): {
32
+ track: (assetId: string) => ProcessingStream;
33
+ closeAll: () => void;
34
+ };
@@ -0,0 +1,100 @@
1
+ import { ref } from "vue";
2
+ export function useMLProcessingStatus() {
3
+ let source = null;
4
+ const streams = /* @__PURE__ */ new Map();
5
+ function ensureConnection() {
6
+ if (source && source.readyState !== EventSource.CLOSED) return;
7
+ if (typeof EventSource === "undefined") return;
8
+ source = new EventSource("/api/media-library/manage/processing/stream");
9
+ source.addEventListener("processing_status", (e) => {
10
+ const data = JSON.parse(e.data);
11
+ const assetId = data.assetId;
12
+ if (!assetId) return;
13
+ const s = streams.get(assetId);
14
+ if (!s) return;
15
+ if (data.status === "pipeline_complete") {
16
+ s.isProcessing.value = false;
17
+ s.isComplete.value = true;
18
+ s.isFailed.value = false;
19
+ s.progress.value = 100;
20
+ s.status.value = data;
21
+ return;
22
+ }
23
+ if (data.status === "pipeline_failed") {
24
+ s.isProcessing.value = false;
25
+ s.isComplete.value = false;
26
+ s.isFailed.value = true;
27
+ s.status.value = data;
28
+ return;
29
+ }
30
+ s.error.value = false;
31
+ s.isProcessing.value = true;
32
+ s.status.value = data;
33
+ if (data.taskId) {
34
+ const updated = new Map(s.taskEvents.value);
35
+ updated.set(data.taskId, data);
36
+ s.taskEvents.value = updated;
37
+ }
38
+ if (data.totalSteps && data.totalSteps > 0 && data.step != null) {
39
+ s.progress.value = Math.round(data.step / data.totalSteps * 100);
40
+ }
41
+ });
42
+ source.onerror = () => {
43
+ if (source?.readyState === EventSource.CLOSED) {
44
+ for (const [, s] of streams) {
45
+ if (s.isProcessing.value) {
46
+ s.isProcessing.value = false;
47
+ s.error.value = true;
48
+ }
49
+ }
50
+ source = null;
51
+ }
52
+ };
53
+ }
54
+ function track(assetId) {
55
+ if (streams.has(assetId)) {
56
+ const existing = streams.get(assetId);
57
+ existing.isProcessing.value = true;
58
+ existing.isComplete.value = false;
59
+ existing.isFailed.value = false;
60
+ existing.error.value = false;
61
+ ensureConnection();
62
+ return { ...existing, close: () => untrack(assetId) };
63
+ }
64
+ const status = ref(null);
65
+ const isProcessing = ref(true);
66
+ const isComplete = ref(false);
67
+ const isFailed = ref(false);
68
+ const error = ref(false);
69
+ const progress = ref(0);
70
+ const taskEvents = ref(/* @__PURE__ */ new Map());
71
+ if (typeof EventSource === "undefined") {
72
+ isProcessing.value = false;
73
+ error.value = true;
74
+ return { status, isProcessing, isComplete, isFailed, error, progress, taskEvents, close: () => {
75
+ } };
76
+ }
77
+ streams.set(assetId, { status, isProcessing, isComplete, isFailed, error, progress, taskEvents });
78
+ ensureConnection();
79
+ return { status, isProcessing, isComplete, isFailed, error, progress, taskEvents, close: () => untrack(assetId) };
80
+ }
81
+ function untrack(assetId) {
82
+ const s = streams.get(assetId);
83
+ if (s) {
84
+ s.isProcessing.value = false;
85
+ streams.delete(assetId);
86
+ }
87
+ if (streams.size === 0 && source) {
88
+ source.close();
89
+ source = null;
90
+ }
91
+ }
92
+ function closeAll() {
93
+ streams.clear();
94
+ if (source) {
95
+ source.close();
96
+ source = null;
97
+ }
98
+ }
99
+ return { track, closeAll };
100
+ }
@@ -0,0 +1,32 @@
1
+ import { type ComputedRef, type MaybeRef, type Ref } from 'vue';
2
+ import type { ProcessingTask, PipelineOptionSchema } from '../types/asset.js';
3
+ export type StepStatus = 'pending' | 'configuring' | 'queued' | 'running' | 'succeeded' | 'failed' | 'skipped';
4
+ export interface StepState {
5
+ name: string;
6
+ displayName: string;
7
+ index: number;
8
+ status: StepStatus;
9
+ task: ProcessingTask | null;
10
+ optionsSchema: Record<string, PipelineOptionSchema>;
11
+ userOptions: Record<string, any>;
12
+ isLocked: boolean;
13
+ isActive: boolean;
14
+ hasOptions: boolean;
15
+ error: string | null;
16
+ }
17
+ export interface UseMLProcessingStepperReturn {
18
+ steps: Ref<StepState[]>;
19
+ loading: Ref<boolean>;
20
+ currentStepIndex: Ref<number>;
21
+ currentStep: ComputedRef<StepState | null>;
22
+ allComplete: ComputedRef<boolean>;
23
+ hasAnyFailed: ComputedRef<boolean>;
24
+ progressPercent: ComputedRef<number>;
25
+ updateStepOptions: (stepName: string, options: Record<string, any>) => void;
26
+ runStep: (stepName: string) => Promise<void>;
27
+ skipStep: (stepName: string) => void;
28
+ retryStep: (stepName: string) => Promise<void>;
29
+ runRemaining: () => Promise<void>;
30
+ cleanup: () => void;
31
+ }
32
+ export declare function useMLProcessingStepper(assetId: MaybeRef<string>): UseMLProcessingStepperReturn;