From f5d1d86b2df5f605644c46b2f3cfed4e44f2208e Mon Sep 17 00:00:00 2001
From: jojohoch <joachim.hoch@iqb.hu-berlin.de>
Date: Wed, 10 Nov 2021 14:37:35 +0100
Subject: [PATCH] [player] Use service to prevent simultaneous media playback

---
 .../element-components/audio.component.ts     |  3 +++
 .../control-bar/control-bar.component.html    | 10 ++++-----
 .../control-bar/control-bar.component.ts      |  3 ++-
 ...edia-player-element-component.directive.ts |  8 +++++++
 .../element-container.component.ts            | 21 +++++++++++++++++++
 .../src/app/services/media-player.service.ts  | 17 +++++++++++++++
 6 files changed, 56 insertions(+), 6 deletions(-)
 create mode 100644 projects/player/src/app/services/media-player.service.ts

diff --git a/projects/common/element-components/audio.component.ts b/projects/common/element-components/audio.component.ts
index 582bdfa8e..314eea51c 100644
--- a/projects/common/element-components/audio.component.ts
+++ b/projects/common/element-components/audio.component.ts
@@ -8,11 +8,14 @@ import { MediaPlayerElementComponent } from '../media-player-element-component.d
     <div [style.width.%]="100"
          [style.height.%]="100">
       <audio #player
+             (playing)="mediaPlay.emit(this.elementModel.id)"
+             (pause)="mediaPause.emit(this.elementModel.id)"
              [style.width.%]="100"
              [src]="elementModel.src | safeResourceUrl">
       </audio>
       <app-control-bar [player]="player"
                        [elementModel]="elementModel"
+                       [active]="active"
                        (playbackTimeChanged)="playbackTimeChanged.emit($event)">
       </app-control-bar>
     </div>
diff --git a/projects/common/element-components/control-bar/control-bar.component.html b/projects/common/element-components/control-bar/control-bar.component.html
index 3867441e3..0a475b307 100644
--- a/projects/common/element-components/control-bar/control-bar.component.html
+++ b/projects/common/element-components/control-bar/control-bar.component.html
@@ -4,9 +4,9 @@
     <button *ngIf="!playing || !elementModel.pauseControl"
             mat-button
             class="control-button"
-            [class.enabled-control]="!disabled"
+            [class.enabled-control]="!disabled && active"
             [class.active-control]="playing"
-            [disabled]="disabled"
+            [disabled]="disabled || !active"
             (click)="play($event)">
       <mat-icon>play_arrow</mat-icon>
     </button>
@@ -15,9 +15,9 @@
     <button *ngIf="playing"
             mat-button
             class="control-button"
-            [class.enabled-control]="!disabled"
+            [class.enabled-control]="!disabled && active"
             [class.active-control]="pausing"
-            [disabled]="disabled"
+            [disabled]="disabled || !active"
             (click)="pause($event)">
       <mat-icon>pause</mat-icon>
     </button>
@@ -33,7 +33,7 @@
                 step="1"
                 [max]="player.duration"
                 [value]="player.currentTime"
-                [disabled]="disabled || !elementModel.interactiveProgressbar"
+                [disabled]="disabled || !elementModel.interactiveProgressbar || !active"
                 (input)="onTimeChange($event)">
     </mat-slider>
   </ng-container>
