includio-cms 0.13.3 → 0.14.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 +53 -0
- package/ROADMAP.md +19 -2
- package/dist/admin/api/handler.js +6 -0
- package/dist/admin/api/media-gc.js +19 -3
- package/dist/admin/api/regenerate-posters.d.ts +2 -0
- package/dist/admin/api/regenerate-posters.js +32 -0
- package/dist/admin/api/system-info.d.ts +2 -0
- package/dist/admin/api/system-info.js +9 -0
- package/dist/admin/api/transcode-videos.d.ts +2 -0
- package/dist/admin/api/transcode-videos.js +32 -0
- package/dist/admin/client/maintenance/maintenance-page.svelte +554 -3
- package/dist/core/fields/structuredToHtml.js +20 -1
- package/dist/core/server/fields/resolveImageFields.js +37 -2
- package/dist/core/server/media/operations/batchRegenerateVideoPosters.d.ts +15 -0
- package/dist/core/server/media/operations/batchRegenerateVideoPosters.js +112 -0
- package/dist/core/server/media/operations/batchTranscodeVideos.d.ts +23 -0
- package/dist/core/server/media/operations/batchTranscodeVideos.js +104 -0
- package/dist/core/server/media/operations/deleteMediaFile.js +16 -1
- package/dist/core/server/media/operations/getDiskUsage.d.ts +24 -0
- package/dist/core/server/media/operations/getDiskUsage.js +103 -0
- package/dist/core/server/media/operations/getSystemInfo.d.ts +27 -0
- package/dist/core/server/media/operations/getSystemInfo.js +90 -0
- package/dist/core/server/media/operations/replaceFile.js +10 -0
- package/dist/core/server/media/operations/uploadFile.js +2 -0
- package/dist/core/server/media/styles/ffmpeg/generateVideoStyle.d.ts +7 -0
- package/dist/core/server/media/styles/ffmpeg/generateVideoStyle.js +59 -0
- package/dist/core/server/media/styles/operations/generateDefaultVideoStyles.d.ts +9 -0
- package/dist/core/server/media/styles/operations/generateDefaultVideoStyles.js +88 -0
- package/dist/db-postgres/index.js +113 -0
- package/dist/db-postgres/schema/index.d.ts +1 -0
- package/dist/db-postgres/schema/index.js +1 -0
- package/dist/db-postgres/schema/videoStyle.d.ts +228 -0
- package/dist/db-postgres/schema/videoStyle.js +18 -0
- package/dist/files-local/index.d.ts +6 -1
- package/dist/files-local/index.js +7 -141
- package/dist/files-local/sanitizeFilename.js +2 -1
- package/dist/files-local/transcode.d.ts +27 -0
- package/dist/files-local/transcode.js +130 -0
- package/dist/files-local/video.d.ts +10 -0
- package/dist/files-local/video.js +167 -0
- package/dist/paraglide/messages/_index.d.ts +36 -3
- package/dist/paraglide/messages/_index.js +71 -3
- package/dist/paraglide/messages/en.d.ts +5 -0
- package/dist/paraglide/messages/en.js +14 -0
- package/dist/paraglide/messages/pl.d.ts +5 -0
- package/dist/paraglide/messages/pl.js +14 -0
- package/dist/sveltekit/components/video.svelte +15 -1
- package/dist/sveltekit/utils/media.js +2 -2
- package/dist/types/adapters/db.d.ts +37 -1
- package/dist/types/cms.d.ts +16 -0
- package/dist/types/fields.d.ts +12 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/media.d.ts +15 -0
- package/dist/updates/0.13.4/index.d.ts +2 -0
- package/dist/updates/0.13.4/index.js +14 -0
- package/dist/updates/0.14.0/index.d.ts +2 -0
- package/dist/updates/0.14.0/index.js +36 -0
- package/dist/updates/index.js +3 -1
- package/package.json +1 -1
- package/dist/paraglide/messages/hello_world.d.ts +0 -5
- package/dist/paraglide/messages/hello_world.js +0 -33
- package/dist/paraglide/messages/login_hello.d.ts +0 -16
- package/dist/paraglide/messages/login_hello.js +0 -34
- package/dist/paraglide/messages/login_please_login.d.ts +0 -16
- package/dist/paraglide/messages/login_please_login.js +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,59 @@
|
|
|
3
3
|
All notable changes to includio-cms are documented here.
|
|
4
4
|
Generated from `src/lib/updates/` — do not edit manually.
|
|
5
5
|
|
|
6
|
+
## 0.14.0 — 2026-03-24
|
|
7
|
+
|
|
8
|
+
Video transcoding & optimization
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Auto-transcode uploaded videos to mp4 (h264) and webm (vp9) in background
|
|
12
|
+
- Video styles system with status tracking (pending/processing/done/failed)
|
|
13
|
+
- Admin maintenance: video transcoding card with batch transcode, purge & retranscode, SSE progress
|
|
14
|
+
- Configurable video transcoding: formats, max resolution, CRF quality, concurrency
|
|
15
|
+
- Skip logic: skip already-optimized mp4 h264 at 1080p or below, skip files over 500MB
|
|
16
|
+
- Frontend video serves multiple source elements (webm, mp4, original fallback)
|
|
17
|
+
- System info endpoint: CMS version, Node, PostgreSQL, ffmpeg (codec checks), sharp, OS diagnostics
|
|
18
|
+
- Disk usage endpoint: breakdown per category (originals by type, image styles, video styles, posters)
|
|
19
|
+
|
|
20
|
+
### Migration
|
|
21
|
+
|
|
22
|
+
```sql
|
|
23
|
+
CREATE TABLE IF NOT EXISTS video_styles (
|
|
24
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
25
|
+
media_file_id UUID NOT NULL REFERENCES media_file(id) ON DELETE CASCADE,
|
|
26
|
+
name TEXT NOT NULL,
|
|
27
|
+
url TEXT NOT NULL,
|
|
28
|
+
width INTEGER,
|
|
29
|
+
height INTEGER,
|
|
30
|
+
format TEXT NOT NULL,
|
|
31
|
+
codec TEXT,
|
|
32
|
+
file_size INTEGER,
|
|
33
|
+
mime_type TEXT NOT NULL,
|
|
34
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
35
|
+
error TEXT,
|
|
36
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE UNIQUE INDEX IF NOT EXISTS video_styles_unique_key
|
|
40
|
+
ON video_styles (media_file_id, name);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Notes
|
|
44
|
+
|
|
45
|
+
Video transcoding requires ffmpeg with libx264 and libvpx-vp9 codecs. Graceful degradation if ffmpeg is unavailable.
|
|
46
|
+
|
|
47
|
+
## 0.13.4 — 2026-03-24
|
|
48
|
+
|
|
49
|
+
Video poster regeneration, filename sanitization
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
- Batch regenerate video posters & thumbnails from admin maintenance page with SSE progress
|
|
53
|
+
- Video poster status reporting in media GC endpoint
|
|
54
|
+
- Extracted video processing to reusable module
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
- Filename sanitization normalizes Unicode to NFC (prevents NFD/NFC mismatch)
|
|
58
|
+
|
|
6
59
|
## 0.13.3 — 2026-03-23
|
|
7
60
|
|
|
8
61
|
Security hardening, per-language content fixes
|
package/ROADMAP.md
CHANGED
|
@@ -239,14 +239,31 @@
|
|
|
239
239
|
- [x] `[fix]` `[P1]` CMS constructor validates non-empty languages array <!-- files: src/lib/core/cms.ts -->
|
|
240
240
|
- [x] `[fix]` `[P2]` Unhandled promise rejection handlers in admin components
|
|
241
241
|
|
|
242
|
-
## 0.
|
|
242
|
+
## 0.13.4 — Video poster regeneration
|
|
243
|
+
|
|
244
|
+
- [x] `[feature]` `[P1]` Batch video poster regeneration — admin maintenance page with SSE progress <!-- files: src/lib/admin/api/regenerate-posters.ts, src/lib/core/server/media/operations/batchRegenerateVideoPosters.ts, src/lib/admin/client/maintenance/maintenance-page.svelte -->
|
|
245
|
+
- [x] `[feature]` `[P1]` Video poster status reporting in media GC endpoint <!-- files: src/lib/admin/api/media-gc.ts -->
|
|
246
|
+
- [x] `[chore]` `[P1]` Extracted video processing to reusable module <!-- files: src/lib/files-local/video.ts, src/lib/files-local/index.ts -->
|
|
247
|
+
- [x] `[fix]` `[P1]` Filename sanitization normalizes Unicode to NFC <!-- files: src/lib/files-local/sanitizeFilename.ts -->
|
|
248
|
+
|
|
249
|
+
## 0.14.0 — Video transcoding
|
|
250
|
+
|
|
251
|
+
- [x] `[feature]` `[P1]` Video styles system — auto-transcode to mp4 h264 + webm vp9 <!-- files: src/lib/db-postgres/schema/videoStyle.ts, src/lib/files-local/transcode.ts -->
|
|
252
|
+
- [x] `[feature]` `[P1]` Background transcoding pipeline on upload with skip logic <!-- files: src/lib/core/server/media/styles/operations/generateDefaultVideoStyles.ts -->
|
|
253
|
+
- [x] `[feature]` `[P1]` Admin maintenance: video transcoding card (batch, purge, SSE progress) <!-- files: src/lib/admin/client/maintenance/maintenance-page.svelte -->
|
|
254
|
+
- [x] `[feature]` `[P2]` Frontend multi-source video delivery (webm, mp4, original) <!-- files: src/lib/sveltekit/components/video.svelte -->
|
|
255
|
+
- [x] `[feature]` `[P2]` Configurable video transcoding in MediaConfig <!-- files: src/lib/types/cms.ts -->
|
|
256
|
+
- [x] `[feature]` `[P2]` System info endpoint — CMS version, Node, PostgreSQL, ffmpeg, sharp, OS <!-- files: src/lib/admin/api/system-info.ts, src/lib/core/server/media/operations/getSystemInfo.ts -->
|
|
257
|
+
- [x] `[feature]` `[P2]` Disk usage endpoint — breakdown per category (originals, image styles, video styles, posters) <!-- files: src/lib/admin/api/system-info.ts, src/lib/core/server/media/operations/getDiskUsage.ts -->
|
|
258
|
+
|
|
259
|
+
## 0.15.0 — SEO module
|
|
243
260
|
|
|
244
261
|
- [ ] `[feature]` `[P1]` SERP preview + character limits for title/description <!-- files: src/lib/admin/components/fields/seo-field.svelte -->
|
|
245
262
|
- [ ] `[feature]` `[P1]` Global SEO settings
|
|
246
263
|
- [ ] `[feature]` `[P1]` Dedicated frontend SEO components <!-- files: src/lib/sveltekit/components/seo.svelte -->
|
|
247
264
|
- [ ] `[feature]` `[P2]` Sitemap generation
|
|
248
265
|
|
|
249
|
-
## 0.
|
|
266
|
+
## 0.16.0 — WCAG/ATAG compliance
|
|
250
267
|
|
|
251
268
|
- [ ] `[chore]` `[P0]` Full WCAG/ATAG audit
|
|
252
269
|
- [ ] `[feature]` `[P0]` Accessibility rework based on audit findings
|
|
@@ -6,7 +6,10 @@ import * as inviteHandlers from './invite.js';
|
|
|
6
6
|
import * as acceptInviteHandlers from './accept-invite.js';
|
|
7
7
|
import * as mediaGcHandlers from './media-gc.js';
|
|
8
8
|
import * as generateStylesHandlers from './generate-styles.js';
|
|
9
|
+
import * as regeneratePostersHandlers from './regenerate-posters.js';
|
|
10
|
+
import * as transcodeVideosHandlers from './transcode-videos.js';
|
|
9
11
|
import * as uploadLimitHandlers from './upload-limit.js';
|
|
12
|
+
import * as systemInfoHandlers from './system-info.js';
|
|
10
13
|
import { requireAuth } from '../remote/middleware/auth.js';
|
|
11
14
|
import { getCMS } from '../../core/cms.js';
|
|
12
15
|
import { lookup } from 'mrmime';
|
|
@@ -19,7 +22,10 @@ export function createAdminApiHandler(options) {
|
|
|
19
22
|
'accept-invite': acceptInviteHandlers,
|
|
20
23
|
'media-gc': mediaGcHandlers,
|
|
21
24
|
'generate-styles': generateStylesHandlers,
|
|
25
|
+
'regenerate-posters': regeneratePostersHandlers,
|
|
26
|
+
'transcode-videos': transcodeVideosHandlers,
|
|
22
27
|
'upload-limit': uploadLimitHandlers,
|
|
28
|
+
'system-info': systemInfoHandlers,
|
|
23
29
|
...options?.extraRoutes
|
|
24
30
|
};
|
|
25
31
|
const privateMediaGet = async (event) => {
|
|
@@ -2,12 +2,16 @@ import { requireRole } from '../remote/middleware/auth.js';
|
|
|
2
2
|
import { purgeAllImageStyles } from '../../core/server/media/operations/purgeImageStyles.js';
|
|
3
3
|
import { getReconciliationReport, deleteOrphanedDiskFiles } from '../../core/server/media/operations/reconcileMedia.js';
|
|
4
4
|
import { getStylesStatus } from '../../core/server/media/styles/operations/batchGenerateStyles.js';
|
|
5
|
+
import { getVideoPosterStatus } from '../../core/server/media/operations/batchRegenerateVideoPosters.js';
|
|
6
|
+
import { getVideoTranscodeStatus, purgeVideoStyles } from '../../core/server/media/operations/batchTranscodeVideos.js';
|
|
5
7
|
import { json } from '@sveltejs/kit';
|
|
6
8
|
export const GET = async ({ url }) => {
|
|
7
9
|
requireRole('admin');
|
|
8
|
-
const [stylesStatus, report] = await Promise.all([
|
|
10
|
+
const [stylesStatus, report, videoPosterStatus, videoTranscodeStatus] = await Promise.all([
|
|
9
11
|
getStylesStatus(),
|
|
10
|
-
getReconciliationReport()
|
|
12
|
+
getReconciliationReport(),
|
|
13
|
+
getVideoPosterStatus(),
|
|
14
|
+
getVideoTranscodeStatus()
|
|
11
15
|
]);
|
|
12
16
|
return json({
|
|
13
17
|
imageStylesCount: stylesStatus.existingStyles,
|
|
@@ -15,7 +19,15 @@ export const GET = async ({ url }) => {
|
|
|
15
19
|
expectedStylesCount: stylesStatus.expectedStyles,
|
|
16
20
|
missingStylesCount: stylesStatus.missingStyles,
|
|
17
21
|
orphanedDiskFiles: report.orphanedDisk,
|
|
18
|
-
missingDiskRecords: report.missingDisk
|
|
22
|
+
missingDiskRecords: report.missingDisk,
|
|
23
|
+
videosCount: videoPosterStatus.videosCount,
|
|
24
|
+
videosWithPosters: videoPosterStatus.videosWithPosters,
|
|
25
|
+
videosMissingPosters: videoPosterStatus.videosMissingPosters,
|
|
26
|
+
videoStylesCount: videoTranscodeStatus.videoStylesCount,
|
|
27
|
+
videoStylesExpected: videoTranscodeStatus.videoStylesExpected,
|
|
28
|
+
videoStylesDone: videoTranscodeStatus.videoStylesDone,
|
|
29
|
+
videoStylesPending: videoTranscodeStatus.videoStylesPending,
|
|
30
|
+
videoStylesFailed: videoTranscodeStatus.videoStylesFailed
|
|
19
31
|
});
|
|
20
32
|
};
|
|
21
33
|
export const DELETE = async ({ url }) => {
|
|
@@ -29,5 +41,9 @@ export const DELETE = async ({ url }) => {
|
|
|
29
41
|
const result = await deleteOrphanedDiskFiles();
|
|
30
42
|
return json(result);
|
|
31
43
|
}
|
|
44
|
+
if (action === 'purge-video-styles') {
|
|
45
|
+
const result = await purgeVideoStyles();
|
|
46
|
+
return json(result);
|
|
47
|
+
}
|
|
32
48
|
return json({ error: 'Unknown action' }, { status: 400 });
|
|
33
49
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { requireRole } from '../remote/middleware/auth.js';
|
|
2
|
+
import { batchRegenerateVideoPosters } from '../../core/server/media/operations/batchRegenerateVideoPosters.js';
|
|
3
|
+
export const POST = async () => {
|
|
4
|
+
requireRole('admin');
|
|
5
|
+
const abort = new AbortController();
|
|
6
|
+
const encoder = new TextEncoder();
|
|
7
|
+
const stream = new ReadableStream({
|
|
8
|
+
async start(controller) {
|
|
9
|
+
try {
|
|
10
|
+
for await (const event of batchRegenerateVideoPosters(abort.signal)) {
|
|
11
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'error', error: e instanceof Error ? e.message : String(e) })}\n\n`));
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
controller.close();
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
cancel() {
|
|
22
|
+
abort.abort();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return new Response(stream, {
|
|
26
|
+
headers: {
|
|
27
|
+
'Content-Type': 'text/event-stream',
|
|
28
|
+
'Cache-Control': 'no-cache',
|
|
29
|
+
Connection: 'keep-alive'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { requireRole } from '../remote/middleware/auth.js';
|
|
2
|
+
import { getDiskUsage } from '../../core/server/media/operations/getDiskUsage.js';
|
|
3
|
+
import { getSystemInfo } from '../../core/server/media/operations/getSystemInfo.js';
|
|
4
|
+
import { json } from '@sveltejs/kit';
|
|
5
|
+
export const GET = async () => {
|
|
6
|
+
requireRole('admin');
|
|
7
|
+
const [diskUsage, systemInfo] = await Promise.all([getDiskUsage(), getSystemInfo()]);
|
|
8
|
+
return json({ diskUsage, systemInfo });
|
|
9
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { requireRole } from '../remote/middleware/auth.js';
|
|
2
|
+
import { batchTranscodeVideos } from '../../core/server/media/operations/batchTranscodeVideos.js';
|
|
3
|
+
export const POST = async () => {
|
|
4
|
+
requireRole('admin');
|
|
5
|
+
const abort = new AbortController();
|
|
6
|
+
const encoder = new TextEncoder();
|
|
7
|
+
const stream = new ReadableStream({
|
|
8
|
+
async start(controller) {
|
|
9
|
+
try {
|
|
10
|
+
for await (const event of batchTranscodeVideos(abort.signal)) {
|
|
11
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'error', error: e instanceof Error ? e.message : String(e) })}\n\n`));
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
controller.close();
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
cancel() {
|
|
22
|
+
abort.abort();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return new Response(stream, {
|
|
26
|
+
headers: {
|
|
27
|
+
'Content-Type': 'text/event-stream',
|
|
28
|
+
'Cache-Control': 'no-cache',
|
|
29
|
+
Connection: 'keep-alive'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
};
|