first commit

This commit is contained in:
hnhx 2022-07-15 19:42:07 +02:00
parent d8a638d9ef
commit 5c7c221d3e
16 changed files with 705 additions and 0 deletions

32
api.php Normal file
View file

@ -0,0 +1,32 @@
<?php
$config = require "config.php";
require "misc/tools.php";
$query = $_REQUEST["q"];
$type = isset($_REQUEST["type"]) ? (int) $_REQUEST["type"] : 0;
$results = array();
switch ($type)
{
case 0:
require "results/search_results.php";
get_search_results($query);
break;
case 1:
require "results/channel_results.php";
get_channel_results($query);
break;
case 2:
require "results/watch_results.php";
get_watch_results($query);
break;
default:
require "results/search_results.php";
get_search_results($query);
break;
}
header("Content-Type: application/json");
echo json_encode($results, JSON_PRETTY_PRINT);
?>

35
channel.php Normal file
View file

@ -0,0 +1,35 @@
<?php
require "static/header.xhtml";
$config = require "config.php";
require "misc/tools.php";
require "results/channel_results.php";
$channel_id = $_REQUEST["id"];
$results = array();
get_channel_results($channel_id);
$channel_info = $results[0];
echo "<img class=\"banner\" src=\"/image_proxy.php?url=" . $channel_info["channel_banner"] . "\" alt=\"banner\"/ >";
echo "<p class=\"small-channel\"><img src=\"/image_proxy.php?url=" . $channel_info["thumbnail"] . "\" width=\"50\" height=\"40\"><a href=\"channel.php?id=" . $channel_info["channel_id"] . "\">" . $channel_info["channel_name"] . "</a> (" . $channel_info["subscribers"] . ")</p>";
echo "<div class=\"video-results\">";
foreach ($results as $result)
{
if (!$result["is_video"])
continue;
echo "<div>";
echo "<a href=\"watch.php?v=" . $result["video_id"] . "\">";
echo "<img src=\"/image_proxy.php?url=" . $result["thumbnail"] . "\" />";
echo "<p>" . $result["title"] . "</p>";
echo "</a>";
echo "<small class=\"channel-name\">" . $result["date"] . " - " . $result["views"] . "</a></small>";
echo "</div>";
}
echo "</div>";
require "static/footer.xhtml";
?>

41
config.php Normal file
View file

@ -0,0 +1,41 @@
<?php
return (object) array(
/*
This will proxy all videos trough your server so clients won't make connections to Google.
!!! THIS WILL USE LOTS OF BANDWIDTH !!!
*/
"proxy_videos" => true,
"default_quality" => "hd720",
/*
To send requests trough a proxy uncomment CURLOPT_PROXY and CURLOPT_PROXYTYPE:
CURLOPT_PROXYTYPE options:
CURLPROXY_HTTP
CURLPROXY_SOCKS4
CURLPROXY_SOCKS4A
CURLPROXY_SOCKS5
CURLPROXY_SOCKS5_HOSTNAME
!!! ONLY CHANGE THE OTHER OPTIONS IF YOU KNOW WHAT YOU ARE DOING !!!
*/
"curl_settings" => array(
// CURLOPT_PROXY => "ip:port",
// CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_USERAGENT => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36",
CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS | CURLPROTO_HTTP,
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS | CURLPROTO_HTTP,
CURLOPT_MAXREDIRS => 5,
CURLOPT_TIMEOUT => 8,
CURLOPT_VERBOSE => false
)
);
?>

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

12
image_proxy.php Normal file
View file

@ -0,0 +1,12 @@
<?php
$config = require "config.php";
require "misc/tools.php";
$image = $_REQUEST["url"];
$image_src = request($image);
header("Content-Type: image/jpeg");
echo $image_src;
?>

42
index.php Normal file
View file

