@ossy/media-tasks 1.40.2 → 3.0.1

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 CHANGED
@@ -2,14 +2,25 @@
2
2
 
3
3
  Changestream-triggered media processing tasks for the Ossy platform.
4
4
 
5
- Tasks in this package are registered with `TaskService.RegisterTask()` at API startup and fire automatically when matching events are detected in the event store change stream.
5
+ Tasks in this package are registered with `TaskService.RegisterTask()` at API startup and fire automatically when matching ADR 0008 resource events appear on the event store change stream.
6
6
 
7
7
  ## Tasks
8
8
 
9
- | Task | Trigger | Description |
10
- |------|---------|-------------|
11
- | `resize-common-web` | `Resource` `Created` (`image/*`) | Generates common web thumbnail and gallery sizes using `sharp` |
12
- | `visual-content-descriptors` | `Resource` `Created` (`image/*`, `video/*`) | Uses GPT-4o to generate title, description, tags, and alt text |
9
+ | Task id | Trigger | Description |
10
+ |---------|---------|-------------|
11
+ | `@ossy/media-tasks/tasks/resize-common-web` | `@ossy/platform/schema/file` `Created` (`content.ContentType: image/*`) | Generates common web thumbnail and gallery sizes using `sharp` |
12
+ | `@ossy/media-tasks/tasks/visual-content-descriptors` | `@ossy/platform/schema/file` `Created` (`image/*`, `video/*`) | Uses GPT-4o to generate title, description, tags, and alt text |
13
+
14
+ ## Schemas
15
+
16
+ Task outcome schemas live in `src/*.schema.js` and are auto-discovered at build time:
17
+
18
+ | Schema | Id |
19
+ |--------|----|
20
+ | Resize Common Web | `@ossy/media-tasks/schema/resize-common-web` |
21
+ | Visual Content Descriptors | `@ossy/media-tasks/schema/visual-content-descriptors` |
22
+
23
+ These describe optional task-run documents; handlers are implemented in the matching `*.task.js` files.
13
24
 
14
25
  ## Task contract
15
26
 
@@ -17,12 +28,22 @@ Each `*.task.js` file exports:
17
28
 
18
29
  ```js
19
30
  export const metadata = {
20
- id: 'my-task',
21
- triggers: [{ aggregateType, event, resource?, location? }],
31
+ id: '@ossy/media-tasks/tasks/my-task',
32
+ triggers: [{ type, event, match?, location? }],
22
33
  }
23
34
 
24
- export default async function ({ event, sdk }) { ... }
35
+ export async function run ({ event, sdk }) { ... }
25
36
  ```
26
37
 
27
- - `event` — the raw eventstore `fullDocument` that triggered this handler
38
+ - `metadata.id` — canonical task id (`@ossy/<package>/tasks/…`)
39
+ - `triggers[].type` — resource schema id to watch (e.g. `@ossy/platform/schema/file`)
40
+ - `triggers[].event` — lifecycle event name (`Created`, `Patched`, …)
41
+ - `triggers[].match` — optional field matcher on `event.payload` (e.g. `{ 'content.ContentType': 'image/*' }`)
42
+ - `event` — the raw eventstore `fullDocument` (`resourceId`, `type`, `event`, `payload`, …)
28
43
  - `sdk` — platform SDK instance (passed from API startup; `null` during local dev if unconfigured)
44
+
45
+ ## Dependencies
46
+
47
+ - **`sharp`** (peer) — image resizing in `resize-common-web`
48
+ - **`openai`** (peer) — GPT-4o vision in `visual-content-descriptors`
49
+ - **`@ossy/platform`** — `StorageClient.load` for reading uploaded file bytes (provided at runtime by the platform server)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/media-tasks",
3
- "version": "1.40.2",
3
+ "version": "3.0.1",
4
4
  "description": "Changestream-triggered media processing tasks (image resize, AI descriptors)",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -15,8 +15,8 @@
15
15
  "typecheck": "echo \"No TypeScript in media-tasks package\" && exit 0"
16
16
  },
17
17
  "dependencies": {
18
- "@ossy/observability": "^1.8.2",
19
- "@ossy/resources": "^1.12.2"
18
+ "@ossy/observability": "^3.0.1",
19
+ "@ossy/resources": "^3.0.1"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "openai": ">=4",
@@ -32,5 +32,5 @@
32
32
  ],
33
33
  "author": "Ossy <yourfriends@ossy.se> (https://ossy.se)",
34
34
  "license": "MIT",
35
- "gitHead": "2b8745d57fee8b6c08787e2755b4df489291a5cd"
35
+ "gitHead": "4a70ec216448680d2fddc57b2a8a0077489720ae"
36
36
  }
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  name: 'Resize Common Web',
3
- id: '@ossy/tasks/resize-common-web',
3
+ id: '@ossy/media-tasks/schema/resize-common-web',
4
4
  categoryName: 'Media processing',
5
5
  icon: 'attribution',
6
6
  fields: [
@@ -1,4 +1,5 @@
1
1
  import { createLogger } from '@ossy/observability'
2
+ import { StorageClient } from '@ossy/platform'
2
3
  import { GetResource } from '@ossy/resources'
3
4
 
4
5
  const log = createLogger('media-tasks')
@@ -13,47 +14,46 @@ const Sizes = [
13
14
  ]
14
15
 
15
16
  export const metadata = {
16
- id: 'resize-common-web',
17
+ id: '@ossy/media-tasks/tasks/resize-common-web',
17
18
  triggers: [
18
19
  {
19
- aggregateType: 'Resource',
20
+ type: '@ossy/platform/schema/file',
20
21
  event: 'Created',
21
- resource: { type: 'image/*' },
22
+ match: { 'content.ContentType': 'image/*' },
22
23
  },
23
24
  ],
24
25
  }
25
26
 
26
27
  export async function run ({ event, sdk }) {
27
28
  const { default: sharp } = await import('sharp')
28
- const resourceId = event.aggregateId
29
+ const resourceId = event.resourceId
29
30
 
30
- log.info(`[resize-common-web] starting for resource ${resourceId}`)
31
+ log.info(`[media-tasks/tasks/resize-common-web] starting for resource ${resourceId}`)
31
32
 
32
- const resource = await sdk.invoke(GetResource, { id: resourceId })
33
- const image = await fetch(resource.content.src).then((response) => response.arrayBuffer())
34
-
35
- log.info(`[resize-common-web] Resizing ${resourceId}`)
33
+ const resource = await sdk.invoke(GetResource, { resourceId })
34
+ const key = resource?.content?.Key
35
+ if (!key) {
36
+ throw new Error(`[media-tasks/tasks/resize-common-web] missing storage key for ${resourceId}`)
37
+ }
36
38
 
37
- for (const { width, height, name } of Sizes) {
38
- const imageBuffer = await sharp(image).resize(width, height).toBuffer()
39
+ const { buffer: sourceBuffer, exists } = await StorageClient.load(key)
40
+ if (!exists || !sourceBuffer) {
41
+ throw new Error(`[media-tasks/tasks/resize-common-web] missing file bytes for ${resourceId}`)
42
+ }
39
43
 
40
- log.info(`[resize-common-web] Created image buffer for ${name}`)
44
+ log.info(`[media-tasks/tasks/resize-common-web] Resizing ${resourceId}`)
41
45
 
42
- const file = new File([new Uint8Array(imageBuffer)], name, { type: resource.type })
46
+ for (const { width, height, name } of Sizes) {
47
+ const resizedBuffer = await sharp(sourceBuffer).resize(width, height).toBuffer()
48
+ const mime = resource.content?.ContentType ?? 'image/jpeg'
49
+ const file = new File([new Uint8Array(resizedBuffer)], name, { type: mime })
43
50
 
44
- log.info(`[resize-common-web] Uploading ${name} for ${resourceId}`)
51
+ log.info(`[media-tasks/tasks/resize-common-web] Uploading ${name} for ${resourceId}`)
45
52
  try {
46
- // TODO: remove named version concept
47
- await sdk.resources.uploadNamedVersion({
48
- id: resourceId,
49
- name: name,
50
- file: file,
51
- })
53
+ await sdk.resources.uploadNamedVersion({ id: resourceId, name, file })
52
54
  } catch (error) {
53
- log.error(`[resize-common-web] Error uploading ${name} for ${resourceId}`, undefined, error)
55
+ log.error(`[media-tasks/tasks/resize-common-web] Error uploading ${name} for ${resourceId}`, undefined, error)
54
56
  throw error
55
57
  }
56
-
57
- log.info(`[resize-common-web] Uploaded ${name} for ${resourceId}`)
58
58
  }
59
59
  }
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  name: 'Visual Content Descriptors',
3
- id: '@ossy/tasks/visual-content-descriptors',
3
+ id: '@ossy/media-tasks/schema/visual-content-descriptors',
4
4
  categoryName: 'Media processing',
5
5
  icon: 'attribution',
6
6
  fields: [
@@ -2,25 +2,25 @@ import { GetResource, UpdateResourceName, UpdateResourceContent } from '@ossy/re
2
2
  import { OpenAi } from './openai.integration.js'
3
3
 
4
4
  export const metadata = {
5
- id: 'visual-content-descriptors',
5
+ id: '@ossy/media-tasks/tasks/visual-content-descriptors',
6
6
  triggers: [
7
7
  {
8
- aggregateType: 'Resource',
8
+ type: '@ossy/platform/schema/file',
9
9
  event: 'Created',
10
- resource: { type: 'image/*' },
10
+ match: { 'content.ContentType': 'image/*' },
11
11
  },
12
12
  {
13
- aggregateType: 'Resource',
13
+ type: '@ossy/platform/schema/file',
14
14
  event: 'Created',
15
- resource: { type: 'video/*' },
15
+ match: { 'content.ContentType': 'video/*' },
16
16
  },
17
17
  ],
18
18
  }
19
19
 
20
20
  export async function run ({ event, sdk }) {
21
- const resourceId = event.aggregateId
21
+ const resourceId = event.resourceId
22
22
 
23
- const resource = await sdk.invoke(GetResource, { id: resourceId })
23
+ const resource = await sdk.invoke(GetResource, { resourceId })
24
24
  const visualContentDescriptors = await OpenAi.getVisualContentDescriptors(resource.content.src)
25
25
 
26
26
  await sdk.invoke(UpdateResourceName, { id: resource.id, name: visualContentDescriptors.title })