mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge remote-tracking branch 'upstream/master' into quickconnect
This commit is contained in:
commit
9476edcbe2
431 changed files with 26974 additions and 36233 deletions
26
src/controllers/dashboard/apikeys.html
Normal file
26
src/controllers/dashboard/apikeys.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
<div id="apiKeysPage" data-role="page" class="page type-interior advancedConfigurationPage fullWidthContent">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="detailSectionHeader">
|
||||
<h2 style="margin:.6em 0;vertical-align:middle;display:inline-block;">${HeaderApiKeys}</h2>
|
||||
<button is="emby-button" type="button" class="fab btnNewKey submit" style="margin-left:1em;" title="${Add}">
|
||||
<span class="material-icons add" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<p>${HeaderApiKeysHelp}</p>
|
||||
<br />
|
||||
<table class="tblApiKeys detailTable">
|
||||
<caption class="clipForScreenReader">${ApiKeysCaption}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="detailTableHeaderCell"></th>
|
||||
<th scope="col" class="detailTableHeaderCell">${HeaderApiKey}</th>
|
||||
<th scope="col" class="detailTableHeaderCell">${HeaderApp}</th>
|
||||
<th scope="col" class="detailTableHeaderCell">${HeaderDateIssued}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="resultBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,6 +1,5 @@
|
|||
import datetime from 'datetime';
|
||||
import loading from 'loading';
|
||||
import libraryMenu from 'libraryMenu';
|
||||
import dom from 'dom';
|
||||
import globalize from 'globalize';
|
||||
import 'emby-button';
|
||||
|
|
119
src/controllers/dashboard/dashboard.html
Normal file
119
src/controllers/dashboard/dashboard.html
Normal file
|
@ -0,0 +1,119 @@
|
|||
<div id="dashboardPage" data-role="page" class="page type-interior dashboardHomePage fullWidthContent">
|
||||
<div class="content-primary">
|
||||
<div class="dashboardSections" style="padding-top:.5em;">
|
||||
<div class="dashboardColumn dashboardColumn-2-60 dashboardColumn-3-46">
|
||||
<div class="dashboardSection">
|
||||
<a is="emby-linkbutton" href="dashboardgeneral.html" class="button-flat sectionTitleTextButton">
|
||||
<h3>${TabServer}</h3>
|
||||
<span class="material-icons chevron_right"></span>
|
||||
</a>
|
||||
|
||||
<div class="paperList" style="padding: 1em;">
|
||||
<p id="serverName"></p>
|
||||
<p id="versionNumber"></p>
|
||||
<p id="operatingSystem"></p>
|
||||
<p id="architecture"></p>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:1em;">
|
||||
<button is="emby-button" type="button" id="btnRestartServer" class="raised" onclick="DashboardPage.restart(this);" style="margin-left:0;">
|
||||
<span>${Restart}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" id="btnShutdown" class="raised" onclick="DashboardPage.shutdown(this);">
|
||||
<span>${ButtonShutdown}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 2em;" class="runningTasksContainer hide">
|
||||
<h3>${HeaderRunningTasks}</h3>
|
||||
<div id="divRunningTasks" class="paperList" style="padding: 1em;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboardSection">
|
||||
<a is="emby-linkbutton" href="devices.html" class="button-flat sectionTitleTextButton">
|
||||
<h3>${HeaderActiveDevices}</h3>
|
||||
<span class="material-icons chevron_right"></span>
|
||||
</a>
|
||||
<div class="activeDevices itemsContainer vertical-wrap">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboardColumn dashboardColumn-2-40 dashboardColumn-3-27">
|
||||
<div class="dashboardSection">
|
||||
<a is="emby-linkbutton" href="serveractivity.html?useractivity=true" class="button-flat sectionTitleTextButton">
|
||||
<h3>${HeaderActivity}</h3>
|
||||
<span class="material-icons chevron_right"></span>
|
||||
</a>
|
||||
<div class="paperList userActivityItems" data-activitylimit="7" data-useractivity="true">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboardColumn dashboardColumn-3-27">
|
||||
|
||||
<div class="dashboardSection activeRecordingsSection hide">
|
||||
<h3>${HeaderActiveRecordings}</h3>
|
||||
<div class="activeRecordingItems vertical-wrap" is="emby-itemscontainer">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboardSection serverActivitySection hide activityContainer">
|
||||
<a is="emby-linkbutton" href="serveractivity.html?useractivity=false" class="button-flat sectionTitleTextButton">
|
||||
<h3>${Alerts}</h3>
|
||||
<span class="material-icons chevron_right"></span>
|
||||
</a>
|
||||
<div class="paperList serverActivityItems" data-activitylimit="4" data-useractivity="false">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboardSection">
|
||||
<a is="emby-linkbutton" href="dashboardgeneral.html" class="button-flat sectionTitleTextButton">
|
||||
<h3>${HeaderPaths}</h3>
|
||||
<span class="material-icons chevron_right"></span>
|
||||
</a>
|
||||
<div class="paperList">
|
||||
<div class="listItem listItem-border">
|
||||
<div class="listItemBody two-line">
|
||||
<div class="listItemBodyText secondary" style="margin:0;">${LabelCache}</div>
|
||||
<div class="listItemBodyText" id="cachePath"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="listItem listItem-border">
|
||||
<div class="listItemBody two-line">
|
||||
<div class="listItemBodyText secondary" style="margin:0;">${LabelLogs}</div>
|
||||
<div class="listItemBodyText" id="logPath"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="listItem listItem-border">
|
||||
<div class="listItemBody two-line">
|
||||
<div class="listItemBodyText secondary" style="margin:0;">${LabelMetadata}</div>
|
||||
<div class="listItemBodyText" id="metadataPath"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="listItem listItem-border">
|
||||
<div class="listItemBody two-line">
|
||||
<div class="listItemBodyText secondary" style="margin:0;">${LabelTranscodes}</div>
|
||||
<div class="listItemBodyText" id="transcodePath"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="listItem listItem-border">
|
||||
<div class="listItemBody two-line">
|
||||
<div class="listItemBodyText secondary" style="margin:0;">${LabelWeb}</div>
|
||||
<div class="listItemBodyText" id="webPath"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboardFooter">
|
||||
<div style="height:1px;" class="ui-bar-inherit"></div>
|
||||
<div style="margin-top:1em;">
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="https://jellyfin.org" target="_blank">Jellyfin</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -7,7 +7,6 @@ import globalize from 'globalize';
|
|||
import * as datefns from 'date-fns';
|
||||
import dfnshelper from 'dfnshelper';
|
||||
import loading from 'loading';
|
||||
import connectionManager from 'connectionManager';
|
||||
import playMethodHelper from 'playMethodHelper';
|
||||
import cardBuilder from 'cardBuilder';
|
||||
import imageLoader from 'imageLoader';
|
||||
|
@ -24,7 +23,7 @@ import 'emby-itemscontainer';
|
|||
function showPlaybackInfo(btn, session) {
|
||||
import('alert').then(({default: alert}) => {
|
||||
let title;
|
||||
let text = [];
|
||||
const text = [];
|
||||
const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);
|
||||
|
||||
if (displayPlayMethod === 'DirectStream') {
|
||||
|
@ -60,7 +59,7 @@ import 'emby-itemscontainer';
|
|||
confirmText: globalize.translate('ButtonSend')
|
||||
}).then(function (text) {
|
||||
if (text) {
|
||||
connectionManager.getApiClient(session.ServerId).sendMessageCommand(session.Id, {
|
||||
window.connectionManager.getApiClient(session.ServerId).sendMessageCommand(session.Id, {
|
||||
Text: text,
|
||||
TimeoutMs: 5e3
|
||||
});
|
||||
|
@ -73,7 +72,7 @@ import 'emby-itemscontainer';
|
|||
import('actionsheet').then(({default: actionsheet}) => {
|
||||
const menuItems = [];
|
||||
|
||||
if (session.ServerId && session.DeviceId !== connectionManager.deviceId()) {
|
||||
if (session.ServerId && session.DeviceId !== window.connectionManager.deviceId()) {
|
||||
menuItems.push({
|
||||
name: globalize.translate('SendMessage'),
|
||||
id: 'sendmessage'
|
||||
|
@ -123,9 +122,9 @@ import 'emby-itemscontainer';
|
|||
} else if (btn.classList.contains('btnSessionSendMessage')) {
|
||||
showSendMessageForm(btn, session);
|
||||
} else if (btn.classList.contains('btnSessionStop')) {
|
||||
connectionManager.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'Stop');
|
||||
window.connectionManager.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'Stop');
|
||||
} else if (btn.classList.contains('btnSessionPlayPause') && session.PlayState) {
|
||||
connectionManager.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'PlayPause');
|
||||
window.connectionManager.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'PlayPause');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -313,7 +312,7 @@ import 'emby-itemscontainer';
|
|||
btnCssClass = session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length ? '' : ' hide';
|
||||
html += '<button is="paper-icon-button-light" class="sessionCardButton btnSessionInfo paper-icon-button-light ' + btnCssClass + '" title="' + globalize.translate('ViewPlaybackInfo') + '"><span class="material-icons info"></span></button>';
|
||||
|
||||
btnCssClass = session.ServerId && -1 !== session.SupportedCommands.indexOf('DisplayMessage') && session.DeviceId !== connectionManager.deviceId() ? '' : ' hide';
|
||||
btnCssClass = session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1 && session.DeviceId !== window.connectionManager.deviceId() ? '' : ' hide';
|
||||
html += '<button is="paper-icon-button-light" class="sessionCardButton btnSessionSendMessage paper-icon-button-light ' + btnCssClass + '" title="' + globalize.translate('SendMessage') + '"><span class="material-icons message"></span></button>';
|
||||
html += '</div>';
|
||||
|
||||
|
@ -346,7 +345,7 @@ import 'emby-itemscontainer';
|
|||
function renderRunningTasks(view, tasks) {
|
||||
let html = '';
|
||||
tasks = tasks.filter(function (task) {
|
||||
if ('Idle' != task.State) {
|
||||
if (task.State != 'Idle') {
|
||||
return !task.IsHidden;
|
||||
}
|
||||
|
||||
|
@ -551,7 +550,7 @@ import 'emby-itemscontainer';
|
|||
row.classList.remove('playingSession');
|
||||
}
|
||||
|
||||
if (session.ServerId && -1 !== session.SupportedCommands.indexOf('DisplayMessage') && session.DeviceId !== connectionManager.deviceId()) {
|
||||
if (session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1 && session.DeviceId !== window.connectionManager.deviceId()) {
|
||||
row.querySelector('.btnSessionSendMessage').classList.remove('hide');
|
||||
} else {
|
||||
row.querySelector('.btnSessionSendMessage').classList.add('hide');
|
||||
|
@ -565,7 +564,7 @@ import 'emby-itemscontainer';
|
|||
|
||||
const btnSessionPlayPause = row.querySelector('.btnSessionPlayPause');
|
||||
|
||||
if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl && session.DeviceId !== connectionManager.deviceId()) {
|
||||
if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl && session.DeviceId !== window.connectionManager.deviceId()) {
|
||||
btnSessionPlayPause.classList.remove('hide');
|
||||
row.querySelector('.btnSessionStop').classList.remove('hide');
|
||||
} else {
|
||||
|
@ -722,9 +721,9 @@ import 'emby-itemscontainer';
|
|||
restart: function (btn) {
|
||||
import('confirm').then(({default: confirm}) => {
|
||||
confirm({
|
||||
title: globalize.translate('HeaderRestart'),
|
||||
title: globalize.translate('Restart'),
|
||||
text: globalize.translate('MessageConfirmRestart'),
|
||||
confirmText: globalize.translate('ButtonRestart'),
|
||||
confirmText: globalize.translate('Restart'),
|
||||
primary: 'delete'
|
||||
}).then(function () {
|
||||
const page = dom.parentWithClass(btn, 'page');
|
||||
|
@ -737,7 +736,7 @@ import 'emby-itemscontainer';
|
|||
shutdown: function (btn) {
|
||||
import('confirm').then(({default: confirm}) => {
|
||||
confirm({
|
||||
title: globalize.translate('HeaderShutdown'),
|
||||
title: globalize.translate('ButtonShutdown'),
|
||||
text: globalize.translate('MessageConfirmShutdown'),
|
||||
confirmText: globalize.translate('ButtonShutdown'),
|
||||
primary: 'delete'
|
||||
|
|
24
src/controllers/dashboard/devices/device.html
Normal file
24
src/controllers/dashboard/devices/device.html
Normal file
|
@ -0,0 +1,24 @@
|
|||
<div id="devicePage" data-role="page" class="page type-interior devicesPage noSecondaryNavPage">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<form class="deviceForm">
|
||||
<div class="verticalSection verticalSection-extrabottompadding">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle reportedName"></h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/devices.html">${Help}</a>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtCustomName" label="${LabelDisplayName}" />
|
||||
<div class="fieldDescription">${LabelCustomDeviceDisplayNameHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,5 +1,4 @@
|
|||
import loading from 'loading';
|
||||
import libraryMenu from 'libraryMenu';
|
||||
import dom from 'dom';
|
||||
import 'emby-input';
|
||||
import 'emby-button';
|
||||
|
|
13
src/controllers/dashboard/devices/devices.html
Normal file
13
src/controllers/dashboard/devices/devices.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<div id="devicesPage" data-role="page" class="page type-interior devicesPage noSecondaryNavPage">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="verticalSection verticalSection">
|
||||
<div class="sectionTitleContainer sectionTitleContainer-cards flex align-items-center">
|
||||
<h2 class="sectionTitle sectionTitle-cards">${HeaderDevices}</h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/devices.html">${Help}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-itemscontainer" class="devicesList vertical-wrap" data-multiselect="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,6 +1,5 @@
|
|||
import loading from 'loading';
|
||||
import dom from 'dom';
|
||||
import libraryMenu from 'libraryMenu';
|
||||
import globalize from 'globalize';
|
||||
import imageHelper from 'scripts/imagehelper';
|
||||
import * as datefns from 'date-fns';
|
||||
|
@ -22,7 +21,7 @@ import 'cardStyle';
|
|||
confirm({
|
||||
text: msg,
|
||||
title: globalize.translate('HeaderDeleteDevice'),
|
||||
confirmText: globalize.translate('ButtonDelete'),
|
||||
confirmText: globalize.translate('Delete'),
|
||||
primary: 'delete'
|
||||
}).then(function () {
|
||||
loading.show();
|
||||
|
@ -39,7 +38,7 @@ import 'cardStyle';
|
|||
}
|
||||
|
||||
function showDeviceMenu(view, btn, deviceId) {
|
||||
let menuItems = [];
|
||||
const menuItems = [];
|
||||
|
||||
if (canEdit) {
|
||||
menuItems.push({
|
||||
|
|
584
src/controllers/dashboard/dlna/profile.html
Normal file
584
src/controllers/dashboard/dlna/profile.html
Normal file
|
@ -0,0 +1,584 @@
|
|||
<div id="dlnaProfilePage" data-role="page" class="page type-interior dlnaPage withTabs">
|
||||
<div data-role="content">
|
||||
<div class="content-primary">
|
||||
<form class="dlnaProfileForm" style="max-width: 650px;">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${HeaderProfileInformation}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div data-role="controlgroup" data-type="horizontal" data-mini="true">
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioInfo" data-value="tabInfo">${ButtonInfo}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioDirectPlay" data-value="tabDirectPlayProfiles">${TabDirectPlay}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioTranscoding" data-value="tabTranscodingProfiles">${Transcoding}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioContainers" data-value="tabContainerProfiles">${TabContainers}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioCodecs" data-value="tabCodecProfiles">${TabCodecs}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioMediaProfiles" data-value="tabMediaProfiles">${TabResponses}</a>
|
||||
</div>
|
||||
<br />
|
||||
<div class="tabContent tabInfo">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtName" required="required" label="${LabelName}" />
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectUser" label="${LabelUserLibrary}"></select>
|
||||
<div class="fieldDescription">${LabelUserLibraryHelp}</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="checkboxListLabel">${LabelSupportedMediaTypes}</h3>
|
||||
<div class="checkboxList paperList checkboxList-paperList">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkAudio" data-value="Audio" class="chkMediaType" />
|
||||
<span>${Audio}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkPhoto" data-value="Photo" class="chkMediaType" />
|
||||
<span>${Photo}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkVideo" data-value="Video" class="chkMediaType" />
|
||||
<span>${Video}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtMaxAllowedBitrate" pattern="[0-9]*" min="1" label="${LabelMaxStreamingBitrate}" />
|
||||
<div class="fieldDescription">${LabelMaxStreamingBitrateHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtMusicStreamingTranscodingBitrate" pattern="[0-9]*" min="1" label="${LabelMusicStreamingTranscodingBitrate}" />
|
||||
<div class="fieldDescription">${LabelMusicStreamingTranscodingBitrateHelp}</div>
|
||||
</div>
|
||||
<div style="display:none;">
|
||||
<label for="chkIgnoreTranscodeByteRangeRequests">${OptionIgnoreTranscodeByteRangeRequests}</label>
|
||||
<input type="checkbox" id="chkIgnoreTranscodeByteRangeRequests" data-mini="true" />
|
||||
<div class="fieldDescription">${OptionIgnoreTranscodeByteRangeRequestsHelp}</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${HeaderIdentification}">
|
||||
<div class="collapseContent">
|
||||
<h3>${HeaderIdentificationCriteriaHelp}</h3>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdFriendlyName" label="${LabelFriendlyName}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdManufacturer" label="${LabelManufacturer}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdManufacturerUrl" label="${LabelManufacturerUrl}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdModelName" label="${LabelModelName}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdModelNumber" label="${LabelModelNumber}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdModelDesription" label="${LabelModelDescription}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdModelUrl" label="${LabelModelUrl}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdSerialNumber" label="${LabelSerialNumber}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdDeviceDescription" label="${LabelDeviceDescription}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2 style="vertical-align:middle;display:inline-block;">${HeaderHttpHeaders}</h2>
|
||||
<button is="emby-button" type="button" class="fab btnAddIdentificationHttpHeader submit sectionTitleButton" style="margin-left:1em;" title="${Add}">
|
||||
<span class="material-icons add"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="httpHeaderIdentificationList"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${Display}">
|
||||
<div class="collapseContent">
|
||||
<br />
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkRequiresPlainFolders" />
|
||||
<span>${OptionPlainStorageFolders}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionPlainStorageFoldersHelp}</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkRequiresPlainVideoItems" />
|
||||
<span>${OptionPlainVideoItems}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionPlainVideoItemsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${HeaderImageSettings}">
|
||||
<div class="collapseContent">
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableAlbumArtInDidl" data-mini="true" />
|
||||
<span>${LabelEmbedAlbumArtDidl}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEmbedAlbumArtDidlHelp}</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableSingleImageLimit" data-mini="true" />
|
||||
<span>${LabelEnableSingleImageInDidlLimit}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableSingleImageInDidlLimitHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input type="text" is="emby-input" id="txtAlbumArtPn" label="${LabelAlbumArtPN}" />
|
||||
<div class="fieldDescription">${LabelAlbumArtHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input type="number" is="emby-input" id="txtAlbumArtMaxWidth" pattern="[0-9]*" min="1" label="${LabelAlbumArtMaxWidth}" />
|
||||
<div class="fieldDescription">${LabelAlbumArtMaxResHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input type="number" is="emby-input" id="txtAlbumArtMaxHeight" pattern="[0-9]*" min="1" label="${LabelAlbumArtMaxHeight}" />
|
||||
<div class="fieldDescription">${LabelAlbumArtMaxResHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input type="number" is="emby-input" id="txtIconMaxWidth" pattern="[0-9]*" min="1" label="${LabelIconMaxWidth}" />
|
||||
<div class="fieldDescription">${LabelIconMaxResHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input type="number" is="emby-input" id="txtIconMaxHeight" pattern="[0-9]*" min="1" label="${LabelIconMaxHeight}" />
|
||||
<div class="fieldDescription">${LabelIconMaxResHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${HeaderServerSettings}">
|
||||
<div class="collapseContent">
|
||||
<p>${HeaderProfileServerSettingsHelp}</p>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoFriendlyName" label="${LabelFriendlyName}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoManufacturer" label="${LabelManufacturer}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoManufacturerUrl" label="${LabelManufacturerUrl}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoModelName" label="${LabelModelName}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoModelNumber" label="${LabelModelNumber}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoModelDesription" label="${LabelModelDescription}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoModelUrl" label="${LabelModelUrl}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoSerialNumber" label="${LabelSerialNumber}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtProtocolInfo" label="${LabelProtocolInfo}" />
|
||||
<div class="fieldDescription">${LabelProtocolInfoHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtXDlnaCap" label="${LabelXDlnaCap}" />
|
||||
<div class="fieldDescription">${LabelXDlnaCapHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtXDlnaDoc" label="${LabelXDlnaDoc}" />
|
||||
<div class="fieldDescription">${LabelXDlnaDocHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtSonyAggregationFlags" label="${LabelSonyAggregationFlags}" />
|
||||
<div class="fieldDescription">${LabelSonyAggregationFlagsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${HeaderSubtitleProfiles}">
|
||||
<div class="collapseContent">
|
||||
<p>${HeaderSubtitleProfilesHelp}</p>
|
||||
<button is="emby-button" type="button" class="raised submit block btnAddSubtitleProfile">
|
||||
<span>${Add}</span>
|
||||
</button>
|
||||
<div class="subtitleProfileList"></div>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${HeaderXmlSettings}">
|
||||
<div class="collapseContent">
|
||||
<div>
|
||||
<h2 style="vertical-align:middle;display:inline-block;">${HeaderXmlDocumentAttributes}</h2>
|
||||
<button is="emby-button" type="button" class="fab btnAddXmlDocumentAttribute submit sectionTitleButton" style="margin-left:1em;" title="${Add}">
|
||||
<span class="material-icons add"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="xmlDocumentAttributeList"></div>
|
||||
<div class="fieldDescription">${XmlDocumentAttributeListHelp}</div>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabContent tabDirectPlayProfiles">
|
||||
<p>${HeaderDirectPlayProfileHelp}</p>
|
||||
<button is="emby-button" class="raised submit block btnAddDirectPlayProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
|
||||
<br />
|
||||
<div class="directPlayProfiles"></div>
|
||||
</div>
|
||||
<div class="tabContent tabTranscodingProfiles">
|
||||
<p>${HeaderTranscodingProfileHelp}</p>
|
||||
<button is="emby-button" class="raised submit block btnAddTranscodingProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
|
||||
<br />
|
||||
<div class="transcodingProfiles"></div>
|
||||
</div>
|
||||
<div class="tabContent tabContainerProfiles">
|
||||
<p>${HeaderContainerProfileHelp}</p>
|
||||
<button is="emby-button" class="raised submit block btnAddContainerProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
|
||||
<br />
|
||||
<div class="containerProfiles"></div>
|
||||
</div>
|
||||
<div class="tabContent tabCodecProfiles">
|
||||
<p>${HeaderCodecProfileHelp}</p>
|
||||
<button is="emby-button" class="raised submit block btnAddCodecProfile" type="button" data-icon="plus">${New}</button>
|
||||
<br />
|
||||
<div class="codecProfiles"></div>
|
||||
</div>
|
||||
<div class="tabContent tabMediaProfiles">
|
||||
<p>${HeaderResponseProfileHelp}</p>
|
||||
<button is="emby-button" class="raised submit block btnAddResponseProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
|
||||
<br />
|
||||
<div class="mediaProfiles"></div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="button-cancel raised block" onclick="Dashboard.navigate('dlnaprofiles.html');">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div data-role="popup" id="popupEditDirectPlayProfile" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="editDirectPlayProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderDirectPlayProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="selectContainer">
|
||||
<select id="selectDirectPlayProfileType" name="selectDirectPlayProfileType" is="emby-select" label="${LabelType}">
|
||||
<option value="Audio">${Audio}</option>
|
||||
<option value="Photo">${Photo}</option>
|
||||
<option value="Video">${Video}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtDirectPlayContainer" label="${LabelProfileContainer}" />
|
||||
<div class="fieldDescription">${LabelProfileContainersHelp}</div>
|
||||
</div>
|
||||
<div id="fldDirectPlayVideoCodec" style="margin: 1em 0;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtDirectPlayVideoCodec" label="${LabelProfileVideoCodecs}" />
|
||||
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldDirectPlayAudioCodec" style="margin: 1em 0 2em;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtDirectPlayAudioCodec" label="${LabelProfileAudioCodecs}" />
|
||||
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="transcodingProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="transcodingProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderTranscodingProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div data-role="controlgroup" data-type="horizontal" data-mini="true">
|
||||
<input type="radio" name="radioTranscodingTab" class="radioTabButton" id="radioTranscodingBasics" value="tabTranscodingBasics">
|
||||
<label for="radioTranscodingBasics">${ButtonInfo}</label>
|
||||
<input type="radio" name="radioTranscodingTab" class="radioTabButton" id="radioTranscodingAdvanced" value="tabTranscodingAdvanced">
|
||||
<label for="radioTranscodingAdvanced">${TabAdvanced}</label>
|
||||
</div>
|
||||
<br />
|
||||
<div class="tabContent tabTranscodingBasics" style="display: none;">
|
||||
<div class="selectContainer">
|
||||
<select id="selectTranscodingProfileType" name="selectTranscodingProfileType" is="emby-select" label="${LabelType}">
|
||||
<option value="Audio">${Audio}</option>
|
||||
<option value="Photo">${Photo}</option>
|
||||
<option value="Video">${Video}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="fldTranscodingProtocol" style="margin: 1em 0;">
|
||||
<div class="selectContainer">
|
||||
<select id="selectTranscodingProtocol" name="selectTranscodingProtocol" is="emby-select" label="${LabelProtocol}">
|
||||
<option value="Http">${OptionProtocolHttp}</option>
|
||||
<option value="Hls">${OptionProtocolHls}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtTranscodingContainer" label="${LabelProfileContainer}"; required="required" />
|
||||
</div>
|
||||
<div id="fldTranscodingVideoCodec" style="margin: 1em 0;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtTranscodingVideoCodec" label="${LabelVideoCodec}" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldTranscodingAudioCodec" style="margin: 1em 0;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtTranscodingAudioCodec" label="${LabelAudioCodec}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabContent tabTranscodingAdvanced" style="display: none;">
|
||||
<div id="fldEnableMpegtsM2TsMode" style="margin: 1em 0;">
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableMpegtsM2TsMode" />
|
||||
<span>${OptionEnableM2tsMode}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionEnableM2tsModeHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldEstimateContentLength" style="margin: 1em 0;">
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEstimateContentLength" />
|
||||
<span>${OptionEstimateContentLength}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldReportByteRangeRequests" style="margin: 1em 0;">
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkReportByteRangeRequests" />
|
||||
<span>${OptionReportByteRangeSeekingWhenTranscoding}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionReportByteRangeSeekingWhenTranscodingHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="containerProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="containerProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderContainerProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<p>${HeaderContainerProfileHelp}</p>
|
||||
<div class="tabContent tabContainerBasics">
|
||||
<div class="selectContainer">
|
||||
<select id="selectContainerProfileType" name="selectContainerProfileType" is="emby-select" label="${LabelType}">
|
||||
<option value="Audio">${Audio}</option>
|
||||
<option value="Photo">${Photo}</option>
|
||||
<option value="Video">${Video}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtContainerProfileContainer" label="${LabelProfileContainer}" />
|
||||
<div class="fieldDescription">${LabelProfileContainersHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabContent tabContainerConditions" style="display: none;"></div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="codecProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="codecProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderCodecProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<p>${HeaderCodecProfileHelp}</p>
|
||||
<div class="selectContainer">
|
||||
<select id="selectCodecProfileType" name="selectCodecProfileType" is="emby-select" label="${LabelType}">
|
||||
<option value="Video">${Video}</option>
|
||||
<option value="VideoAudio">${VideoAudio}</option>
|
||||
<option value="Audio">${Audio}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtCodecProfileCodec" label="${LabelProfileCodecs}" />
|
||||
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="responseProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="editResponseProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderResponseProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="selectContainer">
|
||||
<select id="selectResponseProfileType" name="selectResponseProfileType" is="emby-select" label="${LabelType}">
|
||||
<option value="Audio">${Audio}</option>
|
||||
<option value="Photo">${Photo}</option>
|
||||
<option value="Video">${Video}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtResponseProfileContainer" label="${LabelProfileContainer}" />
|
||||
<div class="fieldDescription">${LabelProfileContainersHelp}</div>
|
||||
</div>
|
||||
<div id="fldResponseProfileVideoCodec" style="margin: 1em 0;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtResponseProfileVideoCodec" label="${LabelProfileVideoCodecs}" />
|
||||
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldResponseProfileAudioCodec" style="margin: 1em 0 2em;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtResponseProfileAudioCodec" label="${LabelProfileAudioCodecs}" />
|
||||
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="identificationHeaderPopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="identificationHeaderForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderIdentificationHeader}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdentificationHeaderName" label="${LabelName}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdentificationHeaderValue" label="${LabelValue}" />
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select id="selectMatchType" name="selectMatchType" is="emby-select" label="${LabelMatchType}">
|
||||
<option value="Equals">${OptionEquals}</option>
|
||||
<option value="Regex">${OptionRegex}</option>
|
||||
<option value="Substring">${OptionSubstring}</option>
|
||||
</select>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="xmlAttributePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="xmlAttributeForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderXmlDocumentAttribute}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtXmlAttributeName" label="${LabelName}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtXmlAttributeValue" label="${LabelValue}" />
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="subtitleProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="subtitleProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderSubtitleProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtSubtitleProfileFormat" label="${LabelFormat}" />
|
||||
<div class="fieldDescription">${LabelSubtitleFormatHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select id="selectSubtitleProfileMethod" name="selectSubtitleProfileMethod" is="emby-select" label="${LabelMethod}">
|
||||
<option value="Embed">${OptionEmbedSubtitles}</option>
|
||||
<option value="External">${OptionExternallyDownloaded}</option>
|
||||
<option value="Hls">${OptionHlsSegmentedSubtitles}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select id="selectSubtitleProfileDidlMode" name="selectSubtitleProfileDidlMode" is="emby-select" label="${LabelDidlMode}">
|
||||
<option value="">${OptionResElement}</option>
|
||||
<option value="CaptionInfoEx">${OptionCaptionInfoExSamsung}</option>
|
||||
</select>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -29,7 +29,7 @@ import 'listViewStyle';
|
|||
function renderProfile(page, profile, users) {
|
||||
$('#txtName', page).val(profile.Name);
|
||||
$('.chkMediaType', page).each(function () {
|
||||
this.checked = -1 != (profile.SupportedMediaTypes || '').split(',').indexOf(this.getAttribute('data-value'));
|
||||
this.checked = (profile.SupportedMediaTypes || '').split(',').indexOf(this.getAttribute('data-value')) != -1;
|
||||
});
|
||||
$('#chkEnableAlbumArtInDidl', page).prop('checked', profile.EnableAlbumArtInDidl);
|
||||
$('#chkEnableSingleImageLimit', page).prop('checked', profile.EnableSingleAlbumArtLimit);
|
||||
|
@ -111,7 +111,7 @@ import 'listViewStyle';
|
|||
}
|
||||
|
||||
function editIdentificationHeader(page, header) {
|
||||
isSubProfileNew = null == header;
|
||||
isSubProfileNew = header == null;
|
||||
header = header || {};
|
||||
currentSubProfile = header;
|
||||
const popup = $('#identificationHeaderPopup', page);
|
||||
|
@ -156,7 +156,7 @@ import 'listViewStyle';
|
|||
}
|
||||
|
||||
function editXmlDocumentAttribute(page, attribute) {
|
||||
isSubProfileNew = null == attribute;
|
||||
isSubProfileNew = attribute == null;
|
||||
attribute = attribute || {};
|
||||
currentSubProfile = attribute;
|
||||
const popup = $('#xmlAttributePopup', page);
|
||||
|
@ -204,7 +204,7 @@ import 'listViewStyle';
|
|||
}
|
||||
|
||||
function editSubtitleProfile(page, profile) {
|
||||
isSubProfileNew = null == profile;
|
||||
isSubProfileNew = profile == null;
|
||||
profile = profile || {};
|
||||
currentSubProfile = profile;
|
||||
const popup = $('#subtitleProfilePopup', page);
|
||||
|
@ -257,7 +257,6 @@ import 'listViewStyle';
|
|||
let currentType;
|
||||
|
||||
for (const [index, profile] of profiles.entries()) {
|
||||
|
||||
if (profile.Type !== currentType) {
|
||||
html += '<li data-role="list-divider">' + profile.Type + '</li>';
|
||||
currentType = profile.Type;
|
||||
|
@ -267,11 +266,11 @@ import 'listViewStyle';
|
|||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + index + '">';
|
||||
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
|
||||
|
||||
if ('Video' == profile.Type) {
|
||||
if (profile.Type == 'Video') {
|
||||
html += '<p>' + globalize.translate('ValueVideoCodec', profile.VideoCodec || allText) + '</p>';
|
||||
html += '<p>' + globalize.translate('ValueAudioCodec', profile.AudioCodec || allText) + '</p>';
|
||||
} else {
|
||||
if ('Audio' == profile.Type) {
|
||||
if (profile.Type == 'Audio') {
|
||||
html += '<p>' + globalize.translate('ValueCodec', profile.AudioCodec || allText) + '</p>';
|
||||
}
|
||||
}
|
||||
|
@ -299,7 +298,7 @@ import 'listViewStyle';
|
|||
}
|
||||
|
||||
function editDirectPlayProfile(page, directPlayProfile) {
|
||||
isSubProfileNew = null == directPlayProfile;
|
||||
isSubProfileNew = directPlayProfile == null;
|
||||
directPlayProfile = directPlayProfile || {};
|
||||
currentSubProfile = directPlayProfile;
|
||||
const popup = $('#popupEditDirectPlayProfile', page);
|
||||
|
@ -316,7 +315,7 @@ import 'listViewStyle';
|
|||
let currentType;
|
||||
|
||||
for (let i = 0, length = profiles.length; i < length; i++) {
|
||||
let profile = profiles[i];
|
||||
const profile = profiles[i];
|
||||
|
||||
if (profile.Type !== currentType) {
|
||||
html += '<li data-role="list-divider">' + profile.Type + '</li>';
|
||||
|
@ -328,11 +327,11 @@ import 'listViewStyle';
|
|||
html += '<p>Protocol: ' + (profile.Protocol || 'Http') + '</p>';
|
||||
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
|
||||
|
||||
if ('Video' == profile.Type) {
|
||||
if (profile.Type == 'Video') {
|
||||
html += '<p>' + globalize.translate('ValueVideoCodec', profile.VideoCodec || allText) + '</p>';
|
||||
html += '<p>' + globalize.translate('ValueAudioCodec', profile.AudioCodec || allText) + '</p>';
|
||||
} else {
|
||||
if ('Audio' == profile.Type) {
|
||||
if (profile.Type == 'Audio') {
|
||||
html += '<p>' + globalize.translate('ValueCodec', profile.AudioCodec || allText) + '</p>';
|
||||
}
|
||||
}
|
||||
|
@ -355,7 +354,7 @@ import 'listViewStyle';
|
|||
}
|
||||
|
||||
function editTranscodingProfile(page, transcodingProfile) {
|
||||
isSubProfileNew = null == transcodingProfile;
|
||||
isSubProfileNew = transcodingProfile == null;
|
||||
transcodingProfile = transcodingProfile || {};
|
||||
currentSubProfile = transcodingProfile;
|
||||
const popup = $('#transcodingProfilePopup', page);
|
||||
|
@ -366,7 +365,7 @@ import 'listViewStyle';
|
|||
$('#selectTranscodingProtocol', popup).val(transcodingProfile.Protocol || 'Http');
|
||||
$('#chkEnableMpegtsM2TsMode', popup).prop('checked', transcodingProfile.EnableMpegtsM2TsMode || false);
|
||||
$('#chkEstimateContentLength', popup).prop('checked', transcodingProfile.EstimateContentLength || false);
|
||||
$('#chkReportByteRangeRequests', popup).prop('checked', 'Bytes' == transcodingProfile.TranscodeSeekInfo);
|
||||
$('#chkReportByteRangeRequests', popup).prop('checked', transcodingProfile.TranscodeSeekInfo == 'Bytes');
|
||||
$('.radioTabButton:first', popup).trigger('click');
|
||||
openPopup(popup[0]);
|
||||
}
|
||||
|
@ -402,7 +401,7 @@ import 'listViewStyle';
|
|||
let currentType;
|
||||
|
||||
for (let i = 0, length = profiles.length; i < length; i++) {
|
||||
let profile = profiles[i];
|
||||
const profile = profiles[i];
|
||||
|
||||
if (profile.Type !== currentType) {
|
||||
html += '<li data-role="list-divider">' + profile.Type + '</li>';
|
||||
|
@ -444,7 +443,7 @@ import 'listViewStyle';
|
|||
}
|
||||
|
||||
function editContainerProfile(page, containerProfile) {
|
||||
isSubProfileNew = null == containerProfile;
|
||||
isSubProfileNew = containerProfile == null;
|
||||
containerProfile = containerProfile || {};
|
||||
currentSubProfile = containerProfile;
|
||||
const popup = $('#containerProfilePopup', page);
|
||||
|
@ -473,7 +472,7 @@ import 'listViewStyle';
|
|||
let currentType;
|
||||
|
||||
for (let i = 0, length = profiles.length; i < length; i++) {
|
||||
let profile = profiles[i];
|
||||
const profile = profiles[i];
|
||||
const type = profile.Type.replace('VideoAudio', 'Video Audio');
|
||||
|
||||
if (type !== currentType) {
|
||||
|
@ -516,7 +515,7 @@ import 'listViewStyle';
|
|||
}
|
||||
|
||||
function editCodecProfile(page, codecProfile) {
|
||||
isSubProfileNew = null == codecProfile;
|
||||
isSubProfileNew = codecProfile == null;
|
||||
codecProfile = codecProfile || {};
|
||||
currentSubProfile = codecProfile;
|
||||
const popup = $('#codecProfilePopup', page);
|
||||
|
@ -556,11 +555,11 @@ import 'listViewStyle';
|
|||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
||||
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
|
||||
|
||||
if ('Video' == profile.Type) {
|
||||
if (profile.Type == 'Video') {
|
||||
html += '<p>' + globalize.translate('ValueVideoCodec', profile.VideoCodec || allText) + '</p>';
|
||||
html += '<p>' + globalize.translate('ValueAudioCodec', profile.AudioCodec || allText) + '</p>';
|
||||
} else {
|
||||
if ('Audio' == profile.Type) {
|
||||
if (profile.Type == 'Audio') {
|
||||
html += '<p>' + globalize.translate('ValueCodec', profile.AudioCodec || allText) + '</p>';
|
||||
}
|
||||
}
|
||||
|
@ -596,7 +595,7 @@ import 'listViewStyle';
|
|||
}
|
||||
|
||||
function editResponseProfile(page, responseProfile) {
|
||||
isSubProfileNew = null == responseProfile;
|
||||
isSubProfileNew = responseProfile == null;
|
||||
responseProfile = responseProfile || {};
|
||||
currentSubProfile = responseProfile;
|
||||
const popup = $('#responseProfilePopup', page);
|
||||
|
@ -697,33 +696,33 @@ import 'listViewStyle';
|
|||
let currentProfile;
|
||||
let currentSubProfile;
|
||||
let isSubProfileNew;
|
||||
const allText = globalize.translate('LabelAll');
|
||||
const allText = globalize.translate('All');
|
||||
|
||||
$(document).on('pageinit', '#dlnaProfilePage', function () {
|
||||
const page = this;
|
||||
$('.radioTabButton', page).on('click', function () {
|
||||
$(this).siblings().removeClass('ui-btn-active');
|
||||
$(this).addClass('ui-btn-active');
|
||||
const value = 'A' == this.tagName ? this.getAttribute('data-value') : this.value;
|
||||
const value = this.tagName == 'A' ? this.getAttribute('data-value') : this.value;
|
||||
const elem = $('.' + value, page);
|
||||
elem.siblings('.tabContent').hide();
|
||||
elem.show();
|
||||
});
|
||||
$('#selectDirectPlayProfileType', page).on('change', function () {
|
||||
if ('Video' == this.value) {
|
||||
if (this.value == 'Video') {
|
||||
$('#fldDirectPlayVideoCodec', page).show();
|
||||
} else {
|
||||
$('#fldDirectPlayVideoCodec', page).hide();
|
||||
}
|
||||
|
||||
if ('Photo' == this.value) {
|
||||
if (this.value == 'Photo') {
|
||||
$('#fldDirectPlayAudioCodec', page).hide();
|
||||
} else {
|
||||
$('#fldDirectPlayAudioCodec', page).show();
|
||||
}
|
||||
});
|
||||
$('#selectTranscodingProfileType', page).on('change', function () {
|
||||
if ('Video' == this.value) {
|
||||
if (this.value == 'Video') {
|
||||
$('#fldTranscodingVideoCodec', page).show();
|
||||
$('#fldTranscodingProtocol', page).show();
|
||||
$('#fldEnableMpegtsM2TsMode', page).show();
|
||||
|
@ -733,7 +732,7 @@ import 'listViewStyle';
|
|||
$('#fldEnableMpegtsM2TsMode', page).hide();
|
||||
}
|
||||
|
||||
if ('Photo' == this.value) {
|
||||
if (this.value == 'Photo') {
|
||||
$('#fldTranscodingAudioCodec', page).hide();
|
||||
$('#fldEstimateContentLength', page).hide();
|
||||
$('#fldReportByteRangeRequests', page).hide();
|
||||
|
@ -744,13 +743,13 @@ import 'listViewStyle';
|
|||
}
|
||||
});
|
||||
$('#selectResponseProfileType', page).on('change', function () {
|
||||
if ('Video' == this.value) {
|
||||
if (this.value == 'Video') {
|
||||
$('#fldResponseProfileVideoCodec', page).show();
|
||||
} else {
|
||||
$('#fldResponseProfileVideoCodec', page).hide();
|
||||
}
|
||||
|
||||
if ('Photo' == this.value) {
|
||||
if (this.value == 'Photo') {
|
||||
$('#fldResponseProfileAudioCodec', page).hide();
|
||||
} else {
|
||||
$('#fldResponseProfileAudioCodec', page).show();
|
||||
|
|
32
src/controllers/dashboard/dlna/profiles.html
Normal file
32
src/controllers/dashboard/dlna/profiles.html
Normal file
|
@ -0,0 +1,32 @@
|
|||
<div id="dlnaProfilesPage" data-role="page" class="page type-interior dlnaPage withTabs">
|
||||
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
|
||||
<div class="readOnlyContent">
|
||||
|
||||
<div class="verticalSection verticalSection-extrabottompadding">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${HeaderCustomDlnaProfiles}</h2>
|
||||
<a is="emby-linkbutton" href="dlnaprofile.html" class="fab submit" style="margin:0 0 0 1em">
|
||||
<span class="material-icons add"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p>${CustomDlnaProfilesHelp}</p>
|
||||
<div class="customProfiles"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${HeaderSystemDlnaProfiles}</h2>
|
||||
</div>
|
||||
|
||||
<p>${SystemDlnaProfilesHelp}</p>
|
||||
<div class="systemProfiles"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -18,13 +18,13 @@ import 'emby-button';
|
|||
|
||||
function renderUserProfiles(page, profiles) {
|
||||
renderProfiles(page, page.querySelector('.customProfiles'), profiles.filter(function (p) {
|
||||
return 'User' == p.Type;
|
||||
return p.Type == 'User';
|
||||
}));
|
||||
}
|
||||
|
||||
function renderSystemProfiles(page, profiles) {
|
||||
renderProfiles(page, page.querySelector('.systemProfiles'), profiles.filter(function (p) {
|
||||
return 'System' == p.Type;
|
||||
return p.Type == 'System';
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ import 'emby-button';
|
|||
}
|
||||
|
||||
for (let i = 0, length = profiles.length; i < length; i++) {
|
||||
let profile = profiles[i];
|
||||
const profile = profiles[i];
|
||||
html += '<div class="listItem listItem-border">';
|
||||
html += '<span class="listItemIcon material-icons live_tv"></span>';
|
||||
html += '<div class="listItemBody two-line">';
|
||||
|
@ -45,8 +45,8 @@ import 'emby-button';
|
|||
html += '</a>';
|
||||
html += '</div>';
|
||||
|
||||
if ('User' == profile.Type) {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile" data-profileid="' + profile.Id + '" title="' + globalize.translate('ButtonDelete') + '"><span class="material-icons delete"></span></button>';
|
||||
if (profile.Type == 'User') {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile" data-profileid="' + profile.Id + '" title="' + globalize.translate('Delete') + '"><span class="material-icons delete"></span></button>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
@ -81,7 +81,7 @@ import 'emby-button';
|
|||
function getTabs() {
|
||||
return [{
|
||||
href: 'dlnasettings.html',
|
||||
name: globalize.translate('TabSettings')
|
||||
name: globalize.translate('Settings')
|
||||
}, {
|
||||
href: 'dlnaprofiles.html',
|
||||
name: globalize.translate('TabProfiles')
|
||||
|
|
69
src/controllers/dashboard/dlna/settings.html
Normal file
69
src/controllers/dashboard/dlna/settings.html
Normal file
|
@ -0,0 +1,69 @@
|
|||
<div id="dlnaSettingsPage" data-role="page" class="page type-interior withTabs">
|
||||
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
|
||||
<form class="dlnaSettingsForm">
|
||||
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${Settings}</h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/networking/dlna.html">${Help}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnablePlayTo" />
|
||||
<span>${LabelEnableDlnaPlayTo}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableDlnaPlayToHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableDlnaDebugLogging" />
|
||||
<span>${LabelEnableDlnaDebugLogging}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableDlnaDebugLoggingHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtClientDiscoveryInterval" min="1" max="300" label="${LabelEnableDlnaClientDiscoveryInterval}" />
|
||||
<div class="fieldDescription">${LabelEnableDlnaClientDiscoveryIntervalHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableServer" />
|
||||
<span>${LabelEnableDlnaServer}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableDlnaServerHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkBlastAliveMessages" />
|
||||
<span>${LabelEnableBlastAliveMessages}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableBlastAliveMessagesHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtBlastInterval" min="1" max="3600" label="${LabelBlastMessageInterval}" />
|
||||
<div class="fieldDescription">${LabelBlastMessageIntervalHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectUser" data-mini="true" label="${LabelDefaultUser}"></select>
|
||||
<div class="fieldDescription">${LabelDefaultUserHelp}</div>
|
||||
</div>
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -38,7 +38,7 @@ import globalize from 'globalize';
|
|||
function getTabs() {
|
||||
return [{
|
||||
href: 'dlnasettings.html',
|
||||
name: globalize.translate('TabSettings')
|
||||
name: globalize.translate('Settings')
|
||||
}, {
|
||||
href: 'dlnaprofiles.html',
|
||||
name: globalize.translate('TabProfiles')
|
||||
|
|
185
src/controllers/dashboard/encodingsettings.html
Normal file
185
src/controllers/dashboard/encodingsettings.html
Normal file
|
@ -0,0 +1,185 @@
|
|||
<div id="encodingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage withTabs">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<form class="encodingSettingsForm">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${Transcoding}</h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/transcoding.html">${Help}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectVideoDecoder" label="${LabelHardwareAccelerationType}">
|
||||
<option value="">${None}</option>
|
||||
<option value="amf">AMD AMF</option>
|
||||
<option value="qsv">Intel Quick Sync</option>
|
||||
<option value="mediacodec">MediaCodec Android</option>
|
||||
<option value="omx">OpenMAX OMX</option>
|
||||
<option value="nvenc">Nvidia NVENC</option>
|
||||
<option value="vaapi">Video Acceleration API (VAAPI)</option>
|
||||
<option value="h264_v4l2m2m">Exynos V4L2 MFC</option>
|
||||
<option value="videotoolbox">Video ToolBox</option>
|
||||
</select>
|
||||
<div class="fieldDescription">
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="https://docs.jellyfin.org/general/administration/hardware-acceleration.html" target="_blank">${LabelHardwareAccelerationTypeHelp}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer hide fldVaapiDevice">
|
||||
<input is="emby-input" type="text" id="txtVaapiDevice" label="${LabelVaapiDevice}" />
|
||||
<div class="fieldDescription">${LabelVaapiDeviceHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="hardwareAccelerationOptions hide">
|
||||
<div class="checkboxListContainer decodingCodecsList">
|
||||
<h3 class="checkboxListLabel">${LabelEnableHardwareDecodingFor}</h3>
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="h264" data-types="amf,qsv,nvenc,vaapi,omx,mediacodec,videotoolbox" />
|
||||
<span>H264</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="hevc" data-types="amf,qsv,nvenc,vaapi,mediacodec,videotoolbox" />
|
||||
<span>HEVC</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="mpeg2video" data-types="amf,qsv,nvenc,vaapi,omx,mediacodec,videotoolbox" />
|
||||
<span>MPEG2</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="mpeg4" data-types="amf,nvenc,omx,mediacodec,videotoolbox" />
|
||||
<span>MPEG4</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="vc1" data-types="amf,qsv,nvenc,vaapi,omx,videotoolbox" />
|
||||
<span>VC1</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="vp8" data-types="qsv,nvenc,vaapi,mediacodec,videotoolbox" />
|
||||
<span>VP8</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="vp9" data-types="amf,qsv,nvenc,vaapi,mediacodec,videotoolbox" />
|
||||
<span>VP9</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxListContainer">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkDecodingColorDepth10Hevc" />
|
||||
<span>${EnableDecodingColorDepth10Hevc}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkboxListContainer">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkDecodingColorDepth10Vp9" />
|
||||
<span>${EnableDecodingColorDepth10Vp9}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkboxListContainer">
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkHardwareEncoding" />
|
||||
<span>${EnableHardwareEncoding}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectThreadCount" label="${LabelTranscodingThreadCount}">
|
||||
<option value="-1">${Auto}</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="0">${OptionMax}</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${LabelTranscodingThreadCountHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer fldEncoderPath">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="flex-grow:1;">
|
||||
<input is="emby-input" class="txtEncoderPath" label="${LabelffmpegPath}" autocomplete="off" />
|
||||
</div>
|
||||
<button type="button" is="paper-icon-button-light" id="btnSelectEncoderPath" class="emby-input-iconbutton"><span class="material-icons search"></span></button>
|
||||
</div>
|
||||
<div class="fieldDescription">
|
||||
<div>${LabelffmpegPathHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="flex-grow:1;">
|
||||
<input is="emby-input" id="txtTranscodingTempPath" label="${LabelTranscodePath}" autocomplete="off" />
|
||||
</div>
|
||||
<button type="button" is="paper-icon-button-light" id="btnSelectTranscodingTempPath" class="emby-input-iconbutton"><span class="material-icons search"></span></button>
|
||||
</div>
|
||||
<div class="fieldDescription">${LabelTranscodingTempPathHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtDownMixAudioBoost" pattern="[0-9]*" required="required" min=".5" max="3" step=".1" label="${LabelDownMixAudioScale}" />
|
||||
<div class="fieldDescription">${LabelDownMixAudioScaleHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectEncoderPreset" label="${LabelEncoderPreset}">
|
||||
<option value="">${Auto}</option>
|
||||
<option value="veryslow">veryslow</option>
|
||||
<option value="slower">slower</option>
|
||||
<option value="slow">slow</option>
|
||||
<option value="medium">medium</option>
|
||||
<option value="fast">fast</option>
|
||||
<option value="faster">faster</option>
|
||||
<option value="veryfast">veryfast</option>
|
||||
<option value="superfast">superfast</option>
|
||||
<option value="ultrafast">ultrafast</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${EncoderPresetHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtH264Crf" pattern="[0-9]*" min="0" max="51" step="1" label="${LabelH264Crf}" />
|
||||
<div class="fieldDescription">${H264CrfHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectDeinterlaceMethod" label="${LabelDeinterlaceMethod}">
|
||||
<option value="yadif">${Yadif}</option>
|
||||
<option value="yadif_bob">${YadifBob}</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${DeinterlaceMethodHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableSubtitleExtraction" />
|
||||
<span>${AllowOnTheFlySubtitleExtraction}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${AllowOnTheFlySubtitleExtractionHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableThrottling" />
|
||||
<span>${AllowFfmpegThrottling}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${AllowFfmpegThrottlingHelp}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -8,7 +8,7 @@ import libraryMenu from 'libraryMenu';
|
|||
|
||||
function loadPage(page, config, systemInfo) {
|
||||
Array.prototype.forEach.call(page.querySelectorAll('.chkDecodeCodec'), function (c) {
|
||||
c.checked = -1 !== (config.HardwareDecodingCodecs || []).indexOf(c.getAttribute('data-codec'));
|
||||
c.checked = (config.HardwareDecodingCodecs || []).indexOf(c.getAttribute('data-codec')) !== -1;
|
||||
});
|
||||
page.querySelector('#chkDecodingColorDepth10Hevc').checked = config.EnableDecodingColorDepth10Hevc;
|
||||
page.querySelector('#chkDecodingColorDepth10Vp9').checked = config.EnableDecodingColorDepth10Vp9;
|
||||
|
@ -45,10 +45,11 @@ import libraryMenu from 'libraryMenu';
|
|||
return ApiClient.ajax({
|
||||
url: ApiClient.getUrl('System/MediaEncoder/Path'),
|
||||
type: 'POST',
|
||||
data: {
|
||||
data: JSON.stringify({
|
||||
Path: form.querySelector('.txtEncoderPath').value,
|
||||
PathType: 'Custom'
|
||||
}
|
||||
}),
|
||||
contentType: 'application/json'
|
||||
}).then(Dashboard.processServerConfigurationUpdateResult, onSaveEncodingPathFailure);
|
||||
});
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ import libraryMenu from 'libraryMenu';
|
|||
updateEncoder(form);
|
||||
}, function () {
|
||||
import('alert').then(({default: alert}) => {
|
||||
alert(globalize.translate('DefaultErrorMessage'));
|
||||
alert(globalize.translate('ErrorDefault'));
|
||||
});
|
||||
|
||||
Dashboard.processServerConfigurationUpdateResult();
|
||||
|
@ -107,7 +108,7 @@ import libraryMenu from 'libraryMenu';
|
|||
value = value || '';
|
||||
let any;
|
||||
Array.prototype.forEach.call(context.querySelectorAll('.chkDecodeCodec'), function (c) {
|
||||
if (-1 === c.getAttribute('data-types').split(',').indexOf(value)) {
|
||||
if (c.getAttribute('data-types').split(',').indexOf(value) === -1) {
|
||||
dom.parentWithTag(c, 'LABEL').classList.add('hide');
|
||||
} else {
|
||||
dom.parentWithTag(c, 'LABEL').classList.remove('hide');
|
||||
|
@ -128,7 +129,7 @@ import libraryMenu from 'libraryMenu';
|
|||
name: globalize.translate('Transcoding')
|
||||
}, {
|
||||
href: 'playbackconfiguration.html',
|
||||
name: globalize.translate('TabResumeSettings')
|
||||
name: globalize.translate('ButtonResume')
|
||||
}, {
|
||||
href: 'streamingsettings.html',
|
||||
name: globalize.translate('TabStreaming')
|
||||
|
@ -138,7 +139,7 @@ import libraryMenu from 'libraryMenu';
|
|||
$(document).on('pageinit', '#encodingSettingsPage', function () {
|
||||
const page = this;
|
||||
page.querySelector('#selectVideoDecoder').addEventListener('change', function () {
|
||||
if ('vaapi' == this.value) {
|
||||
if (this.value == 'vaapi') {
|
||||
page.querySelector('.fldVaapiDevice').classList.remove('hide');
|
||||
page.querySelector('#txtVaapiDevice').setAttribute('required', 'required');
|
||||
} else {
|
||||
|
|
72
src/controllers/dashboard/general.html
Normal file
72
src/controllers/dashboard/general.html
Normal file
|
@ -0,0 +1,72 @@
|
|||
<div id="dashboardGeneralPage" data-role="page" class="page type-interior dashboardHomePage">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<form class="dashboardGeneralForm">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${Settings}</h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/settings.html">${Help}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="verticalSection">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtServerName" label="${LabelServerName}" />
|
||||
<div class="fieldDescription">${LabelServerNameHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectLocalizationLanguage" label="${LabelPreferredDisplayLanguage}"></select>
|
||||
<div class="fieldDescription">
|
||||
<div>${LabelDisplayLanguageHelp}</div>
|
||||
<div style="margin-top: .25em;">
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="https://docs.jellyfin.org/general/contributing/index.html" target="_blank">${LearnHowYouCanContribute}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="verticalSection verticalSection-extrabottompadding">
|
||||
<h2>${HeaderPaths}</h2>
|
||||
<div class="inputContainer">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="flex-grow:1;">
|
||||
<input is="emby-input" id="txtCachePath" label="${LabelCachePath}" autocomplete="off" />
|
||||
</div>
|
||||
<button type="button" is="paper-icon-button-light" id="btnSelectCachePath" title="${ButtonSelectDirectory}" class="emby-input-iconbutton"><span class="material-icons search"></span></button>
|
||||
</div>
|
||||
<div class="fieldDescription">${LabelCachePathHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="flex-grow:1;">
|
||||
<input is="emby-input" id="txtMetadataPath" label="${LabelMetadataPath}" autocomplete="off" />
|
||||
</div>
|
||||
<button type="button" is="paper-icon-button-light" id="btnSelectMetadataPath" title="${ButtonSelectDirectory}" class="emby-input-iconbutton"><span class="material-icons search"></span></button>
|
||||
</div>
|
||||
<div class="fieldDescription">${LabelMetadataPathHelp}</div>
|
||||
<input type="hidden" id="txtMetadataNetworkPath" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="verticalSection">
|
||||
<h2>${HeaderBranding}</h2>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtLoginDisclaimer" label="${LabelLoginDisclaimer}" />
|
||||
<div class="fieldDescription">${LabelLoginDisclaimerHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer customCssContainer">
|
||||
<textarea is="emby-textarea" id="txtCustomCss" label="${LabelCustomCss}" class="textarea-mono"></textarea>
|
||||
<div class="fieldDescription">${LabelCustomCssHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -52,7 +52,7 @@ import 'emby-button';
|
|||
});
|
||||
}, function () {
|
||||
import('alert').then(({default: alert}) => {
|
||||
alert(globalize.translate('DefaultErrorMessage'));
|
||||
alert(globalize.translate('ErrorDefault'));
|
||||
});
|
||||
|
||||
Dashboard.processServerConfigurationUpdateResult();
|
||||
|
|
15
src/controllers/dashboard/library.html
Normal file
15
src/controllers/dashboard/library.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<div id="mediaLibraryPage" data-role="page" class="page type-interior mediaLibraryPage librarySectionPage withTabs fullWidthContent">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="padded-top padded-bottom">
|
||||
<button is="emby-button" type="button" class="raised btnRefresh">
|
||||
<span>${ButtonScanAllLibraries}</span>
|
||||
</button>
|
||||
<progress max="100" min="0" style="display: inline-block; vertical-align: middle;" class="refreshProgress"></progress>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt" target="_blank" href="https://docs.jellyfin.org/general/server/libraries.html">${Help}</a>
|
||||
</div>
|
||||
|
||||
<div id="divVirtualFolders"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,11 +1,9 @@
|
|||
import $ from 'jQuery';
|
||||
import appHost from 'apphost';
|
||||
import taskButton from 'scripts/taskbutton';
|
||||
import loading from 'loading';
|
||||
import libraryMenu from 'libraryMenu';
|
||||
import globalize from 'globalize';
|
||||
import dom from 'dom';
|
||||
import indicators from 'indicators';
|
||||
import imageHelper from 'scripts/imagehelper';
|
||||
import 'cardStyle';
|
||||
import 'emby-itemrefreshindicator';
|
||||
|
@ -95,7 +93,7 @@ import 'emby-itemrefreshindicator';
|
|||
const virtualFolder = virtualFolders[index];
|
||||
const menuItems = [];
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonEditImages'),
|
||||
name: globalize.translate('EditImages'),
|
||||
id: 'editimages',
|
||||
icon: 'photo'
|
||||
});
|
||||
|
@ -158,7 +156,7 @@ import 'emby-itemrefreshindicator';
|
|||
}
|
||||
|
||||
function shouldRefreshLibraryAfterChanges(page) {
|
||||
return 'mediaLibraryPage' === page.id;
|
||||
return page.id === 'mediaLibraryPage';
|
||||
}
|
||||
|
||||
function reloadVirtualFolders(page, virtualFolders) {
|
||||
|
@ -220,29 +218,29 @@ import 'emby-itemrefreshindicator';
|
|||
name: '',
|
||||
value: ''
|
||||
}, {
|
||||
name: globalize.translate('FolderTypeMovies'),
|
||||
name: globalize.translate('Movies'),
|
||||
value: 'movies',
|
||||
message: getLink('MovieLibraryHelp', 'https://docs.jellyfin.org/general/server/media/movies.html')
|
||||
}, {
|
||||
name: globalize.translate('FolderTypeMusic'),
|
||||
name: globalize.translate('TabMusic'),
|
||||
value: 'music',
|
||||
message: getLink('MusicLibraryHelp', 'https://docs.jellyfin.org/general/server/media/music.html')
|
||||
}, {
|
||||
name: globalize.translate('FolderTypeTvShows'),
|
||||
name: globalize.translate('Shows'),
|
||||
value: 'tvshows',
|
||||
message: getLink('TvLibraryHelp', 'https://docs.jellyfin.org/general/server/media/shows.html')
|
||||
}, {
|
||||
name: globalize.translate('FolderTypeBooks'),
|
||||
name: globalize.translate('Books'),
|
||||
value: 'books',
|
||||
message: getLink('BookLibraryHelp', 'https://docs.jellyfin.org/general/server/media/books.html')
|
||||
}, {
|
||||
name: globalize.translate('OptionHomeVideos'),
|
||||
name: globalize.translate('Photos'),
|
||||
value: 'homevideos'
|
||||
}, {
|
||||
name: globalize.translate('FolderTypeMusicVideos'),
|
||||
name: globalize.translate('MusicVideos'),
|
||||
value: 'musicvideos'
|
||||
}, {
|
||||
name: globalize.translate('FolderTypeUnset'),
|
||||
name: globalize.translate('Other'),
|
||||
value: 'mixed',
|
||||
message: globalize.translate('MessageUnsetContentHelp')
|
||||
}];
|
||||
|
@ -288,7 +286,7 @@ import 'emby-itemrefreshindicator';
|
|||
|
||||
if (hasCardImageContainer) {
|
||||
html += '<div class="cardIndicators backdropCardIndicators">';
|
||||
html += '<div is="emby-itemrefreshindicator"' + (virtualFolder.RefreshProgress || virtualFolder.RefreshStatus && 'Idle' !== virtualFolder.RefreshStatus ? '' : ' class="hide"') + ' data-progress="' + (virtualFolder.RefreshProgress || 0) + '" data-status="' + virtualFolder.RefreshStatus + '"></div>';
|
||||
html += '<div is="emby-itemrefreshindicator"' + (virtualFolder.RefreshProgress || virtualFolder.RefreshStatus && virtualFolder.RefreshStatus !== 'Idle' ? '' : ' class="hide"') + ' data-progress="' + (virtualFolder.RefreshProgress || 0) + '" data-status="' + virtualFolder.RefreshStatus + '"></div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
@ -328,7 +326,7 @@ import 'emby-itemrefreshindicator';
|
|||
let typeName = getCollectionTypeOptions().filter(function (t) {
|
||||
return t.value == virtualFolder.CollectionType;
|
||||
})[0];
|
||||
typeName = typeName ? typeName.name : globalize.translate('FolderTypeUnset');
|
||||
typeName = typeName ? typeName.name : globalize.translate('Other');
|
||||
html += "<div class='cardText cardText-secondary'>";
|
||||
|
||||
if (virtualFolder.showType === false) {
|
||||
|
@ -365,10 +363,10 @@ import 'emby-itemrefreshindicator';
|
|||
name: globalize.translate('HeaderLibraries')
|
||||
}, {
|
||||
href: 'librarydisplay.html',
|
||||
name: globalize.translate('TabDisplay')
|
||||
name: globalize.translate('Display')
|
||||
}, {
|
||||
href: 'metadataimages.html',
|
||||
name: globalize.translate('TabMetadata')
|
||||
name: globalize.translate('Metadata')
|
||||
}, {
|
||||
href: 'metadatanfo.html',
|
||||
name: globalize.translate('TabNfoSettings')
|
||||
|
@ -395,7 +393,6 @@ import 'emby-itemrefreshindicator';
|
|||
});
|
||||
});
|
||||
pageIdOn('pagebeforehide', 'mediaLibraryPage', function () {
|
||||
|
||||
const page = this;
|
||||
taskButton({
|
||||
mode: 'off',
|
57
src/controllers/dashboard/librarydisplay.html
Normal file
57
src/controllers/dashboard/librarydisplay.html
Normal file
|
@ -0,0 +1,57 @@
|
|||
<div id="libraryDisplayPage" data-role="page" class="page type-interior librarySectionPage withTabs">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<form>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectDateAdded" data-mini="true" label="${LabelDateAddedBehavior}">
|
||||
<option value="0">${OptionDateAddedImportTime}</option>
|
||||
<option value="1">${OptionDateAddedFileTime}</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${LabelDateAddedBehaviorHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFolderView" />
|
||||
<span>${OptionDisplayFolderView}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionDisplayFolderViewHelp}</div>
|
||||
</div>
|
||||
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDisplaySpecialsWithinSeasons"/>
|
||||
<span>${LabelDisplaySpecialsWithinSeasons}</span>
|
||||
</label>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkGroupMoviesIntoCollections" />
|
||||
<span>${LabelGroupMoviesIntoCollections}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelGroupMoviesIntoCollectionsHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input class="chkExternalContentInSuggestions" type="checkbox" is="emby-checkbox" />
|
||||
<span>${OptionEnableExternalContentInSuggestions}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionEnableExternalContentInSuggestionsHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription fldSaveMetadataHidden hide">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkSaveMetadataHidden" data-filter="Sunday" />
|
||||
<span>${OptionSaveMetadataAsHidden}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionSaveMetadataAsHiddenHelp}</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -12,10 +12,10 @@ import 'emby-button';
|
|||
name: globalize.translate('HeaderLibraries')
|
||||
}, {
|
||||
href: 'librarydisplay.html',
|
||||
name: globalize.translate('TabDisplay')
|
||||
name: globalize.translate('Display')
|
||||
}, {
|
||||
href: 'metadataimages.html',
|
||||
name: globalize.translate('TabMetadata')
|
||||
name: globalize.translate('Metadata')
|
||||
}, {
|
||||
href: 'metadatanfo.html',
|
||||
name: globalize.translate('TabNfoSettings')
|
||||
|
@ -48,7 +48,7 @@ import 'emby-button';
|
|||
ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult);
|
||||
});
|
||||
ApiClient.getNamedConfiguration('metadata').then(function(config) {
|
||||
config.UseFileCreationTimeForDateAdded = '1' === $('#selectDateAdded', form).val();
|
||||
config.UseFileCreationTimeForDateAdded = $('#selectDateAdded', form).val() === '1';
|
||||
ApiClient.updateNamedConfiguration('metadata', config);
|
||||
});
|
||||
|
||||
|
@ -61,7 +61,7 @@ import 'emby-button';
|
|||
libraryMenu.setTabs('librarysetup', 1, getTabs);
|
||||
loadData();
|
||||
ApiClient.getSystemInfo().then(function(info) {
|
||||
if ('Windows' === info.OperatingSystem) {
|
||||
if (info.OperatingSystem === 'Windows') {
|
||||
view.querySelector('.fldSaveMetadataHidden').classList.remove('hide');
|
||||
} else {
|
||||
view.querySelector('.fldSaveMetadataHidden').classList.add('hide');
|
||||
|
|
8
src/controllers/dashboard/logs.html
Normal file
8
src/controllers/dashboard/logs.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
<div id="logPage" data-role="page" class="page type-interior">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="serverLogs readOnlyContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,5 +1,4 @@
|
|||
import $ from 'jQuery';
|
||||
import dom from 'dom';
|
||||
import loading from 'loading';
|
||||
import libraryMenu from 'libraryMenu';
|
||||
import globalize from 'globalize';
|
||||
|
@ -56,10 +55,10 @@ import 'listViewStyle';
|
|||
name: globalize.translate('HeaderLibraries')
|
||||
}, {
|
||||
href: 'librarydisplay.html',
|
||||
name: globalize.translate('TabDisplay')
|
||||
name: globalize.translate('Display')
|
||||
}, {
|
||||
href: 'metadataimages.html',
|
||||
name: globalize.translate('TabMetadata')
|
||||
name: globalize.translate('Metadata')
|
||||
}, {
|
||||
href: 'metadatanfo.html',
|
||||
name: globalize.translate('TabNfoSettings')
|
||||
|
|
25
src/controllers/dashboard/metadataimages.html
Normal file
25
src/controllers/dashboard/metadataimages.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
<div id="metadataImagesConfigurationPage" data-role="page" class="page type-interior metadataConfigurationPage withTabs">
|
||||
|
||||
<div>
|
||||
|
||||
<div class="content-primary">
|
||||
|
||||
<form class="metadataImagesConfigurationForm">
|
||||
<h2 style="margin-top:0;">${HeaderPreferredMetadataLanguage}</h2>
|
||||
|
||||
<p style="margin:1.5em 0;">${DefaultMetadataLangaugeDescription}</p>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectLanguage" required="required" label="${LabelLanguage}"></select>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectCountry" required="required" label="${LabelCountry}"></select>
|
||||
</div>
|
||||
<br />
|
||||
<div><button is="emby-button" type="submit" class="raised button-submit block"><span>${Save}</span></button></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
49
src/controllers/dashboard/metadatanfo.html
Normal file
49
src/controllers/dashboard/metadatanfo.html
Normal file
|
@ -0,0 +1,49 @@
|
|||
<div id="metadataNfoPage" data-role="page" class="page type-interior metadataConfigurationPage withTabs">
|
||||
|
||||
<div>
|
||||
|
||||
<div class="content-primary">
|
||||
<form class="metadataNfoForm">
|
||||
|
||||
<p>${HeaderKodiMetadataHelp}</p>
|
||||
<br />
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" name="selectUser" id="selectUser" label="${LabelKodiMetadataUser}"></select>
|
||||
<div class="fieldDescription">${LabelKodiMetadataUserHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" name="selectReleaseDateFormat" id="selectReleaseDateFormat" label="${LabelKodiMetadataDateFormat}">
|
||||
<option value="yyyy-MM-dd">yyyy-MM-dd</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${LabelKodiMetadataDateFormatHelp}</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkSaveImagePaths" />
|
||||
<span>${LabelKodiMetadataSaveImagePaths}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelKodiMetadataSaveImagePathsHelp}</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnablePathSubstitution" />
|
||||
<span>${LabelKodiMetadataEnablePathSubstitution}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">
|
||||
<div>${LabelKodiMetadataEnablePathSubstitutionHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableExtraThumbs" />
|
||||
<span>${LabelKodiMetadataEnableExtraThumbs}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelKodiMetadataEnableExtraThumbsHelp}</div>
|
||||
</div>
|
||||
<div><button is="emby-button" type="submit" class="raised button-submit block"><span>${Save}</span></button></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -6,7 +6,7 @@ import globalize from 'globalize';
|
|||
/* eslint-disable indent */
|
||||
|
||||
function loadPage(page, config, users) {
|
||||
let html = '<option value="" selected="selected">' + globalize.translate('OptionNone') + '</option>';
|
||||
let html = '<option value="" selected="selected">' + globalize.translate('None') + '</option>';
|
||||
html += users.map(function (user) {
|
||||
return '<option value="' + user.Id + '">' + user.Name + '</option>';
|
||||
}).join('');
|
||||
|
@ -52,10 +52,10 @@ import globalize from 'globalize';
|
|||
name: globalize.translate('HeaderLibraries')
|
||||
}, {
|
||||
href: 'librarydisplay.html',
|
||||
name: globalize.translate('TabDisplay')
|
||||
name: globalize.translate('Display')
|
||||
}, {
|
||||
href: 'metadataimages.html',
|
||||
name: globalize.translate('TabMetadata')
|
||||
name: globalize.translate('Metadata')
|
||||
}, {
|
||||
href: 'metadatanfo.html',
|
||||
name: globalize.translate('TabNfoSettings')
|
||||
|
|
121
src/controllers/dashboard/networking.html
Normal file
121
src/controllers/dashboard/networking.html
Normal file
|
@ -0,0 +1,121 @@
|
|||
<div id="networkingPage" data-role="page" class="page type-interior advancedConfigurationPage">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<form class="dashboardHostingForm">
|
||||
<div class="verticalSection verticalSection-extrabottompadding">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${TabNetworking}</h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/networking/index.html">${Help}</a>
|
||||
</div>
|
||||
|
||||
<fieldset class='verticalSection verticalSection-extrabottompadding'>
|
||||
<legend><h3>${HeaderServerAddressSettings}</h3></legend>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtPortNumber" label="${LabelLocalHttpServerPortNumber}" pattern="[0-9]*" required="required" min="1" max="65535" />
|
||||
<div class="fieldDescription">${LabelLocalHttpServerPortNumberHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableHttps" />
|
||||
<span>${LabelEnableHttps}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableHttpsHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtHttpsPort" pattern="[0-9]*" required="required" min="1" max="65535" label="${LabelHttpsPort}" />
|
||||
<div class="fieldDescription">${LabelHttpsPortHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer fldBaseUrl">
|
||||
<input is="emby-input" id="txtBaseUrl" type="text" label="${LabelBaseUrl}" />
|
||||
<div class="fieldDescription">${LabelBaseUrlHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtLocalAddress" label="${LabelBindToLocalNetworkAddress}" />
|
||||
<div class="fieldDescription">${LabelBindToLocalNetworkAddressHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtLanNetworks" label="${LabelLanNetworks}" />
|
||||
<div class="fieldDescription">${LanNetworksHelp}</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class='verticalSection verticalSection-extrabottompadding'>
|
||||
<legend><h3>${HeaderHttpsSettings}</h3></legend>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkRequireHttps" />
|
||||
<span>${LabelRequireHttps}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelRequireHttpsHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer fldCertificatePath">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="flex-grow:1;">
|
||||
<input is="emby-input" type="text" id="txtCertificatePath" label="${LabelCustomCertificatePath}" autocomplete="off" />
|
||||
</div>
|
||||
<button type="button" is="paper-icon-button-light" id="btnSelectCertPath" title="${ButtonSelectDirectory}" class="emby-input-iconbutton"><span class="material-icons search"></span></button>
|
||||
</div>
|
||||
<div class="fieldDescription">${LabelCustomCertificatePathHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer fldCertPassword">
|
||||
<input is="emby-input" id="txtCertPassword" type="password" label="${LabelCertificatePassword}" autocomplete="new-password" />
|
||||
<div class="fieldDescription">${LabelCertificatePasswordHelp}</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class='verticalSection verticalSection-extrabottompadding'>
|
||||
<legend><h3>${HeaderRemoteAccessSettings}</h3></legend>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkRemoteAccess" />
|
||||
<span>${AllowRemoteAccess}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${AllowRemoteAccessHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer fldExternalAddressFilter hide">
|
||||
<input is="emby-input" type="text" id="txtExternalAddressFilter" label="${LabelAllowedRemoteAddresses}" />
|
||||
<div class="fieldDescription">${AllowedRemoteAddressesHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer fldExternalAddressFilterMode hide">
|
||||
<select is="emby-select" id="selectExternalAddressFilterMode" label="${LabelAllowedRemoteAddressesMode}">
|
||||
<option value="whitelist">${Whitelist}</option>
|
||||
<option value="blacklist">${Blacklist}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription fldEnableUpnp hide">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableUpnp" />
|
||||
<span>${LabelEnableAutomaticPortMap}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableAutomaticPortMapHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer fldPublicPort hide">
|
||||
<input is="emby-input" type="number" label="${LabelPublicHttpPort}" id="txtPublicPort" pattern="[0-9]*" required="required" min="1" max="65535" />
|
||||
<div class="fieldDescription">${LabelPublicHttpPortHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer fldPublicHttpsPort hide">
|
||||
<input is="emby-input" type="number" id="txtPublicHttpsPort" pattern="[0-9]*" required="required" min="1" max="65535" label="${LabelPublicHttpsPort}" />
|
||||
<div class="fieldDescription">${LabelPublicHttpsPortHelp}</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,5 +1,4 @@
|
|||
import loading from 'loading';
|
||||
import libraryMenu from 'libraryMenu';
|
||||
import globalize from 'globalize';
|
||||
import 'emby-checkbox';
|
||||
import 'emby-select';
|
||||
|
@ -31,7 +30,7 @@ import 'emby-select';
|
|||
}).filter(function (s) {
|
||||
return s.length > 0;
|
||||
});
|
||||
config.IsRemoteIPFilterBlacklist = 'blacklist' === form.querySelector('#selectExternalAddressFilterMode').value;
|
||||
config.IsRemoteIPFilterBlacklist = form.querySelector('#selectExternalAddressFilterMode').value === 'blacklist';
|
||||
config.PublicPort = form.querySelector('#txtPublicPort').value;
|
||||
config.PublicHttpsPort = form.querySelector('#txtPublicHttpsPort').value;
|
||||
config.HttpServerPortNumber = form.querySelector('#txtPortNumber').value;
|
||||
|
@ -111,7 +110,7 @@ import 'emby-select';
|
|||
page.querySelector('#txtLanNetworks').value = (config.LocalNetworkSubnets || []).join(', ');
|
||||
page.querySelector('#txtExternalAddressFilter').value = (config.RemoteIPFilter || []).join(', ');
|
||||
page.querySelector('#selectExternalAddressFilterMode').value = config.IsRemoteIPFilterBlacklist ? 'blacklist' : 'whitelist';
|
||||
page.querySelector('#chkRemoteAccess').checked = null == config.EnableRemoteAccess || config.EnableRemoteAccess;
|
||||
page.querySelector('#chkRemoteAccess').checked = config.EnableRemoteAccess == null || config.EnableRemoteAccess;
|
||||
page.querySelector('#txtHttpsPort').value = config.HttpsPortNumber;
|
||||
page.querySelector('#chkEnableHttps').checked = config.EnableHttps;
|
||||
page.querySelector('#chkRequireHttps').checked = config.RequireHttps;
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
<div>
|
||||
<br />
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${ButtonSave}</span>
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
|
||||
<button is="emby-button" type="button" class="raised button-cancel block btnCancel" onclick="history.back();">
|
||||
|
|
|
@ -1,119 +1,118 @@
|
|||
define(['jQuery', 'emby-checkbox'], function ($) {
|
||||
'use strict';
|
||||
import $ from 'jQuery';
|
||||
import 'emby-checkbox';
|
||||
|
||||
function fillItems(elem, items, cssClass, idPrefix, currentList, isEnabledList) {
|
||||
var html = '<div class="checkboxList paperList" style="padding: .5em 1em;">';
|
||||
html += items.map(function (u) {
|
||||
var isChecked = isEnabledList ? currentList.indexOf(u.Id) != -1 : currentList.indexOf(u.Id) == -1;
|
||||
var checkedHtml = isChecked ? ' checked="checked"' : '';
|
||||
return '<label><input is="emby-checkbox" class="' + cssClass + '" type="checkbox" data-itemid="' + u.Id + '"' + checkedHtml + '/><span>' + u.Name + '</span></label>';
|
||||
}).join('');
|
||||
html += '</div>';
|
||||
elem.html(html).trigger('create');
|
||||
}
|
||||
function fillItems(elem, items, cssClass, idPrefix, currentList, isEnabledList) {
|
||||
let html = '<div class="checkboxList paperList" style="padding: .5em 1em;">';
|
||||
html += items.map(function (u) {
|
||||
const isChecked = isEnabledList ? currentList.indexOf(u.Id) != -1 : currentList.indexOf(u.Id) == -1;
|
||||
const checkedHtml = isChecked ? ' checked="checked"' : '';
|
||||
return '<label><input is="emby-checkbox" class="' + cssClass + '" type="checkbox" data-itemid="' + u.Id + '"' + checkedHtml + '/><span>' + u.Name + '</span></label>';
|
||||
}).join('');
|
||||
html += '</div>';
|
||||
elem.html(html).trigger('create');
|
||||
}
|
||||
|
||||
function reload(page) {
|
||||
var type = getParameterByName('type');
|
||||
var promise1 = ApiClient.getUsers();
|
||||
var promise2 = ApiClient.getNamedConfiguration(notificationsConfigurationKey);
|
||||
var promise3 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types'));
|
||||
var promise4 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Services'));
|
||||
Promise.all([promise1, promise2, promise3, promise4]).then(function (responses) {
|
||||
var users = responses[0];
|
||||
var notificationOptions = responses[1];
|
||||
var types = responses[2];
|
||||
var services = responses[3];
|
||||
var notificationConfig = notificationOptions.Options.filter(function (n) {
|
||||
return n.Type == type;
|
||||
})[0];
|
||||
var typeInfo = types.filter(function (n) {
|
||||
return n.Type == type;
|
||||
})[0] || {};
|
||||
function reload(page) {
|
||||
const type = getParameterByName('type');
|
||||
const promise1 = ApiClient.getUsers();
|
||||
const promise2 = ApiClient.getNamedConfiguration(notificationsConfigurationKey);
|
||||
const promise3 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types'));
|
||||
const promise4 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Services'));
|
||||
Promise.all([promise1, promise2, promise3, promise4]).then(function (responses) {
|
||||
const users = responses[0];
|
||||
const notificationOptions = responses[1];
|
||||
const types = responses[2];
|
||||
const services = responses[3];
|
||||
let notificationConfig = notificationOptions.Options.filter(function (n) {
|
||||
return n.Type == type;
|
||||
})[0];
|
||||
const typeInfo = types.filter(function (n) {
|
||||
return n.Type == type;
|
||||
})[0] || {};
|
||||
|
||||
if (typeInfo.IsBasedOnUserEvent) {
|
||||
$('.monitorUsers', page).show();
|
||||
} else {
|
||||
$('.monitorUsers', page).hide();
|
||||
}
|
||||
if (typeInfo.IsBasedOnUserEvent) {
|
||||
$('.monitorUsers', page).show();
|
||||
} else {
|
||||
$('.monitorUsers', page).hide();
|
||||
}
|
||||
|
||||
$('.notificationType', page).html(typeInfo.Name || 'Unknown Notification');
|
||||
$('.notificationType', page).html(typeInfo.Name || 'Unknown Notification');
|
||||
|
||||
if (!notificationConfig) {
|
||||
notificationConfig = {
|
||||
DisabledMonitorUsers: [],
|
||||
SendToUsers: [],
|
||||
DisabledServices: [],
|
||||
SendToUserMode: 'Admins'
|
||||
};
|
||||
}
|
||||
if (!notificationConfig) {
|
||||
notificationConfig = {
|
||||
DisabledMonitorUsers: [],
|
||||
SendToUsers: [],
|
||||
DisabledServices: [],
|
||||
SendToUserMode: 'Admins'
|
||||
};
|
||||
}
|
||||
|
||||
fillItems($('.monitorUsersList', page), users, 'chkMonitor', 'chkMonitor', notificationConfig.DisabledMonitorUsers);
|
||||
fillItems($('.sendToUsersList', page), users, 'chkSendTo', 'chkSendTo', notificationConfig.SendToUsers, true);
|
||||
fillItems($('.servicesList', page), services, 'chkService', 'chkService', notificationConfig.DisabledServices);
|
||||
$('#chkEnabled', page).prop('checked', notificationConfig.Enabled || false);
|
||||
$('#selectUsers', page).val(notificationConfig.SendToUserMode).trigger('change');
|
||||
});
|
||||
}
|
||||
|
||||
function save(page) {
|
||||
var type = getParameterByName('type');
|
||||
var promise1 = ApiClient.getNamedConfiguration(notificationsConfigurationKey);
|
||||
// TODO: Check if this promise is really needed, as it's unused.
|
||||
var promise2 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types'));
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
var notificationOptions = responses[0];
|
||||
var notificationConfig = notificationOptions.Options.filter(function (n) {
|
||||
return n.Type == type;
|
||||
})[0];
|
||||
|
||||
if (!notificationConfig) {
|
||||
notificationConfig = {
|
||||
Type: type
|
||||
};
|
||||
notificationOptions.Options.push(notificationConfig);
|
||||
}
|
||||
|
||||
notificationConfig.Enabled = $('#chkEnabled', page).is(':checked');
|
||||
notificationConfig.SendToUserMode = $('#selectUsers', page).val();
|
||||
notificationConfig.DisabledMonitorUsers = $('.chkMonitor', page).get().filter(function (c) {
|
||||
return !c.checked;
|
||||
}).map(function (c) {
|
||||
return c.getAttribute('data-itemid');
|
||||
});
|
||||
notificationConfig.SendToUsers = $('.chkSendTo', page).get().filter(function (c) {
|
||||
return c.checked;
|
||||
}).map(function (c) {
|
||||
return c.getAttribute('data-itemid');
|
||||
});
|
||||
notificationConfig.DisabledServices = $('.chkService', page).get().filter(function (c) {
|
||||
return !c.checked;
|
||||
}).map(function (c) {
|
||||
return c.getAttribute('data-itemid');
|
||||
});
|
||||
ApiClient.updateNamedConfiguration(notificationsConfigurationKey, notificationOptions).then(function (r) {
|
||||
Dashboard.processServerConfigurationUpdateResult();
|
||||
Dashboard.navigate('notificationsettings.html');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
save($(this).parents('.page'));
|
||||
return false;
|
||||
}
|
||||
|
||||
var notificationsConfigurationKey = 'notifications';
|
||||
$(document).on('pageinit', '#notificationSettingPage', function () {
|
||||
var page = this;
|
||||
$('#selectUsers', page).on('change', function () {
|
||||
if ('Custom' == this.value) {
|
||||
$('.selectCustomUsers', page).show();
|
||||
} else {
|
||||
$('.selectCustomUsers', page).hide();
|
||||
}
|
||||
});
|
||||
$('.notificationSettingForm').off('submit', onSubmit).on('submit', onSubmit);
|
||||
}).on('pageshow', '#notificationSettingPage', function () {
|
||||
reload(this);
|
||||
fillItems($('.monitorUsersList', page), users, 'chkMonitor', 'chkMonitor', notificationConfig.DisabledMonitorUsers);
|
||||
fillItems($('.sendToUsersList', page), users, 'chkSendTo', 'chkSendTo', notificationConfig.SendToUsers, true);
|
||||
fillItems($('.servicesList', page), services, 'chkService', 'chkService', notificationConfig.DisabledServices);
|
||||
$('#chkEnabled', page).prop('checked', notificationConfig.Enabled || false);
|
||||
$('#selectUsers', page).val(notificationConfig.SendToUserMode).trigger('change');
|
||||
});
|
||||
}
|
||||
|
||||
function save(page) {
|
||||
const type = getParameterByName('type');
|
||||
const promise1 = ApiClient.getNamedConfiguration(notificationsConfigurationKey);
|
||||
// TODO: Check if this promise is really needed, as it's unused.
|
||||
const promise2 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types'));
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
const notificationOptions = responses[0];
|
||||
let notificationConfig = notificationOptions.Options.filter(function (n) {
|
||||
return n.Type == type;
|
||||
})[0];
|
||||
|
||||
if (!notificationConfig) {
|
||||
notificationConfig = {
|
||||
Type: type
|
||||
};
|
||||
notificationOptions.Options.push(notificationConfig);
|
||||
}
|
||||
|
||||
notificationConfig.Enabled = $('#chkEnabled', page).is(':checked');
|
||||
notificationConfig.SendToUserMode = $('#selectUsers', page).val();
|
||||
notificationConfig.DisabledMonitorUsers = $('.chkMonitor', page).get().filter(function (c) {
|
||||
return !c.checked;
|
||||
}).map(function (c) {
|
||||
return c.getAttribute('data-itemid');
|
||||
});
|
||||
notificationConfig.SendToUsers = $('.chkSendTo', page).get().filter(function (c) {
|
||||
return c.checked;
|
||||
}).map(function (c) {
|
||||
return c.getAttribute('data-itemid');
|
||||
});
|
||||
notificationConfig.DisabledServices = $('.chkService', page).get().filter(function (c) {
|
||||
return !c.checked;
|
||||
}).map(function (c) {
|
||||
return c.getAttribute('data-itemid');
|
||||
});
|
||||
ApiClient.updateNamedConfiguration(notificationsConfigurationKey, notificationOptions).then(function (r) {
|
||||
Dashboard.processServerConfigurationUpdateResult();
|
||||
Dashboard.navigate('notificationsettings.html');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
save($(this).parents('.page'));
|
||||
return false;
|
||||
}
|
||||
|
||||
const notificationsConfigurationKey = 'notifications';
|
||||
$(document).on('pageinit', '#notificationSettingPage', function () {
|
||||
const page = this;
|
||||
$('#selectUsers', page).on('change', function () {
|
||||
if (this.value == 'Custom') {
|
||||
$('.selectCustomUsers', page).show();
|
||||
} else {
|
||||
$('.selectCustomUsers', page).hide();
|
||||
}
|
||||
});
|
||||
$('.notificationSettingForm').off('submit', onSubmit).on('submit', onSubmit);
|
||||
}).on('pageshow', '#notificationSettingPage', function () {
|
||||
reload(this);
|
||||
});
|
||||
|
|
|
@ -1,60 +1,61 @@
|
|||
define(['loading', 'libraryMenu', 'globalize', 'listViewStyle', 'emby-button'], function(loading, libraryMenu, globalize) {
|
||||
'use strict';
|
||||
import loading from 'loading';
|
||||
import globalize from 'globalize';
|
||||
import 'listViewStyle';
|
||||
import 'emby-button';
|
||||
|
||||
function reload(page) {
|
||||
loading.show();
|
||||
ApiClient.getJSON(ApiClient.getUrl('Notifications/Types')).then(function(list) {
|
||||
var html = '';
|
||||
var lastCategory = '';
|
||||
var showHelp = true;
|
||||
html += list.map(function(notification) {
|
||||
var itemHtml = '';
|
||||
if (notification.Category !== lastCategory) {
|
||||
lastCategory = notification.Category;
|
||||
if (lastCategory) {
|
||||
itemHtml += '</div>';
|
||||
itemHtml += '</div>';
|
||||
}
|
||||
itemHtml += '<div class="verticalSection verticalSection-extrabottompadding">';
|
||||
itemHtml += '<div class="sectionTitleContainer" style="margin-bottom:1em;">';
|
||||
itemHtml += '<h2 class="sectionTitle">';
|
||||
itemHtml += notification.Category;
|
||||
itemHtml += '</h2>';
|
||||
if (showHelp) {
|
||||
showHelp = false;
|
||||
itemHtml += '<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/notifications.html">';
|
||||
itemHtml += globalize.translate('Help');
|
||||
itemHtml += '</a>';
|
||||
}
|
||||
function reload(page) {
|
||||
loading.show();
|
||||
ApiClient.getJSON(ApiClient.getUrl('Notifications/Types')).then(function (list) {
|
||||
let html = '';
|
||||
let lastCategory = '';
|
||||
let showHelp = true;
|
||||
html += list.map(function (notification) {
|
||||
let itemHtml = '';
|
||||
if (notification.Category !== lastCategory) {
|
||||
lastCategory = notification.Category;
|
||||
if (lastCategory) {
|
||||
itemHtml += '</div>';
|
||||
itemHtml += '</div>';
|
||||
itemHtml += '<div class="paperList">';
|
||||
}
|
||||
itemHtml += '<a class="listItem listItem-border" is="emby-linkbutton" data-ripple="false" href="notificationsetting.html?type=' + notification.Type + '">';
|
||||
if (notification.Enabled) {
|
||||
itemHtml += '<span class="listItemIcon material-icons notifications_active"></span>';
|
||||
} else {
|
||||
itemHtml += '<span class="listItemIcon material-icons notifications_off" style="background-color:#999;"></span>';
|
||||
itemHtml += '<div class="verticalSection verticalSection-extrabottompadding">';
|
||||
itemHtml += '<div class="sectionTitleContainer" style="margin-bottom:1em;">';
|
||||
itemHtml += '<h2 class="sectionTitle">';
|
||||
itemHtml += notification.Category;
|
||||
itemHtml += '</h2>';
|
||||
if (showHelp) {
|
||||
showHelp = false;
|
||||
itemHtml += '<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/notifications.html">';
|
||||
itemHtml += globalize.translate('Help');
|
||||
itemHtml += '</a>';
|
||||
}
|
||||
itemHtml += '<div class="listItemBody">';
|
||||
itemHtml += '<div class="listItemBodyText">' + notification.Name + '</div>';
|
||||
itemHtml += '</div>';
|
||||
itemHtml += '<button type="button" is="paper-icon-button-light"><span class="material-icons mode_edit"></span></button>';
|
||||
itemHtml += '</a>';
|
||||
return itemHtml;
|
||||
}).join('');
|
||||
|
||||
if (list.length) {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
itemHtml += '<div class="paperList">';
|
||||
}
|
||||
page.querySelector('.notificationList').innerHTML = html;
|
||||
loading.hide();
|
||||
});
|
||||
}
|
||||
itemHtml += '<a class="listItem listItem-border" is="emby-linkbutton" data-ripple="false" href="notificationsetting.html?type=' + notification.Type + '">';
|
||||
if (notification.Enabled) {
|
||||
itemHtml += '<span class="listItemIcon material-icons notifications_active"></span>';
|
||||
} else {
|
||||
itemHtml += '<span class="listItemIcon material-icons notifications_off" style="background-color:#999;"></span>';
|
||||
}
|
||||
itemHtml += '<div class="listItemBody">';
|
||||
itemHtml += '<div class="listItemBodyText">' + notification.Name + '</div>';
|
||||
itemHtml += '</div>';
|
||||
itemHtml += '<button type="button" is="paper-icon-button-light"><span class="material-icons mode_edit"></span></button>';
|
||||
itemHtml += '</a>';
|
||||
return itemHtml;
|
||||
}).join('');
|
||||
|
||||
return function(view, params) {
|
||||
view.addEventListener('viewshow', function() {
|
||||
reload(view);
|
||||
});
|
||||
};
|
||||
});
|
||||
if (list.length) {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
page.querySelector('.notificationList').innerHTML = html;
|
||||
loading.hide();
|
||||
});
|
||||
}
|
||||
|
||||
export default function (view, params) {
|
||||
view.addEventListener('viewshow', function () {
|
||||
reload(view);
|
||||
});
|
||||
}
|
||||
|
|
30
src/controllers/dashboard/playback.html
Normal file
30
src/controllers/dashboard/playback.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
<div id="playbackConfigurationPage" data-role="page" class="page type-interior playbackConfigurationPage withTabs">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<form class="playbackConfigurationForm">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${ButtonResume}</h2>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtMinResumePct" name="txtMinResumePct" pattern="[0-9]*" required min="0" max="100" label="${LabelMinResumePercentage}"></input>
|
||||
<div class="fieldDescription">
|
||||
${LabelMinResumePercentageHelp}
|
||||
</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtMaxResumePct" name="txtMaxResumePct" pattern="[0-9]*" required min="1" max="100" label="${LabelMaxResumePercentage}"></input>
|
||||
<div class="fieldDescription">
|
||||
${LabelMaxResumePercentageHelp}
|
||||
</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtMinResumeDuration" name="txtMinResumeDuration" pattern="[0-9]*" required min="0" label="${LabelMinResumeDuration}"></input>
|
||||
<div class="fieldDescription">
|
||||
${LabelMinResumeDurationHelp}
|
||||
</div>
|
||||
</div>
|
||||
<div><button is="emby-button" type="submit" class="raised button-submit block"><span>${Save}</span></button></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -32,7 +32,7 @@ import globalize from 'globalize';
|
|||
name: globalize.translate('Transcoding')
|
||||
}, {
|
||||
href: 'playbackconfiguration.html',
|
||||
name: globalize.translate('TabResumeSettings')
|
||||
name: globalize.translate('ButtonResume')
|
||||
}, {
|
||||
href: 'streamingsettings.html',
|
||||
name: globalize.translate('TabStreaming')
|
||||
|
|
|
@ -1,143 +1,146 @@
|
|||
define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'connectionManager', 'emby-button'], function ($, loading, libraryMenu, globalize, connectionManager) {
|
||||
'use strict';
|
||||
import $ from 'jQuery';
|
||||
import loading from 'loading';
|
||||
import globalize from 'globalize';
|
||||
import 'emby-button';
|
||||
|
||||
function populateHistory(packageInfo, page) {
|
||||
var html = '';
|
||||
var length = Math.min(packageInfo.versions.length, 10);
|
||||
function populateHistory(packageInfo, page) {
|
||||
let html = '';
|
||||
const length = Math.min(packageInfo.versions.length, 10);
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var version = packageInfo.versions[i];
|
||||
html += '<h2 style="margin:.5em 0;">' + version.version + '</h2>';
|
||||
html += '<div style="margin-bottom:1.5em;">' + version.changelog + '</div>';
|
||||
}
|
||||
|
||||
$('#revisionHistory', page).html(html);
|
||||
for (let i = 0; i < length; i++) {
|
||||
const version = packageInfo.versions[i];
|
||||
html += '<h2 style="margin:.5em 0;">' + version.version + '</h2>';
|
||||
html += '<div style="margin-bottom:1.5em;">' + version.changelog + '</div>';
|
||||
}
|
||||
|
||||
function populateVersions(packageInfo, page, installedPlugin) {
|
||||
var html = '';
|
||||
$('#revisionHistory', page).html(html);
|
||||
}
|
||||
|
||||
for (var i = 0; i < packageInfo.versions.length; i++) {
|
||||
var version = packageInfo.versions[i];
|
||||
html += '<option value="' + version.version + '">' + version.version + '</option>';
|
||||
}
|
||||
function populateVersions(packageInfo, page, installedPlugin) {
|
||||
let html = '';
|
||||
|
||||
var selectmenu = $('#selectVersion', page).html(html);
|
||||
|
||||
if (!installedPlugin) {
|
||||
$('#pCurrentVersion', page).hide().html('');
|
||||
}
|
||||
|
||||
var packageVersion = packageInfo.versions[0];
|
||||
if (packageVersion) {
|
||||
selectmenu.val(packageVersion.version);
|
||||
}
|
||||
for (let i = 0; i < packageInfo.versions.length; i++) {
|
||||
const version = packageInfo.versions[i];
|
||||
html += '<option value="' + version.version + '">' + version.version + '</option>';
|
||||
}
|
||||
|
||||
function renderPackage(pkg, installedPlugins, page) {
|
||||
var installedPlugin = installedPlugins.filter(function (ip) {
|
||||
return ip.Name == pkg.name;
|
||||
})[0];
|
||||
const selectmenu = $('#selectVersion', page).html(html);
|
||||
|
||||
populateVersions(pkg, page, installedPlugin);
|
||||
populateHistory(pkg, page);
|
||||
|
||||
$('.pluginName', page).html(pkg.name);
|
||||
$('#btnInstallDiv', page).removeClass('hide');
|
||||
$('#pSelectVersion', page).removeClass('hide');
|
||||
|
||||
if (pkg.overview) {
|
||||
$('#overview', page).show().html(pkg.overview);
|
||||
} else {
|
||||
$('#overview', page).hide();
|
||||
}
|
||||
|
||||
$('#description', page).html(pkg.description);
|
||||
$('#developer', page).html(pkg.owner);
|
||||
|
||||
if (installedPlugin) {
|
||||
var currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '<strong>' + installedPlugin.Version + '</strong>');
|
||||
$('#pCurrentVersion', page).show().html(currentVersionText);
|
||||
} else {
|
||||
$('#pCurrentVersion', page).hide().html('');
|
||||
}
|
||||
|
||||
loading.hide();
|
||||
if (!installedPlugin) {
|
||||
$('#pCurrentVersion', page).hide().html('');
|
||||
}
|
||||
|
||||
function alertText(options) {
|
||||
require(['alert'], function (alert) {
|
||||
alert(options);
|
||||
});
|
||||
const packageVersion = packageInfo.versions[0];
|
||||
if (packageVersion) {
|
||||
selectmenu.val(packageVersion.version);
|
||||
}
|
||||
}
|
||||
|
||||
function renderPackage(pkg, installedPlugins, page) {
|
||||
const installedPlugin = installedPlugins.filter(function (ip) {
|
||||
return ip.Name == pkg.name;
|
||||
})[0];
|
||||
|
||||
populateVersions(pkg, page, installedPlugin);
|
||||
populateHistory(pkg, page);
|
||||
|
||||
$('.pluginName', page).html(pkg.name);
|
||||
$('#btnInstallDiv', page).removeClass('hide');
|
||||
$('#pSelectVersion', page).removeClass('hide');
|
||||
|
||||
if (pkg.overview) {
|
||||
$('#overview', page).show().html(pkg.overview);
|
||||
} else {
|
||||
$('#overview', page).hide();
|
||||
}
|
||||
|
||||
function performInstallation(page, name, guid, version) {
|
||||
var developer = $('#developer', page).html().toLowerCase();
|
||||
$('#description', page).html(pkg.description);
|
||||
$('#developer', page).html(pkg.owner);
|
||||
|
||||
var alertCallback = function () {
|
||||
loading.show();
|
||||
page.querySelector('#btnInstall').disabled = true;
|
||||
ApiClient.installPlugin(name, guid, version).then(function () {
|
||||
loading.hide();
|
||||
alertText(globalize.translate('PluginInstalledMessage'));
|
||||
});
|
||||
};
|
||||
if (installedPlugin) {
|
||||
const currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '<strong>' + installedPlugin.Version + '</strong>');
|
||||
$('#pCurrentVersion', page).show().html(currentVersionText);
|
||||
} else {
|
||||
$('#pCurrentVersion', page).hide().html('');
|
||||
}
|
||||
|
||||
if (developer !== 'jellyfin') {
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function alertText(options) {
|
||||
import('alert').then(({default: alert}) => {
|
||||
alert(options);
|
||||
});
|
||||
}
|
||||
|
||||
function performInstallation(page, name, guid, version) {
|
||||
const developer = $('#developer', page).html().toLowerCase();
|
||||
|
||||
const alertCallback = function () {
|
||||
loading.show();
|
||||
page.querySelector('#btnInstall').disabled = true;
|
||||
ApiClient.installPlugin(name, guid, version).then(() => {
|
||||
loading.hide();
|
||||
var msg = globalize.translate('MessagePluginInstallDisclaimer');
|
||||
msg += '<br/>';
|
||||
msg += '<br/>';
|
||||
msg += globalize.translate('PleaseConfirmPluginInstallation');
|
||||
|
||||
require(['confirm'], function (confirm) {
|
||||
confirm.default(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () {
|
||||
alertCallback();
|
||||
}, function () {
|
||||
console.debug('plugin not installed');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
alertCallback();
|
||||
}
|
||||
}
|
||||
|
||||
return function (view, params) {
|
||||
$('.addPluginForm', view).on('submit', function () {
|
||||
loading.show();
|
||||
var page = $(this).parents('#addPluginPage')[0];
|
||||
var name = params.name;
|
||||
var guid = params.guid;
|
||||
ApiClient.getInstalledPlugins().then(function (plugins) {
|
||||
var installedPlugin = plugins.filter(function (plugin) {
|
||||
return plugin.Name == name;
|
||||
})[0];
|
||||
|
||||
var version = $('#selectVersion', page).val();
|
||||
if (installedPlugin) {
|
||||
if (installedPlugin.Version === version) {
|
||||
loading.hide();
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessageAlreadyInstalled'),
|
||||
title: globalize.translate('HeaderPluginInstallation')
|
||||
});
|
||||
}
|
||||
} else {
|
||||
performInstallation(page, name, guid, version);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
view.addEventListener('viewshow', function () {
|
||||
var page = this;
|
||||
loading.show();
|
||||
var name = params.name;
|
||||
var guid = params.guid;
|
||||
var promise1 = ApiClient.getPackageInfo(name, guid);
|
||||
var promise2 = ApiClient.getInstalledPlugins();
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
renderPackage(responses[0], responses[1], page);
|
||||
});
|
||||
alertText(globalize.translate('MessagePluginInstalled'));
|
||||
}).catch(() => {
|
||||
alertText(globalize.translate('MessagePluginInstallError'));
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
if (developer !== 'jellyfin') {
|
||||
loading.hide();
|
||||
let msg = globalize.translate('MessagePluginInstallDisclaimer');
|
||||
msg += '<br/>';
|
||||
msg += '<br/>';
|
||||
msg += globalize.translate('PleaseConfirmPluginInstallation');
|
||||
|
||||
import('confirm').then(({default: confirm}) => {
|
||||
confirm(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () {
|
||||
alertCallback();
|
||||
}).catch(() => {
|
||||
console.debug('plugin not installed');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
alertCallback();
|
||||
}
|
||||
}
|
||||
|
||||
export default function(view, params) {
|
||||
$('.addPluginForm', view).on('submit', function () {
|
||||
loading.show();
|
||||
const page = $(this).parents('#addPluginPage')[0];
|
||||
const name = params.name;
|
||||
const guid = params.guid;
|
||||
ApiClient.getInstalledPlugins().then(function (plugins) {
|
||||
const installedPlugin = plugins.filter(function (plugin) {
|
||||
return plugin.Name == name;
|
||||
})[0];
|
||||
|
||||
const version = $('#selectVersion', page).val();
|
||||
if (installedPlugin && installedPlugin.Version === version) {
|
||||
loading.hide();
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessageAlreadyInstalled'),
|
||||
title: globalize.translate('HeaderPluginInstallation')
|
||||
});
|
||||
} else {
|
||||
performInstallation(page, name, guid, version);
|
||||
}
|
||||
}).catch(() => {
|
||||
alertText(globalize.translate('MessageGetInstalledPluginsError'));
|
||||
});
|
||||
return false;
|
||||
});
|
||||
view.addEventListener('viewshow', function () {
|
||||
const page = this;
|
||||
loading.show();
|
||||
const name = params.name;
|
||||
const guid = params.guid;
|
||||
const promise1 = ApiClient.getPackageInfo(name, guid);
|
||||
const promise2 = ApiClient.getInstalledPlugins();
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
renderPackage(responses[0], responses[1], page);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,142 +1,142 @@
|
|||
define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby-checkbox', 'emby-select'], function (loading, libraryMenu, globalize) {
|
||||
'use strict';
|
||||
import loading from 'loading';
|
||||
import libraryMenu from 'libraryMenu';
|
||||
import globalize from 'globalize';
|
||||
import 'cardStyle';
|
||||
import 'emby-button';
|
||||
import 'emby-checkbox';
|
||||
import 'emby-select';
|
||||
|
||||
function reloadList(page) {
|
||||
loading.show();
|
||||
var promise1 = ApiClient.getAvailablePlugins();
|
||||
var promise2 = ApiClient.getInstalledPlugins();
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
populateList({
|
||||
catalogElement: page.querySelector('#pluginTiles'),
|
||||
noItemsElement: page.querySelector('#noPlugins'),
|
||||
availablePlugins: responses[0],
|
||||
installedPlugins: responses[1]
|
||||
});
|
||||
function reloadList(page) {
|
||||
loading.show();
|
||||
const promise1 = ApiClient.getAvailablePlugins();
|
||||
const promise2 = ApiClient.getInstalledPlugins();
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
populateList({
|
||||
catalogElement: page.querySelector('#pluginTiles'),
|
||||
noItemsElement: page.querySelector('#noPlugins'),
|
||||
availablePlugins: responses[0],
|
||||
installedPlugins: responses[1]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getHeaderText(category) {
|
||||
category = category.replace(' ', '');
|
||||
// TODO: Replace with switch
|
||||
if (category === 'Channel') {
|
||||
category = 'Channels';
|
||||
} else if (category === 'Theme') {
|
||||
category = 'Themes';
|
||||
} else if (category === 'LiveTV') {
|
||||
category = 'LiveTV';
|
||||
} else if (category === 'ScreenSaver') {
|
||||
category = 'HeaderScreenSavers';
|
||||
}
|
||||
|
||||
function getHeaderText(category) {
|
||||
category = category.replace(' ', '');
|
||||
if ('Channel' === category) {
|
||||
category = 'Channels';
|
||||
} else if ('Theme' === category) {
|
||||
category = 'Themes';
|
||||
} else if ('LiveTV' === category) {
|
||||
category = 'HeaderLiveTV';
|
||||
} else if ('ScreenSaver' === category) {
|
||||
category = 'HeaderScreenSavers';
|
||||
return globalize.translate(category);
|
||||
}
|
||||
|
||||
function populateList(options) {
|
||||
const availablePlugins = options.availablePlugins;
|
||||
const installedPlugins = options.installedPlugins;
|
||||
|
||||
availablePlugins.forEach(function (plugin, index, array) {
|
||||
plugin.category = plugin.category || 'General';
|
||||
plugin.categoryDisplayName = getHeaderText(plugin.category);
|
||||
array[index] = plugin;
|
||||
});
|
||||
|
||||
availablePlugins.sort(function (a, b) {
|
||||
if (a.category > b.category) {
|
||||
return 1;
|
||||
} else if (b.category > a.category) {
|
||||
return -1;
|
||||
}
|
||||
if (a.name > b.name) {
|
||||
return 1;
|
||||
} else if (b.name > a.name) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
return globalize.translate(category);
|
||||
}
|
||||
let currentCategory = null;
|
||||
let html = '';
|
||||
|
||||
function populateList(options) {
|
||||
var availablePlugins = options.availablePlugins;
|
||||
var installedPlugins = options.installedPlugins;
|
||||
|
||||
var categories = [];
|
||||
availablePlugins.forEach(function (plugin, index, array) {
|
||||
plugin.category = plugin.category || 'General';
|
||||
plugin.categoryDisplayName = getHeaderText(plugin.category);
|
||||
array[index] = plugin;
|
||||
});
|
||||
|
||||
availablePlugins.sort(function (a, b) {
|
||||
if (a.category > b.category) {
|
||||
return 1;
|
||||
} else if (b.category > a.category) {
|
||||
return -1;
|
||||
for (let i = 0; i < availablePlugins.length; i++) {
|
||||
const plugin = availablePlugins[i];
|
||||
const category = plugin.categoryDisplayName;
|
||||
if (category != currentCategory) {
|
||||
if (currentCategory) {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
if (a.name > b.name) {
|
||||
return 1;
|
||||
} else if (b.name > a.name) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
var currentCategory = null;
|
||||
var html = '';
|
||||
|
||||
for (var i = 0; i < availablePlugins.length; i++) {
|
||||
var plugin = availablePlugins[i];
|
||||
var category = plugin.categoryDisplayName;
|
||||
if (category != currentCategory) {
|
||||
if (currentCategory) {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
html += '<div class="verticalSection">';
|
||||
html += '<h2 class="sectionTitle sectionTitle-cards">' + category + '</h2>';
|
||||
html += '<div class="itemsContainer vertical-wrap">';
|
||||
currentCategory = category;
|
||||
}
|
||||
html += getPluginHtml(plugin, options, installedPlugins);
|
||||
html += '<div class="verticalSection">';
|
||||
html += '<h2 class="sectionTitle sectionTitle-cards">' + category + '</h2>';
|
||||
html += '<div class="itemsContainer vertical-wrap">';
|
||||
currentCategory = category;
|
||||
}
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += getPluginHtml(plugin, options, installedPlugins);
|
||||
}
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
|
||||
if (!availablePlugins.length && options.noItemsElement) {
|
||||
options.noItemsElement.classList.remove('hide');
|
||||
}
|
||||
|
||||
options.catalogElement.innerHTML = html;
|
||||
loading.hide();
|
||||
if (!availablePlugins.length && options.noItemsElement) {
|
||||
options.noItemsElement.classList.remove('hide');
|
||||
}
|
||||
|
||||
function getPluginHtml(plugin, options, installedPlugins) {
|
||||
var html = '';
|
||||
var href = plugin.externalUrl ? plugin.externalUrl : 'addplugin.html?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid;
|
||||
options.catalogElement.innerHTML = html;
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
if (options.context) {
|
||||
href += '&context=' + options.context;
|
||||
}
|
||||
function getPluginHtml(plugin, options, installedPlugins) {
|
||||
let html = '';
|
||||
let href = plugin.externalUrl ? plugin.externalUrl : 'addplugin.html?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid;
|
||||
|
||||
var target = plugin.externalUrl ? ' target="_blank"' : '';
|
||||
html += "<div class='card backdropCard'>";
|
||||
html += '<div class="cardBox visualCardBox">';
|
||||
html += '<div class="cardScalable visualCardBox-cardScalable">';
|
||||
html += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
html += '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="' + href + '"' + target + '>';
|
||||
html += '<span class="cardImageIcon material-icons folder"></span>';
|
||||
html += '</a>';
|
||||
html += '</div>';
|
||||
html += '<div class="cardFooter">';
|
||||
html += "<div class='cardText'>";
|
||||
html += plugin.name;
|
||||
html += '</div>';
|
||||
var installedPlugin = installedPlugins.filter(function (ip) {
|
||||
return ip.Id == plugin.guid;
|
||||
})[0];
|
||||
html += "<div class='cardText cardText-secondary'>";
|
||||
html += installedPlugin ? globalize.translate('LabelVersionInstalled', installedPlugin.Version) : ' ';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html += '</div>';
|
||||
if (options.context) {
|
||||
href += '&context=' + options.context;
|
||||
}
|
||||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: 'installedplugins.html',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: 'availableplugins.html',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: 'repositories.html',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
const target = plugin.externalUrl ? ' target="_blank"' : '';
|
||||
html += "<div class='card backdropCard'>";
|
||||
html += '<div class="cardBox visualCardBox">';
|
||||
html += '<div class="cardScalable visualCardBox-cardScalable">';
|
||||
html += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
html += '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="' + href + '"' + target + '>';
|
||||
html += '<span class="cardImageIcon material-icons folder"></span>';
|
||||
html += '</a>';
|
||||
html += '</div>';
|
||||
html += '<div class="cardFooter">';
|
||||
html += "<div class='cardText'>";
|
||||
html += plugin.name;
|
||||
html += '</div>';
|
||||
const installedPlugin = installedPlugins.filter(function (ip) {
|
||||
return ip.Id == plugin.guid;
|
||||
})[0];
|
||||
html += "<div class='cardText cardText-secondary'>";
|
||||
html += installedPlugin ? globalize.translate('LabelVersionInstalled', installedPlugin.Version) : ' ';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html += '</div>';
|
||||
}
|
||||
|
||||
window.PluginCatalog = {
|
||||
renderCatalog: populateList
|
||||
};
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: 'installedplugins.html',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: 'availableplugins.html',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: 'repositories.html',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
|
||||
return function (view, params) {
|
||||
view.addEventListener('viewshow', function () {
|
||||
libraryMenu.setTabs('plugins', 1, getTabs);
|
||||
reloadList(this);
|
||||
});
|
||||
};
|
||||
});
|
||||
export default function (view) {
|
||||
view.addEventListener('viewshow', function () {
|
||||
libraryMenu.setTabs('plugins', 1, getTabs);
|
||||
reloadList(this);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,190 +1,193 @@
|
|||
define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'], function (loading, libraryMenu, dom, globalize) {
|
||||
'use strict';
|
||||
import loading from 'loading';
|
||||
import libraryMenu from 'libraryMenu';
|
||||
import dom from 'dom';
|
||||
import globalize from 'globalize';
|
||||
import 'cardStyle';
|
||||
import 'emby-button';
|
||||
|
||||
function deletePlugin(page, uniqueid, name) {
|
||||
var msg = globalize.translate('UninstallPluginConfirmation', name);
|
||||
function deletePlugin(page, uniqueid, name) {
|
||||
const msg = globalize.translate('UninstallPluginConfirmation', name);
|
||||
|
||||
require(['confirm'], function (confirm) {
|
||||
confirm.default({
|
||||
title: globalize.translate('UninstallPluginHeader'),
|
||||
text: msg,
|
||||
primary: 'delete',
|
||||
confirmText: globalize.translate('UninstallPluginHeader')
|
||||
}).then(function () {
|
||||
loading.show();
|
||||
ApiClient.uninstallPlugin(uniqueid).then(function () {
|
||||
reloadList(page);
|
||||
});
|
||||
import('confirm').then(({default: confirm}) => {
|
||||
confirm.default({
|
||||
title: globalize.translate('HeaderUninstallPlugin'),
|
||||
text: msg,
|
||||
primary: 'delete',
|
||||
confirmText: globalize.translate('HeaderUninstallPlugin')
|
||||
}).then(function () {
|
||||
loading.show();
|
||||
ApiClient.uninstallPlugin(uniqueid).then(function () {
|
||||
reloadList(page);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showNoConfigurationMessage() {
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('NoPluginConfigurationMessage')
|
||||
});
|
||||
}
|
||||
function showNoConfigurationMessage() {
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessageNoPluginConfiguration')
|
||||
});
|
||||
}
|
||||
|
||||
function showConnectMessage() {
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessagePluginConfigurationRequiresLocalAccess')
|
||||
});
|
||||
}
|
||||
function showConnectMessage() {
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessagePluginConfigurationRequiresLocalAccess')
|
||||
});
|
||||
}
|
||||
|
||||
function getPluginCardHtml(plugin, pluginConfigurationPages) {
|
||||
var configPage = pluginConfigurationPages.filter(function (pluginConfigurationPage) {
|
||||
return pluginConfigurationPage.PluginId == plugin.Id;
|
||||
})[0];
|
||||
var configPageUrl = configPage ? Dashboard.getConfigurationPageUrl(configPage.Name) : null;
|
||||
var html = '';
|
||||
html += "<div data-id='" + plugin.Id + "' data-name='" + plugin.Name + "' data-removable='" + plugin.CanUninstall + "' class='card backdropCard'>";
|
||||
html += '<div class="cardBox visualCardBox">';
|
||||
html += '<div class="cardScalable">';
|
||||
html += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
html += configPageUrl ? '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="' + configPageUrl + '">' : '<div class="cardContent noConfigPluginCard noHoverEffect cardImageContainer emby-button">';
|
||||
html += '<span class="cardImageIcon material-icons folder"></span>';
|
||||
html += configPageUrl ? '</a>' : '</div>';
|
||||
function getPluginCardHtml(plugin, pluginConfigurationPages) {
|
||||
const configPage = pluginConfigurationPages.filter(function (pluginConfigurationPage) {
|
||||
return pluginConfigurationPage.PluginId == plugin.Id;
|
||||
})[0];
|
||||
const configPageUrl = configPage ? Dashboard.getPluginUrl(configPage.Name) : null;
|
||||
let html = '';
|
||||
html += "<div data-id='" + plugin.Id + "' data-name='" + plugin.Name + "' data-removable='" + plugin.CanUninstall + "' class='card backdropCard'>";
|
||||
html += '<div class="cardBox visualCardBox">';
|
||||
html += '<div class="cardScalable">';
|
||||
html += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
html += configPageUrl ? '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="' + configPageUrl + '">' : '<div class="cardContent noConfigPluginCard noHoverEffect cardImageContainer emby-button">';
|
||||
html += '<span class="cardImageIcon material-icons folder"></span>';
|
||||
html += configPageUrl ? '</a>' : '</div>';
|
||||
html += '</div>';
|
||||
html += '<div class="cardFooter">';
|
||||
|
||||
if (configPage || plugin.CanUninstall) {
|
||||
html += '<div style="text-align:right; float:right;padding-top:5px;">';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnCardMenu autoSize"><span class="material-icons more_vert"></span></button>';
|
||||
html += '</div>';
|
||||
html += '<div class="cardFooter">';
|
||||
}
|
||||
|
||||
if (configPage || plugin.CanUninstall) {
|
||||
html += '<div style="text-align:right; float:right;padding-top:5px;">';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnCardMenu autoSize"><span class="material-icons more_vert"></span></button>';
|
||||
html += '</div>';
|
||||
html += "<div class='cardText'>";
|
||||
html += configPage && configPage.DisplayName ? configPage.DisplayName : plugin.Name;
|
||||
html += '</div>';
|
||||
html += "<div class='cardText cardText-secondary'>";
|
||||
html += plugin.Version;
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderPlugins(page, plugins) {
|
||||
ApiClient.getJSON(ApiClient.getUrl('web/configurationpages') + '?pageType=PluginConfiguration').then(function (configPages) {
|
||||
populateList(page, plugins, configPages);
|
||||
});
|
||||
}
|
||||
|
||||
function populateList(page, plugins, pluginConfigurationPages) {
|
||||
plugins = plugins.sort(function (plugin1, plugin2) {
|
||||
if (plugin1.Name > plugin2.Name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
html += "<div class='cardText'>";
|
||||
html += configPage && configPage.DisplayName ? configPage.DisplayName : plugin.Name;
|
||||
html += '</div>';
|
||||
html += "<div class='cardText cardText-secondary'>";
|
||||
html += plugin.Version;
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderPlugins(page, plugins) {
|
||||
ApiClient.getJSON(ApiClient.getUrl('web/configurationpages') + '?pageType=PluginConfiguration').then(function (configPages) {
|
||||
populateList(page, plugins, configPages);
|
||||
});
|
||||
}
|
||||
|
||||
function populateList(page, plugins, pluginConfigurationPages) {
|
||||
plugins = plugins.sort(function (plugin1, plugin2) {
|
||||
if (plugin1.Name > plugin2.Name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
});
|
||||
|
||||
var html = plugins.map(function (p) {
|
||||
return getPluginCardHtml(p, pluginConfigurationPages);
|
||||
}).join('');
|
||||
|
||||
var installedPluginsElement = page.querySelector('.installedPlugins');
|
||||
installedPluginsElement.removeEventListener('click', onInstalledPluginsClick);
|
||||
installedPluginsElement.addEventListener('click', onInstalledPluginsClick);
|
||||
|
||||
if (plugins.length) {
|
||||
installedPluginsElement.classList.add('itemsContainer');
|
||||
installedPluginsElement.classList.add('vertical-wrap');
|
||||
} else {
|
||||
html += '<div class="centerMessage">';
|
||||
html += '<h1>' + globalize.translate('MessageNoPluginsInstalled') + '</h1>';
|
||||
html += '<p><a is="emby-linkbutton" class="button-link" href="availableplugins.html">';
|
||||
html += globalize.translate('BrowsePluginCatalogMessage');
|
||||
html += '</a></p>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
installedPluginsElement.innerHTML = html;
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function showPluginMenu(page, elem) {
|
||||
var card = dom.parentWithClass(elem, 'card');
|
||||
var id = card.getAttribute('data-id');
|
||||
var name = card.getAttribute('data-name');
|
||||
var removable = card.getAttribute('data-removable');
|
||||
var configHref = card.querySelector('.cardContent').getAttribute('href');
|
||||
var menuItems = [];
|
||||
|
||||
if (configHref) {
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonSettings'),
|
||||
id: 'open',
|
||||
icon: 'mode_edit'
|
||||
});
|
||||
}
|
||||
|
||||
if (removable === 'true') {
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonUninstall'),
|
||||
id: 'delete',
|
||||
icon: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
require(['actionsheet'], function (actionsheet) {
|
||||
actionsheet.show({
|
||||
items: menuItems,
|
||||
positionTo: elem,
|
||||
callback: function (resultId) {
|
||||
switch (resultId) {
|
||||
case 'open':
|
||||
Dashboard.navigate(configHref);
|
||||
break;
|
||||
case 'delete':
|
||||
deletePlugin(page, id, name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function reloadList(page) {
|
||||
loading.show();
|
||||
ApiClient.getInstalledPlugins().then(function (plugins) {
|
||||
renderPlugins(page, plugins);
|
||||
});
|
||||
}
|
||||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: 'installedplugins.html',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: 'availableplugins.html',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: 'repositories.html',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
|
||||
function onInstalledPluginsClick(e) {
|
||||
if (dom.parentWithClass(e.target, 'noConfigPluginCard')) {
|
||||
showNoConfigurationMessage();
|
||||
} else if (dom.parentWithClass(e.target, 'connectModePluginCard')) {
|
||||
showConnectMessage();
|
||||
} else {
|
||||
var btnCardMenu = dom.parentWithClass(e.target, 'btnCardMenu');
|
||||
if (btnCardMenu) {
|
||||
showPluginMenu(dom.parentWithClass(btnCardMenu, 'page'), btnCardMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pageIdOn('pageshow', 'pluginsPage', function () {
|
||||
libraryMenu.setTabs('plugins', 0, getTabs);
|
||||
reloadList(this);
|
||||
return -1;
|
||||
});
|
||||
|
||||
window.PluginsPage = {
|
||||
renderPlugins: renderPlugins
|
||||
};
|
||||
let html = plugins.map(function (p) {
|
||||
return getPluginCardHtml(p, pluginConfigurationPages);
|
||||
}).join('');
|
||||
|
||||
const installedPluginsElement = page.querySelector('.installedPlugins');
|
||||
installedPluginsElement.removeEventListener('click', onInstalledPluginsClick);
|
||||
installedPluginsElement.addEventListener('click', onInstalledPluginsClick);
|
||||
|
||||
if (plugins.length) {
|
||||
installedPluginsElement.classList.add('itemsContainer');
|
||||
installedPluginsElement.classList.add('vertical-wrap');
|
||||
} else {
|
||||
html += '<div class="centerMessage">';
|
||||
html += '<h1>' + globalize.translate('MessageNoPluginsInstalled') + '</h1>';
|
||||
html += '<p><a is="emby-linkbutton" class="button-link" href="availableplugins.html">';
|
||||
html += globalize.translate('MessageBrowsePluginCatalog');
|
||||
html += '</a></p>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
installedPluginsElement.innerHTML = html;
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function showPluginMenu(page, elem) {
|
||||
const card = dom.parentWithClass(elem, 'card');
|
||||
const id = card.getAttribute('data-id');
|
||||
const name = card.getAttribute('data-name');
|
||||
const removable = card.getAttribute('data-removable');
|
||||
const configHref = card.querySelector('.cardContent').getAttribute('href');
|
||||
const menuItems = [];
|
||||
|
||||
if (configHref) {
|
||||
menuItems.push({
|
||||
name: globalize.translate('Settings'),
|
||||
id: 'open',
|
||||
icon: 'mode_edit'
|
||||
});
|
||||
}
|
||||
|
||||
if (removable === 'true') {
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonUninstall'),
|
||||
id: 'delete',
|
||||
icon: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
import('actionsheet').then(({default: actionsheet}) => {
|
||||
actionsheet.show({
|
||||
items: menuItems,
|
||||
positionTo: elem,
|
||||
callback: function (resultId) {
|
||||
switch (resultId) {
|
||||
case 'open':
|
||||
Dashboard.navigate(configHref);
|
||||
break;
|
||||
case 'delete':
|
||||
deletePlugin(page, id, name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function reloadList(page) {
|
||||
loading.show();
|
||||
ApiClient.getInstalledPlugins().then(function (plugins) {
|
||||
renderPlugins(page, plugins);
|
||||
});
|
||||
}
|
||||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: 'installedplugins.html',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: 'availableplugins.html',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: 'repositories.html',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
|
||||
function onInstalledPluginsClick(e) {
|
||||
if (dom.parentWithClass(e.target, 'noConfigPluginCard')) {
|
||||
showNoConfigurationMessage();
|
||||
} else if (dom.parentWithClass(e.target, 'connectModePluginCard')) {
|
||||
showConnectMessage();
|
||||
} else {
|
||||
const btnCardMenu = dom.parentWithClass(e.target, 'btnCardMenu');
|
||||
if (btnCardMenu) {
|
||||
showPluginMenu(dom.parentWithClass(btnCardMenu, 'page'), btnCardMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pageIdOn('pageshow', 'pluginsPage', function () {
|
||||
libraryMenu.setTabs('plugins', 0, getTabs);
|
||||
reloadList(this);
|
||||
});
|
||||
|
||||
window.PluginsPage = {
|
||||
renderPlugins: renderPlugins
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="content-primary">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${TabRepositories}</h2>
|
||||
<button is="emby-button" type="button" class="fab btnNewRepository submit" style="margin-left:1em;" title="${ButtonAdd}">
|
||||
<button is="emby-button" type="button" class="fab btnNewRepository submit" style="margin-left:1em;" title="${Add}">
|
||||
<span class="material-icons add" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -69,7 +69,7 @@ function getRepositoryHtml(repository) {
|
|||
html += `<h3 class="listItemBodyText">${repository.Name}</h3>`;
|
||||
html += `<div class="listItemBodyText secondary">${repository.Url}</div>`;
|
||||
html += '</div>';
|
||||
html += `<button type="button" is="paper-icon-button-light" id="${repository.Url}" class="btnDelete" title="${globalize.translate('ButtonDelete')}"><span class="material-icons delete"></span></button>`;
|
||||
html += `<button type="button" is="paper-icon-button-light" id="${repository.Url}" class="btnDelete" title="${globalize.translate('Delete')}"><span class="material-icons delete"></span></button>`;
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
|
@ -105,7 +105,7 @@ export default function(view, params) {
|
|||
});
|
||||
|
||||
view.querySelector('.btnNewRepository').addEventListener('click', () => {
|
||||
let dialog = dialogHelper.createDialog({
|
||||
const dialog = dialogHelper.createDialog({
|
||||
scrollY: false,
|
||||
size: 'large',
|
||||
modal: false,
|
||||
|
@ -127,7 +127,7 @@ export default function(view, params) {
|
|||
html += `<input is="emby-input" id="txtRepositoryUrl" label="${globalize.translate('LabelRepositoryUrl')}" type="url" required />`;
|
||||
html += `<div class="fieldDescription">${globalize.translate('LabelRepositoryUrlHelp')}</div>`;
|
||||
html += '</div>';
|
||||
html += `<button is="emby-button" type="submit" class="raised button-submit block"><span>${globalize.translate('ButtonSave')}</span></button>`;
|
||||
html += `<button is="emby-button" type="submit" class="raised button-submit block"><span>${globalize.translate('Save')}</span></button>`;
|
||||
html += '</div>';
|
||||
html += '</form>';
|
||||
|
||||
|
|
85
src/controllers/dashboard/scheduledtasks/scheduledtask.html
Normal file
85
src/controllers/dashboard/scheduledtasks/scheduledtask.html
Normal file
|
@ -0,0 +1,85 @@
|
|||
<div id="scheduledTaskPage" data-role="page" class="page type-interior scheduledTasksConfigurationPage">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle taskName"></h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/tasks.html">${Help}</a>
|
||||
</div>
|
||||
<p id="pTaskDescription"></p>
|
||||
</div>
|
||||
|
||||
<div class="readOnlyContent">
|
||||
<div>
|
||||
<h2 style="vertical-align: middle; display: inline-block;">${HeaderTaskTriggers}</h2>
|
||||
<button is="emby-button" type="button" class="fab fab-mini btnAddTrigger submit" style="margin-left: 1em;" title="${ButtonAddScheduledTaskTrigger}">
|
||||
<span class="material-icons add"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="taskTriggers"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-role="popup" id="popupAddTrigger" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="addTriggerForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3>${ButtonAddScheduledTaskTrigger}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectTriggerType" class="selectTriggerType" label="${LabelTriggerType}">
|
||||
<option value="DailyTrigger">${OptionDaily}</option>
|
||||
<option value="WeeklyTrigger">${OptionWeekly}</option>
|
||||
<option value="IntervalTrigger">${OptionOnInterval}</option>
|
||||
<option value="StartupTrigger">${OnApplicationStartup}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="fldDayOfWeek" class="selectContainer">
|
||||
<select is="emby-select" id="selectDayOfWeek" name="selectDayOfWeek" label="${LabelDay}">
|
||||
<option value="Sunday">${Sunday}</option>
|
||||
<option value="Monday">${Monday}</option>
|
||||
<option value="Tuesday">${Tuesday}</option>
|
||||
<option value="Wednesday">${Wednesday}</option>
|
||||
<option value="Thursday">${Thursday}</option>
|
||||
<option value="Friday">${Friday}</option>
|
||||
<option value="Saturday">${Saturday}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="fldTimeOfDay" class="selectContainer">
|
||||
<select is="emby-select" id="selectTimeOfDay" label="${LabelTime}"></select>
|
||||
</div>
|
||||
<div id="fldSelectSystemEvent" class="selectContainer">
|
||||
<select is="emby-select" id="selectSystemEvent" name="selectSystemEvent" label="${LabelEvent}">
|
||||
<option value="WakeFromSleep">${OptionWakeFromSleep}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="fldSelectInterval" class="selectContainer">
|
||||
<select is="emby-select" id="selectInterval" label="${LabelEveryXMinutes}">
|
||||
<option value="9000000000">15 minutes</option>
|
||||
<option value="18000000000">30 minutes</option>
|
||||
<option value="27000000000">45 minutes</option>
|
||||
<option value="36000000000">1 hour</option>
|
||||
<option value="72000000000">2 hours</option>
|
||||
<option value="108000000000">3 hours</option>
|
||||
<option value="144000000000">4 hours</option>
|
||||
<option value="216000000000">6 hours</option>
|
||||
<option value="288000000000">8 hours</option>
|
||||
<option value="432000000000">12 hours</option>
|
||||
<option value="864000000000">24 hours</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtTimeLimit" type="number" pattern="[0-9]*" min="1" step=".5" label="${LabelTimeLimitHours}" />
|
||||
</div>
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check">
|
||||
<span>${Add}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -10,7 +10,6 @@ import 'emby-select';
|
|||
/* eslint-disable indent */
|
||||
|
||||
function fillTimeOfDay(select) {
|
||||
|
||||
const options = [];
|
||||
|
||||
for (let i = 0; i < 86400000; i += 900000) {
|
||||
|
@ -34,7 +33,7 @@ import 'emby-select';
|
|||
const ScheduledTaskPage = {
|
||||
refreshScheduledTask: function (view) {
|
||||
loading.show();
|
||||
let id = getParameterByName('id');
|
||||
const id = getParameterByName('id');
|
||||
ApiClient.getScheduledTask(id).then(function (task) {
|
||||
ScheduledTaskPage.loadScheduledTask(view, task);
|
||||
});
|
||||
|
@ -76,7 +75,7 @@ import 'emby-select';
|
|||
}
|
||||
|
||||
html += '</div>';
|
||||
html += '<button class="btnDeleteTrigger" data-index="' + i + '" type="button" is="paper-icon-button-light" title="' + globalize.translate('ButtonDelete') + '"><span class="material-icons delete"></span></button>';
|
||||
html += '<button class="btnDeleteTrigger" data-index="' + i + '" type="button" is="paper-icon-button-light" title="' + globalize.translate('Delete') + '"><span class="material-icons delete"></span></button>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
|
@ -85,21 +84,20 @@ import 'emby-select';
|
|||
},
|
||||
// TODO: Replace this mess with date-fns and remove datetime completely
|
||||
getTriggerFriendlyName: function (trigger) {
|
||||
if ('DailyTrigger' == trigger.Type) {
|
||||
if (trigger.Type == 'DailyTrigger') {
|
||||
return globalize.translate('DailyAt', ScheduledTaskPage.getDisplayTime(trigger.TimeOfDayTicks));
|
||||
}
|
||||
|
||||
if ('WeeklyTrigger' == trigger.Type) {
|
||||
if (trigger.Type == 'WeeklyTrigger') {
|
||||
// TODO: The day of week isn't localised as well
|
||||
return globalize.translate('WeeklyAt', trigger.DayOfWeek, ScheduledTaskPage.getDisplayTime(trigger.TimeOfDayTicks));
|
||||
}
|
||||
|
||||
if ('SystemEventTrigger' == trigger.Type && 'WakeFromSleep' == trigger.SystemEvent) {
|
||||
if (trigger.Type == 'SystemEventTrigger' && trigger.SystemEvent == 'WakeFromSleep') {
|
||||
return globalize.translate('OnWakeFromSleep');
|
||||
}
|
||||
|
||||
if (trigger.Type == 'IntervalTrigger') {
|
||||
|
||||
const hours = trigger.IntervalTicks / 36e9;
|
||||
|
||||
if (hours == 0.25) {
|
||||
|
@ -138,14 +136,14 @@ import 'emby-select';
|
|||
},
|
||||
confirmDeleteTrigger: function (view, index) {
|
||||
import('confirm').then(({default: confirm}) => {
|
||||
confirm.default(globalize.translate('MessageDeleteTaskTrigger'), globalize.translate('HeaderDeleteTaskTrigger')).then(function () {
|
||||
confirm(globalize.translate('MessageDeleteTaskTrigger'), globalize.translate('HeaderDeleteTaskTrigger')).then(function () {
|
||||
ScheduledTaskPage.deleteTrigger(view, index);
|
||||
});
|
||||
});
|
||||
},
|
||||
deleteTrigger: function (view, index) {
|
||||
loading.show();
|
||||
let id = getParameterByName('id');
|
||||
const id = getParameterByName('id');
|
||||
ApiClient.getScheduledTask(id).then(function (task) {
|
||||
task.Triggers.remove(index);
|
||||
ApiClient.updateScheduledTaskTriggers(task.Id, task.Triggers).then(function () {
|
||||
|
@ -213,7 +211,7 @@ import 'emby-select';
|
|||
export default function (view, params) {
|
||||
function onSubmit(e) {
|
||||
loading.show();
|
||||
let id = getParameterByName('id');
|
||||
const id = getParameterByName('id');
|
||||
ApiClient.getScheduledTask(id).then(function (task) {
|
||||
task.Triggers.push(ScheduledTaskPage.getTriggerToAdd(view));
|
||||
ApiClient.updateScheduledTaskTriggers(task.Id, task.Triggers).then(function () {
|
||||
|
|
20
src/controllers/dashboard/scheduledtasks/scheduledtasks.html
Normal file
20
src/controllers/dashboard/scheduledtasks/scheduledtasks.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<div id="scheduledTasksPage" data-role="page" class="page type-interior scheduledTasksConfigurationPage">
|
||||
<style>
|
||||
.taskProgressOuter {
|
||||
height: 6px;
|
||||
background: #eee;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.taskProgressInner {
|
||||
border-radius: 2px;
|
||||
height: 100%;
|
||||
background: #00a4dc;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="divScheduledTasks readOnlyContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -103,7 +103,7 @@ import 'emby-button';
|
|||
}
|
||||
|
||||
function setTaskButtonIcon(button, icon) {
|
||||
let inner = button.querySelector('.material-icons');
|
||||
const inner = button.querySelector('.material-icons');
|
||||
inner.classList.remove('stop', 'play_arrow');
|
||||
inner.classList.add(icon);
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ import 'emby-button';
|
|||
|
||||
$('.divScheduledTasks', view).on('click', '.btnStartTask', function() {
|
||||
const button = this;
|
||||
let id = button.getAttribute('data-taskid');
|
||||
const id = button.getAttribute('data-taskid');
|
||||
ApiClient.startScheduledTask(id).then(function() {
|
||||
updateTaskButton(button, 'Running');
|
||||
reloadList(view);
|
||||
|
@ -169,7 +169,7 @@ import 'emby-button';
|
|||
|
||||
$('.divScheduledTasks', view).on('click', '.btnStopTask', function() {
|
||||
const button = this;
|
||||
let id = button.getAttribute('data-taskid');
|
||||
const id = button.getAttribute('data-taskid');
|
||||
ApiClient.stopScheduledTask(id).then(function() {
|
||||
updateTaskButton(button, '');
|
||||
reloadList(view);
|
||||
|
|
12
src/controllers/dashboard/serveractivity.html
Normal file
12
src/controllers/dashboard/serveractivity.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<div id="serverActivityPage" data-role="page" class="page type-interior serverActivityPage noSecondaryNavPage" data-title="${HeaderActivity}">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="verticalSection">
|
||||
<h2 class="sectionTitle"></h2>
|
||||
</div>
|
||||
<div class="readOnlyContent">
|
||||
<div class="paperList activityItems" data-activitylimit="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
20
src/controllers/dashboard/streaming.html
Normal file
20
src/controllers/dashboard/streaming.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<div id="streamingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage withTabs">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<form class="streamingSettingsForm">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${TabStreaming}</h2>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtRemoteClientBitrateLimit" pattern="[0-9]*" min="0" step=".25" label="${LabelRemoteClientBitrateLimit}" />
|
||||
<div class="fieldDescription">${LabelRemoteClientBitrateLimitHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -27,7 +27,7 @@ import globalize from 'globalize';
|
|||
name: globalize.translate('Transcoding')
|
||||
}, {
|
||||
href: 'playbackconfiguration.html',
|
||||
name: globalize.translate('TabResumeSettings')
|
||||
name: globalize.translate('ButtonResume')
|
||||
}, {
|
||||
href: 'streamingsettings.html',
|
||||
name: globalize.translate('TabStreaming')
|
||||
|
|
205
src/controllers/dashboard/users/useredit.html
Normal file
205
src/controllers/dashboard/users/useredit.html
Normal file
|
@ -0,0 +1,205 @@
|
|||
<div id="editUserPage" data-role="page" class="page type-interior">
|
||||
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle username"></h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/users/">${Help}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-role="controlgroup" data-type="horizontal" class="localnav" id="userProfileNavigation" data-mini="true">
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="ui-btn-active">${Profile}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" onclick="Dashboard.navigate('userlibraryaccess.html', true);">${TabAccess}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" onclick="Dashboard.navigate('userparentalcontrol.html', true);">${TabParentalControl}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" onclick="Dashboard.navigate('userpassword.html', true);">${HeaderPassword}</a>
|
||||
</div>
|
||||
<p class="lnkEditUserPreferencesContainer">
|
||||
<a class="lnkEditUserPreferences button-link" href="#" is="emby-linkbutton">${ButtonEditOtherUserPreferences}</a>
|
||||
</p>
|
||||
<form class="editUserProfileForm">
|
||||
|
||||
<div class="disabledUserBanner" style="display: none;">
|
||||
<div class="btn btnDarkAccent btnStatic">
|
||||
<div>
|
||||
${HeaderThisUserIsCurrentlyDisabled}
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
${MessageReenableUser}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="fldUserName" class="inputContainer">
|
||||
<input is="emby-input" id="txtUserName" required type="text" label="${LabelName}" />
|
||||
</div>
|
||||
|
||||
<div class="selectContainer fldSelectLoginProvider hide">
|
||||
<select class="selectLoginProvider" is="emby-select" label="${LabelAuthProvider}"></select>
|
||||
<div class="fieldDescription">${AuthProviderHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer fldSelectPasswordResetProvider hide">
|
||||
<select class="selectPasswordResetProvider" is="emby-select" label="${LabelPasswordResetProvider}"></select>
|
||||
<div class="fieldDescription">${PasswordResetProviderHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription fldRemoteAccess hide">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkRemoteAccess" />
|
||||
<span>${AllowRemoteAccess}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${AllowRemoteAccessHelp}</div>
|
||||
</div>
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" id="chkIsAdmin" />
|
||||
<span>${OptionAllowUserToManageServer}</span>
|
||||
</label>
|
||||
<div id="featureAccessFields" class="verticalSection">
|
||||
<h2 class="paperListLabel">${HeaderFeatureAccess}</h2>
|
||||
<div class="checkboxList paperList" style="padding:.5em 1em;">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableLiveTvAccess" />
|
||||
<span>${OptionAllowBrowsingLiveTv}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkManageLiveTv" />
|
||||
<span>${OptionAllowManageLiveTv}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="verticalSection">
|
||||
<h2 class="paperListLabel">${HeaderPlayback}</h2>
|
||||
<div class="checkboxList paperList" style="padding:.5em 1em;">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableMediaPlayback" />
|
||||
<span>${OptionAllowMediaPlayback}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableAudioPlaybackTranscoding" />
|
||||
<span>${OptionAllowAudioPlaybackTranscoding}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableVideoPlaybackTranscoding" />
|
||||
<span>${OptionAllowVideoPlaybackTranscoding}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableVideoPlaybackRemuxing" />
|
||||
<span>${OptionAllowVideoPlaybackRemuxing}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkForceRemoteSourceTranscoding" />
|
||||
<span>${OptionForceRemoteSourceTranscoding}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="fieldDescription">${OptionAllowMediaPlaybackTranscodingHelp}</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="verticalSection">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtRemoteClientBitrateLimit" pattern="[0-9]*" min="0" step=".25" label="${LabelRemoteClientBitrateLimit}" />
|
||||
<div class="fieldDescription">${LabelRemoteClientBitrateLimitHelp}</div>
|
||||
<div class="fieldDescription">${LabelUserRemoteClientBitrateLimitHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="verticalSection">
|
||||
<div class="selectContainer fldSelectSyncPlayAccess">
|
||||
<select class="selectSyncPlayAccess" is="emby-select" id="selectSyncPlayAccess" label="${LabelSyncPlayAccess}">
|
||||
<option value="CreateAndJoinGroups">${LabelSyncPlayAccessCreateAndJoinGroups}</option>
|
||||
<option value="JoinGroups">${LabelSyncPlayAccessJoinGroups}</option>
|
||||
<option value="None">${LabelSyncPlayAccessNone}</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${SyncPlayAccessHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="verticalSection">
|
||||
<h2 class="checkboxListLabel" style="margin-bottom:1em;">${HeaderAllowMediaDeletionFrom}</h2>
|
||||
<div class="checkboxList paperList checkboxList-paperList">
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableDeleteAllFolders" />
|
||||
<span>${AllLibraries}</span>
|
||||
</label>
|
||||
<div class="deleteAccess">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="verticalSection">
|
||||
<h2 class="checkboxListLabel">${HeaderRemoteControl}</h2>
|
||||
<div class="checkboxList paperList" style="padding:.5em 1em;">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableRemoteControlOtherUsers" />
|
||||
<span>${OptionAllowRemoteControlOthers}</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkRemoteControlSharedDevices" />
|
||||
<span>${OptionAllowRemoteSharedDevices}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="fieldDescription">${OptionAllowRemoteSharedDevicesHelp}</div>
|
||||
</div>
|
||||
<div class="verticalSection">
|
||||
<h2 class="checkboxListLabel">${HeaderDownloadSync}</h2>
|
||||
<div class="checkboxList paperList" style="padding:.5em 1em;">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableDownloading" />
|
||||
<span>${OptionAllowContentDownloading}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableSyncTranscoding" />
|
||||
<span>${OptionAllowSyncTranscoding}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableConversion" />
|
||||
<span>${AllowMediaConversion}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${AllowMediaConversionHelp}</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableSharing" />
|
||||
<span>${OptionAllowLinkSharing}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription sharingHelp"></div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription" id="fldIsEnabled">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkDisabled" />
|
||||
<span>${OptionDisableUser}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionDisableUserHelp}</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription" id="fldIsHidden">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkIsHidden" />
|
||||
<span>${OptionHideUser}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionHideUserFromLoginHelp}</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class=verticalSection>
|
||||
<div class="inputContainer" id="fldLoginAttemptsBeforeLockout">
|
||||
<input is="emby-input" type="number" id="txtLoginAttemptsBeforeLockout" min="-1" step="1" label="${LabelUserLoginAttemptsBeforeLockout}"/>
|
||||
<div class="fieldDescription">${OptionLoginAttemptsBeforeLockout}</div>
|
||||
<div class="fieldDescription">${OptionLoginAttemptsBeforeLockoutHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
|
||||
<button is="emby-button" type="button" class="raised button-cancel block btnCancel" onclick="history.back();">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -14,13 +14,13 @@ import globalize from 'globalize';
|
|||
let html = '';
|
||||
|
||||
for (const folder of mediaFolders) {
|
||||
isChecked = user.Policy.EnableContentDeletion || -1 != user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id);
|
||||
isChecked = user.Policy.EnableContentDeletion || user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id) != -1;
|
||||
checkedAttribute = isChecked ? ' checked="checked"' : '';
|
||||
html += '<label><input type="checkbox" is="emby-checkbox" class="chkFolder" data-id="' + folder.Id + '" ' + checkedAttribute + '><span>' + folder.Name + '</span></label>';
|
||||
}
|
||||
|
||||
for (const folder of channelsResult.Items) {
|
||||
isChecked = user.Policy.EnableContentDeletion || -1 != user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id);
|
||||
isChecked = user.Policy.EnableContentDeletion || user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id) != -1;
|
||||
checkedAttribute = isChecked ? ' checked="checked"' : '';
|
||||
html += '<label><input type="checkbox" is="emby-checkbox" class="chkFolder" data-id="' + folder.Id + '" ' + checkedAttribute + '><span>' + folder.Name + '</span></label>';
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ import globalize from 'globalize';
|
|||
$('#chkEnableVideoPlaybackTranscoding', page).prop('checked', user.Policy.EnableVideoPlaybackTranscoding);
|
||||
$('#chkEnableVideoPlaybackRemuxing', page).prop('checked', user.Policy.EnablePlaybackRemuxing);
|
||||
$('#chkForceRemoteSourceTranscoding', page).prop('checked', user.Policy.ForceRemoteSourceTranscoding);
|
||||
$('#chkRemoteAccess', page).prop('checked', null == user.Policy.EnableRemoteAccess || user.Policy.EnableRemoteAccess);
|
||||
$('#chkRemoteAccess', page).prop('checked', user.Policy.EnableRemoteAccess == null || user.Policy.EnableRemoteAccess);
|
||||
$('#chkEnableSyncTranscoding', page).prop('checked', user.Policy.EnableSyncTranscoding);
|
||||
$('#chkEnableConversion', page).prop('checked', user.Policy.EnableMediaConversion || false);
|
||||
$('#chkEnableSharing', page).prop('checked', user.Policy.EnablePublicSharing);
|
||||
|
|
68
src/controllers/dashboard/users/userlibraryaccess.html
Normal file
68
src/controllers/dashboard/users/userlibraryaccess.html
Normal file
|
@ -0,0 +1,68 @@
|
|||
<div id="userLibraryAccessPage" data-role="page" class="page type-interior">
|
||||
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle username"></h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/users/">${Help}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('useredit.html', true);">${Profile}</a>
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userlibraryaccess.html', true);" class="ui-btn-active">${TabAccess}</a>
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userparentalcontrol.html', true);">${TabParentalControl}</a>
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userpassword.html', true);">${HeaderPassword}</a>
|
||||
</div>
|
||||
<form class="userLibraryAccessForm">
|
||||
|
||||
<div class="folderAccessContainer">
|
||||
<h2>${HeaderLibraryAccess}</h2>
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableAllFolders" />
|
||||
<span>${OptionEnableAccessToAllLibraries}</span>
|
||||
</label>
|
||||
<div class="folderAccessListContainer">
|
||||
<div class="folderAccess">
|
||||
</div>
|
||||
<div class="fieldDescription">${LibraryAccessHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="channelAccessContainer" style="display:none;">
|
||||
<h2>${HeaderChannelAccess}</h2>
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableAllChannels" />
|
||||
<span>${OptionEnableAccessToAllChannels}</span>
|
||||
</label>
|
||||
<div class="channelAccessListContainer">
|
||||
<div class="channelAccess">
|
||||
</div>
|
||||
<div class="fieldDescription">${ChannelAccessHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="deviceAccessContainer hide">
|
||||
<h2>${HeaderDeviceAccess}</h2>
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableAllDevices" />
|
||||
<span>${OptionEnableAccessFromAllDevices}</span>
|
||||
</label>
|
||||
<div class="deviceAccessListContainer">
|
||||
<div class="deviceAccess">
|
||||
</div>
|
||||
<div class="fieldDescription">${DeviceAccessHelp}</div>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -18,7 +18,7 @@ import globalize from 'globalize';
|
|||
|
||||
for (let i = 0, length = mediaFolders.length; i < length; i++) {
|
||||
const folder = mediaFolders[i];
|
||||
const isChecked = user.Policy.EnableAllFolders || -1 != user.Policy.EnabledFolders.indexOf(folder.Id);
|
||||
const isChecked = user.Policy.EnableAllFolders || user.Policy.EnabledFolders.indexOf(folder.Id) != -1;
|
||||
const checkedAttribute = isChecked ? ' checked="checked"' : '';
|
||||
html += '<label><input type="checkbox" is="emby-checkbox" class="chkFolder" data-id="' + folder.Id + '" ' + checkedAttribute + '><span>' + folder.Name + '</span></label>';
|
||||
}
|
||||
|
@ -32,12 +32,12 @@ import globalize from 'globalize';
|
|||
|
||||
function loadChannels(page, user, channels) {
|
||||
let html = '';
|
||||
html += '<h3 class="checkboxListLabel">' + globalize.translate('HeaderChannels') + '</h3>';
|
||||
html += '<h3 class="checkboxListLabel">' + globalize.translate('Channels') + '</h3>';
|
||||
html += '<div class="checkboxList paperList checkboxList-paperList">';
|
||||
|
||||
for (let i = 0, length = channels.length; i < length; i++) {
|
||||
const folder = channels[i];
|
||||
const isChecked = user.Policy.EnableAllChannels || -1 != user.Policy.EnabledChannels.indexOf(folder.Id);
|
||||
const isChecked = user.Policy.EnableAllChannels || user.Policy.EnabledChannels.indexOf(folder.Id) != -1;
|
||||
const checkedAttribute = isChecked ? ' checked="checked"' : '';
|
||||
html += '<label><input type="checkbox" is="emby-checkbox" class="chkChannel" data-id="' + folder.Id + '" ' + checkedAttribute + '><span>' + folder.Name + '</span></label>';
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ import globalize from 'globalize';
|
|||
|
||||
for (let i = 0, length = devices.length; i < length; i++) {
|
||||
const device = devices[i];
|
||||
const checkedAttribute = user.Policy.EnableAllDevices || -1 != user.Policy.EnabledDevices.indexOf(device.Id) ? ' checked="checked"' : '';
|
||||
const checkedAttribute = user.Policy.EnableAllDevices || user.Policy.EnabledDevices.indexOf(device.Id) != -1 ? ' checked="checked"' : '';
|
||||
html += '<label><input type="checkbox" is="emby-checkbox" class="chkDevice" data-id="' + device.Id + '" ' + checkedAttribute + '><span>' + device.Name + ' - ' + device.AppName + '</span></label>';
|
||||
}
|
||||
|
||||
|
|
62
src/controllers/dashboard/users/usernew.html
Normal file
62
src/controllers/dashboard/users/usernew.html
Normal file
|
@ -0,0 +1,62 @@
|
|||
<div id="newUserPage" data-role="page" class="page type-interior">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<form class="newUserProfileForm">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${ButtonAddUser}</h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/users/">${Help}</a>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtUsername" required type="text" label="${LabelName}" />
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtPassword" type="password" label="${LabelPassword}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="folderAccessContainer verticalSection">
|
||||
<h2 class="sectionTitle">${HeaderLibraryAccess}</h2>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableAllFolders" />
|
||||
<span>${OptionEnableAccessToAllLibraries}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LibraryAccessHelp}</div>
|
||||
</div>
|
||||
<div class="folderAccessListContainer">
|
||||
<div class="folderAccess">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="channelAccessContainer verticalSection verticalSection-extrabottompadding" style="display:none;">
|
||||
<h2 class="sectionTitle">${HeaderChannelAccess}</h2>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableAllChannels" />
|
||||
<span>${OptionEnableAccessToAllChannels}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${ChannelAccessHelp}</div>
|
||||
</div>
|
||||
<div class="channelAccessListContainer">
|
||||
<div class="channelAccess">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
|
||||
<button is="emby-button" type="button" class="raised button-cancel block btnCancel" onclick="history.back();">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -22,7 +22,7 @@ import 'emby-checkbox';
|
|||
|
||||
function loadChannels(page, channels) {
|
||||
let html = '';
|
||||
html += '<h3 class="checkboxListLabel">' + globalize.translate('HeaderChannels') + '</h3>';
|
||||
html += '<h3 class="checkboxListLabel">' + globalize.translate('Channels') + '</h3>';
|
||||
html += '<div class="checkboxList paperList" style="padding:.5em 1em;">';
|
||||
|
||||
for (let i = 0; i < channels.length; i++) {
|
||||
|
@ -89,7 +89,7 @@ import 'emby-checkbox';
|
|||
});
|
||||
}, function (response) {
|
||||
import('toast').then(({default: toast}) => {
|
||||
toast(globalize.translate('DefaultErrorMessage'));
|
||||
toast(globalize.translate('ErrorDefault'));
|
||||
});
|
||||
|
||||
loading.hide();
|
||||
|
|
60
src/controllers/dashboard/users/userparentalcontrol.html
Normal file
60
src/controllers/dashboard/users/userparentalcontrol.html
Normal file
|
@ -0,0 +1,60 @@
|
|||
<div id="userParentalControlPage" data-role="page" class="page type-interior">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle username"></h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/users/">${Help}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('useredit.html', true);">${Profile}</a>
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userlibraryaccess.html', true);">${TabAccess}</a>
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userparentalcontrol.html', true);" class="ui-btn-active">${TabParentalControl}</a>
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userpassword.html', true);">${HeaderPassword}</a>
|
||||
</div>
|
||||
|
||||
<form class="userParentalControlForm">
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectMaxParentalRating" label="${LabelMaxParentalRating}"></select>
|
||||
<div class="fieldDescription">${MaxParentalRatingHelp}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="blockUnratedItems"></div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="verticalSection" style="margin-bottom:2em;">
|
||||
<div class="detailSectionHeader sectionTitleContainer">
|
||||
<h2 class="sectionTitle">${LabelBlockContentWithTags}</h2>
|
||||
<button is="emby-button" type="button" class="fab btnAddBlockedTag submit" style="margin-left:1em;" title="${Add}">
|
||||
<span class="material-icons add"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="blockedTags" style="margin-top:.5em;"></div>
|
||||
</div>
|
||||
|
||||
<div class="accessScheduleSection verticalSection" style="margin-bottom:2em;">
|
||||
<div class="sectionTitleContainer">
|
||||
<h2 class="sectionTitle">${HeaderAccessSchedule}</h2>
|
||||
<button is="emby-button" type="button" class="fab btnAddSchedule submit" style="margin-left:1em;" title="${Add}">
|
||||
<span class="material-icons add"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p>${HeaderAccessScheduleHelp}</p>
|
||||
<div class="accessScheduleList paperList"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -40,25 +40,25 @@ import 'paper-icon-button-light';
|
|||
|
||||
function loadUnratedItems(page, user) {
|
||||
const items = [{
|
||||
name: globalize.translate('OptionBlockBooks'),
|
||||
name: globalize.translate('Books'),
|
||||
value: 'Book'
|
||||
}, {
|
||||
name: globalize.translate('OptionBlockChannelContent'),
|
||||
name: globalize.translate('Channels'),
|
||||
value: 'ChannelContent'
|
||||
}, {
|
||||
name: globalize.translate('OptionBlockLiveTvChannels'),
|
||||
name: globalize.translate('LiveTV'),
|
||||
value: 'LiveTvChannel'
|
||||
}, {
|
||||
name: globalize.translate('OptionBlockMovies'),
|
||||
name: globalize.translate('Movies'),
|
||||
value: 'Movie'
|
||||
}, {
|
||||
name: globalize.translate('OptionBlockMusic'),
|
||||
name: globalize.translate('Music'),
|
||||
value: 'Music'
|
||||
}, {
|
||||
name: globalize.translate('OptionBlockTrailers'),
|
||||
name: globalize.translate('Trailers'),
|
||||
value: 'Trailer'
|
||||
}, {
|
||||
name: globalize.translate('OptionBlockTvShows'),
|
||||
name: globalize.translate('Shows'),
|
||||
value: 'Series'
|
||||
}];
|
||||
let html = '';
|
||||
|
@ -67,7 +67,7 @@ import 'paper-icon-button-light';
|
|||
|
||||
for (let i = 0, length = items.length; i < length; i++) {
|
||||
const item = items[i];
|
||||
const checkedAttribute = -1 != user.Policy.BlockUnratedItems.indexOf(item.value) ? ' checked="checked"' : '';
|
||||
const checkedAttribute = user.Policy.BlockUnratedItems.indexOf(item.value) != -1 ? ' checked="checked"' : '';
|
||||
html += '<label><input type="checkbox" is="emby-checkbox" class="chkUnratedItem" data-itemtype="' + item.value + '" type="checkbox"' + checkedAttribute + '><span>' + item.name + '</span></label>';
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,7 @@ import 'paper-icon-button-light';
|
|||
}).then(function (updatedSchedule) {
|
||||
const schedules = getSchedulesFromPage(page);
|
||||
|
||||
if (-1 == index) {
|
||||
if (index == -1) {
|
||||
index = schedules.length;
|
||||
}
|
||||
|
||||
|
@ -228,14 +228,13 @@ import 'paper-icon-button-light';
|
|||
}
|
||||
|
||||
function showBlockedTagPopup(page) {
|
||||
|
||||
import('prompt').then(({default: prompt}) => {
|
||||
prompt({
|
||||
label: globalize.translate('LabelTag')
|
||||
}).then(function (value) {
|
||||
const tags = getBlockedTagsFromPage(page);
|
||||
|
||||
if (-1 == tags.indexOf(value)) {
|
||||
if (tags.indexOf(value) == -1) {
|
||||
tags.push(value);
|
||||
loadBlockedTags(page, tags);
|
||||
}
|
||||
|
|
72
src/controllers/dashboard/users/userpassword.html
Normal file
72
src/controllers/dashboard/users/userpassword.html
Normal file
|
@ -0,0 +1,72 @@
|
|||
<div id="userPasswordPage" data-role="page" class="page type-interior userPasswordPage">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle username"></h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/users/">${Help}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('useredit.html', true);">${Profile}</a>
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userlibraryaccess.html', true);">${TabAccess}</a>
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userparentalcontrol.html', true);">${TabParentalControl}</a>
|
||||
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userpassword.html', true);" class="ui-btn-active">${HeaderPassword}</a>
|
||||
</div>
|
||||
|
||||
<div class="readOnlyContent">
|
||||
<form class="updatePasswordForm passwordSection hide" style="margin: 0 auto 2em;">
|
||||
<div class="detailSection">
|
||||
<div id="fldCurrentPassword" class="inputContainer hide">
|
||||
<input is="emby-input" type="password" id="txtCurrentPassword" label="${LabelCurrentPassword}" autocomplete="off" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="password" id="txtNewPassword" label="${LabelNewPassword}" autocomplete="off" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="password" id="txtNewPasswordConfirm" label="${LabelNewPasswordConfirm}" autocomplete="off" />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block"><span>${Save}</span></button>
|
||||
<button is="emby-button" type="button" id="btnResetPassword" class="raised button-cancel block hide">
|
||||
<span>${ResetPassword}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<br />
|
||||
<form class="localAccessForm localAccessSection" style="margin: 0 auto;">
|
||||
<div class="detailSection">
|
||||
<div class="detailSectionHeader">
|
||||
${HeaderEasyPinCode}
|
||||
</div>
|
||||
<br />
|
||||
<div>${EasyPasswordHelp}</div>
|
||||
<br />
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtEasyPassword" label="${LabelEasyPinCode}" autocomplete="off" pattern="[0-9]*" step="1" maxlength="5" />
|
||||
</div>
|
||||
<br />
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkEnableLocalEasyPassword" />
|
||||
<span>${LabelInNetworkSignInWithEasyPassword}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelInNetworkSignInWithEasyPasswordHelp}</div>
|
||||
</div>
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" id="btnResetEasyPassword" class="raised button-cancel block hide">
|
||||
<span>${ButtonResetEasyPassword}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -14,7 +14,7 @@ import 'emby-button';
|
|||
let showPasswordSection = true;
|
||||
let showLocalAccessSection = false;
|
||||
|
||||
if ('Guest' == user.ConnectLinkType) {
|
||||
if (user.ConnectLinkType == 'Guest') {
|
||||
page.querySelector('.localAccessSection').classList.add('hide');
|
||||
showPasswordSection = false;
|
||||
} else if (user.HasConfiguredPassword) {
|
||||
|
@ -83,7 +83,7 @@ import 'emby-button';
|
|||
loading.hide();
|
||||
|
||||
import('toast').then(({default: toast}) => {
|
||||
toast(globalize.translate('MessageSettingsSaved'));
|
||||
toast(globalize.translate('SettingsSaved'));
|
||||
});
|
||||
|
||||
loadUser(view, params);
|
||||
|
@ -145,14 +145,14 @@ import 'emby-button';
|
|||
function resetPassword() {
|
||||
const msg = globalize.translate('PasswordResetConfirmation');
|
||||
import('confirm').then(({default: confirm}) => {
|
||||
confirm(msg, globalize.translate('PasswordResetHeader')).then(function () {
|
||||
confirm(msg, globalize.translate('ResetPassword')).then(function () {
|
||||
const userId = params.userId;
|
||||
loading.show();
|
||||
ApiClient.resetUserPassword(userId).then(function () {
|
||||
loading.hide();
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('PasswordResetComplete'),
|
||||
title: globalize.translate('PasswordResetHeader')
|
||||
title: globalize.translate('ResetPassword')
|
||||
});
|
||||
loadUser(view, params);
|
||||
});
|
||||
|
|
16
src/controllers/dashboard/users/userprofiles.html
Normal file
16
src/controllers/dashboard/users/userprofiles.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<div id="userProfilesPage" data-role="page" class="page type-interior userProfilesPage fullWidthContent">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="verticalSection verticalSection-extrabottompadding">
|
||||
<div class="sectionTitleContainer sectionTitleContainer-cards">
|
||||
<h2 class="sectionTitle sectionTitle-cards">${HeaderUsers}</h2>
|
||||
<button is="emby-button" type="button" class="fab btnAddUser submit sectionTitleButton" style="margin-left:1em;" title="${ButtonAddUser}">
|
||||
<span class="material-icons add"></span>
|
||||
</button>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" style="margin-left:2em!important;" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/users/adding-managing-users.html">${Help}</a>
|
||||
</div>
|
||||
<div class="localUsers itemsContainer vertical-wrap"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -18,7 +18,7 @@ import 'flexStyles';
|
|||
confirm({
|
||||
title: globalize.translate('DeleteUser'),
|
||||
text: msg,
|
||||
confirmText: globalize.translate('ButtonDelete'),
|
||||
confirmText: globalize.translate('Delete'),
|
||||
primary: 'delete'
|
||||
}).then(function () {
|
||||
loading.show();
|
||||
|
@ -50,7 +50,7 @@ import 'flexStyles';
|
|||
icon: 'person'
|
||||
});
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonDelete'),
|
||||
name: globalize.translate('Delete'),
|
||||
id: 'delete',
|
||||
icon: 'delete'
|
||||
});
|
||||
|
@ -129,7 +129,7 @@ import 'flexStyles';
|
|||
html += '</div>';
|
||||
html += '<div class="cardText cardText-secondary">';
|
||||
const lastSeen = getLastSeenText(user.LastActivityDate);
|
||||
html += '' != lastSeen ? lastSeen : ' ';
|
||||
html += lastSeen != '' ? lastSeen : ' ';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
|
@ -155,102 +155,12 @@ import 'flexStyles';
|
|||
page.querySelector('.localUsers').innerHTML = getUserSectionHtml(users, true);
|
||||
}
|
||||
|
||||
function showPendingUserMenu(elem) {
|
||||
const menuItems = [];
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonCancel'),
|
||||
id: 'delete',
|
||||
icon: 'delete'
|
||||
});
|
||||
|
||||
import('actionsheet').then(({default: actionsheet}) => {
|
||||
const card = dom.parentWithClass(elem, 'card');
|
||||
const page = dom.parentWithClass(card, 'page');
|
||||
const id = card.getAttribute('data-id');
|
||||
actionsheet.show({
|
||||
items: menuItems,
|
||||
positionTo: card,
|
||||
callback: function (menuItemId) {
|
||||
switch (menuItemId) {
|
||||
case 'delete':
|
||||
cancelAuthorization(page, id);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getPendingUserHtml(user) {
|
||||
let html = '';
|
||||
html += "<div data-id='" + user.Id + "' class='card squareCard scalableCard squareCard-scalable'>";
|
||||
html += '<div class="cardBox cardBox-bottompadded visualCardBox">';
|
||||
html += '<div class="cardScalable visualCardBox-cardScalable">';
|
||||
html += '<div class="cardPadder cardPadder-square"></div>';
|
||||
html += '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="#">';
|
||||
|
||||
if (user.ImageUrl) {
|
||||
html += '<div class="cardImage" style="background-image:url(\'' + user.ImageUrl + "');\">";
|
||||
html += '</div>';
|
||||
} else {
|
||||
html += '<span class="cardImageIcon material-icons person"></span>';
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
html += '</div>';
|
||||
html += '<div class="cardFooter visualCardBox-cardFooter">';
|
||||
html += '<div class="cardText" style="text-align:right; float:right;padding:0;">';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnUserMenu"><span class="material-icons more_vert"></span></button>';
|
||||
html += '</div>';
|
||||
html += '<div class="cardText" style="padding-top:10px;padding-bottom:10px;">';
|
||||
html += user.UserName;
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html + '</div>';
|
||||
}
|
||||
|
||||
function renderPendingGuests(page, users) {
|
||||
if (users.length) {
|
||||
page.querySelector('.sectionPendingGuests').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.sectionPendingGuests').classList.add('hide');
|
||||
}
|
||||
|
||||
page.querySelector('.pending').innerHTML = users.map(getPendingUserHtml).join('');
|
||||
}
|
||||
|
||||
// TODO cvium: maybe reuse for invitation system
|
||||
function cancelAuthorization(page, id) {
|
||||
loading.show();
|
||||
ApiClient.ajax({
|
||||
type: 'DELETE',
|
||||
url: ApiClient.getUrl('Connect/Pending', {
|
||||
Id: id
|
||||
})
|
||||
}).then(function () {
|
||||
loadData(page);
|
||||
});
|
||||
}
|
||||
|
||||
function loadData(page) {
|
||||
loading.show();
|
||||
ApiClient.getUsers().then(function (users) {
|
||||
renderUsers(page, users);
|
||||
loading.hide();
|
||||
});
|
||||
// TODO cvium
|
||||
renderPendingGuests(page, []);
|
||||
// ApiClient.getJSON(ApiClient.getUrl("Connect/Pending")).then(function (pending) {
|
||||
//
|
||||
// });
|
||||
}
|
||||
|
||||
function showInvitePopup(page) {
|
||||
import('components/guestinviter/guestinviter').then(({default: guestinviter}) => {
|
||||
guestinviter.show().then(function () {
|
||||
loadData(page);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pageIdOn('pageinit', 'userProfilesPage', function () {
|
||||
|
@ -265,14 +175,8 @@ import 'flexStyles';
|
|||
showUserMenu(btnUserMenu);
|
||||
}
|
||||
});
|
||||
page.querySelector('.pending').addEventListener('click', function (e__r) {
|
||||
const btnUserMenu = dom.parentWithClass(e__r.target, 'btnUserMenu');
|
||||
|
||||
if (btnUserMenu) {
|
||||
showPendingUserMenu(btnUserMenu);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
pageIdOn('pagebeforeshow', 'userProfilesPage', function () {
|
||||
loadData(this);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue