diff --git a/src/controllers/user/menu/index.js b/src/controllers/user/menu/index.js
index 7a8d619bb..88cf28a21 100644
--- a/src/controllers/user/menu/index.js
+++ b/src/controllers/user/menu/index.js
@@ -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');
diff --git a/src/controllers/user/quickConnect/index.html b/src/controllers/user/quickConnect/index.html
new file mode 100644
index 000000000..15df59ff1
--- /dev/null
+++ b/src/controllers/user/quickConnect/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
diff --git a/src/controllers/user/quickConnect/index.js b/src/controllers/user/quickConnect/index.js
new file mode 100644
index 000000000..00fc5488b
--- /dev/null
+++ b/src/controllers/user/quickConnect/index.js
@@ -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 () {
+ let 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;
+ }
+
+ let code = codeElement.value;
+ quickConnectSettingsInstance.authorize(code);
+ });
+
+ view.querySelector('.quickConnectSettingsContainer').addEventListener('submit', (e) => {
+ e.preventDefault();
+ });
+
+ renderPage();
+ });
+
+ function renderPage(forceActive = false) {
+ ApiClient.getQuickConnect('Status').then((status) => {
+ let btn = view.querySelector('#btnQuickConnectActivate');
+ let 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;
+ });
+ }
+}
diff --git a/src/quickconnect.html b/src/quickconnect.html
new file mode 100644
index 000000000..2b646837c
--- /dev/null
+++ b/src/quickconnect.html
@@ -0,0 +1,24 @@
+
diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js
index 04edecf19..10e4a3c9c 100644
--- a/src/scripts/libraryMenu.js
+++ b/src/scripts/libraryMenu.js
@@ -404,6 +404,12 @@ import 'flexStyles';
pageIds: ['devicesPage', 'devicePage'],
icon: 'devices'
});
+ links.push({
+ name: globalize.translate('QuickConnect'),
+ href: 'quickConnect.html',
+ pageIds: ['quickConnectPage'],
+ icon: 'tap_and_play'
+ });
links.push({
name: globalize.translate('HeaderActivity'),
href: 'serveractivity.html',
diff --git a/src/scripts/routes.js b/src/scripts/routes.js
index 4bb3eb25d..d2d8e9188 100644
--- a/src/scripts/routes.js
+++ b/src/scripts/routes.js
@@ -97,6 +97,13 @@ import 'detailtablecss';
controller: 'user/home/index'
});
+ defineRoute({
+ alias: '/mypreferencesquickconnect.html',
+ path: '/controllers/user/quickConnect/index.html',
+ autoFocus: false,
+ transition: 'fade',
+ controller: 'user/quickConnect/index'
+ });
defineRoute({
alias: '/mypreferencesplayback.html',
path: '/controllers/user/playback/index.html',
@@ -151,6 +158,13 @@ import 'detailtablecss';
controller: 'dashboard/devices/device'
});
+ defineRoute({
+ path: '/quickconnect.html',
+ autoFocus: false,
+ roles: 'admin',
+ controller: 'dashboard/quickconnect'
+ });
+
defineRoute({
alias: '/dlnaprofile.html',
path: '/controllers/dashboard/dlna/profile.html',
diff --git a/src/scripts/site.js b/src/scripts/site.js
index f14670d82..868996f60 100644
--- a/src/scripts/site.js
+++ b/src/scripts/site.js
@@ -625,6 +625,7 @@ function initClient() {
define('displaySettings', [componentsPath + '/displaySettings/displaySettings'], returnFirstDependency);
define('playbackSettings', [componentsPath + '/playbackSettings/playbackSettings'], returnFirstDependency);
define('homescreenSettings', [componentsPath + '/homeScreenSettings/homeScreenSettings'], returnFirstDependency);
+ define('quickConnectSettings', [componentsPath + '/quickConnectSettings/quickConnectSettings'], returnFirstDependency);
define('playbackManager', [componentsPath + '/playback/playbackmanager'], getPlaybackManager);
define('timeSyncManager', [componentsPath + '/syncPlay/timeSyncManager'], returnDefault);
define('groupSelectionMenu', [componentsPath + '/syncPlay/groupSelectionMenu'], returnFirstDependency);
diff --git a/src/strings/en-us.json b/src/strings/en-us.json
index 0c08b77db..7e68ff5fe 100644
--- a/src/strings/en-us.json
+++ b/src/strings/en-us.json
@@ -45,6 +45,7 @@
"Audio": "Audio",
"AuthProviderHelp": "Select an authentication provider to be used to authenticate this user's password.",
"Auto": "Auto",
+ "Authorize": "Authorize",
"Backdrop": "Backdrop",
"Backdrops": "Backdrops",
"Banner": "Banner",
@@ -60,6 +61,7 @@
"Browse": "Browse",
"MessageBrowsePluginCatalog": "Browse our plugin catalog to view available plugins.",
"BurnSubtitlesHelp": "Determines if the server should burn in subtitles when transcoding videos. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VOBSUB, PGS, SUB, IDX, …) and certain ASS or SSA subtitles.",
+ "ButtonActivate": "Activate",
"ButtonAddImage": "Add Image",
"ButtonAddMediaLibrary": "Add Media Library",
"ButtonAddScheduledTaskTrigger": "Add Trigger",
@@ -107,6 +109,7 @@
"ButtonTogglePlaylist": "Playlist",
"ButtonTrailer": "Trailer",
"ButtonUninstall": "Uninstall",
+ "ButtonUseQuickConnect": "Use Quick Connect",
"ButtonWebsite": "Website",
"CancelRecording": "Cancel recording",
"CancelSeries": "Cancel series",
@@ -196,6 +199,7 @@
"EnableNextVideoInfoOverlayHelp": "At the end of a video, display info about the next video coming up in the current playlist.",
"EnablePhotos": "Display photos",
"EnablePhotosHelp": "Images will be detected and displayed alongside other media files.",
+ "EnableQuickConnect": "Enable quick connect on this server",
"EnableStreamLooping": "Auto-loop live streams",
"EnableStreamLoopingHelp": "Enable this if live streams only contain a few seconds of data and need to be continuously requested. Enabling this when not needed may cause problems.",
"EnableThemeSongsHelp": "Play theme songs in the background while browsing the library.",
@@ -507,6 +511,7 @@
"LabelCountry": "Country:",
"LabelCriticRating": "Critic rating:",
"LabelCurrentPassword": "Current password:",
+ "LabelCurrentStatus": "Current status:",
"LabelCustomCertificatePath": "Custom SSL certificate path:",
"LabelCustomCertificatePathHelp": "Path to a PKCS #12 file containing a certificate and private key to enable TLS support on a custom domain.",
"LabelCustomCss": "Custom CSS:",
@@ -713,6 +718,7 @@
"LabelPublicHttpPortHelp": "The public port number that should be mapped to the local HTTP port.",
"LabelPublicHttpsPort": "Public HTTPS port number:",
"LabelPublicHttpsPortHelp": "The public port number that should be mapped to the local HTTPS port.",
+ "LabelQuickConnectCode": "Quick connect code:",
"LabelReasonForTranscoding": "Reason for transcoding:",
"LabelRecord": "Record:",
"LabelRecordingPath": "Default recording path:",
@@ -1147,6 +1153,16 @@
"Profile": "Profile",
"Programs": "Programs",
"Quality": "Quality",
+ "QuickConnect": "Quick Connect",
+ "QuickConnectActivationSuccessful": "Successfully activated",
+ "QuickConnectAuthorizeCode": "Enter code {0} to login",
+ "QuickConnectAuthorizeSuccess": "Request authorized",
+ "QuickConnectAuthorizeFail": "Unknown quick connect code",
+ "QuickConnectDeactivated": "Quick connect was deactivated before the login request could be approved",
+ "QuickConnectDescription": "To sign in with quick connect, select the Quick Connect button on the device you are logging in from and enter the displayed code below.",
+ "QuickConnectInvalidCode": "Invalid quick connect code",
+ "QuickConnectNotAvailable": "Ask your server administrator to enable quick connect",
+ "QuickConnectNotActive": "Quick connect is not active on this server",
"Raised": "Raised",
"Rate": "Rate",
"RecentlyWatched": "Recently watched",