mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
unify guide between web and theater
This commit is contained in:
parent
e8c224ae0b
commit
af4e8c5529
23 changed files with 1314 additions and 1024 deletions
|
@ -1,519 +0,0 @@
|
|||
define(['jQuery', 'livetvcss', 'scripts/livetvcomponents', 'scrollStyles'], function ($) {
|
||||
|
||||
return function (options) {
|
||||
|
||||
var self = this;
|
||||
|
||||
self.refresh = function () {
|
||||
reloadPage(options.element);
|
||||
};
|
||||
|
||||
// 30 mins
|
||||
var cellCurationMinutes = 30;
|
||||
var cellDurationMs = cellCurationMinutes * 60 * 1000;
|
||||
var msPerDay = 86400000;
|
||||
|
||||
var currentDate;
|
||||
|
||||
var defaultChannels = browserInfo.mobile ? 50 : 100;
|
||||
var channelLimit = 1000;
|
||||
|
||||
var channelQuery = {
|
||||
|
||||
StartIndex: 0,
|
||||
Limit: defaultChannels,
|
||||
EnableFavoriteSorting: true
|
||||
};
|
||||
|
||||
var channelsPromise;
|
||||
|
||||
function normalizeDateToTimeslot(date) {
|
||||
|
||||
var minutesOffset = date.getMinutes() - cellCurationMinutes;
|
||||
|
||||
if (minutesOffset >= 0) {
|
||||
|
||||
date.setHours(date.getHours(), cellCurationMinutes, 0, 0);
|
||||
|
||||
} else {
|
||||
|
||||
date.setHours(date.getHours(), 0, 0, 0);
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
function reloadChannels(page) {
|
||||
channelsPromise = null;
|
||||
reloadGuide(page);
|
||||
}
|
||||
|
||||
function reloadGuide(page) {
|
||||
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
channelQuery.UserId = Dashboard.getCurrentUserId();
|
||||
|
||||
channelQuery.Limit = Math.min(channelQuery.Limit || defaultChannels, channelLimit);
|
||||
channelQuery.AddCurrentProgram = false;
|
||||
|
||||
channelsPromise = channelsPromise || ApiClient.getLiveTvChannels(channelQuery);
|
||||
|
||||
var date = currentDate;
|
||||
// Add one second to avoid getting programs that are just ending
|
||||
date = new Date(date.getTime() + 1000);
|
||||
|
||||
// Subtract to avoid getting programs that are starting when the grid ends
|
||||
var nextDay = new Date(date.getTime() + msPerDay - 2000);
|
||||
|
||||
console.log(nextDay);
|
||||
channelsPromise.then(function (channelsResult) {
|
||||
|
||||
ApiClient.getLiveTvPrograms({
|
||||
UserId: Dashboard.getCurrentUserId(),
|
||||
MaxStartDate: nextDay.toISOString(),
|
||||
MinEndDate: date.toISOString(),
|
||||
channelIds: channelsResult.Items.map(function (c) {
|
||||
return c.Id;
|
||||
}).join(','),
|
||||
ImageTypeLimit: 1,
|
||||
EnableImages: false,
|
||||
SortBy: "StartDate"
|
||||
|
||||
}).then(function (programsResult) {
|
||||
|
||||
renderGuide(page, date, channelsResult.Items, programsResult.Items);
|
||||
|
||||
Dashboard.hideLoadingMsg();
|
||||
|
||||
LibraryBrowser.setLastRefreshed(page);
|
||||
|
||||
});
|
||||
|
||||
if (options.enablePaging !== false) {
|
||||
var channelPagingHtml = LibraryBrowser.getQueryPagingHtml({
|
||||
startIndex: channelQuery.StartIndex,
|
||||
limit: channelQuery.Limit,
|
||||
totalRecordCount: channelsResult.TotalRecordCount,
|
||||
updatePageSizeSetting: false,
|
||||
showLimit: true
|
||||
});
|
||||
|
||||
var channelPaging = page.querySelector('.channelPaging');
|
||||
channelPaging.innerHTML = channelPagingHtml;
|
||||
$(channelPaging);
|
||||
}
|
||||
|
||||
page.querySelector('.btnNextPage').addEventListener('click', function () {
|
||||
channelQuery.StartIndex += channelQuery.Limit;
|
||||
reloadChannels(page);
|
||||
});
|
||||
|
||||
page.querySelector('.btnPreviousPage').addEventListener('click', function () {
|
||||
channelQuery.StartIndex -= channelQuery.Limit;
|
||||
reloadChannels(page);
|
||||
});
|
||||
|
||||
page.querySelector('#selectPageSize').addEventListener('change', function () {
|
||||
channelQuery.Limit = parseInt(this.value);
|
||||
channelQuery.StartIndex = 0;
|
||||
reloadChannels(page);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getTimeslotHeadersHtml(startDate, endDateTime) {
|
||||
|
||||
var html = '';
|
||||
|
||||
// clone
|
||||
startDate = new Date(startDate.getTime());
|
||||
|
||||
html += '<div class="timeslotHeadersInner">';
|
||||
|
||||
while (startDate.getTime() < endDateTime) {
|
||||
|
||||
html += '<div class="timeslotHeader">';
|
||||
html += '<div class="timeslotHeaderInner">';
|
||||
|
||||
html += LibraryBrowser.getDisplayTime(startDate);
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
|
||||
// Add 30 mins
|
||||
startDate.setTime(startDate.getTime() + cellDurationMs);
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function parseDates(program) {
|
||||
|
||||
if (!program.StartDateLocal) {
|
||||
try {
|
||||
|
||||
program.StartDateLocal = parseISO8601Date(program.StartDate, { toLocal: true });
|
||||
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!program.EndDateLocal) {
|
||||
try {
|
||||
|
||||
program.EndDateLocal = parseISO8601Date(program.EndDate, { toLocal: true });
|
||||
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getChannelProgramsHtml(page, date, channel, programs) {
|
||||
|
||||
var html = '';
|
||||
|
||||
var startMs = date.getTime();
|
||||
var endMs = startMs + msPerDay - 1;
|
||||
|
||||
programs = programs.filter(function (curr) {
|
||||
return curr.ChannelId == channel.Id;
|
||||
});
|
||||
|
||||
html += '<div class="channelPrograms">';
|
||||
|
||||
for (var i = 0, length = programs.length; i < length; i++) {
|
||||
|
||||
var program = programs[i];
|
||||
|
||||
if (program.ChannelId != channel.Id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parseDates(program);
|
||||
|
||||
if (program.EndDateLocal.getTime() < startMs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (program.StartDateLocal.getTime() > endMs) {
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
var cssClass = "programCell";
|
||||
var addAccent = true;
|
||||
|
||||
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";
|
||||
addAccent = false;
|
||||
}
|
||||
|
||||
html += '<a href="itemdetails.html?id=' + program.Id + '" class="' + cssClass + '" data-programid="' + program.Id + '" style="left:' + startPercent + '%;width:' + endPercent + '%;">';
|
||||
|
||||
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 += LibraryBrowser.getDisplayTime(program.StartDateLocal);
|
||||
html += ' - ';
|
||||
html += LibraryBrowser.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>';
|
||||
|
||||
if (addAccent) {
|
||||
html += '<div class="programAccent"></div>';
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderPrograms(page, date, channels, programs) {
|
||||
|
||||
var html = [];
|
||||
|
||||
for (var i = 0, length = channels.length; i < length; i++) {
|
||||
|
||||
html.push(getChannelProgramsHtml(page, date, channels[i], programs));
|
||||
}
|
||||
|
||||
var programGrid = page.querySelector('.programGrid');
|
||||
programGrid.innerHTML = html.join('');
|
||||
|
||||
$(programGrid).scrollTop(0).scrollLeft(0);
|
||||
}
|
||||
|
||||
function renderChannelHeaders(page, channels) {
|
||||
|
||||
var html = '';
|
||||
|
||||
for (var i = 0, length = channels.length; i < length; i++) {
|
||||
|
||||
var channel = channels[i];
|
||||
|
||||
html += '<div class="channelHeaderCellContainer">';
|
||||
|
||||
html += '<a class="channelHeaderCell" href="itemdetails.html?id=' + channel.Id + '">';
|
||||
|
||||
var hasChannelImage = channel.ImageTags.Primary;
|
||||
var cssClass = hasChannelImage ? 'guideChannelInfo guideChannelInfoWithImage' : 'guideChannelInfo';
|
||||
|
||||
html += '<div class="' + cssClass + '">' + channel.Number + '</div>';
|
||||
|
||||
if (hasChannelImage) {
|
||||
|
||||
var url = ApiClient.getScaledImageUrl(channel.Id, {
|
||||
maxHeight: 44,
|
||||
maxWidth: 70,
|
||||
tag: channel.ImageTags.Primary,
|
||||
type: "Primary"
|
||||
});
|
||||
|
||||
html += '<div class="guideChannelImage lazy" data-src="' + url + '"></div>';
|
||||
} else {
|
||||
html += '<div class="guideChannelName">' + channel.Name + '</div>';
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
var channelList = page.querySelector('.channelList');
|
||||
channelList.innerHTML = html;
|
||||
ImageLoader.lazyChildren(channelList);
|
||||
}
|
||||
|
||||
function renderGuide(page, date, channels, programs) {
|
||||
|
||||
renderChannelHeaders(page, channels);
|
||||
|
||||
var startDate = date;
|
||||
var endDate = new Date(startDate.getTime() + msPerDay);
|
||||
page.querySelector('.timeslotHeaders').innerHTML = getTimeslotHeadersHtml(startDate, endDate);
|
||||
renderPrograms(page, date, channels, programs);
|
||||
}
|
||||
|
||||
var gridScrolling = false;
|
||||
var headersScrolling = false;
|
||||
function onProgramGridScroll(page, elem) {
|
||||
|
||||
if (!headersScrolling) {
|
||||
gridScrolling = true;
|
||||
|
||||
$(page.querySelector('.timeslotHeaders')).scrollLeft($(elem).scrollLeft());
|
||||
gridScrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onTimeslotHeadersScroll(page, elem) {
|
||||
|
||||
if (!gridScrolling) {
|
||||
headersScrolling = true;
|
||||
$(page.querySelector('.programGrid')).scrollLeft($(elem).scrollLeft());
|
||||
headersScrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
function changeDate(page, date) {
|
||||
|
||||
currentDate = normalizeDateToTimeslot(date);
|
||||
|
||||
reloadGuide(page);
|
||||
|
||||
var text = LibraryBrowser.getFutureDateText(date);
|
||||
text = '<span class="currentDay">' + text.replace(' ', ' </span>');
|
||||
page.querySelector('.currentDate').innerHTML = text;
|
||||
}
|
||||
|
||||
var dateOptions = [];
|
||||
|
||||
function setDateRange(page, guideInfo) {
|
||||
|
||||
var today = new Date();
|
||||
today.setHours(today.getHours(), 0, 0, 0);
|
||||
|
||||
var start = parseISO8601Date(guideInfo.StartDate, { toLocal: true });
|
||||
var end = parseISO8601Date(guideInfo.EndDate, { toLocal: true });
|
||||
|
||||
start.setHours(0, 0, 0, 0);
|
||||
end.setHours(0, 0, 0, 0);
|
||||
|
||||
if (start.getTime() >= end.getTime()) {
|
||||
end.setDate(start.getDate() + 1);
|
||||
}
|
||||
|
||||
start = new Date(Math.max(today, start));
|
||||
|
||||
dateOptions = [];
|
||||
|
||||
while (start <= end) {
|
||||
|
||||
dateOptions.push({
|
||||
name: LibraryBrowser.getFutureDateText(start),
|
||||
id: start.getTime(),
|
||||
ironIcon: 'today'
|
||||
});
|
||||
|
||||
start.setDate(start.getDate() + 1);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
var date = new Date();
|
||||
|
||||
if (currentDate) {
|
||||
date.setTime(currentDate.getTime());
|
||||
}
|
||||
|
||||
changeDate(page, date);
|
||||
}
|
||||
|
||||
function reloadPageAfterValidation(page, limit) {
|
||||
|
||||
channelLimit = limit;
|
||||
|
||||
ApiClient.getLiveTvGuideInfo().then(function (guideInfo) {
|
||||
|
||||
setDateRange(page, guideInfo);
|
||||
});
|
||||
}
|
||||
|
||||
function reloadPage(page) {
|
||||
|
||||
$('.guideRequiresUnlock', page).hide();
|
||||
|
||||
RegistrationServices.validateFeature('livetv').then(function () {
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
reloadPageAfterValidation(page, 1000);
|
||||
}, function () {
|
||||
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
var limit = 5;
|
||||
$('.guideRequiresUnlock', page).show();
|
||||
$('.unlockText', page).html(Globalize.translate('MessageLiveTvGuideRequiresUnlock', limit));
|
||||
|
||||
reloadPageAfterValidation(page, limit);
|
||||
});
|
||||
}
|
||||
|
||||
function selectDate(page) {
|
||||
|
||||
require(['actionsheet'], function (actionsheet) {
|
||||
|
||||
actionsheet.show({
|
||||
items: dateOptions,
|
||||
showCancel: true,
|
||||
title: Globalize.translate('HeaderSelectDate'),
|
||||
callback: function (id) {
|
||||
|
||||
var date = new Date();
|
||||
date.setTime(parseInt(id));
|
||||
changeDate(page, date);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'components/tvguide/tvguide.template.html', true);
|
||||
|
||||
xhr.onload = function (e) {
|
||||
|
||||
var template = this.response;
|
||||
var tabContent = options.element;
|
||||
tabContent.innerHTML = Globalize.translateDocument(template);
|
||||
|
||||
tabContent.querySelector('.programGrid').addEventListener('scroll', function (e) {
|
||||
|
||||
onProgramGridScroll(tabContent, e.target);
|
||||
});
|
||||
|
||||
if (browserInfo.mobile) {
|
||||
tabContent.querySelector('.tvGuide').classList.add('mobileGuide');
|
||||
} else {
|
||||
|
||||
tabContent.querySelector('.tvGuide').classList.remove('mobileGuide');
|
||||
|
||||
tabContent.querySelector('.timeslotHeaders').addEventListener('scroll', function (e) {
|
||||
|
||||
onTimeslotHeadersScroll(tabContent, e.target);
|
||||
});
|
||||
}
|
||||
|
||||
if (AppInfo.enableHeadRoom && options.enableHeadRoom) {
|
||||
requirejs(["headroom"], function () {
|
||||
|
||||
// construct an instance of Headroom, passing the element
|
||||
var headroom = new Headroom(tabContent.querySelector('.tvGuideHeader'));
|
||||
// initialise
|
||||
headroom.init();
|
||||
});
|
||||
}
|
||||
|
||||
$('.btnUnlockGuide', tabContent).on('click', function () {
|
||||
|
||||
reloadPage(tabContent);
|
||||
});
|
||||
|
||||
$('.btnSelectDate', tabContent).on('click', function () {
|
||||
|
||||
selectDate(tabContent);
|
||||
});
|
||||
|
||||
self.refresh();
|
||||
}
|
||||
|
||||
xhr.send();
|
||||
};
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
<div class="tvGuide">
|
||||
<div class="tvGuideHeader">
|
||||
<div class="channelTimeslotHeader">
|
||||
<div class="timeslotHeaderInner">
|
||||
<a class="btnSelectDate" href="#" style="display:block;padding-left:5px;color:#fff;text-decoration:none;font-weight:normal!important;padding:5px 10px 4px 0;font-size:13px;"><iron-icon icon="schedule" style="margin-right:3px;width:18px;height:18px;"></iron-icon><span class="currentDate" style="vertical-align: middle;"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeslotHeaders smoothScrollX"></div>
|
||||
</div>
|
||||
<div class="programContainer">
|
||||
<div class="channelList"></div>
|
||||
<div class="programGrid smoothScrollX"></div>
|
||||
</div>
|
||||
<div class="channelPaging"></div>
|
||||
</div>
|
||||
<div class="guideRequiresUnlock readOnlyContent" style="margin:1em auto;text-align:center;display:none;padding:1em;">
|
||||
<p class="unlockText"></p>
|
||||
<paper-button raised class="secondary block btnUnlockGuide"><iron-icon icon="check"></iron-icon><span>${ButtonUnlockGuide}</span></paper-button>
|
||||
</div>
|
Loading…
Add table
Add a link
Reference in a new issue