mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge branch 'master' of https://github.com/jellyfin/jellyfin-web into fix-accese
# Conflicts: # src/scripts/site.js
This commit is contained in:
commit
8e724d3119
168 changed files with 12834 additions and 5898 deletions
|
@ -134,6 +134,7 @@
|
|||
<option value="reinhard">Reinhard</option>
|
||||
<option value="hable">Hable</option>
|
||||
<option value="mobius">Mobius</option>
|
||||
<option value="bt2390">BT.2390</option>
|
||||
</select>
|
||||
<div class="fieldDescription">
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="http://ffmpeg.org/ffmpeg-all.html#tonemap_005fopencl" target="_blank">${TonemappingAlgorithmHelp}</a>
|
||||
|
|
|
@ -49,6 +49,19 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${QuickConnect}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxList paperList" style="padding:.5em 1em;">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkQuickConnectAvailable" />
|
||||
<span>${EnableQuickConnect}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="verticalSection">
|
||||
<h2>${HeaderBranding}</h2>
|
||||
<div class="inputContainer">
|
||||
|
|
|
@ -14,6 +14,7 @@ import alert from '../../components/alert';
|
|||
function loadPage(page, config, languageOptions, systemInfo) {
|
||||
page.querySelector('#txtServerName').value = systemInfo.ServerName;
|
||||
page.querySelector('#txtCachePath').value = systemInfo.CachePath || '';
|
||||
page.querySelector('#chkQuickConnectAvailable').checked = config.QuickConnectAvailable === true;
|
||||
$('#txtMetadataPath', page).val(systemInfo.InternalMetadataPath || '');
|
||||
$('#txtMetadataNetworkPath', page).val(systemInfo.MetadataNetworkPath || '');
|
||||
$('#selectLocalizationLanguage', page).html(languageOptions.map(function (language) {
|
||||
|
@ -33,6 +34,7 @@ import alert from '../../components/alert';
|
|||
config.CachePath = form.querySelector('#txtCachePath').value;
|
||||
config.MetadataPath = $('#txtMetadataPath', form).val();
|
||||
config.MetadataNetworkPath = $('#txtMetadataNetworkPath', form).val();
|
||||
config.QuickConnectAvailable = form.querySelector('#chkQuickConnectAvailable').checked;
|
||||
ApiClient.updateServerConfiguration(config).then(function() {
|
||||
ApiClient.getNamedConfiguration(brandingConfigKey).then(function(brandingConfig) {
|
||||
brandingConfig.LoginDisclaimer = form.querySelector('#txtLoginDisclaimer').value;
|
||||
|
|
|
@ -1,6 +1,31 @@
|
|||
<div id="logPage" data-role="page" class="page type-interior">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<form class="logsForm">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${TabLogs}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="verticalSection">
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkSlowResponseWarning" />
|
||||
<span>${LabelSlowResponseEnabled}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtSlowResponseWarning" label="${LabelSlowResponseTime}" />
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="serverLogs readOnlyContent">
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,32 @@
|
|||
import datetime from '../../scripts/datetime';
|
||||
import loading from '../../components/loading/loading';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../components/listview/listview.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import Dashboard from '../../scripts/clientUtils';
|
||||
import alert from '../../components/alert';
|
||||
|
||||
/* eslint-disable indent */
|
||||
|
||||
function onSubmit() {
|
||||
loading.show();
|
||||
const form = this;
|
||||
ApiClient.getServerConfiguration().then(function (config) {
|
||||
config.EnableSlowResponseWarning = form.querySelector('#chkSlowResponseWarning').checked;
|
||||
config.SlowResponseThresholdMs = form.querySelector('#txtSlowResponseWarning').value;
|
||||
ApiClient.updateServerConfiguration(config).then(function() {
|
||||
Dashboard.processServerConfigurationUpdateResult();
|
||||
}, function () {
|
||||
alert(globalize.translate('ErrorDefault'));
|
||||
Dashboard.processServerConfigurationUpdateResult();
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
export default function(view) {
|
||||
view.querySelector('.logsForm').addEventListener('submit', onSubmit);
|
||||
view.addEventListener('viewbeforeshow', function() {
|
||||
loading.show();
|
||||
const apiClient = ApiClient;
|
||||
|
@ -32,8 +52,14 @@ import '../../assets/css/flexstyles.scss';
|
|||
}).join('');
|
||||
html += '</div>';
|
||||
view.querySelector('.serverLogs').innerHTML = html;
|
||||
loading.hide();
|
||||
});
|
||||
|
||||
apiClient.getServerConfiguration().then(function (config) {
|
||||
view.querySelector('#chkSlowResponseWarning').checked = config.EnableSlowResponseWarning;
|
||||
view.querySelector('#txtSlowResponseWarning').value = config.SlowResponseThresholdMs;
|
||||
});
|
||||
|
||||
loading.hide();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import 'jquery';
|
||||
import marked from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import loading from '../../../../components/loading/loading';
|
||||
import globalize from '../../../../scripts/globalize';
|
||||
import '../../../../elements/emby-button/emby-button';
|
||||
|
@ -13,7 +15,7 @@ function populateHistory(packageInfo, page) {
|
|||
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>';
|
||||
html += '<div style="margin-bottom:1.5em;">' + DOMPurify.sanitize(marked(version.changelog)) + '</div>';
|
||||
}
|
||||
|
||||
$('#revisionHistory', page).html(html);
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<div id="quickConnectPage" data-role="page" class="page type-interior advancedConfigurationPage">
|
||||
<div class="content-primary">
|
||||
<form class="quickConnectSettings">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${QuickConnect}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>${LabelCurrentStatus}<span id="quickConnectStatus" style="padding:0 0.4em;"></span></div>
|
||||
|
||||
<div class="checkboxList paperList" style="padding:.5em 1em;">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkQuickConnectAvailable" />
|
||||
<span>${EnableQuickConnect}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button is="emby-button" id="btnQuickConnectSubmit" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -1,58 +0,0 @@
|
|||
import loading from '../../components/loading/loading';
|
||||
import toast from '../../components/toast/toast';
|
||||
import globalize from '../../scripts/globalize';
|
||||
|
||||
const unavailable = 'Unavailable';
|
||||
const available = 'Available';
|
||||
const active = 'Active';
|
||||
let page;
|
||||
|
||||
export default function(view) {
|
||||
view.addEventListener('viewshow', function () {
|
||||
page = this;
|
||||
loading.show();
|
||||
page.querySelector('#btnQuickConnectSubmit').onclick = onSubmit;
|
||||
updatePage();
|
||||
});
|
||||
}
|
||||
|
||||
function loadPage(status) {
|
||||
const check = status === available || status === active;
|
||||
|
||||
page.querySelector('#quickConnectStatus').textContent = status.toLocaleLowerCase();
|
||||
page.querySelector('#chkQuickConnectAvailable').checked = check;
|
||||
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
loading.show();
|
||||
|
||||
const newStatus = page.querySelector('#chkQuickConnectAvailable').checked ? available : unavailable;
|
||||
|
||||
const url = ApiClient.getUrl('/QuickConnect/Available?Status=' + newStatus);
|
||||
|
||||
ApiClient.ajax({
|
||||
type: 'POST',
|
||||
url: url
|
||||
}, true).then(() => {
|
||||
toast(globalize.translate('SettingsSaved'));
|
||||
setTimeout(updatePage, 500);
|
||||
|
||||
return true;
|
||||
}).catch((e) => {
|
||||
console.error('Unable to set quick connect status. error:', e);
|
||||
});
|
||||
|
||||
loading.hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
function updatePage() {
|
||||
ApiClient.getQuickConnect('Status').then((response) => {
|
||||
loadPage(response);
|
||||
return true;
|
||||
}).catch((e) => {
|
||||
console.error('Unable to get quick connect status. error:', e);
|
||||
});
|
||||
}
|
|
@ -1,15 +1,12 @@
|
|||
<div id="itemDetailPage" data-role="page" class="page libraryPage itemDetailPage noSecondaryNavPage selfBackdropPage" data-backbutton="true">
|
||||
<div id="itemBackdrop" class="itemBackdrop">
|
||||
</div>
|
||||
<div id="itemBackdrop" class="itemBackdrop"></div>
|
||||
|
||||
<div class="detailLogo"></div>
|
||||
|
||||
<div class="detailPageWrapperContainer padded-bottom-page">
|
||||
<div class="detailPagePrimaryContainer padded-left padded-right">
|
||||
<div class="primaryImageWrapper hide">
|
||||
<img id="primaryImage" />
|
||||
</div>
|
||||
|
||||
<div class="infoWrapper infoText">
|
||||
<div class="infoWrapper">
|
||||
<div class="detailImageContainer padded-left"></div>
|
||||
<div class="nameContainer"></div>
|
||||
<div class="itemMiscInfo itemMiscInfo-primary" style="margin-bottom: 0.6em;"></div>
|
||||
<div class="itemMiscInfo itemMiscInfo-secondary" style="margin-bottom: 0.6em;"></div>
|
||||
|
@ -90,29 +87,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="detailPageSecondaryContainer">
|
||||
<div class="detailImageContainer padded-left"></div>
|
||||
<div class="detailPageContent">
|
||||
<div class="detailPagePrimaryContent padded-right">
|
||||
<div class="detailSection">
|
||||
<div class="itemMiscInfo nativeName hide"></div>
|
||||
|
||||
<div class="itemDetailsGroup">
|
||||
<div class="detailsGroupItem genresGroup hide">
|
||||
<div class="genresLabel label"></div>
|
||||
<div class="genres content focuscontainer-x"></div>
|
||||
</div>
|
||||
|
||||
<div class="detailsGroupItem directorsGroup hide">
|
||||
<div class="directorsLabel label"></div>
|
||||
<div class="directors content focuscontainer-x"></div>
|
||||
</div>
|
||||
|
||||
<div class="detailsGroupItem writersGroup hide">
|
||||
<div class="writersLabel label"></div>
|
||||
<div class="writers content focuscontainer-x"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="trackSelections hide focuscontainer-x">
|
||||
<div class="selectContainer selectSourceContainer hide trackSelectionFieldContainer flex-shrink-zero">
|
||||
<select is="emby-select" class="selectSource detailTrackSelect" label=""></select>
|
||||
|
@ -129,6 +108,7 @@
|
|||
</form>
|
||||
|
||||
<div class="recordingFields hide" style="margin: 0.5em 0 1.5em;"></div>
|
||||
|
||||
<div class="detailSectionContent">
|
||||
<div class="itemLastPlayed hide"></div>
|
||||
|
||||
|
@ -147,6 +127,23 @@
|
|||
<div class="itemExternalLinks focuscontainer-x hide" style="margin: 0.7em 0; font-size: 92%;"></div>
|
||||
<div class="seriesRecordingEditor"></div>
|
||||
</div>
|
||||
|
||||
<div class="itemDetailsGroup">
|
||||
<div class="detailsGroupItem genresGroup hide">
|
||||
<div class="genresLabel label"></div>
|
||||
<div class="genres content focuscontainer-x"></div>
|
||||
</div>
|
||||
|
||||
<div class="detailsGroupItem directorsGroup hide">
|
||||
<div class="directorsLabel label"></div>
|
||||
<div class="directors content focuscontainer-x"></div>
|
||||
</div>
|
||||
|
||||
<div class="detailsGroupItem writersGroup hide">
|
||||
<div class="writersLabel label"></div>
|
||||
<div class="writers content focuscontainer-x"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -367,6 +367,14 @@ function reloadPlayButtons(page, item) {
|
|||
hideAll(page, 'btnShuffle');
|
||||
}
|
||||
|
||||
const btnResume = page.querySelector('.mainDetailButtons .btnResume');
|
||||
const btnPlay = page.querySelector('.mainDetailButtons .btnPlay');
|
||||
if (layoutManager.tv && !btnResume.classList.contains('hide')) {
|
||||
btnResume.classList.add('fab');
|
||||
} else if (layoutManager.tv && btnResume.classList.contains('hide')) {
|
||||
btnPlay.classList.add('fab');
|
||||
}
|
||||
|
||||
return canPlay;
|
||||
}
|
||||
|
||||
|
@ -552,14 +560,20 @@ function renderBackdrop(item) {
|
|||
}
|
||||
|
||||
function renderDetailPageBackdrop(page, item, apiClient) {
|
||||
// Details banner is disabled in user settings
|
||||
if (!userSettings.detailsBanner()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable item backdrop for books and people because they only have primary images
|
||||
if (item.Type === 'Person' || item.Type === 'Book') {
|
||||
return false;
|
||||
}
|
||||
|
||||
let imgUrl;
|
||||
let hasbackdrop = false;
|
||||
const itemBackdropElement = page.querySelector('#itemBackdrop');
|
||||
|
||||
if (layoutManager.mobile || !userSettings.detailsBanner()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.BackdropImageTags && item.BackdropImageTags.length) {
|
||||
imgUrl = apiClient.getScaledImageUrl(item.Id, {
|
||||
type: 'Backdrop',
|
||||
|
@ -593,24 +607,6 @@ function renderDetailPageBackdrop(page, item, apiClient) {
|
|||
return hasbackdrop;
|
||||
}
|
||||
|
||||
function renderPrimaryImage(page, item, apiClient) {
|
||||
if (item?.ImageTags?.Primary) {
|
||||
const imageUrl = apiClient.getScaledImageUrl(item.Id, {
|
||||
type: 'Primary',
|
||||
maxWidth: dom.getScreenWidth(),
|
||||
tag: item.ImageTags.Primary
|
||||
});
|
||||
|
||||
const imageElem = page.querySelector('#primaryImage');
|
||||
imageElem.src = imageUrl;
|
||||
imageElem.alt = item.Name;
|
||||
if (item.PrimaryImageAspectRatio === 1) {
|
||||
imageElem.classList.add('aspect-square');
|
||||
}
|
||||
page.querySelector('.primaryImageWrapper')?.classList.remove('hide');
|
||||
}
|
||||
}
|
||||
|
||||
function reloadFromItem(instance, page, params, item, user) {
|
||||
const apiClient = ServerConnections.getApiClient(item.ServerId);
|
||||
|
||||
|
@ -623,9 +619,7 @@ function reloadFromItem(instance, page, params, item, user) {
|
|||
renderLogo(page, item, apiClient);
|
||||
renderDetailPageBackdrop(page, item, apiClient);
|
||||
}
|
||||
if (layoutManager.mobile) {
|
||||
renderPrimaryImage(page, item, apiClient);
|
||||
}
|
||||
|
||||
renderBackdrop(item);
|
||||
|
||||
// Render the main information for the item
|
||||
|
@ -812,8 +806,8 @@ function renderDetailImage(elem, item, imageLoader) {
|
|||
overlayText: false,
|
||||
transition: false,
|
||||
disableIndicators: true,
|
||||
overlayPlayButton: true,
|
||||
action: 'play',
|
||||
overlayPlayButton: layoutManager.mobile ? false : true,
|
||||
action: layoutManager.mobile ? 'none' : 'play',
|
||||
width: dom.getWindowSize().innerWidth * 0.25
|
||||
});
|
||||
|
||||
|
@ -1216,11 +1210,9 @@ function renderMoreFromArtist(view, item, apiClient) {
|
|||
};
|
||||
|
||||
if (item.Type === 'MusicArtist') {
|
||||
query.ContributingArtistIds = item.Id;
|
||||
} else if (apiClient.isMinServerVersion('3.4.1.18')) {
|
||||
query.AlbumArtistIds = item.AlbumArtists[0].Id;
|
||||
query.AlbumArtistIds = item.Id;
|
||||
} else {
|
||||
query.ArtistIds = item.AlbumArtists[0].Id;
|
||||
query.AlbumArtistIds = item.AlbumArtists[0].Id;
|
||||
}
|
||||
|
||||
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (result) {
|
||||
|
@ -2063,16 +2055,6 @@ export default function (view, params) {
|
|||
function init() {
|
||||
const apiClient = getApiClient();
|
||||
|
||||
const btnResume = view.querySelector('.mainDetailButtons .btnResume');
|
||||
const btnPlay = view.querySelector('.mainDetailButtons .btnPlay');
|
||||
if (layoutManager.tv && !btnResume.classList.contains('hide')) {
|
||||
btnResume.classList.add('fab');
|
||||
btnResume.classList.add('detailFloatingButton');
|
||||
} else if (layoutManager.tv && btnResume.classList.contains('hide')) {
|
||||
btnPlay.classList.add('fab');
|
||||
btnPlay.classList.add('detailFloatingButton');
|
||||
}
|
||||
|
||||
view.querySelectorAll('.btnPlay');
|
||||
bindAll(view, '.btnPlay', 'click', onPlayClick);
|
||||
bindAll(view, '.btnResume', 'click', onPlayClick);
|
||||
|
|
|
@ -1115,7 +1115,11 @@ class ItemsView {
|
|||
let imageType = userSettings.get(basekey + '-imageType');
|
||||
|
||||
if (!imageType && params.type === 'nextup') {
|
||||
imageType = 'thumb';
|
||||
if (userSettings.useEpisodeImagesInNextUpAndResume()) {
|
||||
imageType = 'primary';
|
||||
} else {
|
||||
imageType = 'thumb';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<div class="pageTabContent flexPageTabContent absolutePageTabContent" id="guideTab" data-index="1" style="width:auto;padding-top:0; padding-bottom: 0!important;">
|
||||
</div>
|
||||
<div class="pageTabContent" id="channelsTab" data-index="2">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnFilter sectionTitleButton" title="${Filter}"><span class="material-icons filter_list"></span></button>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div id="moviesPage" data-role="page" data-dom-cache="true" class="page libraryPage backdropPage collectionEditorPage pageWithAbsoluteTabs withTabs" data-backdroptype="movie">
|
||||
|
||||
<div class="pageTabContent" id="moviesTab" data-index="0">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
|
||||
<button is="paper-icon-button-light" class="btnSort autoSize" title="${Sort}"><span class="material-icons sort_by_alpha"></span></button>
|
||||
|
@ -13,7 +13,7 @@
|
|||
|
||||
<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right">
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,7 +44,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="pageTabContent" id="trailersTab" data-index="2">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnSort autoSize" title="${Sort}"><span class="material-icons sort_by_alpha"></span></button>
|
||||
<button is="paper-icon-button-light" class="btnFilter autoSize" title="${Filter}"><span class="material-icons filter_list"></span></button>
|
||||
|
@ -55,24 +55,24 @@
|
|||
|
||||
<div is="emby-itemscontainer" class="itemsContainer vertical-wrap padded-left padded-right">
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pageTabContent" id="favoritesTab" data-index="3">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
|
||||
</div>
|
||||
|
||||
<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right">
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pageTabContent" id="collectionsTab" data-index="4">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
|
||||
<button is="paper-icon-button-light" class="btnSort autoSize" title="${Sort}"><span class="material-icons sort_by_alpha"></span></button>
|
||||
|
@ -81,7 +81,7 @@
|
|||
|
||||
<div is="emby-itemscontainer" class="itemsContainer vertical-wrap centered padded-left padded-right" style="text-align:center;">
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
}
|
||||
</style>
|
||||
<div class="pageTabContent pageTabContent" id="albumsTab" data-index="0">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnPlayAll musicglobalButton" title="${HeaderPlayAll}"><span class="material-icons play_arrow"></span></button>
|
||||
<button is="paper-icon-button-light" class="btnShuffle musicglobalButton" title="${Shuffle}"><span class="material-icons shuffle"></span></button>
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right">
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -54,7 +54,7 @@
|
|||
<div class="favoriteSections verticalSection"></div>
|
||||
</div>
|
||||
<div class="pageTabContent" id="albumArtistsTab" data-index="2">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
|
||||
<button is="paper-icon-button-light" class="btnFilter autoSize" title="${Filter}"><span class="material-icons filter_list"></span></button>
|
||||
|
@ -65,12 +65,12 @@
|
|||
|
||||
<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right">
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pageTabContent" id="artistsTab" data-index="3">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
|
||||
<button is="paper-icon-button-light" class="btnFilter autoSize" title="${Filter}"><span class="material-icons filter_list"></span></button>
|
||||
|
@ -81,7 +81,7 @@
|
|||
|
||||
<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right">
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -90,7 +90,7 @@
|
|||
<div is="emby-itemscontainer" id="items" class="itemsContainer padded-left padded-right padded-top vertical-wrap centered"></div>
|
||||
</div>
|
||||
<div class="pageTabContent" id="songsTab" data-index="5">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnSort autoSize" title="${Sort}"><span class="material-icons sort_by_alpha"></span></button>
|
||||
<button is="paper-icon-button-light" class="btnFilter autoSize" title="${Filter}"><span class="material-icons filter_list"></span></button>
|
||||
|
@ -98,7 +98,7 @@
|
|||
|
||||
<div is="emby-itemscontainer" id="items" class="itemsContainer vertical-list" style="max-width:67.5em;margin: 0 auto;"></div>
|
||||
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
<span class="xlargePaperIconButton material-icons skip_previous"></span>
|
||||
</button>
|
||||
|
||||
<button is="paper-icon-button-light" class="btnPreviousChapter autoSize hide" title="${PreviousChapter}">
|
||||
<span class="xlargePaperIconButton material-icons undo"></span>
|
||||
</button>
|
||||
|
||||
<button is="paper-icon-button-light" class="btnRewind" title="${Rewind} (j)">
|
||||
<span class="xlargePaperIconButton material-icons fast_rewind"></span>
|
||||
</button>
|
||||
|
@ -47,6 +51,10 @@
|
|||
<span class="xlargePaperIconButton material-icons fast_forward"></span>
|
||||
</button>
|
||||
|
||||
<button is="paper-icon-button-light" class="btnNextChapter autoSize hide" title="${NextChapter}">
|
||||
<span class="xlargePaperIconButton material-icons redo"></span>
|
||||
</button>
|
||||
|
||||
<button is="paper-icon-button-light" class="btnNextTrack autoSize hide" title="${NextTrack}">
|
||||
<span class="xlargePaperIconButton material-icons skip_next"></span>
|
||||
</button>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { playbackManager } from '../../../components/playback/playbackmanager';
|
||||
import SyncPlay from '../../../components/syncPlay/core';
|
||||
import browser from '../../../scripts/browser';
|
||||
import dom from '../../../scripts/dom';
|
||||
import inputManager from '../../../scripts/inputManager';
|
||||
import mouseManager from '../../../scripts/mouseManager';
|
||||
|
@ -180,6 +181,14 @@ import { appRouter } from '../../../components/appRouter';
|
|||
} else {
|
||||
view.querySelector('.btnAudio').classList.add('hide');
|
||||
}
|
||||
|
||||
if (currentItem.Chapters.length > 1) {
|
||||
view.querySelector('.btnPreviousChapter').classList.remove('hide');
|
||||
view.querySelector('.btnNextChapter').classList.remove('hide');
|
||||
} else {
|
||||
view.querySelector('.btnPreviousChapter').classList.add('hide');
|
||||
view.querySelector('.btnNextChapter').classList.add('hide');
|
||||
}
|
||||
}
|
||||
|
||||
function setTitle(item, parentName) {
|
||||
|
@ -312,8 +321,8 @@ import { appRouter } from '../../../components/appRouter';
|
|||
|
||||
function onPointerMove(e) {
|
||||
if ((e.pointerType || (layoutManager.mobile ? 'touch' : 'mouse')) === 'mouse') {
|
||||
const eventX = e.screenX || 0;
|
||||
const eventY = e.screenY || 0;
|
||||
const eventX = e.screenX || e.clientX || 0;
|
||||
const eventY = e.screenY || e.clientY || 0;
|
||||
const obj = lastPointerMoveData;
|
||||
|
||||
if (!obj) {
|
||||
|
@ -544,7 +553,7 @@ import { appRouter } from '../../../components/appRouter';
|
|||
const player = this;
|
||||
currentRuntimeTicks = playbackManager.duration(player);
|
||||
const currentTime = playbackManager.currentTime(player) * 10000;
|
||||
updateTimeDisplay(currentTime, currentRuntimeTicks, playbackManager.playbackStartTime(player), playbackManager.getBufferedRanges(player));
|
||||
updateTimeDisplay(currentTime, currentRuntimeTicks, playbackManager.playbackStartTime(player), playbackManager.getPlaybackRate(player), playbackManager.getBufferedRanges(player));
|
||||
const item = currentItem;
|
||||
refreshProgramInfoIfNeeded(player, item);
|
||||
showComingUpNextIfNeeded(player, item, currentTime, currentRuntimeTicks);
|
||||
|
@ -639,7 +648,7 @@ import { appRouter } from '../../../components/appRouter';
|
|||
btnRewind.disabled = !playState.CanSeek;
|
||||
const nowPlayingItem = state.NowPlayingItem || {};
|
||||
playbackStartTimeTicks = playState.PlaybackStartTimeTicks;
|
||||
updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks, playState.PlaybackStartTimeTicks, playState.BufferedRanges || []);
|
||||
updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks, playState.PlaybackStartTimeTicks, playState.PlaybackRate, playState.BufferedRanges || []);
|
||||
updateNowPlayingInfo(player, state);
|
||||
|
||||
if (state.MediaSource && state.MediaSource.SupportsTranscoding && supportedCommands.indexOf('SetMaxStreamingBitrate') !== -1) {
|
||||
|
@ -681,7 +690,7 @@ import { appRouter } from '../../../components/appRouter';
|
|||
return (currentTimeMs - programStartDateMs) / programRuntimeMs * 100;
|
||||
}
|
||||
|
||||
function updateTimeDisplay(positionTicks, runtimeTicks, playbackStartTimeTicks, bufferedRanges) {
|
||||
function updateTimeDisplay(positionTicks, runtimeTicks, playbackStartTimeTicks, playbackRate, bufferedRanges) {
|
||||
if (enableProgressByTimeOfDay) {
|
||||
if (nowPlayingPositionSlider && !nowPlayingPositionSlider.dragging) {
|
||||
if (programStartDateMs && programEndDateMs) {
|
||||
|
@ -716,8 +725,8 @@ import { appRouter } from '../../../components/appRouter';
|
|||
nowPlayingPositionSlider.value = 0;
|
||||
}
|
||||
|
||||
if (runtimeTicks && positionTicks != null && currentRuntimeTicks && !enableProgressByTimeOfDay && currentItem.RunTimeTicks && currentItem.Type !== 'Recording') {
|
||||
endsAtText.innerHTML = ' ' + mediaInfo.getEndsAtFromPosition(runtimeTicks, positionTicks, true);
|
||||
if (runtimeTicks && positionTicks != null && currentRuntimeTicks && !enableProgressByTimeOfDay && currentItem.RunTimeTicks && currentItem.Type !== 'Recording' && playbackRate !== null) {
|
||||
endsAtText.innerHTML = ' ' + mediaInfo.getEndsAtFromPosition(runtimeTicks, positionTicks, playbackRate, true);
|
||||
} else {
|
||||
endsAtText.innerHTML = '';
|
||||
}
|
||||
|
@ -987,14 +996,30 @@ import { appRouter } from '../../../components/appRouter';
|
|||
*/
|
||||
let clickedElement;
|
||||
|
||||
function onClickCapture(e) {
|
||||
// Firefox/Edge emits `click` even if `preventDefault` was used on `keydown`
|
||||
// Ignore 'click' if another element was originally clicked
|
||||
if (!e.target.contains(clickedElement)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function onKeyDown(e) {
|
||||
clickedElement = e.target;
|
||||
|
||||
const key = keyboardnavigation.getKeyName(e);
|
||||
const isKeyModified = e.ctrlKey || e.altKey || e.metaKey;
|
||||
|
||||
if (!currentVisibleMenu && e.keyCode === 32) {
|
||||
playbackManager.playPause(currentPlayer);
|
||||
if (e.keyCode === 32) {
|
||||
if (e.target.tagName !== 'BUTTON' || !layoutManager.tv) {
|
||||
playbackManager.playPause(currentPlayer);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// Trick Firefox with a null element to skip next click
|
||||
clickedElement = null;
|
||||
}
|
||||
showOsd();
|
||||
return;
|
||||
}
|
||||
|
@ -1304,6 +1329,9 @@ import { appRouter } from '../../../components/appRouter';
|
|||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
if (browser.firefox || browser.edge) {
|
||||
dom.addEventListener(document, 'click', onClickCapture, { capture: true });
|
||||
}
|
||||
} catch (e) {
|
||||
appRouter.goHome();
|
||||
}
|
||||
|
@ -1342,6 +1370,9 @@ import { appRouter } from '../../../components/appRouter';
|
|||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
if (browser.firefox || browser.edge) {
|
||||
dom.removeEventListener(document, 'click', onClickCapture, { capture: true });
|
||||
}
|
||||
stopOsdHideTimer();
|
||||
headerElement.classList.remove('osdHeader');
|
||||
headerElement.classList.remove('osdHeader-hidden');
|
||||
|
@ -1490,11 +1521,14 @@ import { appRouter } from '../../../components/appRouter';
|
|||
view.querySelector('.btnPreviousTrack').addEventListener('click', function () {
|
||||
playbackManager.previousTrack(currentPlayer);
|
||||
});
|
||||
view.querySelector('.btnPreviousChapter').addEventListener('click', function () {
|
||||
playbackManager.previousChapter(currentPlayer);
|
||||
});
|
||||
view.querySelector('.btnPause').addEventListener('click', function () {
|
||||
// Ignore 'click' if another element was originally clicked (Firefox/Edge issue)
|
||||
if (this.contains(clickedElement)) {
|
||||
playbackManager.playPause(currentPlayer);
|
||||
}
|
||||
playbackManager.playPause(currentPlayer);
|
||||
});
|
||||
view.querySelector('.btnNextChapter').addEventListener('click', function () {
|
||||
playbackManager.nextChapter(currentPlayer);
|
||||
});
|
||||
view.querySelector('.btnNextTrack').addEventListener('click', function () {
|
||||
playbackManager.nextTrack(currentPlayer);
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
<div id="searchPage" data-role="page" class="page libraryPage allLibraryPage noSecondaryNavPage" data-title="${Search}" data-backbutton="true">
|
||||
<div class="padded-left padded-right searchFields"></div>
|
||||
<div class="searchResults padded-bottom-page padded-top"></div>
|
||||
</div>
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import SearchFields from '../components/search/searchfields';
|
||||
import SearchResults from '../components/search/searchresults';
|
||||
import { Events } from 'jellyfin-apiclient';
|
||||
|
||||
export default function (view, params) {
|
||||
function onSearch(e, value) {
|
||||
self.searchResults.search(value);
|
||||
}
|
||||
|
||||
const self = this;
|
||||
view.addEventListener('viewshow', function () {
|
||||
if (!self.searchFields) {
|
||||
self.searchFields = new SearchFields({
|
||||
element: view.querySelector('.searchFields')
|
||||
});
|
||||
self.searchResults = new SearchResults({
|
||||
element: view.querySelector('.searchResults'),
|
||||
serverId: params.serverId || ApiClient.serverId(),
|
||||
parentId: params.parentId,
|
||||
collectionType: params.collectionType
|
||||
});
|
||||
Events.on(self.searchFields, 'search', onSearch);
|
||||
}
|
||||
});
|
||||
view.addEventListener('viewdestroy', function () {
|
||||
if (self.searchFields) {
|
||||
self.searchFields.destroy();
|
||||
self.searchFields = null;
|
||||
}
|
||||
|
||||
if (self.searchResults) {
|
||||
self.searchResults.destroy();
|
||||
self.searchResults = null;
|
||||
}
|
||||
});
|
||||
}
|
|
@ -76,7 +76,7 @@ import cardBuilder from '../../../components/cardbuilder/cardBuilder';
|
|||
dialogHelper.close(dlg);
|
||||
}
|
||||
|
||||
const result = await apiClient.quickConnect(data.Authentication);
|
||||
const result = await apiClient.quickConnect(data.Secret);
|
||||
onLoginSuccessful(result.User.Id, result.AccessToken, apiClient);
|
||||
}, function (e) {
|
||||
clearInterval(interval);
|
||||
|
@ -260,9 +260,9 @@ import cardBuilder from '../../../components/cardbuilder/cardBuilder';
|
|||
|
||||
const apiClient = getApiClient();
|
||||
|
||||
apiClient.getQuickConnect('Status')
|
||||
.then(status => {
|
||||
if (status !== 'Unavailable') {
|
||||
apiClient.getQuickConnect('Enabled')
|
||||
.then(enabled => {
|
||||
if (enabled === true) {
|
||||
view.querySelector('.btnQuick').classList.remove('hide');
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div id="tvRecommendedPage" data-dom-cache="true" data-role="page" class="page libraryPage backdropPage pageWithAbsoluteTabs withTabs" data-backdroptype="series">
|
||||
|
||||
<div class="pageTabContent" id="seriesTab" data-index="0">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
|
||||
<button is="paper-icon-button-light" class="btnSort autoSize" title="${Sort}"><span class="material-icons sort_by_alpha"></span></button>
|
||||
|
@ -11,7 +11,7 @@
|
|||
<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right"></div>
|
||||
<div class="alphaPicker alphaPicker-fixed alphaPicker-vertical"></div>
|
||||
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -55,7 +55,7 @@
|
|||
<div is="emby-itemscontainer" id="items" class="itemsContainer padded-left padded-right padded-top vertical-wrap" style="text-align: center;"></div>
|
||||
</div>
|
||||
<div class="pageTabContent" id="episodesTab" data-index="5">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
|
||||
<button is="paper-icon-button-light" class="btnSort autoSize" title="${Sort}"><span class="material-icons sort_by_alpha"></span></button>
|
||||
|
@ -63,7 +63,7 @@
|
|||
</div>
|
||||
<div is="emby-itemscontainer" class="itemsContainer vertical-wrap padded-left padded-right">
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
|
||||
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x">
|
||||
<div class="paging"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -119,6 +119,7 @@ import autoFocuser from '../../components/autoFocuser';
|
|||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: container,
|
||||
preferThumb: true,
|
||||
inheritThumb: !userSettings.useEpisodeImagesInNextUpAndResume(),
|
||||
shape: getThumbShape(),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
|
@ -197,6 +198,7 @@ import autoFocuser from '../../components/autoFocuser';
|
|||
parentContainer: section,
|
||||
itemsContainer: container,
|
||||
preferThumb: true,
|
||||
inheritThumb: !userSettings.useEpisodeImagesInNextUpAndResume(),
|
||||
shape: 'backdrop',
|
||||
scalable: true,
|
||||
showTitle: true,
|
||||
|
|
24
src/controllers/user/controls/index.html
Normal file
24
src/controllers/user/controls/index.html
Normal file
|
@ -0,0 +1,24 @@
|
|||
<div id="controlsPreferencesPage" data-role="page" class="page libraryPage userPreferencesPage noSecondaryNavPage" data-title="${Controls}" data-menubutton="true">
|
||||
<div class="padded-left padded-right padded-bottom-page">
|
||||
<form style="margin: 0 auto;">
|
||||
<div class="verticalSection verticalSection-extrabottompadding">
|
||||
<h2 class="sectionTitle">
|
||||
${Controls}
|
||||
</h2>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkEnableGamepad" />
|
||||
<span>${LabelEnableGamepad}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${EnableGamepadHelp}</div>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelPleaseRestart}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button is="emby-button" type="submit" class="raised button-submit block btnSave hide">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
28
src/controllers/user/controls/index.js
Normal file
28
src/controllers/user/controls/index.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { Events } from 'jellyfin-apiclient';
|
||||
import toast from '../../../components/toast/toast';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import appSettings from '../../../scripts/settings/appSettings';
|
||||
|
||||
export default function (view) {
|
||||
function submit(e) {
|
||||
appSettings.enableGamepad(view.querySelector('.chkEnableGamepad').checked);
|
||||
|
||||
toast(globalize.translate('SettingsSaved'));
|
||||
|
||||
Events.trigger(view, 'saved');
|
||||
|
||||
e?.preventDefault();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
view.addEventListener('viewshow', function () {
|
||||
view.querySelector('.chkEnableGamepad').checked = appSettings.enableGamepad();
|
||||
view.querySelector('form').addEventListener('submit', submit);
|
||||
view.querySelector('.btnSave').classList.remove('hide');
|
||||
|
||||
import('../../../components/autoFocuser').then(({default: autoFocuser}) => {
|
||||
autoFocuser.autoFocus(view);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -12,6 +12,15 @@
|
|||
</div>
|
||||
</a>
|
||||
|
||||
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="lnkQuickConnectPreferences listItem-border hide">
|
||||
<div class="listItem">
|
||||
<em class="material-icons listItemIcon listItemIcon-transparent">tap_and_play</em>
|
||||
<div class="listItemBody">
|
||||
<div class="listItemBodyText">${QuickConnect}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="lnkDisplayPreferences listItem-border">
|
||||
<div class="listItem">
|
||||
<span class="material-icons listItemIcon listItemIcon-transparent tv"></span>
|
||||
|
@ -48,16 +57,6 @@
|
|||
</div>
|
||||
</a>
|
||||
|
||||
|
||||
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="lnkQuickConnectPreferences listItem-border hide">
|
||||
<div class="listItem">
|
||||
<em class="material-icons listItemIcon listItemIcon-transparent">tap_and_play</em>
|
||||
<div class="listItemBody">
|
||||
<div class="listItemBodyText">${QuickConnect}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="clientSettings listItem-border">
|
||||
<div class="listItem">
|
||||
<span class="material-icons listItemIcon listItemIcon-transparent devices_other"></span>
|
||||
|
@ -66,6 +65,15 @@
|
|||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="lnkControlsPreferences listItem-border">
|
||||
<div class="listItem">
|
||||
<span class="material-icons listItemIcon listItemIcon-transparent keyboard"></span>
|
||||
<div class="listItemBody">
|
||||
<div class="listItemBodyText">${Controls}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="adminSection verticalSection verticalSection-extrabottompadding hide">
|
||||
<h2 class="sectionTitle" style="padding-left:.25em;">${HeaderAdmin}</h2>
|
||||
|
|
|
@ -28,6 +28,7 @@ export default function (view, params) {
|
|||
page.querySelector('.lnkPlaybackPreferences').setAttribute('href', '#!/mypreferencesplayback.html?userId=' + userId);
|
||||
page.querySelector('.lnkSubtitlePreferences').setAttribute('href', '#!/mypreferencessubtitles.html?userId=' + userId);
|
||||
page.querySelector('.lnkQuickConnectPreferences').setAttribute('href', '#!/mypreferencesquickconnect.html');
|
||||
page.querySelector('.lnkControlsPreferences').setAttribute('href', '#!/mypreferencescontrols.html?userId=' + userId);
|
||||
|
||||
const supportsClientSettings = appHost.supports('clientsettings');
|
||||
page.querySelector('.clientSettings').classList.toggle('hide', !supportsClientSettings);
|
||||
|
@ -35,16 +36,17 @@ export default function (view, params) {
|
|||
const supportsMultiServer = appHost.supports('multiserver');
|
||||
page.querySelector('.selectServer').classList.toggle('hide', !supportsMultiServer);
|
||||
|
||||
ApiClient.getQuickConnect('Status')
|
||||
.then(status => {
|
||||
if (status !== 'Unavailable') {
|
||||
page.querySelector('.lnkControlsPreferences').classList.toggle('hide', layoutManager.mobile);
|
||||
|
||||
ApiClient.getQuickConnect('Enabled')
|
||||
.then(enabled => {
|
||||
if (enabled === true) {
|
||||
page.querySelector('.lnkQuickConnectPreferences').classList.remove('hide');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.debug('Failed to get QuickConnect status');
|
||||
});
|
||||
|
||||
ApiClient.getUser(userId).then(function (user) {
|
||||
page.querySelector('.headerUsername').innerHTML = user.Name;
|
||||
if (user.Policy.IsAdministrator && !layoutManager.tv) {
|
||||
|
@ -56,6 +58,7 @@ export default function (view, params) {
|
|||
if (params.userId && params.userId !== Dashboard.getCurrentUserId) {
|
||||
page.querySelector('.userSection').classList.add('hide');
|
||||
page.querySelector('.adminSection').classList.add('hide');
|
||||
page.querySelector('.lnkControlsPreferences').classList.add('hide');
|
||||
}
|
||||
|
||||
import('../../../components/autoFocuser').then(({default: autoFocuser}) => {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import globalize from '../../../scripts/globalize';
|
||||
import toast from '../../../components/toast/toast';
|
||||
import Dashboard from '../../../scripts/clientUtils';
|
||||
|
||||
export const authorize = (code) => {
|
||||
const url = ApiClient.getUrl('/QuickConnect/Authorize?Code=' + code);
|
||||
|
@ -16,22 +15,3 @@ export const authorize = (code) => {
|
|||
// prevent bubbling
|
||||
return false;
|
||||
};
|
||||
|
||||
export const activate = () => {
|
||||
const url = ApiClient.getUrl('/QuickConnect/Activate');
|
||||
return ApiClient.ajax({
|
||||
type: 'POST',
|
||||
url: url
|
||||
}).then(() => {
|
||||
toast(globalize.translate('QuickConnectActivationSuccessful'));
|
||||
return true;
|
||||
}).catch((e) => {
|
||||
console.error('Error activating quick connect. Error:', e);
|
||||
Dashboard.alert({
|
||||
title: globalize.translate('HeaderError'),
|
||||
message: globalize.translate('DefaultErrorMessage')
|
||||
});
|
||||
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
<div id="quickConnectPreferencesPage" data-role="page" class="page libraryPage userPreferencesPage noSecondaryNavPage" data-title="${QuickConnect}" data-backbutton="true" style="margin: 0 auto; max-width: 54em">
|
||||
<div class="settingsContainer padded-left padded-right padded-bottom-page">
|
||||
<button is="emby-button" id="btnQuickConnectActivate" type="button" class="raised button-submit block">
|
||||
<span>${ButtonActivate}</span>
|
||||
</button>
|
||||
|
||||
<form class="quickConnectSettingsContainer">
|
||||
<div style="margin-bottom: 1em">
|
||||
${QuickConnectDescription}
|
||||
</div>
|
||||
<form class="quickConnectSettingsContainer">
|
||||
<div class="verticalSection">
|
||||
<h2 class="sectionTitle">${QuickConnect}</h2>
|
||||
<div>${QuickConnectDescription}</div>
|
||||
<br />
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" min="0" max="999999" required id="txtQuickConnectCode" label="${LabelQuickConnectCode}" autocomplete="off" />
|
||||
</div>
|
||||
<button id="btnQuickConnectAuthorize" is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Authorize}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { activate, authorize } from './helper';
|
||||
import { authorize } from './helper';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import toast from '../../../components/toast/toast';
|
||||
|
||||
|
@ -6,52 +6,16 @@ export default function (view) {
|
|||
view.addEventListener('viewshow', function () {
|
||||
const codeElement = view.querySelector('#txtQuickConnectCode');
|
||||
|
||||
view.querySelector('#btnQuickConnectActivate').addEventListener('click', () => {
|
||||
activate().then(() => {
|
||||
renderPage();
|
||||
});
|
||||
});
|
||||
view.querySelector('.quickConnectSettingsContainer').addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
view.querySelector('#btnQuickConnectAuthorize').addEventListener('click', () => {
|
||||
if (!codeElement.validity.valid) {
|
||||
toast(globalize.translate('QuickConnectInvalidCode'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const code = codeElement.value;
|
||||
authorize(code);
|
||||
authorize(codeElement.value);
|
||||
});
|
||||
|
||||
view.querySelector('.quickConnectSettingsContainer').addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
renderPage();
|
||||
});
|
||||
|
||||
function renderPage(forceActive = false) {
|
||||
ApiClient.getQuickConnect('Status').then((status) => {
|
||||
const btn = view.querySelector('#btnQuickConnectActivate');
|
||||
const container = view.querySelector('.quickConnectSettingsContainer');
|
||||
|
||||
// The activation button should only be visible when quick connect is unavailable (with the text replaced with an error) or when it is available (so it can be activated)
|
||||
// The authorization container is only usable when quick connect is active, so it should be hidden otherwise
|
||||
container.style.display = 'none';
|
||||
|
||||
if (status === 'Unavailable') {
|
||||
btn.textContent = globalize.translate('QuickConnectNotAvailable');
|
||||
btn.disabled = true;
|
||||
btn.classList.remove('button-submit');
|
||||
btn.classList.add('button');
|
||||
} else if (status === 'Active' || forceActive) {
|
||||
container.style.display = '';
|
||||
btn.style.display = 'none';
|
||||
}
|
||||
|
||||
return true;
|
||||
}).catch((e) => {
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue