Skip to content
Snippets Groups Projects
control-bar.component.ts 5.35 KiB
Newer Older
  OnInit, AfterContentInit, OnDestroy, Component, EventEmitter, Input, Output
} from '@angular/core';
import { MatSliderChange } from '@angular/material/slider';
import { AudioElement } from '../../models/audio-element';
import { VideoElement } from '../../models/video-element';
import { ValueChangeElement } from '../../models/uI-element';

@Component({
  selector: 'app-control-bar',
  templateUrl: './control-bar.component.html',
  styleUrls: ['./control-bar.component.css']
})
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;
  currentRestTime!: number;
  started!: boolean;
  playing!: boolean;
  pausing!: boolean;
  runCounter!: number;
  lastVolume!: number;
  restTimeMode: boolean = true;
  showHint!: boolean;
  disabled!: boolean;
  isAspectPlayer!: boolean;

  // TODO:
  // uninterruptible: boolean; // false kein Blättern; starten eines anderen Videos; ....
  // hideOtherPages: boolean; // false (Solange nicht vollständig gespielt, sind alle anderen Seiten verborgen)
  // activeAfterID: string; // '' (andere Audio-id; Audio ist deaktiviert, solange anderes nicht vollständig abgespielt)
  // minRuns: number; // 1

  ngOnInit(): void {
    // Firefox has problems to get the duration
    this.player.ondurationchange = () => this.initTimeValues();
    this.player.ontimeupdate = () => {
      this.currentTime = this.player.currentTime / 60;
      this.currentRestTime = this.player.duration ? (this.player.duration - this.player.currentTime) / 60 : 0;
      this.sendPlaybackTimeChanged();
    };
    this.player.onpause = () => {
      this.playing = false;
      this.pausing = true;
    };
    this.player.onplaying = () => {
      this.playing = true;
      this.pausing = false;
      this.started = true;
      this.showHint = false;
    };
    this.player.onended = () => {
      if (!this.checkStatus(this.runCounter + 1)) {
        this.runCounter += 1;
        if (this.elementModel.loop) {
          this._play();
        }
      }
    };
    this.player.onvolumechange = () => {
      this.player.muted = !this.player.volume;
    };
    this.lastVolume = this.player.volume;
    // player-aspect should work, but doesn't in production
    this.isAspectPlayer = !!this.player.closest('app-element-container');
    if (this.isAspectPlayer) {
      this.initAutostart();
      this.initHint();
    }
  }

  play(event: MouseEvent): void {
    this._play();
    event.stopPropagation();
    event.preventDefault();
  pause(event: MouseEvent): void {
    this.player.pause();
    event.stopPropagation();
    event.preventDefault();
  }

  onTimeChange(event: MatSliderChange): void {
    this.player.currentTime = event.value ? event.value : 0;
  }

  onVolumeChange(event: MatSliderChange): void {
    this.player.volume = event.value ? event.value : 0;
  }

  toggleTime(event: MouseEvent): void {
    this.restTimeMode = !this.restTimeMode;
    event.stopPropagation();
    event.preventDefault();
  }

  toggleVolume(event: MouseEvent): void {
    if (this.player.volume) {
      this.lastVolume = this.player.volume;
      this.player.volume = 0;
    } else {
      this.player.volume = this.lastVolume;
    }
    event.stopPropagation();
    event.preventDefault();
  private checkStatus(runCounter: number): boolean {
    this.disabled = !this.elementModel.maxRuns ? false : this.elementModel.maxRuns <= runCounter;
    return this.disabled;
  }

  private initAutostart(): void {
    if (this.elementModel.autostart && !this.started) {
      setTimeout(() => {
        if (!this.started) {
          this._play();
        }
      }, this.elementModel.autostartDelay);
    }
  }

  private initHint(): void {
    if (this.elementModel.hintLabel && !this.started) {
      setTimeout(() => {
        if (!this.started) {
          this.showHint = true;
        }
      }, this.elementModel.hintLabelDelay);
    }
  }

  private _play(): void {
    this.player.play().then(() => {},
      // eslint-disable-next-line no-console
      () => console.error('player: cannot play this media file'));
  }

  private sendPlaybackTimeChanged() {
    this.playbackTimeChanged.emit({
      id: this.elementModel.id,
      values: [this.playbackTime, this.toPlaybackTime()]
    });
  }

  private toPlaybackTime(): number {
    let newPlayBackTime: number = 0;
    if (this.player.duration && this.player.currentTime) {
      newPlayBackTime = this.runCounter + this.player.currentTime / this.player.duration;
    }
    this.playbackTime = newPlayBackTime;
    return newPlayBackTime;
  }

  private initTimeValues(): void {
    if (!this.duration) {
      if ((this.player.duration !== Infinity) && this.player.duration) {
        this.duration = this.player.duration / 60;
        this.currentRestTime = (this.player.duration - this.player.currentTime) / 60;
        this.runCounter = Math.floor(this.elementModel.playbackTime);
        this.player.currentTime = (this.elementModel.playbackTime - this.runCounter) * this.player.duration;
      } else {
        this.duration = 0;

  ngOnDestroy(): void {
    this.player.pause();
  }