@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.
@@ -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