false, 'data' => null, 'error' => null]; try { // Get action from query parameter $action = isset($_GET['action']) ? $_GET['action'] : null; $method = $_SERVER['REQUEST_METHOD']; // Get authenticated user (supports both session and JWT) $userId = null; // Try JWT authentication first $authHeader = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) ? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] : null); if ($authHeader && preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) { $token = $matches[1]; $tokenData = VAuth::verifyToken($token); if ($tokenData && isset($tokenData['user_id'])) { $userId = $tokenData['user_id']; } } // Fall back to session authentication if (!$userId && isset($_SESSION['USER_ID'])) { $userId = $_SESSION['USER_ID']; } elseif (!$userId && isset($_SESSION['usr_id'])) { $userId = $_SESSION['usr_id']; } if (!$userId) { throw new Exception('Authentication required', 401); } // Route based on method and action switch ($method) { case 'GET': switch ($action) { case 'list': case 'subscriptions': case null: handleGetSubscriptions($userId); break; case 'subscribers': handleGetSubscribers($userId); break; case 'feed': handleGetFeed($userId); break; case 'check': handleCheckSubscription($userId); break; default: throw new Exception('Invalid action', 400); } break; case 'POST': handleSubscribe($userId); break; case 'DELETE': handleUnsubscribe($userId); break; default: throw new Exception('Method not allowed', 405); } } catch (Exception $e) { http_response_code($e->getCode() >= 400 && $e->getCode() < 600 ? $e->getCode() : 500); $response['error'] = $e->getMessage(); echo json_encode($response); exit; } /** * Get user's subscriptions */ function handleGetSubscriptions($userId) { global $class_database, $response; $sql = "SELECT u.usr_id, u.usr_user, u.usr_dname, u.usr_avatar, u.usr_verified, u.usr_partner, s.sub_date, (SELECT COUNT(*) FROM db_videofiles WHERE usr_id = u.usr_id AND approved = 1 AND privacy = 'public') as video_count, (SELECT COUNT(*) FROM db_subscriptions WHERE channel_id = u.usr_id) as subscriber_count, (SELECT MAX(upload_date) FROM db_videofiles WHERE usr_id = u.usr_id AND approved = 1) as last_upload FROM db_subscriptions s LEFT JOIN db_users u ON s.channel_id = u.usr_id WHERE s.usr_id = ? ORDER BY s.sub_date DESC"; $result = $class_database->execute($sql, [$userId]); $subscriptions = $result ? $result->GetArray() : []; $response['success'] = true; $response['data'] = [ 'subscriptions' => $subscriptions, 'total' => count($subscriptions) ]; echo json_encode($response); } /** * Get subscribers for a channel */ function handleGetSubscribers($userId) { global $class_database, $response; $channelId = isset($_GET['channel_id']) ? (int)$_GET['channel_id'] : $userId; $page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; $limit = isset($_GET['limit']) ? min(100, max(1, (int)$_GET['limit'])) : 50; $offset = ($page - 1) * $limit; // Get total count $countSql = "SELECT COUNT(*) FROM db_subscriptions WHERE channel_id = ?"; $total = (int)$class_database->singleFieldValue($countSql, [$channelId]); // Get subscribers $sql = "SELECT u.usr_id, u.usr_user, u.usr_dname, u.usr_avatar, u.usr_verified, s.sub_date FROM db_subscriptions s LEFT JOIN db_users u ON s.usr_id = u.usr_id WHERE s.channel_id = ? ORDER BY s.sub_date DESC LIMIT ? OFFSET ?"; $result = $class_database->execute($sql, [$channelId, $limit, $offset]); $response['success'] = true; $response['data'] = [ 'subscribers' => $result ? $result->GetArray() : [], 'pagination' => [ 'page' => $page, 'limit' => $limit, 'total' => $total, 'pages' => ceil($total / $limit) ] ]; echo json_encode($response); } /** * Get activity feed from subscribed channels */ function handleGetFeed($userId) { global $class_database, $response; $page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; $limit = isset($_GET['limit']) ? min(100, max(1, (int)$_GET['limit'])) : 20; $offset = ($page - 1) * $limit; // Get videos from subscribed channels $sql = "SELECT v.file_key, v.file_title, v.file_description, v.file_duration, v.file_views, v.upload_date, v.thumbnail, v.featured, u.usr_id, u.usr_user, u.usr_dname, u.usr_avatar, u.usr_verified, (SELECT COUNT(*) FROM db_likes WHERE file_key = v.file_key AND like_type = 'like') as like_count, (SELECT COUNT(*) FROM db_comments WHERE file_key = v.file_key) as comment_count FROM db_videofiles v LEFT JOIN db_users u ON v.usr_id = u.usr_id INNER JOIN db_subscriptions s ON s.channel_id = v.usr_id WHERE s.usr_id = ? AND v.approved = 1 AND v.privacy = 'public' ORDER BY v.upload_date DESC LIMIT ? OFFSET ?"; $result = $class_database->execute($sql, [$userId, $limit, $offset]); // Get total count $countSql = "SELECT COUNT(*) FROM db_videofiles v INNER JOIN db_subscriptions s ON s.channel_id = v.usr_id WHERE s.usr_id = ? AND v.approved = 1 AND v.privacy = 'public'"; $total = (int)$class_database->singleFieldValue($countSql, [$userId]); $response['success'] = true; $response['data'] = [ 'videos' => $result ? $result->GetArray() : [], 'pagination' => [ 'page' => $page, 'limit' => $limit, 'total' => $total, 'pages' => ceil($total / $limit) ] ]; echo json_encode($response); } /** * Check if subscribed to a channel */ function handleCheckSubscription($userId) { global $class_database, $response; $channelId = isset($_GET['channel_id']) ? (int)$_GET['channel_id'] : null; if (!$channelId) { throw new Exception('channel_id is required', 400); } $sql = "SELECT sub_date FROM db_subscriptions WHERE usr_id = ? AND channel_id = ?"; $result = $class_database->execute($sql, [$userId, $channelId]); $isSubscribed = $result && $result->RecordCount() > 0; $subDate = $isSubscribed ? $result->fields['sub_date'] : null; $response['success'] = true; $response['data'] = [ 'is_subscribed' => $isSubscribed, 'subscribed_since' => $subDate ]; echo json_encode($response); } /** * Subscribe to a channel */ function handleSubscribe($userId) { global $class_database, $response; // Get JSON input or form data $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $input = $_POST; } $channelId = isset($input['channel_id']) ? (int)$input['channel_id'] : null; if (!$channelId) { throw new Exception('channel_id is required', 400); } if ($channelId == $userId) { throw new Exception('Cannot subscribe to yourself', 400); } // Check if channel exists $channelCheckSql = "SELECT usr_user FROM db_users WHERE usr_id = ?"; $channelExists = $class_database->execute($channelCheckSql, [$channelId]); if (!$channelExists || $channelExists->RecordCount() === 0) { throw new Exception('Channel not found', 404); } // Check if already subscribed $checkSql = "SELECT 1 FROM db_subscriptions WHERE usr_id = ? AND channel_id = ?"; $existing = $class_database->execute($checkSql, [$userId, $channelId]); if ($existing && $existing->RecordCount() > 0) { throw new Exception('Already subscribed to this channel', 400); } // Add subscription $sql = "INSERT INTO db_subscriptions (usr_id, channel_id, sub_date) VALUES (?, ?, NOW())"; $result = $class_database->execute($sql, [$userId, $channelId]); if (!$result) { throw new Exception('Failed to subscribe', 500); } // Create notification for channel owner $notifSql = "INSERT INTO db_notifications (usr_id, notif_type, notif_from, notif_date) VALUES (?, 'subscription', ?, NOW())"; $class_database->execute($notifSql, [$channelId, $userId]); // Get updated subscriber count $countSql = "SELECT COUNT(*) as count FROM db_subscriptions WHERE channel_id = ?"; $countResult = $class_database->execute($countSql, [$channelId]); $subscriberCount = $countResult->fields['count']; // Log the subscription VLogger::log('info', 'User subscribed to channel', [ 'user_id' => $userId, 'channel_id' => $channelId ]); $response['success'] = true; $response['data'] = [ 'message' => 'Subscribed successfully', 'channel_name' => $channelExists->fields['usr_user'], 'subscriber_count' => $subscriberCount ]; echo json_encode($response); } /** * Unsubscribe from a channel */ function handleUnsubscribe($userId) { global $class_database, $response; // Get channel ID from query string or JSON body $channelId = isset($_GET['channel_id']) ? (int)$_GET['channel_id'] : null; if (!$channelId) { $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $input = $_POST; } $channelId = isset($input['channel_id']) ? (int)$input['channel_id'] : null; } if (!$channelId) { throw new Exception('channel_id is required', 400); } // Check if subscribed $checkSql = "SELECT 1 FROM db_subscriptions WHERE usr_id = ? AND channel_id = ?"; $existing = $class_database->execute($checkSql, [$userId, $channelId]); if (!$existing || $existing->RecordCount() === 0) { throw new Exception('Not subscribed to this channel', 400); } // Remove subscription $sql = "DELETE FROM db_subscriptions WHERE usr_id = ? AND channel_id = ?"; $result = $class_database->execute($sql, [$userId, $channelId]); if (!$result) { throw new Exception('Failed to unsubscribe', 500); } // Get updated subscriber count $countSql = "SELECT COUNT(*) as count FROM db_subscriptions WHERE channel_id = ?"; $countResult = $class_database->execute($countSql, [$channelId]); $subscriberCount = $countResult->fields['count']; // Log the unsubscription VLogger::log('info', 'User unsubscribed from channel', [ 'user_id' => $userId, 'channel_id' => $channelId ]); $response['success'] = true; $response['data'] = [ 'message' => 'Unsubscribed successfully', 'subscriber_count' => $subscriberCount ]; echo json_encode($response); }