1
0
Fork 0
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:
Matt Montgomery 2020-09-03 16:17:35 -05:00
commit 9476edcbe2
431 changed files with 26974 additions and 36233 deletions

View 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>

View file

@ -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';

View 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>

View file

@ -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'

View 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>

View file

@ -1,5 +1,4 @@
import loading from 'loading';
import libraryMenu from 'libraryMenu';
import dom from 'dom';
import 'emby-input';
import 'emby-button';

View 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>

View file

@ -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({

View 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>

View file

@ -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();

View 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>

View file

@ -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')

View 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>

View file

@ -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')

View 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>

View file

@ -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 {

View 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>

View file

@ -52,7 +52,7 @@ import 'emby-button';
});
}, function () {
import('alert').then(({default: alert}) => {
alert(globalize.translate('DefaultErrorMessage'));
alert(globalize.translate('ErrorDefault'));
});
Dashboard.processServerConfigurationUpdateResult();

View 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>

View file

@ -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',

View 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>

View file

@ -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');

View 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>

View file

@ -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')

View 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>

View 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>

View file

@ -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')

View 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>

View file

@ -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;

View file

@ -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();">

View file

@ -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);
});

View file

@ -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);
});
}

View 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>

View file

@ -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')

View file

@ -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);
});
});
}

View file

@ -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) : '&nbsp;';
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) : '&nbsp;';
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);
});
}

View file

@ -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
};

View file

@ -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>

View file

@ -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>';

View 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>

View file

@ -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 () {

View 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>

View file

@ -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);

View 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>

View 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>

View file

@ -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')

View 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>

View file

@ -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);

View 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>

View file

@ -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>';
}

View 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>

View file

@ -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();

View 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>

View file

@ -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);
}

View 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>

View file

@ -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);
});

View 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>

View file

@ -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 : '&nbsp;';
html += lastSeen != '' ? lastSeen : '&nbsp;';
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);
});