@adonis-agora/media 0.11.0 → 0.12.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @adonis-agora/media
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#46](https://github.com/DavideCarvalho/adonis-agora-media/pull/46) [`e2baf99`](https://github.com/DavideCarvalho/adonis-agora-media/commit/e2baf993dae1a7d3574128085abe1cf48087a023) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - **Behaviour change — omitting `disk` now really uses Drive's default disk.** Read this if your `config/media.ts` does not set `disk`.
8
+
9
+ The documented fallback ("omit `disk` and the provider uses the `default` of your `config/drive.ts`") had never worked in any environment. `MediaProvider` read it as `app.config.get('drive').default`, but `app.config.get('drive')` returns the value your config file exported — Drive's `defineConfig()` returns an **unresolved `ConfigProvider`** (`{ type: 'provider', resolver }`), so `.default` was always `undefined` and the fallback always returned the literal string `'default'`. (Reading `.config.default` instead does not help either: `{ config: { default, fakes, services } }` is the shape the provider's resolver _returns_, never the shape of the provider itself.) The provider now resolves the config provider — the same thing `@adonisjs/drive`'s own provider does before it builds its manager — and reads the default disk name off the resolved config. Drive's `DriveManager` cannot be asked instead: it keeps that config in a `#private` field and exposes no accessor.
10
+
11
+ **Am I affected?** Look at the `default` key of your `config/drive.ts`:
12
+
13
+ - `default: 'default'` (or you set `disk` in `config/media.ts`, or you have no Drive config at all) → nothing changes.
14
+ - `default` is anything else _and_ your `services` map has **no** disk named `default` → media was hard-broken for you (every disk operation threw inside flydrive, because it was asked for a service named `'default'` that does not exist). This release fixes it; there is nothing to migrate.
15
+ - `default` is anything else _and_ your `services` map **does** have a disk named `default` → **this moves where your files are written and read.** New objects now go to your real Drive default (e.g. `s3`) instead of the disk named `default`.
16
+
17
+ **Migration.** Objects already written under the `'default'` disk will not be found on the new disk — nothing copies them. Media records persist their own `disk`, so rows attached before this upgrade keep resolving to `'default'` and stay readable as long as that disk still exists in `config/drive.ts`; anything that recomputes a location from the _current_ default (or any object you wrote outside the media record store) will look on the new disk and miss. To keep the old behaviour exactly, pin it explicitly:
18
+
19
+ ```ts
20
+ // config/media.ts
21
+ export default defineConfig({
22
+ disk: "default", // was implicit before this release
23
+ });
24
+ ```
25
+
26
+ Otherwise, move the existing objects to your real default disk (or point that Drive service at the same bucket/location) before upgrading.
27
+
28
+ This is a minor, not a major: the package is pre-1.0, where minor is the breaking-change channel, and a `1.0.0` would signal an API stability this package has not declared. The affected surface is narrow (a host that both omits `disk` and keeps a Drive disk literally named `default` alongside a different Drive `default`), the alternative for most affected hosts was a crash, and pinning the old behaviour is a one-line config change.
29
+
3
30
  ## 0.11.0
4
31
 
5
32
  ### Minor Changes
package/README.md CHANGED
@@ -4,12 +4,16 @@ Media-library for **AdonisJS**, part of the [Agora](https://github.com/DavideCar
4
4
  spatie/laravel-media-library feel, on top of [`@adonisjs/drive`](https://github.com/adonisjs/drive).
5
5
 
6
6
  Attach files to entities, organize them into collections (MIME whitelist, single-file replace,
7
- ordering), and generate image conversions (eager or lazy). Storage is delegated entirely to Drive, so
8
- you reuse your existing `local` / `s3` / `gcs` disks — this package never reimplements disk drivers.
7
+ ordering), and generate image conversions (eager or lazy) — plus **content transformers**, the same
8
+ seam driven beyond images: the bundled `transformers.hls()` packages a video into an HLS ladder and
9
+ `transformers.probe()` records duration/codecs as metadata, both persisted into the same
10
+ `record.conversions` map. Storage is delegated entirely to Drive, so you reuse your existing
11
+ `local` / `s3` / `gcs` disks — this package never reimplements disk drivers.
9
12
 
10
13
  This is the **core library**: `MediaLibrary`, `AttachmentManager`, the `MediaStore` SPI (in-memory +
11
- Lucid), the `ImageProcessor` SPI (sharp), the bundled `disks.s3()` driver, proxy/direct/resumable
12
- uploads, configurable delivery, the provider + `defineConfig`, and a testing kit. The management
14
+ Lucid), the `ImageProcessor` SPI (sharp), the `Transformer` SPI (HLS video packaging + metadata
15
+ probe), the bundled `disks.s3()` driver, proxy/direct/resumable uploads, configurable delivery, the
16
+ provider + `defineConfig`, and a testing kit. The management
13
17
  **console ships embedded** — registering `@adonis-agora/media/dashboard_provider` is all a consuming
