Download YouTube videos using cURL and PHP

January 23, 2024


$you_tube_link = 'https://www.youtube.com/watch?v=8a8GlAf6Gv8';

$ch = curl_init($you_tube_link);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_exec($ch);
$responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ( $responseCode !== 200 ) {
echo curl_error($ch);

} else {

$token = "=";
$video_key = "";

$index = strrpos($you_tube_link, $token);

if ($index !== false) {
$apiKey = substr($you_tube_link, $index + strlen($token));
}

preg_match('%(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})%i', $you_tube_link, $match);
$youtubeVideoId = $match[1];
$videoMeta = json_decode(getMetadata($youtubeVideoId, $apiKey));

$videoThumbnails = $videoMeta -> videoDetails -> thumbnail -> thumbnails;
$thumbnail = end( $videoThumbnails ) -> url;

$videoTitle = $videoMeta -> videoDetails -> title;
$videoFormats = $videoMeta -> streamingData -> formats;

if ( empty($videoFormats[0] -> url) ) {
echo 'This video cannot be downloaded at this time.';

} else {


foreach ( $videoFormats as $videoFormat ) {

if ( isset($videoFormat -> url) ) {
$url = $videoFormat -> url;
}

if ( isset($videoFormat -> approxDurationMs) ) {
$video_duration_ms = $videoFormat -> approxDurationMs;
$video_duration = $video_duration_ms / 1000 / 60;
$video_duration = round($video_duration, 2);
$video_duration = str_replace('.', ':', $video_duration);

} else {
$video_duration = 'Unknown';
}

$shortDescription = $videoMeta -> videoDetails -> shortDescription;
$videoDescription = str_split($shortDescription, 100)[0];

$display_main_video = '
< form method="post" action="dl-video.php" >
< h3 > Download Video < / h3 >
< img src=" ' . $thumbnail . ' " >
< p > ' . $videoTitle . ' < / p >
< p >Approximate Running Time: ' . $video_duration . ' < / p >
< p >'.$videoDescription.'< / p >
< / form >
';

} // end of foreach



$adaptiveFormats_count = 0;
$adaptiveFormats = $videoMeta -> streamingData -> adaptiveFormats;

foreach ( $adaptiveFormats as $videoFormat ) {

try {

$adaptive_url = $videoFormat -> url;

} catch (Exception $e) {

echo $e;

// cannot get video. probably due to $videoFormat -> signatureCipher;
// This will be covered in a later post.

} // end of try


if ( isset($videoFormat -> qualityLabel) ) {
$dl_quality = $videoFormat -> qualityLabel;

} else {
$dl_quality = "Unknown";

}


if ( $videoFormat -> mimeType ) {

$filetype_info = $videoFormat -> mimeType;

$filetype_array = explode(";", $filetype_info);
$mimetype = $filetype_array[0];
$codec = $filetype_array[1];

$mimetype_array = explode("/", $mimetype);
$mediatype = $mimetype_array[0];
$extension = $mimetype_array[1];

} else {
$mimetype = "Unknown";
$extension = "Unknown";

}


if ( isset($videoFormat -> contentLength) ) {

$dl_filesize_bytes = $videoFormat -> contentLength;
$dl_filesize = $dl_filesize_bytes / 1000 / 1000;
$dl_filesize = round($dl_filesize, 1);
$dl_filesize = $dl_filesize . 'mb';

} else {
$dl_filesize = "Unknown";

}


$dl_loop = '
< form method="post" action="dl-video.php" >
< input type="hidden" name="link" value=" ' . urlencode($adaptive_url) . ' " >
< input type="hidden" name="title" value=" ' . urlencode($videoTitle) . ' " >
< input type="hidden" name="mimetype" value=" ' . $mimetype . ' " >
< input type="hidden" name="extension" value=" ' . $extension . ' " >
< input type="hidden" name="filesize" value=" ' . $dl_filesize_bytes . ' " >
< input type="hidden" name="video_id" value=" ' . $apiKey . ' " >
< button type="submit" name="download" value="video" > Download Video < / button >
< / form >
';


$formats_loop .= '
< tr >
< td > ' . $mimetype . ' < / td >
< td > ' . $dl_quality . ' < / td >
< td > ' . $dl_filesize . ' < / td >
< td > ' . $dl_loop . ' < / td >
< / tr >
';

$adaptiveFormats_count++;

} // end of foreach


///////////////////////////////////////////////////////////////////////////////
// now display all of your hard work to the world.
///////////////////////////////////////////////////////////////////////////////

echo '
< div >
' . $display_main_video . '
< / div >

< h3 > Formats: < / h3 >

< table >
< tr >
< th > < span > Type < / span > < / th >
< th > < span > Quality < / span > < / th >
< th > < span > Filesize < / span > < / th >
< th > < span > Download < / span > < / th >
< / tr >
' . $formats_loop . '
< / table >
';



///////////////////////////////////////////////////////////////
// contents of function getMetadata();
///////////////////////////////////////////////////////////////

function getMetadata($videoId, $key){

$curlUrl = 'https://www.youtube.com/youtubei/v1/player?key=' . $key;

$ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $curlUrl);
    curl_setopt($ch, CURLOPT_ENCODING, 'gzip, deflate');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    $curlOptions = '
	{
		"context": {
			"client": {
				"hl": "en",
				"clientName": "WEB",
				"clientVersion": "2.20210721.00.00",
				"clientFormFactor": "UNKNOWN_FORM_FACTOR",
				"clientScreen": "WATCH",
				"mainAppWebInfo": {
					"graftUrl": "/watch?v=' . $videoId . '",
					}
				},
			"user": {
				"lockedSafetyMode": false
				},
			"request": {
				"useSsl": true,
				"internalExperimentFlags": [],
				"consistencyTokenJars": []
				}
			},
			"videoId": "' . $videoId . '",
			"playbackContext": {
				"contentPlaybackContext": {
					"vis": 0,
					"splay": false,
					"autoCaptionsDefaultOn": false,
					"autonavState": "STATE_NONE",
					"html5Preference": "HTML5_PREF_WANTS",
					"lactMilliseconds": "-1"
					}
				},
			"racyCheckOk": false,
			"contentCheckOk": false
	}';

    curl_setopt($ch, CURLOPT_POSTFIELDS, $curlOptions);
    $headers = array();
    $headers[] = 'Content-Type: application/json';
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    $curlResult = curl_exec($ch);
	$curlError = curl_error($ch);
	$curlErrno = curl_errno($ch);

    if ($curlError) {
    	return 'Error:' . $curlErrno .' - '. $curlError;
	} else {
		curl_close($ch);
		return $curlResult;
	}

}



/////////////////////////////////////////////
// contents of dl-video.php
////////////////////////////////////////////

if ( isset($_POST['link']) ) {
$posted_link = $_POST['link'];
}

if ( isset($_POST['title']) ) {
$posted_title = $_POST['title'];
}

if ( isset($_POST['mimetype']) ) {
$posted_mimetype = $_POST['mimetype'];
}

if ( isset($_POST['extension']) ) {
$posted_extension = $_POST['extension'];
}

if ( isset($_POST['filesize']) ) {
$posted_filesize = $_POST['filesize'];
}

if ( isset($_POST['video_id']) ) {
$posted_video_id = $_POST['video_id'];
}

$remoteURL = urldecode($posted_link);

if ( ! empty($remoteURL) && substr($remoteURL, 0, 8) === 'https://') {
// looks good
} else {
exit;
}

$posted_title = urldecode($posted_title);
$posted_title = preg_replace('/[^A-Za-z0-9]/', ' ', $posted_title);
$posted_title = trim(preg_replace('/\s\s+/', ' ', str_replace("\n", " ", $posted_title)));
$posted_title = ucwords($posted_title);
$posted_title = str_replace(' ', '-', $posted_title);
$new_filename = $posted_title.'.'.$posted_extension;

$data = get_headers($remoteURL, true);

if ( isset($data['Content-Length']) ) {
$filesize = (int) $data['Content-Length'];
} else {
exit;
}

if ( empty($filesize) ) {
exit;
}

header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".$filesize);
header("Content-type: application/x-file-to-save"); 
header("Content-Disposition: attachment;filename=\"$new_filename\"");
readfile($remoteURL);
exit;

Please be aware of possible copyright issues when downloading videos from YouTube


Comments

There are no comments.


Comment on this Article

Your email address will never be published. Comments are usually approved within an hour or two. (to prevent spam)