Auto commit: 2025-10-24T19:48:28.874Z
This commit is contained in:
parent
1287e70095
commit
c184472fb4
125
admin/dashboard.php
Normal file
125
admin/dashboard.php
Normal 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
93
admin/index.php
Normal 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
12
api/get_stations.php
Normal 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()]);
|
||||
}
|
||||
@ -20,9 +20,9 @@ class RadioWave {
|
||||
this.ui = {}; // To cache UI elements
|
||||
}
|
||||
|
||||
init() {
|
||||
async init() {
|
||||
this.initUI();
|
||||
this.loadStations();
|
||||
await this.loadStations();
|
||||
this.bindEvents();
|
||||
this.applyAccentColor(localStorage.getItem('accentColor') || '#FF8C00');
|
||||
if (localStorage.getItem('theme') === 'light') {
|
||||
@ -102,17 +102,25 @@ class RadioWave {
|
||||
}
|
||||
|
||||
// Station Management
|
||||
loadStations() {
|
||||
const savedStations = localStorage.getItem('stations');
|
||||
this.stations = savedStations ? JSON.parse(savedStations) : [
|
||||
{ name: "Lofi Girl", url: "https://play.streamafrica.net/lofiradio", logo: "https://i.ytimg.com/vi/jfKfPfyJRdk/maxresdefault.jpg", genre: "Lofi" },
|
||||
{ name: "Classic Rock", url: "http://198.178.123.23:8722/stream", logo: "https://cdn-radiotime-logos.tunein.com/s292341q.png", genre: "Rock" },
|
||||
];
|
||||
this.renderStationList();
|
||||
async loadStations() {
|
||||
try {
|
||||
const response = await fetch('/api/get_stations.php');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
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() {
|
||||
@ -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) {
|
||||
const li = e.target.closest('li');
|
||||
if (li) {
|
||||
@ -298,7 +291,7 @@ class RadioWave {
|
||||
if (e.target.type === 'range') {
|
||||
const index = e.target.dataset.index;
|
||||
const value = e.target.value;
|
||||
if (this.eqBands[index]) {
|
||||
if (.eqBands[index]) {
|
||||
this.eqBands[index].gain.value = value;
|
||||
}
|
||||
this.ui.eqPresetSelect.value = 'custom';
|
||||
@ -383,37 +376,14 @@ class RadioWave {
|
||||
}
|
||||
|
||||
// Data & Settings
|
||||
importStations(event) {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const importedStations = JSON.parse(e.target.result);
|
||||
if (Array.isArray(importedStations)) {
|
||||
this.stations = importedStations;
|
||||
this.saveStations();
|
||||
this.renderStationList();
|
||||
alert('Stations imported successfully!');
|
||||
} else {
|
||||
throw new Error('Invalid format');
|
||||
setSleepTimer(minutes) {
|
||||
clearTimeout(this.sleepTimer);
|
||||
if (minutes > 0) {
|
||||
this.sleepTimer = setTimeout(() => {
|
||||
this.togglePlayPause(false); // Pause the player
|
||||
this.ui.sleepTimerSelect.value = 0;
|
||||
}, minutes * 60 * 1000);
|
||||
}
|
||||
} 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) {
|
||||
|
||||
24
index.php
24
index.php
@ -70,7 +70,6 @@
|
||||
<div id="my-stations-tab" class="tab-content active">
|
||||
<div class="station-list-header">
|
||||
<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>
|
||||
<ul id="station-list">
|
||||
<!-- Stations will be dynamically added here -->
|
||||
@ -114,30 +113,11 @@
|
||||
<option value="60">1 Hour</option>
|
||||
</select>
|
||||
</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 id="add-station-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close-button">×</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 class="modal-content">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user