@doedja/scenecut 1.0.0 → 1.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 +280 -280
- package/bin/cli.js +292 -292
- package/dist/decoder/ffmpeg-decoder.d.ts.map +1 -1
- package/dist/decoder/ffmpeg-decoder.js +3 -1
- package/dist/decoder/ffmpeg-decoder.js.map +1 -1
- package/dist/detection.wasm.js +2 -2
- package/dist/detection.wasm.wasm +0 -0
- package/dist/keyframes.cjs.js +4 -1
- package/dist/keyframes.cjs.js.map +1 -1
- package/dist/keyframes.esm.js +3 -1
- package/dist/keyframes.esm.js.map +1 -1
- package/package.json +79 -77
package/README.md
CHANGED
|
@@ -1,280 +1,280 @@
|
|
|
1
|
-
# scenecut
|
|
2
|
-
|
|
3
|
-
Fast, accurate scene change detection for Node.js using Xvid's motion estimation algorithm compiled to WebAssembly.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Fast**: WebAssembly-accelerated motion estimation (35-45 fps on typical hardware)
|
|
8
|
-
- **Accurate**: Uses Xvid's proven motion estimation algorithm from vapoursynth-wwxd
|
|
9
|
-
- **Multiple output formats**: Aegisub keyframes, timecodes, CSV, JSON
|
|
10
|
-
- **Easy to use**: Simple CLI and programmatic API
|
|
11
|
-
- **Cross-platform**: Works on Windows, Linux, and macOS
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
### Global Installation (CLI)
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm install -g @doedja/scenecut
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
### Local Installation (API)
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npm install @doedja/scenecut
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## CLI Usage
|
|
28
|
-
|
|
29
|
-
### Basic Usage
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
# Simple - detects scenes and saves to Aegisub format (default)
|
|
33
|
-
# Creates input_keyframes.txt
|
|
34
|
-
scenecut "input.mkv"
|
|
35
|
-
|
|
36
|
-
# Specify custom output filename
|
|
37
|
-
scenecut "video.mkv" -o keyframes.txt
|
|
38
|
-
|
|
39
|
-
# For timecode output
|
|
40
|
-
scenecut "video.mp4" --format timecode -o timecodes.txt
|
|
41
|
-
|
|
42
|
-
# JSON format with full metadata
|
|
43
|
-
scenecut "movie.avi" --format json
|
|
44
|
-
|
|
45
|
-
# CSV format for spreadsheet analysis
|
|
46
|
-
scenecut "movie.avi" --format csv -o scenes.csv
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### CLI Options
|
|
50
|
-
|
|
51
|
-
| Option | Alias | Description | Default |
|
|
52
|
-
|--------|-------|-------------|---------|
|
|
53
|
-
| `--format` | `-f` | Output format: `aegisub`, `json`, `csv`, `timecode` | `aegisub` |
|
|
54
|
-
| `--output` | `-o` | Output file path | `{filename}_keyframes.txt` |
|
|
55
|
-
| `--sensitivity` | `-s` | Detection sensitivity: `low`, `medium`, `high` | `medium` |
|
|
56
|
-
| `--quiet` | `-q` | Suppress progress output | `false` |
|
|
57
|
-
| `--verbose` | `-v` | Show detailed output including each scene | `false` |
|
|
58
|
-
| `--help` | `-h` | Show help message | - |
|
|
59
|
-
|
|
60
|
-
### Examples
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
# Default - creates anime_keyframes.txt in Aegisub format
|
|
64
|
-
scenecut "anime.mkv"
|
|
65
|
-
|
|
66
|
-
# High sensitivity for subtle scene changes
|
|
67
|
-
scenecut "anime.mkv" --sensitivity high
|
|
68
|
-
|
|
69
|
-
# JSON format with full metadata
|
|
70
|
-
scenecut "video.mp4" --format json -o results.json
|
|
71
|
-
|
|
72
|
-
# Verbose mode with detailed scene information
|
|
73
|
-
scenecut "movie.mkv" --verbose
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Output Formats
|
|
77
|
-
|
|
78
|
-
### Aegisub Format (`.txt`)
|
|
79
|
-
|
|
80
|
-
Aegisub keyframes format for subtitle timing:
|
|
81
|
-
|
|
82
|
-
```
|
|
83
|
-
# keyframe format v1
|
|
84
|
-
fps 23.976
|
|
85
|
-
0
|
|
86
|
-
143
|
|
87
|
-
287
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
**Aegisub Workflow:**
|
|
91
|
-
1. Generate keyframes: `scenecut "video.mkv" -f aegisub -o keyframes.txt`
|
|
92
|
-
2. In Aegisub: **Video** → **Open Keyframes** → Select `keyframes.txt`
|
|
93
|
-
3. Keyframes appear as visual markers on the timeline for precise subtitle timing
|
|
94
|
-
|
|
95
|
-
### Timecode Format (`.txt`)
|
|
96
|
-
|
|
97
|
-
Simple timecode list (HH:MM:SS.mmm):
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
00:00:00.000
|
|
101
|
-
00:00:05.964
|
|
102
|
-
00:00:11.970
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### CSV Format (`.csv`)
|
|
106
|
-
|
|
107
|
-
Spreadsheet-compatible format:
|
|
108
|
-
|
|
109
|
-
```csv
|
|
110
|
-
frame,timestamp,timecode
|
|
111
|
-
0,0.0,00:00:00.000
|
|
112
|
-
143,5.964,00:00:05.964
|
|
113
|
-
287,11.970,00:00:11.970
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### JSON Format (`.json`)
|
|
117
|
-
|
|
118
|
-
Complete metadata and scene information:
|
|
119
|
-
|
|
120
|
-
```json
|
|
121
|
-
{
|
|
122
|
-
"scenes": [
|
|
123
|
-
{
|
|
124
|
-
"frameNumber": 0,
|
|
125
|
-
"timestamp": 0.0,
|
|
126
|
-
"timecode": "00:00:00.000"
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
"frameNumber": 143,
|
|
130
|
-
"timestamp": 5.964,
|
|
131
|
-
"timecode": "00:00:05.964"
|
|
132
|
-
}
|
|
133
|
-
],
|
|
134
|
-
"metadata": {
|
|
135
|
-
"totalFrames": 3000,
|
|
136
|
-
"duration": 125.08,
|
|
137
|
-
"fps": 23.976,
|
|
138
|
-
"resolution": {
|
|
139
|
-
"width": 1920,
|
|
140
|
-
"height": 1080
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## Programmatic API
|
|
147
|
-
|
|
148
|
-
### Basic Usage
|
|
149
|
-
|
|
150
|
-
```javascript
|
|
151
|
-
const { detectSceneChanges } = require('@doedja/scenecut');
|
|
152
|
-
|
|
153
|
-
(async () => {
|
|
154
|
-
const results = await detectSceneChanges('input.mp4');
|
|
155
|
-
|
|
156
|
-
console.log(`Found ${results.scenes.length} scenes`);
|
|
157
|
-
results.scenes.forEach(scene => {
|
|
158
|
-
console.log(`Scene at frame ${scene.frameNumber} (${scene.timecode})`);
|
|
159
|
-
});
|
|
160
|
-
})();
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### Advanced Usage with Options
|
|
164
|
-
|
|
165
|
-
```javascript
|
|
166
|
-
const { detectSceneChanges } = require('@doedja/scenecut');
|
|
167
|
-
|
|
168
|
-
const results = await detectSceneChanges('input.mp4', {
|
|
169
|
-
sensitivity: 'high', // 'low' | 'medium' | 'high'
|
|
170
|
-
searchRange: 'medium', // Motion search range
|
|
171
|
-
|
|
172
|
-
// Progress callback
|
|
173
|
-
onProgress: (progress) => {
|
|
174
|
-
console.log(`Progress: ${progress.percent}%`);
|
|
175
|
-
console.log(`Frame: ${progress.currentFrame}/${progress.totalFrames}`);
|
|
176
|
-
console.log(`FPS: ${progress.fps}, ETA: ${progress.eta}s`);
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
// Scene detection callback
|
|
180
|
-
onScene: (scene) => {
|
|
181
|
-
console.log(`Scene detected at frame ${scene.frameNumber}`);
|
|
182
|
-
console.log(`Timecode: ${scene.timecode}`);
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
console.log('Detection complete!');
|
|
187
|
-
console.log(`Total scenes: ${results.scenes.length}`);
|
|
188
|
-
console.log(`Video duration: ${results.metadata.duration}s`);
|
|
189
|
-
console.log(`Resolution: ${results.metadata.resolution.width}x${results.metadata.resolution.height}`);
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### API Reference
|
|
193
|
-
|
|
194
|
-
#### `detectSceneChanges(videoPath, options?)`
|
|
195
|
-
|
|
196
|
-
Detects scene changes in a video file.
|
|
197
|
-
|
|
198
|
-
**Parameters:**
|
|
199
|
-
- `videoPath` (string): Path to input video file
|
|
200
|
-
- `options` (object, optional):
|
|
201
|
-
- `sensitivity` ('low' | 'medium' | 'high'): Detection sensitivity (default: 'medium')
|
|
202
|
-
- `searchRange` ('auto' | 'small' | 'medium' | 'large'): Motion search range (default: 'medium')
|
|
203
|
-
- `onProgress` (function): Callback for progress updates
|
|
204
|
-
- `onScene` (function): Callback for each detected scene
|
|
205
|
-
|
|
206
|
-
**Returns:** Promise<DetectionResult>
|
|
207
|
-
|
|
208
|
-
**DetectionResult:**
|
|
209
|
-
```typescript
|
|
210
|
-
{
|
|
211
|
-
scenes: Array<{
|
|
212
|
-
frameNumber: number;
|
|
213
|
-
timestamp: number; // Seconds
|
|
214
|
-
timecode: string; // HH:MM:SS.mmm
|
|
215
|
-
}>;
|
|
216
|
-
metadata: {
|
|
217
|
-
totalFrames: number;
|
|
218
|
-
duration: number; // Seconds
|
|
219
|
-
fps: number;
|
|
220
|
-
resolution: {
|
|
221
|
-
width: number;
|
|
222
|
-
height: number;
|
|
223
|
-
};
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## Supported Video Formats
|
|
229
|
-
|
|
230
|
-
Keyframes supports any video format that FFmpeg can decode, including:
|
|
231
|
-
|
|
232
|
-
- MP4 (`.mp4`, `.m4v`)
|
|
233
|
-
- Matroska (`.mkv`)
|
|
234
|
-
- AVI (`.avi`)
|
|
235
|
-
- WebM (`.webm`)
|
|
236
|
-
- MOV (`.mov`)
|
|
237
|
-
- FLV (`.flv`)
|
|
238
|
-
- And many more...
|
|
239
|
-
|
|
240
|
-
## How It Works
|
|
241
|
-
|
|
242
|
-
Keyframes uses Xvid's motion estimation algorithm to detect scene changes:
|
|
243
|
-
|
|
244
|
-
1. **Frame Extraction**: FFmpeg extracts grayscale frames from the video
|
|
245
|
-
2. **Motion Analysis**: WebAssembly-compiled C code analyzes motion vectors between consecutive frames
|
|
246
|
-
3. **Scene Detection**: Frames with high motion complexity are identified as scene changes
|
|
247
|
-
4. **Output Formatting**: Results are formatted according to the requested output format
|
|
248
|
-
|
|
249
|
-
The algorithm is based on [vapoursynth-wwxd](https://github.com/dubhater/vapoursynth-wwxd) by dubhater, which itself uses Xvid's motion estimation code.
|
|
250
|
-
|
|
251
|
-
## Performance
|
|
252
|
-
|
|
253
|
-
Optimized for speed and accuracy:
|
|
254
|
-
- **Processing speed**: 35-45 fps on 1080p video (modern hardware)
|
|
255
|
-
- **Memory usage**: ~200-300 MB with efficient buffer management
|
|
256
|
-
- **Accuracy**: Matches vapoursynth-wwxd output (100% accurate)
|
|
257
|
-
- **Optimizations**: WASM SIMD, pre-allocated buffers, ring buffer streaming
|
|
258
|
-
|
|
259
|
-
## Requirements
|
|
260
|
-
|
|
261
|
-
- **Node.js**: 18.0.0 or higher
|
|
262
|
-
- **FFmpeg**: Automatically installed via `@ffmpeg-installer/ffmpeg`
|
|
263
|
-
|
|
264
|
-
## License
|
|
265
|
-
|
|
266
|
-
GPL-2.0
|
|
267
|
-
|
|
268
|
-
This project is based on:
|
|
269
|
-
- [vapoursynth-wwxd](https://github.com/dubhater/vapoursynth-wwxd) by dubhater (GPL-2.0)
|
|
270
|
-
- Xvid's motion estimation algorithm (GPL-2.0)
|
|
271
|
-
|
|
272
|
-
## Contributing
|
|
273
|
-
|
|
274
|
-
Contributions are welcome! Please feel free to submit issues or pull requests.
|
|
275
|
-
|
|
276
|
-
## Credits
|
|
277
|
-
|
|
278
|
-
- Original vapoursynth-wwxd plugin: [dubhater](https://github.com/dubhater)
|
|
279
|
-
- Xvid motion estimation algorithm: [Xvid Team](https://www.xvid.com)
|
|
280
|
-
- JavaScript/WASM port: This project
|
|
1
|
+
# scenecut
|
|
2
|
+
|
|
3
|
+
Fast, accurate scene change detection for Node.js using Xvid's motion estimation algorithm compiled to WebAssembly.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Fast**: WebAssembly-accelerated motion estimation (35-45 fps on typical hardware)
|
|
8
|
+
- **Accurate**: Uses Xvid's proven motion estimation algorithm from vapoursynth-wwxd
|
|
9
|
+
- **Multiple output formats**: Aegisub keyframes, timecodes, CSV, JSON
|
|
10
|
+
- **Easy to use**: Simple CLI and programmatic API
|
|
11
|
+
- **Cross-platform**: Works on Windows, Linux, and macOS
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### Global Installation (CLI)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @doedja/scenecut
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Local Installation (API)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @doedja/scenecut
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## CLI Usage
|
|
28
|
+
|
|
29
|
+
### Basic Usage
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Simple - detects scenes and saves to Aegisub format (default)
|
|
33
|
+
# Creates input_keyframes.txt
|
|
34
|
+
scenecut "input.mkv"
|
|
35
|
+
|
|
36
|
+
# Specify custom output filename
|
|
37
|
+
scenecut "video.mkv" -o keyframes.txt
|
|
38
|
+
|
|
39
|
+
# For timecode output
|
|
40
|
+
scenecut "video.mp4" --format timecode -o timecodes.txt
|
|
41
|
+
|
|
42
|
+
# JSON format with full metadata
|
|
43
|
+
scenecut "movie.avi" --format json
|
|
44
|
+
|
|
45
|
+
# CSV format for spreadsheet analysis
|
|
46
|
+
scenecut "movie.avi" --format csv -o scenes.csv
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### CLI Options
|
|
50
|
+
|
|
51
|
+
| Option | Alias | Description | Default |
|
|
52
|
+
|--------|-------|-------------|---------|
|
|
53
|
+
| `--format` | `-f` | Output format: `aegisub`, `json`, `csv`, `timecode` | `aegisub` |
|
|
54
|
+
| `--output` | `-o` | Output file path | `{filename}_keyframes.txt` |
|
|
55
|
+
| `--sensitivity` | `-s` | Detection sensitivity: `low`, `medium`, `high` | `medium` |
|
|
56
|
+
| `--quiet` | `-q` | Suppress progress output | `false` |
|
|
57
|
+
| `--verbose` | `-v` | Show detailed output including each scene | `false` |
|
|
58
|
+
| `--help` | `-h` | Show help message | - |
|
|
59
|
+
|
|
60
|
+
### Examples
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Default - creates anime_keyframes.txt in Aegisub format
|
|
64
|
+
scenecut "anime.mkv"
|
|
65
|
+
|
|
66
|
+
# High sensitivity for subtle scene changes
|
|
67
|
+
scenecut "anime.mkv" --sensitivity high
|
|
68
|
+
|
|
69
|
+
# JSON format with full metadata
|
|
70
|
+
scenecut "video.mp4" --format json -o results.json
|
|
71
|
+
|
|
72
|
+
# Verbose mode with detailed scene information
|
|
73
|
+
scenecut "movie.mkv" --verbose
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Output Formats
|
|
77
|
+
|
|
78
|
+
### Aegisub Format (`.txt`)
|
|
79
|
+
|
|
80
|
+
Aegisub keyframes format for subtitle timing:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
# keyframe format v1
|
|
84
|
+
fps 23.976
|
|
85
|
+
0
|
|
86
|
+
143
|
|
87
|
+
287
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Aegisub Workflow:**
|
|
91
|
+
1. Generate keyframes: `scenecut "video.mkv" -f aegisub -o keyframes.txt`
|
|
92
|
+
2. In Aegisub: **Video** → **Open Keyframes** → Select `keyframes.txt`
|
|
93
|
+
3. Keyframes appear as visual markers on the timeline for precise subtitle timing
|
|
94
|
+
|
|
95
|
+
### Timecode Format (`.txt`)
|
|
96
|
+
|
|
97
|
+
Simple timecode list (HH:MM:SS.mmm):
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
00:00:00.000
|
|
101
|
+
00:00:05.964
|
|
102
|
+
00:00:11.970
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### CSV Format (`.csv`)
|
|
106
|
+
|
|
107
|
+
Spreadsheet-compatible format:
|
|
108
|
+
|
|
109
|
+
```csv
|
|
110
|
+
frame,timestamp,timecode
|
|
111
|
+
0,0.0,00:00:00.000
|
|
112
|
+
143,5.964,00:00:05.964
|
|
113
|
+
287,11.970,00:00:11.970
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### JSON Format (`.json`)
|
|
117
|
+
|
|
118
|
+
Complete metadata and scene information:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"scenes": [
|
|
123
|
+
{
|
|
124
|
+
"frameNumber": 0,
|
|
125
|
+
"timestamp": 0.0,
|
|
126
|
+
"timecode": "00:00:00.000"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"frameNumber": 143,
|
|
130
|
+
"timestamp": 5.964,
|
|
131
|
+
"timecode": "00:00:05.964"
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
"metadata": {
|
|
135
|
+
"totalFrames": 3000,
|
|
136
|
+
"duration": 125.08,
|
|
137
|
+
"fps": 23.976,
|
|
138
|
+
"resolution": {
|
|
139
|
+
"width": 1920,
|
|
140
|
+
"height": 1080
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Programmatic API
|
|
147
|
+
|
|
148
|
+
### Basic Usage
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
const { detectSceneChanges } = require('@doedja/scenecut');
|
|
152
|
+
|
|
153
|
+
(async () => {
|
|
154
|
+
const results = await detectSceneChanges('input.mp4');
|
|
155
|
+
|
|
156
|
+
console.log(`Found ${results.scenes.length} scenes`);
|
|
157
|
+
results.scenes.forEach(scene => {
|
|
158
|
+
console.log(`Scene at frame ${scene.frameNumber} (${scene.timecode})`);
|
|
159
|
+
});
|
|
160
|
+
})();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Advanced Usage with Options
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
const { detectSceneChanges } = require('@doedja/scenecut');
|
|
167
|
+
|
|
168
|
+
const results = await detectSceneChanges('input.mp4', {
|
|
169
|
+
sensitivity: 'high', // 'low' | 'medium' | 'high'
|
|
170
|
+
searchRange: 'medium', // Motion search range
|
|
171
|
+
|
|
172
|
+
// Progress callback
|
|
173
|
+
onProgress: (progress) => {
|
|
174
|
+
console.log(`Progress: ${progress.percent}%`);
|
|
175
|
+
console.log(`Frame: ${progress.currentFrame}/${progress.totalFrames}`);
|
|
176
|
+
console.log(`FPS: ${progress.fps}, ETA: ${progress.eta}s`);
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
// Scene detection callback
|
|
180
|
+
onScene: (scene) => {
|
|
181
|
+
console.log(`Scene detected at frame ${scene.frameNumber}`);
|
|
182
|
+
console.log(`Timecode: ${scene.timecode}`);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
console.log('Detection complete!');
|
|
187
|
+
console.log(`Total scenes: ${results.scenes.length}`);
|
|
188
|
+
console.log(`Video duration: ${results.metadata.duration}s`);
|
|
189
|
+
console.log(`Resolution: ${results.metadata.resolution.width}x${results.metadata.resolution.height}`);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### API Reference
|
|
193
|
+
|
|
194
|
+
#### `detectSceneChanges(videoPath, options?)`
|
|
195
|
+
|
|
196
|
+
Detects scene changes in a video file.
|
|
197
|
+
|
|
198
|
+
**Parameters:**
|
|
199
|
+
- `videoPath` (string): Path to input video file
|
|
200
|
+
- `options` (object, optional):
|
|
201
|
+
- `sensitivity` ('low' | 'medium' | 'high'): Detection sensitivity (default: 'medium')
|
|
202
|
+
- `searchRange` ('auto' | 'small' | 'medium' | 'large'): Motion search range (default: 'medium')
|
|
203
|
+
- `onProgress` (function): Callback for progress updates
|
|
204
|
+
- `onScene` (function): Callback for each detected scene
|
|
205
|
+
|
|
206
|
+
**Returns:** Promise<DetectionResult>
|
|
207
|
+
|
|
208
|
+
**DetectionResult:**
|
|
209
|
+
```typescript
|
|
210
|
+
{
|
|
211
|
+
scenes: Array<{
|
|
212
|
+
frameNumber: number;
|
|
213
|
+
timestamp: number; // Seconds
|
|
214
|
+
timecode: string; // HH:MM:SS.mmm
|
|
215
|
+
}>;
|
|
216
|
+
metadata: {
|
|
217
|
+
totalFrames: number;
|
|
218
|
+
duration: number; // Seconds
|
|
219
|
+
fps: number;
|
|
220
|
+
resolution: {
|
|
221
|
+
width: number;
|
|
222
|
+
height: number;
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Supported Video Formats
|
|
229
|
+
|
|
230
|
+
Keyframes supports any video format that FFmpeg can decode, including:
|
|
231
|
+
|
|
232
|
+
- MP4 (`.mp4`, `.m4v`)
|
|
233
|
+
- Matroska (`.mkv`)
|
|
234
|
+
- AVI (`.avi`)
|
|
235
|
+
- WebM (`.webm`)
|
|
236
|
+
- MOV (`.mov`)
|
|
237
|
+
- FLV (`.flv`)
|
|
238
|
+
- And many more...
|
|
239
|
+
|
|
240
|
+
## How It Works
|
|
241
|
+
|
|
242
|
+
Keyframes uses Xvid's motion estimation algorithm to detect scene changes:
|
|
243
|
+
|
|
244
|
+
1. **Frame Extraction**: FFmpeg extracts grayscale frames from the video
|
|
245
|
+
2. **Motion Analysis**: WebAssembly-compiled C code analyzes motion vectors between consecutive frames
|
|
246
|
+
3. **Scene Detection**: Frames with high motion complexity are identified as scene changes
|
|
247
|
+
4. **Output Formatting**: Results are formatted according to the requested output format
|
|
248
|
+
|
|
249
|
+
The algorithm is based on [vapoursynth-wwxd](https://github.com/dubhater/vapoursynth-wwxd) by dubhater, which itself uses Xvid's motion estimation code.
|
|
250
|
+
|
|
251
|
+
## Performance
|
|
252
|
+
|
|
253
|
+
Optimized for speed and accuracy:
|
|
254
|
+
- **Processing speed**: 35-45 fps on 1080p video (modern hardware)
|
|
255
|
+
- **Memory usage**: ~200-300 MB with efficient buffer management
|
|
256
|
+
- **Accuracy**: Matches vapoursynth-wwxd output (100% accurate)
|
|
257
|
+
- **Optimizations**: WASM SIMD, pre-allocated buffers, ring buffer streaming
|
|
258
|
+
|
|
259
|
+
## Requirements
|
|
260
|
+
|
|
261
|
+
- **Node.js**: 18.0.0 or higher
|
|
262
|
+
- **FFmpeg**: Automatically installed via `@ffmpeg-installer/ffmpeg`
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
GPL-2.0
|
|
267
|
+
|
|
268
|
+
This project is based on:
|
|
269
|
+
- [vapoursynth-wwxd](https://github.com/dubhater/vapoursynth-wwxd) by dubhater (GPL-2.0)
|
|
270
|
+
- Xvid's motion estimation algorithm (GPL-2.0)
|
|
271
|
+
|
|
272
|
+
## Contributing
|
|
273
|
+
|
|
274
|
+
Contributions are welcome! Please feel free to submit issues or pull requests.
|
|
275
|
+
|
|
276
|
+
## Credits
|
|
277
|
+
|
|
278
|
+
- Original vapoursynth-wwxd plugin: [dubhater](https://github.com/dubhater)
|
|
279
|
+
- Xvid motion estimation algorithm: [Xvid Team](https://www.xvid.com)
|
|
280
|
+
- JavaScript/WASM port: This project
|