@ -0,0 +1,42 @@
<?php
require "static/header.xhtml";
$config = require "config.php";
require "misc/tools.php";
require "results/search_results.php";
$query = $_REQUEST["q"];
$results = array();
get_search_results($query);
if (empty($query))
{
echo "<p>No popular video feed here, don't be a consoomer.</p>";
}
else
{
echo "<div class=\"video-results\">";
foreach ($results as $result)
{
echo "<div>";
if ($result["is_video"])
{
echo "<a href=\"watch.php?v=" . $result["video_id"] . "\">";
echo "<img src=\"/image_proxy.php?url=" . $result["thumbnail"] . "\" />";
echo "<p>" . $result["title"] . "</p>";
echo "</a>";
echo "<small class=\"channel-name\"><a href=\"channel.php?id=" . $result["channel_id"] . "\">" . $result["channel_name"] . "</a> - " . $result["date"] . " - " . $result["views"] . "</small>";
}
else
{
echo "<a href=\"channel.php?id=" . $result["channel_id"] . "\">";
echo "<img src=\"/image_proxy.php?url=" . $result["thumbnail"] . "\" />";
echo "<p>" . $result["channel_name"] . "</p></a>";
}
echo "</div>";
}
echo "</div>";
}
require "static/footer.xhtml";
?>

13
misc/tools.php Normal file
View file

@ -0,0 +1,13 @@
<?php
function request($url)
{
global $config;
$ch = curl_init($url);
curl_setopt_array($ch, $config->curl_settings);
$response = curl_exec($ch);
return $response;
}
?>

View file

@ -0,0 +1,77 @@
<?php
function get_channel_results($channel_id)
{
global $config;
$url = "https://www.youtube.com/channel/$channel_id/videos?hl=en";
$response = file_get_contents($url);
$data_part1 = explode("var ytInitialData = ", $response);
$data_part2 = explode(";</script>", $data_part1[1]);
$data = json_decode($data_part2[0], true);
$raw_results = $data["contents"]["twoColumnBrowseResultsRenderer"]["tabs"][1]["tabRenderer"]["content"]["sectionListRenderer"]["contents"];
parse_channel_header($data);
parse_channel_results($raw_results);
}
function parse_channel_header($data)
{
global $results;
$header = $data["header"]["c4TabbedHeaderRenderer"];
$channel_name = $header["title"];
$channel_id = $header["channelId"];
$channel_thumbnail = $header["avatar"]["thumbnails"][0]["url"];
$channel_banner = $header["banner"]["thumbnails"][0]["url"];
$subscribers = $header["subscriberCountText"]["simpleText"];
$result = array(
"is_video" => false,
"channel_name" => $channel_name,
"channel_id" => $channel_id,
"thumbnail" => $channel_thumbnail,
"channel_banner" => $channel_banner,
"subscribers" => $subscribers
);
array_push($results, $result);
}
function parse_channel_results($raw_results)
{
global $results;
foreach ($raw_results as $raw_result)
{
$shelf_raw_results = $raw_result["itemSectionRenderer"]["contents"][0]["gridRenderer"]["items"];
foreach ($shelf_raw_results as $shelf_raw_result)
{
if (array_key_exists("gridVideoRenderer", $shelf_raw_result))
{
$video = $shelf_raw_result["gridVideoRenderer"];
$video_id = $video["videoId"];
$thumbnail = $video["thumbnail"]["thumbnails"][0]["url"];
$title = $video["title"]["runs"][0]["text"];
$views = $video["viewCountText"]["simpleText"];
$date = $video["publishedTimeText"]["simpleText"];
$result = array(
"is_video" => true,
"video_id" => $video_id,
"title" => $title,
"thumbnail" => $thumbnail,
"views" => $views,
"date" => $date
);
array_push($results, $result);
}
}
}
}
?>

View file

