UNPKG

spectatr-player-sdk

Version:

A custom video player built with Stencil with Shaka Player integration

300 lines (299 loc) 11.6 kB
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