Auto commit: 2025-10-24T19:48:28.874Z

This commit is contained in:
Flatlogic Bot 2025-10-24 19:48:28 +00:00
parent 1287e70095
commit c184472fb4
5 changed files with 262 additions and 82 deletions

125
admin/dashboard.php Normal file
View File

@ -0,0 +1,125 @@
<?php
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
header('Location: /admin/index.php');
exit;
}
require_once __DIR__ . '/../db/config.php';
try {
$pdo = db();
// Create table if it doesn't exist
$pdo->exec("CREATE TABLE IF NOT EXISTS stations (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
logo_url VARCHAR(255)
)");
// Handle form submissions for add/edit/delete
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['add'])) {
$stmt = $pdo->prepare("INSERT INTO stations (name, url, logo_url) VALUES (?, ?, ?)");
$stmt->execute([$_POST['name'], $_POST['url'], $_POST['logo_url']]);
} elseif (isset($_POST['edit'])) {
$stmt = $pdo->prepare("UPDATE stations SET name = ?, url = ?, logo_url = ? WHERE id = ?");
$stmt->execute([$_POST['name'], $_POST['url'], $_POST['logo_url'], $_POST['id']]);
} elseif (isset($_POST['delete'])) {
$stmt = $pdo->prepare("DELETE FROM stations WHERE id = ?");
$stmt->execute([$_POST['id']]);
}
header("Location: dashboard.php");
exit;
}
// Fetch all stations
$stations = $pdo->query("SELECT * FROM stations ORDER BY name")->fetchAll();
} catch (PDOException $e) {
$error = "Database error: " . $e->getMessage();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body { background-color: #f8f9fa; }
.container { max-width: 900px; }
.card-header { display: flex; justify-content: space-between; align-items: center; }
</style>
</head>
<body>
<div class="container mt-5">
<div class="card">
<div class="card-header">
<h3>Manage Radio Stations</h3>
<a href="/admin/index.php?logout=true" class="btn btn-danger">Logout</a>
</div>
<div class="card-body">
<?php if (isset($error)): ?>
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<!-- Add Station Form -->
<div class="mb-4">
<h4>Add New Station</h4>
<form action="dashboard.php" method="POST">
<div class="row">
<div class="col-md-4">
<input type="text" name="name" class="form-control" placeholder="Station Name" required>
</div>
<div class="col-md-4">
<input type="url" name="url" class="form-control" placeholder="Stream URL" required>
</div>
<div class="col-md-4">
<input type="url" name="logo_url" class="form-control" placeholder="Logo URL">
</div>
</div>
<button type="submit" name="add" class="btn btn-primary mt-2">Add Station</button>
</form>
</div>
<hr>
<h4>Existing Stations</h4>
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Stream URL</th>
<th>Logo URL</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($stations)): ?>
<tr><td colspan="4" class="text-center">No stations found.</td></tr>
<?php else: ?>
<?php foreach ($stations as $station): ?>
<tr>
<form action="dashboard.php" method="POST">
<input type="hidden" name="id" value="<?= $station['id'] ?>">
<td><input type="text" name="name" class="form-control" value="<?= htmlspecialchars($station['name']) ?>"></td>
<td><input type="text" name="url" class="form-control" value="<?= htmlspecialchars($station['url']) ?>"></td>
<td><input type="text" name="logo_url" class="form-control" value="<?= htmlspecialchars($station['logo_url']) ?>"></td>
<td>
<button type="submit" name="edit" class="btn btn-sm btn-success">Save</button>
<button type="submit" name="delete" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure?')">Delete</button>
</td>
</form>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>

93
admin/index.php Normal file
View File

@ -0,0 +1,93 @@
<?php
session_start();
// If already logged in, redirect to dashboard
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
header('Location: dashboard.php');
exit;
}
// Hardcoded password - CHANGE THIS!
define('ADMIN_PASSWORD', 'password123');
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['password']) && $_POST['password'] === ADMIN_PASSWORD) {
$_SESSION['loggedin'] = true;
header('Location: dashboard.php');
exit;
} else {
$error = 'Invalid password.';
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Login</title>
<link rel="stylesheet" href="../assets/css/style.css">
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f2f5;
}
.login-container {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
text-align: center;
width: 100%;
max-width: 400px;
}
.login-container h2 {
margin-bottom: 1.5rem;
}
input[type="password"] {
width: 100%;
padding: 0.75rem;
margin-top: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
button {
width: 100%;
padding: 0.75rem;
margin-top: 1.5rem;
border: none;
border-radius: 4px;
background-color: #007bff;
color: white;
cursor: pointer;
font-size: 1rem;
}
button:hover {
background-color: #0056b3;
}
.error {
color: red;
margin-top: 1rem;
}
</style>
</head>
<body>
<div class="login-container">
<h2>Admin Login</h2>
<form action="index.php" method="POST">
<input type="password" name="password" placeholder="Password" required>
<br>
<button type="submit">Login</button>
</form>
<?php if ($error): ?>
<p class="error"><?php echo htmlspecialchars($error); ?></p>
<?php endif; ?>
</div>
</body>
</html>

12
api/get_stations.php Normal file
View File

@ -0,0 +1,12 @@
<?php
header('Content-Type: application/json');
require_once __DIR__ . '/../db/config.php';
try {
$pdo = db();
$stations = $pdo->query("SELECT id, name, url, logo_url FROM stations ORDER BY name")->fetchAll();
echo json_encode($stations);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

View File

@ -20,9 +20,9 @@ class RadioWave {
this.ui = {}; // To cache UI elements this.ui = {}; // To cache UI elements
} }
init() { async init() {
this.initUI(); this.initUI();
this.loadStations(); await this.loadStations();
this.bindEvents(); this.bindEvents();
this.applyAccentColor(localStorage.getItem('accentColor') || '#FF8C00'); this.applyAccentColor(localStorage.getItem('accentColor') || '#FF8C00');
if (localStorage.getItem('theme') === 'light') { if (localStorage.getItem('theme') === 'light') {
@ -102,17 +102,25 @@ class RadioWave {
} }
// Station Management // Station Management
loadStations() { async loadStations() {
const savedStations = localStorage.getItem('stations'); try {
this.stations = savedStations ? JSON.parse(savedStations) : [ const response = await fetch('/api/get_stations.php');
{ name: "Lofi Girl", url: "https://play.streamafrica.net/lofiradio", logo: "https://i.ytimg.com/vi/jfKfPfyJRdk/maxresdefault.jpg", genre: "Lofi" }, if (!response.ok) {
{ name: "Classic Rock", url: "http://198.178.123.23:8722/stream", logo: "https://cdn-radiotime-logos.tunein.com/s292341q.png", genre: "Rock" }, throw new Error(`HTTP error! status: ${response.status}`);
]; }
this.renderStationList(); const stations = await response.json();
this.stations = stations.map(station => ({
...station,
logo: station.logo_url // map db field to app field
}));
this.renderStationList();
if (this.stations.length > 0 && this.currentStationIndex === -1) {
this.playStation(0);
}
} catch (error) {
console.error("Could not load stations:", error);
this.ui.stationList.innerHTML = `<li class="error">Could not load stations.</li>`;
} }
saveStations() {
localStorage.setItem('stations', JSON.stringify(this.stations));
} }
renderStationList() { renderStationList() {
@ -129,21 +137,6 @@ class RadioWave {
}); });
} }
addStation(e) {
e.preventDefault();
const newStation = {
name: this.ui.newStationName.value,
url: this.ui.newStationUrl.value,
logo: this.ui.newStationLogo.value,
genre: this.ui.newStationGenre.value
};
this.stations.push(newStation);
this.saveStations();
this.renderStationList();
this.ui.addStationForm.reset();
this.hideAllModals();
}
handleStationClick(e) { handleStationClick(e) {
const li = e.target.closest('li'); const li = e.target.closest('li');
if (li) { if (li) {
@ -298,7 +291,7 @@ class RadioWave {
if (e.target.type === 'range') { if (e.target.type === 'range') {
const index = e.target.dataset.index; const index = e.target.dataset.index;
const value = e.target.value; const value = e.target.value;
if (this.eqBands[index]) { if (.eqBands[index]) {
this.eqBands[index].gain.value = value; this.eqBands[index].gain.value = value;
} }
this.ui.eqPresetSelect.value = 'custom'; this.ui.eqPresetSelect.value = 'custom';
@ -383,37 +376,14 @@ class RadioWave {
} }
// Data & Settings // Data & Settings
importStations(event) { setSleepTimer(minutes) {
const file = event.target.files[0]; clearTimeout(this.sleepTimer);
if (!file) return; if (minutes > 0) {
const reader = new FileReader(); this.sleepTimer = setTimeout(() => {
reader.onload = (e) => { this.togglePlayPause(false); // Pause the player
try { this.ui.sleepTimerSelect.value = 0;
const importedStations = JSON.parse(e.target.result); }, minutes * 60 * 1000);
if (Array.isArray(importedStations)) {
this.stations = importedStations;
this.saveStations();
this.renderStationList();
alert('Stations imported successfully!');
} else {
throw new Error('Invalid format');
} }
} catch (err) {
alert('Failed to import stations. Please check the file format.');
}
};
reader.readAsText(file);
}
exportStations() {
const data = JSON.stringify(this.stations, null, 2);
const blob = new Blob([data], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'radiowave_stations.json';
a.click();
URL.revokeObjectURL(url);
} }
setSleepTimer(minutes) { setSleepTimer(minutes) {

View File

@ -70,7 +70,6 @@
<div id="my-stations-tab" class="tab-content active"> <div id="my-stations-tab" class="tab-content active">
<div class="station-list-header"> <div class="station-list-header">
<input type="text" id="search-input" placeholder="Search your stations..."> <input type="text" id="search-input" placeholder="Search your stations...">
<button id="add-station-button" class="control-button"><i class="fas fa-plus"></i></button>
</div> </div>
<ul id="station-list"> <ul id="station-list">
<!-- Stations will be dynamically added here --> <!-- Stations will be dynamically added here -->
@ -114,30 +113,11 @@
<option value="60">1 Hour</option> <option value="60">1 Hour</option>
</select> </select>
</div> </div>
<div class="setting-item">
<h3><i class="fas fa-cogs"></i> Manage Data</h3>
<div class="data-buttons">
<button id="import-button" class="control-button"><i class="fas fa-upload"></i> Import Stations</button>
<button id="export-button" class="control-button"><i class="fas fa-download"></i> Export Stations</button>
</div>
<input type="file" id="import-file-input" style="display: none;" accept=".json">
</div>
</div> </div>
</div> </div>
<div id="add-station-modal" class="modal">
<div class="modal-content">
<span class="close-button">&times;</span>
<h2>Add New Station</h2>
<form id="add-station-form">
<input type="text" id="new-station-name" placeholder="Station Name" required>
<input type="url" id="new-station-url" placeholder="Stream URL" required>
<input type="url" id="new-station-logo" placeholder="Logo URL (Optional)">
<input type="text" id="new-station-genre" placeholder="Genre (Optional)">
<button type="submit" class="control-button">Save Station</button>
</form>
</div>
</div>
<div id="equalizer-modal" class="modal"> <div id="equalizer-modal" class="modal">
<div class="modal-content"> <div class="modal-content">