1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

move emby-webcomponents to components and reflect paths

This commit is contained in:
vitorsemeano 2019-02-23 18:05:42 +00:00
parent e91cbf8438
commit 6ddc62857d
275 changed files with 20 additions and 20 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View file

@ -0,0 +1,116 @@
define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom', 'recordingHelper', 'events', 'registrationServices', 'paper-icon-button-light', 'emby-button', 'css!./recordingfields'], function (globalize, connectionManager, require, loading, appHost, dom, recordingHelper, events, registrationServices) {
'use strict';
function onRecordingButtonClick(e) {
var item = this.item;
if (item) {
var serverId = item.ServerId;
var programId = item.Id;
var timerId = item.TimerId;
var timerStatus = item.Status;
var seriesTimerId = item.SeriesTimerId;
var instance = this;
recordingHelper.toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId).then(function () {
instance.refresh(serverId, programId);
});
}
}
function RecordingButton(options) {
this.options = options;
if (options.item) {
this.refreshItem(options.item);
} else if (options.itemId && options.serverId) {
this.refresh(options.itemId, options.serverId);
}
var button = options.button;
button.querySelector('i').innerHTML = '';
var clickFn = onRecordingButtonClick.bind(this);
this.clickFn = clickFn;
dom.addEventListener(button, 'click', clickFn, {
passive: true
});
}
function getIndicatorIcon(item) {
var status;
if (item.Type === 'SeriesTimer') {
return '';
}
else if (item.TimerId || item.SeriesTimerId) {
status = item.Status || 'Cancelled';
}
else if (item.Type === 'Timer') {
status = item.Status;
}
else {
return '';
}
if (item.SeriesTimerId) {
if (status !== 'Cancelled') {
return '';
}
}
return '';
}
RecordingButton.prototype.refresh = function (serverId, itemId) {
var apiClient = connectionManager.getApiClient(serverId);
var self = this;
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
self.refreshItem(item);
});
};
RecordingButton.prototype.refreshItem = function (item) {
var options = this.options;
var button = options.button;
this.item = item;
button.querySelector('i').innerHTML = getIndicatorIcon(item);
if (item.TimerId && (item.Status || 'Cancelled') !== 'Cancelled') {
button.classList.add('recordingIcon-active');
} else {
button.classList.remove('recordingIcon-active');
}
};
RecordingButton.prototype.destroy = function () {
var options = this.options;
if (options) {
var button = options.button;
var clickFn = this.clickFn;
if (clickFn) {
dom.removeEventListener(button, 'click', clickFn, {
passive: true
});
}
}
this.options = null;
this.item = null;
};
return RecordingButton;
});

View file

@ -0,0 +1,27 @@
.recordingDialog-imageContainer {
flex-shrink: 0;
padding: 1em 1em 1em 0;
width: 25%;
}
.recordingDialog-img {
width: 100%;
}
.recordingDialog-itemName {
margin-top: .7em;
}
.recordingDetailsContainer {
display: flex;
}
.recordingDetails {
flex-grow: 1;
}
.recordingDetailText {
display: flex;
align-items: center;
flex-wrap: wrap;
}

View file

@ -0,0 +1,206 @@
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'datetime', 'imageLoader', 'recordingFields', 'events', 'emby-checkbox', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, datetime, imageLoader, recordingFields, events) {
'use strict';
var currentDialog;
var closeAction;
var currentRecordingFields;
function closeDialog() {
dialogHelper.close(currentDialog);
}
function init(context) {
context.querySelector('.btnPlay').addEventListener('click', function () {
closeAction = 'play';
closeDialog();
});
context.querySelector('.btnCancel').addEventListener('click', function () {
closeAction = null;
closeDialog();
});
}
function getImageUrl(item, apiClient, imageHeight) {
var imageTags = item.ImageTags || {};
if (item.PrimaryImageTag) {
imageTags.Primary = item.PrimaryImageTag;
}
if (imageTags.Primary) {
return apiClient.getScaledImageUrl(item.Id, {
type: "Primary",
maxHeight: imageHeight,
tag: item.ImageTags.Primary
});
}
else if (imageTags.Thumb) {
return apiClient.getScaledImageUrl(item.Id, {
type: "Thumb",
maxHeight: imageHeight,
tag: item.ImageTags.Thumb
});
}
return null;
}
function renderRecording(context, defaultTimer, program, apiClient, refreshRecordingStateOnly) {
if (!refreshRecordingStateOnly) {
var imgUrl = getImageUrl(program, apiClient, 200);
var imageContainer = context.querySelector('.recordingDialog-imageContainer');
if (imgUrl) {
imageContainer.innerHTML = '<img src="' + require.toUrl('.').split('?')[0] + '/empty.png" data-src="' + imgUrl + '" class="recordingDialog-img lazy" />';
imageContainer.classList.remove('hide');
imageLoader.lazyChildren(imageContainer);
} else {
imageContainer.innerHTML = '';
imageContainer.classList.add('hide');
}
context.querySelector('.recordingDialog-itemName').innerHTML = program.Name;
context.querySelector('.formDialogHeaderTitle').innerHTML = program.Name;
context.querySelector('.itemGenres').innerHTML = (program.Genres || []).join(' / ');
context.querySelector('.itemOverview').innerHTML = program.Overview || '';
var formDialogFooter = context.querySelector('.formDialogFooter');
var now = new Date();
if (now >= datetime.parseISO8601Date(program.StartDate, true) && now < datetime.parseISO8601Date(program.EndDate, true)) {
formDialogFooter.classList.remove('hide');
} else {
formDialogFooter.classList.add('hide');
}
context.querySelector('.itemMiscInfoPrimary').innerHTML = mediaInfo.getPrimaryMediaInfoHtml(program);
}
context.querySelector('.itemMiscInfoSecondary').innerHTML = mediaInfo.getSecondaryMediaInfoHtml(program, {
});
loading.hide();
}
function reload(context, programId, serverId, refreshRecordingStateOnly) {
loading.show();
var apiClient = connectionManager.getApiClient(serverId);
var promise1 = apiClient.getNewLiveTvTimerDefaults({ programId: programId });
var promise2 = apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId());
Promise.all([promise1, promise2]).then(function (responses) {
var defaults = responses[0];
var program = responses[1];
renderRecording(context, defaults, program, apiClient, refreshRecordingStateOnly);
});
}
function executeCloseAction(action, programId, serverId) {
if (action === 'play') {
require(['playbackManager'], function (playbackManager) {
var apiClient = connectionManager.getApiClient(serverId);
apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()).then(function (item) {
playbackManager.play({
ids: [item.ChannelId],
serverId: serverId
});
});
});
return;
}
}
function showEditor(itemId, serverId) {
return new Promise(function (resolve, reject) {
closeAction = null;
loading.show();
require(['text!./recordingcreator.template.html'], function (template) {
var dialogOptions = {
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
dlg.classList.add('recordingDialog');
var html = '';
html += globalize.translateDocument(template, 'core');
dlg.innerHTML = html;
currentDialog = dlg;
function onRecordingChanged() {
reload(dlg, itemId, serverId, true);
}
dlg.addEventListener('close', function () {
events.off(currentRecordingFields, 'recordingchanged', onRecordingChanged);
executeCloseAction(closeAction, itemId, serverId);
if (currentRecordingFields && currentRecordingFields.hasChanged()) {
resolve();
} else {
reject();
}
});
if (layoutManager.tv) {
scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false);
}
init(dlg);
reload(dlg, itemId, serverId);
currentRecordingFields = new recordingFields({
parent: dlg.querySelector('.recordingFields'),
programId: itemId,
serverId: serverId
});
events.on(currentRecordingFields, 'recordingchanged', onRecordingChanged);
dialogHelper.open(dlg);
});
});
}
return {
show: showEditor
};
});

View file

@ -0,0 +1,31 @@
<div class="formDialogHeader">
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>
<h3 class="formDialogHeaderTitle"></h3>
</div>
<div class="formDialogContent smoothScrollY">
<form class="dialogContentInner dialog-content-centered">
<div class="recordingDetailsContainer">
<div class="recordingDialog-imageContainer">
</div>
<div class="recordingDetails">
<h1 class="programDialog-itemName recordingDialog-itemName dialogContentTitle"></h1>
<p class="itemMiscInfoPrimary recordingDetailText"></p>
<p class="itemMiscInfoSecondary recordingDetailText secondaryText"></p>
<p class="itemGenres secondaryText"></p>
<div style="margin:.5em 0 1em;" class="recordingFields">
</div>
</div>
</div>
<p class="itemOverview"></p>
<br />
<div class="formDialogFooter hide">
<button is="emby-button" type="button" class="raised btnPlay block formDialogFooterItem button-submit">
<span>${Play}</span>
</button>
</div>
</form>
</div>

View file

@ -0,0 +1,164 @@
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'imageLoader', 'scrollStyles', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons', 'flexStyles'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, imageLoader) {
'use strict';
var currentDialog;
var recordingDeleted = false;
var currentItemId;
var currentServerId;
var currentResolve;
function deleteTimer(apiClient, timerId) {
return new Promise(function (resolve, reject) {
require(['recordingHelper'], function (recordingHelper) {
recordingHelper.cancelTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject);
});
});
}
function renderTimer(context, item, apiClient) {
var program = item.ProgramInfo || {};
context.querySelector('#txtPrePaddingMinutes').value = item.PrePaddingSeconds / 60;
context.querySelector('#txtPostPaddingMinutes').value = item.PostPaddingSeconds / 60;
loading.hide();
}
function closeDialog(isDeleted) {
recordingDeleted = isDeleted;
dialogHelper.close(currentDialog);
}
function onSubmit(e) {
var form = this;
var apiClient = connectionManager.getApiClient(currentServerId);
apiClient.getLiveTvTimer(currentItemId).then(function (item) {
item.PrePaddingSeconds = form.querySelector('#txtPrePaddingMinutes').value * 60;
item.PostPaddingSeconds = form.querySelector('#txtPostPaddingMinutes').value * 60;
apiClient.updateLiveTvTimer(item).then(currentResolve);
});
e.preventDefault();
// Disable default form submission
return false;
}
function init(context) {
context.querySelector('.btnCancel').addEventListener('click', function () {
closeDialog(false);
});
context.querySelector('.btnCancelRecording').addEventListener('click', function () {
var apiClient = connectionManager.getApiClient(currentServerId);
deleteTimer(apiClient, currentItemId).then(function () {
closeDialog(true);
});
});
context.querySelector('form').addEventListener('submit', onSubmit);
}
function reload(context, id) {
loading.show();
currentItemId = id;
var apiClient = connectionManager.getApiClient(currentServerId);
apiClient.getLiveTvTimer(id).then(function (result) {
renderTimer(context, result, apiClient);
loading.hide();
});
}
function showEditor(itemId, serverId, options) {
return new Promise(function (resolve, reject) {
recordingDeleted = false;
currentServerId = serverId;
loading.show();
options = options || {};
currentResolve = resolve;
require(['text!./recordingeditor.template.html'], function (template) {
var dialogOptions = {
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
}
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
dlg.classList.add('recordingDialog');
if (!layoutManager.tv) {
dlg.style['min-width'] = '20%';
dlg.classList.add('dialog-fullscreen-lowres');
}
var html = '';
html += globalize.translateDocument(template, 'core');
dlg.innerHTML = html;
if (options.enableCancel === false) {
dlg.querySelector('.formDialogFooter').classList.add('hide');
}
currentDialog = dlg;
dlg.addEventListener('closing', function () {
if (!recordingDeleted) {
dlg.querySelector('.btnSubmit').click();
}
});
dlg.addEventListener('close', function () {
if (recordingDeleted) {
resolve({
updated: true,
deleted: true
});
}
});
if (layoutManager.tv) {
scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false);
}
init(dlg);
reload(dlg, itemId);
dialogHelper.open(dlg);
});
});
}
return {
show: showEditor
};
});

View file

@ -0,0 +1,46 @@
<div class="formDialogHeader">
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>
<h3 class="formDialogHeaderTitle">
${HeaderRecordingOptions}
</h3>
</div>
<div class="formDialogContent smoothScrollY">
<div class="dialogContentInner dialog-content-centered">
<form>
<br />
<div class="inputContainer">
<div class="flex align-items-center">
<div class="flex-grow">
<input is="emby-input" type="number" id="txtPrePaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelStartWhenPossible}" />
</div>
<div class="fieldDescription" style="margin-left:.5em;font-size:90%;margin-top:1.3em;">
${MinutesBefore}
</div>
</div>
</div>
<div class="inputContainer">
<div class="flex align-items-center">
<div class="flex-grow">
<input is="emby-input" type="number" id="txtPostPaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelStopWhenPossible}" />
</div>
<div class="fieldDescription" style="margin-left:.5em;font-size:90%;margin-top:1.3em;">
${MinutesAfter}
</div>
</div>
</div>
<br />
<div class="formDialogFooter">
<button is="emby-button" type="submit" class="raised btnSubmit block formDialogFooterItem button-submit hide">
<span>${Save}</span>
</button>
<button is="emby-button" type="button" class="raised btnCancelRecording block formDialogFooterItem button-cancel" style="white-space:nowrap;">
<span>${HeaderCancelRecording}</span>
</button>
</div>
</form>
</div>
</div>

View file

@ -0,0 +1,12 @@
.recordingButton {
margin-left: 0;
min-width: 10em;
}
.recordingIcon-active {
color: #cc3333;
}
.recordSeriesContainer {
margin-bottom: .8em;
}

View file

@ -0,0 +1,362 @@
define(['globalize', 'connectionManager', 'serverNotifications', 'require', 'loading', 'apphost', 'dom', 'recordingHelper', 'events', 'registrationServices', 'paper-icon-button-light', 'emby-button', 'css!./recordingfields', 'flexStyles'], function (globalize, connectionManager, serverNotifications, require, loading, appHost, dom, recordingHelper, events, registrationServices) {
'use strict';
function getRegistration(apiClient, feature) {
return registrationServices.validateFeature(feature, {
showDialog: false,
viewOnly: true
});
}
function showConvertRecordingsUnlockMessage(context, apiClient) {
getRegistration(apiClient, getDvrFeatureCode()).then(function () {
context.querySelector('.convertRecordingsContainer').classList.add('hide');
}, function () {
context.querySelector('.convertRecordingsContainer').classList.remove('hide');
});
}
function showSeriesRecordingFields(context, programId, apiClient) {
getRegistration(apiClient, getDvrFeatureCode()).then(function () {
context.querySelector('.supporterContainer').classList.add('hide');
context.querySelector('.convertRecordingsContainer').classList.add('hide');
context.querySelector('.recordSeriesContainer').classList.remove('hide');
}, function () {
context.querySelector('.supporterContainerText').innerHTML = globalize.translate('MessageActiveSubscriptionRequiredSeriesRecordings');
context.querySelector('.supporterContainer').classList.remove('hide');
context.querySelector('.recordSeriesContainer').classList.add('hide');
context.querySelector('.convertRecordingsContainer').classList.add('hide');
});
}
function getDvrFeatureCode() {
return 'dvr';
}
function showSingleRecordingFields(context, programId, apiClient) {
getRegistration(apiClient, getDvrFeatureCode()).then(function () {
context.querySelector('.supporterContainer').classList.add('hide');
showConvertRecordingsUnlockMessage(context, apiClient);
}, function () {
context.querySelector('.supporterContainerText').innerHTML = globalize.translate('DvrSubscriptionRequired');
context.querySelector('.supporterContainer').classList.remove('hide');
context.querySelector('.convertRecordingsContainer').classList.add('hide');
});
}
function showRecordingFieldsContainer(context, programId, apiClient) {
getRegistration(apiClient, getDvrFeatureCode()).then(function () {
context.querySelector('.recordingFields').classList.remove('hide');
}, function () {
context.querySelector('.recordingFields').classList.add('hide');
});
}
function loadData(parent, program, apiClient) {
if (program.IsSeries) {
parent.querySelector('.recordSeriesContainer').classList.remove('hide');
showSeriesRecordingFields(parent, program.Id, apiClient);
} else {
parent.querySelector('.recordSeriesContainer').classList.add('hide');
showSingleRecordingFields(parent, program.Id, apiClient);
}
if (program.SeriesTimerId) {
parent.querySelector('.btnManageSeriesRecording').classList.remove('hide');
parent.querySelector('.seriesRecordingButton .recordingIcon').classList.add('recordingIcon-active');
parent.querySelector('.seriesRecordingButton .buttonText').innerHTML = globalize.translate('CancelSeries');
} else {
parent.querySelector('.btnManageSeriesRecording').classList.add('hide');
parent.querySelector('.seriesRecordingButton .recordingIcon').classList.remove('recordingIcon-active');
parent.querySelector('.seriesRecordingButton .buttonText').innerHTML = globalize.translate('RecordSeries');
}
if (program.TimerId && program.Status !== 'Cancelled') {
parent.querySelector('.btnManageRecording').classList.remove('hide');
parent.querySelector('.singleRecordingButton .recordingIcon').classList.add('recordingIcon-active');
if (program.Status === 'InProgress') {
parent.querySelector('.singleRecordingButton .buttonText').innerHTML = globalize.translate('StopRecording');
} else {
parent.querySelector('.singleRecordingButton .buttonText').innerHTML = globalize.translate('DoNotRecord');
}
} else {
parent.querySelector('.btnManageRecording').classList.add('hide');
parent.querySelector('.singleRecordingButton .recordingIcon').classList.remove('recordingIcon-active');
parent.querySelector('.singleRecordingButton .buttonText').innerHTML = globalize.translate('Record');
}
}
function fetchData(instance) {
var options = instance.options;
var apiClient = connectionManager.getApiClient(options.serverId);
showRecordingFieldsContainer(options.parent, options.programId, apiClient);
return apiClient.getLiveTvProgram(options.programId, apiClient.getCurrentUserId()).then(function (program) {
instance.TimerId = program.TimerId;
instance.Status = program.Status;
instance.SeriesTimerId = program.SeriesTimerId;
loadData(options.parent, program, apiClient);
});
}
function onTimerChangedExternally(e, apiClient, data) {
var options = this.options;
var refresh = false;
if (data.Id) {
if (this.TimerId === data.Id) {
refresh = true;
}
}
if (data.ProgramId && options) {
if (options.programId === data.ProgramId) {
refresh = true;
}
}
if (refresh) {
this.refresh();
}
}
function onSeriesTimerChangedExternally(e, apiClient, data) {
var options = this.options;
var refresh = false;
if (data.Id) {
if (this.SeriesTimerId === data.Id) {
refresh = true;
}
}
if (data.ProgramId && options) {
if (options.programId === data.ProgramId) {
refresh = true;
}
}
if (refresh) {
this.refresh();
}
}
function RecordingEditor(options) {
this.options = options;
this.embed();
var timerChangedHandler = onTimerChangedExternally.bind(this);
this.timerChangedHandler = timerChangedHandler;
events.on(serverNotifications, 'TimerCreated', timerChangedHandler);
events.on(serverNotifications, 'TimerCancelled', timerChangedHandler);
var seriesTimerChangedHandler = onSeriesTimerChangedExternally.bind(this);
this.seriesTimerChangedHandler = seriesTimerChangedHandler;
events.on(serverNotifications, 'SeriesTimerCreated', seriesTimerChangedHandler);
events.on(serverNotifications, 'SeriesTimerCancelled', seriesTimerChangedHandler);
}
function onSupporterButtonClick() {
registrationServices.showPremiereInfo();
}
function onManageRecordingClick(e) {
var options = this.options;
if (!this.TimerId || this.Status === 'Cancelled') {
return;
}
var self = this;
require(['recordingEditor'], function (recordingEditor) {
recordingEditor.show(self.TimerId, options.serverId, {
enableCancel: false
}).then(function () {
self.changed = true;
});
});
}
function onManageSeriesRecordingClick(e) {
var options = this.options;
if (!this.SeriesTimerId) {
return;
}
var self = this;
require(['seriesRecordingEditor'], function (seriesRecordingEditor) {
seriesRecordingEditor.show(self.SeriesTimerId, options.serverId, {
enableCancel: false
}).then(function () {
self.changed = true;
});
});
}
function onRecordChange(e) {
this.changed = true;
var self = this;
var options = this.options;
var apiClient = connectionManager.getApiClient(options.serverId);
var button = dom.parentWithTag(e.target, 'BUTTON');
var isChecked = !button.querySelector('i').classList.contains('recordingIcon-active');
var hasEnabledTimer = this.TimerId && this.Status !== 'Cancelled';
if (isChecked) {
if (!hasEnabledTimer) {
loading.show();
recordingHelper.createRecording(apiClient, options.programId, false).then(function () {
events.trigger(self, 'recordingchanged');
fetchData(self);
loading.hide();
});
}
} else {
if (hasEnabledTimer) {
loading.show();
recordingHelper.cancelTimer(apiClient, this.TimerId, true).then(function () {
events.trigger(self, 'recordingchanged');
fetchData(self);
loading.hide();
});
}
}
}
function sendToast(msg) {
require(['toast'], function (toast) {
toast(msg);
});
}
function onRecordSeriesChange(e) {
this.changed = true;
var self = this;
var options = this.options;
var apiClient = connectionManager.getApiClient(options.serverId);
var button = dom.parentWithTag(e.target, 'BUTTON');
var isChecked = !button.querySelector('i').classList.contains('recordingIcon-active');
if (isChecked) {
showSeriesRecordingFields(options.parent, options.programId, apiClient);
if (!this.SeriesTimerId) {
var promise = this.TimerId ?
recordingHelper.changeRecordingToSeries(apiClient, this.TimerId, options.programId) :
recordingHelper.createRecording(apiClient, options.programId, true);
promise.then(function () {
fetchData(self);
});
}
} else {
showSingleRecordingFields(options.parent, options.programId, apiClient);
if (this.SeriesTimerId) {
apiClient.cancelLiveTvSeriesTimer(this.SeriesTimerId).then(function () {
sendToast(globalize.translate('RecordingCancelled'));
fetchData(self);
});
}
}
}
RecordingEditor.prototype.embed = function () {
var self = this;
return new Promise(function (resolve, reject) {
require(['text!./recordingfields.template.html'], function (template) {
var options = self.options;
var context = options.parent;
context.innerHTML = globalize.translateDocument(template, 'core');
var supporterButtons = context.querySelectorAll('.btnSupporter');
for (var i = 0, length = supporterButtons.length; i < length; i++) {
supporterButtons[i].addEventListener('click', onSupporterButtonClick);
}
context.querySelector('.singleRecordingButton').addEventListener('click', onRecordChange.bind(self));
context.querySelector('.seriesRecordingButton').addEventListener('click', onRecordSeriesChange.bind(self));
context.querySelector('.btnManageRecording').addEventListener('click', onManageRecordingClick.bind(self));
context.querySelector('.btnManageSeriesRecording').addEventListener('click', onManageSeriesRecordingClick.bind(self));
fetchData(self).then(resolve);
});
});
};
RecordingEditor.prototype.hasChanged = function () {
return this.changed;
};
RecordingEditor.prototype.refresh = function () {
fetchData(this);
};
RecordingEditor.prototype.destroy = function () {
var timerChangedHandler = this.timerChangedHandler;
this.timerChangedHandler = null;
events.off(serverNotifications, 'TimerCreated', timerChangedHandler);
events.off(serverNotifications, 'TimerCancelled', timerChangedHandler);
var seriesTimerChangedHandler = this.seriesTimerChangedHandler;
this.seriesTimerChangedHandler = null;
events.off(serverNotifications, 'SeriesTimerCreated', seriesTimerChangedHandler);
events.off(serverNotifications, 'SeriesTimerCancelled', seriesTimerChangedHandler);
};
return RecordingEditor;
});

