nuxt-media-library 0.0.44 → 0.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +4 -1
- package/dist/runtime/components/MLAssetManager/Processing.vue +3 -1
- package/dist/runtime/components/MLMedia/Dropzone.d.vue.ts +60 -0
- package/dist/runtime/components/MLMedia/Dropzone.vue +137 -0
- package/dist/runtime/components/MLMedia/Dropzone.vue.d.ts +60 -0
- package/dist/runtime/components/MLMedia/Editor.d.vue.ts +23 -0
- package/dist/runtime/components/MLMedia/Editor.vue +34 -0
- package/dist/runtime/components/MLMedia/Editor.vue.d.ts +23 -0
- package/dist/runtime/components/MLMedia/EditorPanel.d.vue.ts +38 -0
- package/dist/runtime/components/MLMedia/EditorPanel.vue +220 -0
- package/dist/runtime/components/MLMedia/EditorPanel.vue.d.ts +38 -0
- package/dist/runtime/components/MLMedia/Field.d.vue.ts +65 -0
- package/dist/runtime/components/MLMedia/Field.vue +282 -0
- package/dist/runtime/components/MLMedia/Field.vue.d.ts +65 -0
- package/dist/runtime/components/MLMedia/FileFacts.d.vue.ts +19 -0
- package/dist/runtime/components/MLMedia/FileFacts.vue +38 -0
- package/dist/runtime/components/MLMedia/FileFacts.vue.d.ts +19 -0
- package/dist/runtime/components/MLMedia/FilePicker.d.vue.ts +23 -0
- package/dist/runtime/components/MLMedia/FilePicker.vue +33 -0
- package/dist/runtime/components/MLMedia/FilePicker.vue.d.ts +23 -0
- package/dist/runtime/components/MLMedia/FilePickerPanel.d.vue.ts +33 -0
- package/dist/runtime/components/MLMedia/FilePickerPanel.vue +300 -0
- package/dist/runtime/components/MLMedia/FilePickerPanel.vue.d.ts +33 -0
- package/dist/runtime/components/MLMedia/FilterPill.d.vue.ts +30 -0
- package/dist/runtime/components/MLMedia/FilterPill.vue +84 -0
- package/dist/runtime/components/MLMedia/FilterPill.vue.d.ts +30 -0
- package/dist/runtime/components/MLMedia/Gallery.d.vue.ts +78 -0
- package/dist/runtime/components/MLMedia/Gallery.vue +256 -0
- package/dist/runtime/components/MLMedia/Gallery.vue.d.ts +78 -0
- package/dist/runtime/components/MLMedia/GallerySection.d.vue.ts +102 -0
- package/dist/runtime/components/MLMedia/GallerySection.vue +422 -0
- package/dist/runtime/components/MLMedia/GallerySection.vue.d.ts +102 -0
- package/dist/runtime/components/MLMedia/KindBadge.d.vue.ts +17 -0
- package/dist/runtime/components/MLMedia/KindBadge.vue +22 -0
- package/dist/runtime/components/MLMedia/KindBadge.vue.d.ts +17 -0
- package/dist/runtime/components/MLMedia/MediaTile.d.vue.ts +63 -0
- package/dist/runtime/components/MLMedia/MediaTile.vue +117 -0
- package/dist/runtime/components/MLMedia/MediaTile.vue.d.ts +63 -0
- package/dist/runtime/components/MLMedia/PickerCard.d.vue.ts +42 -0
- package/dist/runtime/components/MLMedia/PickerCard.vue +79 -0
- package/dist/runtime/components/MLMedia/PickerCard.vue.d.ts +42 -0
- package/dist/runtime/components/MLMedia/PickerRow.d.vue.ts +31 -0
- package/dist/runtime/components/MLMedia/PickerRow.vue +65 -0
- package/dist/runtime/components/MLMedia/PickerRow.vue.d.ts +31 -0
- package/dist/runtime/components/MLMedia/PreviewEye.d.vue.ts +16 -0
- package/dist/runtime/components/MLMedia/PreviewEye.vue +21 -0
- package/dist/runtime/components/MLMedia/PreviewEye.vue.d.ts +16 -0
- package/dist/runtime/components/MLMedia/ProgressVeil.d.vue.ts +25 -0
- package/dist/runtime/components/MLMedia/ProgressVeil.vue +98 -0
- package/dist/runtime/components/MLMedia/ProgressVeil.vue.d.ts +25 -0
- package/dist/runtime/components/MLMedia/StatusBadge.d.vue.ts +17 -0
- package/dist/runtime/components/MLMedia/StatusBadge.vue +31 -0
- package/dist/runtime/components/MLMedia/StatusBadge.vue.d.ts +17 -0
- package/dist/runtime/components/MLMedia/Thumbnail.d.vue.ts +36 -0
- package/dist/runtime/components/MLMedia/Thumbnail.vue +84 -0
- package/dist/runtime/components/MLMedia/Thumbnail.vue.d.ts +36 -0
- package/dist/runtime/components/MLMedia/UsageList.d.vue.ts +26 -0
- package/dist/runtime/components/MLMedia/UsageList.vue +66 -0
- package/dist/runtime/components/MLMedia/UsageList.vue.d.ts +26 -0
- package/dist/runtime/components/MLProcessing/StepLayout.d.vue.ts +1 -1
- package/dist/runtime/components/MLProcessing/StepLayout.vue.d.ts +1 -1
- package/dist/runtime/components/MLUploader/Item.d.vue.ts +2 -2
- package/dist/runtime/components/MLUploader/Item.vue.d.ts +2 -2
- package/dist/runtime/composables/useMLAssetPreview.js +17 -1
- package/dist/runtime/composables/useMLFilePicker.d.ts +20 -0
- package/dist/runtime/composables/useMLFilePicker.js +17 -0
- package/dist/runtime/composables/useMLMediaAssets.d.ts +19 -0
- package/dist/runtime/composables/useMLMediaAssets.js +56 -0
- package/dist/runtime/composables/useMLMediaBrowse.d.ts +147 -0
- package/dist/runtime/composables/useMLMediaBrowse.js +72 -0
- package/dist/runtime/composables/useMLMediaEditor.d.ts +22 -0
- package/dist/runtime/composables/useMLMediaEditor.js +20 -0
- package/dist/runtime/composables/useMLMediaField.d.ts +29 -0
- package/dist/runtime/composables/useMLMediaField.js +77 -0
- package/dist/runtime/composables/useMLMediaGallery.d.ts +55 -0
- package/dist/runtime/composables/useMLMediaGallery.js +134 -0
- package/dist/runtime/composables/useMLMediaGrants.d.ts +23 -0
- package/dist/runtime/composables/useMLMediaGrants.js +22 -0
- package/dist/runtime/composables/useMLMediaUpload.d.ts +134 -0
- package/dist/runtime/composables/useMLMediaUpload.js +153 -0
- package/dist/runtime/composables/useMLServiceUpload.d.ts +36 -0
- package/dist/runtime/composables/useMLServiceUpload.js +32 -0
- package/dist/runtime/index.d.ts +11 -0
- package/dist/runtime/index.js +11 -0
- package/dist/runtime/server/create-ml-proxy.d.ts +16 -0
- package/dist/runtime/server/create-ml-proxy.js +138 -40
- package/dist/runtime/server/grants.d.ts +124 -0
- package/dist/runtime/server/grants.js +161 -0
- package/dist/runtime/server/index.d.ts +2 -0
- package/dist/runtime/server/index.js +7 -0
- package/dist/runtime/server/uploads-proxy.js +5 -2
- package/dist/runtime/types/asset.d.ts +3 -1
- package/dist/runtime/utils/assetDisplay.d.ts +8 -0
- package/dist/runtime/utils/assetDisplay.js +29 -0
- package/dist/runtime/utils/assetFormat.d.ts +30 -0
- package/dist/runtime/utils/assetFormat.js +69 -0
- package/dist/runtime/utils/assetThumbnail.d.ts +11 -0
- package/dist/runtime/utils/assetThumbnail.js +14 -0
- package/dist/runtime/utils/fileAccept.d.ts +26 -0
- package/dist/runtime/utils/fileAccept.js +104 -0
- package/dist/runtime/utils/galleryDrag.d.ts +2 -0
- package/dist/runtime/utils/galleryDrag.js +1 -0
- package/dist/runtime/utils/grants/listeners.d.ts +2 -0
- package/dist/runtime/utils/grants/listeners.js +8 -0
- package/dist/runtime/utils/grants/store.d.ts +26 -0
- package/dist/runtime/utils/grants/store.js +37 -0
- package/dist/runtime/utils/uploader/serviceUpload.d.ts +84 -0
- package/dist/runtime/utils/uploader/serviceUpload.js +109 -0
- package/package.json +5 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { ChunkUploaderConfig } from '../types/uploader.js';
|
|
2
|
+
export type MediaUploadPhase = 'uploading' | 'processing' | 'ready' | 'failed' | 'canceled';
|
|
3
|
+
/** What a policy-less upload session takes. See docs/resource-policy/retirement.md. */
|
|
4
|
+
export interface MediaUploadOptions {
|
|
5
|
+
/** Defaults to the kind read from the file. */
|
|
6
|
+
kind?: string;
|
|
7
|
+
visibility?: 'private' | 'public' | 'restricted';
|
|
8
|
+
processingConfig?: {
|
|
9
|
+
stepOptions?: Record<string, Record<string, unknown>> | null;
|
|
10
|
+
useFilenameAsAssetId?: boolean;
|
|
11
|
+
enableTranscription?: boolean;
|
|
12
|
+
};
|
|
13
|
+
/** Stored on the session as a label only. */
|
|
14
|
+
collectionName?: string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Name of one of the app's upload profiles. Sent with the session request;
|
|
17
|
+
* the app's `authorize` hook looks it up and the grant's profile then
|
|
18
|
+
* decides kind, visibility and processing. The media library ignores it.
|
|
19
|
+
*/
|
|
20
|
+
profile?: string | null;
|
|
21
|
+
/** Overrides the uploader config the module provides. */
|
|
22
|
+
chunkUploader?: ChunkUploaderConfig;
|
|
23
|
+
}
|
|
24
|
+
export interface MediaUpload {
|
|
25
|
+
readonly key: string;
|
|
26
|
+
readonly file: File;
|
|
27
|
+
readonly kind: string;
|
|
28
|
+
/** Object URL for images and videos, so the tile shows the file before any rendition exists. */
|
|
29
|
+
readonly localPreview: string | null;
|
|
30
|
+
phase: MediaUploadPhase;
|
|
31
|
+
/** 0–100, or null while a step reports no progress. */
|
|
32
|
+
progress: number | null;
|
|
33
|
+
assetId: string | null;
|
|
34
|
+
error: string | null;
|
|
35
|
+
cancel: () => void;
|
|
36
|
+
retry: () => void;
|
|
37
|
+
/** Resolves with the asset ID once the asset exists (before processing finishes), or null. */
|
|
38
|
+
readonly created: Promise<string | null>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Uploads files one session each and follows them through processing.
|
|
42
|
+
* Every upload gets its own ChunkUploader so cancelling one never aborts another.
|
|
43
|
+
*/
|
|
44
|
+
export declare function useMLMediaUpload(defaults?: MediaUploadOptions): {
|
|
45
|
+
uploads: import("vue").Ref<{
|
|
46
|
+
readonly key: string;
|
|
47
|
+
readonly file: {
|
|
48
|
+
readonly lastModified: number;
|
|
49
|
+
readonly name: string;
|
|
50
|
+
readonly webkitRelativePath: string;
|
|
51
|
+
readonly size: number;
|
|
52
|
+
readonly type: string;
|
|
53
|
+
arrayBuffer: {
|
|
54
|
+
(): Promise<ArrayBuffer>;
|
|
55
|
+
(): Promise<ArrayBuffer>;
|
|
56
|
+
};
|
|
57
|
+
bytes: {
|
|
58
|
+
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
59
|
+
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
60
|
+
};
|
|
61
|
+
slice: {
|
|
62
|
+
(start?: number, end?: number, contentType?: string): Blob;
|
|
63
|
+
(start?: number, end?: number, contentType?: string): Blob;
|
|
64
|
+
};
|
|
65
|
+
stream: {
|
|
66
|
+
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
67
|
+
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
68
|
+
};
|
|
69
|
+
text: {
|
|
70
|
+
(): Promise<string>;
|
|
71
|
+
(): Promise<string>;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
readonly kind: string;
|
|
75
|
+
readonly localPreview: string | null;
|
|
76
|
+
phase: MediaUploadPhase;
|
|
77
|
+
progress: number | null;
|
|
78
|
+
assetId: string | null;
|
|
79
|
+
error: string | null;
|
|
80
|
+
cancel: () => void;
|
|
81
|
+
retry: () => void;
|
|
82
|
+
readonly created: {
|
|
83
|
+
then: <TResult1 = string | null, TResult2 = never>(onfulfilled?: ((value: string | null) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>;
|
|
84
|
+
catch: <TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined) => Promise<string | TResult | null>;
|
|
85
|
+
finally: (onfinally?: (() => void) | null | undefined) => Promise<string | null>;
|
|
86
|
+
readonly [Symbol.toStringTag]: string;
|
|
87
|
+
};
|
|
88
|
+
}[], MediaUpload[] | {
|
|
89
|
+
readonly key: string;
|
|
90
|
+
readonly file: {
|
|
91
|
+
readonly lastModified: number;
|
|
92
|
+
readonly name: string;
|
|
93
|
+
readonly webkitRelativePath: string;
|
|
94
|
+
readonly size: number;
|
|
95
|
+
readonly type: string;
|
|
96
|
+
arrayBuffer: {
|
|
97
|
+
(): Promise<ArrayBuffer>;
|
|
98
|
+
(): Promise<ArrayBuffer>;
|
|
99
|
+
};
|
|
100
|
+
bytes: {
|
|
101
|
+
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
102
|
+
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
103
|
+
};
|
|
104
|
+
slice: {
|
|
105
|
+
(start?: number, end?: number, contentType?: string): Blob;
|
|
106
|
+
(start?: number, end?: number, contentType?: string): Blob;
|
|
107
|
+
};
|
|
108
|
+
stream: {
|
|
109
|
+
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
110
|
+
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
111
|
+
};
|
|
112
|
+
text: {
|
|
113
|
+
(): Promise<string>;
|
|
114
|
+
(): Promise<string>;
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
readonly kind: string;
|
|
118
|
+
readonly localPreview: string | null;
|
|
119
|
+
phase: MediaUploadPhase;
|
|
120
|
+
progress: number | null;
|
|
121
|
+
assetId: string | null;
|
|
122
|
+
error: string | null;
|
|
123
|
+
cancel: () => void;
|
|
124
|
+
retry: () => void;
|
|
125
|
+
readonly created: {
|
|
126
|
+
then: <TResult1 = string | null, TResult2 = never>(onfulfilled?: ((value: string | null) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>;
|
|
127
|
+
catch: <TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined) => Promise<string | TResult | null>;
|
|
128
|
+
finally: (onfinally?: (() => void) | null | undefined) => Promise<string | null>;
|
|
129
|
+
readonly [Symbol.toStringTag]: string;
|
|
130
|
+
};
|
|
131
|
+
}[]>;
|
|
132
|
+
start: (file: File, overrides?: MediaUploadOptions) => MediaUpload;
|
|
133
|
+
remove: (upload: MediaUpload) => void;
|
|
134
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { onBeforeUnmount, reactive, ref, watch } from "vue";
|
|
2
|
+
import { $fetch } from "ofetch";
|
|
3
|
+
import { ChunkUploader } from "../utils/uploader/uploader.js";
|
|
4
|
+
import { kindForFile } from "../utils/fileAccept.js";
|
|
5
|
+
import { useMLChunkUploader } from "./useMLChunkUploader.js";
|
|
6
|
+
import { useMLProcessingStatus } from "./useMLProcessingStatus.js";
|
|
7
|
+
let seq = 0;
|
|
8
|
+
export function useMLMediaUpload(defaults = {}) {
|
|
9
|
+
const base = useMLChunkUploader(defaults.chunkUploader).config;
|
|
10
|
+
const cfg = base.proxyUploads ? { ...base, baseURL: "" } : base;
|
|
11
|
+
const { track } = useMLProcessingStatus();
|
|
12
|
+
const uploads = ref([]);
|
|
13
|
+
const cleanups = /* @__PURE__ */ new Map();
|
|
14
|
+
function start(file, overrides = {}) {
|
|
15
|
+
const opts = { ...defaults, ...overrides };
|
|
16
|
+
const kind = opts.kind ?? kindForFile(file);
|
|
17
|
+
const isVisual = file.type.startsWith("image/") || file.type.startsWith("video/");
|
|
18
|
+
const localPreview = isVisual && typeof URL !== "undefined" ? URL.createObjectURL(file) : null;
|
|
19
|
+
let uploader = null;
|
|
20
|
+
let stream = null;
|
|
21
|
+
let stopWatch = null;
|
|
22
|
+
let resolveCreated;
|
|
23
|
+
const upload = reactive({
|
|
24
|
+
key: `upload-${++seq}`,
|
|
25
|
+
file,
|
|
26
|
+
kind,
|
|
27
|
+
localPreview,
|
|
28
|
+
phase: "uploading",
|
|
29
|
+
progress: 0,
|
|
30
|
+
assetId: null,
|
|
31
|
+
error: null,
|
|
32
|
+
cancel,
|
|
33
|
+
retry,
|
|
34
|
+
created: new Promise((r) => {
|
|
35
|
+
resolveCreated = r;
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
function finish(phase, err = null) {
|
|
39
|
+
upload.phase = phase;
|
|
40
|
+
upload.error = err;
|
|
41
|
+
if (phase === "ready") upload.progress = 100;
|
|
42
|
+
stopWatch?.();
|
|
43
|
+
stopWatch = null;
|
|
44
|
+
stream?.close();
|
|
45
|
+
stream = null;
|
|
46
|
+
}
|
|
47
|
+
async function checkStatus() {
|
|
48
|
+
if (!upload.assetId) return null;
|
|
49
|
+
try {
|
|
50
|
+
const res = await $fetch(
|
|
51
|
+
"/api/media-library/manage/assets/resolve",
|
|
52
|
+
{ method: "POST", body: { assetIds: [upload.assetId] } }
|
|
53
|
+
);
|
|
54
|
+
return res.items?.[0]?.asset?.status?.toLowerCase() ?? null;
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function followProcessing(assetId) {
|
|
60
|
+
upload.phase = "processing";
|
|
61
|
+
upload.progress = null;
|
|
62
|
+
stream = track(assetId);
|
|
63
|
+
stopWatch = watch(
|
|
64
|
+
() => [stream?.progress.value, stream?.isComplete.value, stream?.isFailed.value, stream?.error.value],
|
|
65
|
+
async ([progress, complete, failed, dropped]) => {
|
|
66
|
+
if (upload.phase !== "processing") return;
|
|
67
|
+
if (complete) return finish("ready");
|
|
68
|
+
if (failed) return finish("failed", "Processing failed");
|
|
69
|
+
if (dropped) {
|
|
70
|
+
const status2 = await checkStatus();
|
|
71
|
+
if (status2 === "ready") finish("ready");
|
|
72
|
+
else if (status2 === "failed") finish("failed", "Processing failed");
|
|
73
|
+
else upload.progress = null;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
upload.progress = progress ? progress : null;
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
const status = await checkStatus();
|
|
80
|
+
if (upload.phase !== "processing") return;
|
|
81
|
+
if (status === "ready") finish("ready");
|
|
82
|
+
else if (status === "failed") finish("failed", "Processing failed");
|
|
83
|
+
}
|
|
84
|
+
async function run() {
|
|
85
|
+
uploader = new ChunkUploader(cfg);
|
|
86
|
+
upload.phase = "uploading";
|
|
87
|
+
upload.progress = 0;
|
|
88
|
+
upload.error = null;
|
|
89
|
+
const create = {
|
|
90
|
+
kind,
|
|
91
|
+
visibility: opts.visibility,
|
|
92
|
+
processing_config: opts.processingConfig,
|
|
93
|
+
collection_name: opts.collectionName ?? null,
|
|
94
|
+
...opts.profile ? { profile: opts.profile } : {}
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
const res = await uploader.uploadFile(file, {
|
|
98
|
+
create,
|
|
99
|
+
onProgress: (p) => {
|
|
100
|
+
upload.progress = p.percent;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
const ids = Array.isArray(res?.finalize?.asset_ids) ? res.finalize.asset_ids : res?.finalize?.asset_id ? [res.finalize.asset_id] : [];
|
|
104
|
+
const assetId = ids[0] ?? null;
|
|
105
|
+
if (!assetId) {
|
|
106
|
+
finish("failed", "The upload finished but no file was created");
|
|
107
|
+
resolveCreated(null);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
upload.assetId = assetId;
|
|
111
|
+
resolveCreated(assetId);
|
|
112
|
+
await followProcessing(assetId);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (upload.phase === "canceled") return;
|
|
115
|
+
finish("failed", err?.data?.message ?? err?.message ?? "Upload failed");
|
|
116
|
+
if (!upload.assetId) resolveCreated(null);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function cancel() {
|
|
120
|
+
if (upload.phase !== "uploading" && upload.phase !== "processing") return;
|
|
121
|
+
uploader?.abort();
|
|
122
|
+
finish("canceled");
|
|
123
|
+
if (!upload.assetId) resolveCreated(null);
|
|
124
|
+
}
|
|
125
|
+
function retry() {
|
|
126
|
+
if (upload.phase !== "failed") return;
|
|
127
|
+
if (upload.assetId) {
|
|
128
|
+
void followProcessing(upload.assetId);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
void run();
|
|
132
|
+
}
|
|
133
|
+
cleanups.set(upload.key, () => {
|
|
134
|
+
uploader?.abort();
|
|
135
|
+
stopWatch?.();
|
|
136
|
+
stream?.close();
|
|
137
|
+
if (localPreview) URL.revokeObjectURL(localPreview);
|
|
138
|
+
});
|
|
139
|
+
uploads.value = [...uploads.value, upload];
|
|
140
|
+
void run();
|
|
141
|
+
return upload;
|
|
142
|
+
}
|
|
143
|
+
function remove(upload) {
|
|
144
|
+
cleanups.get(upload.key)?.();
|
|
145
|
+
cleanups.delete(upload.key);
|
|
146
|
+
uploads.value = uploads.value.filter((u) => u.key !== upload.key);
|
|
147
|
+
}
|
|
148
|
+
onBeforeUnmount(() => {
|
|
149
|
+
cleanups.forEach((fn) => fn());
|
|
150
|
+
cleanups.clear();
|
|
151
|
+
});
|
|
152
|
+
return { uploads, start, remove };
|
|
153
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { UploadProgress } from '../types/uploader.js';
|
|
2
|
+
import type { ServiceUploadOptions, ServiceUploadResult } from '../utils/uploader/serviceUpload.js';
|
|
3
|
+
export type { ServiceUploadOptions, ServiceUploadResult } from '../utils/uploader/serviceUpload.js';
|
|
4
|
+
/**
|
|
5
|
+
* Upload a file through one of the app's backend services (py_client
|
|
6
|
+
* `create_upload_router`). The service starts the upload and hands out a
|
|
7
|
+
* grant for that session; the file goes straight to the media library.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const { upload, abort, uploading, progress, error } = useMLServiceUpload()
|
|
11
|
+
* const { sessionId, assetId } = await upload(file, {
|
|
12
|
+
* start: '/api/videos/uploads',
|
|
13
|
+
* body: { profile: 'lesson-video', context: { lessonId } },
|
|
14
|
+
* onProgress: p => console.log(p.percent),
|
|
15
|
+
* })
|
|
16
|
+
* await $fetch(`/api/videos/lessons/${lessonId}/video`, { method: 'PUT', body: { sessionId, assetId } })
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* One upload at a time per call of the composable; call it once per
|
|
20
|
+
* upload field. The media library must allow the site's origin (CORS).
|
|
21
|
+
*/
|
|
22
|
+
export declare function useMLServiceUpload(): {
|
|
23
|
+
upload: (file: File, options: ServiceUploadOptions) => Promise<ServiceUploadResult>;
|
|
24
|
+
abort: () => void;
|
|
25
|
+
uploading: import("vue").Ref<boolean, boolean>;
|
|
26
|
+
progress: import("vue").Ref<{
|
|
27
|
+
totalBytes: number;
|
|
28
|
+
uploadedBytes: number;
|
|
29
|
+
percent: number;
|
|
30
|
+
} | null, UploadProgress | {
|
|
31
|
+
totalBytes: number;
|
|
32
|
+
uploadedBytes: number;
|
|
33
|
+
percent: number;
|
|
34
|
+
} | null>;
|
|
35
|
+
error: import("vue").Ref<unknown, unknown>;
|
|
36
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
import { ChunkUploader } from "../utils/uploader/uploader.js";
|
|
3
|
+
import { createServiceUpload } from "../utils/uploader/serviceUpload.js";
|
|
4
|
+
export function useMLServiceUpload() {
|
|
5
|
+
const uploading = ref(false);
|
|
6
|
+
const progress = ref(null);
|
|
7
|
+
const error = ref(null);
|
|
8
|
+
const runner = createServiceUpload({
|
|
9
|
+
fetch: (url, opts) => $fetch(url, opts),
|
|
10
|
+
createUploader: (cfg) => new ChunkUploader(cfg)
|
|
11
|
+
});
|
|
12
|
+
async function upload(file, options) {
|
|
13
|
+
uploading.value = true;
|
|
14
|
+
progress.value = null;
|
|
15
|
+
error.value = null;
|
|
16
|
+
try {
|
|
17
|
+
return await runner.upload(file, {
|
|
18
|
+
...options,
|
|
19
|
+
onProgress: (p) => {
|
|
20
|
+
progress.value = p;
|
|
21
|
+
options.onProgress?.(p);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
} catch (err) {
|
|
25
|
+
error.value = err;
|
|
26
|
+
throw err;
|
|
27
|
+
} finally {
|
|
28
|
+
uploading.value = false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { upload, abort: runner.abort, uploading, progress, error };
|
|
32
|
+
}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -6,8 +6,19 @@ export * from './composables/useMLAssetManager.js';
|
|
|
6
6
|
export * from './composables/useMLAssetLibrary.js';
|
|
7
7
|
export * from './composables/useMLAssetManagerPreview.js';
|
|
8
8
|
export * from './composables/useElapsedTimer.js';
|
|
9
|
+
export * from './composables/useMLMediaAssets.js';
|
|
10
|
+
export * from './composables/useMLMediaUpload.js';
|
|
11
|
+
export * from './composables/useMLMediaField.js';
|
|
12
|
+
export * from './composables/useMLMediaBrowse.js';
|
|
13
|
+
export * from './composables/useMLFilePicker.js';
|
|
14
|
+
export * from './composables/useMLMediaEditor.js';
|
|
15
|
+
export * from './composables/useMLMediaGallery.js';
|
|
9
16
|
export * from './composables/resourcePolicy/useMLResourcePolicy.js';
|
|
10
17
|
export * from './types/resourcePolicy.js';
|
|
11
18
|
export * from './types/uploader.js';
|
|
12
19
|
export * from './types/asset.js';
|
|
13
20
|
export * from './utils/uploader/uploader.js';
|
|
21
|
+
export * from './utils/assetDisplay.js';
|
|
22
|
+
export * from './utils/assetThumbnail.js';
|
|
23
|
+
export * from './utils/assetFormat.js';
|
|
24
|
+
export * from './utils/fileAccept.js';
|
package/dist/runtime/index.js
CHANGED
|
@@ -6,8 +6,19 @@ export * from "./composables/useMLAssetManager.js";
|
|
|
6
6
|
export * from "./composables/useMLAssetLibrary.js";
|
|
7
7
|
export * from "./composables/useMLAssetManagerPreview.js";
|
|
8
8
|
export * from "./composables/useElapsedTimer.js";
|
|
9
|
+
export * from "./composables/useMLMediaAssets.js";
|
|
10
|
+
export * from "./composables/useMLMediaUpload.js";
|
|
11
|
+
export * from "./composables/useMLMediaField.js";
|
|
12
|
+
export * from "./composables/useMLMediaBrowse.js";
|
|
13
|
+
export * from "./composables/useMLFilePicker.js";
|
|
14
|
+
export * from "./composables/useMLMediaEditor.js";
|
|
15
|
+
export * from "./composables/useMLMediaGallery.js";
|
|
9
16
|
export * from "./composables/resourcePolicy/useMLResourcePolicy.js";
|
|
10
17
|
export * from "./types/resourcePolicy.js";
|
|
11
18
|
export * from "./types/uploader.js";
|
|
12
19
|
export * from "./types/asset.js";
|
|
13
20
|
export * from "./utils/uploader/uploader.js";
|
|
21
|
+
export * from "./utils/assetDisplay.js";
|
|
22
|
+
export * from "./utils/assetThumbnail.js";
|
|
23
|
+
export * from "./utils/assetFormat.js";
|
|
24
|
+
export * from "./utils/fileAccept.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
|
+
import type { MLAuthorize, MLIssueGrant } from './grants.js';
|
|
2
3
|
export type MLProxyType = 'assets' | 'uploads' | 'delivery' | 'processing' | 'resource-policy';
|
|
3
4
|
export interface MLProxyOptions {
|
|
4
5
|
/**
|
|
@@ -8,6 +9,21 @@ export interface MLProxyOptions {
|
|
|
8
9
|
* from runtimeConfig.
|
|
9
10
|
*/
|
|
10
11
|
requestHeaders?: (event: H3Event, incoming: Record<string, string>) => Record<string, string> | Promise<Record<string, string>>;
|
|
12
|
+
/**
|
|
13
|
+
* Media grants (server docs/security/media-grants.md). When set, `/manage`
|
|
14
|
+
* requests no longer carry the browser's or the static credentials: for
|
|
15
|
+
* each one the proxy asks `authorize(event, need)` (your rules: the user
|
|
16
|
+
* from your session, the asset ids in `need`) and sends a short-lived
|
|
17
|
+
* grant scoped to exactly that. `false` → 403. Unknown `/manage` routes →
|
|
18
|
+
* 404. `/delivery` is not affected.
|
|
19
|
+
*/
|
|
20
|
+
authorize?: MLAuthorize<H3Event>;
|
|
21
|
+
/**
|
|
22
|
+
* How grants are issued. Default: `POST {baseURL}/manage/grants` with the
|
|
23
|
+
* private runtime config `mediaLibrary.serviceToken` (the Nuxt server is
|
|
24
|
+
* the app backend). Pass your own to ask your backend instead.
|
|
25
|
+
*/
|
|
26
|
+
issueGrant?: MLIssueGrant;
|
|
11
27
|
}
|
|
12
28
|
/**
|
|
13
29
|
* Create a Nitro event handler that replicates one of the media library's
|