nuxt-media-library 0.0.57 → 0.0.59
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 +41 -8
- package/dist/module.d.mts +13 -3
- package/dist/module.d.ts +13 -3
- package/dist/module.json +1 -1
- package/dist/module.mjs +11 -19
- package/dist/runtime/components/MLMedia/Field.d.vue.ts +24 -11
- package/dist/runtime/components/MLMedia/Field.vue +14 -2
- package/dist/runtime/components/MLMedia/Field.vue.d.ts +24 -11
- package/dist/runtime/components/MLMedia/FileFacts.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/FileFacts.vue.d.ts +1 -1
- package/dist/runtime/components/MLMedia/FilePicker.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/FilePicker.vue.d.ts +1 -1
- package/dist/runtime/components/MLMedia/FilePickerPanel.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/FilePickerPanel.vue.d.ts +1 -1
- package/dist/runtime/components/MLMedia/Gallery.d.vue.ts +22 -9
- package/dist/runtime/components/MLMedia/Gallery.vue +12 -1
- package/dist/runtime/components/MLMedia/Gallery.vue.d.ts +22 -9
- package/dist/runtime/components/MLMedia/GallerySection.d.vue.ts +15 -9
- package/dist/runtime/components/MLMedia/GallerySection.vue +10 -1
- package/dist/runtime/components/MLMedia/GallerySection.vue.d.ts +15 -9
- package/dist/runtime/components/MLMedia/MediaTile.d.vue.ts +4 -4
- package/dist/runtime/components/MLMedia/MediaTile.vue.d.ts +4 -4
- package/dist/runtime/components/MLMedia/PickerCard.d.vue.ts +3 -3
- package/dist/runtime/components/MLMedia/PickerCard.vue.d.ts +3 -3
- 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/components/MLMedia/Thumbnail.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/Thumbnail.vue.d.ts +1 -1
- package/dist/runtime/components/MLMedia/UploadRow.vue +10 -0
- package/dist/runtime/components/MLMedia/UploadSteps.vue +3 -3
- package/dist/runtime/components/MLMedia/UsageList.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/UsageList.vue.d.ts +1 -1
- package/dist/runtime/composables/useMLAssetPreview.js +9 -2
- package/dist/runtime/composables/useMLMediaAssets.js +23 -2
- package/dist/runtime/composables/useMLMediaBrowse.d.ts +6 -0
- package/dist/runtime/composables/useMLMediaGallery.d.ts +2 -0
- package/dist/runtime/composables/useMLMediaGallery.js +2 -0
- package/dist/runtime/composables/useMLMediaMeta.d.ts +18 -0
- package/dist/runtime/composables/useMLMediaMeta.js +55 -0
- package/dist/runtime/composables/useMLMediaUpload.d.ts +28 -8
- package/dist/runtime/composables/useMLMediaUpload.js +2 -1
- package/dist/runtime/composables/useMLUploadQueue.d.ts +5 -3
- package/dist/runtime/composables/useMLUploadQueue.js +56 -8
- package/dist/runtime/composables/useMLWebAppDeploys.d.ts +48 -0
- package/dist/runtime/composables/useMLWebAppDeploys.js +69 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/server/assets-proxy.js +9 -1
- package/dist/runtime/server/create-ml-proxy.js +5 -1
- package/dist/runtime/server/grants.js +3 -0
- package/dist/runtime/server/index.d.ts +3 -0
- package/dist/runtime/server/index.js +1 -0
- package/dist/runtime/types/asset.d.ts +35 -4
- package/dist/runtime/utils/backends.d.ts +63 -0
- package/dist/runtime/utils/backends.js +72 -0
- package/dist/runtime/utils/fileMeta.d.ts +48 -0
- package/dist/runtime/utils/fileMeta.js +84 -0
- package/dist/runtime/utils/processingSteps.d.ts +9 -1
- package/dist/runtime/utils/processingSteps.js +18 -11
- package/dist/runtime/utils/streamUrls.d.ts +9 -0
- package/dist/runtime/utils/streamUrls.js +11 -0
- package/dist/runtime/utils/webAppDeploys.d.ts +14 -0
- package/dist/runtime/utils/webAppDeploys.js +17 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -237,9 +237,11 @@ merges the layers, so every widget on the site can use any backend by name.
|
|
|
237
237
|
export default defineNuxtConfig({
|
|
238
238
|
modules: ['nuxt-media-library'],
|
|
239
239
|
mediaLibrary: {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
backends: {
|
|
241
|
+
videos: {
|
|
242
|
+
upload: { start: '/api/videos/uploads' }, // refresh defaults to `${start}/:sessionId/grant`
|
|
243
|
+
claim: '/api/videos/uploads/:sessionId/attach', // py_client on_attach
|
|
244
|
+
},
|
|
243
245
|
},
|
|
244
246
|
},
|
|
245
247
|
})
|
|
@@ -257,17 +259,26 @@ export default defineNuxtConfig({
|
|
|
257
259
|
```vue
|
|
258
260
|
<MLMediaGallery
|
|
259
261
|
v-model="lesson.mediaIds"
|
|
260
|
-
|
|
262
|
+
backend="videos"
|
|
261
263
|
profile="lesson-video"
|
|
262
264
|
:service-body="{ context: { lessonId } }"
|
|
263
265
|
@uploaded="(assetId, role, sessionId) => attach(assetId, sessionId)"
|
|
264
266
|
/>
|
|
265
267
|
```
|
|
266
268
|
|
|
267
|
-
- `
|
|
268
|
-
`useMLMediaUpload`): a
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
- `backend` on `MLMediaGallery`, `MLMediaField` and the file picker (and
|
|
270
|
+
`useMLMediaUpload`): a name in `mediaLibrary.backends`. `service` is its
|
|
271
|
+
older name and still takes `{ start }` for a one-off; `backend` wins.
|
|
272
|
+
Without either, widgets upload through Nuxt's own route as before; an
|
|
273
|
+
unknown name fails that upload with a message naming the missing backend.
|
|
274
|
+
- Each backend has a `kind`: `media-library` (the default) or `media-kit`.
|
|
275
|
+
This version uploads only to `media-library` backends; a `media-kit` one
|
|
276
|
+
fails the upload with a clear message.
|
|
277
|
+
- With `claim`, the upload queue attaches files whose page was left before
|
|
278
|
+
the widget attached them (`POST { assetId, profile }`; 409 counts as
|
|
279
|
+
attached). The tray shows a refusal with "Add again".
|
|
280
|
+
- `mediaLibrary.serviceUploads.backends` still works: each entry becomes a
|
|
281
|
+
`media-library` backend with that `upload` and no `claim`.
|
|
271
282
|
- The start request carries the file (`mimeType`, `sizeBytes`, `filename`),
|
|
272
283
|
`profile` (each role's, in a gallery with roles) and `service-body`. The
|
|
273
284
|
service's profile decides kind, visibility and processing, so
|
|
@@ -283,6 +294,28 @@ export default defineNuxtConfig({
|
|
|
283
294
|
- There is no default backend: each layer's widgets name theirs. The module
|
|
284
295
|
only stores the paths; each layer's own routes reach its service.
|
|
285
296
|
|
|
297
|
+
### File meta from the widgets
|
|
298
|
+
|
|
299
|
+
`MLMediaField` and `MLMediaGallery` report what they know about their files:
|
|
300
|
+
name, extension, MIME type, kind, size, duration, width, height and pages.
|
|
301
|
+
|
|
302
|
+
```vue
|
|
303
|
+
<MLMediaField v-model="lesson.videoId" v-model:meta="videoMeta" @meta="m => console.log(m?.durationSeconds)" />
|
|
304
|
+
|
|
305
|
+
<MLMediaGallery v-model="post.mediaIds" v-model:meta="mediaMeta" @meta="(assetId, m) => …" />
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
- `v-model:meta` is output only: `MediaFileMeta | null` on the field, a map
|
|
309
|
+
by asset ID on the gallery (every role). `@meta` fires per file that
|
|
310
|
+
changed, with null when it left.
|
|
311
|
+
- A file uploaded in this tab is read in the browser right away
|
|
312
|
+
(`source: 'local'`, duration and size from a hidden video, audio or image
|
|
313
|
+
element). The media library's data replaces it once loaded
|
|
314
|
+
(`source: 'server'`); values it has no rendition for yet stay local.
|
|
315
|
+
- A file needs an asset ID before it has meta: during the byte upload it
|
|
316
|
+
has none yet.
|
|
317
|
+
- `useMLMediaMeta(ids, getAsset)` gives the same map for your own lists.
|
|
318
|
+
|
|
286
319
|
### Vue installation (non-Nuxt)
|
|
287
320
|
|
|
288
321
|
```bash
|
package/dist/module.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
import { ServiceUploadBackend } from '../dist/runtime/utils/uploader/serviceBackends.js';
|
|
3
|
+
import { MediaBackends } from '../dist/runtime/utils/backends.js';
|
|
3
4
|
export * from '../dist/runtime/types/resourcePolicy.js';
|
|
4
5
|
export * from '../dist/runtime/types/uploader.js';
|
|
5
6
|
export * from '../dist/runtime/types/preview.js';
|
|
@@ -43,9 +44,8 @@ interface ModuleOptions {
|
|
|
43
44
|
* public API root with `/v1`, or a proxy route); with `proxy` it defaults
|
|
44
45
|
* to the pass-through route.
|
|
45
46
|
*
|
|
46
|
-
* `backends
|
|
47
|
-
*
|
|
48
|
-
* merges them across layers. Paths only: each layer serves its routes.
|
|
47
|
+
* `backends`: older form of `mediaLibrary.backends` (start/refresh only).
|
|
48
|
+
* Each entry becomes a `media-library` backend with that `upload`.
|
|
49
49
|
*/
|
|
50
50
|
serviceUploads?: {
|
|
51
51
|
proxy?: boolean;
|
|
@@ -62,6 +62,16 @@ interface ModuleOptions {
|
|
|
62
62
|
* pass-through are still registered.
|
|
63
63
|
*/
|
|
64
64
|
manageProxy?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* The backend registry: named places widgets upload to and read from
|
|
67
|
+
* (`<MLMediaGallery backend="videos" />`). Each entry has a `kind`
|
|
68
|
+
* (`media-library`, the default, or `media-kit`) and its routes: `upload`
|
|
69
|
+
* (`{ start, refresh? }`, py_client `create_upload_router`), `claim`
|
|
70
|
+
* (attach a finished upload, `/…/:sessionId/attach`). Each service's Nuxt
|
|
71
|
+
* layer registers its own; Nuxt merges them. Paths only: each layer serves
|
|
72
|
+
* its routes.
|
|
73
|
+
*/
|
|
74
|
+
backends?: MediaBackends;
|
|
65
75
|
}
|
|
66
76
|
declare const SERVICE_UPLOADS_ROUTE = "/api/media-library/service-uploads";
|
|
67
77
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
package/dist/module.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
import { ServiceUploadBackend } from '../dist/runtime/utils/uploader/serviceBackends.js';
|
|
3
|
+
import { MediaBackends } from '../dist/runtime/utils/backends.js';
|
|
3
4
|
export * from '../dist/runtime/types/resourcePolicy.js';
|
|
4
5
|
export * from '../dist/runtime/types/uploader.js';
|
|
5
6
|
export * from '../dist/runtime/types/preview.js';
|
|
@@ -43,9 +44,8 @@ interface ModuleOptions {
|
|
|
43
44
|
* public API root with `/v1`, or a proxy route); with `proxy` it defaults
|
|
44
45
|
* to the pass-through route.
|
|
45
46
|
*
|
|
46
|
-
* `backends
|
|
47
|
-
*
|
|
48
|
-
* merges them across layers. Paths only: each layer serves its routes.
|
|
47
|
+
* `backends`: older form of `mediaLibrary.backends` (start/refresh only).
|
|
48
|
+
* Each entry becomes a `media-library` backend with that `upload`.
|
|
49
49
|
*/
|
|
50
50
|
serviceUploads?: {
|
|
51
51
|
proxy?: boolean;
|
|
@@ -62,6 +62,16 @@ interface ModuleOptions {
|
|
|
62
62
|
* pass-through are still registered.
|
|
63
63
|
*/
|
|
64
64
|
manageProxy?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* The backend registry: named places widgets upload to and read from
|
|
67
|
+
* (`<MLMediaGallery backend="videos" />`). Each entry has a `kind`
|
|
68
|
+
* (`media-library`, the default, or `media-kit`) and its routes: `upload`
|
|
69
|
+
* (`{ start, refresh? }`, py_client `create_upload_router`), `claim`
|
|
70
|
+
* (attach a finished upload, `/…/:sessionId/attach`). Each service's Nuxt
|
|
71
|
+
* layer registers its own; Nuxt merges them. Paths only: each layer serves
|
|
72
|
+
* its routes.
|
|
73
|
+
*/
|
|
74
|
+
backends?: MediaBackends;
|
|
65
75
|
}
|
|
66
76
|
declare const SERVICE_UPLOADS_ROUTE = "/api/media-library/service-uploads";
|
|
67
77
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { defineNuxtModule, createResolver, addImportsDir, addComponentsDir, addServerHandler, useLogger, addPlugin } from '@nuxt/kit';
|
|
4
|
+
import { normalizeBackends } from '../dist/runtime/utils/backends.js';
|
|
4
5
|
export * from '../dist/runtime/types/resourcePolicy.js';
|
|
5
6
|
export * from '../dist/runtime/types/uploader.js';
|
|
6
7
|
export * from '../dist/runtime/types/preview.js';
|
|
@@ -22,23 +23,6 @@ function browserBaseURL(existingPublic, publicBaseURL) {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
const SERVICE_UPLOADS_ROUTE = "/api/media-library/service-uploads";
|
|
25
|
-
function serviceBackends(backends, logger) {
|
|
26
|
-
const out = {};
|
|
27
|
-
for (const [name, backend] of Object.entries(backends ?? {})) {
|
|
28
|
-
if (!backend || typeof backend.start !== "string" || !backend.start) {
|
|
29
|
-
logger.warn(`mediaLibrary.serviceUploads.backends.${name} has no start path; ignored.`);
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (backend.refresh && !backend.refresh.includes(":sessionId")) {
|
|
33
|
-
logger.warn(`mediaLibrary.serviceUploads.backends.${name}.refresh needs a :sessionId placeholder; using the default.`);
|
|
34
|
-
}
|
|
35
|
-
out[name] = {
|
|
36
|
-
start: backend.start,
|
|
37
|
-
...backend.refresh?.includes(":sessionId") ? { refresh: backend.refresh } : {}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
return out;
|
|
41
|
-
}
|
|
42
26
|
function appCatchAlls(layers) {
|
|
43
27
|
const found = [];
|
|
44
28
|
for (const { config } of layers) {
|
|
@@ -171,6 +155,10 @@ const module$1 = defineNuxtModule({
|
|
|
171
155
|
if (existingPublic.proxyUploads === false && !existingPublic.baseURL && !options.publicBaseURL) {
|
|
172
156
|
logger.warn("proxyUploads is false but no public media library address is set: set mediaLibrary.publicBaseURL. baseURL is server-side only.");
|
|
173
157
|
}
|
|
158
|
+
const backends = {
|
|
159
|
+
...normalizeBackends(options.backends, options.serviceUploads?.backends, (m) => logger.warn(m)),
|
|
160
|
+
...normalizeBackends(existingPublic.backends, existingPublic.serviceUploadBackends, (m) => logger.warn(m))
|
|
161
|
+
};
|
|
174
162
|
nuxt.options.runtimeConfig.public.mediaLibrary = {
|
|
175
163
|
...existingPublic,
|
|
176
164
|
baseURL: browserBaseURL(existingPublic.baseURL, options.publicBaseURL),
|
|
@@ -186,8 +174,12 @@ const module$1 = defineNuxtModule({
|
|
|
186
174
|
headers: publicHeaders.headers,
|
|
187
175
|
// Where useMLServiceUpload sends parts (NUXT_PUBLIC_MEDIA_LIBRARY_SERVICE_UPLOAD_BASE_URL).
|
|
188
176
|
serviceUploadBaseURL: existingPublic.serviceUploadBaseURL || options.serviceUploads?.baseURL || (options.serviceUploads?.proxy ? SERVICE_UPLOADS_ROUTE : ""),
|
|
189
|
-
//
|
|
190
|
-
|
|
177
|
+
// The backend registry (NUXT_PUBLIC_MEDIA_LIBRARY_BACKENDS): names widgets pass as `backend`.
|
|
178
|
+
backends,
|
|
179
|
+
// Older form, kept for code that reads it: each backend's upload routes.
|
|
180
|
+
serviceUploadBackends: Object.fromEntries(
|
|
181
|
+
Object.entries(backends).flatMap(([name, b]) => b.upload ? [[name, b.upload]] : [])
|
|
182
|
+
)
|
|
191
183
|
};
|
|
192
184
|
addPlugin({
|
|
193
185
|
src: resolver.resolve("./runtime/plugins/config")
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type MediaFileMeta } from '../../composables/useMLMediaMeta.js';
|
|
1
2
|
import type { MediaUploadOptions } from '../../composables/useMLMediaUpload.js';
|
|
2
3
|
type __VLS_Props = {
|
|
3
4
|
/** Asset kinds the field takes: ['image'], "image,video". Empty means any file. */
|
|
@@ -18,10 +19,12 @@ type __VLS_Props = {
|
|
|
18
19
|
/** Upload profile name for new files. */
|
|
19
20
|
profile?: string | null;
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* The backend registered under `mediaLibrary.backends` that uploads go to
|
|
23
|
+
* (e.g. "videos"). Its `claim` route attaches files whose page was left
|
|
24
|
+
* before they finished. Wins over `service`.
|
|
24
25
|
*/
|
|
26
|
+
backend?: string | null;
|
|
27
|
+
/** Older name for `backend`; also takes an inline `{ start }`. See useMLMediaUpload. */
|
|
25
28
|
service?: MediaUploadOptions['service'];
|
|
26
29
|
/** Extra fields for the service's start request, e.g. `{ context: { lessonId } }`. */
|
|
27
30
|
serviceBody?: Record<string, unknown> | null;
|
|
@@ -40,33 +43,43 @@ type __VLS_ModelProps = {
|
|
|
40
43
|
modelValue?: string | null;
|
|
41
44
|
/** Alt text for the file, stored on the host record. Edited in the media editor. */
|
|
42
45
|
'alt'?: string | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* The file's meta (name, extension, MIME type, kind, size, duration,
|
|
48
|
+
* dimensions), or null when empty. Output only: the field writes it.
|
|
49
|
+
*/
|
|
50
|
+
'meta'?: MediaFileMeta | null;
|
|
43
51
|
};
|
|
44
52
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
45
53
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
46
54
|
uploaded: (assetId: string, sessionId: string | null) => any;
|
|
55
|
+
meta: (meta: MediaFileMeta | null) => any;
|
|
47
56
|
"update:modelValue": (value: string | null) => any;
|
|
48
57
|
"update:alt": (value: string | undefined) => any;
|
|
58
|
+
"update:meta": (value: MediaFileMeta | null) => any;
|
|
49
59
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
50
60
|
onUploaded?: ((assetId: string, sessionId: string | null) => any) | undefined;
|
|
61
|
+
onMeta?: ((meta: MediaFileMeta | null) => any) | undefined;
|
|
51
62
|
"onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
|
|
52
63
|
"onUpdate:alt"?: ((value: string | undefined) => any) | undefined;
|
|
64
|
+
"onUpdate:meta"?: ((value: MediaFileMeta | null) => any) | undefined;
|
|
53
65
|
}>, {
|
|
54
|
-
visibility: "private" | "
|
|
55
|
-
label: string;
|
|
66
|
+
visibility: "private" | "public" | "restricted";
|
|
56
67
|
disabled: boolean;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
label: string;
|
|
69
|
+
backend: string | null;
|
|
70
|
+
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
71
|
+
profile: string | null;
|
|
61
72
|
processingConfig: {
|
|
62
73
|
stepOptions?: Record<string, Record<string, unknown>> | null;
|
|
63
74
|
useFilenameAsAssetId?: boolean;
|
|
64
75
|
enableTranscription?: boolean;
|
|
65
76
|
};
|
|
66
77
|
collectionName: string | null;
|
|
67
|
-
profile: string | null;
|
|
68
|
-
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
69
78
|
serviceBody: Record<string, unknown> | null;
|
|
79
|
+
accept: readonly string[] | string;
|
|
80
|
+
maxSize: number | null;
|
|
81
|
+
required: boolean;
|
|
82
|
+
removeLabel: string;
|
|
70
83
|
hint: string;
|
|
71
84
|
missingAlt: boolean;
|
|
72
85
|
picker: boolean;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed } from "vue";
|
|
2
|
+
import { computed, watch } from "vue";
|
|
3
3
|
import { useMLMediaField } from "../../composables/useMLMediaField";
|
|
4
4
|
import { useMLFilePicker } from "../../composables/useMLFilePicker";
|
|
5
5
|
import { useMLMediaEditor } from "../../composables/useMLMediaEditor";
|
|
6
|
+
import { useMLMediaMeta } from "../../composables/useMLMediaMeta";
|
|
6
7
|
import { assetDimensions, assetDurationSeconds, assetName, assetSizeBytes, formatBytes, formatDuration } from "../../utils/assetFormat";
|
|
7
8
|
const props = defineProps({
|
|
8
9
|
accept: { type: [Array, String], required: false, default: "" },
|
|
@@ -16,6 +17,7 @@ const props = defineProps({
|
|
|
16
17
|
processingConfig: { type: Object, required: false, default: void 0 },
|
|
17
18
|
collectionName: { type: [String, null], required: false, default: null },
|
|
18
19
|
profile: { type: [String, null], required: false, default: null },
|
|
20
|
+
backend: { type: [String, null], required: false, default: null },
|
|
19
21
|
service: { type: null, required: false, default: void 0 },
|
|
20
22
|
serviceBody: { type: [Object, null], required: false, default: null },
|
|
21
23
|
picker: { type: Boolean, required: false, default: true },
|
|
@@ -26,7 +28,8 @@ const props = defineProps({
|
|
|
26
28
|
});
|
|
27
29
|
const model = defineModel({ type: [String, null], ...{ default: null } });
|
|
28
30
|
const alt = defineModel("alt", { type: null, ...{ default: void 0 } });
|
|
29
|
-
const
|
|
31
|
+
const meta = defineModel("meta", { type: [Object, null], ...{ default: null } });
|
|
32
|
+
const emit = defineEmits(["uploaded", "meta"]);
|
|
30
33
|
const field = useMLMediaField(model, {
|
|
31
34
|
accept: () => props.accept,
|
|
32
35
|
maxSize: () => props.maxSize,
|
|
@@ -34,12 +37,20 @@ const field = useMLMediaField(model, {
|
|
|
34
37
|
processingConfig: props.processingConfig,
|
|
35
38
|
collectionName: props.collectionName,
|
|
36
39
|
profile: props.profile,
|
|
40
|
+
backend: props.backend,
|
|
37
41
|
service: props.service,
|
|
38
42
|
serviceBody: props.serviceBody,
|
|
39
43
|
label: props.label || void 0,
|
|
40
44
|
onUploaded: (id, sessionId) => emit("uploaded", id, sessionId)
|
|
41
45
|
});
|
|
42
46
|
const { state, asset, upload, message } = field;
|
|
47
|
+
const metaById = useMLMediaMeta(() => [model.value], (id) => id === model.value ? asset.value : null);
|
|
48
|
+
const currentMeta = computed(() => model.value ? metaById.value[model.value] ?? null : null);
|
|
49
|
+
watch(() => JSON.stringify(currentMeta.value), (now, before) => {
|
|
50
|
+
if (now === before || before === void 0 && !currentMeta.value) return;
|
|
51
|
+
meta.value = currentMeta.value;
|
|
52
|
+
emit("meta", currentMeta.value);
|
|
53
|
+
}, { immediate: true });
|
|
43
54
|
const helpText = computed(() => {
|
|
44
55
|
if (props.hint) return props.hint;
|
|
45
56
|
const parts = [field.acceptLabel.value === "any file" ? "" : `Takes ${field.acceptLabel.value}`];
|
|
@@ -77,6 +88,7 @@ const uploadOptions = computed(() => ({
|
|
|
77
88
|
processingConfig: props.processingConfig,
|
|
78
89
|
collectionName: props.collectionName,
|
|
79
90
|
profile: props.profile,
|
|
91
|
+
backend: props.backend,
|
|
80
92
|
service: props.service,
|
|
81
93
|
serviceBody: props.serviceBody
|
|
82
94
|
}));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type MediaFileMeta } from '../../composables/useMLMediaMeta.js';
|
|
1
2
|
import type { MediaUploadOptions } from '../../composables/useMLMediaUpload.js';
|
|
2
3
|
type __VLS_Props = {
|
|
3
4
|
/** Asset kinds the field takes: ['image'], "image,video". Empty means any file. */
|
|
@@ -18,10 +19,12 @@ type __VLS_Props = {
|
|
|
18
19
|
/** Upload profile name for new files. */
|
|
19
20
|
profile?: string | null;
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* The backend registered under `mediaLibrary.backends` that uploads go to
|
|
23
|
+
* (e.g. "videos"). Its `claim` route attaches files whose page was left
|
|
24
|
+
* before they finished. Wins over `service`.
|
|
24
25
|
*/
|
|
26
|
+
backend?: string | null;
|
|
27
|
+
/** Older name for `backend`; also takes an inline `{ start }`. See useMLMediaUpload. */
|
|
25
28
|
service?: MediaUploadOptions['service'];
|
|
26
29
|
/** Extra fields for the service's start request, e.g. `{ context: { lessonId } }`. */
|
|
27
30
|
serviceBody?: Record<string, unknown> | null;
|
|
@@ -40,33 +43,43 @@ type __VLS_ModelProps = {
|
|
|
40
43
|
modelValue?: string | null;
|
|
41
44
|
/** Alt text for the file, stored on the host record. Edited in the media editor. */
|
|
42
45
|
'alt'?: string | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* The file's meta (name, extension, MIME type, kind, size, duration,
|
|
48
|
+
* dimensions), or null when empty. Output only: the field writes it.
|
|
49
|
+
*/
|
|
50
|
+
'meta'?: MediaFileMeta | null;
|
|
43
51
|
};
|
|
44
52
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
45
53
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
46
54
|
uploaded: (assetId: string, sessionId: string | null) => any;
|
|
55
|
+
meta: (meta: MediaFileMeta | null) => any;
|
|
47
56
|
"update:modelValue": (value: string | null) => any;
|
|
48
57
|
"update:alt": (value: string | undefined) => any;
|
|
58
|
+
"update:meta": (value: MediaFileMeta | null) => any;
|
|
49
59
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
50
60
|
onUploaded?: ((assetId: string, sessionId: string | null) => any) | undefined;
|
|
61
|
+
onMeta?: ((meta: MediaFileMeta | null) => any) | undefined;
|
|
51
62
|
"onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
|
|
52
63
|
"onUpdate:alt"?: ((value: string | undefined) => any) | undefined;
|
|
64
|
+
"onUpdate:meta"?: ((value: MediaFileMeta | null) => any) | undefined;
|
|
53
65
|
}>, {
|
|
54
|
-
visibility: "private" | "
|
|
55
|
-
label: string;
|
|
66
|
+
visibility: "private" | "public" | "restricted";
|
|
56
67
|
disabled: boolean;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
label: string;
|
|
69
|
+
backend: string | null;
|
|
70
|
+
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
71
|
+
profile: string | null;
|
|
61
72
|
processingConfig: {
|
|
62
73
|
stepOptions?: Record<string, Record<string, unknown>> | null;
|
|
63
74
|
useFilenameAsAssetId?: boolean;
|
|
64
75
|
enableTranscription?: boolean;
|
|
65
76
|
};
|
|
66
77
|
collectionName: string | null;
|
|
67
|
-
profile: string | null;
|
|
68
|
-
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
69
78
|
serviceBody: Record<string, unknown> | null;
|
|
79
|
+
accept: readonly string[] | string;
|
|
80
|
+
maxSize: number | null;
|
|
81
|
+
required: boolean;
|
|
82
|
+
removeLabel: string;
|
|
70
83
|
hint: string;
|
|
71
84
|
missingAlt: boolean;
|
|
72
85
|
picker: boolean;
|
|
@@ -10,8 +10,8 @@ type __VLS_Props = {
|
|
|
10
10
|
title?: string;
|
|
11
11
|
};
|
|
12
12
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
13
|
-
asset: AssetDetail | DeliveryAsset | null;
|
|
14
13
|
title: string;
|
|
14
|
+
asset: AssetDetail | DeliveryAsset | null;
|
|
15
15
|
items: AssetFact[];
|
|
16
16
|
extra: AssetFact[];
|
|
17
17
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -10,8 +10,8 @@ type __VLS_Props = {
|
|
|
10
10
|
title?: string;
|
|
11
11
|
};
|
|
12
12
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
13
|
-
asset: AssetDetail | DeliveryAsset | null;
|
|
14
13
|
title: string;
|
|
14
|
+
asset: AssetDetail | DeliveryAsset | null;
|
|
15
15
|
items: AssetFact[];
|
|
16
16
|
extra: AssetFact[];
|
|
17
17
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -14,11 +14,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
14
14
|
onClose?: ((ids: string[] | null) => any) | undefined;
|
|
15
15
|
}>, {
|
|
16
16
|
title: string;
|
|
17
|
+
upload: MediaUploadOptions;
|
|
17
18
|
accept: readonly string[] | string;
|
|
18
19
|
max: number;
|
|
19
20
|
selected: readonly string[];
|
|
20
21
|
maxSize: number | null;
|
|
21
|
-
upload: MediaUploadOptions;
|
|
22
22
|
onUploaded: (assetId: string, sessionId: string | null) => void;
|
|
23
23
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
24
|
declare const _default: typeof __VLS_export;
|
|
@@ -14,11 +14,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
14
14
|
onClose?: ((ids: string[] | null) => any) | undefined;
|
|
15
15
|
}>, {
|
|
16
16
|
title: string;
|
|
17
|
+
upload: MediaUploadOptions;
|
|
17
18
|
accept: readonly string[] | string;
|
|
18
19
|
max: number;
|
|
19
20
|
selected: readonly string[];
|
|
20
21
|
maxSize: number | null;
|
|
21
|
-
upload: MediaUploadOptions;
|
|
22
22
|
onUploaded: (assetId: string, sessionId: string | null) => void;
|
|
23
23
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
24
|
declare const _default: typeof __VLS_export;
|
|
@@ -24,11 +24,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
24
24
|
onDone?: ((ids: string[]) => any) | undefined;
|
|
25
25
|
}>, {
|
|
26
26
|
title: string;
|
|
27
|
+
upload: MediaUploadOptions;
|
|
27
28
|
accept: readonly string[] | string;
|
|
28
29
|
max: number;
|
|
29
30
|
selected: readonly string[];
|
|
30
31
|
maxSize: number | null;
|
|
31
|
-
upload: MediaUploadOptions;
|
|
32
32
|
onUploaded: (assetId: string, sessionId: string | null) => void;
|
|
33
33
|
bodyClass: string;
|
|
34
34
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -24,11 +24,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
24
24
|
onDone?: ((ids: string[]) => any) | undefined;
|
|
25
25
|
}>, {
|
|
26
26
|
title: string;
|
|
27
|
+
upload: MediaUploadOptions;
|
|
27
28
|
accept: readonly string[] | string;
|
|
28
29
|
max: number;
|
|
29
30
|
selected: readonly string[];
|
|
30
31
|
maxSize: number | null;
|
|
31
|
-
upload: MediaUploadOptions;
|
|
32
32
|
onUploaded: (assetId: string, sessionId: string | null) => void;
|
|
33
33
|
bodyClass: string;
|
|
34
34
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MediaUploadOptions } from '../../composables/useMLMediaUpload.js';
|
|
2
|
+
import { type MediaFileMeta } from '../../composables/useMLMediaMeta.js';
|
|
2
3
|
export interface MediaGalleryRole {
|
|
3
4
|
/** Shown on chips, badges and menus, e.g. "Cover". */
|
|
4
5
|
label: string;
|
|
@@ -35,10 +36,12 @@ type __VLS_Props = {
|
|
|
35
36
|
/** Upload profile for the single list (with `profiles`, each role has its own). */
|
|
36
37
|
profile?: string | null;
|
|
37
38
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
39
|
+
* The backend registered under `mediaLibrary.backends` that uploads go to
|
|
40
|
+
* (e.g. "videos"). Its `claim` route attaches files whose page was left
|
|
41
|
+
* before they finished. Each role's profile is sent with the start request. Wins over `service`.
|
|
41
42
|
*/
|
|
43
|
+
backend?: string | null;
|
|
44
|
+
/** Older name for `backend`; also takes an inline `{ start }`. */
|
|
42
45
|
service?: MediaUploadOptions['service'];
|
|
43
46
|
/** Extra fields for the service's start request, e.g. `{ context: { lessonId } }`. */
|
|
44
47
|
serviceBody?: Record<string, unknown> | null;
|
|
@@ -50,34 +53,44 @@ type __VLS_Props = {
|
|
|
50
53
|
type __VLS_ModelProps = {
|
|
51
54
|
modelValue?: string[];
|
|
52
55
|
'alt'?: Record<string, string> | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Meta of every file in the gallery (all roles), by asset ID: name,
|
|
58
|
+
* extension, MIME type, kind, size, duration, dimensions. Output only.
|
|
59
|
+
*/
|
|
60
|
+
'meta'?: Record<string, MediaFileMeta>;
|
|
53
61
|
};
|
|
54
62
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
55
63
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
56
64
|
uploaded: (assetId: string, role: string, sessionId: string | null) => any;
|
|
65
|
+
meta: (assetId: string, meta: MediaFileMeta | null) => any;
|
|
57
66
|
"update:modelValue": (value: string[]) => any;
|
|
58
67
|
"update:alt": (value: Record<string, string> | undefined) => any;
|
|
68
|
+
"update:meta": (value: Record<string, MediaFileMeta>) => any;
|
|
59
69
|
"update:profiles": (profiles: MediaGalleryProfiles) => any;
|
|
60
70
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
61
71
|
onUploaded?: ((assetId: string, role: string, sessionId: string | null) => any) | undefined;
|
|
72
|
+
onMeta?: ((assetId: string, meta: MediaFileMeta | null) => any) | undefined;
|
|
62
73
|
"onUpdate:modelValue"?: ((value: string[]) => any) | undefined;
|
|
63
74
|
"onUpdate:alt"?: ((value: Record<string, string> | undefined) => any) | undefined;
|
|
75
|
+
"onUpdate:meta"?: ((value: Record<string, MediaFileMeta>) => any) | undefined;
|
|
64
76
|
"onUpdate:profiles"?: ((profiles: MediaGalleryProfiles) => any) | undefined;
|
|
65
77
|
}>, {
|
|
66
|
-
visibility: "private" | "restricted" | "public";
|
|
67
78
|
title: string;
|
|
79
|
+
visibility: "private" | "public" | "restricted";
|
|
68
80
|
disabled: boolean;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
backend: string | null;
|
|
82
|
+
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
83
|
+
profile: string | null;
|
|
72
84
|
processingConfig: {
|
|
73
85
|
stepOptions?: Record<string, Record<string, unknown>> | null;
|
|
74
86
|
useFilenameAsAssetId?: boolean;
|
|
75
87
|
enableTranscription?: boolean;
|
|
76
88
|
};
|
|
77
89
|
collectionName: string | null;
|
|
78
|
-
profile: string | null;
|
|
79
|
-
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
80
90
|
serviceBody: Record<string, unknown> | null;
|
|
91
|
+
accept: readonly string[] | string;
|
|
92
|
+
max: number | null;
|
|
93
|
+
maxSize: number | null;
|
|
81
94
|
hint: string;
|
|
82
95
|
picker: boolean;
|
|
83
96
|
onPick: () => void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, ref, watch } from "vue";
|
|
3
3
|
import { $fetch } from "ofetch";
|
|
4
|
+
import { watchMediaMeta } from "../../composables/useMLMediaMeta";
|
|
4
5
|
import { kindForFile } from "../../utils/fileAccept";
|
|
5
6
|
import { GALLERY_TILE_TYPE } from "../../utils/galleryDrag";
|
|
6
7
|
import GallerySection from "./GallerySection.vue";
|
|
@@ -19,6 +20,7 @@ const props = defineProps({
|
|
|
19
20
|
processingConfig: { type: Object, required: false, default: void 0 },
|
|
20
21
|
collectionName: { type: [String, null], required: false, default: null },
|
|
21
22
|
profile: { type: [String, null], required: false, default: null },
|
|
23
|
+
backend: { type: [String, null], required: false, default: null },
|
|
22
24
|
service: { type: null, required: false, default: void 0 },
|
|
23
25
|
serviceBody: { type: [Object, null], required: false, default: null },
|
|
24
26
|
picker: { type: Boolean, required: false, default: true },
|
|
@@ -28,7 +30,12 @@ const props = defineProps({
|
|
|
28
30
|
});
|
|
29
31
|
const model = defineModel({ type: Array, ...{ default: () => [] } });
|
|
30
32
|
const alt = defineModel("alt", { type: null, ...{ default: void 0 } });
|
|
31
|
-
const
|
|
33
|
+
const meta = defineModel("meta", { type: Object, ...{ default: () => ({}) } });
|
|
34
|
+
const emit = defineEmits(["uploaded", "update:profiles", "meta"]);
|
|
35
|
+
const sectionMeta = ref({});
|
|
36
|
+
watchMediaMeta(computed(() => sectionMeta.value), (all) => {
|
|
37
|
+
meta.value = all;
|
|
38
|
+
}, (id, m) => emit("meta", id, m));
|
|
32
39
|
const roles = computed(() => Object.entries(props.profiles ?? {}).map(([key, r]) => ({ key, ...r })));
|
|
33
40
|
const hasRoles = computed(() => roles.value.length > 0);
|
|
34
41
|
const single = (r) => r.max === 1;
|
|
@@ -275,6 +282,7 @@ function chipCount(key, max) {
|
|
|
275
282
|
:profile="defaultRole?.profile ?? null"
|
|
276
283
|
:profile-for="profileFor"
|
|
277
284
|
:on-route-files="routeFiles"
|
|
285
|
+
:backend="backend"
|
|
278
286
|
:service="service"
|
|
279
287
|
:service-body="serviceBody"
|
|
280
288
|
:disabled="disabled"
|
|
@@ -294,6 +302,7 @@ function chipCount(key, max) {
|
|
|
294
302
|
:on-add="onAdd"
|
|
295
303
|
@update:model-value="onUnionUpdate"
|
|
296
304
|
@uploaded="onUploaded"
|
|
305
|
+
@meta="(m) => sectionMeta = m"
|
|
297
306
|
>
|
|
298
307
|
<template #toolbar>
|
|
299
308
|
<div class="flex flex-col gap-1.5">
|
|
@@ -353,6 +362,7 @@ function chipCount(key, max) {
|
|
|
353
362
|
:processing-config="processingConfig"
|
|
354
363
|
:collection-name="collectionName"
|
|
355
364
|
:profile="profile"
|
|
365
|
+
:backend="backend"
|
|
356
366
|
:service="service"
|
|
357
367
|
:service-body="serviceBody"
|
|
358
368
|
:picker="picker"
|
|
@@ -360,5 +370,6 @@ function chipCount(key, max) {
|
|
|
360
370
|
:editor="editor"
|
|
361
371
|
:on-open="onOpen"
|
|
362
372
|
@uploaded="(id, sessionId) => emit('uploaded', id, '', sessionId)"
|
|
373
|
+
@meta="(m) => sectionMeta = m"
|
|
363
374
|
/>
|
|
364
375
|
</template>
|