/*Canopy - The next generation of stoner streaming software Copyright (C) 2024-2025 Rainbownapkin and the TTN Community This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see .*/ /* * Base class for all Canopy Media Handlers * * This is little more than a interface class */ class mediaHandler{ /** * Instantiates a new Media Handler object * @param {channel} client - Parent Client Management Object * @param {player} player - Parent Canopy Player Object * @param {Object} media - De-hydrated media object from server * @param {String} type - Media Handler Source Type */ constructor(client, player, media, type){ /** * Parent Client Management Object */ this.client = client; /** * Parent Canopy Player Object */ this.player = player; /** * Media Handler Source Type */ this.type = type /* * Denotes wether a seek call was made by the syncing function */ this.selfAct = false; /* * Contains the last received time stamp */ this.lastTimestamp = 0; //Ingest media object from server this.startMedia(media); } /** * Ingests media nd starts playback * @param {Object} media - Media object from server */ startMedia(media){ //If we properly ingested the media if(this.ingestMedia(media)){ //Build the video player this.buildPlayer(); //Call the start function this.start(); } } /** * Builds video player element */ buildPlayer(){ //Reset player lock this.lock = false; } /** * Destroys video player element */ destroyPlayer(){ //Null out video property this.video = null; } /** * Ingests media object from server * @param {Object} media - Media object from the server * @returns {Boolean} True upon success */ ingestMedia(media){ //Set now playing this.nowPlaying = media; //return true to signify success return true; } /** * Starts video playback */ start(){ this.setVideoTitle(this.nowPlaying.title); } /** * Syncronizes timestamp based on timestamp received from server * @param {Number} timestamp - Current video timestamp in seconds */ sync(timestamp = this.lastTimestamp){ //Skip sync calls that won't seek so we don't pointlessly throw selfAct if(timestamp != this.video.currentTime){ //Set self act flag this.selfAct = true; } } /** * Reloads media player */ reload(){ //Get current timestamp const timestamp = this.video.currentTime; //Throw self act flag to make sure we don't un-sync the player this.selfAct = true; } /** * Handles media end */ end(){ //Null out current media this.nowPlaying = null; //Throw self act to prevent unlock on video end this.selfAct = true; //Destroy the player this.destroyPlayer(); } /** * Plays video */ play(){ } /** * Pauses video */ pause(){ } /** * Toggles player control lockout * @param {Boolean} lock - Whether or not to lock-out user control of video */ setPlayerLock(lock){ //set lock property this.lock = lock; } /** * Calculates Aspect Ratio of media * @returns {Number} Media Aspect Ratio as Floating Point number */ getRatio(){ return 4/3; } /** * Gets current timestamp from video * @returns {Number} Media Timestamp in seconds */ getTimestamp(){ return 0; } /** * Sets player title * @param {String} title - Title to set */ setVideoTitle(title){ this.player.title.innerText = `Currently Playing: ${utils.unescapeEntities(title)}`; } /** * Called once all video metadata has properly been fetched * @param {Event} event - Event passed down by event handler */ onMetadataLoad(event){ //Resize aspect (if locked), since the video doesn't properly report it's resolution until it's been loaded this.client.chatBox.resizeAspect(); } /** * Called on media pause * @param {Event} event - Event passed down by event handler */ onPause(event){ //If the video was paused out-side of code if(!this.selfAct){ this.player.unlockSync(); } this.selfAct = false; } /** * Called on media volume change * @param {Event} event - Event passed down by event handler */ onVolumeChange(event){ } /** * Called on media seek * @param {Event} event - Event passed down by event handler */ onSeek(event){ //If the video was seeked out-side of code if(!this.selfAct){ this.player.unlockSync(); } //reset self act flag this.selfAct = false; } /** * Called on media buffer * @param {Event} event - Event passed down by event handler */ onBuffer(){ this.selfAct = true; } } /** * Class containing basic building blocks for anything that touches a