/*Canopy - The next generation of stoner streaming software Copyright (C) 2024-2026 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 .*/ class ChannelGuide{ constructor(){ //define property to hold refresh timer this.refreshTimer; //Grab DOM Nodes this.scheduleIndex = document.querySelector('#channel-guide-schedule-index'); this.channelList = document.querySelector('#channel-guide-channel-list'); this.scheduledMediaDiv = document.querySelector('#channel-guide-scheduled-media'); //Refresh the schedule this.refreshList(); } async refreshList(){ //Fetch latest channel list and render it to the schedule page this.renderList(await utils.ajax.getChannels()); } renderList(schedule, now = new Date()){ this.renderIndex(now); this.renderChannelList(schedule, now); this.renderSchedule(schedule, now); //if we've got a job to refresh the list if(this.refreshTimer != null){ //cancel it clearTimeout(this.refreshTimer); } //Set timer to refresh the thing erry minute on the minute setTimeout(this.refreshList.bind(this), 1000 * (60 - now.getSeconds())); } renderIndex(now = new Date()){ //Clear schedule index this.scheduleIndex.innerHTML = ''; //DO EM 3 TEIMS! for(let slot = 0; slot < 3; slot++){ //Clone passed date object to prevent over-writing const setDate = new Date(now.getTime()); //Up timeslot by 30 minutes for each timeslot in the loop setDate.setMinutes(setDate.getMinutes() + (slot * 30)); //Generate timeslot label const timeslotLabel = document.createElement('h3'); timeslotLabel.classList.add('channel-guide-timeslot-label'); timeslotLabel.textContent = utils.ux.timeStringFromDate(setDate, false); //Append timeslot label to schedule index this.scheduleIndex.appendChild(timeslotLabel); } } renderChannelList(schedule, now = new Date()){ //Clean out channel list this.channelList.innerHTML = ''; for(const channel of schedule){ //Create entry for channel const entry = document.createElement('div'); entry.classList.add('channel-guide-channel-list-entry', 'channel-guide-schedule-row'); entry.style.backgroundImage = `Url("${channel.thumbnail}")`; //Throw together a channel label const entryLabel = document.createElement('h3'); entryLabel.textContent = utils.unescapeEntities(channel.name); //Some assembly required entry.appendChild(entryLabel); //Add channel entry to the list this.channelList.appendChild(entry); } } renderSchedule(schedule, now = new Date()){ //Clear out the schedule this.scheduledMediaDiv.innerHTML = ''; //Iterate through all of the received channels for(const channel of schedule){ //Create a div to hold all of the current channels entries const channelTrack = document.createElement('div'); channelTrack.classList.add('channel-guide-schedule-track'); //For every scheduled video for(const entry of channel.schedule){ //Create div for current schedule entry const entryDiv = document.createElement('div'); entryDiv.classList.add('channel-guide-schedule-entry', 'channel-guide-schedule-row'); //Throw down an entry label const entryLabel = document.createElement('h3'); entryLabel.textContent = utils.unescapeEntities(entry.title); //Assemble the entry entryDiv.appendChild(entryLabel); //slot entry into schedule track channelTrack.appendChild(entryDiv); } //Inject channel track into schedule div this.scheduledMediaDiv.appendChild(channelTrack); } } renderScheduleItem(item){ } } const channelGuide = new ChannelGuide();