View file

@ -0,0 +1,41 @@
<div class="convertRecordingsContainer hide infoBanner" style="margin: 1em 0 2em;">
<h1 style="margin: .25em 0 .5em;">${HeaderConvertYourRecordings}</h1>
<div class="fieldDescription">${PromoConvertRecordingsToStreamingFormat}</div>
<br />
<button is="emby-button" type="button" class="raised btnSupporter btnSupporterForConverting button-submit">
<span>${HeaderLearnMore}</span>
</button>
</div>
<div class="supporterContainer hide infoBanner" style="margin: 1em 0 2em;">
<div class="supporterContainerText"></div>
<br />
<button is="emby-button" type="button" class="btnSupporter raised button-submit block">
<span>${HeaderBecomeProjectSupporter}</span>
</button>
</div>
<div class="recordingFields hide">
<div class="recordSeriesContainer recordingFields-buttons flex align-items-center hide">
<div>
<button is="emby-button" type="button" class="raised recordingButton seriesRecordingButton">
<i class="md-icon recordingIcon">&#xE062;</i>
<span class="buttonText">${RecordSeries}</span>
</button>
</div>
<button is="emby-button" type="button" class="button-flat secondaryText manageRecordingButton btnManageSeriesRecording hide">
<span class="manageButtonText">${SeriesSettings}</span>
</button>
</div>
<div class="recordingFields-buttons flex align-items-center">
<div>
<button is="emby-button" type="button" class="raised recordingButton singleRecordingButton">
<i class="md-icon recordingIcon">&#xE061;</i>
<span class="buttonText">${Record}</span>
</button>
</div>
<button is="emby-button" type="button" class="button-flat secondaryText manageRecordingButton btnManageRecording hide">
<span class="manageButtonText">${Settings}</span>
</button>
</div>
</div>

View file

@ -0,0 +1,227 @@
define(['globalize', 'loading', 'connectionManager', 'registrationServices'], function (globalize, loading, connectionManager, registrationServices) {
'use strict';
function changeRecordingToSeries(apiClient, timerId, programId, confirmTimerCancellation) {
loading.show();
return apiClient.getItem(apiClient.getCurrentUserId(), programId).then(function (item) {
if (item.IsSeries) {
// create series
return apiClient.getNewLiveTvTimerDefaults({ programId: programId }).then(function (timerDefaults) {
return apiClient.createLiveTvSeriesTimer(timerDefaults).then(function () {
loading.hide();
sendToast(globalize.translate('SeriesRecordingScheduled'));
});
});
} else {
// cancel
if (confirmTimerCancellation) {
return cancelTimerWithConfirmation(timerId, apiClient.serverId());
}
return cancelTimer(apiClient.serverId(), timerId, true);
}
});
}
function cancelTimerWithConfirmation(timerId, serverId) {
return new Promise(function (resolve, reject) {
require(['confirm'], function (confirm) {
confirm({
text: globalize.translate('MessageConfirmRecordingCancellation'),
primary: 'cancel',
confirmText: globalize.translate('HeaderCancelRecording'),
cancelText: globalize.translate('HeaderKeepRecording')
}).then(function () {
loading.show();
var apiClient = connectionManager.getApiClient(serverId);
cancelTimer(apiClient, timerId, true).then(resolve, reject);
}, reject);
});
});
}
function cancelSeriesTimerWithConfirmation(timerId, serverId) {
return new Promise(function (resolve, reject) {
require(['confirm'], function (confirm) {
confirm({
text: globalize.translate('MessageConfirmRecordingCancellation'),
primary: 'cancel',
confirmText: globalize.translate('HeaderCancelSeries'),
cancelText: globalize.translate('HeaderKeepSeries')
}).then(function () {
loading.show();
var apiClient = connectionManager.getApiClient(serverId);
apiClient.cancelLiveTvSeriesTimer(timerId).then(function () {
require(['toast'], function (toast) {
toast(globalize.translate('SeriesCancelled'));
});
loading.hide();
resolve();
}, reject);
}, reject);
});
});
}
function cancelTimer(apiClient, timerId, hideLoading) {
loading.show();
return apiClient.cancelLiveTvTimer(timerId).then(function () {
if (hideLoading !== false) {
loading.hide();
sendToast(globalize.translate('RecordingCancelled'));
}
});
}
function createRecording(apiClient, programId, isSeries) {
loading.show();
return apiClient.getNewLiveTvTimerDefaults({ programId: programId }).then(function (item) {
var promise = isSeries ?
apiClient.createLiveTvSeriesTimer(item) :
apiClient.createLiveTvTimer(item);
return promise.then(function () {
loading.hide();
sendToast(globalize.translate('RecordingScheduled'));
});
});
}
function sendToast(msg) {
require(['toast'], function (toast) {
toast(msg);
});
}
function showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId) {
return new Promise(function (resolve, reject) {
require(['dialog'], function (dialog) {
var items = [];
items.push({
name: globalize.translate('HeaderKeepRecording'),
id: 'cancel',
type: 'submit'
});
if (timerStatus === 'InProgress') {
items.push({
name: globalize.translate('HeaderStopRecording'),
id: 'canceltimer',
type: 'cancel'
});
} else {
items.push({
name: globalize.translate('HeaderCancelRecording'),
id: 'canceltimer',
type: 'cancel'
});
}
items.push({
name: globalize.translate('HeaderCancelSeries'),
id: 'cancelseriestimer',
type: 'cancel'
});
dialog({
text: globalize.translate('MessageConfirmRecordingCancellation'),
buttons: items
}).then(function (result) {
var apiClient = connectionManager.getApiClient(serverId);
if (result === 'canceltimer') {
loading.show();
cancelTimer(apiClient, timerId, true).then(resolve, reject);
}
else if (result === 'cancelseriestimer') {
loading.show();
apiClient.cancelLiveTvSeriesTimer(seriesTimerId).then(function () {
require(['toast'], function (toast) {
toast(globalize.translate('SeriesCancelled'));
});
loading.hide();
resolve();
}, reject);
} else {
resolve();
}
}, reject);
});
});
}
function toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId) {
return registrationServices.validateFeature('dvr').then(function () {
var apiClient = connectionManager.getApiClient(serverId);
var hasTimer = timerId && timerStatus !== 'Cancelled';
if (seriesTimerId && hasTimer) {
// cancel
return showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId);
} else if (hasTimer && programId) {
// change to series recording, if possible
// otherwise cancel individual recording
return changeRecordingToSeries(apiClient, timerId, programId, true);
} else if (programId) {
// schedule recording
return createRecording(apiClient, programId);
} else {
return Promise.reject();
}
});
}
return {
cancelTimer: cancelTimer,
createRecording: createRecording,
changeRecordingToSeries: changeRecordingToSeries,
toggleRecording: toggleRecording,
cancelTimerWithConfirmation: cancelTimerWithConfirmation,
cancelSeriesTimerWithConfirmation: cancelSeriesTimerWithConfirmation
};
});

View file

@ -0,0 +1,270 @@
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'imageLoader', 'datetime', 'scrollStyles', 'emby-button', 'emby-checkbox', 'emby-input', 'emby-select', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons', 'flexStyles'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, imageLoader, datetime) {
'use strict';
var currentDialog;
var recordingUpdated = false;
var recordingDeleted = false;
var currentItemId;
var currentServerId;
function deleteTimer(apiClient, timerId) {
return new Promise(function (resolve, reject) {
require(['recordingHelper'], function (recordingHelper) {
recordingHelper.cancelSeriesTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject);
});
});
}
function renderTimer(context, item, apiClient) {
var program = item.ProgramInfo || {};
context.querySelector('#txtPrePaddingMinutes').value = item.PrePaddingSeconds / 60;
context.querySelector('#txtPostPaddingMinutes').value = item.PostPaddingSeconds / 60;
context.querySelector('.selectChannels').value = item.RecordAnyChannel ? 'all' : 'one';
context.querySelector('.selectAirTime').value = item.RecordAnyTime ? 'any' : 'original';
context.querySelector('.selectShowType').value = item.RecordNewOnly ? 'new' : 'all';
context.querySelector('.chkSkipEpisodesInLibrary').checked = item.SkipEpisodesInLibrary;
context.querySelector('.selectKeepUpTo').value = item.KeepUpTo || 0;
if (item.ChannelName || item.ChannelNumber) {
context.querySelector('.optionChannelOnly').innerHTML = globalize.translate('ChannelNameOnly', item.ChannelName || item.ChannelNumber);
} else {
context.querySelector('.optionChannelOnly').innerHTML = globalize.translate('OneChannel');
}
context.querySelector('.optionAroundTime').innerHTML = globalize.translate('AroundTime', datetime.getDisplayTime(datetime.parseISO8601Date(item.StartDate)));
loading.hide();
}
function closeDialog(isDeleted) {
recordingUpdated = true;
recordingDeleted = isDeleted;
dialogHelper.close(currentDialog);
}
function onSubmit(e) {
var form = this;
var apiClient = connectionManager.getApiClient(currentServerId);
apiClient.getLiveTvSeriesTimer(currentItemId).then(function (item) {
item.PrePaddingSeconds = form.querySelector('#txtPrePaddingMinutes').value * 60;
item.PostPaddingSeconds = form.querySelector('#txtPostPaddingMinutes').value * 60;
item.RecordAnyChannel = form.querySelector('.selectChannels').value === 'all';
item.RecordAnyTime = form.querySelector('.selectAirTime').value === 'any';
item.RecordNewOnly = form.querySelector('.selectShowType').value === 'new';
item.SkipEpisodesInLibrary = form.querySelector('.chkSkipEpisodesInLibrary').checked;
item.KeepUpTo = form.querySelector('.selectKeepUpTo').value;
apiClient.updateLiveTvSeriesTimer(item);
});
e.preventDefault();
// Disable default form submission
return false;
}
function init(context) {
fillKeepUpTo(context);
context.querySelector('.btnCancel').addEventListener('click', function () {
closeDialog(false);
});
context.querySelector('.btnCancelRecording').addEventListener('click', function () {
var apiClient = connectionManager.getApiClient(currentServerId);
deleteTimer(apiClient, currentItemId).then(function () {
closeDialog(true);
});
});
context.querySelector('form').addEventListener('submit', onSubmit);
}
function reload(context, id) {
var apiClient = connectionManager.getApiClient(currentServerId);
loading.show();
if (typeof id === 'string') {
currentItemId = id;
apiClient.getLiveTvSeriesTimer(id).then(function (result) {
renderTimer(context, result, apiClient);
loading.hide();
});
} else if (id) {
currentItemId = id.Id;
renderTimer(context, id, apiClient);
loading.hide();
}
}
function fillKeepUpTo(context) {
var html = '';
for (var i = 0; i <= 50; i++) {
var text;
if (i === 0) {
text = globalize.translate('AsManyAsPossible');
} else if (i === 1) {
text = globalize.translate('ValueOneEpisode');
} else {
text = globalize.translate('ValueEpisodeCount', i);
}
html += '<option value="' + i + '">' + text + '</option>';
}
context.querySelector('.selectKeepUpTo').innerHTML = html;
}
function onFieldChange(e) {
this.querySelector('.btnSubmit').click();
}
function embed(itemId, serverId, options) {
recordingUpdated = false;
recordingDeleted = false;
currentServerId = serverId;
loading.show();
options = options || {};
require(['text!./seriesrecordingeditor.template.html'], function (template) {
var dialogOptions = {
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'small';
}
var dlg = options.context;
dlg.classList.add('hide');
dlg.innerHTML = globalize.translateDocument(template, 'core');
dlg.querySelector('.formDialogHeader').classList.add('hide');
dlg.querySelector('.formDialogFooter').classList.add('hide');
dlg.querySelector('.formDialogContent').className = '';
dlg.querySelector('.dialogContentInner').className = '';
dlg.classList.remove('hide');
dlg.removeEventListener('change', onFieldChange);
dlg.addEventListener('change', onFieldChange);
currentDialog = dlg;
init(dlg);
reload(dlg, itemId);
});
}
function showEditor(itemId, serverId, options) {
return new Promise(function (resolve, reject) {
recordingUpdated = false;
recordingDeleted = false;
currentServerId = serverId;
loading.show();
options = options || {};
require(['text!./seriesrecordingeditor.template.html'], function (template) {
var dialogOptions = {
removeOnClose: true,
scrollY: false
};
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
dlg.classList.add('recordingDialog');
if (!layoutManager.tv) {
dlg.style['min-width'] = '20%';
}
var html = '';
html += globalize.translateDocument(template, 'core');
dlg.innerHTML = html;
if (options.enableCancel === false) {
dlg.querySelector('.formDialogFooter').classList.add('hide');
}
currentDialog = dlg;
dlg.addEventListener('closing', function () {
if (!recordingDeleted) {
this.querySelector('.btnSubmit').click();
}
});
dlg.addEventListener('close', function () {
if (recordingUpdated) {
resolve({
updated: true,
deleted: recordingDeleted
});
} else {
reject();
}
});
if (layoutManager.tv) {
scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false);
}
init(dlg);
reload(dlg, itemId);
dialogHelper.open(dlg);
});
});
}
return {
show: showEditor,
embed: embed
};
});

View file

@ -0,0 +1,78 @@
<div class="formDialogHeader">
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>
<h3 class="formDialogHeaderTitle">
${HeaderSeriesOptions}
</h3>
</div>
<div class="formDialogContent smoothScrollY">
<div class="dialogContentInner dialog-content-centered" style="padding-top:2em;">
<form style="max-width: none;">
<div class="selectContainer">
<select is="emby-select" class="selectShowType" label="${LabelRecord}">
<option value="new">${NewEpisodesOnly}</option>
<option value="all">${AllEpisodes}</option>
</select>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" class="chkSkipEpisodesInLibrary" />
<span>${SkipEpisodesAlreadyInMyLibrary}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${SkipEpisodesAlreadyInMyLibraryHelp}</div>
</div>
<div class="selectContainer">
<select is="emby-select" class="selectChannels" label="${LabelChannels}">
<option class="optionChannelOnly" value="one">${OneChannel}</option>
<option value="all">${AllChannels}</option>
</select>
</div>
<div class="selectContainer">
<select is="emby-select" class="selectAirTime" label="${LabelAirTime}">
<option class="optionAroundTime" value="original"></option>
<option value="any">${Anytime}</option>
</select>
</div>
<div class="selectContainer">
<select is="emby-select" class="selectKeepUpTo" label="${LabelKeepUpTo}"></select>
</div>
<div class="inputContainer">
<div class="flex align-items-center">
<div class="flex-grow">
<input is="emby-input" type="number" id="txtPrePaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelStartWhenPossible}" />
</div>
<div class="fieldDescription" style="margin-left:.5em;font-size:90%;margin-top:1.3em;">
${MinutesBefore}
</div>
</div>
</div>
<div class="inputContainer">
<div class="flex align-items-center">
<div class="flex-grow">
<input is="emby-input" type="number" id="txtPostPaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelStopWhenPossible}" />
</div>
<div class="fieldDescription" style="margin-left:.5em;font-size:90%;margin-top:1.3em;">
${MinutesAfter}
</div>
</div>
</div>
<br />
<div class="formDialogFooter">
<button is="emby-button" type="submit" class="raised btnSubmit block formDialogFooterItem button-submit hide">
<span>${Save}</span>
</button>
<button is="emby-button" type="button" class="raised btnCancelRecording block formDialogFooterItem button-cancel" style="white-space: nowrap;">
<span>${HeaderCancelRecording}</span>
</button>
</div>
</form>
</div>
</div>