Add files via upload

This commit is contained in:
holyarahippo06 authored and GitHub committed 2025-07-27 19:08:28 +02:00
1 parent 14e07f51dc
commit 782ee417f5
1 file changed
+226
+226
View File
@@ -0,0 +1,226 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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 */
}
</style>
</head>
<body>
<div class="container">
<h1>PS4 CDN Server Control Panel</h1>
<div class="grid">
<div class="panel">
<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>
<div id="tabs-container" class="button-group" style="margin-bottom: 20px;"></div>
<ul id="pkg-list" class="pkg-list">
<li class="loading">Loading...</li>
</ul>
</div>
</div>
</div>
<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');
// --- 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 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';
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>
`;
tabsContainer.appendChild(button);
}
});
};
// --- All other JavaScript functions remain unchanged ---
const renderPackages = () => { /* ... */
const filteredPackages = allPackages.filter(pkg => currentFilter === 'All' || (pkg.apptype || 'Unknown') === currentFilter);
pkgCountSpan.textContent = filteredPackages.length;
pkgList.innerHTML = '';
if (filteredPackages.length === 0) { pkgList.innerHTML = '<li>No packages match this filter.</li>'; return; }
const baseUri = window.location.origin;
filteredPackages.forEach(pkg => {
const li = document.createElement('li');
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>`;
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; }
};
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
initializeLibrary();
});
</script>
</body>
</html>