mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
rework live tv guide to speed up rendering
This commit is contained in:
parent
95fe14c3ba
commit
7a37b23d9e
2 changed files with 138 additions and 187 deletions
|
@ -158,7 +158,13 @@
|
||||||
|
|
||||||
.channelPrograms {
|
.channelPrograms {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
height: 56px;
|
height: 55px;
|
||||||
|
border-bottom: 1px solid #404040;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channelPrograms, .timeslotHeadersInner {
|
||||||
|
width: 8000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeslotHeader {
|
.timeslotHeader {
|
||||||
|
@ -170,14 +176,6 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeslotCell {
|
|
||||||
display: inline-block;
|
|
||||||
border-bottom: 1px solid #404040;
|
|
||||||
border-left: 1px solid #404040;
|
|
||||||
border-collapse: collapse;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channelHeaderCell, .channelTimeslotHeader {
|
.channelHeaderCell, .channelTimeslotHeader {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -192,8 +190,6 @@
|
||||||
|
|
||||||
.channelTimeslotHeader, .timeslotHeader {
|
.channelTimeslotHeader, .timeslotHeader {
|
||||||
background: #38c;
|
background: #38c;
|
||||||
border-bottom: 1px solid #38c;
|
|
||||||
border-left: 1px solid #38c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeslotHeader, .channelTimeslotHeader {
|
.timeslotHeader, .channelTimeslotHeader {
|
||||||
|
@ -226,10 +222,30 @@
|
||||||
margin-left: 190px;
|
margin-left: 190px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeslotCell, .timeslotHeader {
|
.timeslotHeader {
|
||||||
width: 60px;
|
width: 2.0833333333333333333333333333333%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.programCell {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.programCellInner {
|
||||||
|
border-left: 1px solid #404040;
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
color: #fff !important;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400 !important;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.programCellInner:hover {
|
||||||
|
background-color: #38c;
|
||||||
|
}
|
||||||
|
|
||||||
.timeslotCellInner {
|
.timeslotCellInner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -242,15 +258,7 @@
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeslotCellInnerWithProgram {
|
.channelHeaderCell {
|
||||||
z-index: 99;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeslotCellInnerWithProgram:hover {
|
|
||||||
background-color: #38c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channelHeaderCell, .timeslotCell {
|
|
||||||
height: 55px;
|
height: 55px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,6 +314,10 @@
|
||||||
.currentDay {
|
.currentDay {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.channelPrograms, .timeslotHeadersInner {
|
||||||
|
width: 6000px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.channelList, .programGrid {
|
.channelList, .programGrid {
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
(function ($, document) {
|
(function ($, document) {
|
||||||
|
|
||||||
// 30 mins
|
// 30 mins
|
||||||
var cellCurationMinutes = 10;
|
var cellCurationMinutes = 30;
|
||||||
var cellDurationMs = cellCurationMinutes * 60 * 1000;
|
var cellDurationMs = cellCurationMinutes * 60 * 1000;
|
||||||
|
var msPerDay = 86400000;
|
||||||
var gridLocalStartDateMs;
|
|
||||||
var gridLocalEndDateMs;
|
|
||||||
|
|
||||||
var currentDate;
|
var currentDate;
|
||||||
|
|
||||||
|
@ -57,9 +55,7 @@
|
||||||
|
|
||||||
var date = currentDate;
|
var date = currentDate;
|
||||||
|
|
||||||
var nextDay = new Date(date.getTime());
|
var nextDay = new Date(date.getTime() + msPerDay - 1);
|
||||||
nextDay.setHours(0, 0, 0, 0);
|
|
||||||
nextDay.setDate(nextDay.getDate() + 1);
|
|
||||||
console.log(nextDay);
|
console.log(nextDay);
|
||||||
channelsPromise.done(function (channelsResult) {
|
channelsPromise.done(function (channelsResult) {
|
||||||
|
|
||||||
|
@ -74,6 +70,7 @@
|
||||||
}).done(function (programsResult) {
|
}).done(function (programsResult) {
|
||||||
|
|
||||||
renderGuide(page, date, channelsResult.Items, programsResult.Items);
|
renderGuide(page, date, channelsResult.Items, programsResult.Items);
|
||||||
|
|
||||||
hideLoadingMessage(page);
|
hideLoadingMessage(page);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -93,101 +90,65 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTimeslotHeadersHtml(date) {
|
function getTimeslotHeadersHtml(startDate, endDateTime) {
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
|
|
||||||
date = new Date(date.getTime());
|
// clone
|
||||||
var dateNumber = date.getDate();
|
startDate = new Date(startDate.getTime());
|
||||||
|
|
||||||
while (date.getDate() == dateNumber) {
|
html += '<div class="timeslotHeadersInner">'
|
||||||
|
|
||||||
|
while (startDate.getTime() < endDateTime) {
|
||||||
|
|
||||||
html += '<div class="timeslotHeader">';
|
html += '<div class="timeslotHeader">';
|
||||||
html += '<div class="timeslotHeaderInner">';
|
html += '<div class="timeslotHeaderInner">';
|
||||||
|
|
||||||
var minutes = date.getMinutes();
|
html += LiveTvHelpers.getDisplayTime(startDate);
|
||||||
if (minutes == 0 || minutes == 30) {
|
|
||||||
html += LiveTvHelpers.getDisplayTime(date);
|
|
||||||
} else {
|
|
||||||
html += ' ';
|
|
||||||
}
|
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
// Add 30 mins
|
// Add 30 mins
|
||||||
date.setTime(date.getTime() + cellDurationMs);
|
startDate.setTime(startDate.getTime() + cellDurationMs);
|
||||||
}
|
}
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findProgramStartingInCell(programs, startIndex, cellStart, cellEnd, cellIndex) {
|
function parseDates(program) {
|
||||||
|
|
||||||
for (var i = startIndex, length = programs.length; i < length; i++) {
|
if (!program.StartDateLocal) {
|
||||||
|
try {
|
||||||
|
|
||||||
var program = programs[i];
|
program.StartDateLocal = parseISO8601Date(program.StartDate, { toLocal: true });
|
||||||
|
|
||||||
if (!program.StartDateLocal) {
|
} catch (err) {
|
||||||
try {
|
|
||||||
|
|
||||||
program.StartDateLocal = parseISO8601Date(program.StartDate, { toLocal: true });
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!program.EndDateLocal) {
|
}
|
||||||
try {
|
|
||||||
|
|
||||||
program.EndDateLocal = parseISO8601Date(program.EndDate, { toLocal: true });
|
if (!program.EndDateLocal) {
|
||||||
|
try {
|
||||||
|
|
||||||
} catch (err) {
|
program.EndDateLocal = parseISO8601Date(program.EndDate, { toLocal: true });
|
||||||
|
|
||||||
}
|
} catch (err) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var localTime = program.StartDateLocal.getTime();
|
|
||||||
if ((localTime >= cellStart || cellIndex == 0) && localTime < cellEnd && program.EndDateLocal > cellStart) {
|
|
||||||
|
|
||||||
return program;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProgramWidth(program) {
|
|
||||||
|
|
||||||
var end = Math.min(gridLocalEndDateMs, program.EndDateLocal.getTime());
|
|
||||||
var start = Math.max(gridLocalStartDateMs, program.StartDateLocal.getTime());
|
|
||||||
|
|
||||||
var ms = end - start;
|
|
||||||
|
|
||||||
var width = 100 * ms / cellDurationMs;
|
|
||||||
|
|
||||||
// Round to the nearest cell
|
|
||||||
var overlap = width % 100;
|
|
||||||
|
|
||||||
if (overlap) {
|
|
||||||
width = width - overlap + 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width > 300) {
|
|
||||||
width += (width / 100) + 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getChannelProgramsHtml(page, date, channel, programs) {
|
function getChannelProgramsHtml(page, date, channel, programs) {
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
|
|
||||||
var dateNumber = date.getDate();
|
var startMs = date.getTime();
|
||||||
|
var endMs = startMs + msPerDay - 1;
|
||||||
|
|
||||||
programs = programs.filter(function (curr) {
|
programs = programs.filter(function (curr) {
|
||||||
return curr.ChannelId == channel.Id;
|
return curr.ChannelId == channel.Id;
|
||||||
|
@ -195,109 +156,87 @@
|
||||||
|
|
||||||
html += '<div class="channelPrograms">';
|
html += '<div class="channelPrograms">';
|
||||||
|
|
||||||
var cellIndex = 0;
|
for (var i = 0, length = programs.length; i < length; i++) {
|
||||||
|
|
||||||
while (date.getDate() == dateNumber) {
|
var program = programs[i];
|
||||||
|
|
||||||
// Add 30 mins
|
if (program.ChannelId != channel.Id) {
|
||||||
var cellEndDate = new Date(date.getTime() + cellDurationMs);
|
continue;
|
||||||
|
|
||||||
var program = findProgramStartingInCell(programs, 0, date, cellEndDate, cellIndex);
|
|
||||||
|
|
||||||
var minutes = date.getMinutes();
|
|
||||||
if (minutes == 0 || minutes == 30) {
|
|
||||||
html += '<div class="timeslotCell">';
|
|
||||||
} else {
|
|
||||||
html += '<div class="timeslotCell" style="border-left-color:transparent;">';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var cellTagName;
|
parseDates(program);
|
||||||
var href;
|
|
||||||
var cssClass = "timeslotCellInner";
|
|
||||||
var style;
|
|
||||||
var dataProgramId;
|
|
||||||
|
|
||||||
if (program) {
|
if (program.EndDateLocal.getTime() < startMs) {
|
||||||
if (program.IsKids) {
|
continue;
|
||||||
cssClass += " childProgramInfo";
|
|
||||||
} else if (program.IsSports) {
|
|
||||||
cssClass += " sportsProgramInfo";
|
|
||||||
} else if (program.IsNews) {
|
|
||||||
cssClass += " newsProgramInfo";
|
|
||||||
} else if (program.IsMovie) {
|
|
||||||
cssClass += " movieProgramInfo";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cssClass += " plainProgramInfo";
|
|
||||||
}
|
|
||||||
|
|
||||||
cssClass += " timeslotCellInnerWithProgram";
|
|
||||||
|
|
||||||
cellTagName = "a";
|
|
||||||
href = ' href="livetvprogram.html?id=' + program.Id + '"';
|
|
||||||
|
|
||||||
var width = getProgramWidth(program);
|
|
||||||
|
|
||||||
if (width && width != 100) {
|
|
||||||
style = ' style="width:' + width + '%;"';
|
|
||||||
} else {
|
|
||||||
style = '';
|
|
||||||
}
|
|
||||||
dataProgramId = ' data-programid="' + program.Id + '"';
|
|
||||||
} else {
|
|
||||||
cellTagName = "div";
|
|
||||||
href = '';
|
|
||||||
style = '';
|
|
||||||
dataProgramId = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '<' + cellTagName + dataProgramId + ' class="' + cssClass + '"' + href + style + '>';
|
if (program.StartDateLocal.getTime() > endMs) {
|
||||||
|
break;
|
||||||
if (program) {
|
|
||||||
|
|
||||||
html += '<div class="guideProgramName">';
|
|
||||||
html += program.Name;
|
|
||||||
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
html += '<div class="guideProgramTime">';
|
|
||||||
|
|
||||||
if (program.IsLive) {
|
|
||||||
html += '<span class="liveTvProgram">' + Globalize.translate('LabelLiveProgram') + ' </span>';
|
|
||||||
}
|
|
||||||
else if (program.IsPremiere) {
|
|
||||||
html += '<span class="premiereTvProgram">' + Globalize.translate('LabelPremiereProgram') + ' </span>';
|
|
||||||
}
|
|
||||||
else if (program.IsSeries && !program.IsRepeat) {
|
|
||||||
html += '<span class="newTvProgram">' + Globalize.translate('LabelNewProgram') + ' </span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += LiveTvHelpers.getDisplayTime(program.StartDateLocal);
|
|
||||||
html += ' - ';
|
|
||||||
html += LiveTvHelpers.getDisplayTime(program.EndDateLocal);
|
|
||||||
|
|
||||||
if (program.SeriesTimerId) {
|
|
||||||
html += '<div class="timerCircle seriesTimerCircle"></div>';
|
|
||||||
html += '<div class="timerCircle seriesTimerCircle"></div>';
|
|
||||||
html += '<div class="timerCircle seriesTimerCircle"></div>';
|
|
||||||
}
|
|
||||||
else if (program.TimerId) {
|
|
||||||
|
|
||||||
html += '<div class="timerCircle"></div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
} else {
|
|
||||||
html += ' ';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '</' + cellTagName + '>';
|
var renderStartMs = Math.max(program.StartDateLocal.getTime(), startMs);
|
||||||
|
var startPercent = (program.StartDateLocal.getTime() - startMs) / msPerDay;
|
||||||
|
startPercent *= 100;
|
||||||
|
startPercent = Math.max(startPercent, 0);
|
||||||
|
|
||||||
|
var renderEndMs = Math.min(program.EndDateLocal.getTime(), endMs);
|
||||||
|
var endPercent = (renderEndMs - renderStartMs) / msPerDay;
|
||||||
|
endPercent *= 100;
|
||||||
|
|
||||||
|
html += '<div class="programCell" style="left:' + startPercent + '%;width:' + endPercent + '%;">';
|
||||||
|
|
||||||
|
var cssClass = "programCellInner";
|
||||||
|
|
||||||
|
if (program.IsKids) {
|
||||||
|
cssClass += " childProgramInfo";
|
||||||
|
} else if (program.IsSports) {
|
||||||
|
cssClass += " sportsProgramInfo";
|
||||||
|
} else if (program.IsNews) {
|
||||||
|
cssClass += " newsProgramInfo";
|
||||||
|
} else if (program.IsMovie) {
|
||||||
|
cssClass += " movieProgramInfo";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cssClass += " plainProgramInfo";
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '<a href="livetvprogram.html?id=' + program.Id + '" class="' + cssClass + '" data-programid="' + program.Id + '">';
|
||||||
|
|
||||||
|
html += '<div class="guideProgramName">';
|
||||||
|
html += program.Name;
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
date = cellEndDate;
|
html += '<div class="guideProgramTime">';
|
||||||
cellIndex++;
|
if (program.IsLive) {
|
||||||
|
html += '<span class="liveTvProgram">' + Globalize.translate('LabelLiveProgram') + ' </span>';
|
||||||
|
}
|
||||||
|
else if (program.IsPremiere) {
|
||||||
|
html += '<span class="premiereTvProgram">' + Globalize.translate('LabelPremiereProgram') + ' </span>';
|
||||||
|
}
|
||||||
|
else if (program.IsSeries && !program.IsRepeat) {
|
||||||
|
html += '<span class="newTvProgram">' + Globalize.translate('LabelNewProgram') + ' </span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += LiveTvHelpers.getDisplayTime(program.StartDateLocal);
|
||||||
|
html += ' - ';
|
||||||
|
html += LiveTvHelpers.getDisplayTime(program.EndDateLocal);
|
||||||
|
|
||||||
|
if (program.SeriesTimerId) {
|
||||||
|
html += '<div class="timerCircle seriesTimerCircle"></div>';
|
||||||
|
html += '<div class="timerCircle seriesTimerCircle"></div>';
|
||||||
|
html += '<div class="timerCircle seriesTimerCircle"></div>';
|
||||||
|
}
|
||||||
|
else if (program.TimerId) {
|
||||||
|
|
||||||
|
html += '<div class="timerCircle"></div>';
|
||||||
|
}
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '</a>';
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
|
@ -313,7 +252,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.programGrid', page).html(html.join('')).scrollTop(0).scrollLeft(0)
|
$('.programGrid', page).html(html.join('')).scrollTop(0).scrollLeft(0)
|
||||||
.createGuideHoverMenu('.timeslotCellInnerWithProgram');
|
.createGuideHoverMenu('.programCellInner');
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderChannelHeaders(page, channels) {
|
function renderChannelHeaders(page, channels) {
|
||||||
|
@ -355,7 +294,10 @@
|
||||||
function renderGuide(page, date, channels, programs) {
|
function renderGuide(page, date, channels, programs) {
|
||||||
|
|
||||||
renderChannelHeaders(page, channels);
|
renderChannelHeaders(page, channels);
|
||||||
$('.timeslotHeaders', page).html(getTimeslotHeadersHtml(date));
|
|
||||||
|
var startDate = date;
|
||||||
|
var endDate = new Date(startDate.getTime() + msPerDay);
|
||||||
|
$('.timeslotHeaders', page).html(getTimeslotHeadersHtml(startDate, endDate));
|
||||||
renderPrograms(page, date, channels, programs);
|
renderPrograms(page, date, channels, programs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,13 +328,6 @@
|
||||||
|
|
||||||
currentDate = normalizeDateToTimeslot(date);
|
currentDate = normalizeDateToTimeslot(date);
|
||||||
|
|
||||||
gridLocalStartDateMs = currentDate.getTime();
|
|
||||||
|
|
||||||
var clone = new Date(gridLocalStartDateMs);
|
|
||||||
clone.setHours(0, 0, 0, 0);
|
|
||||||
clone.setDate(clone.getDate() + 1);
|
|
||||||
gridLocalEndDateMs = clone.getTime() - 1;
|
|
||||||
|
|
||||||
reloadGuide(page);
|
reloadGuide(page);
|
||||||
|
|
||||||
var text = LibraryBrowser.getFutureDateText(date);
|
var text = LibraryBrowser.getFutureDateText(date);
|
||||||
|
@ -458,8 +393,12 @@
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
date.setTime(parseInt(this.value));
|
date.setTime(parseInt(this.value));
|
||||||
|
|
||||||
changeDate(page, date);
|
|
||||||
$('#popupConfig', page).popup('close');
|
$('#popupConfig', page).popup('close');
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
|
||||||
|
changeDate(page, date);
|
||||||
|
}, 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($.browser.mobile) {
|
if ($.browser.mobile) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue