![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/private_html/ |
# 🚀 Radio Station Licensing - Quick Start Guide
## Overview
This guide will help you get started implementing the radio station licensing system for SoundStudioPro.
## Prerequisites
- PHP 8.0+
- MySQL 5.7+ or MariaDB 10.3+
- Existing SoundStudioPro installation
- Stripe account (for subscription billing)
## Step 1: Run Database Migration
Run the migration script to create all necessary tables:
```bash
php migrations/add_radio_station_system.php
```
Or access via browser:
```
https://soundstudiopro.com/migrations/add_radio_station_system.php
```
This will create:
- 9 new tables for radio station functionality
- Additional columns on existing `music_tracks` and `users` tables
## Step 2: Create Directory Structure
Create the following directories:
```
/radio/
/dashboard/ - Station dashboard pages
/api/ - API endpoints
/playlists/ - Playlist management
/analytics/ - Analytics pages
/admin/ - Admin tools for managing stations
```
## Step 3: Create Basic Station Registration
Create `/radio/register.php` for station signup:
```php
<?php
require_once '../config/database.php';
require_once '../includes/header.php';
// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = getDBConnection();
// Generate API credentials
$api_key = bin2hex(random_bytes(32));
$api_secret = bin2hex(random_bytes(32));
// Insert station
$stmt = $pdo->prepare("
INSERT INTO radio_stations (
station_name, call_sign, station_type, license_tier,
contact_name, contact_email, contact_phone,
city, state, country, timezone,
subscription_status, monthly_play_limit,
api_key, api_secret, api_enabled
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$_POST['station_name'],
$_POST['call_sign'] ?? null,
$_POST['station_type'] ?? 'local',
$_POST['license_tier'] ?? 'local',
$_POST['contact_name'],
$_POST['contact_email'],
$_POST['contact_phone'] ?? null,
$_POST['city'] ?? null,
$_POST['state'] ?? null,
$_POST['country'] ?? 'US',
$_POST['timezone'] ?? 'America/New_York',
'trial',
$_POST['license_tier'] === 'local' ? 500 : ($_POST['license_tier'] === 'regional' ? 2000 : 999999),
$api_key,
password_hash($api_secret, PASSWORD_DEFAULT),
true
]);
$station_id = $pdo->lastInsertId();
// Redirect to Stripe checkout
header("Location: /radio/subscribe.php?station_id=" . $station_id);
exit;
}
?>
<!-- Registration form HTML -->
```
## Step 4: Create API Endpoint Structure
Create `/radio/api/v1/index.php` as the main API router:
```php
<?php
require_once '../../../config/database.php';
header('Content-Type: application/json');
// Authenticate API request
function authenticateAPI() {
$headers = getallheaders();
$auth = $headers['Authorization'] ?? '';
if (!preg_match('/Bearer (.+):(.+)/', $auth, $matches)) {
http_response_code(401);
echo json_encode(['error' => 'Invalid authentication']);
exit;
}
$api_key = $matches[1];
$api_secret = $matches[2];
$pdo = getDBConnection();
$stmt = $pdo->prepare("
SELECT * FROM radio_stations
WHERE api_key = ? AND api_enabled = 1 AND subscription_status = 'active'
");
$stmt->execute([$api_key]);
$station = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$station || !password_verify($api_secret, $station['api_secret'])) {
http_response_code(401);
echo json_encode(['error' => 'Invalid credentials']);
exit;
}
// Check play limit
if ($station['current_month_plays'] >= $station['monthly_play_limit']) {
http_response_code(403);
echo json_encode(['error' => 'Monthly play limit reached']);
exit;
}
return $station;
}
// Route requests
$path = $_SERVER['PATH_INFO'] ?? '/';
$method = $_SERVER['REQUEST_METHOD'];
switch ($path) {
case '/catalog/tracks':
if ($method === 'GET') {
require 'endpoints/catalog_tracks.php';
}
break;
case '/plays':
if ($method === 'POST') {
require 'endpoints/log_play.php';
} elseif ($method === 'GET') {
require 'endpoints/get_plays.php';
}
break;
case '/playlists':
if ($method === 'GET') {
require 'endpoints/get_playlists.php';
} elseif ($method === 'POST') {
require 'endpoints/create_playlist.php';
}
break;
default:
http_response_code(404);
echo json_encode(['error' => 'Endpoint not found']);
}
```
## Step 5: Create Key API Endpoints
### `/radio/api/v1/endpoints/log_play.php`
```php
<?php
$station = authenticateAPI();
$pdo = getDBConnection();
$data = json_decode(file_get_contents('php://input'), true);
// Validate required fields
if (!isset($data['track_id']) || !isset($data['played_at'])) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields']);
exit;
}
// Check if track is licensed
$stmt = $pdo->prepare("
SELECT id FROM radio_licenses
WHERE station_id = ? AND track_id = ? AND status = 'active'
AND (end_date IS NULL OR end_date >= CURDATE())
");
$stmt->execute([$station['id'], $data['track_id']]);
$license = $stmt->fetch();
if (!$license) {
// Auto-create subscription license
$stmt = $pdo->prepare("
INSERT INTO radio_licenses (station_id, track_id, license_type, start_date, status)
VALUES (?, ?, 'subscription', CURDATE(), 'active')
");
$stmt->execute([$station['id'], $data['track_id']]);
}
// Log the play
$stmt = $pdo->prepare("
INSERT INTO radio_play_logs (
station_id, track_id, playlist_id,
played_at, duration_played, play_type,
time_of_day, day_of_week, source
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$played_at = new DateTime($data['played_at']);
$stmt->execute([
$station['id'],
$data['track_id'],
$data['playlist_id'] ?? null,
$data['played_at'],
$data['duration_played'] ?? null,
$data['play_type'] ?? 'full',
$played_at->format('H:i:s'),
$played_at->format('w'),
$data['source'] ?? 'api'
]);
$play_id = $pdo->lastInsertId();
// Update station play count
$pdo->prepare("
UPDATE radio_stations
SET current_month_plays = current_month_plays + 1
WHERE id = ?
")->execute([$station['id']]);
// Update track play count
$pdo->prepare("
UPDATE music_tracks
SET radio_play_count = radio_play_count + 1,
radio_last_played = NOW()
WHERE id = ?
")->execute([$data['track_id']]);
// Calculate royalty (async in production)
calculateRoyalty($pdo, $play_id, $station['id'], $data['track_id']);
// Return success
echo json_encode([
'play_id' => $play_id,
'logged' => true,
'monthly_plays_remaining' => $station['monthly_play_limit'] - $station['current_month_plays'] - 1
]);
```
## Step 6: Create Station Dashboard
Create `/radio/dashboard/index.php`:
```php
<?php
session_start();
require_once '../../config/database.php';
require_once '../../includes/header.php';
// Get station from session or require login
$station_id = $_SESSION['radio_station_id'] ?? null;
if (!$station_id) {
header('Location: /radio/login.php');
exit;
}
$pdo = getDBConnection();
// Get station info
$stmt = $pdo->prepare("SELECT * FROM radio_stations WHERE id = ?");
$stmt->execute([$station_id]);
$station = $stmt->fetch(PDO::FETCH_ASSOC);
// Get stats
$stmt = $pdo->prepare("
SELECT
COUNT(*) as total_plays,
COUNT(DISTINCT track_id) as unique_tracks,
COUNT(DISTINCT DATE(played_at)) as active_days
FROM radio_play_logs
WHERE station_id = ?
AND played_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
");
$stmt->execute([$station_id]);
$stats = $stmt->fetch(PDO::FETCH_ASSOC);
// Get top tracks
$stmt = $pdo->prepare("
SELECT
t.id,
t.title,
t.artist_name,
COUNT(*) as play_count,
MAX(pl.played_at) as last_played
FROM radio_play_logs pl
JOIN music_tracks t ON pl.track_id = t.id
WHERE pl.station_id = ?
AND pl.played_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY t.id
ORDER BY play_count DESC
LIMIT 10
");
$stmt->execute([$station_id]);
$top_tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!-- Dashboard HTML -->
<div class="radio-dashboard">
<h1><?= htmlspecialchars($station['station_name']) ?> Dashboard</h1>
<div class="stats-grid">
<div class="stat-card">
<h3>Monthly Plays</h3>
<p class="stat-number"><?= $station['current_month_plays'] ?> / <?= $station['monthly_play_limit'] ?></p>
</div>
<div class="stat-card">
<h3>Total Plays (30 days)</h3>
<p class="stat-number"><?= $stats['total_plays'] ?></p>
</div>
<div class="stat-card">
<h3>Unique Tracks</h3>
<p class="stat-number"><?= $stats['unique_tracks'] ?></p>
</div>
</div>
<div class="top-tracks">
<h2>Top Tracks This Month</h2>
<table>
<thead>
<tr>
<th>Track</th>
<th>Artist</th>
<th>Plays</th>
<th>Last Played</th>
</tr>
</thead>
<tbody>
<?php foreach ($top_tracks as $track): ?>
<tr>
<td><?= htmlspecialchars($track['title']) ?></td>
<td><?= htmlspecialchars($track['artist_name']) ?></td>
<td><?= $track['play_count'] ?></td>
<td><?= date('M j, Y g:i A', strtotime($track['last_played'])) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
```
## Step 7: Create Playlist Builder
Create `/radio/playlists/create.php`:
```php
<?php
// Playlist creation interface with drag-and-drop
// Search tracks, add to playlist, set rotation rules
?>
```
## Step 8: Set Up Stripe Integration
Create `/radio/subscribe.php` for Stripe checkout:
```php
<?php
require_once '../config/stripe.php'; // Your Stripe config
$station_id = $_GET['station_id'] ?? null;
if (!$station_id) {
die('Invalid station');
}
$pdo = getDBConnection();
$stmt = $pdo->prepare("SELECT * FROM radio_stations WHERE id = ?");
$stmt->execute([$station_id]);
$station = $stmt->fetch(PDO::FETCH_ASSOC);
// Create Stripe checkout session
$checkout_session = \Stripe\Checkout\Session::create([
'customer_email' => $station['contact_email'],
'payment_method_types' => ['card'],
'line_items' => [[
'price_data' => [
'currency' => 'usd',
'product_data' => [
'name' => 'Radio Station License - ' . ucfirst($station['license_tier']),
],
'unit_amount' => getTierPrice($station['license_tier']) * 100,
'recurring' => ['interval' => 'month'],
],
'quantity' => 1,
]],
'mode' => 'subscription',
'success_url' => 'https://soundstudiopro.com/radio/subscription_success.php?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'https://soundstudiopro.com/radio/register.php',
'metadata' => [
'station_id' => $station_id
]
]);
header("Location: " . $checkout_session->url);
```
## Step 9: Create Royalty Calculation Function
Add to `/radio/includes/functions.php`:
```php
<?php
function calculateRoyalty($pdo, $play_log_id, $station_id, $track_id) {
// Get track and artist info
$stmt = $pdo->prepare("
SELECT t.radio_royalty_rate, t.user_id as artist_id
FROM music_tracks t
WHERE t.id = ?
");
$stmt->execute([$track_id]);
$track = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$track) return;
// Get station subscription info for royalty pool calculation
$stmt = $pdo->prepare("
SELECT license_tier, monthly_play_limit
FROM radio_stations
WHERE id = ?
");
$stmt->execute([$station_id]);
$station = $stmt->fetch(PDO::FETCH_ASSOC);
// Calculate royalty (simplified - in production, use actual subscription revenue)
$base_rate = $track['radio_royalty_rate'];
$royalty_amount = $base_rate; // Per play
// Insert royalty record
$stmt = $pdo->prepare("
INSERT INTO radio_royalties (
play_log_id, station_id, track_id, artist_id,
base_rate, total_amount, artist_payout, payment_status
) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending')
");
$stmt->execute([
$play_log_id,
$station_id,
$track_id,
$track['artist_id'],
$base_rate,
$royalty_amount,
$royalty_amount * 0.7 // 70% to artist
]);
// Update track royalty calculated flag
$pdo->prepare("
UPDATE radio_play_logs
SET royalty_calculated = TRUE, royalty_amount = ?
WHERE id = ?
")->execute([$royalty_amount, $play_log_id]);
// Update artist totals
$pdo->prepare("
UPDATE users
SET radio_total_plays = radio_total_plays + 1,
radio_total_royalties = radio_total_royalties + ?
WHERE id = ?
")->execute([$royalty_amount * 0.7, $track['artist_id']]);
}
```
## Step 10: Testing
### Test API Endpoint
```bash
curl -X POST https://soundstudiopro.com/radio/api/v1/plays \
-H "Authorization: Bearer YOUR_API_KEY:YOUR_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"track_id": 123,
"played_at": "2025-01-15T10:30:00Z",
"duration_played": 180,
"play_type": "full"
}'
```
### Test Playlist Creation
1. Log into station dashboard
2. Navigate to Playlists
3. Create new playlist
4. Search and add tracks
5. Verify playlist appears in list
## Next Steps
1. **Build Catalog Browser** - Full search and filter interface
2. **Create Scheduler** - Calendar-based scheduling system
3. **Build Analytics Dashboard** - Charts and reports
4. **Implement PRO Reporting** - Automated compliance reports
5. **Create Artist Dashboard** - Radio performance view for artists
6. **Add Webhooks** - Real-time notifications
7. **Mobile App** - React Native or PWA for station managers
## Resources
- Full Technical Design: `RADIO_STATION_LICENSING_VISION.md`
- Database Schema: See migration script
- API Documentation: Create OpenAPI spec from endpoints
## Support
For questions or issues, refer to the main vision document or create an issue in your project tracker.