Added proper handling of items that begin late and end early.

This commit is contained in:
rainbow napkin 2025-02-09 17:29:07 -05:00
parent 330c4c275b
commit 179a10fb72
5 changed files with 286 additions and 69 deletions

View file

@ -505,18 +505,36 @@ div.queue-entry{
background-color: var(--bg2-alt1);
}
div.started-before-today{
div.started-yesterday{
border-top-left-radius: 0;
border-top-right-radius: 0;
border-top: 1px dashed var(--accent1);
}
div.ends-after-today{
div.ends-tomorrow{
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-bottom: 1px dashed var(--accent1);
}
div.started-late{
border-top-left-radius: 0;
border-top-right-radius: 0;
border-top: 1px dashed var(--danger0-alt1);
}
div.ended-early{
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-bottom: 1px dashed var(--danger0-alt1);
}
div.now-playing{
color: var(--focus0);
box-shadow: var(--focus-glow0);
text-shadow: var(--focus-glow0);
}
/* altcha theming*/
div.altcha{
box-shadow: 4px 4px 1px var(--bg1-alt0) inset;

View file

@ -83,6 +83,7 @@ class queuePanel extends panelObj{
this.client.socket.on("clientMetadata", (data) => {this.renderQueue();});
this.client.socket.on("queue", (data) => {this.renderQueue();});
this.client.socket.on("lock", this.handleScheduleLock.bind(this));
this.client.socket.on("error", this.handleQueueError.bind(this));
}
setupInput(){
@ -123,6 +124,26 @@ class queuePanel extends panelObj{
}
}
handleQueueError(data){
//Create a flag to reload the queue
let reloadQueue = false;
//Check errors
for(let error of data.errors){
//If we have a queue error
if(error.type == "queue"){
//Throw the reload flag
reloadQueue = true;
}
}
//If we need to reload the queue
if(reloadQueue){
//Do so
this.renderQueue();
}
}
/* queue control button functions */
toggleAddMedia(event){
//If the div is hidden
@ -491,13 +512,13 @@ class queuePanel extends panelObj{
entryDiv.style.top = `${this.offsetByDate(dawn)}px`;
//Run apply the rest of the styling rules
entryDiv.classList.add('started-before-today');
entryDiv.classList.add('started-yesterday');
}
//If the item ends today
if(endsToday){
//Place the bottom of the entry div based on time
entryDiv.style.bottom = `${this.offsetByDate(new Date(entry[1].startTime + (entry[1].duration * 1000)), true)}px`;
entryDiv.style.bottom = `${this.offsetByDate(new Date(this.getMediaEnd(entry[1])), true)}px`;
}else{
//Get midnight
const dusk = new Date();
@ -507,7 +528,17 @@ class queuePanel extends panelObj{
entryDiv.style.bottom = `${this.offsetByDate(dusk, true)}px`;
//Run apply the rest of the styling rules
entryDiv.classList.add('ends-after-today');
entryDiv.classList.add('ends-tomorrow');
}
//If we started in the middle of the video
if(entry[1].startTimeStamp > 0){
entryDiv.classList.add('started-late');
}
//If we ended early
if(entry[1].earlyEnd != null){
entryDiv.classList.add('ended-early');
}
//Create entry title
@ -526,8 +557,8 @@ class queuePanel extends panelObj{
`File Name: ${entry[1].fileName}`,
`Source: ${entry[1].type}`,
`Duration: ${entry[1].duration}`,
`Start Time: ${new Date(entry[1].startTime).toLocaleString()}`,
`End Time: ${new Date(entry[1].startTime + (entry[1].duration * 1000)).toLocaleString()}`
`Start Time: ${new Date(entry[1].startTime).toLocaleString()}${entry[1].startTimeStamp == 0 ? '' : ' (Started Late)'}`,
`End Time: ${new Date(this.getMediaEnd(entry[1])).toLocaleString()}`
]){
//Create a 'p' node
const component = document.createElement('p');
@ -546,14 +577,29 @@ class queuePanel extends panelObj{
}
});
//context menu
const menuMap = new Map([
["Play now", ()=>{this.client.socket.emit('move', {uuid: entry[1].uuid})}],
["Move To...", (event)=>{new reschedulePopup(event, this.client, entry[1], null, this.ownerDoc)}],
["Delete", ()=>{this.client.socket.emit('delete', {uuid: entry[1].uuid})}],
["Open in New Tab", ()=>{window.open(entry[1].url, '_blank').focus();}],
["Copy URL", ()=>{navigator.clipboard.writeText(entry[1].url);}],
]);
//Create context menu map
const menuMap = new Map();
//If the item hasn't started yet
if(entry[1].startTime > new Date().getTime()){
//Add 'Play' option to context menu
menuMap.set("Play now", ()=>{this.client.socket.emit('move', {uuid: entry[1].uuid})});
//Add 'Move To...' option to context menu
menuMap.set("Move To...", (event)=>{new reschedulePopup(event, this.client, entry[1], null, this.ownerDoc)});
//Otherwise, if the item is currently playing
}else if(this.getMediaEnd(entry[1]) > new Date().getTime()){
//Add 'Stop' option to context menu
menuMap.set("Stop", ()=>{this.client.socket.emit('stop', {uuid: entry[1].uuid})});
//Add the Now Playing glow, not the prettiest place to add this, but why let a good conditional go to waste?
entryDiv.classList.add('now-playing');
}
//Add 'Delete' option to context menu
menuMap.set("Delete", ()=>{this.client.socket.emit('delete', {uuid: entry[1].uuid})})
//Add 'New Tab' option to context menu
menuMap.set("Open in New Tab", ()=>{window.open(entry[1].url, '_blank').focus();})
//Add 'Copy URL' option to context menu
menuMap.set("Copy URL", ()=>{navigator.clipboard.writeText(entry[1].url);})
//Setup drag n drop
entryDiv.addEventListener('mousedown', clickEntry.bind(this));
@ -582,8 +628,29 @@ class queuePanel extends panelObj{
return;
}
//Create variables to hold width and height
const height = event.target.offsetHeight;
//Grab existing height
let height = event.target.offsetHeight;
let cutoffOffset = 0;
//If the item got cut-off at the bottom
if(event.target.classList.contains("ends-tomorrow") || event.target.classList.contains("ended-early")){
//Calculate height from duration
height = this.offsetByMilliseconds(Number(event.target.dataset['duration']) * 1000);
//If the item got cut-off at the top
}else if(event.target.classList.contains('started-yesterday') || event.target.classList.contains("started-late")){
//Keep old height for now
const oldHeight = height;
//Calculate height from duration
height = this.offsetByMilliseconds(Number(event.target.dataset['duration']) * 1000);
//Calculate the mouse offset needed to keep it properly placed relative to the original click point
cutoffOffset = height - oldHeight;
}
//Remove any cut-off borders
event.target.classList.remove('ends-tomorrow', 'started-yesterday', 'ended-early', 'started-late');
//If we havent set height or width
if(event.target.style.height == ""){
@ -611,7 +678,7 @@ class queuePanel extends panelObj{
this.ownerDoc.body.style.userSelect = 'none';
//Save top of target relative to window minus the mouse position as our drag offset
event.target.dataset['dragoffset'] = (event.target.offsetTop + this.ownerDoc.defaultView.scrollY) - event.clientY;
event.target.dataset['dragoffset'] = (event.target.offsetTop + this.ownerDoc.defaultView.scrollY) - event.clientY - cutoffOffset;
//Call the drag entry function to move the entry on click without re-writing the wheel
(dragEntry.bind(this))(event, event.target, timetip);
@ -711,7 +778,7 @@ class queuePanel extends panelObj{
function dropEntry(event, target, timetip){
//Gross but works :P
if(!target.isConnected){
if(!target.isConnected || target.dataset['drag'] != "true"){
return;
}
@ -861,24 +928,9 @@ class queuePanel extends panelObj{
const dayEpoch = new Date(date).setHours(0,0,0,0);
//Get difference between now and day epoch to get time since the start of the current day in milliseconds
const curTime = date.getTime() - dayEpoch;
//Devide by amount of milliseconds in a day to convert time over to a floating point number between 0 and 1
//Make sure to reverse it if bottomOffset is set to true
const timeFloat = bottomOffset ? 1 - (curTime / 86400000) : curTime / 86400000;
//Get queue markers
const markers = this.panelDocument.querySelectorAll('span.queue-marker');
//If the marker is null for some reason
if(markers[0] == null){
return null;
}
//Get marker position range
const range = [markers[0].offsetTop, markers[markers.length - 1].offsetTop]
//Get maximum position relative to the range itself
const offsetMax = range[1] - range[0];
//return position relative to parent
return (offsetMax * timeFloat) + range[0];
//Calculate the offset from todays milliseconds
return this.offsetByMilliseconds(curTime, bottomOffset);
}
dateByOffset(input = 0){
@ -902,6 +954,38 @@ class queuePanel extends panelObj{
//return our date
return date;
}
offsetByMilliseconds(input = 0, bottomOffset = false){
//Convert amount of milliseconds to a float, 0 representing the start of the day and 1 representing the end.
//Make sure to reverse it if bottomOffset is set to true
const timeFloat = bottomOffset ? 1 - (input / 86400000) : input / 86400000;
//Get queue markers
const markers = this.panelDocument.querySelectorAll('span.queue-marker');
//If the marker is null for some reason
if(markers[0] == null){
return null;
}
//Get marker position range
const range = [markers[0].offsetTop, markers[markers.length - 1].offsetTop]
//Get maximum position relative to the range itself
const offsetMax = range[1] - range[0];
//return position relative to parent
return (offsetMax * timeFloat) + range[0];
}
getMediaEnd(media){
//If we have an early end
if(media.earlyEnd != null){
return media.startTime + (media.earlyEnd * 1000);
//Otherwise
}else{
return media.startTime + ((media.duration - media.startTimeStamp) * 1000);
}
}
}
class schedulePopup{

View file

@ -553,7 +553,19 @@ class canopyUXUtils{
}
fixCutoff(standalone = true, pageBreak = document.body.scrollWidth - document.body.getBoundingClientRect().width){
fixCutoff(standalone = true, pageBreak){
//If we have no pagebreak
if(pageBreak == null){
//If we have a document body
if(document.body != null){
pageBreak = document.body.scrollWidth - document.body.getBoundingClientRect().width
//Otherwise
}else{
//Pretend nothing happened because we probably have bigger issues then a fucked up click-dragger cutoff
return;
}
}
//Fix the page width
if(this.flex){
this.element.style.flexBasis = `${this.calcWidth(this.element.getBoundingClientRect().width + pageBreak)}vw`;