dashcam 1.0.1-beta.8 ā 1.0.2-beta.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/.github/workflows/publish.yml +26 -20
- package/691cc08dc2fc02f59ae66f08 (1).mp4 +0 -0
- package/NPM_PUBLISH_FIX.md +104 -0
- package/SINGLE_FRAME_VIDEO_FIX.md +129 -0
- package/bin/dashcam.js +27 -27
- package/lib/auth.js +5 -5
- package/lib/config.js +1 -1
- package/lib/ffmpeg.js +9 -39
- package/lib/processManager.js +23 -36
- package/lib/recorder.js +130 -26
- package/lib/systemInfo.js +141 -0
- package/lib/tracking/FileTracker.js +1 -1
- package/lib/tracking/icons/index.js +3 -2
- package/lib/tracking/icons/linux.js +370 -0
- package/lib/uploader.js +13 -7
- package/package.json +2 -1
- package/scripts/sync-version.sh +48 -0
- package/test-short-recording.js +287 -0
- package/test_workflow.sh +19 -14
- package/BACKWARD_COMPATIBILITY.md +0 -177
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test script for analyzing short recording issues
|
|
5
|
+
*
|
|
6
|
+
* This tests whether very short recordings produce valid multi-frame videos
|
|
7
|
+
* with properly finalized WebM container metadata.
|
|
8
|
+
*
|
|
9
|
+
* Known issue: If ffmpeg/VP9 encoder is killed too quickly, the WebM container
|
|
10
|
+
* metadata (especially duration) may be incomplete, causing playback issues.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* node test-short-recording.js # Run recording tests
|
|
14
|
+
* node test-short-recording.js analyze <file> # Analyze existing video
|
|
15
|
+
* node test-short-recording.js fix <input> <output> # Fix broken video container
|
|
16
|
+
*
|
|
17
|
+
* Platform notes:
|
|
18
|
+
* - macOS: Uses AVFoundation for screen capture
|
|
19
|
+
* - Linux: Uses X11grab for screen capture
|
|
20
|
+
* - Windows: Uses gdigrab for screen capture
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { startRecording, stopRecording, fixVideoContainer } from './lib/recorder.js';
|
|
24
|
+
import { execa } from 'execa';
|
|
25
|
+
import { getFfprobePath } from './lib/binaries.js';
|
|
26
|
+
import fs from 'fs';
|
|
27
|
+
import path from 'path';
|
|
28
|
+
import os from 'os';
|
|
29
|
+
|
|
30
|
+
async function analyzeVideo(videoPath) {
|
|
31
|
+
const ffprobePath = await getFfprobePath();
|
|
32
|
+
|
|
33
|
+
console.log(`\nš Analyzing video: ${videoPath}`);
|
|
34
|
+
console.log('ā'.repeat(80));
|
|
35
|
+
|
|
36
|
+
// Check if file exists
|
|
37
|
+
if (!fs.existsSync(videoPath)) {
|
|
38
|
+
console.error(`ā Video file does not exist: ${videoPath}`);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const stats = fs.statSync(videoPath);
|
|
43
|
+
console.log(`š File size: ${(stats.size / 1024).toFixed(2)} KB`);
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
// Get basic format info
|
|
47
|
+
const formatResult = await execa(ffprobePath, [
|
|
48
|
+
'-v', 'error',
|
|
49
|
+
'-show_entries', 'format=duration,size,bit_rate',
|
|
50
|
+
'-of', 'json',
|
|
51
|
+
videoPath
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
const formatData = JSON.parse(formatResult.stdout);
|
|
55
|
+
console.log(`ā±ļø Duration: ${formatData.format.duration || 'unknown'}s`);
|
|
56
|
+
console.log(`š Bit rate: ${formatData.format.bit_rate || 'unknown'} bits/s`);
|
|
57
|
+
|
|
58
|
+
// Get stream info
|
|
59
|
+
const streamResult = await execa(ffprobePath, [
|
|
60
|
+
'-v', 'error',
|
|
61
|
+
'-show_entries', 'stream=codec_name,width,height,r_frame_rate,duration',
|
|
62
|
+
'-of', 'json',
|
|
63
|
+
videoPath
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
const streamData = JSON.parse(streamResult.stdout);
|
|
67
|
+
const videoStream = streamData.streams.find(s => s.codec_name);
|
|
68
|
+
|
|
69
|
+
if (videoStream) {
|
|
70
|
+
console.log(`š„ Codec: ${videoStream.codec_name}`);
|
|
71
|
+
console.log(`š Resolution: ${videoStream.width}x${videoStream.height}`);
|
|
72
|
+
console.log(`šļø Frame rate: ${videoStream.r_frame_rate}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Count actual frames
|
|
76
|
+
const frameResult = await execa(ffprobePath, [
|
|
77
|
+
'-v', 'error',
|
|
78
|
+
'-count_frames',
|
|
79
|
+
'-select_streams', 'v:0',
|
|
80
|
+
'-show_entries', 'stream=nb_read_frames',
|
|
81
|
+
'-of', 'default=nokey=1:noprint_wrappers=1',
|
|
82
|
+
videoPath
|
|
83
|
+
], { reject: false });
|
|
84
|
+
|
|
85
|
+
const frameCount = parseInt(frameResult.stdout.trim());
|
|
86
|
+
console.log(`š¼ļø Frame count: ${frameCount || 'unknown'}`);
|
|
87
|
+
|
|
88
|
+
if (frameResult.stderr) {
|
|
89
|
+
console.log(`ā ļø FFprobe warnings: ${frameResult.stderr.trim()}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check if duration is available in container
|
|
93
|
+
const hasDuration = formatData.format.duration && !isNaN(parseFloat(formatData.format.duration));
|
|
94
|
+
|
|
95
|
+
// Determine if this is a single-frame video issue
|
|
96
|
+
const isSingleFrame = frameCount === 1;
|
|
97
|
+
const hasEncodingIssues = frameResult.stderr.includes('File ended prematurely');
|
|
98
|
+
const hasMissingMetadata = !hasDuration;
|
|
99
|
+
|
|
100
|
+
console.log('\nš Analysis Result:');
|
|
101
|
+
console.log(` Single frame: ${isSingleFrame ? 'ā YES (BUG!)' : 'ā
NO'}`);
|
|
102
|
+
console.log(` Encoding issues: ${hasEncodingIssues ? 'ā ļø YES' : 'ā
NO'}`);
|
|
103
|
+
console.log(` Missing metadata: ${hasMissingMetadata ? 'ā ļø YES (container incomplete)' : 'ā
NO'}`);
|
|
104
|
+
console.log(` Platform: ${os.platform()}`);
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
exists: true,
|
|
108
|
+
size: stats.size,
|
|
109
|
+
duration: parseFloat(formatData.format.duration),
|
|
110
|
+
frameCount,
|
|
111
|
+
codec: videoStream?.codec_name,
|
|
112
|
+
resolution: videoStream ? `${videoStream.width}x${videoStream.height}` : 'unknown',
|
|
113
|
+
isSingleFrame,
|
|
114
|
+
hasEncodingIssues,
|
|
115
|
+
hasMissingMetadata,
|
|
116
|
+
platform: os.platform()
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error(`ā Error analyzing video: ${error.message}`);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function testShortRecording(duration = 3000) {
|
|
126
|
+
console.log(`\nš¬ Testing ${duration}ms recording...`);
|
|
127
|
+
console.log('ā'.repeat(80));
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
// Start recording
|
|
131
|
+
console.log('ā¶ļø Starting recording...');
|
|
132
|
+
const { outputPath, startTime } = await startRecording({
|
|
133
|
+
fps: 30,
|
|
134
|
+
includeAudio: false
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
console.log(`ā
Recording started at: ${outputPath}`);
|
|
138
|
+
|
|
139
|
+
// Wait for specified duration
|
|
140
|
+
console.log(`ā³ Recording for ${duration}ms...`);
|
|
141
|
+
await new Promise(resolve => setTimeout(resolve, duration));
|
|
142
|
+
|
|
143
|
+
// Stop recording
|
|
144
|
+
console.log('ā¹ļø Stopping recording...');
|
|
145
|
+
const result = await stopRecording();
|
|
146
|
+
|
|
147
|
+
console.log(`ā
Recording stopped`);
|
|
148
|
+
console.log(` Duration: ${result.duration}ms`);
|
|
149
|
+
console.log(` File: ${result.outputPath}`);
|
|
150
|
+
|
|
151
|
+
// Analyze the output
|
|
152
|
+
await analyzeVideo(result.outputPath);
|
|
153
|
+
|
|
154
|
+
return result;
|
|
155
|
+
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error(`ā Test failed: ${error.message}`);
|
|
158
|
+
console.error(error.stack);
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function testExistingVideo(videoPath) {
|
|
164
|
+
console.log('\nš Testing existing video...');
|
|
165
|
+
console.log('ā'.repeat(80));
|
|
166
|
+
|
|
167
|
+
return await analyzeVideo(videoPath);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Main test runner
|
|
171
|
+
async function main() {
|
|
172
|
+
const args = process.argv.slice(2);
|
|
173
|
+
|
|
174
|
+
console.log('\nš§Ŗ Short Recording Test Suite');
|
|
175
|
+
console.log('ā'.repeat(80));
|
|
176
|
+
console.log(`Platform: ${os.platform()}`);
|
|
177
|
+
console.log(`Architecture: ${os.arch()}`);
|
|
178
|
+
console.log(`Node version: ${process.version}`);
|
|
179
|
+
|
|
180
|
+
if (args[0] === 'analyze' && args[1]) {
|
|
181
|
+
// Analyze existing video
|
|
182
|
+
const videoPath = path.resolve(args[1]);
|
|
183
|
+
const result = await testExistingVideo(videoPath);
|
|
184
|
+
|
|
185
|
+
if (result?.isSingleFrame) {
|
|
186
|
+
console.log('\nā SINGLE-FRAME VIDEO DETECTED!');
|
|
187
|
+
process.exit(1);
|
|
188
|
+
} else if (result?.hasMissingMetadata) {
|
|
189
|
+
console.log('\nā ļø WARNING: Video container metadata is incomplete!');
|
|
190
|
+
console.log(' This can cause playback issues in some players.');
|
|
191
|
+
console.log(' The video has frames but duration is not in the container.');
|
|
192
|
+
console.log('\nš” Try fixing it with:');
|
|
193
|
+
console.log(` node test-short-recording.js fix ${args[1]} ${args[1].replace(/\.(webm|mp4)$/, '-fixed.$1')}`);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
} else if (args[0] === 'fix' && args[1] && args[2]) {
|
|
197
|
+
// Fix existing broken video
|
|
198
|
+
const inputPath = path.resolve(args[1]);
|
|
199
|
+
const outputPath = path.resolve(args[2]);
|
|
200
|
+
|
|
201
|
+
console.log('\nš§ Fixing video container...');
|
|
202
|
+
console.log('ā'.repeat(80));
|
|
203
|
+
console.log(`Input: ${inputPath}`);
|
|
204
|
+
console.log(`Output: ${outputPath}`);
|
|
205
|
+
|
|
206
|
+
if (!fs.existsSync(inputPath)) {
|
|
207
|
+
console.error(`ā Input file does not exist: ${inputPath}`);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Analyze before
|
|
212
|
+
console.log('\nš BEFORE:');
|
|
213
|
+
const beforeResult = await analyzeVideo(inputPath);
|
|
214
|
+
|
|
215
|
+
// Fix the video
|
|
216
|
+
const fixSuccess = await fixVideoContainer(inputPath, outputPath);
|
|
217
|
+
|
|
218
|
+
if (!fixSuccess) {
|
|
219
|
+
console.error('\nā Failed to fix video!');
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Analyze after
|
|
224
|
+
console.log('\nš AFTER:');
|
|
225
|
+
const afterResult = await analyzeVideo(outputPath);
|
|
226
|
+
|
|
227
|
+
console.log('\nā
Video fixed successfully!');
|
|
228
|
+
console.log(` Before: ${beforeResult?.hasMissingMetadata ? 'Missing metadata ā ļø' : 'Has metadata ā
'}`);
|
|
229
|
+
console.log(` After: ${afterResult?.hasMissingMetadata ? 'Missing metadata ā ļø' : 'Has metadata ā
'}`);
|
|
230
|
+
|
|
231
|
+
if (afterResult?.hasMissingMetadata) {
|
|
232
|
+
console.log('\nā ļø Warning: Metadata still missing after fix. The source file may be corrupted.');
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
// Run recording tests with different durations
|
|
237
|
+
const testDurations = [1000, 2000, 3000, 5000];
|
|
238
|
+
const results = [];
|
|
239
|
+
|
|
240
|
+
for (const duration of testDurations) {
|
|
241
|
+
try {
|
|
242
|
+
const result = await testShortRecording(duration);
|
|
243
|
+
results.push({ duration, success: true, result });
|
|
244
|
+
|
|
245
|
+
// Clean up
|
|
246
|
+
try {
|
|
247
|
+
fs.unlinkSync(result.outputPath);
|
|
248
|
+
if (result.gifPath && fs.existsSync(result.gifPath)) {
|
|
249
|
+
fs.unlinkSync(result.gifPath);
|
|
250
|
+
}
|
|
251
|
+
if (result.snapshotPath && fs.existsSync(result.snapshotPath)) {
|
|
252
|
+
fs.unlinkSync(result.snapshotPath);
|
|
253
|
+
}
|
|
254
|
+
} catch (cleanupError) {
|
|
255
|
+
console.warn(`ā ļø Cleanup warning: ${cleanupError.message}`);
|
|
256
|
+
}
|
|
257
|
+
} catch (error) {
|
|
258
|
+
results.push({ duration, success: false, error: error.message });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Wait between tests
|
|
262
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Summary
|
|
266
|
+
console.log('\n\nš TEST SUMMARY');
|
|
267
|
+
console.log('ā'.repeat(80));
|
|
268
|
+
|
|
269
|
+
for (const result of results) {
|
|
270
|
+
const status = result.success ? 'ā
' : 'ā';
|
|
271
|
+
console.log(`${status} ${result.duration}ms recording: ${result.success ? 'PASSED' : result.error}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const allPassed = results.every(r => r.success);
|
|
275
|
+
if (!allPassed) {
|
|
276
|
+
console.log('\nā Some tests failed!');
|
|
277
|
+
process.exit(1);
|
|
278
|
+
} else {
|
|
279
|
+
console.log('\nā
All tests passed!');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
main().catch(error => {
|
|
285
|
+
console.error('Fatal error:', error);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
});
|
package/test_workflow.sh
CHANGED
|
@@ -46,7 +46,7 @@ RECORDING_START=$(date +%s)
|
|
|
46
46
|
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
47
47
|
echo "š“ EVENT 1: Recording START at $(date '+%H:%M:%S')"
|
|
48
48
|
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
49
|
-
echo "[EVENT 1] Recording started at $(date '+%H:%M:%S') - TIMESTAMP: $RECORDING_START" >> "$TEMP_FILE"
|
|
49
|
+
echo "[EVENT 1] š“ Recording started with emoji at $(date '+%H:%M:%S') - TIMESTAMP: $RECORDING_START" >> "$TEMP_FILE"
|
|
50
50
|
|
|
51
51
|
# Verify recording is actually running
|
|
52
52
|
if ps -p $RECORD_PID > /dev/null; then
|
|
@@ -71,25 +71,25 @@ sleep 3
|
|
|
71
71
|
# Event 2 - after 3 seconds
|
|
72
72
|
echo ""
|
|
73
73
|
echo "š” EVENT 2: 3 seconds mark at $(date '+%H:%M:%S')"
|
|
74
|
-
echo "[EVENT 2] 3 seconds elapsed at $(date '+%H:%M:%S')" >> "$TEMP_FILE"
|
|
74
|
+
echo "[EVENT 2] š” 3 seconds elapsed with emoji at $(date '+%H:%M:%S')" >> "$TEMP_FILE"
|
|
75
75
|
sleep 3
|
|
76
76
|
|
|
77
77
|
# Event 3 - after 6 seconds
|
|
78
78
|
echo ""
|
|
79
79
|
echo "š¢ EVENT 3: 6 seconds mark at $(date '+%H:%M:%S')"
|
|
80
|
-
echo "[EVENT 3] 6 seconds elapsed at $(date '+%H:%M:%S')" >> "$TEMP_FILE"
|
|
80
|
+
echo "[EVENT 3] š¢ 6 seconds elapsed with emoji at $(date '+%H:%M:%S')" >> "$TEMP_FILE"
|
|
81
81
|
sleep 3
|
|
82
82
|
|
|
83
83
|
# Event 4 - after 9 seconds
|
|
84
84
|
echo ""
|
|
85
85
|
echo "šµ EVENT 4: 9 seconds mark at $(date '+%H:%M:%S')"
|
|
86
|
-
echo "[EVENT 4] 9 seconds elapsed at $(date '+%H:%M:%S')" >> "$TEMP_FILE"
|
|
86
|
+
echo "[EVENT 4] šµ 9 seconds elapsed with emoji at $(date '+%H:%M:%S')" >> "$TEMP_FILE"
|
|
87
87
|
sleep 3
|
|
88
88
|
|
|
89
89
|
# Event 5 - after 12 seconds
|
|
90
90
|
echo ""
|
|
91
91
|
echo "š£ EVENT 5: 12 seconds mark at $(date '+%H:%M:%S')"
|
|
92
|
-
echo "[EVENT 5] 12 seconds elapsed at $(date '+%H:%M:%S')" >> "$TEMP_FILE"
|
|
92
|
+
echo "[EVENT 5] š£ 12 seconds elapsed with emoji at $(date '+%H:%M:%S')" >> "$TEMP_FILE"
|
|
93
93
|
sleep 3
|
|
94
94
|
|
|
95
95
|
# Event 6 - before ending
|
|
@@ -98,7 +98,7 @@ echo "āāāāāāāāāāāāāāāāāāāāāāāā
|
|
|
98
98
|
echo "ā« EVENT 6: Recording END at $(date '+%H:%M:%S')"
|
|
99
99
|
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
100
100
|
RECORDING_END=$(date +%s)
|
|
101
|
-
echo "[EVENT 6] Recording ending at $(date '+%H:%M:%S') - TIMESTAMP: $RECORDING_END" >> "$TEMP_FILE"
|
|
101
|
+
echo "[EVENT 6] ā« Recording ending with emoji at $(date '+%H:%M:%S') - TIMESTAMP: $RECORDING_END" >> "$TEMP_FILE"
|
|
102
102
|
|
|
103
103
|
DURATION=$((RECORDING_END - RECORDING_START))
|
|
104
104
|
echo ""
|
|
@@ -112,8 +112,13 @@ sleep 2
|
|
|
112
112
|
# 6. Stop recording and upload (this will kill the background recording process)
|
|
113
113
|
echo ""
|
|
114
114
|
echo "6. Stopping recording and uploading..."
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
# Check if recording is still active
|
|
116
|
+
if ./bin/dashcam.js status | grep -q "Recording in progress"; then
|
|
117
|
+
./bin/dashcam.js stop
|
|
118
|
+
echo "ā
Recording stopped and uploaded"
|
|
119
|
+
else
|
|
120
|
+
echo "ā ļø Recording already completed (this is expected with background mode)"
|
|
121
|
+
fi
|
|
117
122
|
|
|
118
123
|
echo ""
|
|
119
124
|
echo "š§¹ Cleaning up..."
|
|
@@ -140,12 +145,12 @@ echo "3. Verify these events appear at the correct times:"
|
|
|
140
145
|
echo ""
|
|
141
146
|
echo " Time | Terminal Display | Log Entry"
|
|
142
147
|
echo " -------|---------------------------|---------------------------"
|
|
143
|
-
echo " 0:00 | š“ EVENT 1 | [EVENT 1] Recording started"
|
|
144
|
-
echo " 0:03 | š” EVENT 2 | [EVENT 2] 3 seconds elapsed"
|
|
145
|
-
echo " 0:06 | š¢ EVENT 3 | [EVENT 3] 6 seconds elapsed"
|
|
146
|
-
echo " 0:09 | šµ EVENT 4 | [EVENT 4] 9 seconds elapsed"
|
|
147
|
-
echo " 0:12 | š£ EVENT 5 | [EVENT 5] 12 seconds elapsed"
|
|
148
|
-
echo " 0:15 | ā« EVENT 6 | [EVENT 6] Recording ending"
|
|
148
|
+
echo " 0:00 | š“ EVENT 1 | [EVENT 1] š“ Recording started"
|
|
149
|
+
echo " 0:03 | š” EVENT 2 | [EVENT 2] š” 3 seconds elapsed"
|
|
150
|
+
echo " 0:06 | š¢ EVENT 3 | [EVENT 3] š¢ 6 seconds elapsed"
|
|
151
|
+
echo " 0:09 | šµ EVENT 4 | [EVENT 4] šµ 9 seconds elapsed"
|
|
152
|
+
echo " 0:12 | š£ EVENT 5 | [EVENT 5] š£ 12 seconds elapsed"
|
|
153
|
+
echo " 0:15 | ā« EVENT 6 | [EVENT 6] ā« Recording ending"
|
|
149
154
|
echo ""
|
|
150
155
|
echo "4. The log timestamps should match the video timeline exactly"
|
|
151
156
|
echo "5. Each colored event marker should appear in the video"
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
# Backward Compatibility Summary
|
|
2
|
-
|
|
3
|
-
This document confirms that `dashcam-cli-minimal` now supports all commands and arguments documented in the README.md.
|
|
4
|
-
|
|
5
|
-
## ā
Implemented Commands
|
|
6
|
-
|
|
7
|
-
### `auth <api-key>`
|
|
8
|
-
Authenticate the dashcam desktop using a team's apiKey.
|
|
9
|
-
```bash
|
|
10
|
-
dashcam auth <api-key>
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
### `create [options]`
|
|
14
|
-
Create a clip from current recording and output the resulting url or markdown. This stops the current recording and uploads it.
|
|
15
|
-
```bash
|
|
16
|
-
# Start instant replay in background
|
|
17
|
-
dashcam start
|
|
18
|
-
|
|
19
|
-
# Later, create a clip from the recording
|
|
20
|
-
dashcam create
|
|
21
|
-
dashcam create -t "My New Title"
|
|
22
|
-
dashcam create --md
|
|
23
|
-
dashcam create -k wef8we72h23012j
|
|
24
|
-
dashcam create -d "Description text"
|
|
25
|
-
cat README.md | dashcam create
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Options:
|
|
29
|
-
- `-t, --title <string>` - Title of the replay
|
|
30
|
-
- `-d, --description [text]` - Replay markdown body (supports piped input)
|
|
31
|
-
- `--md` - Returns rich markdown image link
|
|
32
|
-
- `-k, --project <project>` - Project ID to publish to
|
|
33
|
-
|
|
34
|
-
**Note:** `create` stops the current recording and creates a clip. It's similar to `stop` but focused on outputting URLs/markdown for integration with other tools.
|
|
35
|
-
|
|
36
|
-
### `record [options]`
|
|
37
|
-
Start a recording terminal to be included in your dashcam video recording.
|
|
38
|
-
```bash
|
|
39
|
-
dashcam record
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Options:
|
|
43
|
-
- `-t, --title <title>` - Title for the recording
|
|
44
|
-
- `-d, --description <description>` - Description for the recording
|
|
45
|
-
- `-p, --project <project>` - Project ID to upload to
|
|
46
|
-
- `-a, --audio` - Include audio
|
|
47
|
-
- `-f, --fps <fps>` - Frames per second
|
|
48
|
-
|
|
49
|
-
### `pipe`
|
|
50
|
-
Pipe command output to dashcam to be included in recorded video.
|
|
51
|
-
```bash
|
|
52
|
-
ping 1.1.1.1 | dashcam pipe
|
|
53
|
-
cat /var/log/system.log | dashcam pipe
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### `track [options]`
|
|
57
|
-
Add a logs config to Dashcam.
|
|
58
|
-
|
|
59
|
-
**New Syntax (matches README):**
|
|
60
|
-
```bash
|
|
61
|
-
dashcam track --name=social --type=web --pattern="*facebook.com*" --pattern="*twitter.com*"
|
|
62
|
-
dashcam track --name=app-logs --type=application --pattern="/var/log/*.log"
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
**Old Syntax (still supported):**
|
|
66
|
-
```bash
|
|
67
|
-
dashcam track --web "*facebook.com*"
|
|
68
|
-
dashcam track --app "/var/log/app.log"
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Options:
|
|
72
|
-
- `--name <name>` - Name for the tracking configuration (required with new syntax)
|
|
73
|
-
- `--type <type>` - Type: "application" or "web" (required with new syntax)
|
|
74
|
-
- `--pattern <pattern>` - Pattern to track (can use multiple times)
|
|
75
|
-
- `--web <pattern>` - Web URL pattern (deprecated, use --type=web --pattern)
|
|
76
|
-
- `--app <pattern>` - Application file pattern (deprecated, use --type=application --pattern)
|
|
77
|
-
|
|
78
|
-
### `start`
|
|
79
|
-
Start instant replay recording on dashcam.
|
|
80
|
-
```bash
|
|
81
|
-
dashcam start
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### `stop`
|
|
85
|
-
Stop the current recording and upload.
|
|
86
|
-
```bash
|
|
87
|
-
dashcam stop
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### `status`
|
|
91
|
-
Show current recording status.
|
|
92
|
-
```bash
|
|
93
|
-
dashcam status
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Examples from README
|
|
97
|
-
|
|
98
|
-
All examples from the README should now work:
|
|
99
|
-
|
|
100
|
-
### Basic usage
|
|
101
|
-
```bash
|
|
102
|
-
# Create a replay
|
|
103
|
-
dashcam create
|
|
104
|
-
# Returns: https://dashcam.io/replay/123?share=xyz
|
|
105
|
-
|
|
106
|
-
# With markdown output
|
|
107
|
-
dashcam create --md
|
|
108
|
-
|
|
109
|
-
# With title
|
|
110
|
-
dashcam create -t "My New Title"
|
|
111
|
-
|
|
112
|
-
# With project
|
|
113
|
-
dashcam create -k wef8we72h23012j
|
|
114
|
-
|
|
115
|
-
# Attach last 20 CLI commands
|
|
116
|
-
history -20 | dashcam create
|
|
117
|
-
|
|
118
|
-
# Attach a logfile
|
|
119
|
-
cat /var/log/system.log | dashcam create
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Tracking logs
|
|
123
|
-
```bash
|
|
124
|
-
# Track web URLs
|
|
125
|
-
dashcam track --name=social --type=web --pattern="*facebook.com*" --pattern="*twitter.com*"
|
|
126
|
-
|
|
127
|
-
# Track application files
|
|
128
|
-
dashcam track --name=app-logs --type=application --pattern="/var/log/*.log"
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### Recording
|
|
132
|
-
```bash
|
|
133
|
-
# Start recording
|
|
134
|
-
dashcam record
|
|
135
|
-
|
|
136
|
-
# Pipe output into recording
|
|
137
|
-
ping 1.1.1.1 | dashcam pipe
|
|
138
|
-
|
|
139
|
-
# Stop recording
|
|
140
|
-
dashcam stop
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### GitHub CLI integration
|
|
144
|
-
```bash
|
|
145
|
-
# Create GitHub issue with replay
|
|
146
|
-
gh issue create -w -t "Title" -b "`dashcam create --md`"
|
|
147
|
-
|
|
148
|
-
# With system logs
|
|
149
|
-
gh issue create -w -t "Title" -b "`cat /var/log/system.log | dashcam create --md`"
|
|
150
|
-
|
|
151
|
-
# Create PR with replay
|
|
152
|
-
gh pr create -w -t "Title" -b "`dashcam create --md`"
|
|
153
|
-
|
|
154
|
-
# Append to commit
|
|
155
|
-
git commit -am "`dashcam create`"
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## Key Changes for Backward Compatibility
|
|
159
|
-
|
|
160
|
-
1. **Added `create` command** - Stops current recording and creates a clip with URL/markdown output
|
|
161
|
-
2. **Added `pipe` command** - Allows piping command output into recordings
|
|
162
|
-
3. **Added `start` command** - Simple way to start instant replay recording in background
|
|
163
|
-
4. **Updated `track` command** - Now supports both old syntax (--web, --app) and new syntax (--name, --type, --pattern)
|
|
164
|
-
5. **Updated descriptions** - Match README text exactly
|
|
165
|
-
6. **Updated `auth` parameter** - Changed from `<apiKey>` to `<api-key>` to match README
|
|
166
|
-
7. **Added `-k` alias** - For `--project` option in `create` command
|
|
167
|
-
8. **Shared implementation** - `create`, `record`, and `start` share common code to avoid duplication
|
|
168
|
-
|
|
169
|
-
## Migration Notes
|
|
170
|
-
|
|
171
|
-
- **`start`** - Starts instant replay recording in background (like desktop app's always-on recording)
|
|
172
|
-
- **`create`** - Stops the current recording and outputs URL/markdown (perfect for CI/CD, git hooks, GitHub CLI)
|
|
173
|
-
- **`record`** - Full-featured recording command with all options (terminal recording mode)
|
|
174
|
-
- **`stop`** - Similar to `create` but focused on stopping and uploading vs URL output
|
|
175
|
-
- Old `track` syntax still works for backward compatibility but new syntax is preferred
|
|
176
|
-
- All piped input examples from README are supported
|
|
177
|
-
- Can run just `dashcam` with options (defaults to `create` command)
|