3e4cfcf683
- Create detailed event tracking system (views, watch time, retention, traffic) - Build channel analytics with daily aggregation - Implement real-time stats caching - Add traffic source detection and tracking - Create audience demographics and retention tracking - Build creator dashboard with Chart.js visualizations - Implement automatic video analytics tracker JavaScript - Add cron script for daily stats aggregation - Document complete analytics system Features: - Channel overview (views, watch time, subscribers, engagement) - Time series charts with multiple metrics - Traffic sources breakdown (search, social, direct, etc.) - Audience demographics (countries, devices, age ranges) - Top videos by performance metric - Audience retention graphs (minute-by-minute) - Real-time viewer statistics - Automatic tracking (no manual instrumentation) - Privacy-compliant (hashed IPs, GDPR-ready) - Production-ready with proper indexing
152 lines
4.9 KiB
PHP
152 lines
4.9 KiB
PHP
<?php
|
|
/**
|
|
* Analytics API
|
|
*
|
|
* GET /api/analytics.php?action=overview&days=30
|
|
* GET /api/analytics.php?action=timeseries&metric=views&days=30
|
|
* GET /api/analytics.php?action=top_videos&metric=views&limit=10
|
|
* GET /api/analytics.php?action=demographics&days=30
|
|
* GET /api/analytics.php?action=traffic_sources&days=30
|
|
* GET /api/analytics.php?action=retention&video_id=X&days=30
|
|
* POST /api/analytics.php?action=track - Track event
|
|
*/
|
|
|
|
require_once dirname(__DIR__) . '/f_core/config.boot.php';
|
|
require_once dirname(__DIR__) . '/f_core/f_classes/class.analytics.php';
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
session_start();
|
|
$current_user_id = $_SESSION['user_id'] ?? null;
|
|
$analytics = new VAnalytics();
|
|
|
|
$action = $_GET['action'] ?? 'overview';
|
|
$days = intval($_GET['days'] ?? 30);
|
|
|
|
// Track event (public endpoint)
|
|
if ($action === 'track') {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
http_response_code(405);
|
|
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
|
exit;
|
|
}
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
|
|
|
|
$video_id = $input['video_id'] ?? null;
|
|
$event_type = $input['event_type'] ?? 'view';
|
|
|
|
if (!$video_id) {
|
|
http_response_code(400);
|
|
echo json_encode(['success' => false, 'error' => 'Video ID required']);
|
|
exit;
|
|
}
|
|
|
|
$data = [
|
|
'usr_id' => $current_user_id,
|
|
'watch_duration' => $input['watch_duration'] ?? 0,
|
|
'completion_percentage' => $input['completion_percentage'] ?? 0,
|
|
'country_code' => $input['country_code'] ?? null
|
|
];
|
|
|
|
$result = $analytics->trackEvent($video_id, $event_type, $data);
|
|
|
|
// Track retention if provided
|
|
if (isset($input['minute'])) {
|
|
$analytics->trackRetention($video_id, $input['minute']);
|
|
}
|
|
|
|
// Track traffic source if provided
|
|
if (isset($input['source_type'])) {
|
|
// Get video owner
|
|
global $class_database;
|
|
$sql = "SELECT usr_id FROM db_videofiles WHERE video_id = ?";
|
|
$res = $class_database->execute($sql, [$video_id]);
|
|
if ($res && $row = $class_database->fetch($res)) {
|
|
$analytics->trackTrafficSource(
|
|
$video_id,
|
|
$row['usr_id'],
|
|
$input['source_type'],
|
|
$input['source_detail'] ?? ''
|
|
);
|
|
}
|
|
}
|
|
|
|
echo json_encode(['success' => (bool)$result]);
|
|
exit;
|
|
}
|
|
|
|
// All other actions require authentication
|
|
if (!$current_user_id) {
|
|
http_response_code(401);
|
|
echo json_encode(['success' => false, 'error' => 'Authentication required']);
|
|
exit;
|
|
}
|
|
|
|
// Handle analytics queries
|
|
switch ($action) {
|
|
case 'overview':
|
|
$data = $analytics->getChannelOverview($current_user_id, $days);
|
|
echo json_encode(['success' => true, 'data' => $data]);
|
|
break;
|
|
|
|
case 'timeseries':
|
|
$metric = $_GET['metric'] ?? 'views';
|
|
$data = $analytics->getChannelTimeSeries($current_user_id, $days, $metric);
|
|
echo json_encode(['success' => true, 'data' => $data]);
|
|
break;
|
|
|
|
case 'top_videos':
|
|
$metric = $_GET['metric'] ?? 'views';
|
|
$limit = intval($_GET['limit'] ?? 10);
|
|
$data = $analytics->getTopVideos($current_user_id, $days, $metric, $limit);
|
|
echo json_encode(['success' => true, 'data' => $data]);
|
|
break;
|
|
|
|
case 'demographics':
|
|
$data = $analytics->getDemographics($current_user_id, $days);
|
|
echo json_encode(['success' => true, 'data' => $data]);
|
|
break;
|
|
|
|
case 'traffic_sources':
|
|
$data = $analytics->getTrafficSources($current_user_id, $days);
|
|
echo json_encode(['success' => true, 'data' => $data]);
|
|
break;
|
|
|
|
case 'retention':
|
|
$video_id = $_GET['video_id'] ?? null;
|
|
|
|
if (!$video_id) {
|
|
http_response_code(400);
|
|
echo json_encode(['success' => false, 'error' => 'Video ID required']);
|
|
exit;
|
|
}
|
|
|
|
// Verify ownership
|
|
global $class_database;
|
|
$sql = "SELECT usr_id FROM db_videofiles WHERE video_id = ?";
|
|
$result = $class_database->execute($sql, [$video_id]);
|
|
|
|
if (!$result || $class_database->rowCount($result) == 0) {
|
|
http_response_code(404);
|
|
echo json_encode(['success' => false, 'error' => 'Video not found']);
|
|
exit;
|
|
}
|
|
|
|
$row = $class_database->fetch($result);
|
|
|
|
if ($row['usr_id'] != $current_user_id) {
|
|
http_response_code(403);
|
|
echo json_encode(['success' => false, 'error' => 'Permission denied']);
|
|
exit;
|
|
}
|
|
|
|
$data = $analytics->getAudienceRetention($video_id, $days);
|
|
echo json_encode(['success' => true, 'data' => $data]);
|
|
break;
|
|
|
|
default:
|
|
http_response_code(400);
|
|
echo json_encode(['success' => false, 'error' => 'Invalid action']);
|
|
}
|