mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-03 09:49:28 +02:00
Update
This commit is contained in:
parent
3781bb7a05
commit
80dbf3d58f
5 changed files with 283 additions and 50 deletions
|
@ -482,6 +482,23 @@ function execAsync($command, $keyword = null)
|
||||||
return $pid;
|
return $pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function execFFMPEGAsyncOrRemote($command, $keyword = null)
|
||||||
|
{
|
||||||
|
$obj = AVideoPlugin::getDataObjectIfEnabled('API');
|
||||||
|
if(!empty($obj) && !empty($obj->standAloneFFMPEG)){
|
||||||
|
$url = "{$obj->standAloneFFMPEG}";
|
||||||
|
$url = addQueryStringParameter($url, 'APISecret', $obj->APISecret);
|
||||||
|
$url = addQueryStringParameter($url, 'ffmpegCommand', $command);
|
||||||
|
$url = addQueryStringParameter($url, 'keyword', $keyword);
|
||||||
|
_error_log("execFFMPEGAsyncOrRemote: URL $command");
|
||||||
|
_error_log("execFFMPEGAsyncOrRemote: URL $url");
|
||||||
|
return url_get_contents($url);
|
||||||
|
}else{
|
||||||
|
_error_log("execFFMPEGAsyncOrRemote: Async $command");
|
||||||
|
return execAsync($command, $keyword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Function to find the process by keyword using the pid file
|
// Function to find the process by keyword using the pid file
|
||||||
function findProcess($keyword)
|
function findProcess($keyword)
|
||||||
|
|
|
@ -1,63 +1,86 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
function isAPIKeyValid()
|
||||||
|
{
|
||||||
|
if(php_sapi_name() === 'cli'){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(empty($_REQUEST['APISecret'])){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
global $global;
|
||||||
|
$url = "{$global['webSiteRootURL']}plugin/API/get.json.php?APIName=isAPISecretValid&APISecret={$_REQUEST['APISecret']}";
|
||||||
|
$content = file_get_contents($url);
|
||||||
|
$json = json_decode($content);
|
||||||
|
//var_dump(empty($json->error));
|
||||||
|
return !empty($json) && empty($json->error);
|
||||||
|
}
|
||||||
|
|
||||||
// Function to load standalone configuration
|
// Function to load standalone configuration
|
||||||
function loadStandaloneConfiguration()
|
function loadStandaloneConfiguration()
|
||||||
{
|
{
|
||||||
global $global, $doNotIncludeConfig, $streamerURL;
|
global $global, $doNotIncludeConfig, $streamerURL;
|
||||||
|
global $mysqlHost, $mysqlUser, $mysqlPass, $mysqlDatabase, $mysqlPort, $mysql_connect_was_closed, $mysql_connect_is_persistent;
|
||||||
|
|
||||||
// Define the global system root path
|
$configFile = __DIR__ . "/../videos/configuration.php";
|
||||||
$global['systemRootPath'] = realpath(__DIR__ . '/../') . '/';
|
$global['systemRootPath'] = realpath(__DIR__ . '/../') . '/';
|
||||||
$global['systemRootPath'] = str_replace('\\', '/', $global['systemRootPath']);
|
$global['systemRootPath'] = str_replace('\\', '/', $global['systemRootPath']);
|
||||||
$configFileStandAlone = "{$global['systemRootPath']}videos/standalone.configuration.php";
|
$configFileStandAlone = "{$global['systemRootPath']}videos/standalone.configuration.php";
|
||||||
|
|
||||||
// Load configuration if the file exists
|
if (file_exists($configFile)) {
|
||||||
|
require_once $configFile;
|
||||||
|
$streamerURL = $global['webSiteRootURL'];
|
||||||
|
error_log("Rebroadcaster.json.php is using local configuration");
|
||||||
|
if($isStandAlone){
|
||||||
|
if (!isAPIKeyValid()) {
|
||||||
|
die('Invalid API Key');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (file_exists($configFileStandAlone)) {
|
if (file_exists($configFileStandAlone)) {
|
||||||
$doNotIncludeConfig = 1;
|
$doNotIncludeConfig = 1;
|
||||||
require_once $configFileStandAlone;
|
require_once $configFileStandAlone;
|
||||||
// Set global variables for logging
|
$configFile = "{$global['systemRootPath']}videos/configuration.php";
|
||||||
if ($global['webSiteRootURL'] === _getCurrentUrl()) {
|
|
||||||
$configFile = "{$global['systemRootPath']}videos/configuration.php";
|
|
||||||
|
|
||||||
// Check if configuration.php exists; if not, create it
|
// Check if configuration.php exists; if not, create it
|
||||||
if (!file_exists($configFile)) {
|
if (!file_exists($configFile)) {
|
||||||
$content = "<?php" . PHP_EOL;
|
$content = "<?php" . PHP_EOL;
|
||||||
$content .= "global \$global, \$doNotIncludeConfig, \$doNotConnectDatabaseIncludeConfig, \$doNotStartSessionIncludeConfig, \$isStandAlone;" . PHP_EOL;
|
$content .= "global \$global, \$doNotIncludeConfig, \$doNotConnectDatabaseIncludeConfig, \$doNotStartSessionIncludeConfig, \$isStandAlone;" . PHP_EOL;
|
||||||
$content .= "\$isStandAlone = 1;" . PHP_EOL;
|
$content .= "\$isStandAlone = 1;" . PHP_EOL;
|
||||||
$content .= "\$doNotIncludeConfig = 1;" . PHP_EOL;
|
$content .= "\$doNotIncludeConfig = 1;" . PHP_EOL;
|
||||||
$content .= "\$doNotConnectDatabaseIncludeConfig = 1;" . PHP_EOL;
|
$content .= "\$doNotConnectDatabaseIncludeConfig = 1;" . PHP_EOL;
|
||||||
$content .= "\$doNotStartSessionIncludeConfig = 1;" . PHP_EOL;
|
$content .= "\$doNotStartSessionIncludeConfig = 1;" . PHP_EOL;
|
||||||
$content .= "\$global['salt'] = '" . uniqid() . "';" . PHP_EOL;
|
$content .= "\$global['salt'] = '" . uniqid() . "';" . PHP_EOL;
|
||||||
$content .= "\$global['webSiteRootURL'] = '{$global['webSiteRootURL']}';" . PHP_EOL;
|
$content .= "\$global['webSiteRootURL'] = '{$global['webSiteRootURL']}';" . PHP_EOL;
|
||||||
$content .= "\$global['systemRootPath'] = '{$global['systemRootPath']}';" . PHP_EOL;
|
$content .= "\$global['systemRootPath'] = '{$global['systemRootPath']}';" . PHP_EOL;
|
||||||
$content .= "\$global['logfile'] = \$global['systemRootPath'] . 'videos/avideo.log';" . PHP_EOL . PHP_EOL;
|
$content .= "\$global['systemRootPath'] = '{$global['systemRootPath']}';" . PHP_EOL;
|
||||||
$content .= "require_once \$global['systemRootPath'] . 'objects/include_config.php';" . PHP_EOL;
|
$content .= "require_once \$global['systemRootPath'] . 'objects/include_config.php';" . PHP_EOL;
|
||||||
|
|
||||||
if (file_put_contents($configFile, $content) === false) {
|
if (file_put_contents($configFile, $content) === false) {
|
||||||
die("Failed to create the configuration file at $configFile");
|
die("Failed to create the configuration file at $configFile");
|
||||||
}
|
|
||||||
error_log("configuration.php created at $configFile");
|
|
||||||
}
|
}
|
||||||
|
error_log("configuration.php created at $configFile");
|
||||||
require_once $configFile;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
require_once $configFile;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// If no configuration file exists, output a message
|
||||||
|
$webSiteRootURL = 'https://yourSite.com/';
|
||||||
|
header('Content-Type: text/html');
|
||||||
|
|
||||||
function _getCurrentUrl()
|
echo "<h1>Standalone Configuration File Missing</h1>";
|
||||||
{
|
echo "<p>You need to manually create a file named <code>{$global['systemRootPath']}videos/standalone.configuration.php</code> in the <code>videos</code> directory.</p>";
|
||||||
// Determine the protocol (http or https)
|
echo "<p>Include the following content in the file:</p>";
|
||||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https://" : "http://";
|
echo "<pre>";
|
||||||
|
echo htmlspecialchars("<?php\n");
|
||||||
|
echo htmlspecialchars("\$global['webSiteRootURL'] = '{$webSiteRootURL}';\n");
|
||||||
|
echo htmlspecialchars("?>");
|
||||||
|
echo "</pre>";
|
||||||
|
|
||||||
// Get the host
|
exit;
|
||||||
$host = $_SERVER['HTTP_HOST'];
|
|
||||||
|
|
||||||
// Get the request URI
|
|
||||||
$uri = $_SERVER['REQUEST_URI'];
|
|
||||||
|
|
||||||
// Combine all parts to form the full URL
|
|
||||||
$url = $protocol . $host . $uri;
|
|
||||||
|
|
||||||
return str_replace('plugin/Live/standAloneFiles/restreamer.json.php', '', $url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the function to load configuration
|
// Call the function to load configuration
|
||||||
|
|
|
@ -59,6 +59,7 @@ class API extends PluginAbstract
|
||||||
global $global;
|
global $global;
|
||||||
$obj = new stdClass();
|
$obj = new stdClass();
|
||||||
$obj->APISecret = md5($global['salt'] . $global['systemRootPath'] . 'API');
|
$obj->APISecret = md5($global['salt'] . $global['systemRootPath'] . 'API');
|
||||||
|
$obj->standAloneFFMPEG = '';
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2904,6 +2905,22 @@ class API extends PluginAbstract
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $parameters return true if the secret is valid and false if it is not
|
||||||
|
* 'APISecret' mandatory for security reasons - required
|
||||||
|
* @example {webSiteRootURL}plugin/API/{getOrSet}.json.php?APIName={APIName}&APISecret={APISecret}
|
||||||
|
* @return \ApiObject Returns an ApiObject.
|
||||||
|
*/
|
||||||
|
public function get_api_isAPISecretValid($parameters)
|
||||||
|
{
|
||||||
|
global $global;
|
||||||
|
if (!self::isAPISecretValid()) {
|
||||||
|
return new ApiObject("APISecret is invalid");
|
||||||
|
}else{
|
||||||
|
return new ApiObject("APISecret is valid", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApiObject
|
class ApiObject
|
||||||
|
|
184
plugin/API/standAlone/ffmpeg.json.php
Normal file
184
plugin/API/standAlone/ffmpeg.json.php
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* FFMPEG Command Execution Script with API Secret Validation
|
||||||
|
* -----------------------------------------------------------
|
||||||
|
* This script is used to safely execute FFMPEG commands on the server via HTTP request or command line.
|
||||||
|
* It includes security measures to validate the provided `APISecret` against the AVideo platform API.
|
||||||
|
*
|
||||||
|
* Usage Instructions:
|
||||||
|
* -------------------
|
||||||
|
* 1. **API Secret Validation:**
|
||||||
|
* - All requests (HTTP or command line) must include a valid `APISecret` parameter.
|
||||||
|
* - The `APISecret` is verified by making a request to the AVideo platform's API.
|
||||||
|
* - If the `APISecret` is invalid, the script will terminate with an error message.
|
||||||
|
*
|
||||||
|
* 2. **Via HTTP Request:**
|
||||||
|
* Send a GET or POST request to the script with the following parameters:
|
||||||
|
* - `APISecret` (required): The API secret key obtained from the API plugin in AVideo.
|
||||||
|
* - `ffmpegCommand` (required): The full FFMPEG command to execute. The command must start with `ffmpeg`.
|
||||||
|
* - `keyword` (optional): A unique keyword associated with the process, allowing you to kill a previously started process with the same keyword.
|
||||||
|
*
|
||||||
|
* **Example URL:**
|
||||||
|
* ```
|
||||||
|
* http://yourserver.com/path/to/script.php?APISecret=yourAPISecret&ffmpegCommand=ffmpeg+-i+input.mp4+-vcodec+libx264+-preset+fast+output.mp4&keyword=uniqueProcessKey
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* 3. **Via Command Line:**
|
||||||
|
* Run the script from the terminal with the following parameters:
|
||||||
|
* - `APISecret` (required): The API secret key obtained from the API plugin in AVideo.
|
||||||
|
* - `ffmpegCommand` (required): The full FFMPEG command to execute. The command must start with `ffmpeg`.
|
||||||
|
* - `keyword` (optional): A unique keyword associated with the process.
|
||||||
|
*
|
||||||
|
* **Example Command:**
|
||||||
|
* ```
|
||||||
|
* php script.php APISecret="yourAPISecret" ffmpegCommand="ffmpeg -i input.mp4 -vcodec libx264 -preset fast output.mp4" keyword="uniqueProcessKey"
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Security Features:
|
||||||
|
* -------------------
|
||||||
|
* - **API Secret Validation:** Ensures only authorized users can execute commands.
|
||||||
|
* The API secret is verified by sending a request to the AVideo platform API endpoint:
|
||||||
|
* ```
|
||||||
|
* plugin/API/get.json.php?APIName=isAPISecretValid&APISecret=yourAPISecret
|
||||||
|
* ```
|
||||||
|
* If the validation fails, the script terminates immediately with an error.
|
||||||
|
*
|
||||||
|
* - **Command Validation:**
|
||||||
|
* - Ensures the `ffmpegCommand` starts with `ffmpeg` or an allowed path (`/usr/bin/ffmpeg`, `/bin/ffmpeg`).
|
||||||
|
* - Sanitizes the command by removing potentially dangerous characters (`;`, `&`, `|`, `` ` ``, `<`, `>`).
|
||||||
|
*
|
||||||
|
* - **Kill Process by Keyword:** Allows stopping a previously started process by providing a `keyword`.
|
||||||
|
*
|
||||||
|
* Output:
|
||||||
|
* -------
|
||||||
|
* The script returns a JSON response with the status of the command execution:
|
||||||
|
* - **Success Response:**
|
||||||
|
* ```json
|
||||||
|
* {
|
||||||
|
* "error": false,
|
||||||
|
* "msg": "Command executed",
|
||||||
|
* "command": "ffmpeg -i input.mp4 -vcodec libx264 -preset fast output.mp4",
|
||||||
|
* "pid": 12345
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* - **Error Response:**
|
||||||
|
* ```json
|
||||||
|
* {
|
||||||
|
* "error": true,
|
||||||
|
* "msg": "Invalid or empty ffmpeg command"
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Standalone Configuration File:
|
||||||
|
* ------------------------------
|
||||||
|
* If the standalone configuration file is missing, the script will prompt the user to create it manually:
|
||||||
|
*
|
||||||
|
* **Required File:**
|
||||||
|
* `<installation_root>/videos/standalone.configuration.php`
|
||||||
|
*
|
||||||
|
* **Sample Content:**
|
||||||
|
* ```php
|
||||||
|
* <?php
|
||||||
|
* $global['webSiteRootURL'] = 'https://yourSite.com/';
|
||||||
|
* ?>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Replace `https://yourSite.com/` with your actual website URL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$global_timeLimit = 300;
|
||||||
|
|
||||||
|
ini_set("memory_limit", -1);
|
||||||
|
ini_set('default_socket_timeout', $global_timeLimit);
|
||||||
|
set_time_limit($global_timeLimit);
|
||||||
|
ini_set('max_execution_time', $global_timeLimit);
|
||||||
|
ini_set("memory_limit", "-1");
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
require_once __DIR__ . "/../../../objects/functionsStandAlone.php";
|
||||||
|
|
||||||
|
if (empty($streamerURL)) {
|
||||||
|
echo json_encode(['error' => true, 'message' => 'streamerURL not defined']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to safely get inputs from either command line or request
|
||||||
|
function getInput($key, $default = '') {
|
||||||
|
global $argv;
|
||||||
|
|
||||||
|
// Check if running from command line or HTTP request
|
||||||
|
if (php_sapi_name() === 'cli') {
|
||||||
|
// Look for the parameter in $argv (command line)
|
||||||
|
foreach ($argv as $arg) {
|
||||||
|
if (strpos($arg, "{$key}=") === 0) {
|
||||||
|
return substr($arg, strlen("{$key}="));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback to HTTP request ($_REQUEST)
|
||||||
|
return isset($_REQUEST[$key]) ? $_REQUEST[$key] : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and sanitize the ffmpegCommand
|
||||||
|
function sanitizeFFmpegCommand($command) {
|
||||||
|
// Allowable ffmpeg prefixes
|
||||||
|
$allowedPrefixes = ['ffmpeg', '/usr/bin/ffmpeg', '/bin/ffmpeg'];
|
||||||
|
|
||||||
|
// Remove dangerous characters
|
||||||
|
$command = str_replace('&&', '', $command);
|
||||||
|
$command = preg_replace('/[;|`<]/', '', $command);
|
||||||
|
|
||||||
|
// Ensure it starts with ffmpeg
|
||||||
|
foreach ($allowedPrefixes as $prefix) {
|
||||||
|
if (strpos(trim($command), $prefix) === 0) {
|
||||||
|
return $command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch and sanitize inputs
|
||||||
|
$ffmpegCommand = sanitizeFFmpegCommand(getInput('ffmpegCommand', ''));
|
||||||
|
$keyword = getInput('keyword', '');
|
||||||
|
|
||||||
|
// Kill processes associated with the keyword
|
||||||
|
if (!empty($keyword)) {
|
||||||
|
killProcessFromKeyword($keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that ffmpegCommand is not empty after sanitization
|
||||||
|
if (empty($ffmpegCommand)) {
|
||||||
|
echo json_encode([
|
||||||
|
'error' => true,
|
||||||
|
'msg' => 'Invalid or empty ffmpeg command',
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug output (optional)
|
||||||
|
error_log("Constructed FFMPEG Command: $ffmpegCommand");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pid = execAsync($ffmpegCommand, $keyword);
|
||||||
|
echo json_encode([
|
||||||
|
'error' => false,
|
||||||
|
'msg' => 'Command executed',
|
||||||
|
'command' => $ffmpegCommand,
|
||||||
|
'pid' => $pid,
|
||||||
|
]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode([
|
||||||
|
'error' => true,
|
||||||
|
'msg' => 'Failed to execute command',
|
||||||
|
'errorMsg' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
exit;
|
|
@ -58,16 +58,7 @@ $isATest = false;
|
||||||
|
|
||||||
$logFile = $logFileLocation . "ffmpeg_restreamer_{users_id}_" . date("Y-m-d-h-i-s") . ".log";
|
$logFile = $logFileLocation . "ffmpeg_restreamer_{users_id}_" . date("Y-m-d-h-i-s") . ".log";
|
||||||
|
|
||||||
$configFile = dirname(__FILE__) . '/../../../videos/configuration.php';
|
require_once __DIR__ . "/../../../objects/functionsStandAlone.php";
|
||||||
|
|
||||||
if (file_exists($configFile)) {
|
|
||||||
$doNotIncludeConfig = 1;
|
|
||||||
include_once $configFile;
|
|
||||||
$streamerURL = $global['webSiteRootURL'];
|
|
||||||
error_log("Restreamer.json.php is using local configuration");
|
|
||||||
} else {
|
|
||||||
require_once __DIR__ . "/../../../objects/functionsStandAlone.php";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($streamerURL)) {
|
if (empty($streamerURL)) {
|
||||||
die('streamerURL not defined');
|
die('streamerURL not defined');
|
||||||
|
@ -644,6 +635,7 @@ function startRestream($m3u8, $restreamsDestinations, $logFile, $robj, $tries =
|
||||||
_make_path($logFile);
|
_make_path($logFile);
|
||||||
file_put_contents($logFile, $command . PHP_EOL);
|
file_put_contents($logFile, $command . PHP_EOL);
|
||||||
if (empty($isATest)) {
|
if (empty($isATest)) {
|
||||||
|
// use remote ffmpeg here
|
||||||
exec('nohup ' . $command . ' 2>> ' . $logFile . ' > /dev/null &');
|
exec('nohup ' . $command . ' 2>> ' . $logFile . ' > /dev/null &');
|
||||||
}
|
}
|
||||||
error_log("Restreamer.json.php startRestream finish");
|
error_log("Restreamer.json.php startRestream finish");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue