Duncan Tourolle 9723c13bd3
Some checks failed
Build Plugin / build (push) Failing after 10s
Added sync group page and settings
2025-12-14 11:25:58 +01:00

255 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JellyLMS</title>
</head>
<body>
<div id="JellyLmsConfigPage" data-role="page" class="page type-interior pluginConfigurationPage" data-require="emby-input,emby-button,emby-select,emby-checkbox">
<div data-role="content">
<div class="content-primary">
<h2>JellyLMS Configuration</h2>
<p>Configure the connection between Jellyfin and Logitech Media Server (LMS) for multi-room audio playback.</p>
<form id="JellyLmsConfigForm">
<div class="verticalSection">
<h3>LMS Server Settings</h3>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="LmsServerUrl">LMS Server URL</label>
<input id="LmsServerUrl" name="LmsServerUrl" type="url" is="emby-input" />
<div class="fieldDescription">The URL of your LMS server (e.g., http://192.168.1.100:9000)</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="LmsUsername">LMS Username (optional)</label>
<input id="LmsUsername" name="LmsUsername" type="text" is="emby-input" />
<div class="fieldDescription">Username if LMS authentication is enabled</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="LmsPassword">LMS Password (optional)</label>
<input id="LmsPassword" name="LmsPassword" type="password" is="emby-input" />
<div class="fieldDescription">Password if LMS authentication is enabled</div>
</div>
<div>
<button is="emby-button" type="button" id="btnTestConnection" class="raised button-alt block emby-button">
<span>Test Connection</span>
</button>
<div id="connectionStatus" style="margin-top: 10px;"></div>
</div>
</div>
<div class="verticalSection">
<h3>Jellyfin Server Settings</h3>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="JellyfinServerUrl">Jellyfin Server URL</label>
<input id="JellyfinServerUrl" name="JellyfinServerUrl" type="url" is="emby-input" />
<div class="fieldDescription">The URL that LMS will use to stream audio from Jellyfin. This must be accessible from the LMS server (e.g., http://192.168.1.4:8096).</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="JellyfinApiKey">Jellyfin API Key</label>
<input id="JellyfinApiKey" name="JellyfinApiKey" type="password" is="emby-input" />
<div class="fieldDescription">API key for LMS to authenticate with Jellyfin. Create one in Dashboard > API Keys.</div>
</div>
</div>
<div class="verticalSection">
<h3>Playback Settings</h3>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="ConnectionTimeoutSeconds">Connection Timeout (seconds)</label>
<input id="ConnectionTimeoutSeconds" name="ConnectionTimeoutSeconds" type="number" is="emby-input" min="5" max="60" />
<div class="fieldDescription">Timeout for LMS API requests</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="EnableAutoSync" name="EnableAutoSync" type="checkbox" is="emby-checkbox" />
<span>Enable Auto-Sync</span>
</label>
<div class="fieldDescription checkboxFieldDescription">Automatically sync players when playing to multiple devices</div>
</div>
</div>
<div class="verticalSection">
<h3>LMS Players</h3>
<div id="playersList">
<p>Click "Refresh Players" to discover LMS players.</p>
</div>
<div>
<button is="emby-button" type="button" id="btnRefreshPlayers" class="raised button-alt block emby-button">
<span>Refresh Players</span>
</button>
</div>
<div class="inputContainer" style="margin-top: 15px;">
<label class="inputLabel inputLabelUnfocused" for="DefaultPlayerMac">Default Player</label>
<select is="emby-select" id="DefaultPlayerMac" name="DefaultPlayerMac" class="emby-select-withcolor emby-select">
<option value="">None</option>
</select>
<div class="fieldDescription">Default player to use when none is specified</div>
</div>
</div>
<div class="verticalSection">
<h3>Player Sync Management</h3>
<p>Manage synchronized playback groups for multi-room audio.</p>
<div>
<a is="emby-button" href="configurationpage?name=JellyLMS%20Sync" class="raised button-alt block emby-button" style="display: inline-block; text-decoration: none;">
<span>Open Sync Manager</span>
</a>
</div>
<div class="fieldDescription" style="margin-top: 10px;">
Users can access this page directly at: <code>/web/configurationpage?name=JellyLMS%20Sync</code>
</div>
</div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block emby-button">
<span>Save</span>
</button>
</div>
</form>
</div>
</div>
<script type="text/javascript">
var JellyLmsConfig = {
pluginUniqueId: 'a5b8c9d0-1e2f-3a4b-5c6d-7e8f9a0b1c2d'
};
function loadPlayers() {
var playersList = document.querySelector('#playersList');
var defaultSelect = document.querySelector('#DefaultPlayerMac');
var currentDefault = defaultSelect.value;
playersList.innerHTML = '<p>Loading players...</p>';
ApiClient.ajax({
url: ApiClient.getUrl('JellyLms/Players', { refresh: true }),
type: 'GET',
dataType: 'json'
}).then(function(players) {
defaultSelect.innerHTML = '<option value="">None</option>';
if (!players || players.length === 0) {
playersList.innerHTML = '<p>No players found. Make sure LMS is running and has connected players.</p>';
return;
}
var html = '<table class="tblGenres detailTable" style="width: 100%;">';
html += '<thead><tr><th>Name</th><th>Model</th><th>Status</th><th>Volume</th><th>Synced</th></tr></thead>';
html += '<tbody>';
players.forEach(function(player) {
var status = player.IsConnected ? (player.IsPoweredOn ? 'On' : 'Standby') : 'Disconnected';
var statusColor = player.IsConnected ? (player.IsPoweredOn ? 'green' : 'orange') : 'red';
var syncStatus = player.IsSynced ? 'Yes' : 'No';
html += '<tr>';
html += '<td>' + player.Name + '</td>';
html += '<td>' + player.Model + '</td>';
html += '<td style="color: ' + statusColor + ';">' + status + '</td>';
html += '<td>' + player.Volume + '%</td>';
html += '<td>' + syncStatus + '</td>';
html += '</tr>';
var option = document.createElement('option');
option.value = player.MacAddress;
option.text = player.Name;
if (player.MacAddress === currentDefault) {
option.selected = true;
}
defaultSelect.appendChild(option);
});
html += '</tbody></table>';
playersList.innerHTML = html;
}).catch(function(err) {
playersList.innerHTML = '<p style="color: red;">Error loading players. Check LMS connection.</p>';
console.error('Error loading players:', err);
});
}
function testConnection() {
var statusDiv = document.querySelector('#connectionStatus');
statusDiv.innerHTML = '<span style="color: orange;">Testing connection...</span>';
ApiClient.ajax({
url: ApiClient.getUrl('JellyLms/TestConnection'),
type: 'POST',
dataType: 'json'
}).then(function(result) {
if (result.IsConnected) {
statusDiv.innerHTML = '<span style="color: green;">Connected! Found ' + result.PlayerCount + ' player(s).</span>';
loadPlayers();
} else {
statusDiv.innerHTML = '<span style="color: red;">Connection failed: ' + (result.LastError || 'Unknown error') + '</span>';
}
}).catch(function(err) {
statusDiv.innerHTML = '<span style="color: red;">Connection failed. Check the server URL.</span>';
console.error('Connection test failed:', err);
});
}
document.querySelector('#JellyLmsConfigPage')
.addEventListener('pageshow', function() {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(JellyLmsConfig.pluginUniqueId).then(function (config) {
document.querySelector('#LmsServerUrl').value = config.LmsServerUrl || 'http://localhost:9000';
document.querySelector('#LmsUsername').value = config.LmsUsername || '';
document.querySelector('#LmsPassword').value = config.LmsPassword || '';
document.querySelector('#JellyfinServerUrl').value = config.JellyfinServerUrl || 'http://localhost:8096';
document.querySelector('#JellyfinApiKey').value = config.JellyfinApiKey || '';
document.querySelector('#ConnectionTimeoutSeconds').value = config.ConnectionTimeoutSeconds || 10;
document.querySelector('#EnableAutoSync').checked = config.EnableAutoSync !== false;
document.querySelector('#DefaultPlayerMac').value = config.DefaultPlayerMac || '';
Dashboard.hideLoadingMsg();
});
});
document.querySelector('#btnTestConnection')
.addEventListener('click', function() {
ApiClient.getPluginConfiguration(JellyLmsConfig.pluginUniqueId).then(function (config) {
config.LmsServerUrl = document.querySelector('#LmsServerUrl').value;
config.LmsUsername = document.querySelector('#LmsUsername').value;
config.LmsPassword = document.querySelector('#LmsPassword').value;
ApiClient.updatePluginConfiguration(JellyLmsConfig.pluginUniqueId, config).then(function() {
testConnection();
});
});
});
document.querySelector('#btnRefreshPlayers')
.addEventListener('click', function() {
loadPlayers();
});
document.querySelector('#JellyLmsConfigForm')
.addEventListener('submit', function(e) {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(JellyLmsConfig.pluginUniqueId).then(function (config) {
config.LmsServerUrl = document.querySelector('#LmsServerUrl').value;
config.LmsUsername = document.querySelector('#LmsUsername').value;
config.LmsPassword = document.querySelector('#LmsPassword').value;
config.JellyfinServerUrl = document.querySelector('#JellyfinServerUrl').value;
config.JellyfinApiKey = document.querySelector('#JellyfinApiKey').value;
config.ConnectionTimeoutSeconds = parseInt(document.querySelector('#ConnectionTimeoutSeconds').value) || 10;
config.EnableAutoSync = document.querySelector('#EnableAutoSync').checked;
config.DefaultPlayerMac = document.querySelector('#DefaultPlayerMac').value;
ApiClient.updatePluginConfiguration(JellyLmsConfig.pluginUniqueId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result);
});
});
e.preventDefault();
return false;
});
</script>
</div>
</body>
</html>