unified-video-framework
Version:
Cross-platform video player framework supporting iOS, Android, Web, Smart TVs (Samsung/LG), Roku, and more
130 lines • 3.82 kB
JavaScript
import { EventEmitter } from 'events';
import { PlayerState } from './interfaces';
export class VideoPlayer {
constructor(config = {}) {
this.errors = [];
this.config = {
autoPlay: false,
muted: false,
controls: true,
loop: false,
preload: 'metadata',
playsInline: true,
...config
};
this.eventEmitter = new EventEmitter();
this.state = PlayerState.IDLE;
this.metrics = this.initializeMetrics();
}
on(event, handler) {
this.eventEmitter.on(event, handler);
}
off(event, handler) {
this.eventEmitter.off(event, handler);
}
once(event, handler) {
this.eventEmitter.once(event, handler);
}
removeAllListeners(event) {
if (event) {
this.eventEmitter.removeAllListeners(event);
}
else {
this.eventEmitter.removeAllListeners();
}
}
emit(event, data) {
this.eventEmitter.emit(event, data);
if (this.config.analytics?.enabled) {
this.trackAnalytics(event, data);
}
}
getState() {
return this.state;
}
setState(newState) {
const oldState = this.state;
this.state = newState;
if (oldState !== newState) {
this.emit('statechange', { oldState, newState });
}
}
getConfig() {
return { ...this.config };
}
updateConfig(config) {
this.config = { ...this.config, ...config };
this.applyConfig();
}
getMetrics() {
return {
...this.metrics,
errors: [...this.errors]
};
}
initializeMetrics() {
return {
sessionId: this.generateSessionId(),
totalPlayTime: 0,
bufferingCount: 0,
bufferingDuration: 0,
averageBitrate: 0,
qualityChanges: 0,
errors: []
};
}
generateSessionId() {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
handleError(error) {
this.errors.push(error);
this.emit('onError', error);
if (error.fatal) {
this.setState(PlayerState.ERROR);
}
}
trackAnalytics(event, data) {
if (!this.config.analytics?.providers)
return;
const analyticsData = {
event,
timestamp: Date.now(),
sessionId: this.metrics.sessionId,
currentTime: this.getCurrentTime(),
duration: this.getDuration(),
state: this.state,
...data
};
this.config.analytics.providers.forEach(provider => {
try {
provider.track(event, analyticsData);
}
catch (error) {
console.error(`Analytics provider ${provider.name} failed:`, error);
}
});
}
cleanup() {
this.removeAllListeners();
this.state = PlayerState.IDLE;
this.currentSource = undefined;
}
formatTime(seconds) {
if (!isFinite(seconds))
return '00:00';
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
if (hours > 0) {
return `${hours}:${this.pad(minutes)}:${this.pad(secs)}`;
}
return `${minutes}:${this.pad(secs)}`;
}
pad(num) {
return num.toString().padStart(2, '0');
}
isBuffering() {
return this.state === PlayerState.BUFFERING;
}
}
//# sourceMappingURL=VideoPlayer.js.map