@ -0,0 +1,75 @@
<?php
function get_search_results($query)
{
global $config;
$query = urlencode($query);
$url = "https://www.youtube.com/results?search_query=$query&hl=en";
$response = request($url);
$data_part1 = explode("var ytInitialData = ", $response);
$data_part2 = explode(";</script>", $data_part1[1]);
$data = json_decode($data_part2[0], true);
$raw_results = $data["contents"]["twoColumnSearchResultsRenderer"]["primaryContents"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]["contents"];
parse_search_results($raw_results);
}
function parse_search_results($raw_results)
{
global $results;
foreach ($raw_results as $raw_result)
{
if (array_key_exists("videoRenderer", $raw_result))
{
$video = $raw_result["videoRenderer"];
$video_id = $video["videoId"];
$thumbnail = $video["thumbnail"]["thumbnails"][0]["url"];
$title = $video["title"]["runs"][0]["text"];
$views = $video["shortViewCountText"]["simpleText"];
$date = $video["publishedTimeText"]["simpleText"];
$channel = $video["longBylineText"]["runs"][0];
$channel_name = $channel["text"];
$channel_id = $channel["navigationEndpoint"]["browseEndpoint"]["browseId"];
$result = array(
"is_video" => true,
"video_id" => $video_id,
"title" => $title,
"thumbnail" => $thumbnail,
"channel_id" => $channel_id,
"channel_name" => $channel_name,
"views" => $views,
"date" => $date
);
array_push($results, $result);
}
else if (array_key_exists("shelfRenderer", $raw_result))
{
$shelf_raw_results = $raw_result["shelfRenderer"]["content"]["verticalListRenderer"]["items"];
parse_search_results($shelf_raw_results);
}
else if (array_key_exists("channelRenderer", $raw_result))
{
$channel = $raw_result["channelRenderer"];
$channel_id = $channel["channelId"];
$channel_name = $channel["title"]["simpleText"];
$channel_thumbnail = $channel["thumbnail"]["thumbnails"][1]["url"];
$fixed_channel_thumbnail = "https://" . substr($channel_thumbnail, 2);
$result = array(
"is_video" => false,
"channel_id" => $channel_id,
"channel_name" => $channel_name,
"thumbnail" => $fixed_channel_thumbnail
);
array_push($results, $result);
}
}
}
?>

173
results/watch_results.php Normal file
View file

@ -0,0 +1,173 @@
<?php
function get_watch_results($video_id)
{
global $config;
$mh = curl_multi_init();
// Get directl URL to the video
$direct_ch = curl_init("https://youtubei.googleapis.com/youtubei/v1/player?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8");
curl_setopt_array($direct_ch, $config->curl_settings);
curl_setopt($direct_ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
curl_setopt($direct_ch, CURLOPT_POST, 1);
curl_setopt($direct_ch, CURLOPT_POSTFIELDS, "{
'context': {
'client': {
'hl': 'en',
'clientName': 'WEB',
'clientVersion': '2.20210721.00.00',
'mainAppWebInfo': {
'graftUrl': '/watch?v=$video_id'
}
}
},
'videoId': '$video_id'
}");
curl_multi_add_handle($mh, $direct_ch);
// Get additional info (description, recommended videos etc.)
$video_ch = curl_init("https://www.youtube.com/watch?v=$video_id&hl=en");
curl_setopt_array($video_ch, $config->curl_settings);
curl_multi_add_handle($mh, $video_ch);
$running = null;
do {
curl_multi_exec($mh, $running);
} while ($running);
// Parse the response
$video_response = curl_multi_getcontent($video_ch);
get_video_results($video_response);
$direct_response = curl_multi_getcontent($direct_ch);
get_direct_url($direct_response);
}
function get_direct_url($json_response, $audio_only=false)
{
global $config , $results;
$response = json_decode($json_response, true);
$videos = array_merge($response["streamingData"]["formats"], $response["streamingData"]["adaptiveFormats"]);
$video_url = null;
do
{
foreach ($videos as $video)
{
$type = $video["mimeType"];
$quality = $video["quality"];
if ($audio_only)
{
if (strpos($type, "audio") !== false)
$video_url = $video["url"];
}
else if (strpos($type, "video") !== false && strpos($quality, $config->default_quality) !== false)
$video_url = $video["url"];
if ($video_url)
break;
}
$config->default_quality = "";
} while ($video_url == null);
$results[0]["direct_url"] = urlencode($video_url);
}
function get_video_results($response)
{
$data_part1 = explode("var ytInitialData = ", $response);
$data_part2 = explode(";</script>", $data_part1[1]);
$data = json_decode($data_part2[0], true);
$raw_results = $data["contents"]["twoColumnWatchNextResults"]["secondaryResults"]["secondaryResults"]["results"];
parse_main_video_info($data);
parse_recommended_results($raw_results);
}
function parse_main_video_info($data)
{
global $results;
$main_info = $data["contents"]["twoColumnWatchNextResults"]["results"]["results"]["contents"];
$video_info = $main_info[0]["videoPrimaryInfoRenderer"];
$title = $video_info["title"]["runs"][0]["text"];
$views = $video_info["viewCount"]["videoViewCountRenderer"]["viewCount"]["simpleText"];
$date = $video_info["dateText"]["simpleText"];
$likes = $video_info["videoActions"]["menuRenderer"]["topLevelButtons"][0]["toggleButtonRenderer"]["defaultText"]["accessibility"]["accessibilityData"]["label"];
$channel_info = $main_info[1]["videoSecondaryInfoRenderer"]["owner"]["videoOwnerRenderer"];
$channel_name = $channel_info["title"]["runs"][0]["text"];
$subscribers = $channel_info["subscriberCountText"]["simpleText"];
$channel_thumbnail = urlencode($channel_info["thumbnail"]["thumbnails"][0]["url"]);
$channel_id = $channel_info["navigationEndpoint"]["browseEndpoint"]["browseId"];
$raw_description = $main_info[1]["videoSecondaryInfoRenderer"]["description"]["runs"];
$description = "";
foreach ($raw_description as $part)
{
$text = $part["text"];
if (array_key_exists("navigationEndpoint", $part))
$description .= "$text <br><br>";
else
$description .= $text . "<br><br>";
}
$result = array(
"is_main_video" => true,
"is_video" => true,
"direct_url" => null,
"title" => $title,
"thumbnail" => $channel_thumbnail,
"channel_id" => $channel_id,
"channel_name" => $channel_name,
"description" => $description,
"views" => $views,
"date" => $date,
"likes" => $likes,
"subscribers" => $subscribers
);
array_push($results, $result);
}
function parse_recommended_results($raw_results)
{
global $results;
foreach ($raw_results as $raw_result)
{
if (array_key_exists("compactVideoRenderer", $raw_result))
{
$video = $raw_result["compactVideoRenderer"];
$video_id = $video["videoId"];
$thumbnail = $video["thumbnail"]["thumbnails"][1]["url"];
$title = $video["title"]["simpleText"];
$views = $video["shortViewCountText"]["simpleText"];
$date = $video["publishedTimeText"]["simpleText"];
$channel = $video["longBylineText"]["runs"][0];
$channel_name = $channel["text"];
$channel_id = $channel["navigationEndpoint"]["browseEndpoint"]["browseId"];
$result = array(
"is_video" => true,
"video_id" => $video_id,
"title" => $title,
"thumbnail" => $thumbnail,
"channel_id" => $channel_id,
"channel_name" => $channel_name,
"views" => $views,
"date" => $date
);
array_push($results, $result);
}
}
}
?>

2
static/footer.xhtml Normal file
View file

@ -0,0 +1,2 @@
</body>
</html>

BIN
static/gnutube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

15
static/header.xhtml Normal file
View file

@ -0,0 +1,15 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>GNUTube (BETA)</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="referrer" content="no-referrer" />
<link rel="stylesheet" type="text/css" href="static/styles.css" />
</head>
<body>
<form class="search" action="index.php">
<a href="/"><img src="static/gnutube.png" alt="gnutube"></a>
<input name="q" placeholder="Search" />
<input type="submit" style="display: none;"/>
</form>

113
static/styles.css Normal file
View file

@ -0,0 +1,113 @@
body {
background-color: #181818;
font-family: Arial, Helvetica, sans-serif;
color: #e8eaed;
word-break: break-all;
margin-left: 9%;
margin-right: 9%;
}
video {
width: 100%;
height: 700px;
}
.search {
text-align: center;
}
.search img {
float: left;
margin-left: 35px;
margin-top: 20px;
width: 55px;
height: 40px;
}
.search h3 {
margin-left: 10px;
}
.banner {
width: 100%;
height: 230px;
}
.search input {
width: 500px;
margin-bottom: 30px;
margin-top: 20px;
padding: 10px;
border: 1px solid #202020;
background-color: #121212;
color: inherit;
font-family: inherit;
font-size: 17px;
outline: none;
}
.video-results {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
grid-gap: 1.5rem;
justify-items: center;
padding: 0;
margin-bottom:50px;
}
.video-results a {
text-decoration: none;
color: inherit;
}
.video-results img {
width: 420px;
height: 220px;
border: 1px solid #bd93f9;
}
.channel-name a ,
.small-channel a {
color: #bd93f9;
text-decoration: none;
}
.small-channel img {
width: 50 !important;
height: 50 !important;
border-radius: 50%;
vertical-align: middle;
margin-right: 20px;
}
.description {
width: 50%;
font-size: 14px;
}
/* mobile view */
@media only screen and (max-width: 900px) {
body {
margin-left: 0%;
margin-right: 0%;
}
video {
height: auto;
}
.search input {
width: 90%;
}
.banner {
height: 100px;
}
.search img {
float: unset;
width: 80px;
height: 60px;
margin-left: 0%;
}
}

31
video_proxy.php Normal file
View file

@ -0,0 +1,31 @@
<?php
$config = require "config.php";
if (!$config->proxy_videos)
{
echo "Sorry but the host disabled this feature!";
die();
}
header("Content-Type: video/mp4");
ini_set("memory_limit", "1024M");
set_time_limit(3600);
$url = urldecode($_GET["url"]);
ob_start();
$opts["http"]["header"] = "Range: " . $_SERVER["HTTP_RANGE"];
$opts["http"]["method"] = "HEAD";
$conh=stream_context_create($opts);
$opts["http"]["method"] = "GET";
$cong = stream_context_create($opts);
$out[ ]= file_get_contents($url, false, $conh);
$out[] = $http_response_header;
ob_end_clean();
array_map("header", $http_response_header);
readfile($url, false, $cong);
?>

44
watch.php Normal file
View file

@ -0,0 +1,44 @@
<?php
require "static/header.xhtml";
$config = require "config.php";
require "misc/tools.php";
require "results/watch_results.php";
$video_id = $_REQUEST["v"];
$results = array();
get_watch_results($video_id);
$main_vid = $results[0];
echo "<video controls autoplay>";
if ($config->proxy_videos)
echo "<source src=\"/video_proxy.php?url=" . $main_vid["direct_url"] . "\" type=\"video/mp4\">";
else
echo "<source src=\"" . urldecode($main_vid["direct_url"]) . "\" type=\"video/mp4\">";
echo "</video>";
echo "<h3>" . $main_vid["title"] . "</h3>";
echo "<p>" . $main_vid["date"] . " - " . $main_vid["views"] . " - " . $main_vid["likes"] . "</p>";
echo "<p class=\"small-channel\"><img src=\"/image_proxy.php?url=" . $main_vid["thumbnail"] . "\" width=\"50\" height=\"40\"><a href=\"channel.php?id=" . $main_vid["channel_id"] . "\">" . $main_vid["channel_name"] . "</a> (" . $main_vid["subscribers"] . ")</p>";
//echo "<p class=\"description\">" . $main_vid["description"] . "</p>";
echo "<div class=\"video-results\">";
foreach ($results as $result)
{
if (array_key_exists("is_main_video", $result))
continue;
echo "<div>";
echo "<a href=\"watch.php?v=" . $result["video_id"] . "\">";
echo "<img src=\"/image_proxy.php?url=" . $result["thumbnail"] . "\" />";
echo "<p>" . $result["title"] . "</p>";
echo "</a>";
echo "<span class=\"channel-name\"><a href=\"channel.php?id=" . $result["channel_id"] . "\">" . $result["channel_name"] . "</a> - " . $result["date"] . " - " . $result["views"] . "</span>";
echo "</div>";
}
echo "</div>";
require "static/footer.xhtml";
?>