14
18
  app needs (see [Dashboard](#dashboard) below); no separate install. Pair it with
15
19
  [`@adonis-agora/media-react`](https://github.com/DavideCarvalho/adonis-media/tree/master/packages/react)
@@ -22,11 +26,12 @@ The package uses **subpath exports** (the Agora idiom), so heavy backends stay o
22
26
  | Subpath | What |
23
27
  |---|---|
24
28
  | `@adonis-agora/media` | barrel — `defineConfig`, `stores`, `processors`, `disks`, `uploadSessions`, `MediaManager`, `MediaLibrary`, `AttachmentManager`, `UploadManager`, `ResumableUploadManager`, `MediaDeliveryHandler`, SPIs, errors |
29
+ | `@adonis-agora/media/services/main` | the app-bound `MediaManager` singleton — `import media from '@adonis-agora/media/services/main'`, importable at any time (it resolves lazily on first use) |
25
30
  | `@adonis-agora/media/media_provider` | the service provider (binds `MediaManager`, mounts optional upload/TUS routes) |
26
31
  | `@adonis-agora/media/dashboard_provider` | the embedded management-console provider (React SPA + JSON API) |
27
32
  | `@adonis-agora/media/dashboard` | `defineConfig` + types for `config/media_dashboard.ts`, `DashboardService`, the session-auth helpers, `ObjectInsightProvider` |
28
33
  | `@adonis-agora/media/configure` | `node ace configure` hook |
29
- | `@adonis-agora/media/single-file` | `storeSingleFile` / `removeSingleFile` / `isSingleFileStoreAvailable` — lets other packages delegate single-file uploads (e.g. avatars) to media without a hard dependency |
34
+ | `@adonis-agora/media/single-file` | `storeSingleFile` / `removeSingleFile` / `storeSingleFileWith` / `removeSingleFileWith` / `isSingleFileStoreAvailable` — lets other packages delegate single-file uploads (e.g. avatars) to media without a hard dependency |
30
35
  | `@adonis-agora/media/stores/lucid` | the Lucid `MediaStore` (`@adonisjs/lucid` peer) |
31
36
  | `@adonis-agora/media/upload_sessions/lucid` | the Lucid resumable `UploadSessionStore` (`@adonisjs/lucid` peer) |
32
37
  | `@adonis-agora/media/disks/s3` | the bundled `S3Disk` (`@aws-sdk/client-s3` peer; presigning is hand-rolled SigV4) |
@@ -44,9 +49,13 @@ node ace configure @adonis-agora/media
44
49
  ```
45
50
 
46
51
  `configure` registers the media provider **and the dashboard provider**, publishes `config/media.ts`
47
- and `config/media_dashboard.ts`, and publishes the `media` table migration (delete it if you only use
48
- the in-memory store; otherwise `node ace migration:run`). The management console is live at
49
- `/media/dashboard` the moment your app boots — see [Dashboard](#dashboard).
52
+ and `config/media_dashboard.ts`, and publishes **two** migrations — the `media` table for the `lucid`
53
+ store and the session table for the `lucid` upload-session store. Delete the ones you don't need (the
54
+ in-memory equivalents need no table); otherwise `node ace migration:run`. The management console is
55
+ live at `/media/dashboard` the moment your app boots — see [Dashboard](#dashboard).
56
+
57
+ Required peers: `@adonisjs/core` (`^7.3.0`) and `@adonisjs/drive` — **either major**, `^3.0.0` or
58
+ `^4.0.0`.
50
59
 
51
60
  Optional peers, loaded lazily only when selected:
52
61
 
@@ -141,6 +150,50 @@ A `ConversionPreset` is `{ name, width?, height?, fit?, format?, quality?, eager
141
150
  generated synchronously on `attach`; the rest are generated lazily on the first `url(id, name)` and
142
151
  cached on the record. `fit` and `format` map straight onto sharp.
143
152
 
153
+ ## Transformers and HLS video
154
+
155
+ Image conversions answer "same image, different size". **Transformers** answer everything else:
156
+ *transform this content into that*. A transformer takes a stored media's bytes and produces many
157
+ files (an HLS package), one file (an extracted audio track), or none at all (a metadata probe) —
158
+ persisted into the same `record.conversions` map, so presets and transformers share one namespace
159
+ per collection.
160
+
161
+ Two ship with the package, both over the optional `mediabunny` peer (pure TypeScript — **no ffmpeg
162
+ binary**), imported lazily inside the first `transform()` call:
163
+
164
+ ```ts
165
+ // config/media.ts
166
+ import { defineConfig, transformers } from '@adonis-agora/media'
167
+
168
+ export default defineConfig({
169
+ collections: [
170
+ {
171
+ name: 'videos',
172
+ acceptsMimeTypes: ['video/mp4'],
173
+ transformers: [
174
+ transformers.hls({ targetDuration: 4 }), // MPEG-TS segments behind a master playlist
175
+ transformers.probe({ eager: true }), // duration / resolution / codecs, no artifact
176
+ ],
177
+ },
178
+ ],
179
+ })
180
+ ```
181
+
182
+ Transformations are assumed heavy, so nothing runs on attach unless you mark it `eager`. Trigger one
183
+ from your own job — `transform(id, name)` is idempotent, so a retry skips straight through:
184
+
185
+ ```ts
186
+ await media.library.transform(mediaId, 'hls')
187
+ ```
188
+
189
+ Serving the result is `HlsDeliveryHandler`: playlists come back with every reference rewritten (each
190
+ sub-playlist hop through your own route, so your auth stays in the path), and segments as presigned
191
+ URLs or streamed bytes. Like every handler here it performs **no authorization** — you mount the
192
+ route and guard it. Writing your own transformer is one interface (`name` + `transform(context)`);
193
+ the context hands you the record, the original's bytes/stream, and a sandboxed `write()` that keys
194
+ every artifact under the conversion's own prefix.
195
+
196
+
144
197
  ## Uploads, S3 and delivery
145
198
 
146
199
  Large-file uploads ship in three flavours — **proxy** (bytes stream through your app), **direct-S3
@@ -209,7 +262,8 @@ that already register it directly. Don't register both providers in the same app
209
262
 
210
263
  ## Diagnostics
211
264
 
212
- Lifecycle events (`attach`, `delete`, `conversion`, `attachment.create`, `attachment.delete`) are
265
+ All nine lifecycle events — `attach`, `delete`, `conversion`, `attachment.create`,
266
+ `attachment.delete`, `upload.start`, `upload.progress`, `upload.complete`, `upload.abort` — are
213
267
  emitted on the `agora:media:*` channel via the `@agora/diagnostics:emit` global slot — read
214
268
  structurally, with **no hard dependency** on `@adonis-agora/diagnostics`. When that package isn't
215
269
  installed, emitting is an inert no-op; when it is, the Telescope generic watcher (or