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

View File

@@ -0,0 +1,338 @@
<?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.
|*******************************************************************************************************************/
defined('_ISVALID') or header('Location: /error');
/**
* Branding Helper Functions
* These functions provide easy access to branding features in templates and modules
*/
/**
* Get branding instance
*/
function getBranding() {
return VBranding::getInstance();
}
/**
* Get a branding setting value
*/
function brandingGet($key, $default = null) {
return getBranding()->get($key, $default);
}
/**
* Generate site logo HTML
*/
function siteLogo($type = 'main', $class = '', $alt = null) {
return getBranding()->getLogo($type, $class, $alt);
}
/**
* Generate badge HTML
*/
function badge($type, $text = null, $class = '') {
return getBranding()->getBadge($type, $text, $class);
}
/**
* Get site information array
*/
function siteInfo() {
return getBranding()->getSiteInfo();
}
/**
* Generate dynamic CSS link tag
*/
function dynamicCSSLink() {
$timestamp = time(); // For cache busting
return "<link rel=\"stylesheet\" href=\"/dynamic_theme.php?v=$timestamp\" id=\"dynamic-theme\">";
}
/**
* Generate inline CSS variables for quick access
*/
function inlineCSSVariables() {
$branding = getBranding();
$css = "<style id=\"branding-vars\">\n:root {\n";
$css .= " --color-primary: " . $branding->get('color_primary', '#007bff') . ";\n";
$css .= " --color-primary-dark: " . $branding->get('color_primary_dark', '#0056b3') . ";\n";
$css .= " --color-bg-main: " . $branding->get('color_bg_main', '#ffffff') . ";\n";
$css .= " --color-text-primary: " . $branding->get('color_text_primary', '#212529') . ";\n";
$css .= " --font-family-primary: " . $branding->get('font_family_primary', 'Arial, sans-serif') . ";\n";
$css .= " --layout-max-width: " . $branding->get('layout_max_width', 1200) . "px;\n";
$css .= "}\n</style>\n";
return $css;
}
/**
* Generate meta tags for branding
*/
function brandingMetaTags() {
$siteInfo = siteInfo();
$meta = "<meta name=\"theme-color\" content=\"" . brandingGet('color_primary', '#007bff') . "\">\n";
$meta .= "<meta name=\"description\" content=\"" . htmlspecialchars($siteInfo['description']) . "\">\n";
$meta .= "<meta property=\"og:site_name\" content=\"" . htmlspecialchars($siteInfo['name']) . "\">\n";
$meta .= "<meta property=\"og:description\" content=\"" . htmlspecialchars($siteInfo['description']) . "\">\n";
$meta .= "<link rel=\"icon\" href=\"" . htmlspecialchars($siteInfo['favicon']) . "\">\n";
return $meta;
}
/**
* Generate header HTML with branding
*/
function brandedHeader($includeNav = true) {
$siteInfo = siteInfo();
$branding = getBranding();
$html = "<header class=\"site-header\" style=\"background-color: " . $branding->get('color_bg_header', '#ffffff') . ";\">\n";
$html .= " <div class=\"container\">\n";
$html .= " <div class=\"header-content\">\n";
$html .= " <div class=\"logo-section\">\n";
$html .= " " . siteLogo('main', 'main-logo') . "\n";
$html .= " <span class=\"site-name\">" . htmlspecialchars($siteInfo['name']) . "</span>\n";
$html .= " </div>\n";
if ($includeNav) {
$html .= " <nav class=\"main-nav\">\n";
$html .= " <a href=\"/\" class=\"nav-link\">Home</a>\n";
$html .= " <a href=\"/videos\" class=\"nav-link\">Videos</a>\n";
$html .= " <a href=\"/live\" class=\"nav-link\">Live</a>\n";
$html .= " <a href=\"/upload\" class=\"nav-link btn btn-primary\">Upload</a>\n";
$html .= " </nav>\n";
}
$html .= " </div>\n";
$html .= " </div>\n";
$html .= "</header>\n";
return $html;
}
/**
* Generate footer HTML with branding
*/
function brandedFooter() {
$siteInfo = siteInfo();
$branding = getBranding();
$html = "<footer class=\"site-footer\" style=\"background-color: " . $branding->get('color_bg_footer', '#343a40') . "; color: " . $branding->get('color_text_light', '#ffffff') . ";\">\n";
$html .= " <div class=\"container\">\n";
$html .= " <div class=\"footer-content\">\n";
$html .= " <div class=\"footer-brand\">\n";
$html .= " " . siteLogo('small', 'footer-logo') . "\n";
$html .= " <p>" . htmlspecialchars($siteInfo['tagline']) . "</p>\n";
$html .= " </div>\n";
$html .= " <div class=\"footer-links\">\n";
$html .= " <a href=\"/about\">About</a>\n";
$html .= " <a href=\"/privacy\">Privacy</a>\n";
$html .= " <a href=\"/terms\">Terms</a>\n";
$html .= " <a href=\"/contact\">Contact</a>\n";
$html .= " </div>\n";
$html .= " </div>\n";
$html .= " <div class=\"footer-bottom\">\n";
$html .= " <p>" . htmlspecialchars($siteInfo['footer_text']) . "</p>\n";
$html .= " </div>\n";
$html .= " </div>\n";
$html .= "</footer>\n";
return $html;
}
/**
* Generate video card HTML with branding
*/
function videoCard($video, $showBadges = true) {
$branding = getBranding();
$html = "<div class=\"video-card\">\n";
$html .= " <div class=\"video-thumbnail\">\n";
$html .= " <img src=\"" . htmlspecialchars($video['thumbnail'] ?? brandingGet('default_thumbnail')) . "\" alt=\"" . htmlspecialchars($video['title']) . "\">\n";
if ($showBadges && isset($video['badges'])) {
$html .= " <div class=\"video-badges\">\n";
foreach ($video['badges'] as $badgeType) {
$html .= " " . badge($badgeType) . "\n";
}
$html .= " </div>\n";
}
$html .= " </div>\n";
$html .= " <div class=\"video-info\">\n";
$html .= " <h3 class=\"video-title\">" . htmlspecialchars($video['title']) . "</h3>\n";
$html .= " <p class=\"video-author\">" . htmlspecialchars($video['author'] ?? 'Unknown') . "</p>\n";
$html .= " <div class=\"video-stats\">\n";
$html .= " <span class=\"views\">" . number_format($video['views'] ?? 0) . " views</span>\n";
$html .= " <span class=\"date\">" . htmlspecialchars($video['date'] ?? '') . "</span>\n";
$html .= " </div>\n";
$html .= " </div>\n";
$html .= "</div>\n";
return $html;
}
/**
* Generate user avatar with branding
*/
function userAvatar($user, $size = 'medium', $showBadges = true) {
$branding = getBranding();
$sizes = ['small' => 32, 'medium' => 48, 'large' => 64];
$pixelSize = $sizes[$size] ?? 48;
$avatar = $user['avatar'] ?? brandingGet('default_avatar');
$html = "<div class=\"user-avatar user-avatar-$size\">\n";
$html .= " <img src=\"" . htmlspecialchars($avatar) . "\" alt=\"" . htmlspecialchars($user['username'] ?? 'User') . "\" width=\"$pixelSize\" height=\"$pixelSize\">\n";
if ($showBadges && isset($user['verified']) && $user['verified']) {
$html .= " " . badge('verified') . "\n";
}
if ($showBadges && isset($user['premium']) && $user['premium']) {
$html .= " " . badge('premium') . "\n";
}
$html .= "</div>\n";
return $html;
}
/**
* Generate notification HTML with branding
*/
function notification($message, $type = 'info', $dismissible = true) {
$typeClasses = [
'success' => 'notification-success',
'error' => 'notification-error',
'warning' => 'notification-warning',
'info' => 'notification-info'
];
$class = $typeClasses[$type] ?? 'notification-info';
$html = "<div class=\"notification $class\">\n";
$html .= " <div class=\"notification-content\">\n";
$html .= " " . htmlspecialchars($message) . "\n";
$html .= " </div>\n";
if ($dismissible) {
$html .= " <button class=\"notification-close\" onclick=\"this.parentElement.remove()\">&times;</button>\n";
}
$html .= "</div>\n";
return $html;
}
/**
* Generate button HTML with branding
*/
function button($text, $type = 'primary', $href = null, $attributes = []) {
$tag = $href ? 'a' : 'button';
$class = "btn btn-$type";
if (isset($attributes['class'])) {
$class .= ' ' . $attributes['class'];
unset($attributes['class']);
}
$attrs = '';
foreach ($attributes as $key => $value) {
$attrs .= " $key=\"" . htmlspecialchars($value) . "\"";
}
if ($href) {
$attrs .= " href=\"" . htmlspecialchars($href) . "\"";
}
return "<$tag class=\"$class\"$attrs>" . htmlspecialchars($text) . "</$tag>";
}
/**
* Generate form input with branding
*/
function formInput($name, $type = 'text', $value = '', $attributes = []) {
$class = 'form-input';
if (isset($attributes['class'])) {
$class .= ' ' . $attributes['class'];
unset($attributes['class']);
}
$attrs = "class=\"$class\" name=\"" . htmlspecialchars($name) . "\" type=\"$type\"";
if ($value) {
$attrs .= " value=\"" . htmlspecialchars($value) . "\"";
}
foreach ($attributes as $key => $val) {
$attrs .= " $key=\"" . htmlspecialchars($val) . "\"";
}
return "<input $attrs>";
}
/**
* Check if dark mode is enabled
*/
function isDarkMode() {
return brandingGet('enable_dark_mode', true);
}
/**
* Get current theme preset name
*/
function getCurrentTheme() {
// This could be enhanced to track the current active theme
return brandingGet('current_theme', 'Default Blue');
}
/**
* Generate theme switcher HTML
*/
function themeSwitcher() {
if (!isDarkMode()) {
return '';
}
$html = "<div class=\"theme-switcher\">\n";
$html .= " <button class=\"theme-toggle\" onclick=\"toggleTheme()\" title=\"Toggle Dark Mode\">\n";
$html .= " <span class=\"theme-icon\">🌙</span>\n";
$html .= " </button>\n";
$html .= "</div>\n";
$html .= "<script>\n";
$html .= "function toggleTheme() {\n";
$html .= " document.body.classList.toggle('dark-theme');\n";
$html .= " const icon = document.querySelector('.theme-icon');\n";
$html .= " icon.textContent = document.body.classList.contains('dark-theme') ? '☀️' : '🌙';\n";
$html .= " localStorage.setItem('theme', document.body.classList.contains('dark-theme') ? 'dark' : 'light');\n";
$html .= "}\n";
$html .= "// Load saved theme\n";
$html .= "if (localStorage.getItem('theme') === 'dark') {\n";
$html .= " document.body.classList.add('dark-theme');\n";
$html .= " document.querySelector('.theme-icon').textContent = '☀️';\n";
$html .= "}\n";
$html .= "</script>\n";
return $html;
}
?>

View File

@@ -0,0 +1,34 @@
<?php
// Core utility functions
if (!defined("_VALID_ACCESS")) { exit("Direct access not allowed"); }
function sanitize_input($input) {
if (is_array($input)) {
return array_map("sanitize_input", $input);
}
return htmlspecialchars(trim($input), ENT_QUOTES, "UTF-8");
}
function redirect($url, $code = 302) {
header("Location: " . $url, true, $code);
exit();
}
function json_response($data, $code = 200) {
http_response_code($code);
header("Content-Type: application/json");
echo json_encode($data);
exit();
}
function error_response($message, $code = 400) {
json_response(["error" => $message], $code);
}
function success_response($data = null, $message = "Success") {
$response = ["success" => true, "message" => $message];
if ($data !== null) {
$response["data"] = $data;
}
json_response($response);
}

View File

@@ -0,0 +1,40 @@
<?php
// Database utility functions
if (!defined("_VALID_ACCESS")) { exit("Direct access not allowed"); }
function get_db_connection() {
global $db;
if ($db === null) {
try {
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4";
$db = new PDO($dsn, DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]);
} catch (PDOException $e) {
error_log("Database connection failed: " . $e->getMessage());
throw new Exception("Database connection failed");
}
}
return $db;
}
function db_query($sql, $params = []) {
$db = get_db_connection();
$stmt = $db->prepare($sql);
$stmt->execute($params);
return $stmt;
}
function db_fetch($sql, $params = []) {
return db_query($sql, $params)->fetch();
}
function db_fetch_all($sql, $params = []) {
return db_query($sql, $params)->fetchAll();
}
function db_execute($sql, $params = []) {
return db_query($sql, $params)->rowCount();
}

View File

@@ -0,0 +1,364 @@
<?php
defined('_ISVALID') or header('Location: /error');
use Akismet\Akismet;
use Akismet\Comment;
use GeoIp2\Database\Reader;
use IPQualityScore\IPQualityScore;
function insert_swiperJS($p)
{return VGenerate::swiperjs($p["for"]);}
function insert_socialMediaLinks()
{return VGenerate::socialMediaLinks();}
function insert_themeSwitch()
{return VGenerate::themeSwitch();}
function insert_loadcssplugins()
{return VGenerate::cssplugins();}
function insert_loadbecssplugins()
{return VGenerate::becssplugins();}
function insert_loadjsplugins()
{return VGenerate::jsplugins();}
function insert_loadbejsplugins()
{return VGenerate::bejsplugins();}
function insert_getSubCount()
{return VHome::getSubCount();}
function insert_getFollowCount()
{return VHome::getSubCount(1);}
function insert_getCurrentSection()
{return VHref::currentSection();}
function insert_getSearchSection()
{return VSearch::getSearchSection();}
function insert_newMessages()
{return VMessages::getnew_nr();}
function insert_viewFileColumn()
{return VView::sideColumn();}
function insert_bePlayerAdCounts($p)
{return VbeAdvertising::playerAdCount($p["for"]);}
function insert_H2span($t)
{return VGenerate::H2span($t["for"], $t["f"]);}
function insert_beUserCount($p)
{return VbeMembers::userCount($p["for"]);}
function insert_beFileCount($p)
{return VbeFiles::fileCount($p["for"]);}
function insert_beSectionList($p)
{return VbeSettings::sectionList($p["for"]);}
function insert_beAdvCount_banner($p)
{return VbeAdvertising::advCount_banner($p["for"]);}
function insert_beAdvCount_grp($p)
{return VbeAdvertising::advCount_group($p["for"]);}
function insert_beMenuToggle()
{return VGenerate::menuToggle();}
function insert_beMenuEntries()
{return VGenerate::menuEntries();}
function insert_advHTML($id)
{return VGenerate::advHTML($id["id"]);}
function insert_getVJSJS($p)
{return VPlayers::VJSJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
function insert_getJWJS($p)
{return VPlayers::JWJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
function insert_getFPJS($p)
{return VPlayers::FPJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
function insert_getDOCJS($p)
{return VPlayers::DOCJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
function insert_getFLEXJS($p)
{return VPlayers::FLEXJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
function insert_getFREEJS($p)
{return VPlayers::FREEJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
function insert_getImageJS($p)
{return VPlayers::imageJS('view', $p["pl_key"]);}
function insert_getPageMeta($for)
{return VHref::getPageMeta($for["for"]);}
function insert_categoryMenu()
{$a = new VBrowse;return VBrowse::categoryMenu();}
function insert_channelsMenu()
{$a = new VChannels;return VChannels::categoryMenu();}
function insert_promotedChannelsMenu()
{$c = new VChannels;return VChannels::promotedMenu();}
function insert_uploadResponse()
{return VResponses::uploadResponse();}
function insert_getUserStats($type)
{return VUseraccount::getUserStats($type["type"]);}
function insert_subsConfig()
{return VFiles::subsConfig();}
function insert_fileListSelect($for)
{return VMessages::fileListSelect($for["for"]);}
function insert_updateSubsCount()
{return VFiles::userSubs(1);}
function insert_updateOsubCount()
{return VFiles::userSubs(2);}
function insert_getUserSubs()
{$a = new VFiles;return VFiles::userSubs();}
function insert_getPlaylistEditID($key)
{return VFiles::userPlaylistEditID($key["key"], $key["get"]);}
function insert_getUserPlaylists($for)
{return VFiles::userPlaylists($for["for"]);}
function insert_getChannelTabs()
{return VUserpage::channelTabs();}
function insert_getChannelLayout()
{return VUserpage::channelLayout();}
function insert_getCustomStyles()
{return VUserpage::customStyles();}
function insert_userLabelCheckboxes()
{return VContacts::labelCheckboxes();}
function insert_sectionLabel($for)
{return VMessages::sectionLabel($for["for"]);}
function insert_addToLabel($for)
{return VMessages::addToLabel($for["for"]);}
function insert_getLabelName()
{return VMessages::getLabelName();}
function insert_fileCount($for)
{return VFiles::fileCount($for["for"]);}
function insert_msgCount($for)
{return VMessages::messageCount($for["for"]);}
function insert_getMessageSubject($msg_id)
{return VMessages::getMessageInfo('msg_subj', $msg_id["msg_id"]);}
function insert_beFileCategories($type)
{return VbeFiles::fileCategories($type["type"]);}
function insert_footerInit()
{return VGenerate::footerInit();}
function insert_langInit()
{return VGenerate::langInit();}
function insert_langInit_be()
{return VGenerate::langInit_be();}
function insert_footerText($p)
{return VGenerate::footerText($p["ct"]);}
function insert_getUserNameKey($key)
{
global $class_database, $class_filter;
$k = strlen($key["key"]) == 11 ? substr($key["key"], 1) : $key["key"];
return $class_database->singleFieldValue('db_accountuser', 'usr_user', 'usr_key', $class_filter->clr_str($k));
}
function insert_phpInfo()
{
ob_start();
phpinfo();
$i = ob_get_contents();
ob_end_clean();
return $i;
}
function insert_currentMenuEntry($for)
{
return VMessages::currentMenuEntry($for["for"]);
}
function insert_getMessageDate($msg_id)
{
global $class_database;
return $for = $_GET["f"] == 'comm' ? $class_database->singleFieldValue('db_channelcomments', 'c_datetime', 'c_id', $msg_id["msg_id"]) : VMessages::getMessageInfo('msg_date', $msg_id["msg_id"]);
}
function insert_getMessageText($msg_id)
{
global $class_database;
return '<pre>' . ($_GET["f"] == 'comm' ? $class_database->singleFieldValue('db_channelcomments', 'c_body', 'c_id', $msg_id["msg_id"]) : VMessages::getMessageInfo('msg_body', $msg_id["msg_id"])) . '</pre>';
}
function insert_getUsername($user_id)
{
$user_details = VUserinfo::getUserInfo($user_id["user_id"]);
return $user_details["uname"];
}
function insert_getProfileImage($for)
{
$_for = $for["for"] != '' ? $for["for"] : '';
return VUseraccount::getProfileImage($_for);
}
function insert_phpInfoText()
{
global $language;
return '<span class="">' . $language["backend.menu.entry1.sub7.active"] . '</span> <b>post_max_size ' . ini_get("post_max_size") . '</b> <span class="">' . $language["frontend.global.and"] . '</span> <b>upload_max_filesize ' . ini_get("upload_max_filesize") . '</b>';
}
function insert_generateCountryList()
{
global $cfg, $language;
include_once 'f_core/config.countries.php';
$i = 0;
$disabled = $_SESSION["signup_location"] != '' ? 'disabled="disabled"' : null;
$disabled = $cfg['global_signup'] == 0 ? 'disabled="disabled"' : $disabled;
$disabled = ($cfg["signup_ip_access"] == 1 and !VIPaccess::checkIPlist($cfg["list_ip_signup"])) ? 'disabled="disabled"' : $disabled;
$select = '<select ' . $disabled . ' name="frontend_signup_location_sel" class="signup-select" onChange="$(\'#input-loc\').val(\'' . $language["frontend.signup.location"] . ': \'+this.value);">';
foreach ($_countries as $value) {
$selected = $_SESSION["signup_location"] == $value ? ' selected="selected"' : ($_POST["frontend_signup_location"] == $value ? ' selected="selected"' : null);
$selected = $i == 0 ? ' disabled="disabled" selected="selected"' : $selected;
$option .= '<option value="' . $value . '"' . $selected . '>' . $value . '</option>';
$i += 1;
}
$select .= $option . '</select>';
return $select;
}
function insert_getListContent($from)
{
global $class_database, $smarty;
$cfg = $class_database->getConfigurations('list_signup_terms,list_ip_signup,list_email_domains,list_reserved_users,list_ip_access,list_ip_backend');
switch ($from["from"]) {
case 'terms':$url = $cfg["list_signup_terms"];
break;
case 'ip-backend':$url = $cfg["list_ip_backend"];
break;
case 'ip-access':$url = $cfg["list_ip_access"];
break;
case 'ip-signup':$url = $cfg["list_ip_signup"];
break;
case 'email-domains':$url = $cfg["list_email_domains"];
break;
case 'usernames':$url = $cfg["list_reserved_users"];
break;
}
return $smarty->fetch($url);
}
function insert_arrayFromString($opt)
{
global $cfg, $language;
include_once 'f_data/data_languages/' . $_SESSION["fe_lang"] . '/lang_frontend/' . $opt["from"] . '.php';
$array = explode(',', $language[$opt["entry"]]);
return $array;
}
function insert_sizeFormat($size)
{
global $cfg, $language;
$dlm = $cfg["numeric_delimiter"];
$size["size"] = $size["size"] * (1024 * 1024);
if ($size["size"] < 1024) {return number_format($size["size"], 0, $dlm, $dlm) . $language["frontend.sizeformat.bytes"];} elseif ($size["size"] < (1024 * 1024)) {$size2 = round($size["size"] / 1024, 1);return number_format(($size["size"] / 1024), 0, $dlm, $dlm) . $language["frontend.sizeformat.kb"];} elseif ($size["size"] < (1024 * 1024 * 1024)) {return number_format(($size["size"] / (1024 * 1024)), 0, $dlm, $dlm) . $language["frontend.sizeformat.mb"];} else { $size2 = round($size["size"] / (1024 * 1024 * 1024), 1);return number_format(($size["size"] / (1024 * 1024 * 1024)), 0, $dlm, $dlm) . $language["frontend.sizeformat.gb"];}
}
function secured_encrypt($data)
{
$method = "aes-256-cbc";
$first_key = base64_decode(ENC_FIRSTKEY);
$second_key = base64_decode(ENC_SECONDKEY);
$iv_length = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($iv_length);
$first_encrypted = openssl_encrypt($data, $method, $first_key, OPENSSL_RAW_DATA, $iv);
$second_encrypted = hash_hmac('sha3-512', $first_encrypted, $second_key, true);
$output = base64_encode($iv . $second_encrypted . $first_encrypted);
return $output;
}
function secured_decrypt($input)
{
$method = "aes-256-cbc";
$first_key = base64_decode(ENC_FIRSTKEY);
$second_key = base64_decode(ENC_SECONDKEY);
$mix = base64_decode($input);
$iv_length = openssl_cipher_iv_length($method);
$iv = substr($mix, 0, $iv_length);
$second_encrypted = substr($mix, $iv_length, 64);
$first_encrypted = substr($mix, $iv_length + 64);
$data = openssl_decrypt($first_encrypted, $method, $first_key, OPENSSL_RAW_DATA, $iv);
$second_encrypted_new = hash_hmac('sha3-512', $first_encrypted, $second_key, true);
if ($second_encrypted and hash_equals($second_encrypted, $second_encrypted_new)) {
return $data;
}
return false;
}
function check_ip_quality_score($ip)
{
global $cfg;
include 'f_core/f_classes/class_ipquality/vendor/autoload.php';
$key = $cfg['ipqualityscore_api'];
try {
$qualityScore = new IPQualityScore($key);
$result = $qualityScore->IPAddressVerification
->setUserLanguage($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '')
->setUserAgent($_SERVER['HTTP_USER_AGENT'] ?? '')
->getResponse($ip);
return $result;
} catch (Exception $e) {
//echo 'Caught exception: ', $e->getMessage(), "\n";
}
}
function akismet_spam_check($c)
{
global $cfg;
include 'f_core/f_classes/class_akismet/vendor/autoload.php';
$key = $cfg['akismet_api'];
$site = $cfg['akismet_site'];
try {
$akismet = new Akismet($key, $site);
$comment = new Comment();
$comment->setContent($c);
$comment->includeServerVariables();
$result = $akismet->check($comment);
if ($result->isSpam) {
return true;
}
if ($result->isDiscardable) {
return true;
}
} catch (Exception $e) {
//echo 'Caught exception: ', $e->getMessage(), "\n";
}
return false;
}
function maxmind_country()
{
global $cfg;
include 'f_core/f_classes/class_maxmind/vendor/autoload.php';
try {
$reader = new Reader($cfg['maxmind_db']);
$record = $reader->country($_SERVER[REM_ADDR]);
return $record->country->isoCode;
} catch (Exception $e) {
//echo 'Caught exception: ', $e->getMessage(), "\n";
}
}
function proxy_check($address)
{
global $cfg;
include 'f_core/f_classes/class_proxycheck/vendor/autoload.php';
try {
$proxycheck_options = array(
'API_KEY' => $cfg['proxycheck_api'], // Your API Key.
'ASN_DATA' => 1, // Enable ASN data response.
'DAY_RESTRICTOR' => 7, // Restrict checking to proxies seen in the past # of days.
'VPN_DETECTION' => 1, // Check for both VPN's and Proxies instead of just Proxies.
'RISK_DATA' => 1, // 0 = Off, 1 = Risk Score (0-100), 2 = Risk Score & Attack History.
'INF_ENGINE' => 0, // Enable or disable the real-time inference engine.
'TLS_SECURITY' => 0, // Enable or disable transport security (TLS).
'QUERY_TAGGING' => 1, // Enable or disable query tagging.
'MASK_ADDRESS' => 1, // Anonymises the local-part of an email address (e.g. anonymous@domain.tld)
'CUSTOM_TAG' => '', // Specify a custom query tag instead of the default (Domain+Page).
'BLOCKED_COUNTRIES' => array(), // Specify an array of countries or isocodes to be blocked.
'ALLOWED_COUNTRIES' => array(), // Specify an array of countries or isocodes to be allowed.
);
$result_array = \proxycheck\proxycheck::check($address, $proxycheck_options);
if (is_array($result_array) and ($result_array[$address]["proxy"] == 'yes' or $result_array["block"] == 'yes')) {
return true;
}
} catch (Exception $e) {
//echo 'Caught exception: ', $e->getMessage(), "\n";
}
return false;
}

View File

@@ -0,0 +1,69 @@
<?php
defined('_ISVALID') or header('Location: /error');
/**
* Lightweight queue helpers with Redis fallback to filesystem.
*/
function queue_redis_client()
{
static $redis = null;
if ($redis !== null) return $redis;
$host = getenv('REDIS_HOST') ?: ($GLOBALS['cfg']['redis_host'] ?? null);
$port = (int) (getenv('REDIS_PORT') ?: ($GLOBALS['cfg']['redis_port'] ?? 6379));
$db = (int) (getenv('REDIS_DB') ?: ($GLOBALS['cfg']['redis_db'] ?? 0));
if (!$host) return $redis = false;
if (!class_exists('Redis')) return $redis = false;
try {
$r = new Redis();
if (!$r->connect($host, $port, 2.0)) return $redis = false;
if ($db) $r->select($db);
return $redis = $r;
} catch (Exception $e) {
return $redis = false;
}
}
function queue_enqueue($queue, array $payload)
{
$data = json_encode(['t' => time(), 'payload' => $payload]);
if ($r = queue_redis_client()) {
return (bool) $r->lPush("q:$queue", $data);
}
// FS fallback
$dir = 'f_data/queues/' . preg_replace('/[^a-z0-9:_-]/i', '_', $queue);
if (!is_dir($dir)) @mkdir($dir, 0755, true);
$fn = $dir . '/' . microtime(true) . '-' . bin2hex(random_bytes(4)) . '.json';
return (bool) @file_put_contents($fn, $data);
}
function queue_dequeue($queue, $timeout = 2)
{
if ($r = queue_redis_client()) {
$res = $r->brPop(["q:$queue"], $timeout);
if (is_array($res) && count($res) === 2) {
return json_decode($res[1], true);
}
return null;
}
// FS fallback (non-blocking, best effort)
$dir = 'f_data/queues/' . preg_replace('/[^a-z0-9:_-]/i', '_', $queue);
if (!is_dir($dir)) return null;
$files = glob($dir . '/*.json');
if (!$files) return null;
sort($files);
$fn = $files[0];
$raw = @file_get_contents($fn);
@unlink($fn);
return $raw ? json_decode($raw, true) : null;
}
function queue_available()
{
return (bool) queue_redis_client();
}

View File

@@ -0,0 +1,344 @@
<?php
/*******************************************************************************************************************
| RBAC Helper Functions
| Convenience functions for role-based access control
|*******************************************************************************************************************/
defined('_ISVALID') or header('Location: /error');
/**
* Check if current user has permission
* @param string $permission Permission name
* @param array $context Additional context
* @return bool True if user has permission
*/
function has_permission($permission, $context = [])
{
$rbac = VRBAC::getInstance();
return $rbac->hasPermission('current', $permission, $context);
}
/**
* Check if user has permission
* @param int $userId User ID
* @param string $permission Permission name
* @param array $context Additional context
* @return bool True if user has permission
*/
function user_has_permission($userId, $permission, $context = [])
{
$rbac = VRBAC::getInstance();
return $rbac->hasPermission($userId, $permission, $context);
}
/**
* Require permission or redirect/exit
* @param string $permission Permission name
* @param array $context Additional context
* @param string $redirectUrl Redirect URL on failure
* @param string $errorMessage Error message
*/
function require_permission($permission, $context = [], $redirectUrl = '/error', $errorMessage = 'Access denied')
{
if (!has_permission($permission, $context)) {
if ($redirectUrl) {
header("Location: {$redirectUrl}");
exit;
} else {
http_response_code(403);
die($errorMessage);
}
}
}
/**
* Get current user roles
* @return array User roles
*/
function get_user_roles($userId = null)
{
$rbac = VRBAC::getInstance();
$userId = $userId ?: ($_SESSION['USER_ID'] ?? null);
if (!$userId) {
return [VRBAC::ROLE_GUEST];
}
return $rbac->getUserRoles($userId);
}
/**
* Check if user has role
* @param string $role Role name
* @param int $userId User ID (optional, defaults to current user)
* @return bool True if user has role
*/
function user_has_role($role, $userId = null)
{
$userRoles = get_user_roles($userId);
return in_array($role, $userRoles);
}
/**
* Check if current user is admin
* @return bool True if user is admin
*/
function is_admin()
{
return user_has_role(VRBAC::ROLE_ADMIN) || user_has_role(VRBAC::ROLE_SUPER_ADMIN);
}
/**
* Check if current user is moderator or higher
* @return bool True if user is moderator or higher
*/
function is_moderator()
{
return user_has_role(VRBAC::ROLE_MODERATOR) || is_admin();
}
/**
* Check if current user is verified
* @return bool True if user is verified
*/
function is_verified()
{
$roles = get_user_roles();
return !in_array(VRBAC::ROLE_GUEST, $roles) && !empty(array_intersect($roles, [
VRBAC::ROLE_VERIFIED, VRBAC::ROLE_CREATOR, VRBAC::ROLE_MODERATOR,
VRBAC::ROLE_ADMIN, VRBAC::ROLE_SUPER_ADMIN
]));
}
/**
* Assign role to user
* @param int $userId User ID
* @param string $role Role name
* @param string $reason Reason for assignment
* @return bool Success status
*/
function assign_user_role($userId, $role, $reason = '')
{
$rbac = VRBAC::getInstance();
$assignedBy = $_SESSION['USER_ID'] ?? null;
return $rbac->assignRole($userId, $role, $assignedBy, $reason);
}
/**
* Remove role from user
* @param int $userId User ID
* @param string $role Role name
* @param string $reason Reason for removal
* @return bool Success status
*/
function remove_user_role($userId, $role, $reason = '')
{
$rbac = VRBAC::getInstance();
$removedBy = $_SESSION['USER_ID'] ?? null;
return $rbac->removeRole($userId, $role, $removedBy, $reason);
}
/**
* Get permission-based navigation menu
* @return array Navigation menu items
*/
function get_permission_menu()
{
$menu = [];
// Public menu items
$menu[] = ['title' => 'Home', 'url' => '/', 'permission' => 'content.view'];
$menu[] = ['title' => 'Browse', 'url' => '/videos', 'permission' => 'content.view'];
$menu[] = ['title' => 'Search', 'url' => '/search', 'permission' => 'content.search'];
// Member menu items
if (has_permission('user.upload.basic')) {
$menu[] = ['title' => 'Upload', 'url' => '/upload', 'permission' => 'user.upload.basic'];
}
// Creator menu items
if (has_permission('live.stream.basic')) {
$menu[] = ['title' => 'Go Live', 'url' => '/live', 'permission' => 'live.stream.basic'];
}
if (has_permission('analytics.view')) {
$menu[] = ['title' => 'Analytics', 'url' => '/analytics', 'permission' => 'analytics.view'];
}
// Admin menu items
if (has_permission('admin.dashboard')) {
$menu[] = ['title' => 'Admin', 'url' => '/admin', 'permission' => 'admin.dashboard'];
}
// Filter menu items based on permissions
return array_filter($menu, function($item) {
return has_permission($item['permission']);
});
}
/**
* Get user role display name
* @param string $role Role name
* @return string Display name
*/
function get_role_display_name($role)
{
$displayNames = [
VRBAC::ROLE_GUEST => 'Guest',
VRBAC::ROLE_MEMBER => 'Member',
VRBAC::ROLE_VERIFIED => 'Verified User',
VRBAC::ROLE_CREATOR => 'Content Creator',
VRBAC::ROLE_MODERATOR => 'Moderator',
VRBAC::ROLE_ADMIN => 'Administrator',
VRBAC::ROLE_SUPER_ADMIN => 'Super Administrator'
];
return $displayNames[$role] ?? ucfirst(str_replace('_', ' ', $role));
}
/**
* Get available roles for assignment
* @return array Available roles
*/
function get_assignable_roles()
{
$roles = [
VRBAC::ROLE_MEMBER => 'Member',
VRBAC::ROLE_VERIFIED => 'Verified User',
VRBAC::ROLE_CREATOR => 'Content Creator'
];
// Add moderator and admin roles if user has permission
if (has_permission('admin.users.manage')) {
$roles[VRBAC::ROLE_MODERATOR] = 'Moderator';
if (user_has_role(VRBAC::ROLE_SUPER_ADMIN)) {
$roles[VRBAC::ROLE_ADMIN] = 'Administrator';
}
}
return $roles;
}
/**
* Check content ownership or permission
* @param string $resourceType Resource type (video, channel, playlist)
* @param string $resourceId Resource ID
* @param string $permission Required permission
* @return bool True if user owns resource or has permission
*/
function can_access_resource($resourceType, $resourceId, $permission)
{
$context = [
'resource_type' => $resourceType,
'resource_id' => $resourceId
];
return has_permission($permission, $context);
}
/**
* Get user's highest role
* @param int $userId User ID (optional)
* @return string Highest role
*/
function get_highest_role($userId = null)
{
$roles = get_user_roles($userId);
$roleHierarchy = [
VRBAC::ROLE_SUPER_ADMIN => 7,
VRBAC::ROLE_ADMIN => 6,
VRBAC::ROLE_MODERATOR => 5,
VRBAC::ROLE_CREATOR => 4,
VRBAC::ROLE_VERIFIED => 3,
VRBAC::ROLE_MEMBER => 2,
VRBAC::ROLE_GUEST => 1
];
$highestRole = VRBAC::ROLE_GUEST;
$highestLevel = 0;
foreach ($roles as $role) {
$level = $roleHierarchy[$role] ?? 0;
if ($level > $highestLevel) {
$highestLevel = $level;
$highestRole = $role;
}
}
return $highestRole;
}
/**
* Create permission middleware for routes
* @param string $permission Required permission
* @param array $context Permission context
* @return callable Middleware function
*/
function permission_middleware($permission, $context = [])
{
return function() use ($permission, $context) {
if (!has_permission($permission, $context)) {
http_response_code(403);
if (isset($_SESSION['USER_ID'])) {
// Logged in user without permission
include _FPATH . 'f_templates/tpl_frontend/tpl_error/tpl_403.tpl';
} else {
// Not logged in - redirect to login
header('Location: /signin?redirect=' . urlencode($_SERVER['REQUEST_URI']));
}
exit;
}
};
}
/**
* Get permission-based upload limits
* @return array Upload limits
*/
function get_upload_limits()
{
$limits = [
'max_file_size' => 50 * 1024 * 1024, // 50MB default
'max_duration' => 600, // 10 minutes default
'allowed_formats' => ['mp4', 'avi', 'mov'],
'max_uploads_per_day' => 5
];
if (has_permission('user.upload.advanced')) {
$limits['max_file_size'] = 500 * 1024 * 1024; // 500MB
$limits['max_duration'] = 3600; // 1 hour
$limits['allowed_formats'] = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'];
$limits['max_uploads_per_day'] = 20;
}
if (user_has_role(VRBAC::ROLE_CREATOR)) {
$limits['max_file_size'] = 2 * 1024 * 1024 * 1024; // 2GB
$limits['max_duration'] = 7200; // 2 hours
$limits['max_uploads_per_day'] = 50;
}
if (is_admin()) {
$limits['max_file_size'] = -1; // Unlimited
$limits['max_duration'] = -1; // Unlimited
$limits['max_uploads_per_day'] = -1; // Unlimited
}
return $limits;
}
/**
* Log security event
* @param string $event Event description
* @param array $context Event context
*/
if (!function_exists('log_security_event')) {
function log_security_event($event, $context = [])
{
VSecurity::logSecurityEvent($event, $context);
}
}

View File

@@ -0,0 +1,169 @@
<?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.
|*******************************************************************************************************************/
defined('_ISVALID') or header('Location: /error');
/**
* Security helper functions
*/
/**
* Secure output escaping for templates
* @param string $string String to escape
* @return string Escaped string
*/
function secure_output($string)
{
return VSecurity::escapeOutput($string);
}
/**
* Secure JavaScript output escaping
* @param string $string String to escape for JS
* @return string Escaped string
*/
function secure_js($string)
{
return VSecurity::escapeJS($string);
}
/**
* Generate CSRF token field for forms
* @param string $action Action name
* @return string HTML input field
*/
function csrf_field($action = 'default')
{
return VSecurity::getCSRFField($action);
}
/**
* Validate CSRF token from current request
* @param string $action Action name
* @return bool True if valid
*/
function validate_csrf($action = 'default')
{
return VSecurity::validateCSRFFromPost($action);
}
/**
* Get secure parameter from GET
* @param string $key Parameter name
* @param string $type Parameter type
* @param mixed $default Default value
* @param array $options Validation options
* @return mixed Sanitized value
*/
function get_param($key, $type = 'string', $default = null, $options = [])
{
return VSecurity::getParam($key, $type, $default, $options);
}
/**
* Get secure parameter from POST
* @param string $key Parameter name
* @param string $type Parameter type
* @param mixed $default Default value
* @param array $options Validation options
* @return mixed Sanitized value
*/
function post_param($key, $type = 'string', $default = null, $options = [])
{
return VSecurity::postParam($key, $type, $default, $options);
}
/**
* Check rate limiting
* @param string $key Unique identifier
* @param int $maxAttempts Maximum attempts
* @param int $timeWindow Time window in seconds
* @return bool True if within limits
*/
function check_rate_limit($key, $maxAttempts = 10, $timeWindow = 300)
{
return VSecurity::checkRateLimit($key, $maxAttempts, $timeWindow);
}
/**
* Validate file upload securely
* @param array $file $_FILES array element
* @param array $allowedTypes Allowed MIME types
* @param int $maxSize Maximum file size
* @return array Validation result
*/
function validate_file_upload($file, $allowedTypes = [], $maxSize = 10485760)
{
return VSecurity::validateFileUpload($file, $allowedTypes, $maxSize);
}
/**
* Log security events
* @param string $event Event description
* @param array $context Additional context
*/
function log_security_event($event, $context = [])
{
$logger = VLogger::getInstance();
$logger->logSecurityEvent($event, $context);
}
/**
* Log application errors with context
* @param string $message Error message
* @param array $context Additional context
*/
function log_app_error($message, $context = [])
{
$errorHandler = VErrorHandler::getInstance();
$errorHandler->logApplicationError($message, $context);
}
/**
* Log validation errors
* @param string $field Field name
* @param mixed $value Field value
* @param string $rule Validation rule
* @param array $context Additional context
*/
function log_validation_error($field, $value, $rule, $context = [])
{
$errorHandler = VErrorHandler::getInstance();
$errorHandler->logValidationError($field, $value, $rule, $context);
}
/**
* Log authentication errors
* @param string $message Error message
* @param string $username Username (if available)
* @param array $context Additional context
*/
function log_auth_error($message, $username = null, $context = [])
{
$errorHandler = VErrorHandler::getInstance();
$errorHandler->logAuthError($message, $username, $context);
}
/**
* Log performance issues
* @param string $message Performance issue description
* @param float $executionTime Execution time in seconds
* @param array $context Additional context
*/
function log_performance_issue($message, $executionTime, $context = [])
{
$logger = VLogger::getInstance();
$logger->logPerformanceIssue($message, $executionTime, $context);
}

View File

@@ -0,0 +1,304 @@
<?php
/*******************************************************************************************************************
| Video Processing Helper Functions
| Convenience functions for video processing integration
|*******************************************************************************************************************/
defined('_ISVALID') or header('Location: /error');
/**
* Queue video for processing
* @param string $videoKey Video key
* @param string $inputFile Input file path
* @param array $options Processing options
* @return string|false Job ID or false on failure
*/
function queue_video_processing($videoKey, $inputFile, $options = [])
{
try {
$processor = new VVideoProcessor();
return $processor->queueVideoProcessing($inputFile, $videoKey, $options);
} catch (Exception $e) {
VLogger::getInstance()->error('Failed to queue video processing', [
'video_key' => $videoKey,
'input_file' => $inputFile,
'error' => $e->getMessage()
]);
return false;
}
}
/**
* Process video immediately (synchronous)
* @param string $videoKey Video key
* @param string $inputFile Input file path
* @param array $options Processing options
* @return array Processing result
*/
function process_video_sync($videoKey, $inputFile, $options = [])
{
try {
$processor = new VVideoProcessor();
return $processor->processVideo($inputFile, $videoKey, $options);
} catch (Exception $e) {
VLogger::getInstance()->error('Synchronous video processing failed', [
'video_key' => $videoKey,
'input_file' => $inputFile,
'error' => $e->getMessage()
]);
return [
'success' => false,
'error' => $e->getMessage(),
'video_key' => $videoKey
];
}
}
/**
* Get video processing status
* @param string $videoKey Video key
* @return array|false Processing status or false if not found
*/
function get_video_processing_status($videoKey)
{
try {
$db = VDatabase::getInstance();
$query = "SELECT processing_status, processed_at, processing_error,
available_formats, hls_available, thumbnails_generated
FROM db_videofiles WHERE file_key = ?";
$result = $db->doQuery($query, [$videoKey]);
$row = $db->doFetch($result);
if ($row) {
return [
'status' => $row['processing_status'],
'processed_at' => $row['processed_at'],
'error' => $row['processing_error'],
'available_formats' => json_decode($row['available_formats'], true) ?: [],
'hls_available' => (bool)$row['hls_available'],
'thumbnails_generated' => (bool)$row['thumbnails_generated']
];
}
return false;
} catch (Exception $e) {
VLogger::getInstance()->error('Failed to get video processing status', [
'video_key' => $videoKey,
'error' => $e->getMessage()
]);
return false;
}
}
/**
* Queue thumbnail generation
* @param string $fileKey File key
* @param string $filePath File path
* @param string $fileType File type (video, image)
* @param array $sizes Thumbnail sizes
* @return string|false Job ID or false on failure
*/
function queue_thumbnail_generation($fileKey, $filePath, $fileType, $sizes = [])
{
try {
$queue = new VQueue();
$jobData = [
'file_key' => $fileKey,
'file_path' => $filePath,
'file_type' => $fileType,
'sizes' => $sizes ?: [
'small' => [160, 120],
'medium' => [320, 240],
'large' => [640, 480]
]
];
return $queue->enqueue('ThumbnailGenerationJob', $jobData, 'thumbnails');
} catch (Exception $e) {
VLogger::getInstance()->error('Failed to queue thumbnail generation', [
'file_key' => $fileKey,
'file_path' => $filePath,
'error' => $e->getMessage()
]);
return false;
}
}
/**
* Queue notification
* @param string $type Notification type (email, push, in_app, sms, webhook)
* @param string $recipient Recipient (email, user ID, phone, URL)
* @param string $subject Subject/title
* @param string $message Message content
* @param array $templateData Additional template data
* @param string $priority Priority (low, normal, high, urgent)
* @return string|false Job ID or false on failure
*/
function queue_notification($type, $recipient, $subject, $message, $templateData = [], $priority = 'normal')
{
try {
$queue = new VQueue();
$jobData = [
'type' => $type,
'recipient' => $recipient,
'subject' => $subject,
'message' => $message,
'template_data' => $templateData,
'priority' => $priority
];
$queuePriority = $priority === 'urgent' ? 2 : ($priority === 'high' ? 1 : 0);
return $queue->enqueue('NotificationJob', $jobData, 'notifications', 0, $queuePriority);
} catch (Exception $e) {
VLogger::getInstance()->error('Failed to queue notification', [
'type' => $type,
'recipient' => $recipient,
'error' => $e->getMessage()
]);
return false;
}
}
/**
* Get queue statistics
* @param string $queue Queue name (optional)
* @return array Queue statistics
*/
function get_queue_stats($queue = null)
{
try {
$queueInstance = new VQueue();
return $queueInstance->getQueueStats($queue);
} catch (Exception $e) {
VLogger::getInstance()->error('Failed to get queue statistics', [
'queue' => $queue,
'error' => $e->getMessage()
]);
return [];
}
}
/**
* Check if video processing is available
* @return bool True if FFmpeg is available
*/
function is_video_processing_available()
{
static $available = null;
if ($available === null) {
// Check if FFmpeg is available
$output = shell_exec('ffmpeg -version 2>&1');
$available = $output && strpos($output, 'ffmpeg version') !== false;
}
return $available;
}
/**
* Get supported video formats
* @return array Supported formats
*/
function get_supported_video_formats()
{
return [
'mp4' => 'MP4 Video',
'avi' => 'AVI Video',
'mov' => 'QuickTime Video',
'wmv' => 'Windows Media Video',
'flv' => 'Flash Video',
'webm' => 'WebM Video',
'mkv' => 'Matroska Video',
'3gp' => '3GP Video'
];
}
/**
* Get video processing formats
* @return array Available processing formats
*/
function get_video_processing_formats()
{
return [
'1080p' => ['width' => 1920, 'height' => 1080, 'bitrate' => '5000k'],
'720p' => ['width' => 1280, 'height' => 720, 'bitrate' => '2500k'],
'480p' => ['width' => 854, 'height' => 480, 'bitrate' => '1000k'],
'360p' => ['width' => 640, 'height' => 360, 'bitrate' => '750k'],
'240p' => ['width' => 426, 'height' => 240, 'bitrate' => '400k']
];
}
/**
* Estimate video processing time
* @param string $inputFile Input file path
* @param array $formats Formats to process
* @return int Estimated processing time in seconds
*/
function estimate_video_processing_time($inputFile, $formats = ['720p', '480p', '360p'])
{
if (!file_exists($inputFile)) {
return 0;
}
$fileSize = filesize($inputFile);
$fileSizeMB = $fileSize / (1024 * 1024);
// Rough estimation: 1MB takes about 2-5 seconds per format
$baseTimePerMB = 3; // seconds
$timePerFormat = $fileSizeMB * $baseTimePerMB;
return (int)($timePerFormat * count($formats));
}
/**
* Clean up old processed files
* @param int $daysOld Days old threshold
* @return int Number of files cleaned
*/
function cleanup_old_processed_files($daysOld = 30)
{
try {
$processedDir = _FPATH . 'f_data/processed/';
$cutoffTime = time() - ($daysOld * 24 * 60 * 60);
$cleanedCount = 0;
if (!is_dir($processedDir)) {
return 0;
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($processedDir),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $file) {
if ($file->isFile() && $file->getMTime() < $cutoffTime) {
if (unlink($file->getPathname())) {
$cleanedCount++;
}
}
}
VLogger::getInstance()->info('Cleaned up old processed files', [
'days_old' => $daysOld,
'files_cleaned' => $cleanedCount
]);
return $cleanedCount;
} catch (Exception $e) {
VLogger::getInstance()->error('Failed to cleanup old processed files', [
'days_old' => $daysOld,
'error' => $e->getMessage()
]);
return 0;
}
}