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