diff --git a/projects/common/element-components/control-bar/control-bar.component.ts b/projects/common/element-components/control-bar/control-bar.component.ts
index 2c684cee7..4c81b1fce 100644
--- a/projects/common/element-components/control-bar/control-bar.component.ts
+++ b/projects/common/element-components/control-bar/control-bar.component.ts
@@ -14,6 +14,7 @@ import { ValueChangeElement } from '../../models/uI-element';
 export class ControlBarComponent implements OnInit, AfterContentInit, OnDestroy {
   @Input() player!: HTMLVideoElement | HTMLAudioElement;
   @Input() elementModel!: AudioElement | VideoElement;
+  @Input() active!: boolean;
   @Output() playbackTimeChanged = new EventEmitter<ValueChangeElement>();
   duration!: number;
   currentTime!: number;
@@ -141,7 +142,7 @@ export class ControlBarComponent implements OnInit, AfterContentInit, OnDestroy
   private _play(): void {
     this.player.play().then(() => {},
       // eslint-disable-next-line no-console
-      () => console.error('error'));
+      () => console.error('player: cannot play this media file'));
   }
 
   private sendPlaybackTimeChanged() {
diff --git a/projects/common/media-player-element-component.directive.ts b/projects/common/media-player-element-component.directive.ts
index fd1875481..9b29d9d1c 100644
--- a/projects/common/media-player-element-component.directive.ts
+++ b/projects/common/media-player-element-component.directive.ts
@@ -5,4 +5,12 @@ import { ElementComponent } from './element-component.directive';
 @Directive()
 export abstract class MediaPlayerElementComponent extends ElementComponent {
   @Output() playbackTimeChanged = new EventEmitter<ValueChangeElement>();
+  @Output() mediaPlay = new EventEmitter<string>();
+  @Output() mediaPause = new EventEmitter<string>();
+
+  active: boolean = true;
+
+  setActualPlayingMediaId(id: string | null): void {
+    this.active = !id || id === this.elementModel.id;
+  }
 }
diff --git a/projects/player/src/app/components/element-container/element-container.component.ts b/projects/player/src/app/components/element-container/element-container.component.ts
index c95a4fcb6..2e8e186e6 100644
--- a/projects/player/src/app/components/element-container/element-container.component.ts
+++ b/projects/player/src/app/components/element-container/element-container.component.ts
@@ -28,6 +28,8 @@ import { VideoElement } from '../../../../../common/models/video-element';
 import { AudioElement } from '../../../../../common/models/audio-element';
 import { ImageElement } from '../../../../../common/models/image-element';
 import { VeronaPostService } from '../../services/verona-post.service';
+import { MediaPlayerElementComponent } from '../../../../../common/media-player-element-component.directive';
+import { MediaPlayerService } from '../../services/media-player.service';
 
 @Component({
   selector: 'app-element-container',
@@ -54,6 +56,7 @@ export class ElementContainerComponent implements OnInit {
               private unitStateService: UnitStateService,
               private formBuilder: FormBuilder,
               private veronaPostService: VeronaPostService,
+              private mediaPlayerService: MediaPlayerService,
               private markingService: MarkingService) {
   }
 
@@ -98,6 +101,22 @@ export class ElementContainerComponent implements OnInit {
         });
     }
 
+    if (elementComponent.mediaPause) {
+      elementComponent.mediaPause
+        .pipe(takeUntil(this.ngUnsubscribe))
+        .subscribe(() => {
+          this.mediaPlayerService.broadCastPlayChanges(null);
+        });
+    }
+
+    if (elementComponent.mediaPlay) {
+      elementComponent.mediaPlay
+        .pipe(takeUntil(this.ngUnsubscribe))
+        .subscribe((playId: string) => {
+          this.mediaPlayerService.broadCastPlayChanges(playId);
+        });
+    }
+
     if (elementComponent.applySelection) {
       elementComponent.applySelection
         .pipe(takeUntil(this.ngUnsubscribe))
@@ -181,6 +200,8 @@ export class ElementContainerComponent implements OnInit {
             formGroup: elementForm
           });
         });
+    } else if (elementComponent instanceof MediaPlayerElementComponent) {
+      this.mediaPlayerService.registerMediaElement(elementComponent);
     }
   }
 
diff --git a/projects/player/src/app/services/media-player.service.ts b/projects/player/src/app/services/media-player.service.ts
new file mode 100644
index 000000000..ff0df1433
--- /dev/null
+++ b/projects/player/src/app/services/media-player.service.ts
@@ -0,0 +1,17 @@
+import { Injectable } from '@angular/core';
+import { MediaPlayerElementComponent } from '../../../../common/media-player-element-component.directive';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class MediaPlayerService {
+  mediaElements: MediaPlayerElementComponent[] = [];
+
+  registerMediaElement(mediaElement: MediaPlayerElementComponent): void {
+    this.mediaElements.push(mediaElement);
+  }
+
+  broadCastPlayChanges(actualId: string | null): void {
+    this.mediaElements.forEach(mediaElement => mediaElement.setActualPlayingMediaId(actualId));
+  }
+}
-- 
GitLab