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

merge branch master into standalone

This commit is contained in:
dkanada 2020-10-29 18:09:23 +09:00
commit fa1483d3c0
195 changed files with 7490 additions and 3044 deletions

View file

@ -16,6 +16,9 @@
</div>
<div style="margin-top:1em;">
<button is="emby-button" type="button" class="raised btnRefresh">
<span>${ButtonScanAllLibraries}</span>
</button>
<button is="emby-button" type="button" id="btnRestartServer" class="raised" onclick="DashboardPage.restart(this);" style="margin-left:0;">
<span>${Restart}</span>
</button>

View file

@ -3,6 +3,7 @@ import events from 'events';
import itemHelper from 'itemHelper';
import serverNotifications from 'serverNotifications';
import dom from 'dom';
import taskButton from 'scripts/taskbutton';
import globalize from 'globalize';
import * as datefns from 'date-fns';
import dfnshelper from 'dfnshelper';
@ -550,13 +551,13 @@ import 'emby-itemscontainer';
row.classList.remove('playingSession');
}
if (session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1 && session.DeviceId !== window.connectionManager.deviceId()) {
if (session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1) {
row.querySelector('.btnSessionSendMessage').classList.remove('hide');
} else {
row.querySelector('.btnSessionSendMessage').classList.add('hide');
}
if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons.length) {
if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo) {
row.querySelector('.btnSessionInfo').classList.remove('hide');
} else {
row.querySelector('.btnSessionInfo').classList.add('hide');
@ -564,7 +565,7 @@ import 'emby-itemscontainer';
const btnSessionPlayPause = row.querySelector('.btnSessionPlayPause');
if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl && session.DeviceId !== window.connectionManager.deviceId()) {
if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl) {
btnSessionPlayPause.classList.remove('hide');
row.querySelector('.btnSessionStop').classList.remove('hide');
} else {
@ -827,9 +828,17 @@ import 'emby-itemscontainer';
refreshActiveRecordings(view, apiClient);
loading.hide();
}
taskButton({
mode: 'on',
taskKey: 'RefreshLibrary',
button: page.querySelector('.btnRefresh')
});
});
view.addEventListener('viewbeforehide', function () {
const apiClient = ApiClient;
const page = this;
events.off(serverNotifications, 'RestartRequired', onRestartRequired);
events.off(serverNotifications, 'ServerShuttingDown', onServerShuttingDown);
events.off(serverNotifications, 'ServerRestarting', onServerRestarting);
@ -841,6 +850,12 @@ import 'emby-itemscontainer';
if (apiClient) {
DashboardPage.stopInterval(apiClient);
}
taskButton({
mode: 'off',
taskKey: 'RefreshLibrary',
button: page.querySelector('.btnRefresh')
});
});
view.addEventListener('viewdestroy', function () {
const page = this;

View file

@ -31,6 +31,11 @@
<div class="fieldDescription">${LabelVaapiDeviceHelp}</div>
</div>
<div class="inputContainer hide fldOpenclDevice">
<input is="emby-input" type="text" id="txtOpenclDevice" label="${LabelOpenclDevice}" />
<div class="fieldDescription">${LabelOpenclDeviceHelp}</div>
</div>
<div class="hardwareAccelerationOptions hide">
<div class="checkboxListContainer decodingCodecsList">
<h3 class="checkboxListLabel">${LabelEnableHardwareDecodingFor}</h3>
@ -89,6 +94,54 @@
</div>
</div>
<div class="tonemappingOptions hide">
<div class="checkboxListContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkTonemapping" />
<span>${EnableTonemapping}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${AllowTonemappingHelp}</div>
</div>
<div class="selectContainer">
<select is="emby-select" id="selectTonemappingAlgorithm" label="${LabelTonemappingAlgorithm}">
<option value="none">None</option>
<option value="clip">Clip</option>
<option value="linear">Linear</option>
<option value="gamma">Gamma</option>
<option value="reinhard">Reinhard</option>
<option value="hable">Hable</option>
<option value="mobius">Mobius</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>
</div>
</div>
<div class="selectContainer">
<select is="emby-select" id="selectTonemappingRange" label="${LabelTonemappingRange}">
<option value="auto">${Auto}</option>
<option value="tv">TV</option>
<option value="pc">PC</option>
</select>
<div class="fieldDescription">${TonemappingRangeHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="number" id="txtTonemappingDesat" pattern="[0-9]*" min="0" max="1.79769e+308" step=".00001" label="${LabelTonemappingDesat}" />
<div class="fieldDescription">${LabelTonemappingDesatHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="number" id="txtTonemappingThreshold" pattern="[0-9]*" min="0" max="1.79769e+308" step=".00001" label="${LabelTonemappingThreshold}" />
<div class="fieldDescription">${LabelTonemappingThresholdHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="number" id="txtTonemappingPeak" pattern="[0-9]*" min="0" max="1.79769e+308" step=".00001" label="${LabelTonemappingPeak}" />
<div class="fieldDescription">${LabelTonemappingPeakHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="number" id="txtTonemappingParam" pattern="[0-9]*" min="2.22507e-308" max="1.79769e+308" step=".00001" label="${LabelTonemappingParam}" />
<div class="fieldDescription">${LabelTonemappingParamHelp}</div>
</div>
</div>
<div class="selectContainer">
<select is="emby-select" id="selectThreadCount" label="${LabelTranscodingThreadCount}">
<option value="-1">${Auto}</option>
@ -129,6 +182,10 @@
<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="inputContainer">
<input is="emby-input" type="number" id="txtMaxMuxingQueueSize" pattern="[0-9]*" required="required" min="128" max="2147483647" step="1" label="${LabelMaxMuxingQueueSize}" />
<div class="fieldDescription">${LabelMaxMuxingQueueSizeHelp}</div>
</div>
<div class="selectContainer">
<select is="emby-select" id="selectEncoderPreset" label="${LabelEncoderPreset}">

View file

@ -16,9 +16,18 @@ import libraryMenu from 'libraryMenu';
$('#selectVideoDecoder', page).val(config.HardwareAccelerationType);
$('#selectThreadCount', page).val(config.EncodingThreadCount);
$('#txtDownMixAudioBoost', page).val(config.DownMixAudioBoost);
page.querySelector('#txtMaxMuxingQueueSize').value = config.MaxMuxingQueueSize || '';
page.querySelector('.txtEncoderPath').value = config.EncoderAppPathDisplay || '';
$('#txtTranscodingTempPath', page).val(systemInfo.TranscodingTempPath || '');
$('#txtVaapiDevice', page).val(config.VaapiDevice || '');
page.querySelector('#chkTonemapping').checked = config.EnableTonemapping;
page.querySelector('#txtOpenclDevice').value = config.OpenclDevice || '';
page.querySelector('#selectTonemappingAlgorithm').value = config.TonemappingAlgorithm;
page.querySelector('#selectTonemappingRange').value = config.TonemappingRange;
page.querySelector('#txtTonemappingDesat').value = config.TonemappingDesat;
page.querySelector('#txtTonemappingThreshold').value = config.TonemappingThreshold;
page.querySelector('#txtTonemappingPeak').value = config.TonemappingPeak;
page.querySelector('#txtTonemappingParam').value = config.TonemappingParam || '';
page.querySelector('#selectEncoderPreset').value = config.EncoderPreset || '';
page.querySelector('#txtH264Crf').value = config.H264Crf || '';
page.querySelector('#selectDeinterlaceMethod').value = config.DeinterlaceMethod || '';
@ -62,10 +71,19 @@ import libraryMenu from 'libraryMenu';
loading.show();
ApiClient.getNamedConfiguration('encoding').then(function (config) {
config.DownMixAudioBoost = $('#txtDownMixAudioBoost', form).val();
config.MaxMuxingQueueSize = form.querySelector('#txtMaxMuxingQueueSize').value;
config.TranscodingTempPath = $('#txtTranscodingTempPath', form).val();
config.EncodingThreadCount = $('#selectThreadCount', form).val();
config.HardwareAccelerationType = $('#selectVideoDecoder', form).val();
config.VaapiDevice = $('#txtVaapiDevice', form).val();
config.OpenclDevice = form.querySelector('#txtOpenclDevice').value;
config.EnableTonemapping = form.querySelector('#chkTonemapping').checked;
config.TonemappingAlgorithm = form.querySelector('#selectTonemappingAlgorithm').value;
config.TonemappingRange = form.querySelector('#selectTonemappingRange').value;
config.TonemappingDesat = form.querySelector('#txtTonemappingDesat').value;
config.TonemappingThreshold = form.querySelector('#txtTonemappingThreshold').value;
config.TonemappingPeak = form.querySelector('#txtTonemappingPeak').value;
config.TonemappingParam = form.querySelector('#txtTonemappingParam').value || '0';
config.EncoderPreset = form.querySelector('#selectEncoderPreset').value;
config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0');
config.DeinterlaceMethod = form.querySelector('#selectDeinterlaceMethod').value;
@ -149,6 +167,16 @@ import libraryMenu from 'libraryMenu';
page.querySelector('#txtVaapiDevice').removeAttribute('required');
}
if (this.value == 'nvenc' || this.value == 'amf') {
page.querySelector('.fldOpenclDevice').classList.remove('hide');
page.querySelector('#txtOpenclDevice').setAttribute('required', 'required');
page.querySelector('.tonemappingOptions').classList.remove('hide');
} else {
page.querySelector('.fldOpenclDevice').classList.add('hide');
page.querySelector('#txtOpenclDevice').removeAttribute('required');
page.querySelector('.tonemappingOptions').classList.add('hide');
}
if (this.value) {
page.querySelector('.hardwareAccelerationOptions').classList.remove('hide');
} else {

View file

@ -9,21 +9,21 @@ import 'flexStyles';
export default function(view, params) {
view.addEventListener('viewbeforeshow', function() {
loading.show();
var apiClient = ApiClient;
const apiClient = ApiClient;
apiClient.getJSON(apiClient.getUrl('System/Logs')).then(function(logs) {
var html = '';
let html = '';
html += '<div class="paperList">';
html += logs.map(function(log) {
var logUrl = apiClient.getUrl('System/Logs/Log', {
let logUrl = apiClient.getUrl('System/Logs/Log', {
name: log.Name
});
logUrl += '&api_key=' + apiClient.accessToken();
var logHtml = '';
let logHtml = '';
logHtml += '<a is="emby-linkbutton" href="' + logUrl + '" target="_blank" class="listItem listItem-border" style="color:inherit;">';
logHtml += '<div class="listItemBody two-line">';
logHtml += "<h3 class='listItemBodyText'>" + log.Name + '</h3>';
var date = datetime.parseISO8601Date(log.DateModified, true);
var text = datetime.toLocaleDateString(date);
const date = datetime.parseISO8601Date(log.DateModified, true);
let text = datetime.toLocaleDateString(date);
text += ' ' + datetime.getDisplayTime(date);
logHtml += '<div class="listItemBodyText secondary">' + text + '</div>';
logHtml += '</div>';

View file

@ -43,6 +43,11 @@
<input is="emby-input" type="text" id="txtLanNetworks" label="${LabelLanNetworks}" />
<div class="fieldDescription">${LanNetworksHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtKnownProxies" label="${LabelKnownProxies}" />
<div class="fieldDescription">${KnownProxiesHelp}</div>
</div>
</fieldset>
<fieldset class='verticalSection verticalSection-extrabottompadding'>

View file

@ -30,6 +30,11 @@ import 'emby-select';
}).filter(function (s) {
return s.length > 0;
});
config.KnownProxies = form.querySelector('#txtKnownProxies').value.split(',').map(function (s) {
return s.trim();
}).filter(function (s) {
return s.length > 0;
});
config.IsRemoteIPFilterBlacklist = form.querySelector('#selectExternalAddressFilterMode').value === 'blacklist';
config.PublicPort = form.querySelector('#txtPublicPort').value;
config.PublicHttpsPort = form.querySelector('#txtPublicHttpsPort').value;
@ -108,6 +113,7 @@ import 'emby-select';
page.querySelector('#txtPublicHttpsPort').value = config.PublicHttpsPort;
page.querySelector('#txtLocalAddress').value = config.LocalNetworkAddresses[0] || '';
page.querySelector('#txtLanNetworks').value = (config.LocalNetworkSubnets || []).join(', ');
page.querySelector('#txtKnownProxies').value = (config.KnownProxies || []).join(', ');
page.querySelector('#txtExternalAddressFilter').value = (config.RemoteIPFilter || []).join(', ');
page.querySelector('#selectExternalAddressFilterMode').value = config.IsRemoteIPFilterBlacklist ? 'blacklist' : 'whitelist';
page.querySelector('#chkRemoteAccess').checked = config.EnableRemoteAccess == null || config.EnableRemoteAccess;

View file

@ -42,10 +42,10 @@ function saveList(page) {
}
function populateList(options) {
var html = '';
let html = '';
html += '<div class="paperList">';
for (var i = 0; i < options.repositories.length; i++) {
for (let i = 0; i < options.repositories.length; i++) {
html += getRepositoryHtml(options.repositories[i]);
}
@ -59,7 +59,7 @@ function populateList(options) {
}
function getRepositoryHtml(repository) {
var html = '';
let html = '';
html += '<div class="listItem listItem-border">';
html += `<a is="emby-linkbutton" style="margin:0;padding:0" class="clearLink listItemIconContainer" href="${repository.Url}">`;
@ -93,9 +93,9 @@ export default function(view, params) {
libraryMenu.setTabs('plugins', 2, getTabs);
reloadList(this);
var save = this;
const save = this;
$('#repositories', view).on('click', '.btnDelete', function() {
var button = this;
const button = this;
repositories = repositories.filter(function (r) {
return r.Url !== button.id;
});

View file

@ -0,0 +1,24 @@
<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>

View file

@ -0,0 +1,58 @@
import loading from 'loading';
import toast from 'toast';
import globalize from '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);
});
}

View file

@ -140,32 +140,13 @@
</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>
<h2 class="checkboxListLabel">${Other}</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableConversion" />
<span>${AllowMediaConversion}</span>
<input type="checkbox" is="emby-checkbox" id="chkEnableDownloading" />
<span>${OptionAllowContentDownload}</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 class="fieldDescription checkboxFieldDescription">${OptionAllowContentDownloadHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription" id="fldIsEnabled">
<label>
@ -190,6 +171,14 @@
</div>
</div>
<br />
<div class=verticalSection>
<div class="inputContainer" id="fldMaxActiveSessions">
<input is="emby-input" type="number" id="txtMaxActiveSessions" min="0" step="1" label="${LabelUserMaxActiveSessions}"/>
<div class="fieldDescription">${OptionMaxActiveSessions}</div>
<div class="fieldDescription">${OptionMaxActiveSessionsHelp}</div>
</div>
</div>
<br />
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>

View file

@ -97,11 +97,9 @@ import globalize from 'globalize';
$('#chkEnableVideoPlaybackRemuxing', page).prop('checked', user.Policy.EnablePlaybackRemuxing);
$('#chkForceRemoteSourceTranscoding', page).prop('checked', user.Policy.ForceRemoteSourceTranscoding);
$('#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);
$('#txtRemoteClientBitrateLimit', page).val(user.Policy.RemoteClientBitrateLimit / 1e6 || '');
$('#txtLoginAttemptsBeforeLockout', page).val(user.Policy.LoginAttemptsBeforeLockout || '0');
$('#txtMaxActiveSessions', page).val(user.Policy.MaxActiveSessions || '0');
if (ApiClient.isMinServerVersion('10.6.0')) {
$('#selectSyncPlayAccess').val(user.Policy.SyncPlayAccess);
}
@ -132,12 +130,10 @@ import globalize from 'globalize';
user.Policy.EnablePlaybackRemuxing = $('#chkEnableVideoPlaybackRemuxing', page).is(':checked');
user.Policy.ForceRemoteSourceTranscoding = $('#chkForceRemoteSourceTranscoding', page).is(':checked');
user.Policy.EnableContentDownloading = $('#chkEnableDownloading', page).is(':checked');
user.Policy.EnableSyncTranscoding = $('#chkEnableSyncTranscoding', page).is(':checked');
user.Policy.EnableMediaConversion = $('#chkEnableConversion', page).is(':checked');
user.Policy.EnablePublicSharing = $('#chkEnableSharing', page).is(':checked');
user.Policy.EnableRemoteAccess = $('#chkRemoteAccess', page).is(':checked');
user.Policy.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($('#txtRemoteClientBitrateLimit', page).val() || '0'));
user.Policy.LoginAttemptsBeforeLockout = parseInt($('#txtLoginAttemptsBeforeLockout', page).val() || '0');
user.Policy.MaxActiveSessions = parseInt($('#txtMaxActiveSessions', page).val() || '0');
user.Policy.AuthenticationProviderId = page.querySelector('.selectLoginProvider').value;
user.Policy.PasswordResetProviderId = page.querySelector('.selectPasswordResetProvider').value;
user.Policy.EnableContentDeletion = $('#chkEnableDeleteAllFolders', page).is(':checked');

View file

@ -51,7 +51,9 @@ import globalize from 'globalize';
$('.channelAccessContainer', page).hide();
}
$('#chkEnableAllChannels', page).prop('checked', user.Policy.EnableAllChannels);
const chkEnableAllChannels = page.querySelector('#chkEnableAllChannels');
chkEnableAllChannels.checked = user.Policy.EnableAllChannels;
triggerChange(chkEnableAllChannels);
}
function loadDevices(page, user, devices) {
@ -67,7 +69,9 @@ import globalize from 'globalize';
html += '</div>';
$('.deviceAccess', page).show().html(html);
$('#chkEnableAllDevices', page).prop('checked', user.Policy.EnableAllDevices);
const chkEnableAllDevices = page.querySelector('#chkEnableAllDevices');
chkEnableAllDevices.checked = user.Policy.EnableAllDevices;
triggerChange(chkEnableAllDevices);
if (user.Policy.IsAdministrator) {
page.querySelector('.deviceAccessContainer').classList.add('hide');

View file

@ -20,7 +20,7 @@ export default function (view, params) {
});
MetadataEditor.setCurrentItemId(null);
view.querySelector('.libraryTree').addEventListener('itemclicked', function (event) {
var data = event.detail;
const data = event.detail;
if (data.id != MetadataEditor.getCurrentItemId()) {
MetadataEditor.setCurrentItemId(data.id);

View file

@ -163,7 +163,7 @@ import 'emby-scroller';
instance.setFilterStatus(hasFilters);
if (instance.alphaPicker) {
query.NameStartsWithOrGreater = instance.alphaPicker.value();
query.NameStartsWith = instance.alphaPicker.value();
}
return query;

View file

@ -1,6 +1,6 @@
<div id="moviesPage" data-role="page" data-dom-cache="true" class="page libraryPage backdropPage collectionEditorPage pageWithAbsoluteTabs withTabs" data-backdroptype="movie">
<div class="pageTabContent" data-index="0">
<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="paging"></div>
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
@ -17,7 +17,7 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="1">
<div class="pageTabContent" id="suggestionsTab" data-index="1">
<div id="resumableSection" class="verticalSection hide">
<div class="sectionTitleContainer sectionTitleContainer-cards">
<h2 class="sectionTitle sectionTitle-cards padded-left">${HeaderContinueWatching}</h2>
@ -43,7 +43,7 @@
<p>${MessageNoMovieSuggestionsAvailable}</p>
</div>
</div>
<div class="pageTabContent" data-index="2">
<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="paging"></div>
<button is="paper-icon-button-light" class="btnSort autoSize" title="${Sort}"><span class="material-icons sort_by_alpha"></span></button>
@ -59,7 +59,7 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="3">
<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="paging"></div>
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
@ -71,7 +71,7 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="4">
<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="paging"></div>
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
@ -85,9 +85,7 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="5">
<div class="pageTabContent" id="genresTab" data-index="5">
<div id="items"></div>
</div>
<div class="pageTabContent" data-index="6">
</div>
</div>

View file

@ -25,7 +25,12 @@ import 'emby-itemscontainer';
const updateFilterControls = () => {
if (this.alphaPicker) {
this.alphaPicker.value(query.NameStartsWithOrGreater);
this.alphaPicker.value(query.NameStartsWith);
if (query.SortBy.indexOf('SortName') === 0) {
this.alphaPicker.visible(true);
} else {
this.alphaPicker.visible(false);
}
}
};
@ -163,12 +168,12 @@ import 'emby-itemscontainer';
itemsContainer.fetchData = fetchData;
itemsContainer.getItemsHtml = getItemsHtml;
itemsContainer.afterRefresh = afterRefresh;
let alphaPickerElement = tabContent.querySelector('.alphaPicker');
const alphaPickerElement = tabContent.querySelector('.alphaPicker');
if (alphaPickerElement) {
alphaPickerElement.addEventListener('alphavaluechanged', function (e) {
let newValue = e.detail.value;
query.NameStartsWithOrGreater = newValue;
const newValue = e.detail.value;
query.NameStartsWith = newValue;
query.StartIndex = 0;
itemsContainer.refreshItems();
});
@ -237,7 +242,7 @@ import 'emby-itemscontainer';
libraryBrowser.showLayoutMenu(e.target, this.getCurrentViewStyle, 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
});
btnSelectView.addEventListener('layoutchange', function (e) {
let viewStyle = e.detail.viewStyle;
const viewStyle = e.detail.viewStyle;
userSettings.set(savedViewKey, viewStyle);
query.StartIndex = 0;
onViewStyleChange();
@ -274,7 +279,7 @@ import 'emby-itemscontainer';
this.showFilterMenu = function () {
import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => {
let filterDialog = new filterDialogFactory({
const filterDialog = new filterDialogFactory({
query: query,
mode: 'movies',
serverId: ApiClient.serverId()

View file

@ -58,7 +58,7 @@ import 'emby-button';
}
function loadResume(page, userId, parentId) {
let screenWidth = dom.getWindowSize().innerWidth;
const screenWidth = dom.getWindowSize().innerWidth;
const options = {
SortBy: 'DatePlayed',
SortOrder: 'Descending',
@ -154,8 +154,8 @@ import 'emby-button';
}
function loadSuggestions(page, userId, parentId) {
let screenWidth = dom.getWindowSize().innerWidth;
let url = ApiClient.getUrl('Movies/Recommendations', {
const screenWidth = dom.getWindowSize().innerWidth;
const url = ApiClient.getUrl('Movies/Recommendations', {
userId: userId,
categoryLimit: 6,
ItemLimit: screenWidth >= 1920 ? 8 : screenWidth >= 1600 ? 8 : screenWidth >= 1200 ? 6 : 5,
@ -172,7 +172,7 @@ import 'emby-button';
const html = recommendations.map(getRecommendationHtml).join('');
page.querySelector('.noItemsMessage').classList.add('hide');
let recs = page.querySelector('.recommendations');
const recs = page.querySelector('.recommendations');
recs.innerHTML = html;
imageLoader.lazyChildren(recs);
@ -320,11 +320,6 @@ import 'emby-button';
if (index === suggestionsTabIndex) {
controller = this;
} else if (index === 6) {
controller = new controllerFactory(view, tabContent, {
collectionType: 'movies',
parentId: params.topParentId
});
} else if (index == 0 || index == 3) {
controller = new controllerFactory(view, params, tabContent, {
mode: index ? 'favorites' : 'movies'
@ -381,21 +376,21 @@ import 'emby-button';
const suggestionsTabIndex = 1;
this.initTab = function () {
let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
const tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
initSuggestedTab(view, tabContent);
};
this.renderTab = function () {
let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
const tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
loadSuggestionsTab(view, params, tabContent);
};
let tabControllers = [];
const tabControllers = [];
let renderedTabs = [];
view.addEventListener('viewshow', function (e) {
initTabs();
if (!view.getAttribute('data-title')) {
var parentId = params.topParentId;
const parentId = params.topParentId;
if (parentId) {
ApiClient.getItem(ApiClient.getCurrentUserId(), parentId).then(function (item) {

View file

@ -185,7 +185,7 @@ import 'emby-itemscontainer';
const updateFilterControls = (tabContent) => {
const query = getQuery(tabContent);
this.alphaPicker.value(query.NameStartsWithOrGreater);
this.alphaPicker.value(query.NameStartsWith);
};
const data = {};
@ -216,7 +216,7 @@ import 'emby-itemscontainer';
alphaPickerElement.addEventListener('alphavaluechanged', function (e) {
const newValue = e.detail.value;
const query = getQuery(tabContent);
query.NameStartsWithOrGreater = newValue;
query.NameStartsWith = newValue;
query.StartIndex = 0;
reloadItems();
});

View file

@ -8,7 +8,26 @@
}
}
</style>
<div class="pageTabContent pageTabContent" id="suggestionsTab" data-index="0">
<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="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>
<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>
<button is="paper-icon-button-light" class="btnFilter autoSize" title="${Filter}"><span class="material-icons filter_list"></span></button>
</div>
<div class="alphaPicker alphaPicker-fixed alphaPicker-vertical">
</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="paging"></div>
</div>
</div>
<div class="pageTabContent pageTabContent" id="suggestionsTab" data-index="1">
<div class="verticalSection">
@ -34,25 +53,6 @@
<div class="favoriteSections verticalSection"></div>
</div>
<div class="pageTabContent pageTabContent" id="albumsTab" data-index="1">
<div class="flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom">
<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>
<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>
<button is="paper-icon-button-light" class="btnFilter autoSize" title="${Filter}"><span class="material-icons filter_list"></span></button>
</div>
<div class="alphaPicker alphaPicker-fixed alphaPicker-vertical">
</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="paging"></div>
</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="paging"></div>
@ -85,7 +85,7 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="4">
<div class="pageTabContent" id="playlistsTab" data-index="4">
<div is="emby-itemscontainer" id="items" class="itemsContainer padded-left padded-right padded-top vertical-wrap centered"></div>
</div>
@ -105,6 +105,4 @@
<div class="pageTabContent" id="genresTab" data-index="6">
<div is="emby-itemscontainer" id="items" class="itemsContainer padded-left padded-right padded-top vertical-wrap"></div>
</div>
<div class="pageTabContent" data-index="7">
</div>
</div>

View file

@ -155,20 +155,21 @@ import 'emby-itemscontainer';
overlayPlayButton: true
});
}
let elems = tabContent.querySelectorAll('.paging');
for (let i = 0, length = elems.length; i < length; i++) {
elems[i].innerHTML = pagingHtml;
for (const elem of elems) {
elem.innerHTML = pagingHtml;
}
elems = tabContent.querySelectorAll('.btnNextPage');
for (let i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onNextPageClick);
for (const elem of elems) {
elem.addEventListener('click', onNextPageClick);
}
elems = tabContent.querySelectorAll('.btnPreviousPage');
for (let i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onPreviousPageClick);
for (const elem of elems) {
elem.addEventListener('click', onPreviousPageClick);
}
const itemsContainer = tabContent.querySelector('.itemsContainer');
@ -186,7 +187,16 @@ import 'emby-itemscontainer';
const updateFilterControls = (tabContent) => {
const query = getQuery();
this.alphaPicker.value(query.NameStartsWithOrGreater);
if (this.alphaPicker) {
this.alphaPicker.value(query.NameStartsWith);
if (query.SortBy.indexOf('SortName') === 0) {
this.alphaPicker.visible(true);
} else {
this.alphaPicker.visible(false);
}
}
};
let savedQueryKey;
@ -200,10 +210,12 @@ import 'emby-itemscontainer';
mode: 'albums',
serverId: ApiClient.serverId()
});
events.on(filterDialog, 'filterchange', function () {
getQuery().StartIndex = 0;
reloadItems(tabContent);
});
filterDialog.show();
});
};
@ -219,10 +231,11 @@ import 'emby-itemscontainer';
alphaPickerElement.addEventListener('alphavaluechanged', function (e) {
const newValue = e.detail.value;
const query = getQuery();
query.NameStartsWithOrGreater = newValue;
query.NameStartsWith = newValue;
query.StartIndex = 0;
reloadItems(tabContent);
});
this.alphaPicker = new AlphaPicker({
element: alphaPickerElement,
valueChangeEvent: 'click'
@ -235,6 +248,7 @@ import 'emby-itemscontainer';
tabContent.querySelector('.btnFilter').addEventListener('click', () => {
this.showFilterMenu();
});
tabContent.querySelector('.btnSort').addEventListener('click', (e) => {
libraryBrowser.showSortMenu({
items: [{
@ -267,10 +281,12 @@ import 'emby-itemscontainer';
button: e.target
});
});
const btnSelectView = tabContent.querySelector('.btnSelectView');
btnSelectView.addEventListener('click', (e) => {
libraryBrowser.showLayoutMenu(e.target, this.getCurrentViewStyle(), 'List,Poster,PosterCard'.split(','));
});
btnSelectView.addEventListener('layoutchange', function (e) {
const viewStyle = e.detail.viewStyle;
getPageData().view = viewStyle;
@ -279,6 +295,7 @@ import 'emby-itemscontainer';
onViewStyleChange();
reloadItems(tabContent);
});
tabContent.querySelector('.btnPlayAll').addEventListener('click', playAll);
tabContent.querySelector('.btnShuffle').addEventListener('click', shuffle);
};

View file

@ -169,7 +169,7 @@ import 'emby-itemscontainer';
const updateFilterControls = (tabContent) => {
const query = getQuery(tabContent);
this.alphaPicker.value(query.NameStartsWithOrGreater);
this.alphaPicker.value(query.NameStartsWith);
};
const data = {};
@ -201,7 +201,7 @@ import 'emby-itemscontainer';
alphaPickerElement.addEventListener('alphavaluechanged', function (e) {
const newValue = e.detail.value;
const query = getQuery(tabContent);
query.NameStartsWithOrGreater = newValue;
query.NameStartsWith = newValue;
query.StartIndex = 0;
reloadItems(tabContent);
});

View file

@ -56,7 +56,7 @@ import 'flexStyles';
EnableTotalRecordCount: false
};
ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(function (items) {
var elem = page.querySelector('#recentlyAddedSongs');
const elem = page.querySelector('#recentlyAddedSongs');
elem.innerHTML = cardBuilder.getCardsHtml({
items: items,
showUnplayedIndicator: false,
@ -103,7 +103,7 @@ import 'flexStyles';
elem.classList.add('hide');
}
var itemsContainer = elem.querySelector('.itemsContainer');
const itemsContainer = elem.querySelector('.itemsContainer');
itemsContainer.innerHTML = cardBuilder.getCardsHtml({
items: result.Items,
showUnplayedIndicator: false,
@ -145,7 +145,7 @@ import 'flexStyles';
elem.classList.add('hide');
}
var itemsContainer = elem.querySelector('.itemsContainer');
const itemsContainer = elem.querySelector('.itemsContainer');
itemsContainer.innerHTML = cardBuilder.getCardsHtml({
items: result.Items,
showUnplayedIndicator: false,
@ -177,9 +177,9 @@ import 'flexStyles';
function getTabs() {
return [{
name: globalize.translate('Suggestions')
}, {
name: globalize.translate('Albums')
}, {
name: globalize.translate('Suggestions')
}, {
name: globalize.translate('HeaderAlbumArtists')
}, {
@ -195,7 +195,7 @@ import 'flexStyles';
function getDefaultTabIndex(folderId) {
switch (userSettings.get('landing-' + folderId)) {
case 'albums':
case 'suggestions':
return 1;
case 'albumartists':
@ -221,7 +221,7 @@ import 'flexStyles';
export default function (view, params) {
function reload() {
loading.show();
const tabContent = view.querySelector(".pageTabContent[data-index='0']");
const tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
loadSuggestionsTab(view, tabContent, params.topParentId);
}
@ -268,11 +268,11 @@ import 'flexStyles';
switch (index) {
case 0:
depends = 'controllers/music/musicrecommended';
depends = 'controllers/music/musicalbums';
break;
case 1:
depends = 'controllers/music/musicalbums';
depends = 'controllers/music/musicrecommended';
break;
case 2:
@ -296,7 +296,7 @@ import 'flexStyles';
import(depends).then(({default: controllerFactory}) => {
let tabContent;
if (index == 0) {
if (index == 1) {
tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']");
this.tabContent = tabContent;
}
@ -306,13 +306,8 @@ import 'flexStyles';
if (!controller) {
tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']");
if (index === 0) {
if (index === 1) {
controller = this;
} else if (index === 7) {
controller = new controllerFactory(view, tabContent, {
collectionType: 'music',
parentId: params.topParentId
});
} else {
controller = new controllerFactory(view, params, tabContent);
}
@ -360,9 +355,10 @@ import 'flexStyles';
}
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
const suggestionsTabIndex = 1;
this.initTab = function () {
const tabContent = view.querySelector(".pageTabContent[data-index='0']");
const tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
const containers = tabContent.querySelectorAll('.itemsContainer');
for (let i = 0, length = containers.length; i < length; i++) {

View file

@ -1,11 +1,9 @@
<div id="videoOsdPage" data-role="page" class="page libraryPage" data-backbutton="true">
<div class="upNextContainer hide"></div>
<div class="videoOsdBottom videoOsdBottom-maincontrols">
<div class="osdPoster"></div>
<div class="osdControls">
<div class="osdTextContainer osdMainTextContainer">
<h3 class="osdTitle"></h3>
<div class="osdMediaInfo"></div>
<div class="osdMediaStatus hide">
<span class="material-icons animate autorenew"></span>
<span>${FetchingData}</span>
@ -16,7 +14,7 @@
<div class="flex flex-direction-row align-items-center">
<div class="osdTextContainer startTimeText" style="margin: 0 .25em 0 0;"></div>
<div class="sliderContainer flex-grow" style="margin: .5em .5em .25em;">
<div class="sliderContainer flex-grow" style="margin: .5em 0 .25em;">
<input type="range" step=".01" min="0" max="100" value="0" is="emby-slider" class="osdPositionSlider" data-slider-keep-progress="true" />
</div>
<div class="osdTextContainer endTimeText" style="margin: 0 0 0 .25em;"></div>
@ -47,32 +45,18 @@
<span class="xlargePaperIconButton material-icons skip_next"></span>
</button>
<button is="paper-icon-button-light" class="btnAudio hide autoSize" title="${Audio}">
<span class="xlargePaperIconButton material-icons audiotrack"></span>
</button>
<button is="paper-icon-button-light" class="btnSubtitles hide autoSize" title="${Subtitles}">
<span class="xlargePaperIconButton material-icons closed_caption"></span>
</button>
<button is="paper-icon-button-light" class="btnVideoOsdSettings hide autoSize" title="${Settings}">
<span class="largePaperIconButton material-icons settings"></span>
</button>
<button is="paper-icon-button-light" class="btnFullscreen hide autoSize" title="${Fullscreen} (f)">
<span class="xlargePaperIconButton material-icons fullscreen"></span>
</button>
<button is="paper-icon-button-light" class="btnPip hide autoSize" title="${PictureInPicture}">
<span class="xlargePaperIconButton material-icons picture_in_picture_alt"></span>
</button>
<button is="paper-icon-button-light" class="btnAirPlay hide autoSize" title="${AirPlay}">
<span class="xlargePaperIconButton material-icons airplay"></span>
</button>
<div class="osdTimeText">
<span class="osdPositionText"></span>
<span class="osdDurationText"></span>
<span class="endsAtText"></span>
</div>
<button is="paper-icon-button-light" class="btnSubtitles hide autoSize" title="${Subtitles}">
<span class="xlargePaperIconButton material-icons closed_caption"></span>
</button>
<button is="paper-icon-button-light" class="btnAudio hide autoSize" title="${Audio}">
<span class="xlargePaperIconButton material-icons audiotrack"></span>
</button>
<div class="volumeButtons hide-mouse-idle-tv">
<button is="paper-icon-button-light" class="buttonMute autoSize" title="${Mute} (m)">
<span class="xlargePaperIconButton material-icons volume_up"></span>
@ -81,6 +65,18 @@
<input is="emby-slider" type="range" step="1" min="0" max="100" value="0" class="osdVolumeSlider" />
</div>
</div>
<button is="paper-icon-button-light" class="btnVideoOsdSettings hide autoSize" title="${Settings}">
<span class="largePaperIconButton material-icons settings"></span>
</button>
<button is="paper-icon-button-light" class="btnAirPlay hide autoSize" title="${AirPlay}">
<span class="xlargePaperIconButton material-icons airplay"></span>
</button>
<button is="paper-icon-button-light" class="btnPip hide autoSize" title="${PictureInPicture}">
<span class="xlargePaperIconButton material-icons picture_in_picture_alt"></span>
</button>
<button is="paper-icon-button-light" class="btnFullscreen hide autoSize" title="${Fullscreen} (f)">
<span class="xlargePaperIconButton material-icons fullscreen"></span>
</button>
</div>
</div>
</div>

View file

@ -20,50 +20,6 @@ import 'css!assets/css/videoosd';
/* eslint-disable indent */
function seriesImageUrl(item, options) {
if (item.Type !== 'Episode') {
return null;
}
options = options || {};
options.type = options.type || 'Primary';
if (options.type === 'Primary' && item.SeriesPrimaryImageTag) {
options.tag = item.SeriesPrimaryImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options);
}
if (options.type === 'Thumb') {
if (item.SeriesThumbImageTag) {
options.tag = item.SeriesThumbImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options);
}
if (item.ParentThumbImageTag) {
options.tag = item.ParentThumbImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options);
}
}
return null;
}
function imageUrl(item, options) {
options = options || {};
options.type = options.type || 'Primary';
if (item.ImageTags && item.ImageTags[options.type]) {
options.tag = item.ImageTags[options.type];
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options);
}
if (options.type === 'Primary' && item.AlbumId && item.AlbumPrimaryImageTag) {
options.tag = item.AlbumPrimaryImageTag;
return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options);
}
return null;
}
function getOpenedDialog() {
return document.querySelector('.dialogContainer .dialog.opened');
}
@ -163,7 +119,6 @@ import 'css!assets/css/videoosd';
currentItem = item;
const displayItem = itemInfo.displayItem || item;
updateRecordingButton(displayItem);
setPoster(displayItem, item);
let parentName = displayItem.SeriesName || displayItem.Album;
if (displayItem.EpisodeTitle || displayItem.IsSeries) {
@ -171,42 +126,6 @@ import 'css!assets/css/videoosd';
}
setTitle(displayItem, parentName);
const titleElement = view.querySelector('.osdTitle');
let displayName = itemHelper.getDisplayName(displayItem, {
includeParentInfo: displayItem.Type !== 'Program',
includeIndexNumber: displayItem.Type !== 'Program'
});
if (!displayName) {
displayName = displayItem.Type;
}
titleElement.innerHTML = displayName;
if (displayName) {
titleElement.classList.remove('hide');
} else {
titleElement.classList.add('hide');
}
const mediaInfoHtml = mediaInfo.getPrimaryMediaInfoHtml(displayItem, {
runtime: false,
subtitles: false,
tomatoes: false,
endsAt: false,
episodeTitle: false,
originalAirDate: displayItem.Type !== 'Program',
episodeTitleIndexNumber: displayItem.Type !== 'Program',
programIndicator: false
});
const osdMediaInfo = view.querySelector('.osdMediaInfo');
osdMediaInfo.innerHTML = mediaInfoHtml;
if (mediaInfoHtml) {
osdMediaInfo.classList.remove('hide');
} else {
osdMediaInfo.classList.add('hide');
}
const secondaryMediaInfo = view.querySelector('.osdSecondaryMediaInfo');
const secondaryMediaInfoHtml = mediaInfo.getSecondaryMediaInfoHtml(displayItem, {
@ -221,12 +140,6 @@ import 'css!assets/css/videoosd';
secondaryMediaInfo.classList.add('hide');
}
if (displayName) {
view.querySelector('.osdMainTextContainer').classList.remove('hide');
} else {
view.querySelector('.osdMainTextContainer').classList.add('hide');
}
if (enableProgressByTimeOfDay) {
setDisplayTime(startTimeText, displayItem.StartDate);
setDisplayTime(endTimeText, displayItem.EndDate);
@ -276,7 +189,6 @@ import 'css!assets/css/videoosd';
currentItem = item;
if (!item) {
setPoster(null);
updateRecordingButton(null);
Emby.Page.setTitle('');
nowPlayingVolumeSlider.disabled = true;
@ -313,7 +225,20 @@ import 'css!assets/css/videoosd';
}
function setTitle(item, parentName) {
Emby.Page.setTitle(parentName || '');
let itemName = itemHelper.getDisplayName(item, {
includeParentInfo: item.Type !== 'Program',
includeIndexNumber: item.Type !== 'Program'
});
if (itemName && parentName) {
itemName = `${parentName} - ${itemName}`;
}
if (!itemName) {
itemName = parentName || '';
}
Emby.Page.setTitle(itemName);
const documentTitle = parentName || (item ? item.Name : null);
@ -322,38 +247,6 @@ import 'css!assets/css/videoosd';
}
}
function setPoster(item, secondaryItem) {
const osdPoster = view.querySelector('.osdPoster');
if (item) {
let imgUrl = seriesImageUrl(item, {
maxWidth: osdPoster.clientWidth,
type: 'Primary'
}) || seriesImageUrl(item, {
maxWidth: osdPoster.clientWidth,
type: 'Thumb'
}) || imageUrl(item, {
maxWidth: osdPoster.clientWidth,
type: 'Primary'
});
if (!imgUrl && secondaryItem && (imgUrl = seriesImageUrl(secondaryItem, {
maxWidth: osdPoster.clientWidth,
type: 'Primary'
}) || seriesImageUrl(secondaryItem, {
maxWidth: osdPoster.clientWidth,
type: 'Thumb'
}) || imageUrl(secondaryItem, {
maxWidth: osdPoster.clientWidth,
type: 'Primary'
})), imgUrl) {
return void (osdPoster.innerHTML = '<img src="' + imgUrl + '" />');
}
}
osdPoster.innerHTML = '';
}
let mouseIsDown = false;
function showOsd() {

View file

@ -41,6 +41,7 @@ import globalize from 'globalize';
type: 'POST',
url: ApiClient.getUrl('Users/ForgotPassword'),
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({
EnteredUsername: view.querySelector('#txtName').value
})

View file

@ -42,6 +42,10 @@
<button is="emby-button" type="button" class="raised cancel block btnManual">
<span>${ButtonManualLogin}</span>
</button>
<button is="emby-button" type="button" class="raised cancel block btnQuick">
<span>${ButtonUseQuickConnect}</span>
</button>
<button is="emby-button" type="button" class="raised cancel block btnForgotPassword">
<span>${ButtonForgotPassword}</span>

View file

@ -16,11 +16,10 @@ import 'emby-checkbox';
function authenticateUserByName(page, apiClient, username, password) {
loading.show();
apiClient.authenticateUserByName(username, password).then(function (result) {
var user = result.User;
const user = result.User;
loading.hide();
Dashboard.onServerChanged(user.Id, result.AccessToken, apiClient);
Dashboard.navigate('home.html');
onLoginSuccessful(user.Id, result.AccessToken, apiClient);
}, function (response) {
page.querySelector('#txtManualName').value = '';
page.querySelector('#txtManualPassword').value = '';
@ -41,6 +40,60 @@ import 'emby-checkbox';
});
}
function authenticateQuickConnect(apiClient) {
const url = apiClient.getUrl('/QuickConnect/Initiate');
apiClient.getJSON(url).then(function (json) {
if (!json.Secret || !json.Code) {
console.error('Malformed quick connect response', json);
return false;
}
Dashboard.alert({
message: globalize.translate('QuickConnectAuthorizeCode', json.Code),
title: globalize.translate('QuickConnect')
});
const connectUrl = apiClient.getUrl('/QuickConnect/Connect?Secret=' + json.Secret);
const interval = setInterval(function() {
apiClient.getJSON(connectUrl).then(async function(data) {
if (!data.Authenticated) {
return;
}
clearInterval(interval);
const result = await apiClient.quickConnect(data.Authentication);
onLoginSuccessful(result.User.Id, result.AccessToken, apiClient);
}, function (e) {
clearInterval(interval);
Dashboard.alert({
message: globalize.translate('QuickConnectDeactivated'),
title: globalize.translate('HeaderError')
});
console.error('Unable to login with quick connect', e);
});
}, 5000, connectUrl);
return true;
}, function(e) {
Dashboard.alert({
message: globalize.translate('QuickConnectNotActive'),
title: globalize.translate('HeaderError')
});
console.error('Quick connect error: ', e);
return false;
});
}
function onLoginSuccessful(id, accessToken, apiClient) {
Dashboard.onServerChanged(id, accessToken, apiClient);
Dashboard.navigate('home.html');
}
function showManualForm(context, showCancel, focusPassword) {
context.querySelector('.chkRememberLogin').checked = appSettings.enableAutoLogin();
context.querySelector('.manualLoginForm').classList.remove('hide');
@ -187,6 +240,11 @@ import 'emby-checkbox';
Dashboard.navigate('forgotpassword.html');
});
view.querySelector('.btnCancel').addEventListener('click', showVisualForm);
view.querySelector('.btnQuick').addEventListener('click', function () {
const apiClient = getApiClient();
authenticateQuickConnect(apiClient);
return false;
});
view.querySelector('.btnManual').addEventListener('click', function () {
view.querySelector('#txtManualName').value = '';
showManualForm(view, true);
@ -194,6 +252,7 @@ import 'emby-checkbox';
view.querySelector('.btnSelectServer').addEventListener('click', function () {
Dashboard.selectServer();
});
view.addEventListener('viewshow', function (e) {
loading.show();
libraryMenu.setTransparentMenu(true);

View file

@ -1,68 +0,0 @@
import loading from 'loading';
import groupedcards from 'components/groupedcards';
import cardBuilder from 'cardBuilder';
import imageLoader from 'imageLoader';
/* eslint-disable indent */
function getLatestPromise(context, params) {
loading.show();
const userId = ApiClient.getCurrentUserId();
const parentId = params.topParentId;
const options = {
IncludeItemTypes: 'Episode',
Limit: 30,
Fields: 'PrimaryImageAspectRatio,BasicSyncInfo',
ParentId: parentId,
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb'
};
return ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options));
}
function loadLatest(context, params, promise) {
promise.then(function (items) {
let html = '';
html += cardBuilder.getCardsHtml({
items: items,
shape: 'backdrop',
preferThumb: true,
showTitle: true,
showSeriesYear: true,
showParentTitle: true,
overlayText: false,
cardLayout: false,
showUnplayedIndicator: false,
showChildCountIndicator: true,
centerText: true,
lazy: true,
overlayPlayButton: true,
lines: 2
});
const elem = context.querySelector('#latestEpisodes');
elem.innerHTML = html;
imageLoader.lazyChildren(elem);
loading.hide();
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(context);
});
});
}
export default function (view, params, tabContent) {
const self = this;
let latestPromise;
self.preRender = function () {
latestPromise = getLatestPromise(view, params);
};
self.renderTab = function () {
loadLatest(tabContent, params, latestPromise);
};
tabContent.querySelector('#latestEpisodes').addEventListener('click', groupedcards);
}
/* eslint-enable indent */

View file

@ -23,8 +23,15 @@
<div is="emby-itemscontainer" id="resumableItems" class="itemsContainer padded-left padded-right"></div>
</div>
<div id="latestItemsSection" class="hide verticalSection">
<div class="sectionTitleContainer sectionTitleContainer-cards">
<h2 class="sectionTitle sectionTitle-cards padded-left">${HeaderLatestEpisodes}</h2>
</div>
<div class="verticalSection">
<div is="emby-itemscontainer" id="latestEpisodesItems" class="itemsContainer padded-left padded-right"></div>
</div>
<div id="nextUpItemsSection" class="hide verticalSection">
<div class="sectionTitleContainer sectionTitleContainer-cards">
<h2 class="sectionTitle sectionTitle-cards padded-left nextUpHeader">${NextUp}</h2>
</div>
@ -33,16 +40,7 @@
</div>
<p class="noNextUpItems" style="display: none;">${MessageNoNextUpItems}</p>
</div>
<div class="pageTabContent" id="latestTab" data-index="2">
<div class="verticalSection">
<div class="sectionTitleContainer sectionTitleContainer-cards">
<h2 class="sectionTitle sectionTitle-cards padded-left">${HeaderLatestEpisodes}</h2>
</div>
<div is="emby-itemscontainer" id="latestEpisodes" class="itemsContainer vertical-wrap padded-left padded-right">
</div>
</div>
</div>
<div class="pageTabContent" id="upcomingTab" data-index="3">
<div class="pageTabContent" id="upcomingTab" data-index="2">
<div id="upcomingItems">
</div>
<div class="noItemsMessage centerMessage" style="display: none;">
@ -50,13 +48,13 @@
<p>${MessagePleaseEnsureInternetMetadata}</p>
</div>
</div>
<div class="pageTabContent" id="genresTab" data-index="4">
<div class="pageTabContent" id="genresTab" data-index="3">
<div id="items"></div>
</div>
<div class="pageTabContent" id="studiosTab" data-index="5">
<div class="pageTabContent" id="studiosTab" data-index="4">
<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" data-index="6">
<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="paging"></div>
<button is="paper-icon-button-light" class="btnSelectView autoSize" title="${ButtonSelectView}"><span class="material-icons view_comfy"></span></button>
@ -69,6 +67,4 @@
<div class="paging"></div>
</div>
</div>
<div class="pageTabContent" data-index="7">
</div>
</div>

View file

@ -20,8 +20,6 @@ import 'emby-button';
name: globalize.translate('Shows')
}, {
name: globalize.translate('Suggestions')
}, {
name: globalize.translate('TabLatest')
}, {
name: globalize.translate('TabUpcoming')
}, {
@ -38,15 +36,18 @@ import 'emby-button';
case 'suggestions':
return 1;
case 'latest':
case 'upcoming':
return 2;
case 'favorites':
return 1;
case 'genres':
return 3;
case 'networks':
return 4;
case 'episodes':
return 5;
default:
return 0;
}
@ -70,102 +71,159 @@ import 'emby-button';
}
}
function initSuggestedTab(page, tabContent) {
const containers = tabContent.querySelectorAll('.itemsContainer');
for (let i = 0, length = containers.length; i < length; i++) {
setScrollClasses(containers[i], enableScrollX());
}
}
function loadSuggestionsTab(view, params, tabContent) {
const parentId = params.topParentId;
const userId = ApiClient.getCurrentUserId();
console.debug('loadSuggestionsTab');
loadResume(tabContent, userId, parentId);
loadLatest(tabContent, userId, parentId);
loadNextUp(tabContent, userId, parentId);
}
function loadResume(view, userId, parentId) {
const screenWidth = dom.getWindowSize().innerWidth;
const options = {
SortBy: 'DatePlayed',
SortOrder: 'Descending',
IncludeItemTypes: 'Episode',
Filters: 'IsResumable',
Limit: screenWidth >= 1920 ? 5 : screenWidth >= 1600 ? 5 : 3,
Recursive: true,
Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo',
CollapseBoxSetItems: false,
ParentId: parentId,
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb',
EnableTotalRecordCount: false
};
ApiClient.getItems(userId, options).then(function (result) {
if (result.Items.length) {
view.querySelector('#resumableSection').classList.remove('hide');
} else {
view.querySelector('#resumableSection').classList.add('hide');
}
const allowBottomPadding = !enableScrollX();
const container = view.querySelector('#resumableItems');
cardBuilder.buildCards(result.Items, {
itemsContainer: container,
preferThumb: true,
shape: getThumbShape(),
scalable: true,
overlayPlayButton: true,
allowBottomPadding: allowBottomPadding,
cardLayout: false,
showTitle: true,
showYear: true,
centerText: true
});
loading.hide();
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(view);
});
});
}
function loadLatest(view, userId, parentId) {
const options = {
userId: userId,
IncludeItemTypes: 'Episode',
Limit: 30,
Fields: 'PrimaryImageAspectRatio,BasicSyncInfo',
ParentId: parentId,
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb'
};
ApiClient.getLatestItems(options).then(function (items) {
const section = view.querySelector('#latestItemsSection');
const allowBottomPadding = !enableScrollX();
const container = section.querySelector('#latestEpisodesItems');
cardBuilder.buildCards(items, {
parentContainer: section,
itemsContainer: container,
items: items,
shape: 'backdrop',
preferThumb: true,
showTitle: true,
showSeriesYear: true,
showParentTitle: true,
overlayText: false,
cardLayout: false,
allowBottomPadding: allowBottomPadding,
showUnplayedIndicator: false,
showChildCountIndicator: true,
centerText: true,
lazy: true,
overlayPlayButton: true,
lines: 2
});
loading.hide();
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(view);
});
});
}
function loadNextUp(view, userId, parentId) {
const query = {
userId: userId,
Limit: 24,
Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo',
ParentId: parentId,
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb',
EnableTotalRecordCount: false
};
query.ParentId = libraryMenu.getTopParentId();
ApiClient.getNextUpEpisodes(query).then(function (result) {
if (result.Items.length) {
view.querySelector('.noNextUpItems').classList.add('hide');
} else {
view.querySelector('.noNextUpItems').classList.remove('hide');
}
const section = view.querySelector('#nextUpItemsSection');
const container = section.querySelector('#nextUpItems');
cardBuilder.buildCards(result.Items, {
parentContainer: section,
itemsContainer: container,
preferThumb: true,
shape: 'backdrop',
scalable: true,
showTitle: true,
showParentTitle: true,
overlayText: false,
centerText: true,
overlayPlayButton: true,
cardLayout: false
});
loading.hide();
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(view);
});
});
}
function enableScrollX() {
return !layoutManager.desktop;
}
function getThumbShape() {
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
}
export default function (view, params) {
function reload() {
loading.show();
loadResume();
loadNextUp();
}
function loadNextUp() {
const query = {
Limit: 24,
Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo',
UserId: ApiClient.getCurrentUserId(),
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb',
EnableTotalRecordCount: false
};
query.ParentId = libraryMenu.getTopParentId();
ApiClient.getNextUpEpisodes(query).then(function (result) {
if (result.Items.length) {
view.querySelector('.noNextUpItems').classList.add('hide');
} else {
view.querySelector('.noNextUpItems').classList.remove('hide');
}
const container = view.querySelector('#nextUpItems');
cardBuilder.buildCards(result.Items, {
itemsContainer: container,
preferThumb: true,
shape: 'backdrop',
scalable: true,
showTitle: true,
showParentTitle: true,
overlayText: false,
centerText: true,
overlayPlayButton: true,
cardLayout: false
});
loading.hide();
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(view);
});
});
}
function enableScrollX() {
return !layoutManager.desktop;
}
function getThumbShape() {
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
}
function loadResume() {
const parentId = libraryMenu.getTopParentId();
const screenWidth = dom.getWindowSize().innerWidth;
const limit = screenWidth >= 1600 ? 5 : 6;
const options = {
SortBy: 'DatePlayed',
SortOrder: 'Descending',
IncludeItemTypes: 'Episode',
Filters: 'IsResumable',
Limit: limit,
Recursive: true,
Fields: 'PrimaryImageAspectRatio,SeriesInfo,UserData,BasicSyncInfo',
ExcludeLocationTypes: 'Virtual',
ParentId: parentId,
ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Thumb',
EnableTotalRecordCount: false
};
ApiClient.getItems(ApiClient.getCurrentUserId(), options).then(function (result) {
if (result.Items.length) {
view.querySelector('#resumableSection').classList.remove('hide');
} else {
view.querySelector('#resumableSection').classList.add('hide');
}
const allowBottomPadding = !enableScrollX();
const container = view.querySelector('#resumableItems');
cardBuilder.buildCards(result.Items, {
itemsContainer: container,
preferThumb: true,
shape: getThumbShape(),
scalable: true,
showTitle: true,
showParentTitle: true,
overlayText: false,
centerText: true,
overlayPlayButton: true,
allowBottomPadding: allowBottomPadding,
cardLayout: false
});
});
}
function onBeforeTabChange(e) {
preLoadTab(view, parseInt(e.detail.selectedTabIndex));
}
@ -196,22 +254,18 @@ import 'emby-button';
break;
case 2:
depends = 'controllers/shows/tvlatest';
break;
case 3:
depends = 'controllers/shows/tvupcoming';
break;
case 4:
case 3:
depends = 'controllers/shows/tvgenres';
break;
case 5:
case 4:
depends = 'controllers/shows/tvstudios';
break;
case 6:
case 5:
depends = 'controllers/shows/episodes';
break;
}
@ -231,11 +285,6 @@ import 'emby-button';
if (index === 1) {
controller = self;
} else if (index === 7) {
controller = new controllerFactory(view, tabContent, {
collectionType: 'tvshows',
parentId: params.topParentId
});
} else {
controller = new controllerFactory(view, params, tabContent);
}
@ -294,19 +343,20 @@ import 'emby-button';
const self = this;
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
const suggestionsTabIndex = 1;
self.initTab = function () {
const tabContent = self.tabContent;
setScrollClasses(tabContent.querySelector('#resumableItems'), enableScrollX());
const tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
initSuggestedTab(view, tabContent);
};
self.renderTab = function () {
reload();
const tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
loadSuggestionsTab(view, params, tabContent);
};
const tabControllers = [];
let renderedTabs = [];
setScrollClasses(view.querySelector('#resumableItems'), enableScrollX());
view.addEventListener('viewshow', function (e) {
initTabs();
if (!view.getAttribute('data-title')) {

View file

@ -54,8 +54,8 @@ import 'emby-itemscontainer';
return context.savedQueryKey;
}
function onViewStyleChange() {
const viewStyle = self.getCurrentViewStyle();
const onViewStyleChange = () => {
const viewStyle = this.getCurrentViewStyle();
const itemsContainer = tabContent.querySelector('.itemsContainer');
if (viewStyle == 'List') {
@ -67,13 +67,13 @@ import 'emby-itemscontainer';
}
itemsContainer.innerHTML = '';
}
};
function reloadItems(page) {
const reloadItems = (page) => {
loading.show();
isLoading = true;
const query = getQuery(page);
ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) {
ApiClient.getItems(ApiClient.getCurrentUserId(), query).then((result) => {
function onNextPageClick() {
if (isLoading) {
return;
@ -109,7 +109,7 @@ import 'emby-itemscontainer';
sortButton: false,
filterButton: false
});
const viewStyle = self.getCurrentViewStyle();
const viewStyle = this.getCurrentViewStyle();
if (viewStyle == 'Thumb') {
html = cardBuilder.getCardsHtml({
items: result.Items,
@ -169,18 +169,18 @@ import 'emby-itemscontainer';
let elems = tabContent.querySelectorAll('.paging');
for (let i = 0, length = elems.length; i < length; i++) {
elems[i].innerHTML = pagingHtml;
for (const elem of elems) {
elem.innerHTML = pagingHtml;
}
elems = tabContent.querySelectorAll('.btnNextPage');
for (let i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onNextPageClick);
for (const elem of elems) {
elem.addEventListener('click', onNextPageClick);
}
elems = tabContent.querySelectorAll('.btnPreviousPage');
for (let i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onPreviousPageClick);
for (const elem of elems) {
elem.addEventListener('click', onPreviousPageClick);
}
const itemsContainer = tabContent.querySelector('.itemsContainer');
@ -194,18 +194,26 @@ import 'emby-itemscontainer';
autoFocuser.autoFocus(page);
});
});
}
};
function updateFilterControls(tabContent) {
const updateFilterControls = (tabContent) => {
const query = getQuery(tabContent);
self.alphaPicker.value(query.NameStartsWithOrGreater);
}
const self = this;
if (this.alphaPicker) {
this.alphaPicker.value(query.NameStartsWith);
if (query.SortBy.indexOf('SortName') === 0) {
this.alphaPicker.visible(true);
} else {
this.alphaPicker.visible(false);
}
}
};
const data = {};
let isLoading = false;
self.showFilterMenu = function () {
this.showFilterMenu = function () {
import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => {
const filterDialog = new filterDialogFactory({
query: getQuery(tabContent),
@ -220,22 +228,22 @@ import 'emby-itemscontainer';
});
};
self.getCurrentViewStyle = function () {
this.getCurrentViewStyle = function () {
return getPageData(tabContent).view;
};
function initPage(tabContent) {
const initPage = (tabContent) => {
const alphaPickerElement = tabContent.querySelector('.alphaPicker');
const itemsContainer = tabContent.querySelector('.itemsContainer');
alphaPickerElement.addEventListener('alphavaluechanged', function (e) {
const newValue = e.detail.value;
const query = getQuery(tabContent);
query.NameStartsWithOrGreater = newValue;
query.NameStartsWith = newValue;
query.StartIndex = 0;
reloadItems(tabContent);
});
self.alphaPicker = new AlphaPicker({
this.alphaPicker = new AlphaPicker({
element: alphaPickerElement,
valueChangeEvent: 'click'
});
@ -244,8 +252,8 @@ import 'emby-itemscontainer';
alphaPickerElement.classList.add('alphaPicker-fixed-right');
itemsContainer.classList.add('padded-right-withalphapicker');
tabContent.querySelector('.btnFilter').addEventListener('click', function () {
self.showFilterMenu();
tabContent.querySelector('.btnFilter').addEventListener('click', () => {
this.showFilterMenu();
});
tabContent.querySelector('.btnSort').addEventListener('click', function (e) {
libraryBrowser.showSortMenu({
@ -277,8 +285,8 @@ import 'emby-itemscontainer';
});
});
const btnSelectView = tabContent.querySelector('.btnSelectView');
btnSelectView.addEventListener('click', function (e) {
libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
btnSelectView.addEventListener('click', (e) => {
libraryBrowser.showLayoutMenu(e.target, this.getCurrentViewStyle(), 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
});
btnSelectView.addEventListener('layoutchange', function (e) {
const viewStyle = e.detail.viewStyle;
@ -288,17 +296,17 @@ import 'emby-itemscontainer';
onViewStyleChange();
reloadItems(tabContent);
});
}
};
initPage(tabContent);
onViewStyleChange();
self.renderTab = function () {
this.renderTab = function () {
reloadItems(tabContent);
updateFilterControls(tabContent);
};
self.destroy = function () {};
this.destroy = function () {};
}
/* eslint-enable indent */

View file

@ -48,6 +48,16 @@
</div>
</a>
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="lnkQuickConnectPreferences listItem-border">
<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>

View file

@ -26,6 +26,7 @@ export default function (view, params) {
page.querySelector('.lnkHomePreferences').setAttribute('href', 'mypreferenceshome.html?userId=' + userId);
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');
if (window.NativeShell && window.NativeShell.AppHost.supports('clientsettings')) {
page.querySelector('.clientSettings').classList.remove('hide');

View file

@ -0,0 +1,17 @@
<div id="quickConnectPreferencesPage" data-role="page" class="page libraryPage userPreferencesPage noSecondaryNavPage" data-title="${QuickConnect}" data-backbutton="true" style="margin: 0 auto; max-width: 54em">
<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>
<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>

View file

@ -0,0 +1,61 @@
import QuickConnectSettings from 'quickConnectSettings';
import globalize from 'globalize';
import toast from 'toast';
export default function (view) {
let quickConnectSettingsInstance = null;
view.addEventListener('viewshow', function () {
const codeElement = view.querySelector('#txtQuickConnectCode');
quickConnectSettingsInstance = new QuickConnectSettings();
view.querySelector('#btnQuickConnectActivate').addEventListener('click', () => {
quickConnectSettingsInstance.activate(quickConnectSettingsInstance).then(() => {
renderPage();
});
});
view.querySelector('#btnQuickConnectAuthorize').addEventListener('click', () => {
if (!codeElement.validity.valid) {
toast(globalize.translate('QuickConnectInvalidCode'));
return;
}
const code = codeElement.value;
quickConnectSettingsInstance.authorize(code);
});
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;
});
}
}