nuxt-media-library 0.0.50 → 0.0.52
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 -4
- package/dist/module.d.mts +16 -0
- package/dist/module.d.ts +16 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +50 -5
- package/dist/runtime/components/MLMedia/Dropzone.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/Dropzone.vue.d.ts +1 -1
- package/dist/runtime/components/MLMedia/Field.d.vue.ts +17 -4
- package/dist/runtime/components/MLMedia/Field.vue +19 -2
- package/dist/runtime/components/MLMedia/Field.vue.d.ts +17 -4
- package/dist/runtime/components/MLMedia/FilePicker.d.vue.ts +2 -0
- package/dist/runtime/components/MLMedia/FilePicker.vue +3 -1
- package/dist/runtime/components/MLMedia/FilePicker.vue.d.ts +2 -0
- package/dist/runtime/components/MLMedia/FilePickerPanel.d.vue.ts +3 -0
- package/dist/runtime/components/MLMedia/FilePickerPanel.vue +6 -1
- package/dist/runtime/components/MLMedia/FilePickerPanel.vue.d.ts +3 -0
- package/dist/runtime/components/MLMedia/Gallery.d.vue.ts +15 -5
- package/dist/runtime/components/MLMedia/Gallery.vue +9 -3
- package/dist/runtime/components/MLMedia/Gallery.vue.d.ts +15 -5
- package/dist/runtime/components/MLMedia/GallerySection.d.vue.ts +11 -5
- package/dist/runtime/components/MLMedia/GallerySection.vue +21 -3
- package/dist/runtime/components/MLMedia/GallerySection.vue.d.ts +11 -5
- package/dist/runtime/components/MLMedia/MediaTile.d.vue.ts +3 -3
- package/dist/runtime/components/MLMedia/MediaTile.vue.d.ts +3 -3
- package/dist/runtime/components/MLMedia/PickerCard.d.vue.ts +2 -2
- package/dist/runtime/components/MLMedia/PickerCard.vue.d.ts +2 -2
- package/dist/runtime/components/MLMedia/PickerRow.d.vue.ts +2 -2
- package/dist/runtime/components/MLMedia/PickerRow.vue.d.ts +2 -2
- package/dist/runtime/components/MLMedia/ProgressVeil.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/ProgressVeil.vue.d.ts +1 -1
- package/dist/runtime/composables/useMLFilePicker.d.ts +2 -0
- package/dist/runtime/composables/useMLMediaField.d.ts +6 -2
- package/dist/runtime/composables/useMLMediaField.js +1 -1
- package/dist/runtime/composables/useMLMediaGallery.d.ts +5 -1
- package/dist/runtime/composables/useMLMediaGallery.js +2 -2
- package/dist/runtime/composables/useMLMediaUpload.d.ts +21 -0
- package/dist/runtime/composables/useMLMediaUpload.js +41 -12
- package/dist/runtime/utils/uploader/serviceBackends.d.ts +26 -0
- package/dist/runtime/utils/uploader/serviceBackends.js +18 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,8 +47,11 @@ export default defineNuxtConfig({
|
|
|
47
47
|
mediaLibrary: {
|
|
48
48
|
// Auto-register Vue components from src/runtime/components
|
|
49
49
|
components: true,
|
|
50
|
-
//
|
|
51
|
-
|
|
50
|
+
// Where the Nuxt server reaches the media library (with /v1). Server-side
|
|
51
|
+
// only, never sent to browsers; prefer NUXT_MEDIA_LIBRARY_BASE_URL.
|
|
52
|
+
baseURL: 'http://media-library.internal/v1',
|
|
53
|
+
// Only for direct browser uploads (proxyUploads: false): a public address.
|
|
54
|
+
// publicBaseURL: 'https://media.example.com/v1',
|
|
52
55
|
// Uploads always go through Nuxt server (/api/media-library/uploads/*)
|
|
53
56
|
routes: {
|
|
54
57
|
createSession: '/uploads/sessions',
|
|
@@ -60,11 +63,18 @@ export default defineNuxtConfig({
|
|
|
60
63
|
partSize: 8 * 1024 * 1024,
|
|
61
64
|
concurrency: 3,
|
|
62
65
|
checksum: { algorithm: 'SHA-256', perPart: true, final: true },
|
|
63
|
-
|
|
66
|
+
// Sent to browsers too, except credentials: Authorization, Cookie,
|
|
67
|
+
// Proxy-Authorization and X-Api-Key stay server-side (the proxies add them).
|
|
68
|
+
headers: { 'X-Tenant': 'acme' },
|
|
64
69
|
},
|
|
65
70
|
})
|
|
66
71
|
```
|
|
67
72
|
|
|
73
|
+
Nuxt puts the public runtime config in every page, so the module never copies
|
|
74
|
+
`baseURL` or credential headers there. Set the media library's internal
|
|
75
|
+
address and any service token only server-side
|
|
76
|
+
(`NUXT_MEDIA_LIBRARY_BASE_URL`, `NUXT_MEDIA_LIBRARY_SERVICE_TOKEN`).
|
|
77
|
+
|
|
68
78
|
### Nuxt usage
|
|
69
79
|
|
|
70
80
|
```vue
|
|
@@ -164,7 +174,7 @@ app's decision, not the service's:
|
|
|
164
174
|
// nuxt.config.ts
|
|
165
175
|
export default defineNuxtConfig({
|
|
166
176
|
mediaLibrary: {
|
|
167
|
-
|
|
177
|
+
// server side only: set NUXT_MEDIA_LIBRARY_BASE_URL (e.g. http://media-library.internal/v1)
|
|
168
178
|
serviceUploads: { proxy: true }, // parts go through /api/media-library/service-uploads
|
|
169
179
|
// or, direct to the media library (needs CORS for the site):
|
|
170
180
|
// serviceUploads: { baseURL: 'https://media.example.com/v1' },
|
|
@@ -198,6 +208,63 @@ await $fetch(`/api/videos/lessons/${lessonId}/video`, { method: 'PUT', body: { s
|
|
|
198
208
|
grant mode replaces the browser's grant with its own, and the service's
|
|
199
209
|
`claim_upload` rejects the session.
|
|
200
210
|
|
|
211
|
+
#### One layer per service, widgets by backend name
|
|
212
|
+
|
|
213
|
+
Each service can ship a Nuxt layer that registers its upload backend and
|
|
214
|
+
serves its own routes (start, refresh, attach, data) to its service. Nuxt
|
|
215
|
+
merges the layers, so every widget on the site can use any backend by name.
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
// itq-videos-layer/nuxt.config.ts
|
|
219
|
+
export default defineNuxtConfig({
|
|
220
|
+
modules: ['nuxt-media-library'],
|
|
221
|
+
mediaLibrary: {
|
|
222
|
+
serviceUploads: {
|
|
223
|
+
backends: { videos: { start: '/api/videos/uploads' } },
|
|
224
|
+
// refresh defaults to `${start}/:sessionId/grant`
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
// the site
|
|
232
|
+
export default defineNuxtConfig({
|
|
233
|
+
extends: ['itq-videos-layer', 'itq-courses-layer'],
|
|
234
|
+
modules: ['nuxt-media-library'],
|
|
235
|
+
mediaLibrary: { serviceUploads: { proxy: true } }, // where parts go, for every backend
|
|
236
|
+
})
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
```vue
|
|
240
|
+
<MLMediaGallery
|
|
241
|
+
v-model="lesson.mediaIds"
|
|
242
|
+
service="videos"
|
|
243
|
+
profile="lesson-video"
|
|
244
|
+
:service-body="{ context: { lessonId } }"
|
|
245
|
+
@uploaded="(assetId, role, sessionId) => attach(assetId, sessionId)"
|
|
246
|
+
/>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
- `service` on `MLMediaGallery`, `MLMediaField` and the file picker (and
|
|
250
|
+
`useMLMediaUpload`): a backend name, or `{ start }` for a one-off. Without
|
|
251
|
+
it, widgets upload through Nuxt's own route as before; an unknown name
|
|
252
|
+
fails that upload with a message naming the missing backend.
|
|
253
|
+
- The start request carries the file (`mimeType`, `sizeBytes`, `filename`),
|
|
254
|
+
`profile` (each role's, in a gallery with roles) and `service-body`. The
|
|
255
|
+
service's profile decides kind, visibility and processing, so
|
|
256
|
+
`visibility`, `processing-config` and `collection-name` are not used with
|
|
257
|
+
`service`.
|
|
258
|
+
- `uploaded` also gives the upload's `sessionId` (gallery:
|
|
259
|
+
`(assetId, role, sessionId)`, field: `(assetId, sessionId)`), including
|
|
260
|
+
files uploaded from the picker and chosen. Send both to the service's
|
|
261
|
+
attach endpoint: its `claim_upload` needs them.
|
|
262
|
+
- Processing progress uses `/api/media-library/manage/assets/resolve` and
|
|
263
|
+
the processing stream, so the app's proxy must allow `read` on a file the
|
|
264
|
+
user just uploaded.
|
|
265
|
+
- There is no default backend: each layer's widgets name theirs. The module
|
|
266
|
+
only stores the paths; each layer's own routes reach its service.
|
|
267
|
+
|
|
201
268
|
### Vue installation (non-Nuxt)
|
|
202
269
|
|
|
203
270
|
```bash
|
package/dist/module.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { ServiceUploadBackend } from '../dist/runtime/utils/uploader/serviceBackends.js';
|
|
2
3
|
export * from '../dist/runtime/types/resourcePolicy.js';
|
|
3
4
|
export * from '../dist/runtime/types/uploader.js';
|
|
4
5
|
export * from '../dist/runtime/types/preview.js';
|
|
@@ -14,7 +15,17 @@ interface ChunkUploaderRoutesConfig {
|
|
|
14
15
|
}
|
|
15
16
|
interface ModuleOptions {
|
|
16
17
|
components?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Where the Nuxt server reaches the media library, with `/v1` (often an
|
|
20
|
+
* internal address). Server-side only: never sent to browsers. Prefer
|
|
21
|
+
* NUXT_MEDIA_LIBRARY_BASE_URL at runtime.
|
|
22
|
+
*/
|
|
17
23
|
baseURL?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The media library address browsers use for direct uploads
|
|
26
|
+
* (`proxyUploads: false`), with `/v1`. Public: only set a public address.
|
|
27
|
+
*/
|
|
28
|
+
publicBaseURL?: string;
|
|
18
29
|
routes?: ChunkUploaderRoutesConfig;
|
|
19
30
|
partSize?: number;
|
|
20
31
|
concurrency?: number;
|
|
@@ -31,10 +42,15 @@ interface ModuleOptions {
|
|
|
31
42
|
* `baseURL` is where the browser sends those uploads (the media library's
|
|
32
43
|
* public API root with `/v1`, or a proxy route); with `proxy` it defaults
|
|
33
44
|
* to the pass-through route.
|
|
45
|
+
*
|
|
46
|
+
* `backends` names the services that start uploads, so widgets can use
|
|
47
|
+
* `service="videos"`. Each service's Nuxt layer registers its own; Nuxt
|
|
48
|
+
* merges them across layers. Paths only: each layer serves its routes.
|
|
34
49
|
*/
|
|
35
50
|
serviceUploads?: {
|
|
36
51
|
proxy?: boolean;
|
|
37
52
|
baseURL?: string;
|
|
53
|
+
backends?: Record<string, ServiceUploadBackend>;
|
|
38
54
|
};
|
|
39
55
|
}
|
|
40
56
|
declare const SERVICE_UPLOADS_ROUTE = "/api/media-library/service-uploads";
|
package/dist/module.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { ServiceUploadBackend } from '../dist/runtime/utils/uploader/serviceBackends.js';
|
|
2
3
|
export * from '../dist/runtime/types/resourcePolicy.js';
|
|
3
4
|
export * from '../dist/runtime/types/uploader.js';
|
|
4
5
|
export * from '../dist/runtime/types/preview.js';
|
|
@@ -14,7 +15,17 @@ interface ChunkUploaderRoutesConfig {
|
|
|
14
15
|
}
|
|
15
16
|
interface ModuleOptions {
|
|
16
17
|
components?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Where the Nuxt server reaches the media library, with `/v1` (often an
|
|
20
|
+
* internal address). Server-side only: never sent to browsers. Prefer
|
|
21
|
+
* NUXT_MEDIA_LIBRARY_BASE_URL at runtime.
|
|
22
|
+
*/
|
|
17
23
|
baseURL?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The media library address browsers use for direct uploads
|
|
26
|
+
* (`proxyUploads: false`), with `/v1`. Public: only set a public address.
|
|
27
|
+
*/
|
|
28
|
+
publicBaseURL?: string;
|
|
18
29
|
routes?: ChunkUploaderRoutesConfig;
|
|
19
30
|
partSize?: number;
|
|
20
31
|
concurrency?: number;
|
|
@@ -31,10 +42,15 @@ interface ModuleOptions {
|
|
|
31
42
|
* `baseURL` is where the browser sends those uploads (the media library's
|
|
32
43
|
* public API root with `/v1`, or a proxy route); with `proxy` it defaults
|
|
33
44
|
* to the pass-through route.
|
|
45
|
+
*
|
|
46
|
+
* `backends` names the services that start uploads, so widgets can use
|
|
47
|
+
* `service="videos"`. Each service's Nuxt layer registers its own; Nuxt
|
|
48
|
+
* merges them across layers. Paths only: each layer serves its routes.
|
|
34
49
|
*/
|
|
35
50
|
serviceUploads?: {
|
|
36
51
|
proxy?: boolean;
|
|
37
52
|
baseURL?: string;
|
|
53
|
+
backends?: Record<string, ServiceUploadBackend>;
|
|
38
54
|
};
|
|
39
55
|
}
|
|
40
56
|
declare const SERVICE_UPLOADS_ROUTE = "/api/media-library/service-uploads";
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,10 +1,42 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, addImportsDir, addComponentsDir, addServerHandler, addPlugin } from '@nuxt/kit';
|
|
1
|
+
import { defineNuxtModule, createResolver, addImportsDir, addComponentsDir, addServerHandler, useLogger, addPlugin } from '@nuxt/kit';
|
|
2
2
|
export * from '../dist/runtime/types/resourcePolicy.js';
|
|
3
3
|
export * from '../dist/runtime/types/uploader.js';
|
|
4
4
|
export * from '../dist/runtime/types/preview.js';
|
|
5
5
|
export * from '../dist/runtime/types/asset.js';
|
|
6
6
|
|
|
7
|
+
const CREDENTIAL_HEADERS = ["authorization", "proxy-authorization", "cookie", "x-api-key"];
|
|
8
|
+
function browserHeaders(headers) {
|
|
9
|
+
if (!headers || typeof headers !== "object") return { headers: {}, dropped: [] };
|
|
10
|
+
const kept = {};
|
|
11
|
+
const dropped = [];
|
|
12
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
13
|
+
if (CREDENTIAL_HEADERS.includes(name.toLowerCase())) dropped.push(name);
|
|
14
|
+
else kept[name] = value;
|
|
15
|
+
}
|
|
16
|
+
return { headers: kept, dropped };
|
|
17
|
+
}
|
|
18
|
+
function browserBaseURL(existingPublic, publicBaseURL) {
|
|
19
|
+
return existingPublic ?? publicBaseURL ?? "";
|
|
20
|
+
}
|
|
21
|
+
|
|
7
22
|
const SERVICE_UPLOADS_ROUTE = "/api/media-library/service-uploads";
|
|
23
|
+
function serviceBackends(backends, logger) {
|
|
24
|
+
const out = {};
|
|
25
|
+
for (const [name, backend] of Object.entries(backends ?? {})) {
|
|
26
|
+
if (!backend || typeof backend.start !== "string" || !backend.start) {
|
|
27
|
+
logger.warn(`mediaLibrary.serviceUploads.backends.${name} has no start path; ignored.`);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (backend.refresh && !backend.refresh.includes(":sessionId")) {
|
|
31
|
+
logger.warn(`mediaLibrary.serviceUploads.backends.${name}.refresh needs a :sessionId placeholder; using the default.`);
|
|
32
|
+
}
|
|
33
|
+
out[name] = {
|
|
34
|
+
start: backend.start,
|
|
35
|
+
...backend.refresh?.includes(":sessionId") ? { refresh: backend.refresh } : {}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
8
40
|
const module$1 = defineNuxtModule({
|
|
9
41
|
meta: {
|
|
10
42
|
name: "media-library",
|
|
@@ -38,7 +70,10 @@ const module$1 = defineNuxtModule({
|
|
|
38
70
|
const envTenantHeader = process.env.NUXT_MEDIA_LIBRARY_HEADERS_X_TENANT;
|
|
39
71
|
const envHeaders = envTenantHeader ? { "X-tenant": envTenantHeader } : {};
|
|
40
72
|
const existingHeaders = typeof existingPrivate.headers === "object" ? existingPrivate.headers : {};
|
|
41
|
-
const
|
|
73
|
+
const optionCredentials = Object.fromEntries(
|
|
74
|
+
browserHeaders(options.headers).dropped.map((name) => [name, options.headers[name]])
|
|
75
|
+
);
|
|
76
|
+
const resolvedHeaders = { ...optionCredentials, ...existingHeaders };
|
|
42
77
|
for (const [key, value] of Object.entries(envHeaders)) {
|
|
43
78
|
if (!resolvedHeaders[key]) {
|
|
44
79
|
resolvedHeaders[key] = value;
|
|
@@ -105,9 +140,17 @@ const module$1 = defineNuxtModule({
|
|
|
105
140
|
// grants). Declared so NUXT_MEDIA_LIBRARY_SERVICE_TOKEN applies.
|
|
106
141
|
serviceToken: existingPrivate.serviceToken ?? ""
|
|
107
142
|
};
|
|
143
|
+
const logger = useLogger("media-library");
|
|
144
|
+
const publicHeaders = browserHeaders(existingPublic.headers ?? options.headers);
|
|
145
|
+
if (publicHeaders.dropped.length) {
|
|
146
|
+
logger.warn(`mediaLibrary.headers: ${publicHeaders.dropped.join(", ")} kept server-side, not sent to browsers.`);
|
|
147
|
+
}
|
|
148
|
+
if (existingPublic.proxyUploads === false && !existingPublic.baseURL && !options.publicBaseURL) {
|
|
149
|
+
logger.warn("proxyUploads is false but no public media library address is set: set mediaLibrary.publicBaseURL. baseURL is server-side only.");
|
|
150
|
+
}
|
|
108
151
|
nuxt.options.runtimeConfig.public.mediaLibrary = {
|
|
109
152
|
...existingPublic,
|
|
110
|
-
baseURL: existingPublic.baseURL
|
|
153
|
+
baseURL: browserBaseURL(existingPublic.baseURL, options.publicBaseURL),
|
|
111
154
|
routes: existingPublic.routes ?? routes,
|
|
112
155
|
proxyUploads: existingPublic.proxyUploads ?? true,
|
|
113
156
|
partSize: existingPublic.partSize ?? options.partSize ?? 8 * 1024 * 1024,
|
|
@@ -117,9 +160,11 @@ const module$1 = defineNuxtModule({
|
|
|
117
160
|
perPart: existingPublic.checksum?.perPart ?? options.checksum?.perPart ?? true,
|
|
118
161
|
final: existingPublic.checksum?.final ?? options.checksum?.final ?? true
|
|
119
162
|
},
|
|
120
|
-
headers:
|
|
163
|
+
headers: publicHeaders.headers,
|
|
121
164
|
// Where useMLServiceUpload sends parts (NUXT_PUBLIC_MEDIA_LIBRARY_SERVICE_UPLOAD_BASE_URL).
|
|
122
|
-
serviceUploadBaseURL: existingPublic.serviceUploadBaseURL || options.serviceUploads?.baseURL || (options.serviceUploads?.proxy ? SERVICE_UPLOADS_ROUTE : "")
|
|
165
|
+
serviceUploadBaseURL: existingPublic.serviceUploadBaseURL || options.serviceUploads?.baseURL || (options.serviceUploads?.proxy ? SERVICE_UPLOADS_ROUTE : ""),
|
|
166
|
+
// Names widgets can pass as `service` (NUXT_PUBLIC_MEDIA_LIBRARY_SERVICE_UPLOAD_BACKENDS).
|
|
167
|
+
serviceUploadBackends: { ...serviceBackends(options.serviceUploads?.backends, logger), ...existingPublic.serviceUploadBackends }
|
|
123
168
|
};
|
|
124
169
|
addPlugin({
|
|
125
170
|
src: resolver.resolve("./runtime/plugins/config")
|
|
@@ -39,11 +39,11 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
39
39
|
onFiles?: ((files: File[]) => any) | undefined;
|
|
40
40
|
onRejected?: ((files: RejectedFile[]) => any) | undefined;
|
|
41
41
|
}>, {
|
|
42
|
+
title: string;
|
|
42
43
|
variant: "area" | "overlay";
|
|
43
44
|
compact: boolean;
|
|
44
45
|
disabled: boolean;
|
|
45
46
|
multiple: boolean;
|
|
46
|
-
title: string;
|
|
47
47
|
accept: string;
|
|
48
48
|
maxSize: number | null;
|
|
49
49
|
maxFiles: number | null;
|
|
@@ -39,11 +39,11 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
39
39
|
onFiles?: ((files: File[]) => any) | undefined;
|
|
40
40
|
onRejected?: ((files: RejectedFile[]) => any) | undefined;
|
|
41
41
|
}>, {
|
|
42
|
+
title: string;
|
|
42
43
|
variant: "area" | "overlay";
|
|
43
44
|
compact: boolean;
|
|
44
45
|
disabled: boolean;
|
|
45
46
|
multiple: boolean;
|
|
46
|
-
title: string;
|
|
47
47
|
accept: string;
|
|
48
48
|
maxSize: number | null;
|
|
49
49
|
maxFiles: number | null;
|
|
@@ -15,6 +15,16 @@ type __VLS_Props = {
|
|
|
15
15
|
visibility?: MediaUploadOptions['visibility'];
|
|
16
16
|
processingConfig?: MediaUploadOptions['processingConfig'];
|
|
17
17
|
collectionName?: string | null;
|
|
18
|
+
/** Upload profile name for new files. */
|
|
19
|
+
profile?: string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Start uploads at a backend service instead of Nuxt's own route: a name
|
|
22
|
+
* registered under `mediaLibrary.serviceUploads.backends` (e.g. "videos"),
|
|
23
|
+
* or `{ start }`. See useMLMediaUpload.
|
|
24
|
+
*/
|
|
25
|
+
service?: MediaUploadOptions['service'];
|
|
26
|
+
/** Extra fields for the service's start request, e.g. `{ context: { lessonId } }`. */
|
|
27
|
+
serviceBody?: Record<string, unknown> | null;
|
|
18
28
|
/** Show "Select existing", which opens the library picker. */
|
|
19
29
|
picker?: boolean;
|
|
20
30
|
/** Replaces the built-in picker, e.g. with the host's own dialog. */
|
|
@@ -33,17 +43,17 @@ type __VLS_ModelProps = {
|
|
|
33
43
|
};
|
|
34
44
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
35
45
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
36
|
-
uploaded: (assetId: string) => any;
|
|
46
|
+
uploaded: (assetId: string, sessionId: string | null) => any;
|
|
37
47
|
"update:modelValue": (value: string | null) => any;
|
|
38
48
|
"update:alt": (value: string | undefined) => any;
|
|
39
49
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
40
|
-
onUploaded?: ((assetId: string) => any) | undefined;
|
|
50
|
+
onUploaded?: ((assetId: string, sessionId: string | null) => any) | undefined;
|
|
41
51
|
"onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
|
|
42
52
|
"onUpdate:alt"?: ((value: string | undefined) => any) | undefined;
|
|
43
53
|
}>, {
|
|
44
|
-
|
|
45
|
-
visibility: "private" | "public" | "restricted";
|
|
54
|
+
visibility: "private" | "restricted" | "public";
|
|
46
55
|
label: string;
|
|
56
|
+
disabled: boolean;
|
|
47
57
|
accept: readonly string[] | string;
|
|
48
58
|
maxSize: number | null;
|
|
49
59
|
required: boolean;
|
|
@@ -54,6 +64,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
54
64
|
enableTranscription?: boolean;
|
|
55
65
|
};
|
|
56
66
|
collectionName: string | null;
|
|
67
|
+
profile: string | null;
|
|
68
|
+
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
69
|
+
serviceBody: Record<string, unknown> | null;
|
|
57
70
|
hint: string;
|
|
58
71
|
missingAlt: boolean;
|
|
59
72
|
picker: boolean;
|
|
@@ -15,6 +15,9 @@ const props = defineProps({
|
|
|
15
15
|
visibility: { type: String, required: false, default: void 0 },
|
|
16
16
|
processingConfig: { type: Object, required: false, default: void 0 },
|
|
17
17
|
collectionName: { type: [String, null], required: false, default: null },
|
|
18
|
+
profile: { type: [String, null], required: false, default: null },
|
|
19
|
+
service: { type: null, required: false, default: void 0 },
|
|
20
|
+
serviceBody: { type: [Object, null], required: false, default: null },
|
|
18
21
|
picker: { type: Boolean, required: false, default: true },
|
|
19
22
|
onPick: { type: Function, required: false, default: void 0 },
|
|
20
23
|
editor: { type: Boolean, required: false, default: true },
|
|
@@ -30,7 +33,10 @@ const field = useMLMediaField(model, {
|
|
|
30
33
|
visibility: props.visibility,
|
|
31
34
|
processingConfig: props.processingConfig,
|
|
32
35
|
collectionName: props.collectionName,
|
|
33
|
-
|
|
36
|
+
profile: props.profile,
|
|
37
|
+
service: props.service,
|
|
38
|
+
serviceBody: props.serviceBody,
|
|
39
|
+
onUploaded: (id, sessionId) => emit("uploaded", id, sessionId)
|
|
34
40
|
});
|
|
35
41
|
const { state, asset, upload, message } = field;
|
|
36
42
|
const helpText = computed(() => {
|
|
@@ -65,19 +71,30 @@ const veilPhase = computed(() => {
|
|
|
65
71
|
});
|
|
66
72
|
const filePicker = useMLFilePicker();
|
|
67
73
|
const canPick = computed(() => !!props.onPick || props.picker);
|
|
74
|
+
const uploadOptions = computed(() => ({
|
|
75
|
+
visibility: props.visibility,
|
|
76
|
+
processingConfig: props.processingConfig,
|
|
77
|
+
collectionName: props.collectionName,
|
|
78
|
+
profile: props.profile,
|
|
79
|
+
service: props.service,
|
|
80
|
+
serviceBody: props.serviceBody
|
|
81
|
+
}));
|
|
68
82
|
async function pickExisting() {
|
|
69
83
|
if (props.onPick) return props.onPick();
|
|
84
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
70
85
|
const ids = await filePicker.open({
|
|
71
86
|
accept: props.accept,
|
|
72
87
|
max: 1,
|
|
73
88
|
selected: [model.value],
|
|
74
89
|
title: props.label ? `Select ${props.label.toLowerCase()}` : "Select file",
|
|
75
90
|
maxSize: props.maxSize,
|
|
76
|
-
upload:
|
|
91
|
+
upload: uploadOptions.value,
|
|
92
|
+
onUploaded: (id, sessionId) => sessions.set(id, sessionId)
|
|
77
93
|
});
|
|
78
94
|
if (ids) {
|
|
79
95
|
field.clearMessage();
|
|
80
96
|
model.value = ids[0] ?? null;
|
|
97
|
+
if (model.value && sessions.has(model.value)) emit("uploaded", model.value, sessions.get(model.value) ?? null);
|
|
81
98
|
}
|
|
82
99
|
}
|
|
83
100
|
const mediaEditor = useMLMediaEditor();
|
|
@@ -15,6 +15,16 @@ type __VLS_Props = {
|
|
|
15
15
|
visibility?: MediaUploadOptions['visibility'];
|
|
16
16
|
processingConfig?: MediaUploadOptions['processingConfig'];
|
|
17
17
|
collectionName?: string | null;
|
|
18
|
+
/** Upload profile name for new files. */
|
|
19
|
+
profile?: string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Start uploads at a backend service instead of Nuxt's own route: a name
|
|
22
|
+
* registered under `mediaLibrary.serviceUploads.backends` (e.g. "videos"),
|
|
23
|
+
* or `{ start }`. See useMLMediaUpload.
|
|
24
|
+
*/
|
|
25
|
+
service?: MediaUploadOptions['service'];
|
|
26
|
+
/** Extra fields for the service's start request, e.g. `{ context: { lessonId } }`. */
|
|
27
|
+
serviceBody?: Record<string, unknown> | null;
|
|
18
28
|
/** Show "Select existing", which opens the library picker. */
|
|
19
29
|
picker?: boolean;
|
|
20
30
|
/** Replaces the built-in picker, e.g. with the host's own dialog. */
|
|
@@ -33,17 +43,17 @@ type __VLS_ModelProps = {
|
|
|
33
43
|
};
|
|
34
44
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
35
45
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
36
|
-
uploaded: (assetId: string) => any;
|
|
46
|
+
uploaded: (assetId: string, sessionId: string | null) => any;
|
|
37
47
|
"update:modelValue": (value: string | null) => any;
|
|
38
48
|
"update:alt": (value: string | undefined) => any;
|
|
39
49
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
40
|
-
onUploaded?: ((assetId: string) => any) | undefined;
|
|
50
|
+
onUploaded?: ((assetId: string, sessionId: string | null) => any) | undefined;
|
|
41
51
|
"onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
|
|
42
52
|
"onUpdate:alt"?: ((value: string | undefined) => any) | undefined;
|
|
43
53
|
}>, {
|
|
44
|
-
|
|
45
|
-
visibility: "private" | "public" | "restricted";
|
|
54
|
+
visibility: "private" | "restricted" | "public";
|
|
46
55
|
label: string;
|
|
56
|
+
disabled: boolean;
|
|
47
57
|
accept: readonly string[] | string;
|
|
48
58
|
maxSize: number | null;
|
|
49
59
|
required: boolean;
|
|
@@ -54,6 +64,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
54
64
|
enableTranscription?: boolean;
|
|
55
65
|
};
|
|
56
66
|
collectionName: string | null;
|
|
67
|
+
profile: string | null;
|
|
68
|
+
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
69
|
+
serviceBody: Record<string, unknown> | null;
|
|
57
70
|
hint: string;
|
|
58
71
|
missingAlt: boolean;
|
|
59
72
|
picker: boolean;
|
|
@@ -6,6 +6,7 @@ type __VLS_Props = {
|
|
|
6
6
|
title?: string;
|
|
7
7
|
maxSize?: number | null;
|
|
8
8
|
upload?: MediaUploadOptions;
|
|
9
|
+
onUploaded?: (assetId: string, sessionId: string | null) => void;
|
|
9
10
|
};
|
|
10
11
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
11
12
|
close: (ids: string[] | null) => any;
|
|
@@ -18,6 +19,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
18
19
|
selected: readonly string[];
|
|
19
20
|
maxSize: number | null;
|
|
20
21
|
upload: MediaUploadOptions;
|
|
22
|
+
onUploaded: (assetId: string, sessionId: string | null) => void;
|
|
21
23
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
22
24
|
declare const _default: typeof __VLS_export;
|
|
23
25
|
export default _default;
|
|
@@ -5,7 +5,8 @@ defineProps({
|
|
|
5
5
|
selected: { type: Array, required: false, default: () => [] },
|
|
6
6
|
title: { type: String, required: false, default: "Select file" },
|
|
7
7
|
maxSize: { type: [Number, null], required: false, default: null },
|
|
8
|
-
upload: { type: Object, required: false, default: () => ({}) }
|
|
8
|
+
upload: { type: Object, required: false, default: () => ({}) },
|
|
9
|
+
onUploaded: { type: Function, required: false, default: void 0 }
|
|
9
10
|
});
|
|
10
11
|
const emit = defineEmits(["close"]);
|
|
11
12
|
</script>
|
|
@@ -25,6 +26,7 @@ const emit = defineEmits(["close"]);
|
|
|
25
26
|
:selected="selected"
|
|
26
27
|
:max-size="maxSize"
|
|
27
28
|
:upload="upload"
|
|
29
|
+
:on-uploaded="onUploaded"
|
|
28
30
|
@done="(ids) => emit('close', ids)"
|
|
29
31
|
@cancel="emit('close', null)"
|
|
30
32
|
/>
|
|
@@ -6,6 +6,7 @@ type __VLS_Props = {
|
|
|
6
6
|
title?: string;
|
|
7
7
|
maxSize?: number | null;
|
|
8
8
|
upload?: MediaUploadOptions;
|
|
9
|
+
onUploaded?: (assetId: string, sessionId: string | null) => void;
|
|
9
10
|
};
|
|
10
11
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
11
12
|
close: (ids: string[] | null) => any;
|
|
@@ -18,6 +19,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
18
19
|
selected: readonly string[];
|
|
19
20
|
maxSize: number | null;
|
|
20
21
|
upload: MediaUploadOptions;
|
|
22
|
+
onUploaded: (assetId: string, sessionId: string | null) => void;
|
|
21
23
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
22
24
|
declare const _default: typeof __VLS_export;
|
|
23
25
|
export default _default;
|
|
@@ -11,6 +11,8 @@ type __VLS_Props = {
|
|
|
11
11
|
maxSize?: number | null;
|
|
12
12
|
/** Upload session settings for files dropped into the picker. */
|
|
13
13
|
upload?: MediaUploadOptions;
|
|
14
|
+
/** A file uploaded from the picker exists in the library (with its upload session). */
|
|
15
|
+
onUploaded?: (assetId: string, sessionId: string | null) => void;
|
|
14
16
|
/** Grid rows for a fixed-height body; the list scrolls inside it. */
|
|
15
17
|
bodyClass?: string;
|
|
16
18
|
};
|
|
@@ -27,6 +29,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
27
29
|
selected: readonly string[];
|
|
28
30
|
maxSize: number | null;
|
|
29
31
|
upload: MediaUploadOptions;
|
|
32
|
+
onUploaded: (assetId: string, sessionId: string | null) => void;
|
|
30
33
|
bodyClass: string;
|
|
31
34
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
32
35
|
declare const _default: typeof __VLS_export;
|
|
@@ -15,6 +15,7 @@ const props = defineProps({
|
|
|
15
15
|
title: { type: String, required: false, default: void 0 },
|
|
16
16
|
maxSize: { type: [Number, null], required: false, default: null },
|
|
17
17
|
upload: { type: Object, required: false, default: () => ({}) },
|
|
18
|
+
onUploaded: { type: Function, required: false, default: void 0 },
|
|
18
19
|
bodyClass: { type: String, required: false, default: "max-h-[min(60vh,560px)]" }
|
|
19
20
|
});
|
|
20
21
|
const emit = defineEmits(["done", "cancel"]);
|
|
@@ -69,7 +70,11 @@ function onFiles(files) {
|
|
|
69
70
|
rejectNote.value = null;
|
|
70
71
|
for (const file of files) {
|
|
71
72
|
const u = uploader.start(file);
|
|
72
|
-
void u.created.then((id) =>
|
|
73
|
+
void u.created.then((id) => {
|
|
74
|
+
if (!id) return;
|
|
75
|
+
props.onUploaded?.(id, u.sessionId);
|
|
76
|
+
if (!isChosen(id)) choose(id);
|
|
77
|
+
});
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
80
|
function onRejected(rejected) {
|
|
@@ -11,6 +11,8 @@ type __VLS_Props = {
|
|
|
11
11
|
maxSize?: number | null;
|
|
12
12
|
/** Upload session settings for files dropped into the picker. */
|
|
13
13
|
upload?: MediaUploadOptions;
|
|
14
|
+
/** A file uploaded from the picker exists in the library (with its upload session). */
|
|
15
|
+
onUploaded?: (assetId: string, sessionId: string | null) => void;
|
|
14
16
|
/** Grid rows for a fixed-height body; the list scrolls inside it. */
|
|
15
17
|
bodyClass?: string;
|
|
16
18
|
};
|
|
@@ -27,6 +29,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
27
29
|
selected: readonly string[];
|
|
28
30
|
maxSize: number | null;
|
|
29
31
|
upload: MediaUploadOptions;
|
|
32
|
+
onUploaded: (assetId: string, sessionId: string | null) => void;
|
|
30
33
|
bodyClass: string;
|
|
31
34
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
32
35
|
declare const _default: typeof __VLS_export;
|
|
@@ -30,6 +30,14 @@ type __VLS_Props = {
|
|
|
30
30
|
collectionName?: string | null;
|
|
31
31
|
/** Upload profile for the single list (with `profiles`, each role has its own). */
|
|
32
32
|
profile?: string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Start uploads at a backend service instead of Nuxt's own route: a name
|
|
35
|
+
* registered under `mediaLibrary.serviceUploads.backends` (e.g. "videos"),
|
|
36
|
+
* or `{ start }`. Each role's profile is sent with the start request.
|
|
37
|
+
*/
|
|
38
|
+
service?: MediaUploadOptions['service'];
|
|
39
|
+
/** Extra fields for the service's start request, e.g. `{ context: { lessonId } }`. */
|
|
40
|
+
serviceBody?: Record<string, unknown> | null;
|
|
33
41
|
picker?: boolean;
|
|
34
42
|
onPick?: () => void;
|
|
35
43
|
editor?: boolean;
|
|
@@ -41,20 +49,19 @@ type __VLS_ModelProps = {
|
|
|
41
49
|
};
|
|
42
50
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
43
51
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
44
|
-
uploaded: (assetId: string, role: string) => any;
|
|
52
|
+
uploaded: (assetId: string, role: string, sessionId: string | null) => any;
|
|
45
53
|
"update:modelValue": (value: string[]) => any;
|
|
46
54
|
"update:alt": (value: Record<string, string> | undefined) => any;
|
|
47
55
|
"update:profiles": (profiles: MediaGalleryProfiles) => any;
|
|
48
56
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
49
|
-
onUploaded?: ((assetId: string, role: string) => any) | undefined;
|
|
57
|
+
onUploaded?: ((assetId: string, role: string, sessionId: string | null) => any) | undefined;
|
|
50
58
|
"onUpdate:modelValue"?: ((value: string[]) => any) | undefined;
|
|
51
59
|
"onUpdate:alt"?: ((value: Record<string, string> | undefined) => any) | undefined;
|
|
52
60
|
"onUpdate:profiles"?: ((profiles: MediaGalleryProfiles) => any) | undefined;
|
|
53
61
|
}>, {
|
|
54
|
-
|
|
62
|
+
visibility: "private" | "restricted" | "public";
|
|
55
63
|
title: string;
|
|
56
|
-
|
|
57
|
-
profile: string | null;
|
|
64
|
+
disabled: boolean;
|
|
58
65
|
accept: readonly string[] | string;
|
|
59
66
|
max: number | null;
|
|
60
67
|
maxSize: number | null;
|
|
@@ -64,6 +71,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
64
71
|
enableTranscription?: boolean;
|
|
65
72
|
};
|
|
66
73
|
collectionName: string | null;
|
|
74
|
+
profile: string | null;
|
|
75
|
+
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
76
|
+
serviceBody: Record<string, unknown> | null;
|
|
67
77
|
hint: string;
|
|
68
78
|
picker: boolean;
|
|
69
79
|
onPick: () => void;
|