feat: Add comprehensive documentation suite and reorganize project structure

- Created complete documentation in docs/ directory
- Added PROJECT_OVERVIEW.md with feature highlights and getting started guide
- Added ARCHITECTURE.md with system design and technical details
- Added SECURITY.md with comprehensive security implementation guide
- Added DEVELOPMENT.md with development workflows and best practices
- Added DEPLOYMENT.md with production deployment instructions
- Added API.md with complete REST API documentation
- Added CONTRIBUTING.md with contribution guidelines
- Added CHANGELOG.md with version history and migration notes
- Reorganized all documentation files into docs/ directory for better organization
- Updated README.md with proper documentation links and quick navigation
- Enhanced project structure with professional documentation standards
This commit is contained in:
SamiAhmed7777
2025-10-21 00:39:45 -07:00
commit 0b7e2d0a5b
6080 changed files with 1332936 additions and 0 deletions

257
api/auth.php Normal file
View File

@@ -0,0 +1,257 @@
<?php
/*******************************************************************************************************************
| Software Name : EasyStream
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
| Software Author : (c) Sami Ahmed
|*******************************************************************************************************************
|
|*******************************************************************************************************************
| This source file is subject to the EasyStream Proprietary License Agreement.
|
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|*******************************************************************************************************************
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|*******************************************************************************************************************/
define('_ISVALID', true);
// Set JSON content type
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
// Handle preflight requests
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
require_once '../f_core/config.core.php';
// Initialize classes
$auth = VAuth::getInstance();
$security = VSecurity::getInstance();
$logger = VLogger::getInstance();
/**
* Send JSON response
*/
function sendResponse($data, $statusCode = 200) {
http_response_code($statusCode);
echo json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
exit;
}
/**
* Get JSON input
*/
function getJsonInput() {
$input = file_get_contents('php://input');
return json_decode($input, true) ?: [];
}
/**
* Validate required fields
*/
function validateRequired($data, $fields) {
$missing = [];
foreach ($fields as $field) {
if (!isset($data[$field]) || empty($data[$field])) {
$missing[] = $field;
}
}
return $missing;
}
try {
// Get request method and action
$method = $_SERVER['REQUEST_METHOD'];
$action = VSecurity::getParam('action', 'string') ?: VSecurity::postParam('action', 'string');
// Route requests based on action
switch ($action) {
case 'register':
if ($method !== 'POST') {
sendResponse(['success' => false, 'message' => 'Method not allowed'], 405);
}
// Validate CSRF token
if (!VSecurity::validateCSRFFromPost('register')) {
sendResponse(['success' => false, 'message' => 'Invalid CSRF token'], 403);
}
$data = array_merge($_POST, getJsonInput());
$missing = validateRequired($data, ['username', 'email', 'password']);
if (!empty($missing)) {
sendResponse([
'success' => false,
'message' => 'Missing required fields: ' . implode(', ', $missing)
], 400);
}
$result = $auth->register($data);
sendResponse($result, $result['success'] ? 201 : 400);
break;
case 'verify_email':
if ($method !== 'POST') {
sendResponse(['success' => false, 'message' => 'Method not allowed'], 405);
}
$data = array_merge($_POST, getJsonInput());
$token = $data['token'] ?? '';
if (empty($token)) {
sendResponse(['success' => false, 'message' => 'Verification token is required'], 400);
}
$result = $auth->verifyEmail($token);
sendResponse($result, $result['success'] ? 200 : 400);
break;
case 'login':
if ($method !== 'POST') {
sendResponse(['success' => false, 'message' => 'Method not allowed'], 405);
}
// Validate CSRF token
if (!VSecurity::validateCSRFFromPost('login')) {
sendResponse(['success' => false, 'message' => 'Invalid CSRF token'], 403);
}
$data = array_merge($_POST, getJsonInput());
$missing = validateRequired($data, ['identifier', 'password']);
if (!empty($missing)) {
sendResponse([
'success' => false,
'message' => 'Username/email and password are required'
], 400);
}
$rememberMe = !empty($data['remember_me']);
$result = $auth->login($data['identifier'], $data['password'], $rememberMe);
sendResponse($result, $result['success'] ? 200 : 401);
break;
case 'logout':
if ($method !== 'POST') {
sendResponse(['success' => false, 'message' => 'Method not allowed'], 405);
}
// Validate CSRF token
if (!VSecurity::validateCSRFFromPost('logout')) {
sendResponse(['success' => false, 'message' => 'Invalid CSRF token'], 403);
}
$result = $auth->logout();
sendResponse($result);
break;
case 'me':
if ($method !== 'GET') {
sendResponse(['success' => false, 'message' => 'Method not allowed'], 405);
}
if (!$auth->isAuthenticated()) {
sendResponse(['success' => false, 'message' => 'Not authenticated'], 401);
}
$user = $auth->getCurrentUser();
sendResponse(['success' => true, 'user' => $user]);
break;
case 'request_password_reset':
if ($method !== 'POST') {
sendResponse(['success' => false, 'message' => 'Method not allowed'], 405);
}
// Validate CSRF token
if (!VSecurity::validateCSRFFromPost('password_reset')) {
sendResponse(['success' => false, 'message' => 'Invalid CSRF token'], 403);
}
$data = array_merge($_POST, getJsonInput());
$email = $data['email'] ?? '';
if (empty($email)) {
sendResponse(['success' => false, 'message' => 'Email is required'], 400);
}
$result = $auth->requestPasswordReset($email);
sendResponse($result);
break;
case 'reset_password':
if ($method !== 'POST') {
sendResponse(['success' => false, 'message' => 'Method not allowed'], 405);
}
// Validate CSRF token
if (!VSecurity::validateCSRFFromPost('password_reset')) {
sendResponse(['success' => false, 'message' => 'Invalid CSRF token'], 403);
}
$data = array_merge($_POST, getJsonInput());
$missing = validateRequired($data, ['token', 'password']);
if (!empty($missing)) {
sendResponse([
'success' => false,
'message' => 'Reset token and new password are required'
], 400);
}
$result = $auth->resetPassword($data['token'], $data['password']);
sendResponse($result, $result['success'] ? 200 : 400);
break;
case 'csrf_token':
if ($method !== 'GET') {
sendResponse(['success' => false, 'message' => 'Method not allowed'], 405);
}
$action = VSecurity::getParam('for', 'string', 'default');
$token = VSecurity::generateCSRFToken($action);
sendResponse([
'success' => true,
'token' => $token,
'action' => $action
]);
break;
case 'status':
if ($method !== 'GET') {
sendResponse(['success' => false, 'message' => 'Method not allowed'], 405);
}
$isAuthenticated = $auth->isAuthenticated();
$user = $isAuthenticated ? $auth->getCurrentUser() : null;
sendResponse([
'success' => true,
'authenticated' => $isAuthenticated,
'user' => $user
]);
break;
default:
sendResponse(['success' => false, 'message' => 'Invalid action'], 400);
}
} catch (Exception $e) {
$logger->error('Auth API error', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'action' => $action ?? 'unknown',
'method' => $method ?? 'unknown'
]);
sendResponse([
'success' => false,
'message' => 'An internal error occurred'
], 500);
}

161
api/auto_post.php Normal file
View File

@@ -0,0 +1,161 @@
<?php
define('_ISVALID', true);
include_once '../f_core/config.core.php';
// Set up logging
$log_file = __DIR__ . '/auto_post.log';
function writeLog($message) {
global $log_file;
$timestamp = date('Y-m-d H:i:s');
$log_message = "[$timestamp] $message\n";
file_put_contents($log_file, $log_message, FILE_APPEND);
}
// Load API configuration
$api_config = require_once __DIR__ . '/config.php';
$telegram_bot_token = $api_config['telegram']['bot_token'];
$telegram_channel_id = $api_config['telegram']['channel_id'];
// Verify configuration
if (empty($telegram_bot_token) || $telegram_bot_token === '123456789:ABCdefGHIjklmNOPQRstuvwxyz') {
writeLog("ERROR: Invalid bot token. Please update config.php with your actual bot token.");
die("Invalid bot token. Please check the logs.");
}
if (empty($telegram_channel_id) || $telegram_channel_id === 'YOUR_CHANNEL_ID') {
writeLog("ERROR: Invalid channel ID. Please update config.php with your actual channel ID.");
die("Invalid channel ID. Please check the logs.");
}
// Function to send data to Telegram channel
function sendToChannel($message, $parse_mode = 'HTML') {
global $telegram_bot_token, $telegram_channel_id;
try {
$url = "https://api.telegram.org/bot{$telegram_bot_token}/sendMessage";
$data = [
'chat_id' => $telegram_channel_id,
'text' => $message,
'parse_mode' => $parse_mode
];
$options = [
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
'content' => http_build_query($data)
]
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === false) {
writeLog("ERROR: Failed to send message to channel");
return false;
}
$response = json_decode($result, true);
if (!$response['ok']) {
writeLog("ERROR: Telegram API error: " . ($response['description'] ?? 'Unknown error'));
return false;
}
writeLog("SUCCESS: Message sent to channel");
return $result;
} catch (Exception $e) {
writeLog("ERROR: Telegram API Exception: " . $e->getMessage());
return false;
}
}
// Function to format video/stream message
function formatContentMessage($content) {
$message = "🎥 <b>{$content['title']}</b>\n\n";
$message .= "📝 {$content['description']}\n\n";
$message .= "👤 Posted by: {$content['username']}\n";
$message .= "👁 Views: {$content['views']}\n";
$message .= "🔗 <a href='{$content['url']}'>Watch on EasyStream</a>\n";
// Add hashtags if available
if (!empty($content['tags'])) {
$message .= "\n🏷 Tags: " . implode(' ', array_map(function($tag) {
return "#" . str_replace(' ', '', $tag);
}, $content['tags']));
}
// Branding normalization - already using EasyStream above
return $message;
}
// Function to check and post new content
function checkAndPostNewContent() {
global $class_database, $cfg, $api_config;
writeLog("Starting content check...");
// Get latest videos (last 5 minutes)
try {
$videos = $class_database->getLatestVideos(
$api_config['content']['max_items'],
$api_config['content']['time_window']
);
writeLog("Found " . count($videos) . " new videos");
foreach ($videos as $video) {
$content = [
'title' => htmlspecialchars($video['title']),
'description' => htmlspecialchars($video['description']),
'username' => htmlspecialchars($video['username']),
'views' => $video['views'],
'url' => $cfg['main_url'] . '/video/' . $video['file_key'],
'tags' => explode(',', $video['tags'])
];
$message = formatContentMessage($content);
sendToChannel($message);
// Add a small delay between posts to avoid rate limiting
sleep(1);
}
} catch (Exception $e) {
writeLog("ERROR: Failed to process videos: " . $e->getMessage());
}
// Get latest streams (last 5 minutes)
try {
$streams = $class_database->getLatestStreams(
$api_config['content']['max_items'],
$api_config['content']['time_window']
);
writeLog("Found " . count($streams) . " new streams");
foreach ($streams as $stream) {
$content = [
'title' => htmlspecialchars($stream['title']),
'description' => htmlspecialchars($stream['description']),
'username' => htmlspecialchars($stream['username']),
'views' => $stream['views'],
'url' => $cfg['main_url'] . '/stream/' . $stream['stream_key'],
'tags' => explode(',', $stream['tags'])
];
$message = formatContentMessage($content);
$message = "🔴 LIVE NOW: " . $message; // Add LIVE indicator for streams
sendToChannel($message);
// Add a small delay between posts to avoid rate limiting
sleep(1);
}
} catch (Exception $e) {
writeLog("ERROR: Failed to process streams: " . $e->getMessage());
}
writeLog("Content check completed");
}
// Run the check
writeLog("Script started");
checkAndPostNewContent();
writeLog("Script finished");

22
api/config.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
// API Configuration
return [
'telegram' => [
'bot_token' => '123456789:ABCdefGHIjklmNOPQRstuvwxyz', // Replace with your actual bot token from BotFather
'channel_id' => 'YOUR_CHANNEL_ID', // Replace with your channel ID (e.g., -100xxxxxxxxxx)
'allowed_ips' => [
// Add Telegram's IP ranges here for security
'149.154.160.0/20',
'91.108.4.0/22'
]
],
'security' => [
'rate_limit' => 30, // requests per minute
'max_requests' => 1000 // per hour
],
'content' => [
'check_interval' => 5, // minutes between checks
'max_items' => 10, // maximum items to post per check
'time_window' => 5 // minutes to look back for new content
]
];

55
api/privacy.php Normal file
View File

@@ -0,0 +1,55 @@
<?php
define('_ISVALID', true);
include_once __DIR__ . '/../f_core/config.core.php';
header('Content-Type: application/json');
// Require login
if (!VSession::isLoggedIn()) {
http_response_code(401);
echo json_encode(['status' => 'error', 'message' => 'Authentication required']);
exit;
}
$action = VSecurity::getParam('action', 'alpha', 'export');
$uid = (int) $_SESSION['USER_ID'];
// Basic rate limit
if (!VSecurity::checkRateLimit('privacy_' . $uid, 5, 60)) {
http_response_code(429);
echo json_encode(['status' => 'error', 'message' => 'Too many requests']);
exit;
}
switch ($action) {
case 'export':
// TODO: Collect actual data
$bundle = [
'user' => [
'id' => $uid,
'username' => $_SESSION['USER_NAME'] ?? null,
'display_name' => $_SESSION['USER_DNAME'] ?? null,
],
'files' => [],
'subscriptions' => [],
];
echo json_encode(['status' => 'ok', 'data' => $bundle]);
break;
case 'delete':
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !VSecurity::validateCSRFFromPost('privacy_delete')) {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Invalid CSRF or method']);
exit;
}
// TODO: Implement soft-delete/anonymization workflow
VLogger::getInstance()->warning('User requested account deletion', ['user_id' => $uid]);
http_response_code(202);
echo json_encode(['status' => 'accepted', 'message' => 'Deletion request received']);
break;
default:
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Unknown action']);
}

380
api/social.php Normal file
View File

@@ -0,0 +1,380 @@
<?php
/*******************************************************************************************************************
| Software Name : EasyStream
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
| Software Author : (c) Sami Ahmed
|*******************************************************************************************************************
|
|*******************************************************************************************************************
| This source file is subject to the EasyStream Proprietary License Agreement.
|
| By using this software, you acknowledge have read this Agreement and agree to be bound thereby.
|*******************************************************************************************************************
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|*******************************************************************************************************************/
define('_ISVALID', true);
// Include core configuration
include_once '../f_core/config.core.php';
// Set JSON response headers
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
// Handle preflight requests
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
try {
// Initialize social system
$social = VSocial::getInstance();
// Get request method and path
$method = $_SERVER['REQUEST_METHOD'];
$path = $_SERVER['PATH_INFO'] ?? $_SERVER['REQUEST_URI'] ?? '';
$path = parse_url($path, PHP_URL_PATH);
$pathParts = array_filter(explode('/', $path));
// Get current user
$userId = VSession::isLoggedIn() ? $_SESSION['user_id'] : null;
// Route the request
switch ($method) {
case 'POST':
handlePostRequest($social, $pathParts, $userId);
break;
case 'GET':
handleGetRequest($social, $pathParts, $userId);
break;
case 'DELETE':
handleDeleteRequest($social, $pathParts, $userId);
break;
default:
sendError('Method not allowed', 405);
}
} catch (Exception $e) {
VLogger::getInstance()->error('Social API error', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
sendError('Internal server error', 500);
}
/**
* Handle POST requests
*/
function handlePostRequest($social, $pathParts, $userId)
{
$action = end($pathParts);
$data = getJsonInput();
switch ($action) {
case 'vote':
handleVote($social, $data, $userId);
break;
case 'comment':
handleAddComment($social, $data, $userId);
break;
case 'share':
handleShare($social, $data, $userId);
break;
default:
sendError('Invalid action', 400);
}
}
/**
* Handle GET requests
*/
function handleGetRequest($social, $pathParts, $userId)
{
$action = end($pathParts);
switch ($action) {
case 'comments':
handleGetComments($social, $_GET, $userId);
break;
case 'replies':
handleGetReplies($social, $_GET, $userId);
break;
case 'sharing-urls':
handleGetSharingUrls($social, $_GET);
break;
case 'stats':
handleGetStats($social, $_GET);
break;
default:
sendError('Invalid action', 400);
}
}
/**
* Handle DELETE requests
*/
function handleDeleteRequest($social, $pathParts, $userId)
{
$action = $pathParts[count($pathParts) - 2] ?? '';
$id = end($pathParts);
switch ($action) {
case 'comment':
handleDeleteComment($social, $id, $userId);
break;
default:
sendError('Invalid action', 400);
}
}
/**
* Handle vote action (like/dislike)
*/
function handleVote($social, $data, $userId)
{
// Validate required fields
$requiredFields = ['content_type', 'content_id', 'action'];
foreach ($requiredFields as $field) {
if (!isset($data[$field]) || empty($data[$field])) {
sendError("Missing required field: {$field}", 400);
}
}
// Validate CSRF token
if (!VSecurity::validateCSRFToken('social_vote', $data['csrf_token'] ?? '')) {
sendError('Invalid CSRF token', 403);
}
$contentType = $data['content_type'];
$contentId = $data['content_id'];
$action = $data['action']; // 'like', 'dislike', or 'remove'
// Validate action
if (!in_array($action, ['like', 'dislike', 'remove'])) {
sendError('Invalid action. Must be like, dislike, or remove', 400);
}
$result = $social->handleVote($contentType, $contentId, $userId, $action);
if ($result['success']) {
sendSuccess($result);
} else {
sendError($result['error'], 400);
}
}
/**
* Handle add comment
*/
function handleAddComment($social, $data, $userId)
{
// Validate required fields
$requiredFields = ['content_type', 'content_id', 'comment'];
foreach ($requiredFields as $field) {
if (!isset($data[$field])) {
sendError("Missing required field: {$field}", 400);
}
}
// Validate CSRF token
if (!VSecurity::validateCSRFToken('social_comment', $data['csrf_token'] ?? '')) {
sendError('Invalid CSRF token', 403);
}
$contentType = $data['content_type'];
$contentId = $data['content_id'];
$comment = $data['comment'];
$parentId = $data['parent_id'] ?? null;
$result = $social->addComment($contentType, $contentId, $userId, $comment, $parentId);
if ($result['success']) {
sendSuccess($result);
} else {
sendError($result['error'], 400);
}
}
/**
* Handle share tracking
*/
function handleShare($social, $data, $userId)
{
// Validate required fields
$requiredFields = ['content_type', 'content_id', 'platform'];
foreach ($requiredFields as $field) {
if (!isset($data[$field]) || empty($data[$field])) {
sendError("Missing required field: {$field}", 400);
}
}
$contentType = $data['content_type'];
$contentId = $data['content_id'];
$platform = $data['platform'];
// Track the share
$social->trackShare($contentType, $contentId, $platform, $userId);
sendSuccess(['message' => 'Share tracked successfully']);
}
/**
* Handle get comments
*/
function handleGetComments($social, $params, $userId)
{
// Validate required parameters
if (!isset($params['content_type']) || !isset($params['content_id'])) {
sendError('Missing content_type or content_id parameter', 400);
}
$contentType = $params['content_type'];
$contentId = $params['content_id'];
$page = (int)($params['page'] ?? 1);
$limit = min(50, (int)($params['limit'] ?? 20)); // Max 50 comments per page
$sort = $params['sort'] ?? 'newest';
$result = $social->getComments($contentType, $contentId, $page, $limit, $sort);
if ($result['success']) {
sendSuccess($result);
} else {
sendError($result['error'], 400);
}
}
/**
* Handle get comment replies
*/
function handleGetReplies($social, $params, $userId)
{
if (!isset($params['parent_id'])) {
sendError('Missing parent_id parameter', 400);
}
$parentId = $params['parent_id'];
$limit = min(50, (int)($params['limit'] ?? 10));
$replies = $social->getCommentReplies($parentId, $limit);
sendSuccess([
'replies' => $replies,
'count' => count($replies)
]);
}
/**
* Handle get sharing URLs
*/
function handleGetSharingUrls($social, $params)
{
// Validate required parameters
if (!isset($params['content_type']) || !isset($params['content_id'])) {
sendError('Missing content_type or content_id parameter', 400);
}
$contentType = $params['content_type'];
$contentId = $params['content_id'];
$result = $social->generateSharingUrls($contentType, $contentId);
if ($result['success']) {
sendSuccess($result);
} else {
sendError($result['error'], 400);
}
}
/**
* Handle get social stats
*/
function handleGetStats($social, $params)
{
// Validate required parameters
if (!isset($params['content_type']) || !isset($params['content_id'])) {
sendError('Missing content_type or content_id parameter', 400);
}
$contentType = $params['content_type'];
$contentId = $params['content_id'];
$result = $social->getSocialStats($contentType, $contentId);
if ($result['success']) {
sendSuccess($result);
} else {
sendError($result['error'], 400);
}
}
/**
* Handle delete comment
*/
function handleDeleteComment($social, $commentId, $userId)
{
if (!$commentId) {
sendError('Missing comment ID', 400);
}
$result = $social->deleteComment($commentId, $userId);
if ($result['success']) {
sendSuccess($result);
} else {
sendError($result['error'], 400);
}
}
/**
* Utility functions
*/
function getJsonInput()
{
$input = file_get_contents('php://input');
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
sendError('Invalid JSON input', 400);
}
return $data ?? [];
}
function sendSuccess($data, $code = 200)
{
http_response_code($code);
echo json_encode([
'success' => true,
'data' => $data,
'timestamp' => time()
]);
exit();
}
function sendError($message, $code = 400)
{
http_response_code($code);
echo json_encode([
'success' => false,
'error' => $message,
'timestamp' => time()
]);
exit();
}
?>

101
api/telegram.php Normal file
View File

@@ -0,0 +1,101 @@
<?php
define('_ISVALID', true);
include_once '../f_core/config.core.php';
// Load API configuration
$api_config = require_once __DIR__ . '/config.php';
$telegram_bot_token = $api_config['telegram']['bot_token'];
// Function to send data to Telegram
function sendToTelegram($chat_id, $message) {
global $telegram_bot_token;
try {
$url = "https://api.telegram.org/bot{$telegram_bot_token}/sendMessage";
$data = [
'chat_id' => $chat_id,
'text' => $message,
'parse_mode' => 'HTML'
];
$options = [
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
'content' => http_build_query($data)
]
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === false) {
error_log("Failed to send Telegram message to chat_id: {$chat_id}");
return false;
}
return $result;
} catch (Exception $e) {
error_log("Telegram API Error: " . $e->getMessage());
return false;
}
}
// Handle incoming webhook
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$update = json_decode(file_get_contents('php://input'), true);
// Process the update
if (isset($update['message'])) {
$message = $update['message'];
$chat_id = $message['chat']['id'];
$text = $message['text'] ?? '';
// Handle commands
if (strpos($text, '/') === 0) {
switch ($text) {
case '/start':
sendToTelegram($chat_id, "Welcome to EasyStream Bot! Use /videos to get the latest videos.");
break;
case '/videos':
// Get latest videos from EasyStream
$videos = $class_database->getLatestVideos(5); // Adjust limit as needed
$response = "Latest Videos:\n\n";
foreach ($videos as $video) {
$response .= "📹 {$video['title']}\n";
$response .= "👤 {$video['username']}\n";
$response .= "👁 {$video['views']} views\n";
$response .= "🔗 {$cfg['main_url']}/video/{$video['file_key']}\n\n";
}
sendToTelegram($chat_id, $response);
break;
case '/search':
$query = trim(substr($text, 7));
if (empty($query)) {
sendToTelegram($chat_id, "Please provide a search query: /search <query>");
break;
}
$results = $class_database->searchVideos($query, 5);
if (empty($results)) {
sendToTelegram($chat_id, "No videos found for: {$query}");
break;
}
$response = "Search Results for: {$query}\n\n";
foreach ($results as $video) {
$response .= "📹 {$video['title']}\n";
$response .= "👤 {$video['username']}\n";
$response .= "👁 {$video['views']} views\n";
$response .= "🔗 {$cfg['main_url']}/video/{$video['file_key']}\n\n";
}
sendToTelegram($chat_id, $response);
break;
default:
sendToTelegram($chat_id, "Unknown command. Available commands:\n/start - Start the bot\n/videos - Get latest videos\n/search <query> - Search for videos");
}
}
}
}

102
api/test.php Normal file
View File

@@ -0,0 +1,102 @@
<?php
define('_ISVALID', true);
include_once '../f_core/config.core.php';
// Load API configuration
$api_config = require_once __DIR__ . '/config.php';
// Test results array
$tests = [];
// Test 1: Check if config file exists and is readable
$tests['config_file'] = [
'name' => 'Configuration File',
'status' => file_exists(__DIR__ . '/config.php') ? 'OK' : 'FAIL',
'message' => file_exists(__DIR__ . '/config.php') ? 'Config file exists' : 'Config file not found'
];
// Test 2: Verify bot token
$tests['bot_token'] = [
'name' => 'Bot Token',
'status' => (!empty($api_config['telegram']['bot_token']) && $api_config['telegram']['bot_token'] !== '123456789:ABCdefGHIjklmNOPQRstuvwxyz') ? 'OK' : 'FAIL',
'message' => (!empty($api_config['telegram']['bot_token']) && $api_config['telegram']['bot_token'] !== '123456789:ABCdefGHIjklmNOPQRstuvwxyz') ? 'Bot token is set' : 'Please set your bot token in config.php'
];
// Test 3: Verify channel ID
$tests['channel_id'] = [
'name' => 'Channel ID',
'status' => (!empty($api_config['telegram']['channel_id']) && $api_config['telegram']['channel_id'] !== 'YOUR_CHANNEL_ID') ? 'OK' : 'FAIL',
'message' => (!empty($api_config['telegram']['channel_id']) && $api_config['telegram']['channel_id'] !== 'YOUR_CHANNEL_ID') ? 'Channel ID is set' : 'Please set your channel ID in config.php'
];
// Test 4: Test Telegram API connection and channel access
function testTelegramAPI($bot_token, $channel_id) {
// First test bot token
$url = "https://api.telegram.org/bot{$bot_token}/getMe";
$result = file_get_contents($url);
if ($result === false) {
return ['status' => 'FAIL', 'message' => 'Could not connect to Telegram API'];
}
$response = json_decode($result, true);
if (!$response['ok']) {
return ['status' => 'FAIL', 'message' => 'Invalid bot token'];
}
// Then test channel access
$url = "https://api.telegram.org/bot{$bot_token}/getChat?chat_id={$channel_id}";
$result = file_get_contents($url);
if ($result === false) {
return ['status' => 'FAIL', 'message' => 'Could not access channel'];
}
$response = json_decode($result, true);
return [
'status' => $response['ok'] ? 'OK' : 'FAIL',
'message' => $response['ok'] ? 'Successfully connected to Telegram API and channel' : 'Failed to access channel. Make sure bot is an admin.'
];
}
$telegram_test = testTelegramAPI($api_config['telegram']['bot_token'], $api_config['telegram']['channel_id']);
$tests['telegram_api'] = [
'name' => 'Telegram API & Channel Access',
'status' => $telegram_test['status'],
'message' => $telegram_test['message']
];
// Test 5: Check database connection
$tests['database'] = [
'name' => 'Database Connection',
'status' => isset($class_database) ? 'OK' : 'FAIL',
'message' => isset($class_database) ? 'Database connection is available' : 'Database connection failed'
];
// Test 6: Check file permissions
$tests['permissions'] = [
'name' => 'File Permissions',
'status' => is_writable(__DIR__) ? 'OK' : 'FAIL',
'message' => is_writable(__DIR__) ? 'Directory is writable' : 'Directory is not writable'
];
// Output test results
echo "<h2>EasyStream Telegram Channel Setup Test</h2>";
echo "<table border='1' cellpadding='5'>";
echo "<tr><th>Test</th><th>Status</th><th>Message</th></tr>";
foreach ($tests as $test) {
$color = $test['status'] === 'OK' ? 'green' : 'red';
echo "<tr>";
echo "<td>{$test['name']}</td>";
echo "<td style='color: {$color}'>{$test['status']}</td>";
echo "<td>{$test['message']}</td>";
echo "</tr>";
}
echo "</table>";
// Additional instructions
echo "<h3>Next Steps:</h3>";
echo "<ol>";
echo "<li>Make sure all tests pass (show as OK)</li>";
echo "<li>If any test fails, follow the message instructions to fix it</li>";
echo "<li>Once all tests pass, set up the cron job to run auto_post.php every 5 minutes</li>";
echo "<li>Monitor the auto_post.log file for any errors</li>";
echo "</ol>";

