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 -->
|
<!-- Plex Settings -->
|
||||||
<div class="server-config-container" id="plex-container">
|
<div class="server-config-container" id="plex-container">
|
||||||
<div class="form-group">
|
<div id="plex-configuration">
|
||||||
<label>Plex Server URL:</label>
|
<div class="form-group">
|
||||||
<input type="url" id="plex-url" placeholder="http://localhost:32400">
|
<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>
|
||||||
<div class="form-group">
|
<div id="plex-setup">
|
||||||
<label>Plex Token:</label>
|
<div class="form-actions">
|
||||||
<input type="password" id="plex-token" placeholder="X-Plex-Token">
|
<button id="plex-configure-button" class="detect-button configure-button">Configure</button>
|
||||||
</div>
|
<button id="plex-view-config-button" class="detect-button view-config-button" onclick="showPlexConfiguration()">View Configuration</button>
|
||||||
<div class="form-group" id="plex-library-selector-container" style="display: none;">
|
</div>
|
||||||
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6026,6 +6026,15 @@ async function loadSettingsData() {
|
||||||
// Populate Plex settings
|
// Populate Plex settings
|
||||||
document.getElementById('plex-url').value = settings.plex?.base_url || '';
|
document.getElementById('plex-url').value = settings.plex?.base_url || '';
|
||||||
document.getElementById('plex-token').value = settings.plex?.token || '';
|
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
|
// Populate Jellyfin settings
|
||||||
document.getElementById('jellyfin-url').value = settings.jellyfin?.base_url || '';
|
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) {
|
function toggleServer(serverType) {
|
||||||
// Update toggle buttons
|
// Update toggle buttons
|
||||||
document.getElementById('plex-toggle').classList.remove('active');
|
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('navidrome-container').classList.toggle('hidden', serverType !== 'navidrome');
|
||||||
document.getElementById('soulsync-container')?.classList.toggle('hidden', serverType !== 'soulsync');
|
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
|
// Load Plex music libraries when switching to Plex
|
||||||
if (serverType === 'plex') {
|
if (serverType === 'plex') {
|
||||||
loadPlexMusicLibraries();
|
loadPlexMusicLibraries();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue