Started work on queueEntries for active livestreams.
This commit is contained in:
parent
a927b31919
commit
afa57e8080
|
|
@ -49,9 +49,9 @@ module.exports = class{
|
||||||
this.preSwitchTimer = null;
|
this.preSwitchTimer = null;
|
||||||
//Create variable to hold currently playing media object
|
//Create variable to hold currently playing media object
|
||||||
this.nowPlaying = null;
|
this.nowPlaying = null;
|
||||||
|
|
||||||
//Create variable to lock standard queuing functions during livestreams
|
//Create variable to lock standard queuing functions during livestreams
|
||||||
this.streamLock = false;
|
this.streamLock = false;
|
||||||
|
|
||||||
//create boolean to hold schedule lock
|
//create boolean to hold schedule lock
|
||||||
this.locked = false;
|
this.locked = false;
|
||||||
|
|
||||||
|
|
@ -1179,7 +1179,7 @@ module.exports = class{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create a new array to hold the new schedule so we only have to write to the DB once.
|
//Create a new array to hold the new schedule since we'll only be keeping select items
|
||||||
let newSched = [];
|
let newSched = [];
|
||||||
|
|
||||||
//For every saved scheduled item
|
//For every saved scheduled item
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
--danger0: firebrick;
|
--danger0: firebrick;
|
||||||
--danger0-alt0: rgb(121, 11, 11);
|
--danger0-alt0: rgb(121, 11, 11);
|
||||||
--danger0-alt1: rgb(255, 105, 105);
|
--danger0-alt1: rgb(255, 105, 105);
|
||||||
|
--danger0-alt2: rgb(242, 189, 189);
|
||||||
--danger-glow0: 2px 2px 3px var(--danger0), -2px 2px 3px var(--danger0), 2px -2px 3px var(--danger0), -2px -2px 3px var(--danger0);
|
--danger-glow0: 2px 2px 3px var(--danger0), -2px 2px 3px var(--danger0), 2px -2px 3px var(--danger0), -2px -2px 3px var(--danger0);
|
||||||
--danger-glow0-alt1: 2px 2px 3px var(--danger0-alt1), -2px 2px 3px var(--danger0-alt1), 2px -2px 3px var(--danger0-alt1), -2px -2px 3px var(--danger0-alt1);
|
--danger-glow0-alt1: 2px 2px 3px var(--danger0-alt1), -2px 2px 3px var(--danger0-alt1), 2px -2px 3px var(--danger0-alt1), -2px -2px 3px var(--danger0-alt1);
|
||||||
|
|
||||||
|
|
@ -504,6 +505,17 @@ div.queue-entry{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.queue-entry.live {
|
||||||
|
background-color: var(--danger0);
|
||||||
|
border: 1px solid var(--danger0-alt0);
|
||||||
|
box-shadow: 0px 9px 10px -3px var(--danger0-alt1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.queue-entry.live p{
|
||||||
|
color: var(--danger0-alt2);
|
||||||
|
}
|
||||||
|
|
||||||
.media-tooltip{
|
.media-tooltip{
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ class queuePanel extends panelObj{
|
||||||
|
|
||||||
closer(){
|
closer(){
|
||||||
//Clear any remaining timers
|
//Clear any remaining timers
|
||||||
clearTimeout(this.timeMarkerTimer);
|
clearTimeout(this.renderIntervalTimer);
|
||||||
//Clear timetips
|
//Clear timetips
|
||||||
this.killTimetips();
|
this.killTimetips();
|
||||||
}
|
}
|
||||||
|
|
@ -382,7 +382,7 @@ class queuePanel extends panelObj{
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set scale label text to humie readable time scale
|
//Set scale label text to humie readable time scale
|
||||||
this.scaleLabel.innerHTML = `Time Scale:<br>${this.humieFriendlyDuration(this.scale)}`
|
this.scaleLabel.innerHTML = `Time Scale:<br>${utils.ux.humieFriendlyDuration(this.scale)}`
|
||||||
|
|
||||||
//Clear any previous rescale timer
|
//Clear any previous rescale timer
|
||||||
clearTimeout(this.rescaleTimer);
|
clearTimeout(this.rescaleTimer);
|
||||||
|
|
@ -405,63 +405,6 @@ class queuePanel extends panelObj{
|
||||||
this.scrollLockButton.classList.remove('positive-button');
|
this.scrollLockButton.classList.remove('positive-button');
|
||||||
}
|
}
|
||||||
|
|
||||||
humieFriendlyDuration(seconds){
|
|
||||||
//If we have an invalid duration
|
|
||||||
if(seconds <= 0){
|
|
||||||
//bitch, moan, and complain!
|
|
||||||
return('Invalid input duration');
|
|
||||||
}
|
|
||||||
|
|
||||||
//Create an empty array to hold the time strings
|
|
||||||
const timeStrings = [];
|
|
||||||
|
|
||||||
//Pull hours from time
|
|
||||||
const hours = Math.floor(seconds / 3600);
|
|
||||||
//Remove recorded hours
|
|
||||||
seconds -= hours * 3600;
|
|
||||||
|
|
||||||
//Pull minutes from time
|
|
||||||
const minutes = Math.floor(seconds / 60);
|
|
||||||
//Remove recorded minutes
|
|
||||||
seconds -= minutes * 60;
|
|
||||||
|
|
||||||
//If we have an hour
|
|
||||||
if(hours == 1){
|
|
||||||
//Add the string
|
|
||||||
timeStrings.push('1 Hour');
|
|
||||||
//If we have hours
|
|
||||||
}else if(hours > 0){
|
|
||||||
//Add the string
|
|
||||||
timeStrings.push(`${hours} Hours`);
|
|
||||||
}
|
|
||||||
|
|
||||||
//If we have a minute
|
|
||||||
if(minutes == 1){
|
|
||||||
//Add the string
|
|
||||||
timeStrings.push('1 Minute');
|
|
||||||
//If we have minutes
|
|
||||||
}else if(minutes > 0){
|
|
||||||
//Add the string
|
|
||||||
timeStrings.push(`${minutes} Minutes`);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add the 'and ' if we need it
|
|
||||||
const secondsPrefix = timeStrings.length > 0 ? 'and ' : '';
|
|
||||||
|
|
||||||
//If we have a second
|
|
||||||
if(seconds == 1){
|
|
||||||
//Add the string
|
|
||||||
timeStrings.push(`${secondsPrefix}1 Second`);
|
|
||||||
//If we have more than a second
|
|
||||||
}else if(seconds > 1){
|
|
||||||
//Add the string
|
|
||||||
timeStrings.push(`${secondsPrefix}${seconds} Seconds`);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Join the time strings together
|
|
||||||
return timeStrings.join(', ');
|
|
||||||
}
|
|
||||||
|
|
||||||
resizeRender(event){
|
resizeRender(event){
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
this.renderQueue(date);
|
this.renderQueue(date);
|
||||||
|
|
@ -490,7 +433,7 @@ class queuePanel extends panelObj{
|
||||||
}
|
}
|
||||||
|
|
||||||
//Clear any marker timers
|
//Clear any marker timers
|
||||||
clearTimeout(this.timeMarkerTimer);
|
clearTimeout(this.renderIntervalTimer);
|
||||||
|
|
||||||
//If we have an existing time marker
|
//If we have an existing time marker
|
||||||
if(this.timeMarker != null){
|
if(this.timeMarker != null){
|
||||||
|
|
@ -501,7 +444,6 @@ class queuePanel extends panelObj{
|
||||||
}
|
}
|
||||||
|
|
||||||
async fullRender(date = new Date()){
|
async fullRender(date = new Date()){
|
||||||
|
|
||||||
//Clear the queue
|
//Clear the queue
|
||||||
this.clearQueue();
|
this.clearQueue();
|
||||||
|
|
||||||
|
|
@ -524,9 +466,12 @@ class queuePanel extends panelObj{
|
||||||
await utils.ux.awaitNextFrame();
|
await utils.ux.awaitNextFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
//render the time marker
|
//render the time marker w/ force scroll
|
||||||
this.renderTimeMarker(date, true);
|
this.renderTimeMarker(date, true);
|
||||||
|
|
||||||
|
//Kick off render interval
|
||||||
|
this.renderInterval(date);
|
||||||
|
|
||||||
//render out the queue
|
//render out the queue
|
||||||
this.renderQueue(date);
|
this.renderQueue(date);
|
||||||
}
|
}
|
||||||
|
|
@ -575,7 +520,7 @@ class queuePanel extends panelObj{
|
||||||
//Place item beginning at dawn
|
//Place item beginning at dawn
|
||||||
entryDiv.style.top = `${this.offsetByDate(dawn)}px`;
|
entryDiv.style.top = `${this.offsetByDate(dawn)}px`;
|
||||||
|
|
||||||
//Run apply the rest of the styling rules
|
//Apply style rules for items that starrted yesterday
|
||||||
entryDiv.classList.add('started-yesterday');
|
entryDiv.classList.add('started-yesterday');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -620,14 +565,14 @@ class queuePanel extends panelObj{
|
||||||
`Title: ${entry[1].title}`,
|
`Title: ${entry[1].title}`,
|
||||||
`File Name: ${entry[1].fileName}`,
|
`File Name: ${entry[1].fileName}`,
|
||||||
`Source: ${entry[1].type}`,
|
`Source: ${entry[1].type}`,
|
||||||
`Duration: ${entry[1].duration}`,
|
`Duration: ${utils.ux.humieFriendlyDuration(entry[1].duration)}`,
|
||||||
`Start Time: ${new Date(entry[1].startTime).toLocaleString()}${entry[1].startTimeStamp == 0 ? '' : ' (Started Late)'}`,
|
`Start Time: ${new Date(entry[1].startTime).toLocaleString()}${entry[1].startTimeStamp == 0 ? '' : ' (Started Late)'}`,
|
||||||
`End Time: ${new Date(this.getMediaEnd(entry[1])).toLocaleString()}${entry[1].earlyEnd == null ? '' : ' (Ended Early)'}`
|
`End Time: ${new Date(this.getMediaEnd(entry[1])).toLocaleString()}${entry[1].earlyEnd == null ? '' : ' (Ended Early)'}`
|
||||||
]){
|
]){
|
||||||
//Create a 'p' node
|
//Create a 'p' node
|
||||||
const component = document.createElement('p');
|
const component = document.createElement('p');
|
||||||
//Fill it with the string
|
//Fill it with the string
|
||||||
component.textContent = string;
|
component.textContent = utils.unescapeEntities(string);
|
||||||
//Append it to the tooltip div
|
//Append it to the tooltip div
|
||||||
tooltipDiv.append(component);
|
tooltipDiv.append(component);
|
||||||
}
|
}
|
||||||
|
|
@ -690,6 +635,9 @@ class queuePanel extends panelObj{
|
||||||
//Append entry
|
//Append entry
|
||||||
this.queueContainer.append(entryDiv);
|
this.queueContainer.append(entryDiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Render out any playing livestreams
|
||||||
|
this.renderLiveStream(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickEntry(event){
|
function clickEntry(event){
|
||||||
|
|
@ -888,6 +836,18 @@ class queuePanel extends panelObj{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderInterval(date = new Date()){
|
||||||
|
this.renderTimeMarker(date);
|
||||||
|
this.renderLiveStream(date);
|
||||||
|
|
||||||
|
|
||||||
|
//Clear update timer
|
||||||
|
clearTimeout(this.renderIntervalTimer);
|
||||||
|
|
||||||
|
//Set timer to update marker every second
|
||||||
|
this.renderIntervalTimer = setTimeout(this.renderInterval.bind(this), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
renderTimeMarker(date = new Date(), forceScroll = false){
|
renderTimeMarker(date = new Date(), forceScroll = false){
|
||||||
//Calculate marker position by date
|
//Calculate marker position by date
|
||||||
const markerPosition = Math.round(this.offsetByDate(date));
|
const markerPosition = Math.round(this.offsetByDate(date));
|
||||||
|
|
@ -895,7 +855,7 @@ class queuePanel extends panelObj{
|
||||||
//If markers are null
|
//If markers are null
|
||||||
if(markerPosition == null){
|
if(markerPosition == null){
|
||||||
//Try again in a second since the user probably just popped the panel or something :P
|
//Try again in a second since the user probably just popped the panel or something :P
|
||||||
(smackTimer.bind(this))();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//If we're not looking at today
|
//If we're not looking at today
|
||||||
|
|
@ -940,16 +900,6 @@ class queuePanel extends panelObj{
|
||||||
behavior: scrollBehavior
|
behavior: scrollBehavior
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set the timer to run the function again
|
|
||||||
(smackTimer.bind(this))();
|
|
||||||
|
|
||||||
function smackTimer(){
|
|
||||||
//Clear update timer
|
|
||||||
clearTimeout(this.timeMarkerTimer);
|
|
||||||
//Set timer to update marker every second
|
|
||||||
this.timeMarkerTimer = setTimeout(this.renderTimeMarker.bind(this), 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderQueueScale(inputDate = new Date()){
|
renderQueueScale(inputDate = new Date()){
|
||||||
|
|
@ -1015,6 +965,135 @@ class queuePanel extends panelObj{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderLiveStream(date = new Date()){
|
||||||
|
//Grab all live queue entries
|
||||||
|
const staleEntry = this.queueContainer.querySelector('.queue-entry.live');
|
||||||
|
|
||||||
|
//If we're not livestreaming
|
||||||
|
if(client.player.mediaHandler.type != "livehls"){
|
||||||
|
//If we have a stale entry
|
||||||
|
if(staleEntry != null){
|
||||||
|
//Remove stale entry since we're no longer streaming
|
||||||
|
staleEntry.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fuck off and die
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Grab currently playing media
|
||||||
|
const nowPlaying = client.player.mediaHandler.nowPlaying;
|
||||||
|
|
||||||
|
//If we don't have a good stale entry to re-use
|
||||||
|
if(staleEntry == null || staleEntry.dataset.uuid != nowPlaying.uuid){
|
||||||
|
//If it wasn't null but just old
|
||||||
|
if(staleEntry != null){
|
||||||
|
//Nukem
|
||||||
|
staleEntry.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create entry div
|
||||||
|
const entryDiv = document.createElement('div');
|
||||||
|
entryDiv.classList.add('queue-entry', 'live');
|
||||||
|
|
||||||
|
//Iterate through nowPlaying's properties
|
||||||
|
for(let key of Object.keys(nowPlaying)){
|
||||||
|
//Add them to the entry div's dataset
|
||||||
|
entryDiv.dataset[key] = nowPlaying[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert start epoch to JS date object
|
||||||
|
const started = new Date(nowPlaying.startTime);
|
||||||
|
|
||||||
|
//If this started today
|
||||||
|
if(utils.isSameDate(this.day, started)){
|
||||||
|
//Set entryDiv top-border location based on start time
|
||||||
|
entryDiv.style.top = `${this.offsetByDate(started)}px`
|
||||||
|
}else{
|
||||||
|
//Get dawn
|
||||||
|
const dawn = new Date();
|
||||||
|
dawn.setHours(0,0,0,0)
|
||||||
|
|
||||||
|
//Place item at the beginning of dawn
|
||||||
|
entryDiv.style.top = `${this.offsetByDate(dawn)}px`;
|
||||||
|
|
||||||
|
//Apply rest of the styling rules for items that started yestarday
|
||||||
|
entryDiv.classList.add('started-yesterday')
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create entry title
|
||||||
|
const entryTitle = document.createElement('p');
|
||||||
|
entryTitle.textContent = utils.unescapeEntities(nowPlaying.title);
|
||||||
|
|
||||||
|
//Set entry div bottom-border location based on current time
|
||||||
|
entryDiv.style.bottom = `${this.offsetByDate(date, true)}px`
|
||||||
|
|
||||||
|
//Assembly entryDiv
|
||||||
|
entryDiv.appendChild(entryTitle);
|
||||||
|
|
||||||
|
//Setup media tooltip
|
||||||
|
entryDiv.addEventListener('mouseenter',(event)=>{
|
||||||
|
//Construct tooltip on mouseover to re-calculate duration for live media
|
||||||
|
const tooltipDiv = buildTooltip();
|
||||||
|
|
||||||
|
//Display tooltip
|
||||||
|
utils.ux.displayTooltip(event, tooltipDiv, false, null, true, this.ownerDoc);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Append entry div to queue container
|
||||||
|
this.queueContainer.appendChild(entryDiv);
|
||||||
|
}else{
|
||||||
|
//Update existing entry
|
||||||
|
staleEntry.style.bottom = `${this.offsetByDate(date, true)}px`
|
||||||
|
}
|
||||||
|
|
||||||
|
//Keep tooltip date seperate so it re-calculates live duration properly
|
||||||
|
function buildTooltip(date = new Date()){
|
||||||
|
//Tooltip generation
|
||||||
|
//tooltip div
|
||||||
|
const tooltipDiv = document.createElement('div');
|
||||||
|
tooltipDiv.classList.add('media-tooltip');
|
||||||
|
|
||||||
|
//tooltip components
|
||||||
|
const tooltipStrings = [
|
||||||
|
`Title: ${nowPlaying.title}`,
|
||||||
|
`File Name: ${nowPlaying.fileName}`,
|
||||||
|
`Source: HLS Livestream (${nowPlaying.url})`,
|
||||||
|
`Duration: ${utils.ux.humieFriendlyDuration((date.getTime() - nowPlaying.startTime) / 1000)}`,
|
||||||
|
`Start Time: ${new Date(nowPlaying.startTime).toLocaleString()}`,
|
||||||
|
];
|
||||||
|
|
||||||
|
//For each string in the tooltip
|
||||||
|
for(const string of tooltipStrings){
|
||||||
|
|
||||||
|
//Create a 'p' node
|
||||||
|
const component = document.createElement('p');
|
||||||
|
//Fill it with the string
|
||||||
|
component.textContent = utils.unescapeEntities(string);
|
||||||
|
|
||||||
|
//Append it to the tooltip div
|
||||||
|
tooltipDiv.append(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create End Date
|
||||||
|
const source = document.createElement('p');
|
||||||
|
const liveSpan = document.createElement('span');
|
||||||
|
|
||||||
|
//Fill end date label
|
||||||
|
source.textContent = "End Time: "
|
||||||
|
liveSpan.textContent = 'LIVE'
|
||||||
|
|
||||||
|
//Set class
|
||||||
|
liveSpan.classList.add('critical-danger-text');
|
||||||
|
|
||||||
|
//Assemble end date
|
||||||
|
source.appendChild(liveSpan);
|
||||||
|
tooltipDiv.appendChild(source);
|
||||||
|
|
||||||
|
return tooltipDiv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
offsetByDate(date = new Date(), bottomOffset = false){
|
offsetByDate(date = new Date(), bottomOffset = false){
|
||||||
//Pull start of day epoch from given date, make sure to use a new date object so we don't fuck up any date objects passed to us
|
//Pull start of day epoch from given date, make sure to use a new date object so we don't fuck up any date objects passed to us
|
||||||
const dayEpoch = new Date(date).setHours(0,0,0,0);
|
const dayEpoch = new Date(date).setHours(0,0,0,0);
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,63 @@ class canopyUXUtils{
|
||||||
return outString;
|
return outString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
humieFriendlyDuration(seconds){
|
||||||
|
//If we have an invalid duration
|
||||||
|
if(seconds <= 0){
|
||||||
|
//bitch, moan, and complain!
|
||||||
|
return('Invalid input duration');
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create an empty array to hold the time strings
|
||||||
|
const timeStrings = [];
|
||||||
|
|
||||||
|
//Pull hours from time
|
||||||
|
const hours = Math.floor(seconds / 3600);
|
||||||
|
//Remove recorded hours
|
||||||
|
seconds -= hours * 3600;
|
||||||
|
|
||||||
|
//Pull minutes from time
|
||||||
|
const minutes = Math.floor(seconds / 60);
|
||||||
|
//Remove recorded minutes
|
||||||
|
seconds -= minutes * 60;
|
||||||
|
|
||||||
|
//If we have an hour
|
||||||
|
if(hours == 1){
|
||||||
|
//Add the string
|
||||||
|
timeStrings.push('1 Hour');
|
||||||
|
//If we have hours
|
||||||
|
}else if(hours > 0){
|
||||||
|
//Add the string
|
||||||
|
timeStrings.push(`${hours} Hours`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we have a minute
|
||||||
|
if(minutes == 1){
|
||||||
|
//Add the string
|
||||||
|
timeStrings.push('1 Minute');
|
||||||
|
//If we have minutes
|
||||||
|
}else if(minutes > 0){
|
||||||
|
//Add the string
|
||||||
|
timeStrings.push(`${minutes} Minutes`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add the 'and ' if we need it
|
||||||
|
const secondsPrefix = timeStrings.length > 0 ? 'and ' : '';
|
||||||
|
|
||||||
|
//If we have a second
|
||||||
|
if(seconds == 1){
|
||||||
|
//Add the string
|
||||||
|
timeStrings.push(`${secondsPrefix}1 Second`);
|
||||||
|
//If we have more than a second
|
||||||
|
}else if(seconds > 1){
|
||||||
|
//Add the string
|
||||||
|
timeStrings.push(`${secondsPrefix}${Math.round(seconds)} Seconds`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Join the time strings together
|
||||||
|
return timeStrings.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
//Update this and popup class to use nodes
|
//Update this and popup class to use nodes
|
||||||
//and display multiple errors in one popup
|
//and display multiple errors in one popup
|
||||||
displayResponseError(body){
|
displayResponseError(body){
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue