I have a PHP page I used to download YouTube videos, hosted on my Pi 4.
The site is only going to be used on my home network (so a dot local address), but it allows me to download videos on my phone or computer without downloading any executables or special apps.
The user can submit a request on an HTML page with four options:
Form ID : Description to user
----------------------------------------
bestquality : MP4 (video with sound)
worstquality : MP4 (video without sound)
bestaudio : M4A
worstaudio : WEBM
This is a majority of the code used to make this:
<?php
//the file extension
$formatExt = "";
switch($_GET['format']) {
case 'bestvideo': case 'worstvideo': $formatExt = "MP4"; break;
case 'bestaudio': $formatExt = "m4a"; break;
case 'worstaudio': $formatExt = "webm";
break;
default:
echo("Illegal video format '".$_GET['format']."'!");
exit(1);
}
$url = $_GET['url'];
$format = $_GET['format'];
$ytcmd = 'youtube-dl';
$directoriesInUse = array_filter(glob("./workers/*"), 'is_dir');
$integerIDs = array(sizeof($directoriesInUse));
//if the working directory changes, make sure to change the 2 at the end of the
//expression in the for loop to match the amount of '/' characters.
for($i = 0; $i < sizeof($directoriesInUse); $i++)
$integerIDs[$i] = (int)explode("/", $directoriesInUse[$i])[2];
//If these are not sorted, it will see a list of 1 thru 14 and pick 9 as the largest number (because that's how the directory would be listed)
sort($integerIDs);
$numberToUse = $integerIDs[sizeof($integerIDs) - 1] + 1;
//prevent exploiting and issuing server commands
$url = str_replace(" ", "", $url);
$url = str_replace("|", "", $url);
//url may contain & for a playlist, so it will be removed here to prevent other issues.
$url = substr($url, 0, strpos($url, "&"));
//Download the video, the "--format" option will not download audio for a video (worstvideo is video w/o audio)
if($format === "bestaudio" or $format === "worstaudio" or $format === "worstvideo")
$response = exec('mkdir ./workers/'.$numberToUse.' && cd ./workers/'.$numberToUse.'/ && '.$ytcmd.' --no-playlist --format '.$format.' -o \'%(title)s.%(ext)s\' '.$url);
else
$response = exec('mkdir ./workers/'.$numberToUse.' && cd ./workers/'.$numberToUse.'/ && '.$ytcmd.' --no-playlist -o \'%(title)s.%(ext)s\' '.$url);
//Get the first file in a directory, this SHOULD be the video that was downloaded. It is not possible (AFAIK) to get the file name of a video from the youtube-dl binary
function magicWorkers($path) {
return array_diff(scandir($path), array('.', '..'))[2];
}
//change file date so the crontab doesn't delete the file too early (youtube-dl may save the file with the date it was uploaded)
echo exec("touch ./workers/".$numberToUse."/".magicWorkers("./workers/".$numberToUse));
//sleep 2 seconds for good measure (in case things haven't updated for whatever reason, this has happened!)
sleep(2);
//This works, but I would like to find an alternative soon (prompt download vs direct)
header("Location: ./workers/".$numberToUse.'/'.magicWorkers("./workers/".$numberToUse));
exit(0);
The server is being hosted on an Apache2 web server in the /var/www/html/
directory, and Apache2 has rw- access in the /var/www/html/workers/
directory. It is running running PHP 7.2.24.
I have seen some security problems with this before, for instance, inputting example.com/video && cowsay MOO
will execute the cowsay command, so I made a small patch by disabling the characters " " (space), "&", and "|".
My other concern is that I am linking the user directly to the file to download, whereas it would be better (and probably more secure) if they were prompted for the download.
One last thing: I have a crontab that runs every minute to delete files in the workers
directory (older than two hours) to make sure that the space on the Pi doesn't fill up (directories do not get deleted).
$(command-here)
. \$\endgroup\$exit 0;
I tried it on sandbox.onlinephpfunctions.com with various versions of 4,5 and 7 and all of them show that syntax error... \$\endgroup\$