buttons to configure plex or view the current configuration
This commit is contained in:
parent
919cd1a2b2
commit
6344f250fc
3 changed files with 177 additions and 19 deletions
104
plex_pin_auth.py
Normal file
104
plex_pin_auth.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to authenticate with Plex using PIN-based login.
|
||||
This mimics the app's /api/plex/pin/start and /api/plex/pin/status flows.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
from plexapi.myplex import MyPlexAccount, MyPlexPinLogin
|
||||
|
||||
|
||||
def main():
|
||||
print('Starting Plex PIN login flow...')
|
||||
try:
|
||||
pinlogin = MyPlexPinLogin(oauth=False)
|
||||
except Exception as exc:
|
||||
print(f'Failed to create MyPlexPinLogin: {exc}', file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
return 1
|
||||
|
||||
pin = getattr(pinlogin, 'pin', None)
|
||||
if not pin:
|
||||
print('Failed to obtain PIN from Plex', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
print(f'\nPlease go to the following URL in your browser and enter the PIN: {pin}')
|
||||
print(f' https://plex.tv/link')
|
||||
print('Then return here and wait for authentication to complete.')
|
||||
|
||||
while True:
|
||||
if getattr(pinlogin, 'expired', False):
|
||||
print('\nPIN has expired. Please rerun this script to start again.', file=sys.stderr)
|
||||
return 2
|
||||
|
||||
try:
|
||||
if pinlogin.checkLogin():
|
||||
token = getattr(pinlogin, 'token', None)
|
||||
if not token:
|
||||
print('Login succeeded but no token was returned.', file=sys.stderr)
|
||||
return 3
|
||||
|
||||
print('\nLogin complete!')
|
||||
print(f'Plex token: {token}')
|
||||
|
||||
try:
|
||||
account = MyPlexAccount(token=token)
|
||||
except Exception as exc:
|
||||
print(f'Failed to create MyPlexAccount: {exc}', file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
return 4
|
||||
|
||||
print(f'Authenticated Plex username: {getattr(account, "username", None)}')
|
||||
print(f'Authenticated Plex title: {getattr(account, "title", None)}')
|
||||
|
||||
print('\nFetching available Plex resources...')
|
||||
try:
|
||||
resources = account.resources()
|
||||
except Exception as exc:
|
||||
print(f'Failed to fetch resources: {exc}', file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
return 5
|
||||
|
||||
server_resources = [r for r in resources if 'server' in (getattr(r, 'provides', '') or '')]
|
||||
if server_resources:
|
||||
print(f'Found {len(server_resources)} Plex server resource(s):')
|
||||
for idx, resource in enumerate(server_resources, 1):
|
||||
print(f'[{idx}] {getattr(resource, "name", None)} ({getattr(resource, "product", None)})')
|
||||
connections = getattr(resource, 'connections', []) or []
|
||||
for conn in connections:
|
||||
uri = getattr(conn, 'uri', None)
|
||||
local = getattr(conn, 'local', False)
|
||||
relay = getattr(conn, 'relay', False)
|
||||
print(f' - {uri} {'(LOCAL)' if local else ''}{' (RELAY)' if relay else ''}')
|
||||
|
||||
local_conn = None
|
||||
for resource in server_resources:
|
||||
for conn in getattr(resource, 'connections', []) or []:
|
||||
if getattr(conn, 'local', False):
|
||||
local_conn = conn
|
||||
break
|
||||
if local_conn:
|
||||
break
|
||||
|
||||
if local_conn:
|
||||
print(f'\nSelected local server URI: {getattr(local_conn, "uri", None)}')
|
||||
else:
|
||||
print('\nNo local server connection found. Use one of the above URIs manually.')
|
||||
else:
|
||||
print('No Plex server resources were found for this account.')
|
||||
|
||||
return 0
|
||||
|
||||
print('PIN not yet authorized. Polling again in 5 seconds...')
|
||||
except Exception as exc:
|
||||
print(f'Error checking PIN login status: {exc}', file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
return 6
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
|
@ -4318,26 +4318,35 @@
|
|||
|
||||
<!-- Plex Settings -->
|
||||
<div class="server-config-container" id="plex-container">
|
||||
<div class="form-group">
|
||||
<label>Plex Server URL:</label>
|
||||
<input type="url" id="plex-url" placeholder="http://localhost:32400">
|
||||
<div id="plex-configuration">
|
||||
<div class="form-group">
|
||||
<label>Plex Server URL:</label>
|
||||
<input type="url" id="plex-url" placeholder="http://localhost:32400">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Plex Token:</label>
|
||||
<input type="password" id="plex-token" placeholder="X-Plex-Token">
|
||||
</div>
|
||||
<div class="form-group" id="plex-library-selector-container" style="display: none;">
|
||||
<label>Music Library:</label>
|
||||
<select id="plex-music-library" onchange="selectPlexLibrary()">
|
||||
<option value="">Loading...</option>
|
||||
</select>
|
||||
<small style="color: #999; font-size: 0.9em; display: block; margin-top: 5px;">
|
||||
Select which music library to use (doesn't affect config file)
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="detect-button" onclick="autoDetectPlex()">Auto-detect</button>
|
||||
<button class="test-button" onclick="testConnection('plex')">Test</button>
|
||||
<button class="detect-button" onclick="clearPlexConfiguration()">Clear Configuration</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Plex Token:</label>
|
||||
<input type="password" id="plex-token" placeholder="X-Plex-Token">
|
||||
</div>
|
||||
<div class="form-group" id="plex-library-selector-container" style="display: none;">
|
||||
<label>Music Library:</label>
|
||||
<select id="plex-music-library" onchange="selectPlexLibrary()">
|
||||
<option value="">Loading...</option>
|
||||
</select>
|
||||
<small style="color: #999; font-size: 0.9em; display: block; margin-top: 5px;">
|
||||
Select which music library to use (doesn't affect config file)
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="detect-button" onclick="autoDetectPlex()">Auto-detect</button>
|
||||
<button class="test-button" onclick="testConnection('plex')">Test</button>
|
||||
<div id="plex-setup">
|
||||
<div class="form-actions">
|
||||
<button id="plex-configure-button" class="detect-button configure-button">Configure</button>
|
||||
<button id="plex-view-config-button" class="detect-button view-config-button" onclick="showPlexConfiguration()">View Configuration</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -6026,6 +6026,15 @@ async function loadSettingsData() {
|
|||
// Populate Plex settings
|
||||
document.getElementById('plex-url').value = settings.plex?.base_url || '';
|
||||
document.getElementById('plex-token').value = settings.plex?.token || '';
|
||||
const hasPlexConfig = Boolean(settings.plex?.base_url || settings.plex?.token);
|
||||
const plexViewConfigButton = document.getElementById('plex-view-config-button');
|
||||
const plexConfigureButton = document.getElementById('plex-configure-button');
|
||||
if (plexViewConfigButton) {
|
||||
plexViewConfigButton.style.display = hasPlexConfig ? '' : 'none';
|
||||
}
|
||||
if (plexConfigureButton) {
|
||||
plexConfigureButton.style.display = hasPlexConfig ? 'none' : '';
|
||||
}
|
||||
|
||||
// Populate Jellyfin settings
|
||||
document.getElementById('jellyfin-url').value = settings.jellyfin?.base_url || '';
|
||||
|
|
@ -6416,6 +6425,36 @@ function updateMediaServerFields() {
|
|||
}
|
||||
}
|
||||
|
||||
function showPlexConfiguration() {
|
||||
const plexConfig = document.getElementById('plex-configuration');
|
||||
const plexSetup = document.getElementById('plex-setup');
|
||||
if (plexConfig) plexConfig.style.display = '';
|
||||
if (plexSetup) plexSetup.style.display = 'none';
|
||||
}
|
||||
|
||||
function clearPlexConfiguration() {
|
||||
const plexUrl = document.getElementById('plex-url');
|
||||
const plexToken = document.getElementById('plex-token');
|
||||
const plexConfig = document.getElementById('plex-configuration');
|
||||
const plexSetup = document.getElementById('plex-setup');
|
||||
const plexViewConfigButton = document.getElementById('plex-view-config-button');
|
||||
const plexConfigureButton = document.getElementById('plex-configure-button');
|
||||
|
||||
if (plexUrl) plexUrl.value = '';
|
||||
if (plexToken) plexToken.value = '';
|
||||
if (plexConfig) plexConfig.style.display = 'none';
|
||||
if (plexSetup) plexSetup.style.display = '';
|
||||
if (plexViewConfigButton) plexViewConfigButton.style.display = 'none';
|
||||
if (plexConfigureButton) plexConfigureButton.style.display = '';
|
||||
|
||||
if (typeof saveSettings === 'function') {
|
||||
saveSettings(true);
|
||||
}
|
||||
if (typeof showToast === 'function') {
|
||||
showToast('Plex configuration cleared', 'success');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleServer(serverType) {
|
||||
// Update toggle buttons
|
||||
document.getElementById('plex-toggle').classList.remove('active');
|
||||
|
|
@ -6430,6 +6469,12 @@ function toggleServer(serverType) {
|
|||
document.getElementById('navidrome-container').classList.toggle('hidden', serverType !== 'navidrome');
|
||||
document.getElementById('soulsync-container')?.classList.toggle('hidden', serverType !== 'soulsync');
|
||||
|
||||
// Show Plex setup when Plex is selected; otherwise hide both Plex panels
|
||||
const plexConfig = document.getElementById('plex-configuration');
|
||||
const plexSetup = document.getElementById('plex-setup');
|
||||
if (plexConfig) plexConfig.style.display = serverType === 'plex' ? 'none' : '';
|
||||
if (plexSetup) plexSetup.style.display = serverType === 'plex' ? '' : 'none';
|
||||
|
||||
// Load Plex music libraries when switching to Plex
|
||||
if (serverType === 'plex') {
|
||||
loadPlexMusicLibraries();
|
||||
|
|
|
|||
Loading…
Reference in a new issue