@fuzionx/framework 0.1.20 → 0.1.21

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.
@@ -323,8 +323,11 @@ export default class Application {
323
323
 
324
324
  if (!this._booted) await this.boot();
325
325
 
326
- // 포트 사용 여부 확인 — 충돌 명확한 에러
327
- await this._checkPort(port);
326
+ // 포트 사용 여부 확인 — primary에서만 (워커는 SO_REUSEPORT로 공유)
327
+ const cluster = await import('node:cluster');
328
+ if (!cluster.default?.isWorker) {
329
+ await this._checkPort(port);
330
+ }
328
331
 
329
332
  await this.emit('ready');
330
333
 
@@ -35,4 +35,50 @@ export default class MediaHelper {
35
35
  if (this._bridge?.mediaVideoInfo) return this._bridge.mediaVideoInfo(filePath);
36
36
  throw new Error('Video info requires ffprobe + Bridge.');
37
37
  }
38
+
39
+ /**
40
+ * 이미지에 워터마크를 합성한다.
41
+ * @param {string} targetPath - 대상 이미지 경로
42
+ * @param {string} watermarkPath - 워터마크 이미지 경로
43
+ * @param {number} [opacity=50] - 불투명도 (0-100)
44
+ * @param {string} [format] - 출력 포맷 (기본: 원본 확장자)
45
+ * @param {number} [quality=90] - 출력 품질
46
+ */
47
+ applyWatermark(targetPath, watermarkPath, opacity = 50, format, quality = 90) {
48
+ if (this._bridge?.mediaApplyWatermark) {
49
+ return this._bridge.mediaApplyWatermark(targetPath, watermarkPath, opacity, format, quality);
50
+ }
51
+ throw new Error('Watermark requires Bridge (Rust).');
52
+ }
53
+
54
+ /**
55
+ * 비디오에서 N초 간격으로 다중 썸네일을 추출한다.
56
+ * @param {string} inputPath - 비디오 경로
57
+ * @param {string} outputDir - 출력 디렉토리
58
+ * @param {number} [interval=5] - 초 간격
59
+ * @param {number} [width=320] - 썸네일 가로 크기
60
+ * @param {string} [format='jpeg'] - 포맷
61
+ * @returns {string[]} 생성된 파일 경로 목록
62
+ */
63
+ videoThumbnails(inputPath, outputDir, interval = 5, width = 320, format = 'jpeg') {
64
+ if (this._bridge?.mediaVideoThumbnails) {
65
+ return this._bridge.mediaVideoThumbnails(inputPath, outputDir, interval, width, format);
66
+ }
67
+ throw new Error('Video thumbnails requires ffmpeg + Bridge.');
68
+ }
69
+
70
+ /**
71
+ * 썸네일 목록을 스프라이트 시트(그리드)로 합성한다.
72
+ * @param {string[]} thumbPaths - 썸네일 경로 목록
73
+ * @param {string} outputPath - 출력 스프라이트 시트 경로
74
+ * @param {number} [cols=10] - 그리드 열 수
75
+ * @param {number} [thumbWidth=160] - 각 썸네일 가로
76
+ * @param {number} [thumbHeight=0] - 각 썸네일 세로 (0=자동)
77
+ */
78
+ videoPreviewSheet(thumbPaths, outputPath, cols = 10, thumbWidth = 160, thumbHeight = 0) {
79
+ if (this._bridge?.mediaVideoPreviewSheet) {
80
+ return this._bridge.mediaVideoPreviewSheet(thumbPaths, outputPath, cols, thumbWidth, thumbHeight);
81
+ }
82
+ throw new Error('Preview sheet requires Bridge (Rust).');
83
+ }
38
84
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzionx/framework",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "type": "module",
5
5
  "description": "Full-stack MVC framework built on @fuzionx/core — Controller, Service, Model, Middleware, DI, EventBus",
6
6
  "main": "index.js",
@@ -34,7 +34,7 @@
34
34
  "url": "https://github.com/saytohenry/fuzionx"
35
35
  },
36
36
  "dependencies": {
37
- "@fuzionx/core": "^0.1.20",
37
+ "@fuzionx/core": "^0.1.21",
38
38
  "better-sqlite3": "^12.8.0",
39
39
  "knex": "^3.2.5",
40
40
  "mongoose": "^9.3.2",