@design.estate/dees-wcctools 1.2.1 → 2.0.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/dist_bundle/bundle.js +1764 -218
- package/dist_bundle/bundle.js.map +4 -4
- package/dist_ts_demotools/demotools.d.ts +1 -1
- package/dist_ts_demotools/demotools.js +86 -38
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/wcc-dashboard.d.ts +11 -10
- package/dist_ts_web/elements/wcc-dashboard.js +370 -246
- package/dist_ts_web/elements/wcc-frame.d.ts +3 -3
- package/dist_ts_web/elements/wcc-frame.js +108 -57
- package/dist_ts_web/elements/wcc-properties.d.ts +14 -8
- package/dist_ts_web/elements/wcc-properties.js +442 -323
- package/dist_ts_web/elements/wcc-record-button.d.ts +12 -0
- package/dist_ts_web/elements/wcc-record-button.js +165 -0
- package/dist_ts_web/elements/wcc-recording-panel.d.ts +42 -0
- package/dist_ts_web/elements/wcc-recording-panel.js +1067 -0
- package/dist_ts_web/elements/wcc-sidebar.d.ts +7 -5
- package/dist_ts_web/elements/wcc-sidebar.js +250 -81
- package/dist_ts_web/elements/wcctools.helpers.d.ts +13 -0
- package/dist_ts_web/elements/wcctools.helpers.js +26 -1
- package/dist_ts_web/index.d.ts +3 -0
- package/dist_ts_web/index.js +5 -1
- package/dist_ts_web/services/ffmpeg.service.d.ts +42 -0
- package/dist_ts_web/services/ffmpeg.service.js +276 -0
- package/dist_ts_web/services/mp4.service.d.ts +32 -0
- package/dist_ts_web/services/mp4.service.js +139 -0
- package/dist_ts_web/services/recorder.service.d.ts +44 -0
- package/dist_ts_web/services/recorder.service.js +307 -0
- package/dist_watch/bundle.js +2126 -541
- package/dist_watch/bundle.js.map +4 -4
- package/package.json +8 -8
- package/readme.md +133 -141
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/wcc-dashboard.ts +86 -26
- package/ts_web/elements/wcc-frame.ts +3 -3
- package/ts_web/elements/wcc-properties.ts +53 -9
- package/ts_web/elements/wcc-record-button.ts +108 -0
- package/ts_web/elements/wcc-recording-panel.ts +978 -0
- package/ts_web/elements/wcc-sidebar.ts +133 -22
- package/ts_web/elements/wcctools.helpers.ts +31 -0
- package/ts_web/index.ts +5 -0
- package/ts_web/readme.md +123 -0
- package/ts_web/services/recorder.service.ts +393 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RecorderService - Handles all MediaRecorder, audio monitoring, and video export logic
|
|
3
|
+
*/
|
|
4
|
+
export class RecorderService {
|
|
5
|
+
// Recording state
|
|
6
|
+
mediaRecorder = null;
|
|
7
|
+
recordedChunks = [];
|
|
8
|
+
durationInterval = null;
|
|
9
|
+
_duration = 0;
|
|
10
|
+
_recordedBlob = null;
|
|
11
|
+
_isRecording = false;
|
|
12
|
+
// Audio monitoring state
|
|
13
|
+
audioContext = null;
|
|
14
|
+
audioAnalyser = null;
|
|
15
|
+
audioMonitoringInterval = null;
|
|
16
|
+
monitoringStream = null;
|
|
17
|
+
// Current recording stream
|
|
18
|
+
currentStream = null;
|
|
19
|
+
// Event callbacks
|
|
20
|
+
events = {};
|
|
21
|
+
constructor(events) {
|
|
22
|
+
if (events) {
|
|
23
|
+
this.events = events;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Public getters
|
|
27
|
+
get isRecording() {
|
|
28
|
+
return this._isRecording;
|
|
29
|
+
}
|
|
30
|
+
get duration() {
|
|
31
|
+
return this._duration;
|
|
32
|
+
}
|
|
33
|
+
get recordedBlob() {
|
|
34
|
+
return this._recordedBlob;
|
|
35
|
+
}
|
|
36
|
+
// Update event callbacks
|
|
37
|
+
setEvents(events) {
|
|
38
|
+
this.events = { ...this.events, ...events };
|
|
39
|
+
}
|
|
40
|
+
// ==================== Microphone Management ====================
|
|
41
|
+
async loadMicrophones(requestPermission = false) {
|
|
42
|
+
try {
|
|
43
|
+
if (requestPermission) {
|
|
44
|
+
// Request permission by getting a temporary stream
|
|
45
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
46
|
+
stream.getTracks().forEach(track => track.stop());
|
|
47
|
+
}
|
|
48
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
49
|
+
return devices.filter(d => d.kind === 'audioinput');
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error('Error loading microphones:', error);
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async startAudioMonitoring(deviceId) {
|
|
57
|
+
this.stopAudioMonitoring();
|
|
58
|
+
if (!deviceId)
|
|
59
|
+
return;
|
|
60
|
+
try {
|
|
61
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
62
|
+
audio: { deviceId: { exact: deviceId } }
|
|
63
|
+
});
|
|
64
|
+
this.monitoringStream = stream;
|
|
65
|
+
this.audioContext = new AudioContext();
|
|
66
|
+
const source = this.audioContext.createMediaStreamSource(stream);
|
|
67
|
+
this.audioAnalyser = this.audioContext.createAnalyser();
|
|
68
|
+
this.audioAnalyser.fftSize = 256;
|
|
69
|
+
source.connect(this.audioAnalyser);
|
|
70
|
+
const dataArray = new Uint8Array(this.audioAnalyser.frequencyBinCount);
|
|
71
|
+
this.audioMonitoringInterval = window.setInterval(() => {
|
|
72
|
+
if (this.audioAnalyser) {
|
|
73
|
+
this.audioAnalyser.getByteFrequencyData(dataArray);
|
|
74
|
+
const average = dataArray.reduce((a, b) => a + b) / dataArray.length;
|
|
75
|
+
const level = Math.min(100, (average / 128) * 100);
|
|
76
|
+
this.events.onAudioLevelUpdate?.(level);
|
|
77
|
+
}
|
|
78
|
+
}, 50);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error('Error starting audio monitoring:', error);
|
|
82
|
+
this.events.onAudioLevelUpdate?.(0);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
stopAudioMonitoring() {
|
|
86
|
+
if (this.audioMonitoringInterval) {
|
|
87
|
+
clearInterval(this.audioMonitoringInterval);
|
|
88
|
+
this.audioMonitoringInterval = null;
|
|
89
|
+
}
|
|
90
|
+
if (this.audioContext) {
|
|
91
|
+
this.audioContext.close();
|
|
92
|
+
this.audioContext = null;
|
|
93
|
+
}
|
|
94
|
+
if (this.monitoringStream) {
|
|
95
|
+
this.monitoringStream.getTracks().forEach(track => track.stop());
|
|
96
|
+
this.monitoringStream = null;
|
|
97
|
+
}
|
|
98
|
+
this.audioAnalyser = null;
|
|
99
|
+
}
|
|
100
|
+
// ==================== Recording Control ====================
|
|
101
|
+
async startRecording(options) {
|
|
102
|
+
try {
|
|
103
|
+
// Stop audio monitoring before recording
|
|
104
|
+
this.stopAudioMonitoring();
|
|
105
|
+
// Get video stream based on mode
|
|
106
|
+
const displayMediaOptions = {
|
|
107
|
+
video: {
|
|
108
|
+
displaySurface: options.mode === 'viewport' ? 'browser' : 'monitor'
|
|
109
|
+
},
|
|
110
|
+
audio: false
|
|
111
|
+
};
|
|
112
|
+
// Add preferCurrentTab hint for viewport mode
|
|
113
|
+
if (options.mode === 'viewport') {
|
|
114
|
+
displayMediaOptions.preferCurrentTab = true;
|
|
115
|
+
}
|
|
116
|
+
const videoStream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
|
|
117
|
+
// If viewport mode, try to crop to viewport element using Element Capture API
|
|
118
|
+
if (options.mode === 'viewport' && options.viewportElement) {
|
|
119
|
+
try {
|
|
120
|
+
if ('CropTarget' in window) {
|
|
121
|
+
const cropTarget = await window.CropTarget.fromElement(options.viewportElement);
|
|
122
|
+
const [videoTrack] = videoStream.getVideoTracks();
|
|
123
|
+
await videoTrack.cropTo(cropTarget);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
console.warn('Element Capture not supported, recording full tab:', e);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Combine video with audio if enabled
|
|
131
|
+
let combinedStream = videoStream;
|
|
132
|
+
if (options.audioDeviceId) {
|
|
133
|
+
try {
|
|
134
|
+
const audioStream = await navigator.mediaDevices.getUserMedia({
|
|
135
|
+
audio: { deviceId: { exact: options.audioDeviceId } }
|
|
136
|
+
});
|
|
137
|
+
combinedStream = new MediaStream([
|
|
138
|
+
...videoStream.getVideoTracks(),
|
|
139
|
+
...audioStream.getAudioTracks()
|
|
140
|
+
]);
|
|
141
|
+
}
|
|
142
|
+
catch (audioError) {
|
|
143
|
+
console.warn('Could not add audio:', audioError);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Store stream for cleanup
|
|
147
|
+
this.currentStream = combinedStream;
|
|
148
|
+
// Create MediaRecorder
|
|
149
|
+
const mimeType = MediaRecorder.isTypeSupported('video/webm;codecs=vp9')
|
|
150
|
+
? 'video/webm;codecs=vp9'
|
|
151
|
+
: 'video/webm';
|
|
152
|
+
this.mediaRecorder = new MediaRecorder(combinedStream, { mimeType });
|
|
153
|
+
this.recordedChunks = [];
|
|
154
|
+
this.mediaRecorder.ondataavailable = (e) => {
|
|
155
|
+
if (e.data.size > 0) {
|
|
156
|
+
this.recordedChunks.push(e.data);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
this.mediaRecorder.onstop = () => this.handleRecordingComplete();
|
|
160
|
+
// Handle stream ending (user clicks "Stop sharing")
|
|
161
|
+
videoStream.getVideoTracks()[0].onended = () => {
|
|
162
|
+
if (this._isRecording) {
|
|
163
|
+
this.stopRecording();
|
|
164
|
+
this.events.onStreamEnded?.();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
this.mediaRecorder.start(1000); // Capture in 1-second chunks
|
|
168
|
+
// Start duration timer
|
|
169
|
+
this._duration = 0;
|
|
170
|
+
this.durationInterval = window.setInterval(() => {
|
|
171
|
+
this._duration++;
|
|
172
|
+
this.events.onDurationUpdate?.(this._duration);
|
|
173
|
+
}, 1000);
|
|
174
|
+
this._isRecording = true;
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console.error('Error starting recording:', error);
|
|
178
|
+
this._isRecording = false;
|
|
179
|
+
this.events.onError?.(error);
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
stopRecording() {
|
|
184
|
+
if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
|
|
185
|
+
this.mediaRecorder.stop();
|
|
186
|
+
}
|
|
187
|
+
if (this.durationInterval) {
|
|
188
|
+
clearInterval(this.durationInterval);
|
|
189
|
+
this.durationInterval = null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async handleRecordingComplete() {
|
|
193
|
+
// Create blob from recorded chunks
|
|
194
|
+
const blob = new Blob(this.recordedChunks, { type: 'video/webm' });
|
|
195
|
+
this._recordedBlob = blob;
|
|
196
|
+
// Stop all tracks
|
|
197
|
+
if (this.currentStream) {
|
|
198
|
+
this.currentStream.getTracks().forEach(track => track.stop());
|
|
199
|
+
this.currentStream = null;
|
|
200
|
+
}
|
|
201
|
+
this._isRecording = false;
|
|
202
|
+
this.events.onRecordingComplete?.(this._recordedBlob);
|
|
203
|
+
}
|
|
204
|
+
// ==================== Trim & Export ====================
|
|
205
|
+
async exportTrimmedVideo(videoElement, trimStart, trimEnd) {
|
|
206
|
+
return new Promise((resolve, reject) => {
|
|
207
|
+
// Create a canvas for capturing frames
|
|
208
|
+
const canvas = document.createElement('canvas');
|
|
209
|
+
canvas.width = videoElement.videoWidth || 1280;
|
|
210
|
+
canvas.height = videoElement.videoHeight || 720;
|
|
211
|
+
const ctx = canvas.getContext('2d');
|
|
212
|
+
if (!ctx) {
|
|
213
|
+
reject(new Error('Could not get canvas context'));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
// Create canvas stream for video
|
|
217
|
+
const canvasStream = canvas.captureStream(30);
|
|
218
|
+
// Try to capture audio from video element
|
|
219
|
+
let combinedStream;
|
|
220
|
+
try {
|
|
221
|
+
// Create audio context to capture video's audio
|
|
222
|
+
const audioCtx = new AudioContext();
|
|
223
|
+
const source = audioCtx.createMediaElementSource(videoElement);
|
|
224
|
+
const destination = audioCtx.createMediaStreamDestination();
|
|
225
|
+
source.connect(destination);
|
|
226
|
+
source.connect(audioCtx.destination); // Also play through speakers
|
|
227
|
+
// Combine video (from canvas) and audio (from video element)
|
|
228
|
+
combinedStream = new MediaStream([
|
|
229
|
+
...canvasStream.getVideoTracks(),
|
|
230
|
+
...destination.stream.getAudioTracks()
|
|
231
|
+
]);
|
|
232
|
+
// Store audioCtx for cleanup
|
|
233
|
+
const cleanup = () => {
|
|
234
|
+
audioCtx.close();
|
|
235
|
+
};
|
|
236
|
+
this.recordTrimmedStream(videoElement, canvas, ctx, combinedStream, trimStart, trimEnd, cleanup, resolve, reject);
|
|
237
|
+
}
|
|
238
|
+
catch (audioError) {
|
|
239
|
+
console.warn('Could not capture audio, recording video only:', audioError);
|
|
240
|
+
combinedStream = canvasStream;
|
|
241
|
+
this.recordTrimmedStream(videoElement, canvas, ctx, combinedStream, trimStart, trimEnd, () => { }, resolve, reject);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
recordTrimmedStream(video, canvas, ctx, stream, trimStart, trimEnd, cleanup, resolve, reject) {
|
|
246
|
+
const mimeType = MediaRecorder.isTypeSupported('video/webm;codecs=vp9')
|
|
247
|
+
? 'video/webm;codecs=vp9'
|
|
248
|
+
: 'video/webm';
|
|
249
|
+
const recorder = new MediaRecorder(stream, { mimeType });
|
|
250
|
+
const chunks = [];
|
|
251
|
+
recorder.ondataavailable = (e) => {
|
|
252
|
+
if (e.data.size > 0) {
|
|
253
|
+
chunks.push(e.data);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
recorder.onstop = () => {
|
|
257
|
+
cleanup();
|
|
258
|
+
resolve(new Blob(chunks, { type: 'video/webm' }));
|
|
259
|
+
};
|
|
260
|
+
recorder.onerror = (e) => {
|
|
261
|
+
cleanup();
|
|
262
|
+
reject(new Error('Recording error: ' + e));
|
|
263
|
+
};
|
|
264
|
+
// Seek to trim start
|
|
265
|
+
video.currentTime = trimStart;
|
|
266
|
+
video.onseeked = () => {
|
|
267
|
+
// Start recording
|
|
268
|
+
recorder.start(100);
|
|
269
|
+
// Start playing
|
|
270
|
+
video.play();
|
|
271
|
+
// Draw frames to canvas
|
|
272
|
+
const drawFrame = () => {
|
|
273
|
+
if (video.currentTime >= trimEnd || video.paused || video.ended) {
|
|
274
|
+
video.pause();
|
|
275
|
+
video.onseeked = null;
|
|
276
|
+
// Give a small delay before stopping to ensure last frame is captured
|
|
277
|
+
setTimeout(() => {
|
|
278
|
+
if (recorder.state === 'recording') {
|
|
279
|
+
recorder.stop();
|
|
280
|
+
}
|
|
281
|
+
}, 100);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
285
|
+
requestAnimationFrame(drawFrame);
|
|
286
|
+
};
|
|
287
|
+
drawFrame();
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
// ==================== Cleanup ====================
|
|
291
|
+
reset() {
|
|
292
|
+
this._recordedBlob = null;
|
|
293
|
+
this.recordedChunks = [];
|
|
294
|
+
this._duration = 0;
|
|
295
|
+
this._isRecording = false;
|
|
296
|
+
}
|
|
297
|
+
dispose() {
|
|
298
|
+
this.stopRecording();
|
|
299
|
+
this.stopAudioMonitoring();
|
|
300
|
+
this.reset();
|
|
301
|
+
if (this.currentStream) {
|
|
302
|
+
this.currentStream.getTracks().forEach(track => track.stop());
|
|
303
|
+
this.currentStream = null;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVjb3JkZXIuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX3dlYi9zZXJ2aWNlcy9yZWNvcmRlci5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBZ0JILE1BQU0sT0FBTyxlQUFlO0lBQzFCLGtCQUFrQjtJQUNWLGFBQWEsR0FBeUIsSUFBSSxDQUFDO0lBQzNDLGNBQWMsR0FBVyxFQUFFLENBQUM7SUFDNUIsZ0JBQWdCLEdBQWtCLElBQUksQ0FBQztJQUN2QyxTQUFTLEdBQVcsQ0FBQyxDQUFDO0lBQ3RCLGFBQWEsR0FBZ0IsSUFBSSxDQUFDO0lBQ2xDLFlBQVksR0FBWSxLQUFLLENBQUM7SUFFdEMseUJBQXlCO0lBQ2pCLFlBQVksR0FBd0IsSUFBSSxDQUFDO0lBQ3pDLGFBQWEsR0FBd0IsSUFBSSxDQUFDO0lBQzFDLHVCQUF1QixHQUFrQixJQUFJLENBQUM7SUFDOUMsZ0JBQWdCLEdBQXVCLElBQUksQ0FBQztJQUVwRCwyQkFBMkI7SUFDbkIsYUFBYSxHQUF1QixJQUFJLENBQUM7SUFFakQsa0JBQWtCO0lBQ1YsTUFBTSxHQUFvQixFQUFFLENBQUM7SUFFckMsWUFBWSxNQUF3QjtRQUNsQyxJQUFJLE1BQU0sRUFBRSxDQUFDO1lBQ1gsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDdkIsQ0FBQztJQUNILENBQUM7SUFFRCxpQkFBaUI7SUFDakIsSUFBSSxXQUFXO1FBQ2IsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDO0lBQzNCLENBQUM7SUFFRCxJQUFJLFFBQVE7UUFDVixPQUFPLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDeEIsQ0FBQztJQUVELElBQUksWUFBWTtRQUNkLE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQztJQUM1QixDQUFDO0lBRUQseUJBQXlCO0lBQ3pCLFNBQVMsQ0FBQyxNQUF1QjtRQUMvQixJQUFJLENBQUMsTUFBTSxHQUFHLEVBQUUsR0FBRyxJQUFJ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
|