diff --git a/objects/functions.php b/objects/functions.php index 90f3d74696..703c8a02f3 100644 --- a/objects/functions.php +++ b/objects/functions.php @@ -7265,7 +7265,7 @@ function calculateCenterCrop($originalWidth, $originalHeight, $aspectRatio) return ['newWidth' => intval($newWidth), 'newHeight' => intval($newHeight), 'x' => intval($x), 'y' => intval($y)]; } -function getTourHelpButton($stepsFileRelativePath, $class = 'btn btn-default') +function getTourHelpButton($stepsFileRelativePath, $class = 'btn btn-default', $showLabel = true) { /* [ @@ -7279,7 +7279,11 @@ function getTourHelpButton($stepsFileRelativePath, $class = 'btn btn-default') } ] */ - return ""; + $label = ''; + if($showLabel){ + $label = __('Help'); + } + return ""; } function getInfoButton($info) diff --git a/plugin/Live/Objects/LiveTransmition.php b/plugin/Live/Objects/LiveTransmition.php index 7ce54bb871..dce11a3952 100644 --- a/plugin/Live/Objects/LiveTransmition.php +++ b/plugin/Live/Objects/LiveTransmition.php @@ -140,7 +140,7 @@ class LiveTransmition extends ObjectYPT { return true; } - public static function getFromDbByUser($user_id, $refreshCache = false) { + public static function getFromDbByUser($user_id, $refreshCache = false, $allowOnlineIndex = false) { global $global; if (!self::isTableInstalled(static::getTableName())) { _error_log("Save error, table " . static::getTableName() . " does not exists", AVideoLog::$ERROR); @@ -162,7 +162,7 @@ class LiveTransmition extends ObjectYPT { $data['live_servers_id'] = Live::getLiveServersIdRequest(); $liveStreamObject = new LiveStreamObject($data['key'], $data['live_servers_id']); } - $data['key_with_index'] = $liveStreamObject->getKeyWithIndex(true); + $data['key_with_index'] = $liveStreamObject->getKeyWithIndex(true, $allowOnlineIndex); $data['live_index'] = $liveStreamObject->getIndex(); $user = $data; @@ -223,7 +223,7 @@ class LiveTransmition extends ObjectYPT { return LiveTransmition::getFromDbByUserName($_REQUEST['u']); } elseif (!empty($_REQUEST['c'])) { //_error_log('LiveTransmition::getFromRequest line'.__LINE__); - return LiveTransmition::getFromDbByChannelName($_REQUEST['c']); + return LiveTransmition::getFromDbByChannelName($_REQUEST['c'], true); } //_error_log('LiveTransmition::getFromRequest line'.__LINE__); return false; @@ -249,7 +249,7 @@ class LiveTransmition extends ObjectYPT { } } - public static function getFromDbByChannelName($channelName) { + public static function getFromDbByChannelName($channelName, $allowOnlineIndex = false) { global $global; _mysql_connect(); $sql = "SELECT * FROM users WHERE channelName = ? LIMIT 1"; @@ -261,7 +261,7 @@ class LiveTransmition extends ObjectYPT { if (empty($user)) { return false; } - return static::getFromDbByUser($user['id']); + return static::getFromDbByUser($user['id'], false, $allowOnlineIndex); } else { return false; } diff --git a/plugin/Live/Objects/LiveTransmitionHistory.php b/plugin/Live/Objects/LiveTransmitionHistory.php index 15fc85705a..031b1bbe94 100644 --- a/plugin/Live/Objects/LiveTransmitionHistory.php +++ b/plugin/Live/Objects/LiveTransmitionHistory.php @@ -211,7 +211,7 @@ class LiveTransmitionHistory extends ObjectYPT $users_id = $lth->getUsers_id(); $key = $lth->getKey(); $title = $lth->getTitle(); - $live_servers_id = $lth->getLive_servers_id(); + $live_servers_id = intval($lth->getLive_servers_id()); $playlists_id_live = 0; $type = 'LiveObject'; @@ -252,6 +252,7 @@ class LiveTransmitionHistory extends ObjectYPT $obj = Live::getLiveApplicationModelArray($array); $obj['key'] = $key; + $obj['live_servers_id'] = $live_servers_id; $obj['live_transmitions_history_id'] = $liveTransmitionHistory_id; $obj['isPrivate'] = self::isPrivate($liveTransmitionHistory_id); $obj['isPasswordProtected'] = self::isPasswordProtected($liveTransmitionHistory_id); @@ -402,7 +403,7 @@ class LiveTransmitionHistory extends ObjectYPT $sql .= " AND (total_viewers>0 OR (SELECT count(id) FROM live_transmition_history_log WHERE live_transmitions_history_id=lth.id )>0) "; } $limit = intval($limit); - if(!empty($limit)){ + if (!empty($limit)) { $sql .= "ORDER BY CASE WHEN finished IS NULL THEN 0 @@ -410,7 +411,7 @@ class LiveTransmitionHistory extends ObjectYPT END, modified DESC LIMIT {$limit}"; - }else{ + } else { $sql .= self::getSqlFromPost(); } //echo $sql;exit; @@ -419,8 +420,8 @@ class LiveTransmitionHistory extends ObjectYPT sqlDAL::close($res); $rows = []; if ($res != false) { - foreach ($fullData as $row) { - $row['activeStatus'] = empty($row['finished'])?__('Active'):__('Inactive'); + foreach ($fullData as $row) { + $row['activeStatus'] = empty($row['finished']) ? __('Active') : __('Inactive'); if (empty($row['total_viewers'])) { $row['total_viewers'] = $row['total_viewers_from_history']; } @@ -497,7 +498,7 @@ class LiveTransmitionHistory extends ObjectYPT $row = $data; } else { if (is_int($active)) { - _error_log("LiveTransmitionHistory::getLatest not found ($key, $live_servers_id, $active, $users_id, $categories_id) ".$sql); + _error_log("LiveTransmitionHistory::getLatest not found ($key, $live_servers_id, $active, $users_id, $categories_id) " . $sql); } $row = false; } @@ -516,7 +517,7 @@ class LiveTransmitionHistory extends ObjectYPT public static function finishFromTransmitionHistoryId($live_transmitions_history_id) { - if(isBot(false)){ + if (isBot(false)) { return false; } global $global; @@ -529,7 +530,7 @@ class LiveTransmitionHistory extends ObjectYPT $sql = "UPDATE " . static::getTableName() . " SET finished = now() WHERE id = {$live_transmitions_history_id} "; $insert_row = sqlDAL::writeSql($sql); - _error_log("LiveTransmitionHistory::finishFromTransmitionHistoryId: live_transmitions_history_id=$live_transmitions_history_id users_id=". User::getId() .' IP='. getRealIpAddr(). ' ' . $_SERVER['HTTP_USER_AGENT'] . ' '. json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS))); + _error_log("LiveTransmitionHistory::finishFromTransmitionHistoryId: live_transmitions_history_id=$live_transmitions_history_id users_id=" . User::getId() . ' IP=' . getRealIpAddr() . ' ' . $_SERVER['HTTP_USER_AGENT'] . ' ' . json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS))); _mysql_commit(); @@ -557,7 +558,7 @@ class LiveTransmitionHistory extends ObjectYPT public static function unfinishFromTransmitionHistoryId($live_transmitions_history_id) { - if(isBot(false)){ + if (isBot(false)) { //_error_log("LiveTransmitionHistory::unfinishFromTransmitionHistoryId: isBot "); return false; } @@ -576,7 +577,7 @@ class LiveTransmitionHistory extends ObjectYPT public static function finishALL($olderThan = '') { - if(isBot(false)){ + if (isBot(false)) { return false; } $sql = "UPDATE " . static::getTableName() . " SET finished = now() WHERE finished IS NULL "; @@ -833,7 +834,7 @@ class LiveTransmitionHistory extends ObjectYPT if (empty($this->id)) { $activeLive = self::getLatest($this->key, $this->live_servers_id, LiveTransmitionHistory::$reconnectionTimeoutInMinutes); if (!empty($activeLive)) { - if($activeLive['key'] == $this->key){ + if ($activeLive['key'] == $this->key) { //_error_log("LiveTransmitionHistory::save: active live found $this->key, $this->live_servers_id " . json_encode($activeLive)); foreach ($activeLive as $key => $value) { if (empty($this->$key)) { @@ -843,8 +844,8 @@ class LiveTransmitionHistory extends ObjectYPT } self::unfinishFromTransmitionHistoryId($activeLive['id']); $this->finished = null; - }else{ - // _error_log("LiveTransmitionHistory::save: active live NOT match $this->key, $this->live_servers_id " . _json_encode(array($this->key, $this->live_servers_id, $activeLive))); + } else { + // _error_log("LiveTransmitionHistory::save: active live NOT match $this->key, $this->live_servers_id " . _json_encode(array($this->key, $this->live_servers_id, $activeLive))); } } else { //_error_log("LiveTransmitionHistory::save: active live NOT found $this->key, $this->live_servers_id " . _json_encode(array($this->key, $this->live_servers_id, $activeLive))); diff --git a/plugin/Live/index.php b/plugin/Live/index.php index 30895cf06e..f653adf5a7 100644 --- a/plugin/Live/index.php +++ b/plugin/Live/index.php @@ -39,7 +39,7 @@ if (!empty($_GET['u'])) { $link = addQueryStringParameter($link, 'return_line', $info['return_line']); //var_dump($link, $info['otherLivesSameUser'][0]);exit; //var_dump($link,$info['users_id'], $info['otherLivesSameUser']);exit; - _error_log("Redirecting to {$link} info=".json_encode($info).' $_REQUEST='.json_encode($_REQUEST)); + _error_log("Redirecting to {$link} info=" . json_encode($info) . ' $_REQUEST=' . json_encode($_REQUEST)); header("Location: {$link}"); exit; /* @@ -84,6 +84,7 @@ if (!empty($_GET['users_id']) && User::isAdmin()) { // if user already have a key $trasnmition = LiveTransmition::createTransmitionIfNeed($users_id); +//var_dump($trasnmition);exit; $getLiveKey = ['key' => $trasnmition['key'], 'live_servers_id' => Live::getLiveServersIdRequest()]; setLiveKey($trasnmition['key'], Live::getLiveServersIdRequest(), @$_REQUEST['live_index']); if (!empty($_GET['resetKey'])) { @@ -165,11 +166,11 @@ include $global['systemRootPath'] . 'view/bootstrap/fileinput.php'; if (User::isAdmin()) { ?> - + ?> @@ -199,6 +200,9 @@ include $global['systemRootPath'] . 'view/bootstrap/fileinput.php'; $getLiveKey['live_servers_id'] = $_REQUEST['live_servers_id']; $getLiveKey['live_index'] = @$_REQUEST['live_index']; $poster = Live::getPosterImage(User::getId(), $_REQUEST['live_servers_id']); + + $liveStreamObject = new LiveStreamObject($trasnmition['key'], $trasnmition['live_servers_id']); + Live::getLiveControls($liveStreamObject->getKeyWithIndex(true,true), $trasnmition['live_servers_id']); ?> @@ -243,7 +247,7 @@ include $global['systemRootPath'] . 'view/bootstrap/fileinput.php'; }); $.ajax({ - url: webSiteRootURL+'plugin/Live/saveLive.php', + url: webSiteRootURL + 'plugin/Live/saveLive.php', data: { "title": $('#title').val(), "description": $('#description').val(), diff --git a/plugin/Live/myLiveControls.json b/plugin/Live/myLiveControls.json new file mode 100644 index 0000000000..32fabdb08f --- /dev/null +++ b/plugin/Live/myLiveControls.json @@ -0,0 +1,14 @@ +[ + { + "element": "#sendViewersControls", + "intro": "Welcome to the Viewer Redirect feature! This allows you to send your live viewers to another stream or page when you're ending your broadcast." + }, + { + "element": "#promptForUrlBtn", + "intro": "First, click this button to select or enter a URL where you want to redirect your viewers. You can choose from a list of active streams or enter a custom URL." + }, + { + "element": "#sendViewersButtons", + "intro": "When you're ready to redirect your viewers, click this button. Note: This will also stop your live stream." + } +] diff --git a/plugin/Live/myLiveControls.php b/plugin/Live/myLiveControls.php new file mode 100644 index 0000000000..5dbb9dfa49 --- /dev/null +++ b/plugin/Live/myLiveControls.php @@ -0,0 +1,242 @@ +.{$class} {display: none;}body.{$bodyClass} .{$class} {display: block !important;}body.{$bodyClass} .{$hideClass} {display: none !important;}"; +} + +$row = LiveTransmition::keyExists($live_key); +if(empty($row)){ + echo ''; + return; +} +if(User::getId() != $row['users_id'] && !User::isAdmin()){ + echo ''; + return; +} +?> + +
+ +
+ + +
+ + + + +
+
+ + +
+ + + + + \ No newline at end of file diff --git a/plugin/Live/sendViewers.json.php b/plugin/Live/sendViewers.json.php new file mode 100644 index 0000000000..c64a7a0ef6 --- /dev/null +++ b/plugin/Live/sendViewers.json.php @@ -0,0 +1,36 @@ +row = $row; +$obj->viewerUrl = $_REQUEST['viewerUrl']; +$obj->live_key = $_REQUEST['live_key']; +$obj->live_servers_id = intval(@$_REQUEST['live_servers_id']); + +$obj->sendSocketMessage = sendSocketMessage(array('redirectLive'=>$obj), 'redirectLive', 0); + +$obj->msg = ''; +$obj->error = false; +$obj->dropURL = Live::getDropURL($obj->live_key, $obj->live_servers_id); +$obj->dropURLResponse = _json_decode(url_get_contents($obj->dropURL)); + +die(_json_encode($obj)); diff --git a/plugin/Live/standAloneFiles/restreamer.json.php b/plugin/Live/standAloneFiles/restreamer.json.php index f29d2da87d..f28ebbf7a6 100644 --- a/plugin/Live/standAloneFiles/restreamer.json.php +++ b/plugin/Live/standAloneFiles/restreamer.json.php @@ -567,7 +567,7 @@ function startRestream($m3u8, $restreamsDestinations, $logFile, $robj, $tries = */ - $FFMPEGcommand = "{$ffmpegBinary} -re -rw_timeout 30000000 -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 30 -y -i \"{$m3u8}\" -preset veryfast "; + $FFMPEGcommand = "{$ffmpegBinary} -re -rw_timeout 30000000 -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 30 -y -i \"{$m3u8}\" -preset veryfast "; $FFMPEGComplement = " -max_muxing_queue_size 1024 " . '{audioConfig}' . "-c:v libx264 " diff --git a/plugin/Live/standAloneFiles/saveDVR.json.php b/plugin/Live/standAloneFiles/saveDVR.json.php index 3b0ef214e1..27e514115f 100644 --- a/plugin/Live/standAloneFiles/saveDVR.json.php +++ b/plugin/Live/standAloneFiles/saveDVR.json.php @@ -1,82 +1,4 @@ = 0; $i--) { - if (preg_match('/[0-9]+.ts$/', $array[$i])) { - if ($total) { - $total--; - } else { - unset($array[$i]); - unset($array[$i - 1]); - } - $i--; - } - } - - $newcontent = implode(PHP_EOL, $array); - error_log("setLastSegments 2 " . json_encode($newcontent)); - $bytes = file_put_contents($DVRFile, $newcontent); - error_log("setLastSegments 3 " . $bytes); -} - // this file MUST be on the same directory as getRecordedFile.php $hls_path = "/HLS/live/"; //update this URL @@ -250,3 +172,81 @@ if ($return_var !== 0) { } else { error_log("saveDVR: Temporary directory removed successfully"); } + + +// Define the copyDirectory function +function copyDirectory($src, $dst) { + if (!file_exists($src)) { + error_log("copyDirectory: Source file or directory does not exist: $src"); + return false; + } + if (is_dir($src)) { + @mkdir($dst, 0777, true); + $dir = opendir($src); + if (!$dir) { + error_log("copyDirectory: Failed to open directory: $src"); + return false; + } + while (false !== ($file = readdir($dir))) { + if ($file != '.' && $file != '..') { + $srcPath = $src . DIRECTORY_SEPARATOR . $file; + $dstPath = $dst . DIRECTORY_SEPARATOR . $file; + if (is_dir($srcPath)) { + if (!copyDirectory($srcPath, $dstPath)) { + return false; + } + } else { + if (!copy($srcPath, $dstPath)) { + error_log("copyDirectory: Failed to copy file $srcPath to $dstPath"); + return false; + } + chmod($dstPath, 0777); + } + } + } + closedir($dir); + return true; + } else { + // $src is a file + @mkdir(dirname($dst), 0777, true); + if (copy($src, $dst)) { + chmod($dst, 0777); + return true; + } else { + error_log("copyDirectory: Failed to copy file $src to $dst"); + return false; + } + } +} + +function setLastSegments($DVRFile, $total) +{ + $parts = explode(DIRECTORY_SEPARATOR, $DVRFile); + array_pop($parts); + $dir = implode(DIRECTORY_SEPARATOR, $parts) . DIRECTORY_SEPARATOR; + + $text = file_get_contents($DVRFile); + + error_log("setLastSegments 1 $dir $DVRFile, $total " . json_encode($text)); + if (empty($total)) { + return $text; + } + + $array = preg_split('/$\R?^/m', $text); + for ($i = count($array) - 1; $i >= 0; $i--) { + if (preg_match('/[0-9]+.ts$/', $array[$i])) { + if ($total) { + $total--; + } else { + unset($array[$i]); + unset($array[$i - 1]); + } + $i--; + } + } + + $newcontent = implode(PHP_EOL, $array); + error_log("setLastSegments 2 " . json_encode($newcontent)); + $bytes = file_put_contents($DVRFile, $newcontent); + error_log("setLastSegments 3 " . $bytes); +} diff --git a/plugin/Live/view/live.css b/plugin/Live/view/live.css index e69de29bb2..402482352f 100644 --- a/plugin/Live/view/live.css +++ b/plugin/Live/view/live.css @@ -0,0 +1,23 @@ + +/* Default state when body does not have IAmOnline or IAmNOTOnline classes */ +.ShowIfIAmOnline, .ShowIfIAmNOTOnline { + display: none; /* Hide elements by default */ +} + +/* Show elements only if the body has class 'IAmOnline' */ +body.IAmOnline .ShowIfIAmOnline { + display: block !important; /* Show the element */ +} + +body.IAmOnline .HideIfIAmOnline { + display: none !important; /* Hide the element */ +} + +/* Show elements only if the body has class 'IAmNOTOnline' */ +body.IAmNOTOnline .ShowIfIAmNOTOnline { + display: block !important; /* Show the element */ +} + +body.IAmNOTOnline .HideIfIAmNOTOnline { + display: none !important; /* Hide the element */ +} diff --git a/plugin/Live/view/menuRight.php b/plugin/Live/view/menuRight.php index 63eca55d0d..8f17ed5d9f 100644 --- a/plugin/Live/view/menuRight.php +++ b/plugin/Live/view/menuRight.php @@ -79,8 +79,49 @@ if (User::canStream()) { }, 1000); // give some time to load the new images } + var IAmOnline = false; + var my_current_live_key = 0; + var my_current_live_servers_id = 0; + async function amILive(stats) { + IAmOnline = false; + for (var i in stats) { + if (typeof stats[i] !== 'object') { + continue; + } + var stat = stats[i]; + console.log('amILive', stat); + if (stat.type == 'live' || stat.type == "LiveObject") { + if (stat.users_id == my_users_id) { + my_current_live_key = stat.key; + my_current_live_servers_id = stat.live_servers_id; + IAmOnline = true; + break; + } + } + } + console.log('amILive', IAmOnline, stats); + $('body').each(function() { + var classes = $(this).attr('class').split(' ').filter(function(c) { + return !c.startsWith('body_live_'); + }); + $(this).attr('class', classes.join(' ')); + }); + + if (IAmOnline) { + $('body').addClass('IAmOnline'); + $('body').removeClass('IAmNOTOnline'); + $('body').addClass('body_live_' + my_current_live_servers_id + '_' + my_current_live_key); + } else { + $('body').removeClass('IAmOnline'); + $('body').addClass('IAmNOTOnline'); + live_transmitions_history_id = 0; + } + } + var _processLiveStats_processingNow = 0; + var lastProcessLiveStats = []; async function processLiveStats(response) { + lastProcessLiveStats = response; if (_processLiveStats_processingNow) { return false; } @@ -89,6 +130,8 @@ if (User::canStream()) { _processLiveStats_processingNow = 0; }, 200); if (typeof response !== 'undefined') { + console.trace('processApplication add notificationLiveItemRemoveThis'); + $('.stats_app').addClass('notificationLiveItemRemoveThis'); if (isArray(response)) { for (var i in response) { if (typeof response[i] !== 'object') { @@ -101,6 +144,7 @@ if (User::canStream()) { //console.log('processLiveStats not array', response); processApplicationLive(response); } + $('.notificationLiveItemRemoveThis').remove(); if (!response.countLiveStream) { availableLiveStreamNotFound(); } else { @@ -109,6 +153,7 @@ if (User::canStream()) { $('.onlineApplications').text($('#availableLiveStream > div').length); } + setTimeout(function() { playLiveInFullScreenOnIframe)) { @@ -154,6 +199,7 @@ if (User::canStream()) { } } + amILive(response.applications); if (typeof onlineLabelOnline == 'function' && response.applications.length) { //console.log('processApplicationLive 1', response.applications, response.applications.length); for (i = 0; i < response.applications.length; i++) { @@ -162,7 +208,9 @@ if (User::canStream()) { return false; } processApplication(response.applications[i]); + $('#' + response.applications[i].className).removeClass('notificationLiveItemRemoveThis'); if (!response.applications[i].comingsoon) { + console.log('processApplicationLive', response.applications[i]); if (typeof response.applications[i].live_cleanKey !== 'undefined') { selector = '.liveViewStatusClass_' + response.applications[i].live_cleanKey; onlineLabelOnline(selector); @@ -262,7 +310,10 @@ if (User::canStream()) { ////console.log('processApplication remove class .live_' + key); $('.live_' + key).remove(); } - if (!$('#' + notificatioID).length) { + var selector = '#' + notificatioID; + $(selector).removeClass('notificationLiveItemRemoveThis'); + console.log('processApplication notificatioID ', selector); + if (!$(selector).length) { notificationHTML.attr('id', notificatioID); if (application.comingsoon) { //console.log('application.comingsoon 1', application.comingsoon, application.method); @@ -292,7 +343,10 @@ if (User::canStream()) { ?> var id = $(html).attr('id').replace(/[&=]/g, ''); - if ($('#' + id).length) { + var selector = '#' + id; + $(selector).removeClass('notificationLiveItemRemoveThis'); + console.log('processApplication key ', selector); + if ($(selector).length) { //console.log('processApplication key found', id); return false; } @@ -341,7 +395,7 @@ if (User::canStream()) { itemsArray.image = application.poster; itemsArray.title = application.title; itemsArray.href = application.href; - itemsArray.element_class = application.className + ' ' + application.type + '_app'; + itemsArray.element_class = application.className + ' ' + application.type + '_app' + ' stats_app'; itemsArray.element_id = application.className; itemsArray.icon = 'fas fa-video'; itemsArray.type = 'info'; diff --git a/plugin/Live/view/modeYoutubeLive.php b/plugin/Live/view/modeYoutubeLive.php index 37d3c2449f..7fb71f70ff 100644 --- a/plugin/Live/view/modeYoutubeLive.php +++ b/plugin/Live/view/modeYoutubeLive.php @@ -21,7 +21,7 @@ if (!empty($_GET['c'])) { } $livet = LiveTransmition::getFromRequest(); -//var_dump($livet);exit; +//var_dump($livet, $_REQUEST);exit; setLiveKey($livet['key'], Live::getLiveServersIdRequest(), @$_REQUEST['live_index']); Live::checkIfPasswordIsGood($livet['key']); @@ -212,7 +212,10 @@ $_page->setExtraStyles( }); }); - + setExtraStyles( -print(); ?> \ No newline at end of file diff --git a/plugin/Live/view/socket.js b/plugin/Live/view/socket.js index 155a5e648a..e38dedec22 100644 --- a/plugin/Live/view/socket.js +++ b/plugin/Live/view/socket.js @@ -1,6 +1,14 @@ function socketLiveONCallback(json) { - console.log('socketLiveONCallback', json); - if(typeof processLiveStats == 'undefined'){ + if (typeof json === 'string') { + try { + json = JSON.parse(json); + } catch (error) { + console.error("Invalid JSON string:", error); + return null; // or return the original string if you prefer + } + } + console.log('socketLiveONCallback live plugin', json); + if (typeof processLiveStats == 'function') { processLiveStats(json.stats); } var selector = '.live_' + json.live_servers_id + "_" + json.key; @@ -31,7 +39,15 @@ function socketLiveONCallback(json) { } } function socketLiveOFFCallback(json) { - console.log('socketLiveOFFCallback', json); + if (typeof json === 'string') { + try { + json = JSON.parse(json); + } catch (error) { + console.error("Invalid JSON string:", error); + return null; // or return the original string if you prefer + } + } + console.log('socketLiveOFFCallback live socket', json); var selector = '.live_' + json.live_servers_id + "_" + json.key; selector += ', .liveVideo_live_' + json.live_servers_id + "_" + json.key; selector += ', .live_' + json.key; @@ -49,11 +65,13 @@ function socketLiveOFFCallback(json) { } setTimeout(function () { //console.log('socketLiveOFFCallback processLiveStats'); - if(typeof processLiveStats == 'undefined'){ + if (typeof processLiveStats == 'function') { processLiveStats(json.stats); } setTimeout(function () { - hideExtraVideosIfEmpty(); + if (typeof hideExtraVideosIfEmpty == 'function') { + hideExtraVideosIfEmpty(); + } }, 500); }, 500); @@ -63,4 +81,64 @@ function socketLiveOFFCallback(json) { if (typeof updateUserNotificationCount == 'function') { updateUserNotificationCount(); } -} \ No newline at end of file +} + +function redirectLive(json, countdown = 15) { + if (typeof json === 'string') { + try { + json = JSON.parse(json); + } catch (error) { + console.error("Invalid JSON string:", error); + return null; + } + } + + var viewerUrl = json.redirectLive && json.redirectLive.viewerUrl; + if (!viewerUrl) { + console.error("Viewer URL not found."); + return; + } + + var countdownInterval; + var initialCountdown = countdown; + + // Function to handle the redirection + function redirectToUrl(url) { + window.location.href = url; + } + + // Function to update the progress bar and auto-redirect + function startCountdown() { + countdownInterval = setInterval(function () { + countdown--; + + // Calculate the progress percentage + var progressPercent = ((initialCountdown - countdown) / initialCountdown) * 100; + + // Update the progress bar in the avideoConfirm modal with smooth transition + $('#countdownProgressBar').css({ + 'width': progressPercent + '%', + 'transition': 'width 1s linear' + }); + + if (countdown <= 0) { + clearInterval(countdownInterval); + redirectToUrl(viewerUrl); + } + }, 1000); + } + + // Use avideoConfirm to ask for user confirmation and include a progress bar + avideoConfirm(__("You will be redirected to the following URL:") + "
" + viewerUrl + "
").then(function (confirmed) { + if (confirmed) { + clearInterval(countdownInterval); // Stop countdown if user confirms + redirectToUrl(viewerUrl); + } else { + clearInterval(countdownInterval); // Stop countdown if user cancels + } + }); + + // Start countdown + startCountdown(); +} + diff --git a/plugin/Scheduler/watchDog.php b/plugin/Scheduler/watchDog.php index 9378b673ef..2ffa5d9c28 100644 --- a/plugin/Scheduler/watchDog.php +++ b/plugin/Scheduler/watchDog.php @@ -69,8 +69,10 @@ function secureAVideoFolder($folderPath = '/var/www/html/AVideo/videos') $htaccessVersion = '5.7'; // Define the .htaccess content with updated security rules - $htaccessContent = "# version $htaccessVersion -# SQL was required for the clone plugin" . PHP_EOL; + $htaccessContent = "# version $htaccessVersion". PHP_EOL; + $htaccessContent .= "# SQL was required for the clone plugin" . PHP_EOL; + $htaccessContent .= "# generated in ".date('Y-m-d H:i:s') . PHP_EOL; + $htaccessContent .= file_get_contents(__DIR__.'/htaccess.sample.txt'); // Path to .htaccess file $htaccessFile = $folderPath . '/.htaccess'; @@ -81,9 +83,10 @@ function secureAVideoFolder($folderPath = '/var/www/html/AVideo/videos') // Read the current .htaccess file content $currentContent = file_get_contents($htaccessFile); // Check if the version in the file matches the current version - if (!strpos($currentContent, "# version $htaccessVersion")) { + $version = "# version $htaccessVersion"; + if (strpos($currentContent, $version) === false) { $updateHtaccess = true; - _error_log(".htaccess version mismatch. Updating to version $htaccessVersion.\n"); + _error_log(".htaccess version mismatch $version. Updating to version $htaccessVersion.\n"); } else { _error_log(".htaccess file is already up-to-date.\n"); } diff --git a/plugin/UserNotifications/script.js b/plugin/UserNotifications/script.js index 0736f93917..0a6094ec5d 100644 --- a/plugin/UserNotifications/script.js +++ b/plugin/UserNotifications/script.js @@ -37,7 +37,10 @@ function addTemplateFromArray(itemsArray, prepend) { return false; } //console.log('addTemplateFromArray', itemsArray); + console.trace('processApplication addTemplateFromArray ', itemsArray); if (!empty(itemsArray.element_id) && $('#' + (itemsArray.element_id)).length) { + var selector = '#' + (itemsArray.element_id); + $(selector).removeClass('notificationLiveItemRemoveThis'); return false; } var template = getTemplateFromArray(itemsArray); diff --git a/plugin/YPTSocket/MessageSQLiteV2.php b/plugin/YPTSocket/MessageSQLiteV2.php index e89890fd2c..833c9ae5ea 100644 --- a/plugin/YPTSocket/MessageSQLiteV2.php +++ b/plugin/YPTSocket/MessageSQLiteV2.php @@ -289,6 +289,9 @@ class Message implements MessageComponentInterface { $this->msgToResourceId($json, $json['resourceId']); } else if (!empty($json['to_users_id'])) { $this->msgToUsers_id($json, $json['to_users_id']); + } else if (!empty($json['json']['redirectLive'])) { + _log_message("onMessage:msgToAllSameLive: " . json_encode($json)); + $this->msgToAllSameLive($json['json']['redirectLive']['live_key'], $json['json']['redirectLive']['live_servers_id'], $json); } else { $this->msgToAll($json); } @@ -350,6 +353,7 @@ class Message implements MessageComponentInterface { unset($msg['webSocketToken']); } if (empty($type)) { + _log_message("msgToResourceId: empty message type "); $type = \SocketMessageType::DEFAULT_MESSAGE; } @@ -404,7 +408,7 @@ class Message implements MessageComponentInterface { $obj['mem'] = Message::$mem; $msgToSend = json_encode($obj); - //_log_message("msgToResourceId: resourceId=({$resourceId}) {$type} users_id={$obj['users_id']}"); + _log_message("msgToResourceId: resourceId=({$resourceId}) {$type} users_id={$obj['users_id']}"); $this->clients[$resourceId]->send($msgToSend); return true; } @@ -542,13 +546,17 @@ class Message implements MessageComponentInterface { $this->msgToArray($msg); } + $live_servers_id = intval($live_servers_id); + $rows = dbGetAllResourcesIdFromLive($live_key, $live_servers_id); $totals = $this->getTotals(); + _log_message("msgToAllSameLive: {$live_key}_{$live_servers_id} total=".count($rows).' '.json_encode($msg)); foreach ($rows as $value) { - if($value['isCommandLine']){ + if(!empty($value['isCommandLine'])){ continue; } + _log_message("msgToAllSameLive: {$value['resourceId']} "); $this->msgToResourceId($msg, $value['resourceId'], \SocketMessageType::ON_LIVE_MSG, $totals); } } diff --git a/plugin/YPTSocket/YPTSocket.php b/plugin/YPTSocket/YPTSocket.php index 143b98a878..1cb2391c94 100644 --- a/plugin/YPTSocket/YPTSocket.php +++ b/plugin/YPTSocket/YPTSocket.php @@ -47,7 +47,7 @@ class YPTSocket extends PluginAbstract public static function getServerVersion() { - return "6.2"; + return "7.0"; } public function updateScript() diff --git a/plugin/YPTSocket/script.js b/plugin/YPTSocket/script.js index 2d1a74b6d4..16fafb6219 100644 --- a/plugin/YPTSocket/script.js +++ b/plugin/YPTSocket/script.js @@ -9,12 +9,19 @@ var users_id_online = undefined; var socketConnectRetryTimeout = 15000; function processSocketJson(json){ + if (json.type == webSocketTypes.UNDEFINED) { + console.log("processSocketJson UNDEFINED", json); + if(typeof json.msg === 'object' && typeof json.msg.callback === 'string'){ + console.log("Socket onmessage UNDEFINED process subobject", json.msg); + return processSocketJson(json.msg) + } + } if (json.type == webSocketTypes.ON_VIDEO_MSG) { - console.log("Socket onmessage ON_VIDEO_MSG", json); + console.log("processSocketJson ON_VIDEO_MSG", json); $('.videoUsersOnline, .videoUsersOnline_' + json.videos_id).text(json.total); } if (json.type == webSocketTypes.ON_LIVE_MSG && typeof json.is_live !== 'undefined') { - console.log("Socket onmessage ON_LIVE_MSG", json); + console.log("processSocketJson ON_LIVE_MSG", json); var selector = '#liveViewStatusID_' + json.live_key.key + '_' + json.live_key.live_servers_id; if (json.is_live) { onlineLabelOnline(selector); @@ -23,27 +30,27 @@ function processSocketJson(json){ } } if (json.type == webSocketTypes.NEW_CONNECTION) { - //console.log("Socket onmessage NEW_CONNECTION", json); + //console.log("processSocketJson NEW_CONNECTION", json); if (typeof onUserSocketConnect === 'function') { onUserSocketConnect(json); } } else if (json.type == webSocketTypes.NEW_DISCONNECTION) { - //console.log("Socket onmessage NEW_DISCONNECTION", json); + //console.log("processSocketJson NEW_DISCONNECTION", json); if (typeof onUserSocketDisconnect === 'function') { onUserSocketDisconnect(json); } } else { var myfunc; if (json.callback) { - //console.log("Socket onmessage json.callback ", json.resourceId, json.callback); + //console.log("processSocketJson json.callback ", json.resourceId, json.callback); var code = "if(typeof " + json.callback + " == 'function'){myfunc = " + json.callback + ";}else{myfunc = defaultCallback;}"; - //console.log(code); + console.log('processSocketJson: code='+code); eval(code); } else { - //console.log("onmessage: callback not found", json); + //console.log("processSocketJson: callback not found", json); myfunc = defaultCallback; } - //console.log("onmessage: callback ", myfunc, json); + //console.log("onmessage: callback ", myfunc, json.msg); myfunc(json.msg); } } diff --git a/view/css/social.css b/view/css/social.css index a9ad53298a..2bf8ea8c38 100644 --- a/view/css/social.css +++ b/view/css/social.css @@ -112,7 +112,7 @@ ul.social-network li { /* Define a base class for common styles */ .social-network a, .social-network button{ - border-color: var(--hover-color) !important; + border: #FFFFFF55 1px solid; color: var(--hover-color); /* Change text color on hover */ box-shadow: 0 0 1px 1px var(--hover-color); /* Outer glow effect */ transition: box-shadow 0.3s ease; /* Smooth transition for the glow effect */ diff --git a/view/include/social.php b/view/include/social.php index ca09dfe4b2..2cefca3d7d 100644 --- a/view/include/social.php +++ b/view/include/social.php @@ -2,6 +2,7 @@ global $socialAdded, $global; $titleSocial = @$title; $urlShort = @$url; + if (empty($video['id']) && !empty(getVideos_id())) { $video['id'] = getVideos_id(); } @@ -16,6 +17,19 @@ if (!empty($video['id'])) { } } +if(empty($urlShort)){ + $live = isLive(); + if(!empty($live)){ + $livet = LiveTransmition::getFromRequest(); + $titleSocial = $livet['title']; + $urlShort = Live::getLinkToLiveFromUsers_idAndLiveServer($livet['users_id'], $livet['live_servers_id'], $livet['live_index'], $live['live_schedule'] ); + } + //var_dump($livet['users_id'], $livet['live_servers_id'], $livet['live_index'], $live['live_schedule'] , $urlShort, $titleSocial, $live, $livet, debug_backtrace());exit; +} +if(empty($urlShort)){ + echo ''; + return; +} if (empty($advancedCustom)) { $advancedCustom = AVideoPlugin::getObjectData("CustomizeAdvanced"); } @@ -25,6 +39,7 @@ $titleSocial = getSEOTitle($titleSocial); $urlSocial = urlencode($url); //set the $urlSocial and the $titleSocial before include this + if(!isset($global)){ $global = []; } diff --git a/view/js/script.js b/view/js/script.js index e8e51982ef..ecfa8025b5 100644 --- a/view/js/script.js +++ b/view/js/script.js @@ -1307,7 +1307,7 @@ async function avideoConfirmCallBack(msg, confirmCallBackFunction, cancelCallBac async function avideoConfirm(msg) { var span = document.createElement("span"); - span.innerHTML = __(msg); + span.innerHTML = __(msg, true); var response = await swal({ title: 'Confrim', content: span, @@ -4332,6 +4332,10 @@ function startTour(stepsFileRelativePath) { // Load Intro.js CSS $('head').append(''); + if (isCurrentThemeDark) { + $('head').append(''); + } + // Load Intro.js JavaScript $.getScript(webSiteRootURL + 'node_modules/intro.js/minified/intro.min.js', function () { loadAndStartTour(stepsFileRelativePath);