@lee-jisoo/n8n-nodes-mediafx 1.6.14 → 1.6.16
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.ko-KR.md +86 -1
- package/README.md +88 -1
- package/dist/nodes/MediaFX/MediaFX.node.js +41 -3
- package/dist/nodes/MediaFX/operations/addSubtitle.js +20 -6
- package/dist/nodes/MediaFX/operations/addText.js +20 -5
- package/dist/nodes/MediaFX/operations/getMetadata.d.ts +41 -0
- package/dist/nodes/MediaFX/operations/getMetadata.js +104 -0
- package/dist/nodes/MediaFX/operations/index.d.ts +1 -0
- package/dist/nodes/MediaFX/operations/index.js +1 -0
- package/dist/nodes/MediaFX/properties/font.properties.js +8 -0
- package/dist/nodes/MediaFX/properties/probe.properties.d.ts +2 -0
- package/dist/nodes/MediaFX/properties/probe.properties.js +84 -0
- package/dist/nodes/MediaFX/properties/resources.properties.js +5 -0
- package/dist/nodes/MediaFX/properties/subtitle.properties.js +31 -0
- package/dist/nodes/MediaFX/utils.d.ts +3 -1
- package/dist/nodes/MediaFX/utils.js +111 -4
- package/package.json +3 -2
package/README.ko-KR.md
CHANGED
|
@@ -9,7 +9,20 @@
|
|
|
9
9
|
<!-- Optional: Add a GIF of the node in action here -->
|
|
10
10
|
<!-- <p align="center"><img src="link/to/your/demo.gif" alt="MediaFX Node Demo"></p> -->
|
|
11
11
|
|
|
12
|
-
## v1.
|
|
12
|
+
## v1.6.15의 새로운 기능
|
|
13
|
+
|
|
14
|
+
- **🔍 미디어 분석 (Probe)**: 비디오/오디오 파일에서 메타정보를 추출하는 새로운 기능
|
|
15
|
+
- 포맷 정보: 파일명, 포맷, 재생시간, 파일크기, 비트레이트
|
|
16
|
+
- 비디오 스트림: 코덱, 해상도, 프레임레이트, 화면비율, 픽셀포맷
|
|
17
|
+
- 오디오 스트림: 코덱, 샘플레이트, 채널 수, 채널 레이아웃
|
|
18
|
+
- 태그: 제목, 아티스트, 앨범 등 파일에 포함된 메타데이터
|
|
19
|
+
|
|
20
|
+
- **🔤 시스템 폰트 지원**: 시스템에 설치된 폰트를 사용 가능
|
|
21
|
+
- 글꼴 > 목록 작업에서 "시스템 폰트 포함" 옵션으로 시스템 폰트 스캔
|
|
22
|
+
- 텍스트/자막 작업에서 시스템 폰트 경로 직접 지정 가능
|
|
23
|
+
- macOS, Linux, Windows의 시스템 폰트 디렉토리 지원
|
|
24
|
+
|
|
25
|
+
## v1.4.2의 기능
|
|
13
26
|
|
|
14
27
|
- **버그 수정**: 비디오 소스가 올바르게 설정되지 않은 경우 오디오 분리 작업에서 "did not produce an output" 오류 수정
|
|
15
28
|
- 소스가 누락된 경우 명확한 오류 메시지와 함께 적절한 유효성 검사 추가
|
|
@@ -104,6 +117,9 @@
|
|
|
104
117
|
- `문자열 추가`: 비디오에 텍스트 오버레이를 추가합니다.
|
|
105
118
|
- `자막 추가`: `.srt` 파일에서 자막을 추가합니다.
|
|
106
119
|
|
|
120
|
+
#### **분석 (Probe)** 리소스
|
|
121
|
+
- `메타정보 조회`: 비디오/오디오 파일에서 포맷 정보, 스트림 정보, 태그 등 상세한 메타데이터를 추출합니다.
|
|
122
|
+
|
|
107
123
|
#### **글꼴** 리소스
|
|
108
124
|
- `목록`: 사용 가능한 모든 시스템 및 사용자 업로드 글꼴 목록을 가져옵니다.
|
|
109
125
|
- `업로드`: 텍스트 작업에 사용할 사용자 지정 글꼴 파일(`.ttf`, `.otf`)을 업로드합니다.
|
|
@@ -111,6 +127,75 @@
|
|
|
111
127
|
|
|
112
128
|
## 사용 예제
|
|
113
129
|
|
|
130
|
+
### 미디어 메타정보 조회 (v1.6.15 신규)
|
|
131
|
+
비디오 또는 오디오 파일에서 메타정보를 추출합니다.
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"resource": "probe",
|
|
136
|
+
"operation": "getMetadata",
|
|
137
|
+
"probeSource": {
|
|
138
|
+
"source": { "sourceType": "binary", "binaryProperty": "data" }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
출력 예시:
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"success": true,
|
|
147
|
+
"operation": "getMetadata",
|
|
148
|
+
"format": {
|
|
149
|
+
"filename": "video.mp4",
|
|
150
|
+
"formatName": "mov,mp4,m4a,3gp,3g2,mj2",
|
|
151
|
+
"duration": 120.5,
|
|
152
|
+
"size": 15728640,
|
|
153
|
+
"bitRate": 1048576
|
|
154
|
+
},
|
|
155
|
+
"video": {
|
|
156
|
+
"codec": "h264",
|
|
157
|
+
"width": 1920,
|
|
158
|
+
"height": 1080,
|
|
159
|
+
"frameRate": 30,
|
|
160
|
+
"aspectRatio": "16:9"
|
|
161
|
+
},
|
|
162
|
+
"audio": {
|
|
163
|
+
"codec": "aac",
|
|
164
|
+
"sampleRate": 48000,
|
|
165
|
+
"channels": 2,
|
|
166
|
+
"channelLayout": "stereo"
|
|
167
|
+
},
|
|
168
|
+
"hasVideo": true,
|
|
169
|
+
"hasAudio": true
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 시스템 폰트 사용하기 (v1.6.15 신규)
|
|
174
|
+
|
|
175
|
+
먼저 시스템에 설치된 폰트 목록을 확인합니다:
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"resource": "font",
|
|
179
|
+
"operation": "list",
|
|
180
|
+
"filterOptions": {
|
|
181
|
+
"includeSystemFonts": true,
|
|
182
|
+
"fontType": "system"
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
시스템 폰트로 자막 추가하기:
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"resource": "subtitle",
|
|
191
|
+
"operation": "addSubtitle",
|
|
192
|
+
"fontSource": "system",
|
|
193
|
+
"systemFontPath": "/System/Library/Fonts/AppleSDGothicNeo.ttc",
|
|
194
|
+
"size": 48,
|
|
195
|
+
"color": "white"
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
114
199
|
### Merge 노드와 함께 사용하기 (v1.4.0 신규)
|
|
115
200
|
n8n의 Merge 노드를 사용하여 여러 비디오 입력을 결합할 때:
|
|
116
201
|
|
package/README.md
CHANGED
|
@@ -10,6 +10,20 @@ This is a custom n8n node for comprehensive, local media processing using FFmpeg
|
|
|
10
10
|
|
|
11
11
|
## 🆕 What's New in This Fork
|
|
12
12
|
|
|
13
|
+
### v1.6.15
|
|
14
|
+
**New Features**
|
|
15
|
+
|
|
16
|
+
- **🔍 Get Metadata (Probe)**: Extract comprehensive metadata from video/audio files
|
|
17
|
+
- Format info: filename, format, duration, size, bitrate
|
|
18
|
+
- Video stream: codec, resolution, frame rate, aspect ratio, pixel format
|
|
19
|
+
- Audio stream: codec, sample rate, channels, channel layout
|
|
20
|
+
- Tags: title, artist, album, and other embedded metadata
|
|
21
|
+
|
|
22
|
+
- **🔤 System Font Support**: Use fonts installed on your system
|
|
23
|
+
- Scan system fonts with Font > List operation (enable "Include System Fonts")
|
|
24
|
+
- Directly specify system font path in Text/Subtitle operations
|
|
25
|
+
- Supports macOS, Linux, and Windows system font directories
|
|
26
|
+
|
|
13
27
|
### v1.6.14
|
|
14
28
|
**Subtitle Enhancements (v1.6.1 ~ v1.6.14)**
|
|
15
29
|
|
|
@@ -98,6 +112,11 @@ RUN cd /home/node/.n8n/nodes && npm install @lee-jisoo/n8n-nodes-mediafx
|
|
|
98
112
|
| **Add String** | Burn text overlay with styling |
|
|
99
113
|
| **Add Subtitle** | Add subtitles from SRT file ⭐ FIXED |
|
|
100
114
|
|
|
115
|
+
### Probe Operations
|
|
116
|
+
| Operation | Description |
|
|
117
|
+
|-----------|-------------|
|
|
118
|
+
| **Get Metadata** | Extract metadata from video/audio files (format, streams, tags) |
|
|
119
|
+
|
|
101
120
|
### Font Operations
|
|
102
121
|
| Operation | Description |
|
|
103
122
|
|-----------|-------------|
|
|
@@ -107,7 +126,75 @@ RUN cd /home/node/.n8n/nodes && npm install @lee-jisoo/n8n-nodes-mediafx
|
|
|
107
126
|
|
|
108
127
|
## Usage Examples
|
|
109
128
|
|
|
110
|
-
###
|
|
129
|
+
### Get Media Metadata (New!)
|
|
130
|
+
Extract metadata from video or audio files:
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"resource": "probe",
|
|
134
|
+
"operation": "getMetadata",
|
|
135
|
+
"probeSource": {
|
|
136
|
+
"source": { "sourceType": "binary", "binaryProperty": "data" }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Example output:
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"success": true,
|
|
145
|
+
"operation": "getMetadata",
|
|
146
|
+
"format": {
|
|
147
|
+
"filename": "video.mp4",
|
|
148
|
+
"formatName": "mov,mp4,m4a,3gp,3g2,mj2",
|
|
149
|
+
"duration": 120.5,
|
|
150
|
+
"size": 15728640,
|
|
151
|
+
"bitRate": 1048576
|
|
152
|
+
},
|
|
153
|
+
"video": {
|
|
154
|
+
"codec": "h264",
|
|
155
|
+
"width": 1920,
|
|
156
|
+
"height": 1080,
|
|
157
|
+
"frameRate": 30,
|
|
158
|
+
"aspectRatio": "16:9"
|
|
159
|
+
},
|
|
160
|
+
"audio": {
|
|
161
|
+
"codec": "aac",
|
|
162
|
+
"sampleRate": 48000,
|
|
163
|
+
"channels": 2,
|
|
164
|
+
"channelLayout": "stereo"
|
|
165
|
+
},
|
|
166
|
+
"hasVideo": true,
|
|
167
|
+
"hasAudio": true
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Using System Fonts (New!)
|
|
172
|
+
|
|
173
|
+
First, list available system fonts:
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"resource": "font",
|
|
177
|
+
"operation": "list",
|
|
178
|
+
"filterOptions": {
|
|
179
|
+
"includeSystemFonts": true,
|
|
180
|
+
"fontType": "system"
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Add subtitles with a system font:
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"resource": "subtitle",
|
|
189
|
+
"operation": "addSubtitle",
|
|
190
|
+
"fontSource": "system",
|
|
191
|
+
"systemFontPath": "/System/Library/Fonts/Helvetica.ttc",
|
|
192
|
+
"size": 48,
|
|
193
|
+
"color": "white"
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Speed Adjustment
|
|
111
198
|
Create a 2x speed video:
|
|
112
199
|
```json
|
|
113
200
|
{
|
|
@@ -33,6 +33,7 @@ const operations_1 = require("./operations");
|
|
|
33
33
|
const audio_properties_1 = require("./properties/audio.properties");
|
|
34
34
|
const font_properties_1 = require("./properties/font.properties");
|
|
35
35
|
const image_properties_1 = require("./properties/image.properties");
|
|
36
|
+
const probe_properties_1 = require("./properties/probe.properties");
|
|
36
37
|
const resources_properties_1 = require("./properties/resources.properties");
|
|
37
38
|
const subtitle_properties_1 = require("./properties/subtitle.properties");
|
|
38
39
|
const video_properties_1 = require("./properties/video.properties");
|
|
@@ -59,6 +60,7 @@ class MediaFX {
|
|
|
59
60
|
...audio_properties_1.audioProperties,
|
|
60
61
|
...subtitle_properties_1.subtitleProperties,
|
|
61
62
|
...image_properties_1.imageProperties,
|
|
63
|
+
...probe_properties_1.probeProperties,
|
|
62
64
|
...font_properties_1.fontProperties,
|
|
63
65
|
],
|
|
64
66
|
};
|
|
@@ -126,6 +128,7 @@ class MediaFX {
|
|
|
126
128
|
};
|
|
127
129
|
}
|
|
128
130
|
async execute() {
|
|
131
|
+
var _a;
|
|
129
132
|
const items = this.getInputData();
|
|
130
133
|
const returnData = [];
|
|
131
134
|
// Periodically clean up old temporary files (every 10th execution)
|
|
@@ -150,7 +153,8 @@ class MediaFX {
|
|
|
150
153
|
case 'list': {
|
|
151
154
|
const filterOptions = this.getNodeParameter('filterOptions', i, {});
|
|
152
155
|
const fontTypeFilter = filterOptions.fontType || 'all';
|
|
153
|
-
const
|
|
156
|
+
const includeSystemFonts = (_a = filterOptions.includeSystemFonts) !== null && _a !== void 0 ? _a : false;
|
|
157
|
+
const allFonts = (0, utils_1.getAvailableFonts)(includeSystemFonts);
|
|
154
158
|
if (fontTypeFilter === 'all') {
|
|
155
159
|
resultData = allFonts;
|
|
156
160
|
}
|
|
@@ -196,6 +200,34 @@ class MediaFX {
|
|
|
196
200
|
}
|
|
197
201
|
}
|
|
198
202
|
// ===================================
|
|
203
|
+
// PROBE RESOURCE OPERATIONS
|
|
204
|
+
// ===================================
|
|
205
|
+
else if (resource === 'probe') {
|
|
206
|
+
switch (operation) {
|
|
207
|
+
case 'getMetadata': {
|
|
208
|
+
const sourceParam = this.getNodeParameter('probeSource', i, {});
|
|
209
|
+
if (!sourceParam.source) {
|
|
210
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Media source is required. Please add a media source.', { itemIndex: i });
|
|
211
|
+
}
|
|
212
|
+
const { paths, cleanup: c } = await (0, utils_1.resolveInputs)(this, i, [sourceParam.source]);
|
|
213
|
+
cleanup = c;
|
|
214
|
+
const metadata = await operations_1.executeGetMetadata.call(this, paths[0], i);
|
|
215
|
+
// Push result with metadata as JSON
|
|
216
|
+
returnData.push({
|
|
217
|
+
json: {
|
|
218
|
+
success: true,
|
|
219
|
+
operation: 'getMetadata',
|
|
220
|
+
...metadata,
|
|
221
|
+
},
|
|
222
|
+
pairedItem: { item: i },
|
|
223
|
+
});
|
|
224
|
+
// Cleanup and continue to next item
|
|
225
|
+
await cleanup();
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// ===================================
|
|
199
231
|
// MEDIA RESOURCE OPERATIONS
|
|
200
232
|
// ===================================
|
|
201
233
|
else {
|
|
@@ -304,8 +336,11 @@ class MediaFX {
|
|
|
304
336
|
await subFileCleanup();
|
|
305
337
|
};
|
|
306
338
|
// Collect style options from individual parameters
|
|
339
|
+
const fontSource = this.getNodeParameter('fontSource', i, 'bundled');
|
|
307
340
|
const style = {
|
|
308
|
-
|
|
341
|
+
fontSource,
|
|
342
|
+
fontKey: fontSource === 'bundled' ? this.getNodeParameter('fontKey', i, 'noto-sans-kr') : undefined,
|
|
343
|
+
systemFontPath: fontSource === 'system' ? this.getNodeParameter('systemFontPath', i, '') : undefined,
|
|
309
344
|
size: this.getNodeParameter('size', i, 48),
|
|
310
345
|
color: this.getNodeParameter('color', i, 'white'),
|
|
311
346
|
outlineWidth: this.getNodeParameter('outlineWidth', i, 1),
|
|
@@ -333,8 +368,11 @@ class MediaFX {
|
|
|
333
368
|
const startTime = this.getNodeParameter('startTime', i, 0);
|
|
334
369
|
const endTime = this.getNodeParameter('endTime', i, 5);
|
|
335
370
|
// Collect style options from individual parameters
|
|
371
|
+
const textFontSource = this.getNodeParameter('fontSource', i, 'bundled');
|
|
336
372
|
const textOptions = {
|
|
337
|
-
|
|
373
|
+
fontSource: textFontSource,
|
|
374
|
+
fontKey: textFontSource === 'bundled' ? this.getNodeParameter('fontKey', i, 'noto-sans-kr') : undefined,
|
|
375
|
+
systemFontPath: textFontSource === 'system' ? this.getNodeParameter('systemFontPath', i, '') : undefined,
|
|
338
376
|
size: this.getNodeParameter('size', i, 48),
|
|
339
377
|
color: this.getNodeParameter('color', i, 'white'),
|
|
340
378
|
outlineWidth: this.getNodeParameter('outlineWidth', i, 1),
|
|
@@ -190,12 +190,26 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|
|
190
190
|
async function executeAddSubtitle(video, subtitleFile, style, itemIndex) {
|
|
191
191
|
var _a, _b, _c, _d, _e;
|
|
192
192
|
const outputPath = (0, utils_1.getTempFile)(path.extname(video));
|
|
193
|
-
// 1. Get Font
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
193
|
+
// 1. Get Font (support both bundled and system fonts)
|
|
194
|
+
const fontSource = style.fontSource || 'bundled';
|
|
195
|
+
let font = null;
|
|
196
|
+
if (fontSource === 'system') {
|
|
197
|
+
const systemFontPath = style.systemFontPath;
|
|
198
|
+
if (!systemFontPath) {
|
|
199
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'System font path is required when using system fonts.', { itemIndex });
|
|
200
|
+
}
|
|
201
|
+
font = (0, utils_1.getFontByPath)(systemFontPath);
|
|
202
|
+
if (!font) {
|
|
203
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `System font not found at path '${systemFontPath}'. Please check the path and ensure it's a valid font file (TTF, OTF, TTC).`, { itemIndex });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
const allFonts = (0, utils_1.getAvailableFonts)();
|
|
208
|
+
const fontKey = style.fontKey || 'noto-sans-kr';
|
|
209
|
+
font = allFonts[fontKey] || null;
|
|
210
|
+
if (!font || !font.path) {
|
|
211
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Selected font key '${fontKey}' is not valid or its file path is missing.`, { itemIndex });
|
|
212
|
+
}
|
|
199
213
|
}
|
|
200
214
|
const fontName = font.name || 'Sans';
|
|
201
215
|
const fontSize = style.size || 48;
|
|
@@ -62,11 +62,26 @@ function getPositionFromAlignment(horizontalAlign, verticalAlign, paddingX, padd
|
|
|
62
62
|
}
|
|
63
63
|
async function executeAddText(video, text, options, itemIndex) {
|
|
64
64
|
var _a, _b, _c, _d;
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
65
|
+
// Support both bundled and system fonts
|
|
66
|
+
const fontSource = options.fontSource || 'bundled';
|
|
67
|
+
let font = null;
|
|
68
|
+
if (fontSource === 'system') {
|
|
69
|
+
const systemFontPath = options.systemFontPath;
|
|
70
|
+
if (!systemFontPath) {
|
|
71
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'System font path is required when using system fonts.', { itemIndex });
|
|
72
|
+
}
|
|
73
|
+
font = (0, utils_1.getFontByPath)(systemFontPath);
|
|
74
|
+
if (!font) {
|
|
75
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `System font not found at path '${systemFontPath}'. Please check the path and ensure it's a valid font file (TTF, OTF, TTC).`, { itemIndex });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const allFonts = (0, utils_1.getAvailableFonts)();
|
|
80
|
+
const fontKey = options.fontKey || 'noto-sans-kr';
|
|
81
|
+
font = allFonts[fontKey] || null;
|
|
82
|
+
if (!font || !font.path) {
|
|
83
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Selected font key '${fontKey}' is not valid or its file path is missing.`, { itemIndex });
|
|
84
|
+
}
|
|
70
85
|
}
|
|
71
86
|
const fontPath = font.path;
|
|
72
87
|
// Set default values for text options
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { IExecuteFunctions } from 'n8n-workflow';
|
|
2
|
+
import { IDataObject } from 'n8n-workflow';
|
|
3
|
+
interface StreamInfo {
|
|
4
|
+
index: number;
|
|
5
|
+
type: string;
|
|
6
|
+
codec: string;
|
|
7
|
+
codecLongName?: string;
|
|
8
|
+
profile?: string;
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
aspectRatio?: string;
|
|
12
|
+
frameRate?: number;
|
|
13
|
+
bitRate?: number;
|
|
14
|
+
pixelFormat?: string;
|
|
15
|
+
sampleRate?: number;
|
|
16
|
+
channels?: number;
|
|
17
|
+
channelLayout?: string;
|
|
18
|
+
bitsPerSample?: number;
|
|
19
|
+
duration?: number;
|
|
20
|
+
language?: string;
|
|
21
|
+
title?: string;
|
|
22
|
+
}
|
|
23
|
+
interface MediaMetadata {
|
|
24
|
+
format: {
|
|
25
|
+
filename: string;
|
|
26
|
+
formatName: string;
|
|
27
|
+
formatLongName?: string;
|
|
28
|
+
duration: number;
|
|
29
|
+
size: number;
|
|
30
|
+
bitRate: number;
|
|
31
|
+
probeScore?: number;
|
|
32
|
+
};
|
|
33
|
+
streams: StreamInfo[];
|
|
34
|
+
video?: StreamInfo;
|
|
35
|
+
audio?: StreamInfo;
|
|
36
|
+
hasVideo: boolean;
|
|
37
|
+
hasAudio: boolean;
|
|
38
|
+
tags?: IDataObject;
|
|
39
|
+
}
|
|
40
|
+
export declare function executeGetMetadata(this: IExecuteFunctions, inputPath: string, _itemIndex: number): Promise<MediaMetadata>;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeGetMetadata = void 0;
|
|
4
|
+
const ffmpeg = require("fluent-ffmpeg");
|
|
5
|
+
async function executeGetMetadata(inputPath, _itemIndex) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
ffmpeg.ffprobe(inputPath, (err, metadata) => {
|
|
8
|
+
if (err) {
|
|
9
|
+
return reject(new Error(`Failed to probe media file: ${err.message}`));
|
|
10
|
+
}
|
|
11
|
+
const streams = [];
|
|
12
|
+
let videoStream;
|
|
13
|
+
let audioStream;
|
|
14
|
+
for (const stream of metadata.streams) {
|
|
15
|
+
const streamInfo = {
|
|
16
|
+
index: stream.index,
|
|
17
|
+
type: stream.codec_type || 'unknown',
|
|
18
|
+
codec: stream.codec_name || 'unknown',
|
|
19
|
+
codecLongName: stream.codec_long_name,
|
|
20
|
+
profile: stream.profile !== undefined ? String(stream.profile) : undefined,
|
|
21
|
+
};
|
|
22
|
+
if (stream.codec_type === 'video') {
|
|
23
|
+
streamInfo.width = stream.width;
|
|
24
|
+
streamInfo.height = stream.height;
|
|
25
|
+
streamInfo.aspectRatio = stream.display_aspect_ratio;
|
|
26
|
+
streamInfo.pixelFormat = stream.pix_fmt;
|
|
27
|
+
// Calculate frame rate from r_frame_rate (e.g., "30000/1001" or "30/1")
|
|
28
|
+
if (stream.r_frame_rate) {
|
|
29
|
+
const parts = stream.r_frame_rate.split('/');
|
|
30
|
+
if (parts.length === 2) {
|
|
31
|
+
const num = parseFloat(parts[0]);
|
|
32
|
+
const den = parseFloat(parts[1]);
|
|
33
|
+
if (den !== 0) {
|
|
34
|
+
streamInfo.frameRate = Math.round((num / den) * 100) / 100;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (stream.bit_rate) {
|
|
39
|
+
streamInfo.bitRate = parseInt(stream.bit_rate, 10);
|
|
40
|
+
}
|
|
41
|
+
if (stream.duration) {
|
|
42
|
+
streamInfo.duration = parseFloat(stream.duration);
|
|
43
|
+
}
|
|
44
|
+
// Extract tags
|
|
45
|
+
if (stream.tags) {
|
|
46
|
+
streamInfo.language = stream.tags.language;
|
|
47
|
+
streamInfo.title = stream.tags.title;
|
|
48
|
+
}
|
|
49
|
+
if (!videoStream) {
|
|
50
|
+
videoStream = streamInfo;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (stream.codec_type === 'audio') {
|
|
54
|
+
streamInfo.sampleRate = stream.sample_rate ? parseInt(String(stream.sample_rate), 10) : undefined;
|
|
55
|
+
streamInfo.channels = stream.channels;
|
|
56
|
+
streamInfo.channelLayout = stream.channel_layout;
|
|
57
|
+
streamInfo.bitsPerSample = stream.bits_per_sample;
|
|
58
|
+
if (stream.bit_rate) {
|
|
59
|
+
streamInfo.bitRate = parseInt(stream.bit_rate, 10);
|
|
60
|
+
}
|
|
61
|
+
if (stream.duration) {
|
|
62
|
+
streamInfo.duration = parseFloat(stream.duration);
|
|
63
|
+
}
|
|
64
|
+
// Extract tags
|
|
65
|
+
if (stream.tags) {
|
|
66
|
+
streamInfo.language = stream.tags.language;
|
|
67
|
+
streamInfo.title = stream.tags.title;
|
|
68
|
+
}
|
|
69
|
+
if (!audioStream) {
|
|
70
|
+
audioStream = streamInfo;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
streams.push(streamInfo);
|
|
74
|
+
}
|
|
75
|
+
const format = metadata.format;
|
|
76
|
+
const result = {
|
|
77
|
+
format: {
|
|
78
|
+
filename: format.filename || inputPath,
|
|
79
|
+
formatName: format.format_name || 'unknown',
|
|
80
|
+
formatLongName: format.format_long_name,
|
|
81
|
+
duration: format.duration || 0,
|
|
82
|
+
size: format.size || 0,
|
|
83
|
+
bitRate: format.bit_rate ? parseInt(String(format.bit_rate), 10) : 0,
|
|
84
|
+
probeScore: format.probe_score,
|
|
85
|
+
},
|
|
86
|
+
streams,
|
|
87
|
+
hasVideo: !!videoStream,
|
|
88
|
+
hasAudio: !!audioStream,
|
|
89
|
+
};
|
|
90
|
+
if (videoStream) {
|
|
91
|
+
result.video = videoStream;
|
|
92
|
+
}
|
|
93
|
+
if (audioStream) {
|
|
94
|
+
result.audio = audioStream;
|
|
95
|
+
}
|
|
96
|
+
// Extract format tags (metadata like title, artist, album, etc.)
|
|
97
|
+
if (format.tags) {
|
|
98
|
+
result.tags = format.tags;
|
|
99
|
+
}
|
|
100
|
+
resolve(result);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
exports.executeGetMetadata = executeGetMetadata;
|
|
@@ -160,6 +160,13 @@ exports.fontProperties = [
|
|
|
160
160
|
},
|
|
161
161
|
default: {},
|
|
162
162
|
options: [
|
|
163
|
+
{
|
|
164
|
+
displayName: 'Include System Fonts',
|
|
165
|
+
name: 'includeSystemFonts',
|
|
166
|
+
type: 'boolean',
|
|
167
|
+
default: false,
|
|
168
|
+
description: 'Whether to include fonts installed on the system (may be slow on first scan)',
|
|
169
|
+
},
|
|
163
170
|
{
|
|
164
171
|
displayName: 'Font Type',
|
|
165
172
|
name: 'fontType',
|
|
@@ -169,6 +176,7 @@ exports.fontProperties = [
|
|
|
169
176
|
{ name: 'Korean Fonts', value: 'korean' },
|
|
170
177
|
{ name: 'Global Fonts', value: 'global' },
|
|
171
178
|
{ name: 'User Fonts', value: 'user' },
|
|
179
|
+
{ name: 'System Fonts', value: 'system' },
|
|
172
180
|
{ name: 'Fallback Fonts', value: 'fallback' },
|
|
173
181
|
],
|
|
174
182
|
default: 'all',
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.probeProperties = void 0;
|
|
4
|
+
exports.probeProperties = [
|
|
5
|
+
// Probe Operations
|
|
6
|
+
{
|
|
7
|
+
displayName: 'Operation',
|
|
8
|
+
name: 'operation',
|
|
9
|
+
type: 'options',
|
|
10
|
+
noDataExpression: true,
|
|
11
|
+
displayOptions: {
|
|
12
|
+
show: {
|
|
13
|
+
resource: ['probe'],
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
options: [
|
|
17
|
+
{
|
|
18
|
+
name: 'Get Metadata',
|
|
19
|
+
value: 'getMetadata',
|
|
20
|
+
description: 'Extract metadata information from video or audio files',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
default: 'getMetadata',
|
|
24
|
+
},
|
|
25
|
+
// ===================
|
|
26
|
+
// GET METADATA FIELDS
|
|
27
|
+
// ===================
|
|
28
|
+
{
|
|
29
|
+
displayName: 'Media Source',
|
|
30
|
+
name: 'probeSource',
|
|
31
|
+
type: 'fixedCollection',
|
|
32
|
+
placeholder: 'Add Media Source',
|
|
33
|
+
displayOptions: {
|
|
34
|
+
show: {
|
|
35
|
+
resource: ['probe'],
|
|
36
|
+
operation: ['getMetadata'],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
default: {},
|
|
40
|
+
options: [
|
|
41
|
+
{
|
|
42
|
+
displayName: 'Source',
|
|
43
|
+
name: 'source',
|
|
44
|
+
values: [
|
|
45
|
+
{
|
|
46
|
+
displayName: 'Source Type',
|
|
47
|
+
name: 'sourceType',
|
|
48
|
+
type: 'options',
|
|
49
|
+
options: [
|
|
50
|
+
{
|
|
51
|
+
name: 'URL',
|
|
52
|
+
value: 'url',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'Binary Data from Previous Node',
|
|
56
|
+
value: 'binary',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
default: 'url',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
displayName: 'Value',
|
|
63
|
+
name: 'value',
|
|
64
|
+
type: 'string',
|
|
65
|
+
default: '',
|
|
66
|
+
placeholder: 'https://example.com/video.mp4 or https://example.com/audio.mp3',
|
|
67
|
+
description: 'URL of the media file to analyze',
|
|
68
|
+
displayOptions: { show: { sourceType: ['url'] } },
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
displayName: 'Binary Property',
|
|
72
|
+
name: 'binaryProperty',
|
|
73
|
+
type: 'string',
|
|
74
|
+
default: 'data',
|
|
75
|
+
description: 'Name of the binary property from the previous node',
|
|
76
|
+
placeholder: 'e.g., data',
|
|
77
|
+
displayOptions: { show: { sourceType: ['binary'] } },
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
description: 'The media file (video or audio) to analyze',
|
|
83
|
+
},
|
|
84
|
+
];
|
|
@@ -129,6 +129,22 @@ exports.subtitleProperties = [
|
|
|
129
129
|
// ===================
|
|
130
130
|
// COMMON FONT & STYLE OPTIONS
|
|
131
131
|
// ===================
|
|
132
|
+
{
|
|
133
|
+
displayName: 'Font Source',
|
|
134
|
+
name: 'fontSource',
|
|
135
|
+
type: 'options',
|
|
136
|
+
options: [
|
|
137
|
+
{ name: 'Bundled/Uploaded Fonts', value: 'bundled', description: 'Use fonts bundled with MediaFX or uploaded by user' },
|
|
138
|
+
{ name: 'System Font Path', value: 'system', description: 'Specify path to a system font file' },
|
|
139
|
+
],
|
|
140
|
+
default: 'bundled',
|
|
141
|
+
displayOptions: {
|
|
142
|
+
show: {
|
|
143
|
+
resource: ['subtitle'],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
description: 'Choose font source type',
|
|
147
|
+
},
|
|
132
148
|
{
|
|
133
149
|
displayName: 'Font Key',
|
|
134
150
|
name: 'fontKey',
|
|
@@ -138,10 +154,25 @@ exports.subtitleProperties = [
|
|
|
138
154
|
displayOptions: {
|
|
139
155
|
show: {
|
|
140
156
|
resource: ['subtitle'],
|
|
157
|
+
fontSource: ['bundled'],
|
|
141
158
|
},
|
|
142
159
|
},
|
|
143
160
|
description: 'Font to use for the text',
|
|
144
161
|
},
|
|
162
|
+
{
|
|
163
|
+
displayName: 'System Font Path',
|
|
164
|
+
name: 'systemFontPath',
|
|
165
|
+
type: 'string',
|
|
166
|
+
default: '',
|
|
167
|
+
placeholder: '/System/Library/Fonts/AppleSDGothicNeo.ttc',
|
|
168
|
+
displayOptions: {
|
|
169
|
+
show: {
|
|
170
|
+
resource: ['subtitle'],
|
|
171
|
+
fontSource: ['system'],
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
description: 'Full path to system font file (TTF, OTF, TTC). Use Font > List operation with "Include System Fonts" to find available fonts.',
|
|
175
|
+
},
|
|
145
176
|
{
|
|
146
177
|
displayName: 'Font Size',
|
|
147
178
|
name: 'size',
|
|
@@ -31,8 +31,10 @@ export declare function runFfmpeg(command: ffmpeg.FfmpegCommand): Promise<void>;
|
|
|
31
31
|
export declare function getVideoStreamInfo(filePath: string): Promise<ffmpeg.FfprobeStream | undefined>;
|
|
32
32
|
export declare function fileHasAudio(filePath: string): Promise<boolean>;
|
|
33
33
|
export declare const REGISTERED_FONTS: IDataObject;
|
|
34
|
+
export declare function getSystemFonts(): IDataObject;
|
|
34
35
|
export declare function getUserFonts(): IDataObject;
|
|
35
|
-
export declare function getAvailableFonts(): IDataObject;
|
|
36
|
+
export declare function getAvailableFonts(includeSystemFonts?: boolean): IDataObject;
|
|
37
|
+
export declare function getFontByPath(fontPath: string): IDataObject | null;
|
|
36
38
|
export declare function validateFontKey(fontKey: string): void;
|
|
37
39
|
export declare function saveUserFont(fontKey: string, fontName: string, description: string, originalFilename: string, buffer: Buffer): {
|
|
38
40
|
fontKey: string;
|
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.checkTransitionSupport = exports.getFFmpegCapabilities = exports.deleteUserFont = exports.saveUserFont = exports.validateFontKey = exports.getAvailableFonts = exports.getUserFonts = exports.REGISTERED_FONTS = exports.fileHasAudio = exports.getVideoStreamInfo = exports.runFfmpeg = exports.createSilentAudio = exports.getDuration = exports.resolveInputs = exports.createTempFileFromBuffer = exports.downloadSource = exports.cleanupOldTempFiles = exports.getTempFile = exports.verifyFfmpegAvailability = void 0;
|
|
29
|
+
exports.checkTransitionSupport = exports.getFFmpegCapabilities = exports.deleteUserFont = exports.saveUserFont = exports.validateFontKey = exports.getFontByPath = exports.getAvailableFonts = exports.getUserFonts = exports.getSystemFonts = exports.REGISTERED_FONTS = exports.fileHasAudio = exports.getVideoStreamInfo = exports.runFfmpeg = exports.createSilentAudio = exports.getDuration = exports.resolveInputs = exports.createTempFileFromBuffer = exports.downloadSource = exports.cleanupOldTempFiles = exports.getTempFile = exports.verifyFfmpegAvailability = void 0;
|
|
30
30
|
const fs = __importStar(require("fs-extra"));
|
|
31
31
|
const path = __importStar(require("path"));
|
|
32
32
|
const uuid_1 = require("uuid");
|
|
@@ -308,6 +308,28 @@ exports.fileHasAudio = fileHasAudio;
|
|
|
308
308
|
const BASE_FONTS_DIR = path.resolve(__dirname, '..', '..', 'fonts');
|
|
309
309
|
const USER_FONTS_DIR = path.join(BASE_FONTS_DIR, 'user');
|
|
310
310
|
const USER_FONTS_JSON = path.join(USER_FONTS_DIR, 'user-fonts.json');
|
|
311
|
+
// System font directories by platform
|
|
312
|
+
const SYSTEM_FONT_DIRS = {
|
|
313
|
+
darwin: [
|
|
314
|
+
'/System/Library/Fonts',
|
|
315
|
+
'/Library/Fonts',
|
|
316
|
+
path.join(os.homedir(), 'Library/Fonts'),
|
|
317
|
+
],
|
|
318
|
+
linux: [
|
|
319
|
+
'/usr/share/fonts',
|
|
320
|
+
'/usr/local/share/fonts',
|
|
321
|
+
path.join(os.homedir(), '.fonts'),
|
|
322
|
+
path.join(os.homedir(), '.local/share/fonts'),
|
|
323
|
+
],
|
|
324
|
+
win32: [
|
|
325
|
+
'C:\\Windows\\Fonts',
|
|
326
|
+
path.join(os.homedir(), 'AppData\\Local\\Microsoft\\Windows\\Fonts'),
|
|
327
|
+
],
|
|
328
|
+
};
|
|
329
|
+
// Cache for system fonts (to avoid repeated filesystem scans)
|
|
330
|
+
let systemFontsCache = null;
|
|
331
|
+
let systemFontsCacheTime = 0;
|
|
332
|
+
const SYSTEM_FONTS_CACHE_TTL = 60 * 60 * 1000; // 1 hour cache
|
|
311
333
|
// System-registered fonts (must exist in BASE_FONTS_DIR)
|
|
312
334
|
exports.REGISTERED_FONTS = {
|
|
313
335
|
'noto-sans-kr': { name: 'Noto Sans KR', filename: 'NotoSansKR-Regular.ttf', description: 'Google Noto Sans KR', type: 'korean' },
|
|
@@ -317,6 +339,62 @@ exports.REGISTERED_FONTS = {
|
|
|
317
339
|
'inter': { name: 'Inter', filename: 'Inter-Regular.ttf', description: 'Inter UI Font', type: 'global' },
|
|
318
340
|
'dejavu-sans': { name: 'DejaVu Sans', filename: 'DejaVuSans.ttf', description: 'Default fallback font', type: 'fallback' },
|
|
319
341
|
};
|
|
342
|
+
// Scan a directory for font files
|
|
343
|
+
function scanFontDirectory(dir, fonts) {
|
|
344
|
+
if (!fs.existsSync(dir))
|
|
345
|
+
return;
|
|
346
|
+
try {
|
|
347
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
348
|
+
for (const entry of entries) {
|
|
349
|
+
const fullPath = path.join(dir, entry.name);
|
|
350
|
+
if (entry.isDirectory()) {
|
|
351
|
+
// Recursively scan subdirectories
|
|
352
|
+
scanFontDirectory(fullPath, fonts);
|
|
353
|
+
}
|
|
354
|
+
else if (entry.isFile()) {
|
|
355
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
356
|
+
if (ext === '.ttf' || ext === '.otf' || ext === '.ttc' || ext === '.otc') {
|
|
357
|
+
// Generate a unique key from the filename
|
|
358
|
+
const baseName = path.basename(entry.name, ext);
|
|
359
|
+
const fontKey = `system-${baseName.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
|
|
360
|
+
// Skip if already exists (avoid duplicates)
|
|
361
|
+
if (!fonts[fontKey]) {
|
|
362
|
+
fonts[fontKey] = {
|
|
363
|
+
name: baseName,
|
|
364
|
+
filename: entry.name,
|
|
365
|
+
path: fullPath,
|
|
366
|
+
description: `System font from ${dir}`,
|
|
367
|
+
type: 'system',
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
// Ignore permission errors for system directories
|
|
376
|
+
console.warn(`Could not scan font directory ${dir}:`, error.message);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// Get system fonts with caching
|
|
380
|
+
function getSystemFonts() {
|
|
381
|
+
const now = Date.now();
|
|
382
|
+
// Return cached result if still valid
|
|
383
|
+
if (systemFontsCache && (now - systemFontsCacheTime) < SYSTEM_FONTS_CACHE_TTL) {
|
|
384
|
+
return systemFontsCache;
|
|
385
|
+
}
|
|
386
|
+
const fonts = {};
|
|
387
|
+
const platform = os.platform();
|
|
388
|
+
const fontDirs = SYSTEM_FONT_DIRS[platform] || [];
|
|
389
|
+
for (const dir of fontDirs) {
|
|
390
|
+
scanFontDirectory(dir, fonts);
|
|
391
|
+
}
|
|
392
|
+
// Update cache
|
|
393
|
+
systemFontsCache = fonts;
|
|
394
|
+
systemFontsCacheTime = now;
|
|
395
|
+
return fonts;
|
|
396
|
+
}
|
|
397
|
+
exports.getSystemFonts = getSystemFonts;
|
|
320
398
|
// Helper functions for font management
|
|
321
399
|
function ensureUserFontsDirectory() {
|
|
322
400
|
if (!fs.existsSync(USER_FONTS_DIR)) {
|
|
@@ -341,13 +419,13 @@ function saveUserFonts(userFonts) {
|
|
|
341
419
|
ensureUserFontsDirectory();
|
|
342
420
|
fs.writeFileSync(USER_FONTS_JSON, JSON.stringify(userFonts, null, 2));
|
|
343
421
|
}
|
|
344
|
-
function getAvailableFonts() {
|
|
422
|
+
function getAvailableFonts(includeSystemFonts = false) {
|
|
345
423
|
const fonts = {};
|
|
346
|
-
// Add registered fonts
|
|
424
|
+
// Add registered fonts (bundled with the package)
|
|
347
425
|
for (const [key, font] of Object.entries(exports.REGISTERED_FONTS)) {
|
|
348
426
|
const fontPath = path.join(BASE_FONTS_DIR, font.filename);
|
|
349
427
|
if (fs.existsSync(fontPath)) {
|
|
350
|
-
fonts[key] = { ...font, path: fontPath, type: font.type || '
|
|
428
|
+
fonts[key] = { ...font, path: fontPath, type: font.type || 'bundled' };
|
|
351
429
|
}
|
|
352
430
|
}
|
|
353
431
|
// Add user fonts
|
|
@@ -358,9 +436,38 @@ function getAvailableFonts() {
|
|
|
358
436
|
fonts[key] = { ...font, path: fontPath, type: 'user' };
|
|
359
437
|
}
|
|
360
438
|
}
|
|
439
|
+
// Add system fonts if requested
|
|
440
|
+
if (includeSystemFonts) {
|
|
441
|
+
const systemFonts = getSystemFonts();
|
|
442
|
+
for (const [key, font] of Object.entries(systemFonts)) {
|
|
443
|
+
// Don't override bundled or user fonts with system fonts
|
|
444
|
+
if (!fonts[key]) {
|
|
445
|
+
fonts[key] = font;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
361
449
|
return fonts;
|
|
362
450
|
}
|
|
363
451
|
exports.getAvailableFonts = getAvailableFonts;
|
|
452
|
+
// Get font info by path (for system fonts specified by path)
|
|
453
|
+
function getFontByPath(fontPath) {
|
|
454
|
+
if (!fs.existsSync(fontPath)) {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
const ext = path.extname(fontPath).toLowerCase();
|
|
458
|
+
if (ext !== '.ttf' && ext !== '.otf' && ext !== '.ttc' && ext !== '.otc') {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
const baseName = path.basename(fontPath, ext);
|
|
462
|
+
return {
|
|
463
|
+
name: baseName,
|
|
464
|
+
filename: path.basename(fontPath),
|
|
465
|
+
path: fontPath,
|
|
466
|
+
description: 'System font',
|
|
467
|
+
type: 'system',
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
exports.getFontByPath = getFontByPath;
|
|
364
471
|
function validateFontKey(fontKey) {
|
|
365
472
|
const keyPattern = /^[a-zA-Z0-9_-]{3,50}$/;
|
|
366
473
|
if (!keyPattern.test(fontKey)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lee-jisoo/n8n-nodes-mediafx",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.16",
|
|
4
4
|
"description": "N8N custom nodes for video editing and media processing (Enhanced fork with Speed control and Subtitle fixes)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"dist",
|
|
30
|
-
"fonts"
|
|
30
|
+
"fonts",
|
|
31
|
+
"README.md"
|
|
31
32
|
],
|
|
32
33
|
"n8n": {
|
|
33
34
|
"n8nNodesApiVersion": 1,
|