Major additions: - Web-based setup wizard (setup.php, setup_wizard.php, setup-wizard.js) - Production Docker configuration (docker-compose.prod.yml, .env.production) - Database initialization SQL files (deploy/init_settings.sql) - Template builder system with drag-and-drop UI - Advanced features (OAuth, CDN, enhanced analytics, monetization) - Comprehensive documentation (deployment guides, quick start, feature docs) - Design system with accessibility and responsive layout - Deployment automation scripts (deploy.ps1, generate-secrets.ps1) Setup wizard allows customization of: - Platform name and branding - Domain configuration - Membership tiers and pricing - Admin credentials - Feature toggles Database includes 270+ tables for complete video streaming platform with advanced features for analytics, moderation, template building, and monetization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
241 lines
8.0 KiB
PHP
241 lines
8.0 KiB
PHP
<?php
|
|
/**
|
|
* EasyStream Enhanced Analytics System
|
|
* Event tracking, retention graphs, heatmaps, demographics
|
|
* Version: 1.0
|
|
*/
|
|
|
|
defined('_ISVALID') or header('Location: /error');
|
|
|
|
class VAnalyticsEnhanced {
|
|
private static $db;
|
|
|
|
public static function init() {
|
|
self::$db = VDatabase::getInstance();
|
|
}
|
|
|
|
/**
|
|
* Track event
|
|
* @param string $event_type Event type (view, play, pause, seek, etc.)
|
|
* @param string $file_key File key
|
|
* @param array $data Additional event data
|
|
*/
|
|
public static function trackEvent($event_type, $file_key, $data = []) {
|
|
self::init();
|
|
|
|
$usr_id = isset($_SESSION['USER_ID']) ? (int)$_SESSION['USER_ID'] : 'NULL';
|
|
$session_id = session_id();
|
|
$event_type_safe = VDatabase::escape($event_type);
|
|
$file_key_safe = VDatabase::escape($file_key);
|
|
$session_safe = VDatabase::escape($session_id);
|
|
$data_json = VDatabase::escape(json_encode($data));
|
|
$timestamp = isset($data['timestamp']) ? (int)$data['timestamp'] : 'NULL';
|
|
$ip = VDatabase::escape($_SERVER['REMOTE_ADDR'] ?? '');
|
|
$ua = VDatabase::escape($_SERVER['HTTP_USER_AGENT'] ?? '');
|
|
$referrer = VDatabase::escape($_SERVER['HTTP_REFERER'] ?? '');
|
|
|
|
// Get file type
|
|
$typeResult = self::$db->execute("SELECT file_type FROM db_videofiles WHERE file_key = '$file_key_safe'");
|
|
$file_type = 'NULL';
|
|
if ($typeResult && $typeResult->RecordCount() > 0) {
|
|
$row = $typeResult->FetchRow();
|
|
$file_type = "'" . VDatabase::escape($row['file_type']) . "'";
|
|
}
|
|
|
|
$sql = "INSERT INTO db_analytics_events
|
|
(usr_id, session_id, event_type, file_key, file_type, event_data, timestamp_sec, ip_address, user_agent, referrer, created_at)
|
|
VALUES ($usr_id, '$session_safe', '$event_type_safe', '$file_key_safe', $file_type, '$data_json', $timestamp, '$ip', '$ua', '$referrer', NOW())";
|
|
|
|
self::$db->execute($sql);
|
|
|
|
// Update retention data for video events
|
|
if ($event_type == 'play' || $event_type == 'pause') {
|
|
self::updateRetention($file_key, $timestamp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update retention data
|
|
*/
|
|
private static function updateRetention($file_key, $timestamp_sec) {
|
|
if (!$timestamp_sec) return;
|
|
|
|
$file_key_safe = VDatabase::escape($file_key);
|
|
$timestamp = (int)$timestamp_sec;
|
|
|
|
$sql = "INSERT INTO db_analytics_retention
|
|
(file_key, timestamp_sec, viewers, updated_at)
|
|
VALUES ('$file_key_safe', $timestamp, 1, NOW())
|
|
ON DUPLICATE KEY UPDATE viewers = viewers + 1, updated_at = NOW()";
|
|
|
|
self::$db->execute($sql);
|
|
}
|
|
|
|
/**
|
|
* Get retention graph data
|
|
* @param string $file_key File key
|
|
* @return array Retention data by second
|
|
*/
|
|
public static function getRetentionGraph($file_key) {
|
|
self::init();
|
|
|
|
$file_key_safe = VDatabase::escape($file_key);
|
|
|
|
$sql = "SELECT timestamp_sec, viewers
|
|
FROM db_analytics_retention
|
|
WHERE file_key = '$file_key_safe'
|
|
ORDER BY timestamp_sec ASC";
|
|
|
|
$result = self::$db->execute($sql);
|
|
$data = [];
|
|
|
|
if ($result) {
|
|
while ($row = $result->FetchRow()) {
|
|
$data[] = [
|
|
'time' => (int)$row['timestamp_sec'],
|
|
'viewers' => (int)$row['viewers']
|
|
];
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Get traffic sources
|
|
* @param string $file_key File key
|
|
* @param string $date_from Start date
|
|
* @param string $date_to End date
|
|
* @return array Traffic sources
|
|
*/
|
|
public static function getTrafficSources($file_key, $date_from, $date_to) {
|
|
self::init();
|
|
|
|
$file_key_safe = VDatabase::escape($file_key);
|
|
$from_safe = VDatabase::escape($date_from);
|
|
$to_safe = VDatabase::escape($date_to);
|
|
|
|
// Analyze referrers from events
|
|
$sql = "SELECT
|
|
CASE
|
|
WHEN referrer = '' THEN 'direct'
|
|
WHEN referrer LIKE '%google%' OR referrer LIKE '%bing%' THEN 'search'
|
|
WHEN referrer LIKE '%facebook%' OR referrer LIKE '%twitter%' OR referrer LIKE '%instagram%' THEN 'social'
|
|
WHEN referrer LIKE '%{$_SERVER['HTTP_HOST']}%' THEN 'internal'
|
|
ELSE 'external'
|
|
END as source_type,
|
|
COUNT(*) as visits
|
|
FROM db_analytics_events
|
|
WHERE file_key = '$file_key_safe'
|
|
AND event_type = 'view'
|
|
AND DATE(created_at) BETWEEN '$from_safe' AND '$to_safe'
|
|
GROUP BY source_type";
|
|
|
|
$result = self::$db->execute($sql);
|
|
$sources = [];
|
|
|
|
if ($result) {
|
|
while ($row = $result->FetchRow()) {
|
|
$sources[] = [
|
|
'source' => $row['source_type'],
|
|
'visits' => (int)$row['visits']
|
|
];
|
|
}
|
|
}
|
|
|
|
return $sources;
|
|
}
|
|
|
|
/**
|
|
* Get analytics summary
|
|
* @param string $file_key File key
|
|
* @param string $date_from Start date
|
|
* @param string $date_to End date
|
|
* @return array Summary data
|
|
*/
|
|
public static function getSummary($file_key, $date_from, $date_to) {
|
|
self::init();
|
|
|
|
$file_key_safe = VDatabase::escape($file_key);
|
|
$from_safe = VDatabase::escape($date_from);
|
|
$to_safe = VDatabase::escape($date_to);
|
|
|
|
$sql = "SELECT
|
|
COUNT(DISTINCT session_id) as unique_viewers,
|
|
COUNT(*) as total_views,
|
|
SUM(CASE WHEN event_type = 'like' THEN 1 ELSE 0 END) as likes,
|
|
SUM(CASE WHEN event_type = 'comment' THEN 1 ELSE 0 END) as comments,
|
|
SUM(CASE WHEN event_type = 'share' THEN 1 ELSE 0 END) as shares
|
|
FROM db_analytics_events
|
|
WHERE file_key = '$file_key_safe'
|
|
AND DATE(created_at) BETWEEN '$from_safe' AND '$to_safe'";
|
|
|
|
$result = self::$db->execute($sql);
|
|
|
|
if ($result && $result->RecordCount() > 0) {
|
|
return $result->FetchRow();
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Track heatmap click
|
|
* @param string $file_key File key
|
|
* @param float $x X coordinate (0-1)
|
|
* @param float $y Y coordinate (0-1)
|
|
* @param string $type Type (click or hover)
|
|
*/
|
|
public static function trackHeatmap($file_key, $x, $y, $type = 'click') {
|
|
self::init();
|
|
|
|
$file_key_safe = VDatabase::escape($file_key);
|
|
$x = (float)$x;
|
|
$y = (float)$y;
|
|
$date = date('Y-m-d');
|
|
|
|
$field = $type == 'hover' ? 'hovers' : 'clicks';
|
|
|
|
$sql = "INSERT INTO db_analytics_heatmaps
|
|
(file_key, x_coord, y_coord, $field, date)
|
|
VALUES ('$file_key_safe', $x, $y, 1, '$date')
|
|
ON DUPLICATE KEY UPDATE $field = $field + 1";
|
|
|
|
self::$db->execute($sql);
|
|
}
|
|
|
|
/**
|
|
* Get heatmap data
|
|
* @param string $file_key File key
|
|
* @param string $date Date
|
|
* @return array Heatmap coordinates
|
|
*/
|
|
public static function getHeatmap($file_key, $date) {
|
|
self::init();
|
|
|
|
$file_key_safe = VDatabase::escape($file_key);
|
|
$date_safe = VDatabase::escape($date);
|
|
|
|
$sql = "SELECT x_coord, y_coord, clicks, hovers
|
|
FROM db_analytics_heatmaps
|
|
WHERE file_key = '$file_key_safe' AND date = '$date_safe'";
|
|
|
|
$result = self::$db->execute($sql);
|
|
$heatmap = [];
|
|
|
|
if ($result) {
|
|
while ($row = $result->FetchRow()) {
|
|
$heatmap[] = [
|
|
'x' => (float)$row['x_coord'],
|
|
'y' => (float)$row['y_coord'],
|
|
'clicks' => (int)$row['clicks'],
|
|
'hovers' => (int)$row['hovers'],
|
|
'intensity' => (int)$row['clicks'] + ((int)$row['hovers'] / 10)
|
|
];
|
|
}
|
|
}
|
|
|
|
return $heatmap;
|
|
}
|
|
}
|