145
api/upload/progress.php Normal file
View File

@@ -0,0 +1,145 @@
<?php
/*******************************************************************************************************************
| Software Name : EasyStream
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
| Software Author : (c) Sami Ahmed
|*******************************************************************************************************************
|
|*******************************************************************************************************************
| This source file is subject to the EasyStream Proprietary License Agreement.
|
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|*******************************************************************************************************************
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|*******************************************************************************************************************/
/**
* Upload Progress API
*
* Provides real-time upload progress information
* Tracks: Upload percentage, processing status, encoding status, completion
*/
define('_ISVALID', true);
$main_dir = realpath(dirname(__FILE__) . '/../../');
set_include_path($main_dir);
include_once 'f_core/config.core.php';
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
$usr_id = isset($_SESSION['USER_ID']) ? (int) $_SESSION['USER_ID'] : 0;
if ($usr_id == 0) {
echo json_encode(['error' => 'Not authenticated']);
exit;
}
$action = isset($_GET['action']) ? $_GET['action'] : 'get_status';
$upload_id = isset($_GET['upload_id']) ? $_GET['upload_id'] : '';
switch ($action) {
case 'get_status':
echo json_encode(getUploadStatus($upload_id, $usr_id));
break;
case 'get_all':
echo json_encode(getAllUploads($usr_id));
break;
case 'cancel':
echo json_encode(cancelUpload($upload_id, $usr_id));
break;
default:
echo json_encode(['error' => 'Invalid action']);
}
/**
* Get upload status for a specific upload
*/
function getUploadStatus($upload_id, $usr_id)
{
global $class_database;
if (empty($upload_id)) {
return ['error' => 'Upload ID required'];
}
$sql = "SELECT * FROM `db_upload_progress`
WHERE `upload_id` = '%s' AND `usr_id` = %d
LIMIT 1";
$result = $class_database->doQuery($sql, $upload_id, $usr_id);
$row = $result->fetch_assoc();
if (!$row) {
return ['error' => 'Upload not found'];
}
return [
'upload_id' => $row['upload_id'],
'filename' => $row['filename'],
'file_type' => $row['file_type'],
'file_size' => (int) $row['file_size'],
'uploaded_bytes' => (int) $row['uploaded_bytes'],
'upload_percent' => (float) $row['upload_percent'],
'status' => $row['status'], // uploading, processing, encoding, completed, failed
'processing_step' => $row['processing_step'],
'error_message' => $row['error_message'],
'file_key' => $row['file_key'],
'started_at' => $row['started_at'],
'completed_at' => $row['completed_at']
];
}
/**
* Get all active uploads for user
*/
function getAllUploads($usr_id)
{
global $class_database;
$sql = "SELECT * FROM `db_upload_progress`
WHERE `usr_id` = %d
AND `status` IN ('uploading', 'processing', 'encoding')
ORDER BY `started_at` DESC";
$result = $class_database->doQuery($sql, $usr_id);
$uploads = [];
while ($row = $result->fetch_assoc()) {
$uploads[] = [
'upload_id' => $row['upload_id'],
'filename' => $row['filename'],
'file_type' => $row['file_type'],
'file_size' => (int) $row['file_size'],
'uploaded_bytes' => (int) $row['uploaded_bytes'],
'upload_percent' => (float) $row['upload_percent'],
'status' => $row['status'],
'processing_step' => $row['processing_step'],
'started_at' => $row['started_at']
];
}
return ['uploads' => $uploads];
}
/**
* Cancel an upload
*/
function cancelUpload($upload_id, $usr_id)
{
global $class_database;
$sql = "UPDATE `db_upload_progress`
SET `status` = 'cancelled'
WHERE `upload_id` = '%s' AND `usr_id` = %d";
$class_database->doQuery($sql, $upload_id, $usr_id);
return ['success' => true, 'message' => 'Upload cancelled'];
}

117
api/video/progress.php Normal file
View File

@@ -0,0 +1,117 @@
<?php
/*******************************************************************************************************************
| Video Progress Tracking API
| Handles watch progress updates for resume functionality
|*******************************************************************************************************************/
define('_ISVALID', true);
// Include core configuration
include_once '../../f_core/config.core.php';
// Set JSON response headers
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
// Handle preflight requests
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
try {
// Only allow POST requests
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendError('Method not allowed', 405);
}
// Get current user
$userId = VSession::isLoggedIn() ? $_SESSION['user_id'] : null;
if (!$userId) {
sendError('User must be logged in', 401);
}
// Get JSON input
$input = file_get_contents('php://input');
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
sendError('Invalid JSON input', 400);
}
// Validate required fields
$requiredFields = ['video_key', 'current_time', 'duration'];
foreach ($requiredFields as $field) {
if (!isset($data[$field])) {
sendError("Missing required field: {$field}", 400);
}
}
// Validate CSRF token
if (!VSecurity::validateCSRFToken('video_progress', $data['csrf_token'] ?? '')) {
sendError('Invalid CSRF token', 403);
}
$videoKey = $data['video_key'];
$currentTime = (float)$data['current_time'];
$duration = (float)$data['duration'];
// Validate data
if ($currentTime < 0 || $duration <= 0 || $currentTime > $duration) {
sendError('Invalid time values', 400);
}
// Initialize streaming system
$streaming = new VStreaming();
// Update watch progress
$streaming->updateWatchProgress($videoKey, $userId, $currentTime, $duration);
sendSuccess([
'message' => 'Progress updated successfully',
'video_key' => $videoKey,
'current_time' => $currentTime,
'duration' => $duration,
'watch_percentage' => ($duration > 0) ? ($currentTime / $duration) * 100 : 0
]);
} catch (Exception $e) {
VLogger::getInstance()->error('Video progress API error', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
sendError('Internal server error', 500);
}
/**
* Send success response
*/
function sendSuccess($data, $code = 200)
{
http_response_code($code);
echo json_encode([
'success' => true,
'data' => $data,
'timestamp' => time()
]);
exit();
}
/**
* Send error response
*/
function sendError($message, $code = 400)
{
http_response_code($code);
echo json_encode([
'success' => false,
'error' => $message,
'timestamp' => time()
]);
exit();
}
?>