@ruc-lib/screen-recorder 2.9.2 → 3.1.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/README.md +1 -150
- package/{fesm2020 → fesm2022}/ruc-lib-screen-recorder.mjs +622 -634
- package/fesm2022/ruc-lib-screen-recorder.mjs.map +1 -0
- package/index.d.ts +288 -4
- package/package.json +13 -25
- package/esm2020/index.mjs +0 -5
- package/esm2020/lib/models/screen-recorder.models.mjs +0 -20
- package/esm2020/lib/ruclib-screen-recorder.module.mjs +0 -20
- package/esm2020/lib/screen-recorder/screen-recorder-constant.mjs +0 -44
- package/esm2020/lib/screen-recorder/screen-recorder.component.mjs +0 -242
- package/esm2020/lib/services/screen-recorder.service.mjs +0 -333
- package/esm2020/ruc-lib-screen-recorder.mjs +0 -5
- package/fesm2015/ruc-lib-screen-recorder.mjs +0 -644
- package/fesm2015/ruc-lib-screen-recorder.mjs.map +0 -1
- package/fesm2020/ruc-lib-screen-recorder.mjs.map +0 -1
- package/lib/models/screen-recorder.models.d.ts +0 -51
- package/lib/ruclib-screen-recorder.module.d.ts +0 -8
- package/lib/screen-recorder/screen-recorder-constant.d.ts +0 -27
- package/lib/screen-recorder/screen-recorder.component.d.ts +0 -100
- package/lib/services/screen-recorder.service.d.ts +0 -111
|
@@ -1,644 +0,0 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, Component, ChangeDetectionStrategy, Input, ViewChild, NgModule } from '@angular/core';
|
|
3
|
-
import * as i4 from '@angular/common';
|
|
4
|
-
import { CommonModule } from '@angular/common';
|
|
5
|
-
import { __awaiter } from 'tslib';
|
|
6
|
-
import { BehaviorSubject, Subject, timer, Subscription } from 'rxjs';
|
|
7
|
-
import { tap, takeWhile } from 'rxjs/operators';
|
|
8
|
-
import * as i2 from '@angular/platform-browser';
|
|
9
|
-
|
|
10
|
-
var ScreenRecorderPosition;
|
|
11
|
-
(function (ScreenRecorderPosition) {
|
|
12
|
-
ScreenRecorderPosition["Top"] = "top";
|
|
13
|
-
ScreenRecorderPosition["Bottom"] = "bottom";
|
|
14
|
-
ScreenRecorderPosition["Left"] = "left";
|
|
15
|
-
ScreenRecorderPosition["Right"] = "right";
|
|
16
|
-
})(ScreenRecorderPosition || (ScreenRecorderPosition = {}));
|
|
17
|
-
var DisplayFormat;
|
|
18
|
-
(function (DisplayFormat) {
|
|
19
|
-
DisplayFormat["Icon"] = "icon";
|
|
20
|
-
DisplayFormat["Text"] = "text";
|
|
21
|
-
})(DisplayFormat || (DisplayFormat = {}));
|
|
22
|
-
var RecordingState;
|
|
23
|
-
(function (RecordingState) {
|
|
24
|
-
RecordingState["Idle"] = "idle";
|
|
25
|
-
RecordingState["Recording"] = "recording";
|
|
26
|
-
RecordingState["Paused"] = "paused";
|
|
27
|
-
RecordingState["Stopped"] = "stopped";
|
|
28
|
-
})(RecordingState || (RecordingState = {}));
|
|
29
|
-
|
|
30
|
-
class ScreenRecorderConstants {
|
|
31
|
-
constructor() {
|
|
32
|
-
this.DEFAULT_ICONS = {
|
|
33
|
-
record: 'fiber_manual_record',
|
|
34
|
-
stop: 'stop',
|
|
35
|
-
pause: 'pause',
|
|
36
|
-
resume: 'play_arrow',
|
|
37
|
-
play: 'play_circle_filled',
|
|
38
|
-
download: 'download',
|
|
39
|
-
audioOn: 'volume_up',
|
|
40
|
-
audioOff: 'volume_off',
|
|
41
|
-
};
|
|
42
|
-
this.DEFAULT_LABELS = {
|
|
43
|
-
recordButton: 'Start Recording',
|
|
44
|
-
stopButton: 'Stop Recording',
|
|
45
|
-
pauseButton: 'Pause Recording',
|
|
46
|
-
resumeButton: 'Resume Recording',
|
|
47
|
-
audioToggleButton: 'Toggle Audio',
|
|
48
|
-
downloadButton: 'Download Recording',
|
|
49
|
-
playbackVideo: 'Screen Recording Playback',
|
|
50
|
-
};
|
|
51
|
-
this.INACTIVE = 'inactive';
|
|
52
|
-
this.AUDIO_ON = 'Audio ON';
|
|
53
|
-
this.AUDIO_OFF = 'Audio OFF';
|
|
54
|
-
this.RECORDING_IN_PROGRESS_ERROR = 'Recording is already in progress.';
|
|
55
|
-
this.FAILED_TO_GET_DISPLAY_MEDIA_STREAM_ERROR = 'Failed to get display media stream';
|
|
56
|
-
this.NO_SUPPORT_MIME = 'No supported MIME type found for MediaRecorder.';
|
|
57
|
-
this.NO_RECORDING_DOWNLOAD = 'No recording available to download.';
|
|
58
|
-
this.MEDIA_ERROR = 'MediaRecorder error';
|
|
59
|
-
this.PERMISSION_DENIED = "Error starting screen recording: NotAllowedError - Permission denied";
|
|
60
|
-
this.NO_SUPPORT = 'Screen recording is not supported in this browser.';
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
ScreenRecorderConstants.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ScreenRecorderConstants, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
64
|
-
ScreenRecorderConstants.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ScreenRecorderConstants, providedIn: 'root' });
|
|
65
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ScreenRecorderConstants, decorators: [{
|
|
66
|
-
type: Injectable,
|
|
67
|
-
args: [{
|
|
68
|
-
providedIn: 'root',
|
|
69
|
-
}]
|
|
70
|
-
}], ctorParameters: function () { return []; } });
|
|
71
|
-
|
|
72
|
-
class ScreenRecorderService {
|
|
73
|
-
constructor(screenRecordingConstant) {
|
|
74
|
-
this.screenRecordingConstant = screenRecordingConstant;
|
|
75
|
-
this.stream = null;
|
|
76
|
-
this.mediaRecorder = null;
|
|
77
|
-
this.recordedBlobs = [];
|
|
78
|
-
this.currentRecordedTime = 0;
|
|
79
|
-
this.recordingStateSubject = new BehaviorSubject(RecordingState.Idle);
|
|
80
|
-
this.recordingState$ = this.recordingStateSubject.asObservable();
|
|
81
|
-
this.recordedTimeSubject = new BehaviorSubject(0);
|
|
82
|
-
this.recordedTime$ = this.recordedTimeSubject.asObservable();
|
|
83
|
-
this.recordedUrlSubject = new BehaviorSubject(null);
|
|
84
|
-
this.recordedUrl$ = this.recordedUrlSubject.asObservable();
|
|
85
|
-
this.recordingTimestampSubject = new BehaviorSubject('');
|
|
86
|
-
this.recordingTimestamp$ = this.recordingTimestampSubject.asObservable();
|
|
87
|
-
this.errorSubject = new Subject();
|
|
88
|
-
this.error$ = this.errorSubject.asObservable();
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Starts the screen recording with optional audio
|
|
92
|
-
*
|
|
93
|
-
* @param options - Configuration options for recording
|
|
94
|
-
* @param options.audio - Whether to include audio in the recording
|
|
95
|
-
*
|
|
96
|
-
* @throws Error if recording is already in progress or if media access is denied
|
|
97
|
-
* @returns Promise<void>
|
|
98
|
-
*/
|
|
99
|
-
startRecording(options) {
|
|
100
|
-
var _a, _b, _c, _d;
|
|
101
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
-
if (this.recordingStateSubject.value === RecordingState.Recording || this.recordingStateSubject.value === RecordingState.Paused) {
|
|
103
|
-
this.handleError(this.screenRecordingConstant.RECORDING_IN_PROGRESS_ERROR);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
this.cleanupPreviousRecording();
|
|
107
|
-
try {
|
|
108
|
-
// The browser's native picker handles "specific area" selection (window, tab, screen).
|
|
109
|
-
const displayStream = yield navigator.mediaDevices.getDisplayMedia({
|
|
110
|
-
video: true
|
|
111
|
-
});
|
|
112
|
-
// If audio is enabled, get the audio stream
|
|
113
|
-
let audioStream = null;
|
|
114
|
-
if (options.audio) {
|
|
115
|
-
audioStream = yield navigator.mediaDevices.getUserMedia({
|
|
116
|
-
audio: {
|
|
117
|
-
echoCancellation: true,
|
|
118
|
-
noiseSuppression: true,
|
|
119
|
-
sampleRate: 44100
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
// Combine the streams if audio is enabled
|
|
124
|
-
if (audioStream) {
|
|
125
|
-
this.stream = new MediaStream([...displayStream.getTracks(), ...audioStream.getTracks()]);
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
this.stream = displayStream;
|
|
129
|
-
}
|
|
130
|
-
// Listen for when the user stops sharing via browser UI
|
|
131
|
-
this.stream.getVideoTracks()[0].onended = () => {
|
|
132
|
-
if (this.recordingStateSubject.value === RecordingState.Recording || this.recordingStateSubject.value === RecordingState.Paused) {
|
|
133
|
-
this.stopRecordingInternal();
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
// Handle errors
|
|
137
|
-
if (!this.stream) {
|
|
138
|
-
this.handleError(this.screenRecordingConstant.FAILED_TO_GET_DISPLAY_MEDIA_STREAM_ERROR);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
// Set recording timestamp
|
|
142
|
-
const now = new Date();
|
|
143
|
-
const timestamp = now.toLocaleString('en-IN', {
|
|
144
|
-
year: 'numeric',
|
|
145
|
-
month: '2-digit',
|
|
146
|
-
day: '2-digit',
|
|
147
|
-
hour: '2-digit',
|
|
148
|
-
minute: '2-digit',
|
|
149
|
-
hour12: true
|
|
150
|
-
});
|
|
151
|
-
this.recordingTimestampSubject.next(timestamp);
|
|
152
|
-
this.recordedBlobs = [];
|
|
153
|
-
const mimeType = this.getSupportedMimeType();
|
|
154
|
-
if (!mimeType) {
|
|
155
|
-
this.handleError(this.screenRecordingConstant.NO_SUPPORT_MIME);
|
|
156
|
-
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach(track => track.stop());
|
|
157
|
-
this.stream = null;
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
// Create a canvas for timestamp overlay
|
|
161
|
-
const canvas = document.createElement('canvas');
|
|
162
|
-
const videoTrack = (_b = this.stream) === null || _b === void 0 ? void 0 : _b.getVideoTracks()[0];
|
|
163
|
-
if (videoTrack) {
|
|
164
|
-
const video = document.createElement('video');
|
|
165
|
-
video.srcObject = this.stream;
|
|
166
|
-
// Wait for video to load metadata
|
|
167
|
-
video.onloadedmetadata = () => {
|
|
168
|
-
canvas.width = video.videoWidth;
|
|
169
|
-
canvas.height = video.videoHeight;
|
|
170
|
-
const ctx = canvas.getContext('2d');
|
|
171
|
-
if (ctx) {
|
|
172
|
-
// Draw video frame
|
|
173
|
-
ctx.drawImage(video, 0, 0);
|
|
174
|
-
// Draw timestamp
|
|
175
|
-
ctx.font = '16px Arial';
|
|
176
|
-
ctx.fillStyle = 'white';
|
|
177
|
-
ctx.textAlign = 'right';
|
|
178
|
-
ctx.textBaseline = 'top';
|
|
179
|
-
ctx.fillText(`Recorded on: ${this.recordingTimestampSubject.value}`, canvas.width - 10, 10);
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
// Create a new stream with the canvas
|
|
184
|
-
const canvasStream = canvas.captureStream();
|
|
185
|
-
const combinedStream = new MediaStream([...(_d = (_c = this.stream) === null || _c === void 0 ? void 0 : _c.getTracks()) !== null && _d !== void 0 ? _d : [], canvasStream.getVideoTracks()[0]]);
|
|
186
|
-
this.mediaRecorder = new MediaRecorder(combinedStream, { mimeType });
|
|
187
|
-
this.mediaRecorder.ondataavailable = (event) => {
|
|
188
|
-
if (event.data && event.data.size > 0) {
|
|
189
|
-
this.recordedBlobs.push(event.data);
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
this.mediaRecorder.onstop = () => {
|
|
193
|
-
const superBuffer = new Blob(this.recordedBlobs, { type: mimeType });
|
|
194
|
-
const url = window.URL.createObjectURL(superBuffer);
|
|
195
|
-
this.recordedUrlSubject.next(url);
|
|
196
|
-
this.recordingStateSubject.next(RecordingState.Stopped);
|
|
197
|
-
this.stopTimer();
|
|
198
|
-
};
|
|
199
|
-
this.mediaRecorder.onerror = (event) => {
|
|
200
|
-
const errorEvent = event; // More specific type if available
|
|
201
|
-
let message = this.screenRecordingConstant.MEDIA_ERROR;
|
|
202
|
-
if (errorEvent.error && errorEvent.error.name) {
|
|
203
|
-
message += `: ${errorEvent.error.name}`;
|
|
204
|
-
if (errorEvent.error.message)
|
|
205
|
-
message += ` - ${errorEvent.error.message}`;
|
|
206
|
-
}
|
|
207
|
-
this.handleError(message);
|
|
208
|
-
this.recordingStateSubject.next(RecordingState.Idle);
|
|
209
|
-
this.stopTimer();
|
|
210
|
-
};
|
|
211
|
-
this.mediaRecorder.start(); // Start recording
|
|
212
|
-
this.recordingStateSubject.next(RecordingState.Recording);
|
|
213
|
-
this.startTimer();
|
|
214
|
-
}
|
|
215
|
-
catch (err) {
|
|
216
|
-
this.handleError(`Error starting screen recording: ${err.name} - ${err.message}`);
|
|
217
|
-
this.recordingStateSubject.next(RecordingState.Idle);
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Gets the first supported MIME type for MediaRecorder
|
|
223
|
-
*
|
|
224
|
-
* @returns string|null - The supported MIME type or null if none found
|
|
225
|
-
*/
|
|
226
|
-
getSupportedMimeType() {
|
|
227
|
-
const mimeTypes = [
|
|
228
|
-
'video/webm;codecs=vp9,opus',
|
|
229
|
-
'video/webm;codecs=vp8,opus',
|
|
230
|
-
'video/webm;codecs=h264,opus',
|
|
231
|
-
'video/mp4;codecs=h264,aac',
|
|
232
|
-
'video/webm',
|
|
233
|
-
];
|
|
234
|
-
for (const mimeType of mimeTypes) {
|
|
235
|
-
if (MediaRecorder.isTypeSupported(mimeType)) {
|
|
236
|
-
return mimeType;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Internal method to stop the recording process
|
|
243
|
-
*
|
|
244
|
-
* @private
|
|
245
|
-
* @returns void
|
|
246
|
-
*/
|
|
247
|
-
stopRecordingInternal() {
|
|
248
|
-
var _a;
|
|
249
|
-
if (this.mediaRecorder && (this.recordingStateSubject.value === RecordingState.Recording || this.recordingStateSubject.value === RecordingState.Paused)) {
|
|
250
|
-
this.mediaRecorder.stop();
|
|
251
|
-
}
|
|
252
|
-
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach(track => track.stop());
|
|
253
|
-
this.stream = null;
|
|
254
|
-
// State will be updated by onstop handler
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
*
|
|
258
|
-
* @returns Promise<void>
|
|
259
|
-
* @throws Error if recording is not in progress
|
|
260
|
-
*/
|
|
261
|
-
stopRecording() {
|
|
262
|
-
this.stopRecordingInternal();
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Pauses the current recording
|
|
266
|
-
*
|
|
267
|
-
* @returns Promise<void>
|
|
268
|
-
* @throws Error if recording is not in progress
|
|
269
|
-
*/
|
|
270
|
-
pauseRecording() {
|
|
271
|
-
if (this.mediaRecorder && this.recordingStateSubject.value === RecordingState.Recording) {
|
|
272
|
-
this.mediaRecorder.pause();
|
|
273
|
-
this.recordingStateSubject.next(RecordingState.Paused);
|
|
274
|
-
this.stopTimer(); // Pauses the timer display
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Resumes a paused recording
|
|
279
|
-
*
|
|
280
|
-
* @returns Promise<void>
|
|
281
|
-
* @throws Error if recording is not paused
|
|
282
|
-
*/
|
|
283
|
-
resumeRecording() {
|
|
284
|
-
if (this.mediaRecorder && this.recordingStateSubject.value === RecordingState.Paused) {
|
|
285
|
-
this.mediaRecorder.resume();
|
|
286
|
-
this.recordingStateSubject.next(RecordingState.Recording);
|
|
287
|
-
this.startTimer(this.currentRecordedTime); // Resumes timer display
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
*
|
|
292
|
-
* @param fileName
|
|
293
|
-
*/
|
|
294
|
-
downloadRecording(fileName = 'recording.webm') {
|
|
295
|
-
const url = this.recordedUrlSubject.value;
|
|
296
|
-
const timestamp = this.recordingTimestampSubject.value;
|
|
297
|
-
if (url && timestamp) {
|
|
298
|
-
// Format the timestamp for filename (remove spaces and special characters)
|
|
299
|
-
const formattedTimestamp = timestamp.replace(/[^a-zA-Z0-9]/g, '_');
|
|
300
|
-
// Create filename with timestamp
|
|
301
|
-
const fileExtension = fileName.split('.').pop() || 'webm';
|
|
302
|
-
const newFileName = `screen_recording_${formattedTimestamp}.${fileExtension}`;
|
|
303
|
-
const a = document.createElement('a');
|
|
304
|
-
a.style.display = 'none';
|
|
305
|
-
a.href = url;
|
|
306
|
-
a.download = newFileName;
|
|
307
|
-
document.body.appendChild(a);
|
|
308
|
-
a.click();
|
|
309
|
-
document.body.removeChild(a);
|
|
310
|
-
// No need to revoke URL here if user might want to play it again.
|
|
311
|
-
// Revoke on cleanup or new recording.
|
|
312
|
-
}
|
|
313
|
-
else {
|
|
314
|
-
this.handleError(this.screenRecordingConstant.NO_RECORDING_DOWNLOAD);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Starts the recording timer
|
|
319
|
-
*
|
|
320
|
-
* @param startTime - Optional start time for the timer
|
|
321
|
-
* @private
|
|
322
|
-
* @returns void
|
|
323
|
-
*/
|
|
324
|
-
startTimer(startTime = 0) {
|
|
325
|
-
this.stopTimer(); // Ensure no multiple timers
|
|
326
|
-
this.currentRecordedTime = startTime;
|
|
327
|
-
this.recordedTimeSubject.next(this.currentRecordedTime);
|
|
328
|
-
this.timerSubscription = timer(0, 1000)
|
|
329
|
-
.pipe(tap(() => {
|
|
330
|
-
if (this.recordingStateSubject.value === RecordingState.Recording) {
|
|
331
|
-
this.currentRecordedTime++;
|
|
332
|
-
this.recordedTimeSubject.next(this.currentRecordedTime);
|
|
333
|
-
}
|
|
334
|
-
}), takeWhile(() => this.recordingStateSubject.value === RecordingState.Recording))
|
|
335
|
-
.subscribe();
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Stops the recording timer
|
|
339
|
-
*
|
|
340
|
-
* @private
|
|
341
|
-
* @returns void
|
|
342
|
-
*/
|
|
343
|
-
stopTimer() {
|
|
344
|
-
if (this.timerSubscription) {
|
|
345
|
-
this.timerSubscription.unsubscribe();
|
|
346
|
-
this.timerSubscription = null;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* Cleans up resources from previous recording
|
|
351
|
-
*
|
|
352
|
-
* @private
|
|
353
|
-
* @returns void
|
|
354
|
-
*/
|
|
355
|
-
cleanupPreviousRecording() {
|
|
356
|
-
if (this.recordedUrlSubject.value) {
|
|
357
|
-
window.URL.revokeObjectURL(this.recordedUrlSubject.value);
|
|
358
|
-
this.recordedUrlSubject.next(null);
|
|
359
|
-
}
|
|
360
|
-
this.recordedBlobs = [];
|
|
361
|
-
this.currentRecordedTime = 0;
|
|
362
|
-
this.recordedTimeSubject.next(0);
|
|
363
|
-
if (this.stream) {
|
|
364
|
-
this.stream.getTracks().forEach(track => track.stop());
|
|
365
|
-
this.stream = null;
|
|
366
|
-
}
|
|
367
|
-
if (this.mediaRecorder && this.mediaRecorder.state !== this.screenRecordingConstant.INACTIVE) {
|
|
368
|
-
this.mediaRecorder.stop();
|
|
369
|
-
}
|
|
370
|
-
this.mediaRecorder = null;
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Resets the recording state to idle
|
|
374
|
-
*
|
|
375
|
-
* @returns void
|
|
376
|
-
*/
|
|
377
|
-
resetToIdle() {
|
|
378
|
-
this.cleanupPreviousRecording();
|
|
379
|
-
this.recordingStateSubject.next(RecordingState.Idle);
|
|
380
|
-
this.errorSubject.next(''); // Clear any previous errors
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* Handles and broadcasts recording errors
|
|
384
|
-
*
|
|
385
|
-
* @private
|
|
386
|
-
* @param error - The error message to handle
|
|
387
|
-
* @returns void
|
|
388
|
-
*/
|
|
389
|
-
handleError(error) {
|
|
390
|
-
this.errorSubject.next(error);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
ScreenRecorderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ScreenRecorderService, deps: [{ token: ScreenRecorderConstants }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
394
|
-
ScreenRecorderService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ScreenRecorderService, providedIn: 'root' });
|
|
395
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ScreenRecorderService, decorators: [{
|
|
396
|
-
type: Injectable,
|
|
397
|
-
args: [{
|
|
398
|
-
providedIn: 'root', // Or provide in your module if preferred
|
|
399
|
-
}]
|
|
400
|
-
}], ctorParameters: function () { return [{ type: ScreenRecorderConstants }]; } });
|
|
401
|
-
|
|
402
|
-
class ScreenRecorderComponent {
|
|
403
|
-
set rucInputData(value) {
|
|
404
|
-
var _a;
|
|
405
|
-
this._options = Object.assign({ position: ScreenRecorderPosition.Bottom, displayFormat: DisplayFormat.Icon, showAudioToggle: true, defaultAudioState: true, downloadFileName: 'screen-recording.webm', icons: Object.assign({}, this.screenRecordingConstant.DEFAULT_ICONS), showTimer: true, showPauseResume: true, showPlaybackControls: true, showDownloadButton: true, useUTCForTimer: false, allowSpecificAreaSelection: true, accessibilityLabels: Object.assign({}, this.screenRecordingConstant.DEFAULT_LABELS) }, value);
|
|
406
|
-
// Deep merge icons and labels
|
|
407
|
-
if (value.icons) {
|
|
408
|
-
this._options.icons = Object.assign(Object.assign({}, this.screenRecordingConstant.DEFAULT_ICONS), value.icons);
|
|
409
|
-
}
|
|
410
|
-
if (value.accessibilityLabels) {
|
|
411
|
-
this._options.accessibilityLabels = Object.assign(Object.assign({}, this.screenRecordingConstant.DEFAULT_LABELS), value.accessibilityLabels);
|
|
412
|
-
}
|
|
413
|
-
this.isAudioEnabled = (_a = this._options.defaultAudioState) !== null && _a !== void 0 ? _a : true;
|
|
414
|
-
}
|
|
415
|
-
get rucInputData() {
|
|
416
|
-
return this._options;
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* Checks if recording is currently in progress
|
|
420
|
-
*
|
|
421
|
-
* @returns boolean - True if recording is in progress, false otherwise
|
|
422
|
-
*/
|
|
423
|
-
isRecordingInProgress() {
|
|
424
|
-
return this.recordingState === RecordingState.Recording;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Gets the appropriate icon for recording based on audio state and settings
|
|
428
|
-
* @returns string - The icon name to display
|
|
429
|
-
*/
|
|
430
|
-
getRecordingIcon() {
|
|
431
|
-
if (this.isAudioEnabled && this.rucInputData.showAudioToggle) {
|
|
432
|
-
return this.getIcon('recordWithAudio');
|
|
433
|
-
}
|
|
434
|
-
return this.getIcon('record');
|
|
435
|
-
}
|
|
436
|
-
constructor(screenRecorderService, sanitizer, cdr, screenRecordingConstant) {
|
|
437
|
-
this.screenRecorderService = screenRecorderService;
|
|
438
|
-
this.sanitizer = sanitizer;
|
|
439
|
-
this.cdr = cdr;
|
|
440
|
-
this.screenRecordingConstant = screenRecordingConstant;
|
|
441
|
-
this.recordingState = RecordingState.Idle;
|
|
442
|
-
this.recordedTime = 0;
|
|
443
|
-
this.formattedTime = '00:00:00';
|
|
444
|
-
this.safeRecordedUrl = null;
|
|
445
|
-
this.recordingTimestamp = '';
|
|
446
|
-
this.errorMessage = null;
|
|
447
|
-
this.isBrowserSupported = true;
|
|
448
|
-
this.isAudioEnabled = true;
|
|
449
|
-
// Expose enums to template
|
|
450
|
-
this.DisplayFormat = DisplayFormat;
|
|
451
|
-
this.RecordingState = RecordingState;
|
|
452
|
-
this.ScreenRecorderPosition = ScreenRecorderPosition;
|
|
453
|
-
this.subscriptions = new Subscription();
|
|
454
|
-
// Initialize with default options. The setter will override if an input is provided.
|
|
455
|
-
if (!this._options) {
|
|
456
|
-
this.rucInputData = {}; // Trigger setter with empty object to apply defaults
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
ngOnInit() {
|
|
460
|
-
this.subscriptions.add(this.screenRecorderService.recordingState$.subscribe((state) => {
|
|
461
|
-
this.recordingState = state;
|
|
462
|
-
if (state === RecordingState.Idle || state === RecordingState.Stopped) {
|
|
463
|
-
// Ensure audio toggle is reset to default if needed, or based on user action
|
|
464
|
-
}
|
|
465
|
-
this.cdr.detectChanges();
|
|
466
|
-
}));
|
|
467
|
-
this.subscriptions.add(this.screenRecorderService.recordingTimestamp$.subscribe((timestamp) => {
|
|
468
|
-
this.recordingTimestamp = timestamp;
|
|
469
|
-
this.cdr.detectChanges();
|
|
470
|
-
}));
|
|
471
|
-
this.subscriptions.add(this.screenRecorderService.recordedTime$.subscribe((time) => {
|
|
472
|
-
this.recordedTime = time;
|
|
473
|
-
this.formattedTime = this.formatTimeDisplay(time);
|
|
474
|
-
this.cdr.detectChanges();
|
|
475
|
-
}));
|
|
476
|
-
this.subscriptions.add(this.screenRecorderService.recordedUrl$.subscribe((url) => {
|
|
477
|
-
this.safeRecordedUrl = url ? this.sanitizer.bypassSecurityTrustUrl(url) : null;
|
|
478
|
-
this.cdr.detectChanges();
|
|
479
|
-
}));
|
|
480
|
-
this.subscriptions.add(this.screenRecorderService.error$.subscribe((error) => {
|
|
481
|
-
if (error != this.screenRecordingConstant.PERMISSION_DENIED) {
|
|
482
|
-
this.errorMessage = error;
|
|
483
|
-
}
|
|
484
|
-
this.cdr.detectChanges();
|
|
485
|
-
}));
|
|
486
|
-
}
|
|
487
|
-
ngOnDestroy() {
|
|
488
|
-
if (this.subscriptions) {
|
|
489
|
-
this.subscriptions.unsubscribe();
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* Toggles the recording state between start and stop
|
|
494
|
-
*
|
|
495
|
-
* @returns Promise<void>
|
|
496
|
-
*/
|
|
497
|
-
toggleRecord() {
|
|
498
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
499
|
-
this.errorMessage = null; // Clear previous errors
|
|
500
|
-
if (this.recordingState === RecordingState.Idle || this.recordingState === RecordingState.Stopped) {
|
|
501
|
-
// Set recording timestamp when starting recording
|
|
502
|
-
const now = new Date();
|
|
503
|
-
this.recordingTimestamp = now.toLocaleString('en-IN', {
|
|
504
|
-
year: 'numeric',
|
|
505
|
-
month: '2-digit',
|
|
506
|
-
day: '2-digit',
|
|
507
|
-
hour: '2-digit',
|
|
508
|
-
minute: '2-digit',
|
|
509
|
-
hour12: true
|
|
510
|
-
});
|
|
511
|
-
yield this.screenRecorderService.startRecording({ audio: this.isAudioEnabled });
|
|
512
|
-
}
|
|
513
|
-
else {
|
|
514
|
-
this.screenRecorderService.stopRecording();
|
|
515
|
-
}
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
/**
|
|
519
|
-
* Toggles between pause and resume states during recording
|
|
520
|
-
*
|
|
521
|
-
* @returns void
|
|
522
|
-
*/
|
|
523
|
-
togglePauseResume() {
|
|
524
|
-
this.errorMessage = null;
|
|
525
|
-
if (this.recordingState === RecordingState.Recording) {
|
|
526
|
-
this.screenRecorderService.pauseRecording();
|
|
527
|
-
}
|
|
528
|
-
else if (this.recordingState === RecordingState.Paused) {
|
|
529
|
-
this.screenRecorderService.resumeRecording();
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
/**
|
|
533
|
-
* Checks if recording can be resumed
|
|
534
|
-
*
|
|
535
|
-
* @returns boolean - True if recording can be resumed, false otherwise
|
|
536
|
-
*/
|
|
537
|
-
canResume() {
|
|
538
|
-
return this.recordingState === RecordingState.Paused;
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* Downloads the recorded video file
|
|
542
|
-
*
|
|
543
|
-
* @returns void
|
|
544
|
-
*/
|
|
545
|
-
onDownload() {
|
|
546
|
-
this.errorMessage = null;
|
|
547
|
-
this.screenRecorderService.downloadRecording(this.rucInputData.downloadFileName);
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* Checks if recording can be downloaded
|
|
551
|
-
*
|
|
552
|
-
* @returns boolean - True if recording can be downloaded, false otherwise
|
|
553
|
-
*/
|
|
554
|
-
canDownload() {
|
|
555
|
-
return this.recordingState === RecordingState.Stopped;
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* Toggles audio recording on/off
|
|
559
|
-
*
|
|
560
|
-
* @returns void
|
|
561
|
-
*/
|
|
562
|
-
toggleAudio() {
|
|
563
|
-
this.isAudioEnabled = !this.isAudioEnabled;
|
|
564
|
-
this.cdr.detectChanges();
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
*
|
|
568
|
-
* @param iconName
|
|
569
|
-
* @returns
|
|
570
|
-
*/
|
|
571
|
-
getIcon(iconName) {
|
|
572
|
-
var _a;
|
|
573
|
-
return ((_a = this.rucInputData.icons) === null || _a === void 0 ? void 0 : _a[iconName]) || this.screenRecordingConstant.DEFAULT_ICONS[iconName];
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
*
|
|
577
|
-
* @param labelName
|
|
578
|
-
* @returns
|
|
579
|
-
*/
|
|
580
|
-
getLabel(labelName) {
|
|
581
|
-
var _a;
|
|
582
|
-
return ((_a = this.rucInputData.accessibilityLabels) === null || _a === void 0 ? void 0 : _a[labelName]) || this.screenRecordingConstant.DEFAULT_LABELS[labelName];
|
|
583
|
-
}
|
|
584
|
-
/**
|
|
585
|
-
*
|
|
586
|
-
* @param time - The time in seconds to format
|
|
587
|
-
* @returns string - Formatted time string (HH:MM:SS)
|
|
588
|
-
*/
|
|
589
|
-
formatTimeDisplay(time) {
|
|
590
|
-
const hours = Math.floor(time / 3600);
|
|
591
|
-
const minutes = Math.floor((time % 3600) / 60);
|
|
592
|
-
const seconds = time % 60;
|
|
593
|
-
const pad = (num) => num.toString().padStart(2, '0');
|
|
594
|
-
if (this.rucInputData && this.rucInputData.useUTCForTimer) {
|
|
595
|
-
// This is a simple duration format, not a specific UTC time.
|
|
596
|
-
// For actual UTC time of day, you'd use new Date().toUTCString() or similar.
|
|
597
|
-
// Assuming timer should show elapsed time formatted as HH:MM:SS.
|
|
598
|
-
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
|
599
|
-
}
|
|
600
|
-
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
|
601
|
-
}
|
|
602
|
-
// Method to reset the component and service to initial state
|
|
603
|
-
reset() {
|
|
604
|
-
var _a;
|
|
605
|
-
this.screenRecorderService.resetToIdle();
|
|
606
|
-
this.isAudioEnabled = (_a = this.rucInputData.defaultAudioState) !== null && _a !== void 0 ? _a : true;
|
|
607
|
-
this.cdr.detectChanges();
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
ScreenRecorderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ScreenRecorderComponent, deps: [{ token: ScreenRecorderService }, { token: i2.DomSanitizer }, { token: i0.ChangeDetectorRef }, { token: ScreenRecorderConstants }], target: i0.ɵɵFactoryTarget.Component });
|
|
611
|
-
ScreenRecorderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: ScreenRecorderComponent, selector: "uxp-screen-recorder", inputs: { customTheme: "customTheme", rucInputData: "rucInputData" }, viewQueries: [{ propertyName: "videoPlayer", first: true, predicate: ["videoPlayer"], descendants: true }], ngImport: i0, template: "<div class=\"screen-recorder-controls\" [ngClass]=\"customTheme + ' position-' + (rucInputData.position || ScreenRecorderPosition.Bottom)\"\r\n *ngIf=\"isBrowserSupported\">\r\n\r\n <!-- Error Message -->\r\n <div *ngIf=\"errorMessage\" class=\"recorder-error\">\r\n {{ errorMessage }}\r\n </div>\r\n\r\n <!-- Record/Stop Button -->\r\n <button\r\n *ngIf=\"recordingState === RecordingState.Idle || recordingState === RecordingState.Stopped\" (click)=\"toggleRecord()\"\r\n [attr.aria-label]=\"getLabel('recordButton')\" [attr.aria-pressed]=\"false\" class=\"recorder-button record-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ getRecordingIcon() || getIcon('record') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">{{ getLabel('recordButton')\r\n }}</ng-container>\r\n </button>\r\n\r\n <button\r\n *ngIf=\"recordingState === RecordingState.Recording || recordingState === RecordingState.Paused\"\r\n (click)=\"toggleRecord()\" [attr.aria-label]=\"getLabel('stopButton')\" [attr.aria-pressed]=\"true\"\r\n class=\"recorder-button stop-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ getIcon('stop') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">{{ getLabel('stopButton') }}</ng-container>\r\n </button>\r\n\r\n <!-- Pause/Resume Button -->\r\n <button *ngIf=\"rucInputData.showPauseResume && recordingState === RecordingState.Recording\"\r\n (click)=\"togglePauseResume()\" [attr.aria-label]=\"getLabel('pauseButton')\" [attr.aria-pressed]=\"false\"\r\n class=\"recorder-button pause-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ getIcon('pause') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">{{ getLabel('pauseButton') }}</ng-container>\r\n </button>\r\n <button *ngIf=\"rucInputData.showPauseResume && recordingState === RecordingState.Paused\"\r\n (click)=\"togglePauseResume()\" [attr.aria-label]=\"getLabel('resumeButton')\" [attr.aria-pressed]=\"true\"\r\n class=\"recorder-button resume-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ getIcon('resume') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">{{ getLabel('resumeButton')\r\n }}</ng-container>\r\n </button>\r\n\r\n <!-- Audio Toggle Button -->\r\n <button\r\n *ngIf=\"rucInputData.showAudioToggle && (recordingState === RecordingState.Idle || recordingState === RecordingState.Stopped)\"\r\n (click)=\"toggleAudio()\" [attr.aria-label]=\"getLabel('audioToggleButton')\" [attr.aria-pressed]=\"isAudioEnabled\"\r\n class=\"recorder-button audio-toggle-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ isAudioEnabled ? getIcon('audioOn') : getIcon('audioOff') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">\r\n {{ isAudioEnabled ? screenRecordingConstant.AUDIO_ON : screenRecordingConstant.AUDIO_OFF }}\r\n </ng-container>\r\n </button>\r\n\r\n <!-- Timer -->\r\n <div\r\n *ngIf=\"rucInputData.showTimer && (recordingState === RecordingState.Recording || recordingState === RecordingState.Paused)\"\r\n class=\"recorder-timer\" aria-live=\"polite\" aria-atomic=\"true\">\r\n {{ formattedTime }}\r\n </div>\r\n\r\n <!-- Download Button -->\r\n <button\r\n *ngIf=\"rucInputData.showDownloadButton && recordingState === RecordingState.Stopped && safeRecordedUrl\"\r\n (click)=\"onDownload()\" [attr.aria-label]=\"getLabel('downloadButton')\" class=\"recorder-button download-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ getIcon('download') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">{{ getLabel('downloadButton')\r\n }}</ng-container>\r\n </button>\r\n\r\n <!-- Playback Area -->\r\n <div *ngIf=\"rucInputData.showPlaybackControls && recordingState === RecordingState.Stopped && safeRecordedUrl\"\r\n class=\"playback-area\">\r\n <video #videoPlayer [src]=\"safeRecordedUrl\" controls [attr.aria-label]=\"getLabel('playbackVideo')\"></video>\r\n <div class=\"video-timer-overlay\">\r\n <span>Recorded on: {{ recordingTimestamp }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Reset Button (optional, good for testing/clearing state) -->\r\n <button *ngIf=\"recordingState === RecordingState.Stopped && safeRecordedUrl\" (click)=\"reset()\"\r\n aria-label=\"New Recording\" class=\"recorder-button reset-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">refresh</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">New</ng-container>\r\n </button>\r\n\r\n</div>\r\n\r\n<div *ngIf=\"!isBrowserSupported\" class=\"recorder-notsupported\" [ngClass]=\"customTheme\">\r\n {{screenRecordingConstant.NO_SUPPORT}}\r\n</div>", styles: [":host{display:block;font-family:sans-serif}.screen-recorder-controls{display:flex;gap:8px;align-items:center;padding:8px;border-radius:4px;position:relative;z-index:1000}.screen-recorder-controls.position-top-left{margin-top:10px;margin-left:10px;flex-direction:row}.screen-recorder-controls.position-top-right{margin-top:10px;margin-right:10px;flex-direction:row}.screen-recorder-controls.position-bottom-left{margin-bottom:10px;margin-left:10px;flex-direction:row}.screen-recorder-controls.position-bottom-right{margin-bottom:10px;margin-right:10px;flex-direction:row}.screen-recorder-controls.position-left,.screen-recorder-controls.position-right{float:right}.recorder-button{padding:8px 12px;border:1px solid #ccc;border-radius:4px;background-color:#fff;cursor:pointer;display:inline-flex;align-items:center;gap:4px}.recorder-button:hover{background-color:#e9e9e9}.recorder-button:active{background-color:#d0d0d0}.recorder-button.record-button{color:red}.material-icons{font-family:Material Icons;font-weight:400;font-style:normal;font-size:20px;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:\"liga\"}.recorder-timer{padding:0 8px;font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums}.playback-area{width:100%;max-width:100%;margin-top:1rem;position:relative}.playback-area video{width:100%;height:auto;max-width:100%}.playback-area .video-timer-overlay{position:absolute;top:10px;right:10px;background:rgba(0,0,0,.7);color:#fff;padding:5px 10px;border-radius:4px;font-size:14px;z-index:10}.recorder-error{color:red;padding:8px;border:1px solid red;background-color:#ffe0e0;border-radius:4px;margin-bottom:8px}.recorder-notsupported{padding:10px;background-color:#fff3cd;border:1px solid #ffeeba;color:#856404;border-radius:4px}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
612
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ScreenRecorderComponent, decorators: [{
|
|
613
|
-
type: Component,
|
|
614
|
-
args: [{ selector: 'uxp-screen-recorder', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"screen-recorder-controls\" [ngClass]=\"customTheme + ' position-' + (rucInputData.position || ScreenRecorderPosition.Bottom)\"\r\n *ngIf=\"isBrowserSupported\">\r\n\r\n <!-- Error Message -->\r\n <div *ngIf=\"errorMessage\" class=\"recorder-error\">\r\n {{ errorMessage }}\r\n </div>\r\n\r\n <!-- Record/Stop Button -->\r\n <button\r\n *ngIf=\"recordingState === RecordingState.Idle || recordingState === RecordingState.Stopped\" (click)=\"toggleRecord()\"\r\n [attr.aria-label]=\"getLabel('recordButton')\" [attr.aria-pressed]=\"false\" class=\"recorder-button record-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ getRecordingIcon() || getIcon('record') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">{{ getLabel('recordButton')\r\n }}</ng-container>\r\n </button>\r\n\r\n <button\r\n *ngIf=\"recordingState === RecordingState.Recording || recordingState === RecordingState.Paused\"\r\n (click)=\"toggleRecord()\" [attr.aria-label]=\"getLabel('stopButton')\" [attr.aria-pressed]=\"true\"\r\n class=\"recorder-button stop-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ getIcon('stop') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">{{ getLabel('stopButton') }}</ng-container>\r\n </button>\r\n\r\n <!-- Pause/Resume Button -->\r\n <button *ngIf=\"rucInputData.showPauseResume && recordingState === RecordingState.Recording\"\r\n (click)=\"togglePauseResume()\" [attr.aria-label]=\"getLabel('pauseButton')\" [attr.aria-pressed]=\"false\"\r\n class=\"recorder-button pause-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ getIcon('pause') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">{{ getLabel('pauseButton') }}</ng-container>\r\n </button>\r\n <button *ngIf=\"rucInputData.showPauseResume && recordingState === RecordingState.Paused\"\r\n (click)=\"togglePauseResume()\" [attr.aria-label]=\"getLabel('resumeButton')\" [attr.aria-pressed]=\"true\"\r\n class=\"recorder-button resume-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ getIcon('resume') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">{{ getLabel('resumeButton')\r\n }}</ng-container>\r\n </button>\r\n\r\n <!-- Audio Toggle Button -->\r\n <button\r\n *ngIf=\"rucInputData.showAudioToggle && (recordingState === RecordingState.Idle || recordingState === RecordingState.Stopped)\"\r\n (click)=\"toggleAudio()\" [attr.aria-label]=\"getLabel('audioToggleButton')\" [attr.aria-pressed]=\"isAudioEnabled\"\r\n class=\"recorder-button audio-toggle-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ isAudioEnabled ? getIcon('audioOn') : getIcon('audioOff') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">\r\n {{ isAudioEnabled ? screenRecordingConstant.AUDIO_ON : screenRecordingConstant.AUDIO_OFF }}\r\n </ng-container>\r\n </button>\r\n\r\n <!-- Timer -->\r\n <div\r\n *ngIf=\"rucInputData.showTimer && (recordingState === RecordingState.Recording || recordingState === RecordingState.Paused)\"\r\n class=\"recorder-timer\" aria-live=\"polite\" aria-atomic=\"true\">\r\n {{ formattedTime }}\r\n </div>\r\n\r\n <!-- Download Button -->\r\n <button\r\n *ngIf=\"rucInputData.showDownloadButton && recordingState === RecordingState.Stopped && safeRecordedUrl\"\r\n (click)=\"onDownload()\" [attr.aria-label]=\"getLabel('downloadButton')\" class=\"recorder-button download-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">{{ getIcon('download') }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">{{ getLabel('downloadButton')\r\n }}</ng-container>\r\n </button>\r\n\r\n <!-- Playback Area -->\r\n <div *ngIf=\"rucInputData.showPlaybackControls && recordingState === RecordingState.Stopped && safeRecordedUrl\"\r\n class=\"playback-area\">\r\n <video #videoPlayer [src]=\"safeRecordedUrl\" controls [attr.aria-label]=\"getLabel('playbackVideo')\"></video>\r\n <div class=\"video-timer-overlay\">\r\n <span>Recorded on: {{ recordingTimestamp }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Reset Button (optional, good for testing/clearing state) -->\r\n <button *ngIf=\"recordingState === RecordingState.Stopped && safeRecordedUrl\" (click)=\"reset()\"\r\n aria-label=\"New Recording\" class=\"recorder-button reset-button\">\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Icon\">\r\n <span class=\"material-icons\">refresh</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"rucInputData.displayFormat === DisplayFormat.Text\">New</ng-container>\r\n </button>\r\n\r\n</div>\r\n\r\n<div *ngIf=\"!isBrowserSupported\" class=\"recorder-notsupported\" [ngClass]=\"customTheme\">\r\n {{screenRecordingConstant.NO_SUPPORT}}\r\n</div>", styles: [":host{display:block;font-family:sans-serif}.screen-recorder-controls{display:flex;gap:8px;align-items:center;padding:8px;border-radius:4px;position:relative;z-index:1000}.screen-recorder-controls.position-top-left{margin-top:10px;margin-left:10px;flex-direction:row}.screen-recorder-controls.position-top-right{margin-top:10px;margin-right:10px;flex-direction:row}.screen-recorder-controls.position-bottom-left{margin-bottom:10px;margin-left:10px;flex-direction:row}.screen-recorder-controls.position-bottom-right{margin-bottom:10px;margin-right:10px;flex-direction:row}.screen-recorder-controls.position-left,.screen-recorder-controls.position-right{float:right}.recorder-button{padding:8px 12px;border:1px solid #ccc;border-radius:4px;background-color:#fff;cursor:pointer;display:inline-flex;align-items:center;gap:4px}.recorder-button:hover{background-color:#e9e9e9}.recorder-button:active{background-color:#d0d0d0}.recorder-button.record-button{color:red}.material-icons{font-family:Material Icons;font-weight:400;font-style:normal;font-size:20px;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:\"liga\"}.recorder-timer{padding:0 8px;font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums}.playback-area{width:100%;max-width:100%;margin-top:1rem;position:relative}.playback-area video{width:100%;height:auto;max-width:100%}.playback-area .video-timer-overlay{position:absolute;top:10px;right:10px;background:rgba(0,0,0,.7);color:#fff;padding:5px 10px;border-radius:4px;font-size:14px;z-index:10}.recorder-error{color:red;padding:8px;border:1px solid red;background-color:#ffe0e0;border-radius:4px;margin-bottom:8px}.recorder-notsupported{padding:10px;background-color:#fff3cd;border:1px solid #ffeeba;color:#856404;border-radius:4px}\n"] }]
|
|
615
|
-
}], ctorParameters: function () { return [{ type: ScreenRecorderService }, { type: i2.DomSanitizer }, { type: i0.ChangeDetectorRef }, { type: ScreenRecorderConstants }]; }, propDecorators: { customTheme: [{
|
|
616
|
-
type: Input
|
|
617
|
-
}], rucInputData: [{
|
|
618
|
-
type: Input
|
|
619
|
-
}], videoPlayer: [{
|
|
620
|
-
type: ViewChild,
|
|
621
|
-
args: ['videoPlayer']
|
|
622
|
-
}] } });
|
|
623
|
-
|
|
624
|
-
class RuclibScreenRecorderModule {
|
|
625
|
-
}
|
|
626
|
-
RuclibScreenRecorderModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibScreenRecorderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
627
|
-
RuclibScreenRecorderModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: RuclibScreenRecorderModule, declarations: [ScreenRecorderComponent], imports: [CommonModule], exports: [ScreenRecorderComponent] });
|
|
628
|
-
RuclibScreenRecorderModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibScreenRecorderModule, providers: [ScreenRecorderService], imports: [CommonModule] });
|
|
629
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RuclibScreenRecorderModule, decorators: [{
|
|
630
|
-
type: NgModule,
|
|
631
|
-
args: [{
|
|
632
|
-
imports: [CommonModule],
|
|
633
|
-
declarations: [ScreenRecorderComponent],
|
|
634
|
-
exports: [ScreenRecorderComponent],
|
|
635
|
-
providers: [ScreenRecorderService],
|
|
636
|
-
}]
|
|
637
|
-
}] });
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
* Generated bundle index. Do not edit.
|
|
641
|
-
*/
|
|
642
|
-
|
|
643
|
-
export { DisplayFormat, RecordingState, RuclibScreenRecorderModule, ScreenRecorderComponent, ScreenRecorderPosition, ScreenRecorderService };
|
|
644
|
-
//# sourceMappingURL=ruc-lib-screen-recorder.mjs.map
|