Skip to content

Instantly share code, notes, and snippets.

@darrenmothersele
Last active February 19, 2018 16:21
Show Gist options
  • Select an option

  • Save darrenmothersele/34996e6704f8a702d5823c5278c9b023 to your computer and use it in GitHub Desktop.

Select an option

Save darrenmothersele/34996e6704f8a702d5823c5278c9b023 to your computer and use it in GitHub Desktop.
Angular 2, 4, 5+ Service for controlling a YouTube audio player
import { Injectable, NgZone } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { interval } from 'rxjs/observable/interval';
import { combineLatest, filter, map, merge, startWith, switchMap, takeUntil } from 'rxjs/operators';
import { Observable } from 'rxjs/Observable';
declare const YT: any;
export enum YouTubePlayerState {
Unstarted = -1,
Ended = 0,
Playing = 1,
Paused = 2,
Buffering = 3,
VideoCued = 5
}
@Injectable()
export class YouTubePlayerService {
isPlayerReady$: BehaviorSubject<boolean>;
playerState$: BehaviorSubject<YouTubePlayerState>;
duration$: Observable<number>;
currentTime$: Observable<number>;
seek$: BehaviorSubject<number>;
// The actual YouTube player object
private player;
constructor(private zone: NgZone) {
this.isPlayerReady$ = new BehaviorSubject<boolean>(false);
this.playerState$ = new BehaviorSubject<YouTubePlayerState>(YouTubePlayerState.Unstarted);
this.seek$ = new BehaviorSubject<number>(0);
this.duration$ = this.playerState$.pipe(
filter(state => state === YouTubePlayerState.Playing),
map(() => this.player.getDuration()),
startWith(0)
);
this.currentTime$ = this.playerState$
.pipe(
filter(state => state === YouTubePlayerState.Playing),
switchMap(() => interval(500).pipe(
startWith(0),
takeUntil(this.playerState$.pipe(
filter(state => state !== YouTubePlayerState.Playing)
))
)),
map(() => Math.round(this.player.getCurrentTime())),
merge(this.seek$),
startWith(0)
);
this.initYouTubeAPI();
}
load(id: string) {
this.player.loadVideoById(id);
}
play() {
this.player.playVideo();
}
pause() {
this.player.pauseVideo();
}
seekTo(seconds: number, allowSeekAhead = false) {
this.player.seekTo(seconds, allowSeekAhead);
this.seek$.next(seconds);
}
seekToPercent(percent: number, allowSeekAhead = false) {
this.seekTo(this.player.getDuration() * percent, allowSeekAhead);
}
/**
* Fetch the YouTube API scripts and initialise the player
*/
private initYouTubeAPI() {
window['onYouTubeIframeAPIReady'] = () => this.onYouTubeIframeAPIReady();
const tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
document.body.appendChild(tag);
}
onYouTubeIframeAPIReady() {
console.log(this);
const playerElement = document.createElement('div');
playerElement.style.display = 'none';
document.body.appendChild(playerElement);
this.player = new YT.Player(playerElement, {
height: '256',
width: '256',
playerVars: {
autoplay: 0,
controls: 0,
disablekb: 1,
showinfo: 0,
},
events: {
onReady: () => {
this.zone.run(() => this.isPlayerReady$.next(true));
},
onStateChange: ({ data: state }) => {
this.zone.run(() => this.playerState$.next(state));
}
}
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment