spectatr-player-sdk
Version:
A custom video player built with Stencil with Shaka Player integration
300 lines (299 loc) • 11.6 kB
JavaScript
import { addReaction } from "../../services/videoService";
import { setupQualityTrack } from "./tracker-util";
export function togglePlay(component) {
const video = component.videoElement;
if (video.paused) {
video.play();
component.playVideo.emit();
window.parent.postMessage({
type: 'ON_VIDEO_PLAY',
}, '*');
}
else {
video.pause();
component.pauseVideo.emit();
window.parent.postMessage({
type: 'ON_VIDEO_PAUSE',
}, '*');
}
}
export function toggleMute(component) {
component.videoElement.muted = !component.videoElement.muted;
}
export function setVolume(component, volume) {
if (volume !== 0 && component.videoElement.muted) {
component.videoElement.muted = false;
}
component.videoElement.volume = volume;
}
export function seek(component, time) {
if (component.isLive) {
const dvrStartTime = component.player.seekRange().start;
component.videoElement.currentTime = Math.max(time, dvrStartTime);
component.currentTime = component.videoElement.currentTime;
}
else {
component.videoElement.currentTime = time;
component.currentTime = time;
}
}
export function skipForward(component) {
component.videoElement.currentTime = Math.min(component.duration, component.videoElement.currentTime + 10);
}
export function skipBackward(component) {
let seekToTime = component.videoElement.currentTime - 10;
if (component.isLive) {
const dvrStartTime = component.player.seekRange().start;
if (seekToTime < dvrStartTime) {
seekToTime = dvrStartTime;
}
}
component.videoElement.currentTime = seekToTime;
}
export const toggleFullscreen = (component) => {
// Get the video container directly from the component reference
const videoContainer = component.videoContainer;
if (!videoContainer) {
console.error('Video container element not found');
component.videoError.emit('Video container element not found');
return;
}
if (!document.fullscreenElement) {
// Enter fullscreen
if (videoContainer.requestFullscreen) {
videoContainer
.requestFullscreen()
.then(() => {
component.isFullscreen = true;
})
.catch(err => {
console.error('Error attempting to enable fullscreen:', err);
component.videoError.emit('Error attempting to enable fullscreen');
});
}
}
else {
// Exit fullscreen
if (document.exitFullscreen) {
document
.exitFullscreen()
.then(() => {
component.isFullscreen = false;
})
.catch(err => {
console.error('Error attempting to exit fullscreen:', err);
component.videoError.emit('Error attempting to exit fullscreen');
});
}
}
};
export function startProgressUpdate(component) {
component.progressUpdateInterval = setInterval(() => {
component.currentTime = component.videoElement.currentTime;
}, 100);
}
export function stopProgressUpdate(component) {
if (component.progressUpdateInterval) {
clearInterval(component.progressUpdateInterval);
}
}
export async function setQuality(component, qualityIndex) {
if (!component.player)
return;
if (qualityIndex === -1) {
// Auto quality
component.player.configure({ abr: { enabled: true } });
}
else {
// Specific quality
component.player.configure({ abr: { enabled: false } });
const tracks = component.player.getVariantTracks().filter(track => !track.label || track.label === component.audioTracks[component.currentAudioTrack]?.label);
component.player.selectVariantTrack(tracks[qualityIndex], true);
}
component.currentQuality = qualityIndex;
}
export function setAudioTrack(component, trackIndex) {
if (!component.player)
return;
const audioTracks = component.player.getAudioTracks();
if (audioTracks[trackIndex]) {
component.player.selectAudioTrack(audioTracks[trackIndex]);
component.currentAudioTrack = trackIndex;
setupQualityTrack(component);
}
}
export function toggleSettings(component, e) {
e.stopPropagation(); // Prevent component click from triggering the outside click handler
component.showSettings = !component.showSettings;
if (component.showSettings) {
component.showMainSettings = true;
component.showQualityOptions = false;
component.showAudioOptions = false;
}
}
export function handleGoLive(component) {
component.duration = component.player.seekRange().end;
component.videoElement.currentTime = component.duration;
}
export function handleClickOutsideSetting(component, event) {
if (!component.showSettings)
return;
const settingsMenu = component.hostElement.shadowRoot?.querySelector('.settings-menu');
const settingsButton = component.hostElement.shadowRoot?.querySelector('.control-btn');
// Check if click was outside both the menu and the settings button
const clickedInsideMenu = settingsMenu?.contains(event.target);
const clickedSettingsButton = settingsButton?.contains(event.target);
if (!clickedInsideMenu && !clickedSettingsButton) {
component.showSettings = false;
component.showQualityOptions = false;
component.showAudioOptions = false;
}
}
export function shouldShowToggle(component) {
// Only show toggle if description is longer than 2 lines
return component.videoDescription.length > 200; // Adjust this threshold as needed
}
export function toggleDescription(component) {
component.showFullDescription = !component.showFullDescription;
}
export async function toggleLike(component) {
try {
component.isLiked = !component.isLiked;
component.isDisliked = false;
component.totalLikes = component.isLiked ? component.totalLikes + 1 : component.totalLikes - 1;
// Track like click
component.analyticsTracker.trackReactionClick('like', component.isLiked);
await addReaction(component, 'like');
}
catch (error) {
component.isLiked = !component.isLiked;
component.isDisliked = false;
component.totalLikes = component.isLiked ? component.totalLikes + 1 : component.totalLikes - 1;
console.error('like toggle failed:', error);
component.videoError.emit('React to video failed');
}
}
export async function toggleDislike(component) {
const wasLiked = component.isLiked;
const wasDisliked = component.isDisliked;
const originalLikes = component.totalLikes;
try {
if (component.isLiked) {
component.isLiked = false;
component.totalLikes--;
}
component.isDisliked = !component.isDisliked;
// Track dislike click
component.analyticsTracker.trackReactionClick('dislike', component.isDisliked);
await addReaction(component, 'dislike');
}
catch (error) {
component.isLiked = wasLiked;
component.isDisliked = wasDisliked;
component.totalLikes = originalLikes;
console.error('Dislike toggle failed:', error);
component.videoError.emit('React to video failed');
}
}
export function formatTimeAgo(createdAt) {
const date = new Date(createdAt);
const now = new Date();
const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);
const intervals = {
year: 31536000,
month: 2592000,
week: 604800,
day: 86400,
hour: 3600,
minute: 60,
second: 1,
};
for (const [unit, secondsInUnit] of Object.entries(intervals)) {
const interval = Math.floor(seconds / secondsInUnit);
if (interval >= 1) {
return new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(-interval, unit);
}
}
return 'just now';
}
export function formatLikeCount(count) {
return Math.abs(count) >= 1e9
? (count / 1e9).toFixed(1).replace(/\.0$/, '') + 'B'
: Math.abs(count) >= 1e6
? (count / 1e6).toFixed(1).replace(/\.0$/, '') + 'M'
: Math.abs(count) >= 1e3
? (count / 1e3).toFixed(1).replace(/\.0$/, '') + 'K'
: count.toString();
}
export function getUserUUID(userId) {
if (userId)
return userId;
if (localStorage.getItem('spactatr_user_id')) {
return localStorage.getItem('spactatr_user_id');
}
else {
const id = crypto.randomUUID();
localStorage.setItem('spactatr_user_id', id);
return id;
}
}
export function cleanupData(component) {
component.src = null;
component.token = null;
component.videoDescription = '';
component.videoTitle = '';
component.isLoading = true;
component.showPoll = false;
component.showQuiz = false;
component.pollAnswered = [];
component.quizAnswered = [];
component.quizzes = null;
component.timelineEvents = [];
component.isLive = false;
component.currentTime = 0;
component.duration = 0;
clearTimeout(component.controlsTimeout);
clearInterval(component.progressUpdateInterval);
clearInterval(component.livePollingInterval);
clearInterval(component.reactionPollingInterval);
}
export function applyTheme(themeId, root) {
switch (themeId) {
case 'minimal-dark': //pass
root.style.setProperty('--vp-primary-bg', '#121212');
root.style.setProperty('--vp-controls-bg', 'rgba(0, 0, 0, 0.7)');
root.style.setProperty('--vp-accent', '#ff5555');
root.style.setProperty('--vp-video-text', '#ffffff');
root.style.setProperty('--vp-text', '#ffffff');
break;
case 'minimal-light': //pass
root.style.setProperty('--vp-primary-bg', '#f8f8f8');
root.style.setProperty('--vp-controls-bg', 'rgba(48, 48, 48, 0.7)');
root.style.setProperty('--vp-accent', '#e53e3e'); // Deep red for contrast
root.style.setProperty('--vp-video-text', '#ffffff');
root.style.setProperty('--vp-text', '#1a202c');
break;
case 'classic-youtube': //pass
root.style.setProperty('--vp-primary-bg', '#f9f9f9');
root.style.setProperty('--vp-controls-bg', 'rgba(0, 0, 0, 0.7)');
root.style.setProperty('--vp-accent', '#065fd4');
root.style.setProperty('--vp-video-text', '#ffffff');
root.style.setProperty('--vp-text', '#000');
break;
case 'cinema-mode': //pass
root.style.setProperty('--vp-primary-bg', '#f9f9f9');
root.style.setProperty('--vp-controls-bg', 'rgba(156, 3, 29, 0.45)');
root.style.setProperty('--vp-accent', '#cd2843ff');
root.style.setProperty('--vp-video-text', '#ffffff');
root.style.setProperty('--vp-text', '#000');
break;
case 'true-back': //pass
root.style.setProperty('--vp-primary-bg', '#000000');
root.style.setProperty('--vp-controls-bg', 'rgba(255, 255, 255, 0.3)');
root.style.setProperty('--vp-accent', '#000000');
root.style.setProperty('--vp-video-text', '#ffffff');
root.style.setProperty('--vp-text', '#aaaaaa');
break;
}
}
//# sourceMappingURL=control-utils.js.map