Implemented basic channel guide, needs moar polish

This commit is contained in:
rainbow napkin 2026-11-11 16:26:33 -05:00
parent c78b056cab
commit f7db5ea31d
3 changed files with 109 additions and 20 deletions

View file

@ -47,7 +47,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
} }
.channel-guide-schedule-row{ .channel-guide-schedule-row{
height: 2.2em; height: 3.4em;
} }
.channel-guide-channel-list-entry { .channel-guide-channel-list-entry {
@ -67,15 +67,20 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
.channel-guide-schedule-track { .channel-guide-schedule-track {
display: flex; display: flex;
position: relative;
} }
.channel-guide-schedule-entry { .channel-guide-schedule-entry {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
overflow: hidden;
position: absolute;
} }
.channel-guide-schedule-entry h3 { .channel-guide-schedule-entry h3 {
margin: 0; margin: 0;
text-overflow: ellipsis;
overflow: hidden;
text-wrap: nowrap; text-wrap: nowrap;
} }

View file

@ -393,6 +393,20 @@ input.control-prompt, input.control-prompt:focus{
border-bottom: 1px solid var(--accent0); border-bottom: 1px solid var(--accent0);
} }
.channel-guide-schedule-entry{
padding: 0 0.7em;
background-color: var(--bg1);
border-left: 1px solid var(--accent0);
}
.channel-guide-schedule-entry.starts-before{
border-left: none;
}
.channel-guide-schedule-entry.last-ends-before{
border-right: 1px solid var(--accent0);
}
/* channel settings */ /* channel settings */
.channel-info-label{ .channel-info-label{
color: var(--accent1-alt0); color: var(--accent1-alt0);

View file

@ -19,6 +19,9 @@ class ChannelGuide{
//define property to hold refresh timer //define property to hold refresh timer
this.refreshTimer; this.refreshTimer;
//define property to hold schedule cache
this.cache;
//Grab DOM Nodes //Grab DOM Nodes
this.scheduleIndex = document.querySelector('#channel-guide-schedule-index'); this.scheduleIndex = document.querySelector('#channel-guide-schedule-index');
this.channelList = document.querySelector('#channel-guide-channel-list'); this.channelList = document.querySelector('#channel-guide-channel-list');
@ -28,14 +31,17 @@ class ChannelGuide{
this.refreshList(); this.refreshList();
} }
async refreshList(){ async refreshList(now = new Date()){
//Fetch latest channel list and render it to the schedule page //Freshen up the local schedule cache
this.renderList(await utils.ajax.getChannels()); this.cache = await utils.ajax.getChannels();
//render the freshly pulled schedule data to the page
this.renderList(this.cache, now);
} }
renderList(schedule, now = new Date()){ renderList(schedule, now = new Date()){
this.renderIndex(now); this.renderIndex(now);
this.renderChannelList(schedule, now); this.renderChannelList(schedule);
this.renderSchedule(schedule, now); this.renderSchedule(schedule, now);
//if we've got a job to refresh the list //if we've got a job to refresh the list
@ -70,7 +76,7 @@ class ChannelGuide{
} }
} }
renderChannelList(schedule, now = new Date()){ renderChannelList(schedule){
//Clean out channel list //Clean out channel list
this.channelList.innerHTML = ''; this.channelList.innerHTML = '';
@ -96,27 +102,24 @@ class ChannelGuide{
//Clear out the schedule //Clear out the schedule
this.scheduledMediaDiv.innerHTML = ''; this.scheduledMediaDiv.innerHTML = '';
schedule = this.fillDeadAir(schedule, now);
//Iterate through all of the received channels //Iterate through all of the received channels
for(const channel of schedule){ for(const channel of schedule){
//Create a div to hold all of the current channels entries //Create a div to hold all of the current channels entries
const channelTrack = document.createElement('div'); const channelTrack = document.createElement('div');
channelTrack.classList.add('channel-guide-schedule-track'); channelTrack.classList.add('channel-guide-schedule-track', 'channel-guide-schedule-row');
//For every scheduled video //For every scheduled video
for(const entry of channel.schedule){ for(const entryIndex in channel.schedule){
//Create div for current schedule entry //Get entry by index
const entryDiv = document.createElement('div'); const entry = channel.schedule[entryIndex];
entryDiv.classList.add('channel-guide-schedule-entry', 'channel-guide-schedule-row'); //Check if this is the last item
const last = entryIndex == channel.schedule.length - 1;
//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 //slot entry into schedule track
channelTrack.appendChild(entryDiv); channelTrack.appendChild(this.renderScheduleItem(entry, now, last));
} }
//Inject channel track into schedule div //Inject channel track into schedule div
@ -124,7 +127,74 @@ class ChannelGuide{
} }
} }
renderScheduleItem(item){
fillDeadAir(schedule){
//For each video in the schedule
for(const video of schedule){
}
//Return processed schedule
return schedule;
}
renderScheduleItem(item, now = new Date(), last = false){
//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(item.title);
//calculate end epoch
const end = item.startTime + (item.duration * 1000);
//Place entryDiv by scheduled time
entryDiv.style.left = `${this.offsetByEpoch(item.startTime, now)}px`;
entryDiv.style.right = `${this.offsetByEpoch(end, now, true)}px`;
//if this started before the schedule time range
if(item.startTime < now.getTime()){
//mark it for css
entryDiv.classList.add('starts-before');
}
//if this is the last item and it ends before the schedule time range
if(last && end < now.getTime() + 5400000){
//mark it for css
entryDiv.classList.add('last-ends-before');
}
//Assemble the entry
entryDiv.appendChild(entryLabel);
//Return assembled entry div
return entryDiv;
}
offsetByEpoch(scheduleEpoch, now = new Date(), rightOffset = false){
//Calculate how many milliseconds after the current time our scheduled item is
const ms = scheduleEpoch - now.getTime();
//Calculate and return offset by milliseconds
return this.offsetByMilliseconds(ms, rightOffset, rightOffset);
}
offsetByMilliseconds(ms, rightOffset = false){
//Calculate place in schedule as float between 0 and 1 by deviding input by schedules range (90 minutes)
//Making sure to reverse value if rightOffset is thrown
const timeFloat = rightOffset ? 1 - ms / 5400000 : ms / 5400000;
//Get maximum position relative to the range itself
const offsetMax = this.scheduledMediaDiv.getBoundingClientRect().width;
//Calculate offset by timefloat and offset max
const offset = (offsetMax * timeFloat) < 0 ? 0 : (offsetMax * timeFloat);
//return position relative to parent, adding channel list width to left side to handle offset
return offset;
} }
} }