mirror of
https://github.com/holyarahippo06/PS4-CDN-Server-py.git
synced 2026-10-06 07:00:23 +02:00
Update index.html
This commit is contained in:
1 parent
cda1dad597
commit
6498fdf12c
1 file changed
+34
-141
@@ -5,38 +5,18 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PS4 CDN Server Control Panel</title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
<!-- MODIFIED: Styles updated for the new two-line tab layout -->
|
||||
<style>
|
||||
.button-group button.active { background-color: #007bff; color: white; }
|
||||
#tabs-container { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 10px; -webkit-overflow-scrolling: touch; scrollbar-width: thin; }
|
||||
#tabs-container::-webkit-scrollbar { height: 8px; }
|
||||
#tabs-container::-webkit-scrollbar-thumb { background-color: #555; border-radius: 4px; }
|
||||
|
||||
/* --- NEW CSS FOR TWO-LINE TABS --- */
|
||||
.tab-button {
|
||||
display: flex;
|
||||
flex-direction: column; /* This is the key: stack items vertically */
|
||||
align-items: center; /* Center horizontally */
|
||||
justify-content: center; /* Center vertically */
|
||||
padding: 6px 10px; /* Adjust padding for a tighter look */
|
||||
line-height: 1.3; /* Improve spacing between lines */
|
||||
min-width: 80px; /* Give buttons a consistent minimum width */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tab-button .tab-title {
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tab-button .tab-count {
|
||||
font-size: 0.85em;
|
||||
font-weight: 400;
|
||||
opacity: 0.8; /* Make the count slightly less prominent */
|
||||
}
|
||||
.tab-button { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 6px 10px; line-height: 1.3; min-width: 80px; text-align: center; }
|
||||
.tab-button .tab-title { font-size: 1em; font-weight: 500; }
|
||||
.tab-button .tab-count { font-size: 0.85em; font-weight: 400; opacity: 0.8; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- The main HTML structure remains unchanged -->
|
||||
<div class="container">
|
||||
<h1>PS4 CDN Server Control Panel</h1>
|
||||
<div class="grid">
|
||||
@@ -44,30 +24,24 @@
|
||||
<h2>Server Configuration</h2>
|
||||
<label for="basePath">PKG Folder Path</label>
|
||||
<input type="text" id="basePath" value="{{ server_state.config.base_path }}">
|
||||
|
||||
<div class="button-group">
|
||||
<button id="scanBtn">Scan Folder</button>
|
||||
<button id="saveSettingsBtn">Save Settings</button>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>HB-Store Management</h2>
|
||||
<label for="ps4Ip">PS4 IP Address</label>
|
||||
<input type="text" id="ps4Ip" value="{{ server_state.config.ps4_ip }}" placeholder="e.g., 192.168.1.12">
|
||||
<label for="ps4Port">PS4 FTP Port</label>
|
||||
<input type="text" id="ps4Port" value="{{ server_state.config.ps4_port }}">
|
||||
|
||||
<div class="button-group">
|
||||
<button id="updateCdnBtn">Set My Server as CDN</button>
|
||||
<button id="restoreCdnBtn" class="secondary">Restore Official CDN</button>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button id="updateBinariesBtn" class="secondary">Update HB-Store Binaries</button>
|
||||
<button id="rebuildBtn" class="dangerous">Full Rescan</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h2>Package Library (<span id="pkg-count">0</span>)</h2>
|
||||
@@ -81,58 +55,30 @@
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// --- DOM Elements ---
|
||||
const basePathInput = document.getElementById('basePath');
|
||||
const ps4IpInput = document.getElementById('ps4Ip');
|
||||
const ps4PortInput = document.getElementById('ps4Port');
|
||||
const scanBtn = document.getElementById('scanBtn');
|
||||
const saveSettingsBtn = document.getElementById('saveSettingsBtn');
|
||||
const rebuildBtn = document.getElementById('rebuildBtn');
|
||||
const updateCdnBtn = document.getElementById('updateCdnBtn');
|
||||
const restoreCdnBtn = document.getElementById('restoreCdnBtn');
|
||||
const updateBinariesBtn = document.getElementById('updateBinariesBtn');
|
||||
const pkgList = document.getElementById('pkg-list');
|
||||
const pkgCountSpan = document.getElementById('pkg-count');
|
||||
const tabsContainer = document.getElementById('tabs-container');
|
||||
// --- All other JS is the same, only renderPackages is modified ---
|
||||
const basePathInput = document.getElementById('basePath'), ps4IpInput = document.getElementById('ps4Ip'), ps4PortInput = document.getElementById('ps4Port'), scanBtn = document.getElementById('scanBtn'), saveSettingsBtn = document.getElementById('saveSettingsBtn'), rebuildBtn = document.getElementById('rebuildBtn'), updateCdnBtn = document.getElementById('updateCdnBtn'), restoreCdnBtn = document.getElementById('restoreCdnBtn'), updateBinariesBtn = document.getElementById('updateBinariesBtn'), pkgList = document.getElementById('pkg-list'), pkgCountSpan = document.getElementById('pkg-count'), tabsContainer = document.getElementById('tabs-container');
|
||||
let allPackages = [], currentFilter = 'All';
|
||||
|
||||
// --- State ---
|
||||
let allPackages = [];
|
||||
let currentFilter = 'All';
|
||||
|
||||
// --- MODIFIED: The setupTabs function now uses the new classes and structure ---
|
||||
const setupTabs = () => {
|
||||
const displayNameMap = {
|
||||
"All": "All", "HB Game": "Games", "App": "Apps", "DLC": "DLCs",
|
||||
"Patch": "Patches", "Theme": "Themes", "Other": "Other", "Unknown": "Unknown"
|
||||
};
|
||||
const displayNameMap = { "All": "All", "HB Game": "Games", "App": "Apps", "DLC": "DLCs", "Patch": "Patches", "Theme": "Themes", "Other": "Other", "Unknown": "Unknown" };
|
||||
const counts = { 'All': allPackages.length };
|
||||
allPackages.forEach(pkg => { const type = pkg.apptype || 'Unknown'; counts[type] = (counts[type] || 0) + 1; });
|
||||
const tabOrder = ['All', 'HB Game', 'App', 'DLC', 'Patch', 'Theme', 'Other', 'Unknown'];
|
||||
tabsContainer.innerHTML = '';
|
||||
|
||||
tabOrder.forEach(type => {
|
||||
if (counts[type] > 0) {
|
||||
const count = counts[type];
|
||||
const displayName = displayNameMap[type] || type;
|
||||
const button = document.createElement('button');
|
||||
|
||||
// Add our new special class for styling
|
||||
button.className = 'secondary tab-button';
|
||||
const count = counts[type], displayName = displayNameMap[type] || type, button = document.createElement('button');
|
||||
button.className = 'secondary tab-button';
|
||||
if (type === currentFilter) button.classList.add('active');
|
||||
button.dataset.filter = type;
|
||||
|
||||
// Use spans to separate the title and count for CSS control
|
||||
button.innerHTML = `
|
||||
<span class="tab-title">${displayName}</span>
|
||||
<span class="tab-count">(${count})</span>
|
||||
`;
|
||||
button.innerHTML = `<span class="tab-title">${displayName}</span><span class="tab-count">(${count})</span>`;
|
||||
tabsContainer.appendChild(button);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// --- All other JavaScript functions remain unchanged ---
|
||||
const renderPackages = () => { /* ... */
|
||||
// --- MODIFIED: The renderPackages function now displays the new info ---
|
||||
const renderPackages = () => {
|
||||
const filteredPackages = allPackages.filter(pkg => currentFilter === 'All' || (pkg.apptype || 'Unknown') === currentFilter);
|
||||
pkgCountSpan.textContent = filteredPackages.length;
|
||||
pkgList.innerHTML = '';
|
||||
@@ -140,87 +86,34 @@
|
||||
const baseUri = window.location.origin;
|
||||
filteredPackages.forEach(pkg => {
|
||||
const li = document.createElement('li');
|
||||
|
||||
// Add the scraped description as a tooltip
|
||||
li.title = pkg.description || 'No description available.';
|
||||
|
||||
const iconUrl = pkg.icon_url ? `${baseUri}${pkg.icon_url}` : '/static/default.png';
|
||||
li.innerHTML = `<img src="${iconUrl}" alt="icon" class="pkg-icon" onerror="this.onerror=null;this.src='/static/default.png';"><div class="pkg-info"><span class="pkg-title">${pkg.TITLE || 'No Title'}</span><span class="pkg-details">${pkg.apptype || 'Unknown'} | ${pkg.SIZE}</span></div>`;
|
||||
const rating = pkg.rating || 'N/A'; // Get the rating
|
||||
|
||||
// Add the rating to the details string
|
||||
li.innerHTML = `<img src="${iconUrl}" alt="icon" class="pkg-icon" onerror="this.onerror=null;this.src='/static/default.png';">
|
||||
<div class="pkg-info">
|
||||
<span class="pkg-title">${pkg.TITLE || 'No Title'}</span>
|
||||
<span class="pkg-details">${pkg.apptype || 'Unknown'} | Rating: ${rating} | ${pkg.SIZE}</span>
|
||||
</div>`;
|
||||
pkgList.appendChild(li);
|
||||
});
|
||||
};
|
||||
const initializeLibrary = async () => { /* ... */
|
||||
try {
|
||||
pkgList.innerHTML = '<li class="loading">Fetching packages...</li>';
|
||||
const response = await fetch('/api/packages');
|
||||
allPackages = await response.json();
|
||||
setupTabs();
|
||||
renderPackages();
|
||||
} catch (error) { console.error("Error initializing library:", error); pkgList.innerHTML = '<li>Error loading packages.</li>'; }
|
||||
};
|
||||
saveSettingsBtn.addEventListener('click', async () => { /* ... */
|
||||
saveSettingsBtn.textContent = 'Saving...'; saveSettingsBtn.disabled = true;
|
||||
try {
|
||||
const configData = { base_path: basePathInput.value, ps4_ip: ps4IpInput.value, ps4_port: parseInt(ps4PortInput.value) || 2121 };
|
||||
const response = await fetch('/api/actions/save_config', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(configData) });
|
||||
const result = await response.json();
|
||||
if (!response.ok) throw new Error(result.detail || 'Failed to save.');
|
||||
alert(result.message);
|
||||
} catch (error) { alert('Error saving settings: ' + error.message);
|
||||
} finally { saveSettingsBtn.textContent = 'Save Settings'; saveSettingsBtn.disabled = false; }
|
||||
});
|
||||
scanBtn.addEventListener('click', async () => { /* ... */
|
||||
scanBtn.textContent = 'Scanning...'; scanBtn.disabled = true;
|
||||
try {
|
||||
await fetch('/api/actions/scan', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({base_path: basePathInput.value}) });
|
||||
alert('Scan complete!');
|
||||
initializeLibrary();
|
||||
} catch (error) { alert('Error during scan: ' + error.message);
|
||||
} finally { scanBtn.textContent = 'Scan Folder'; scanBtn.disabled = false; }
|
||||
});
|
||||
rebuildBtn.addEventListener('click', async () => { /* ... */
|
||||
if (!confirm("Are you sure? This will delete the database and rescan everything.")) return;
|
||||
rebuildBtn.textContent = 'Rebuilding...'; rebuildBtn.disabled = true;
|
||||
try {
|
||||
const response = await fetch('/api/actions/full_rescan', { method: 'POST' });
|
||||
const result = await response.json();
|
||||
alert(result.message);
|
||||
initializeLibrary();
|
||||
} catch (error) { alert('Error during rebuild: ' + error.message);
|
||||
} finally { rebuildBtn.textContent = 'Full Rescan'; rebuildBtn.disabled = false; }
|
||||
});
|
||||
updateBinariesBtn.addEventListener('click', async () => { /* ... */
|
||||
updateBinariesBtn.textContent = 'Updating...'; updateBinariesBtn.disabled = true;
|
||||
try {
|
||||
const response = await fetch('/api/actions/update_binaries', { method: 'POST' });
|
||||
const result = await response.json();
|
||||
alert(result.message);
|
||||
} catch (error) { alert('Error updating binaries: ' + error.message);
|
||||
} finally { updateBinariesBtn.textContent = 'Update HB-Store Binaries'; updateBinariesBtn.disabled = false; }
|
||||
});
|
||||
const handleCdnUpdate = async (isRestore) => { /* ... */
|
||||
const ps4Ip = ps4IpInput.value; if (!ps4Ip) { alert("Please enter the PS4 IP address."); return; }
|
||||
const button = isRestore ? restoreCdnBtn : updateCdnBtn;
|
||||
button.textContent = 'Updating...'; button.disabled = true;
|
||||
const newUrl = isRestore ? 'https://www.ps4-hen.com/store' : `http://${window.location.hostname}:{{ request.url.port }}`;
|
||||
try {
|
||||
const response = await fetch('/api/ps4/update_cdn', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ ps4_ip: ps4Ip, ps4_port: parseInt(ps4PortInput.value) || 2121, new_cdn_url: newUrl }) });
|
||||
const result = await response.json();
|
||||
alert(result.message);
|
||||
} catch (error) { alert('Error updating CDN: ' + error.message);
|
||||
} finally { button.textContent = isRestore ? 'Restore Official CDN' : 'Set My Server as CDN'; button.disabled = false; }
|
||||
};
|
||||
|
||||
const initializeLibrary = async () => { try { pkgList.innerHTML = '<li class="loading">Fetching packages...</li>'; const response = await fetch('/api/packages'); allPackages = await response.json(); setupTabs(); renderPackages(); } catch (error) { console.error("Error initializing library:", error); pkgList.innerHTML = '<li>Error loading packages.</li>'; } };
|
||||
saveSettingsBtn.addEventListener('click', async () => { saveSettingsBtn.textContent = 'Saving...'; saveSettingsBtn.disabled = true; try { const configData = { base_path: basePathInput.value, ps4_ip: ps4IpInput.value, ps4_port: parseInt(ps4PortInput.value) || 2121 }; const response = await fetch('/api/actions/save_config', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(configData) }); const result = await response.json(); if (!response.ok) throw new Error(result.detail || 'Failed to save.'); alert(result.message); } catch (error) { alert('Error saving settings: ' + error.message); } finally { saveSettingsBtn.textContent = 'Save Settings'; saveSettingsBtn.disabled = false; } });
|
||||
scanBtn.addEventListener('click', async () => { scanBtn.textContent = 'Scanning...'; scanBtn.disabled = true; try { await fetch('/api/actions/scan', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({base_path: basePathInput.value}) }); alert('Scan complete!'); initializeLibrary(); } catch (error) { alert('Error during scan: ' + error.message); } finally { scanBtn.textContent = 'Scan Folder'; scanBtn.disabled = false; } });
|
||||
rebuildBtn.addEventListener('click', async () => { if (!confirm("Are you sure? This will delete the database and rescan everything.")) return; rebuildBtn.textContent = 'Rebuilding...'; rebuildBtn.disabled = true; try { const response = await fetch('/api/actions/full_rescan', { method: 'POST' }); const result = await response.json(); alert(result.message); initializeLibrary(); } catch (error) { alert('Error during rebuild: ' + error.message); } finally { rebuildBtn.textContent = 'Full Rescan'; rebuildBtn.disabled = false; } });
|
||||
updateBinariesBtn.addEventListener('click', async () => { updateBinariesBtn.textContent = 'Updating...'; updateBinariesBtn.disabled = true; try { const response = await fetch('/api/actions/update_binaries', { method: 'POST' }); const result = await response.json(); alert(result.message); } catch (error) { alert('Error updating binaries: ' + error.message); } finally { updateBinariesBtn.textContent = 'Update HB-Store Binaries'; updateBinariesBtn.disabled = false; } });
|
||||
const handleCdnUpdate = async (isRestore) => { const ps4Ip = ps4IpInput.value; if (!ps4Ip) { alert("Please enter the PS4 IP address."); return; } const button = isRestore ? restoreCdnBtn : updateCdnBtn; button.textContent = 'Updating...'; button.disabled = true; const newUrl = isRestore ? 'https://www.ps4-hen.com/store' : `http://${window.location.hostname}:{{ request.url.port }}`; try { const response = await fetch('/api/ps4/update_cdn', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ ps4_ip: ps4Ip, ps4_port: parseInt(ps4PortInput.value) || 2121, new_cdn_url: newUrl }) }); const result = await response.json(); alert(result.message); } catch (error) { alert('Error updating CDN: ' + error.message); } finally { button.textContent = isRestore ? 'Restore Official CDN' : 'Set My Server as CDN'; button.disabled = false; } };
|
||||
updateCdnBtn.addEventListener('click', () => handleCdnUpdate(false));
|
||||
restoreCdnBtn.addEventListener('click', () => handleCdnUpdate(true));
|
||||
tabsContainer.addEventListener('click', (event) => { /* ... */
|
||||
const target = event.target.closest('button');
|
||||
if (target) {
|
||||
currentFilter = target.dataset.filter;
|
||||
tabsContainer.querySelector('.active')?.classList.remove('active');
|
||||
target.classList.add('active');
|
||||
renderPackages();
|
||||
}
|
||||
});
|
||||
|
||||
// Initializer
|
||||
tabsContainer.addEventListener('click', (event) => { const target = event.target.closest('button'); if (target) { currentFilter = target.dataset.filter; tabsContainer.querySelector('.active')?.classList.remove('active'); target.classList.add('active'); renderPackages(); } });
|
||||
initializeLibrary();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
Reference in new issue
Block a user