<?php

//$yt = new youtube();
//header("Content-Type: application/json");
//echo json_encode($yt->video("minecraft", null, "today", "any", "any", "live", "relevance"));

class yt{
	
	public function __construct(){
		
		include "lib/backend.php";
		$this->backend = new backend("yt");
	}
	
	public function getfilters($page){
		
		if($page != "videos"){
			
			return [];
		}
		
		return [
			"date" => [
				"display" => "Time posted",
				"option" => [
					"any" => "Any time",
					"hour" => "Last hour",
					"today" => "Today",
					"week" => "This week",
					"month" => "This month",
					"year" => "This year"
				]
			],
			"type" => [
				"display" => "Type",
				"option" => [
					"video" => "Video",
					"channel" => "Channel",
					"playlist" => "Playlist",
					"Movie" => "Movie"
				]
			],
			"duration" => [
				"display" => "Duration",
				"option" => [
					"any" => "Any duration",
					"short" => "Short (>4min)",
					"medium" => "Medium (4-20min)",
					"long" => "Long (<20min)"
				]
			],
			"feature" => [
				"display" => "Feature",
				"option" => [
					"any" => "No features",
					"live" => "Live",
					"4k" => "4K",
					"hd" => "HD",
					"subtitles" => "Subtitles/CC",
					"creativecommons" => "Creative Commons",
					"360" => "VR 360°",
					"vr180" => "VR 180°",
					"3d" => "3D",
					"hdr" => "HDR"
				]
			],
			"sort" => [
				"display" => "Sort by",
				"option" => [
					"relevance" => "Relevance",
					"upload_date" => "Upload date",
					"view_count" => "View count",
					"rating" => "Rating"
				]
			]
		];
	}
	
	private function ytfilter($date, $type, $duration, $feature, $sort){
		
		// ------------
		// INCOMPATIBLE FILTERS
		// channel,playlist		DURATION, FEATURES, SORT BY
		// Movie				Features=[live, subtitles, creative commons, 3d]
		
		// live, 3D
		// Type[channel, playlist, movie]
		
		// UPLOAD DATE, DURATION, 4k, 360, VR180, HDR
		// Type[channel, playlist]
		
		// -----------
		
		// MUST BE TOGETHER
		// Relevance,upload date		Type=Video
		
		switch($type){
			
			case "channel":
			case "playlist":
				if($duration != "any"){ $duration = "any"; }
				if($feature != "any"){ $feature = "any"; }
				if($sort != "any"){ $sort = "any"; }
				break;
			
			case "movie":
				if(
					in_array(
						$feature,
						[
							"live",
							"subtitles",
							"creative_commons",
							"3d"
						],
					)
				){
					
					$feature = "any";
				}
				break;
		}
		
		switch($feature){
			
			case "live":
			case "3d":
				if(
					in_array(
						$type,
						[
							"channel",
							"playlist",
							"movie"
						],
					)
				){
					
					$type = "video";
				}
				break;
		}
		
		if(
			(
				$date != "any" ||
				$duration != "any" ||
				$feature == "4k" ||
				$feature == "360" ||
				$feature == "vr180" ||
				$feature == "hdr"
			) &&
			(
				$type == "channel" ||
				$type == "playlist"
			)
		){
			
			$type = "video";
		}
		
		if(
			$date == "any" &&
			$type == "video" &&
			$duration == "any" &&
			$feature == "any" &&
			$sort == "relevance"
		){
			
			return null;
		}
		
		//print_r([$date, $type, $duration, $feature, $sort]);
		
		/*
			Encode hex data
		*/
		
		// UPLOAD DATE
		// hour				EgQIARAB			12 04 08 01 10 01
		// today			EgQIAhAB			12 04 08 02 10 01
		// week				EgQIAxAB			12 04 08 03 10 01
		// month			EgQIBBAB			12 04 08 04 10 01
		// year				EgQIBRAB			12 04 08 05 10 01
		
		// TYPE
		// video			EgIQAQ%253D%253D	12 02 10 01
		// channel			EgIQAg%253D%253D	12 02 10 02
		// playlist			EgIQAw%253D%253D	12 02 10 03
		// movie			EgIQBA%253D%253D	12 02 10 04
		
		// DURATION
		// -4min			EgIYAQ%253D%253D	12 02 18 01
		// 4-20min			EgIYAw%253D%253D	12 02 18 03
		// 20+min			EgIYAg%253D%253D	12 02 18 02
		
		// FEATURE
		// live				EgJAAQ%253D%253D	12 02 40 01
		// 4K				EgJwAQ%253D%253D	12 02 70 01
		// HD				EgIgAQ%253D%253D	12 02 20 01
		// Subtitles/CC		EgIoAQ%253D%253D	12 02 28 01
		// Creative Commons	EgIwAQ%253D%253D	12 02 30 01
		// 360				EgJ4AQ%253D%253D	12 02 78 01
		// VR180			EgPQAQE%253D		12 03 d0 01 01
		// 3D				EgI4AQ%253D%253D	12 02 38 01
		// HDR				EgPIAQE%253D		12 03 c8 01 01
		// (location & purchased unused)
		
		// SORT BY
		// Relevance		CAASAhAB			08 00 12 02 10 01 (is nothing by default)
		// Upload date		CAI%253D			08 02
		// View count		CAM%253D			08 03
		// Rating			CAE%253D			08 01
		
		// video
		// 12 02 10 01
		
		// under 4 minutes
		// 12 02 18 01
		
		// video + under 4 minutes
		// 12 04 10 01 18 01
		
		// video + under 4 minutes + HD
		// 08 00 12 06 10 01 18 01 20 01
		
		// video + under 4 minutes + upload date
		// 08 02 12 04 10 01 18 01
		
		// video + under 4 minutes + HD + upload date
		// 08 02 12 06 10 01 18 01 20 01
		
		// this year + video + under 4 minutes + HD + upload date
		// 08 02 12 08 08 05 10 01 18 01 20 01
		
		// this week + video + over 20 minutes + HD + view count
		// 08 03 12 08 08 03 10 01 18 02 20 01
		
		//echo urlencode(urlencode(base64_encode(hex2bin($str))));
		//echo bin2hex(base64_decode(urldecode(urldecode("CAI%253D"))));
		
		// week + video + 20min + rating
		// 08 01 12 06 08 03 10 01 18 02
		
		// week + video + 20min + live + rating
		// 08 01 12 08 08 03 10 01 18 02 40 01
		
		// live 12 02 40 01
		
		$hex = null;
		if(
			$date == "any" &&
			$type == "video" &&
			$duration == "any" &&
			$feature == "any" &&
			$sort == "relevance"
		){
			
			return $hex;
		}
		
		$opcode = 0;
		
		if($date != "any"){ $opcode += 2; }
		if($type != "any"){ $opcode += 2; }
		if($duration != "any"){ $opcode += 2; }
		
		switch($feature){
			
			case "live":
			case "4k":
			case "hd":
			case "subtitles":
			case "creativecommons":
			case "360":
			case "3d":
				$opcode += 2;
				break;
			
			case "hdr":
			case "vr180":
				$opcode += 3;
				break;
		}
		
		switch($sort){
			
			case "relevance": $hex .= "0800"; break;
			case "upload_date": $hex .= "0802"; break;
			case "view_count": $hex .= "0803"; break;
			case "rating": $hex .= "0801"; break;
		}
		
		$hex .= "12" . "0".$opcode;
		
		switch($date){
			
			case "hour": $hex .= "0801"; break;
			case "today": $hex .= "0802"; break;
			case "week": $hex .= "0803"; break;
			case "month": $hex .= "0804"; break;
			case "year": $hex .= "0805"; break;
		}
		
		switch($type){
			
			case "video": $hex .= "1001"; break;
			case "channel": $hex .= "1002"; break;
			case "playlist": $hex .= "1003"; break;
			case "movie": $hex .= "1004"; break;
		}
		
		switch($duration){
			
			case "short": $hex .= "1801"; break;
			case "medium": $hex .= "1803"; break;
			case "long": $hex .= "1802"; break;
		}
		
		switch($feature){
			
			case "live": $hex .= "4001"; break;
			case "4k": $hex .= "7001"; break;
			case "hd": $hex .= "2001"; break;
			case "subtitles": $hex .= "2801"; break;
			case "creativecommons": $hex .= "3001"; break;
			case "360": $hex .= "7801"; break;
			case "vr180": $hex .= "d00101"; break;
			case "3d": $hex .= "3801"; break;
			case "hdr": $hex .= "c80101"; break;
		}
		
		//echo $hex . "\n\n";
		return urlencode(base64_encode(hex2bin($hex)));
	}
	
	// me reading youtube's json
	// https://imgur.com/X9hVlFX
	
	const req_web = 0;
	const req_xhr = 1;
	
	private function get($proxy, $url, $get = [], $reqtype = self::req_web, $continuation = null){
		
		$curlproc = curl_init();
		
		if($get !== []){
			$get = http_build_query($get);
			$url .= "?" . $get;
		}
		
		curl_setopt($curlproc, CURLOPT_URL, $url);
		
		switch($reqtype){
			case self::req_web:
				$headers =
					["User-Agent: " . config::USER_AGENT,
					"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
					"Accept-Language: en-US,en;q=0.5",
					"Accept-Encoding: gzip",
					"Cookie: PREF=tz=America.New_York",
					"DNT: 1",
					"Connection: keep-alive",
					"Upgrade-Insecure-Requests: 1",
					"Sec-Fetch-Dest: document",
					"Sec-Fetch-Mode: navigate",
					"Sec-Fetch-Site: none",
					"Sec-Fetch-User: ?1"];
				break;
			
			case self::req_xhr:
				$headers =
					["User-Agent: " . config::USER_AGENT,
					"Accept: */*",
					"Accept-Language: en-US,en;q=0.5",
					"Accept-Encoding: gzip",
					"Cookie: PREF=tz=America.New_York",
					"Referer: https://youtube.com.com/",
					"Content-Type: application/json",
					"Content-Length: " . strlen($continuation),
					"DNT: 1",
					"Connection: keep-alive",
					"Sec-Fetch-Dest: empty",
					"Sec-Fetch-Mode: same-origin",
					"Sec-Fetch-Site: same-origin"];
				
				curl_setopt($curlproc, CURLOPT_POST, true);
				curl_setopt($curlproc, CURLOPT_POSTFIELDS, $continuation);
				break;
		}
		
		curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
		curl_setopt($curlproc, CURLOPT_HTTPHEADER, $headers);
		
		curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
		curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
		curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
		curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);

		$this->backend->assign_proxy($curlproc, $proxy);
		
		$data = curl_exec($curlproc);
		
		if(curl_errno($curlproc)){
			
			throw new Exception(curl_error($curlproc));
		}
		
		curl_close($curlproc);
		return $data;
	}
	
	public function video($get){
		
		$this->out = [
			"status" => "ok",
			"npt" => null,
			"video" => [],
			"author" => [],
			"livestream" => [],
			"playlist" => [],
			"reel" => []
		];
		
		if($get["npt"]){
			
			// parse nextPage
			// https://www.youtube.com/youtubei/v1/search?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8&prettyPrint=false
			/*
			$handle = fopen("nextpage.json", "r");
			$json = fread($handle, filesize("nextpage.json"));
			fclose($handle);*/
			
			[$npt, $proxy] =
				$this->backend->get(
					$get["npt"],
					"videos"
				);
			
			$npt = json_decode($npt, true);
			
			try{
				$json = $this->get(
					$proxy,
					"https://www.youtube.com/youtubei/v1/search",
					[
						"key" => $npt["key"],
						"prettyPrint" => "false"
					],
					self::req_xhr,
					json_encode($npt["post"])
				);
			}catch(Exception $error){
				
				throw new Exception("Could not fetch results page");
			}
			
			$json = json_decode($json);
			
			foreach(
				$json
				->onResponseReceivedCommands[0]
				->appendContinuationItemsAction
				->continuationItems[0]
				->itemSectionRenderer
				->contents
				as $video
			){
				
				$this->parsevideoobject($video);
			}
			
			if(
				!isset(
					$json
					->onResponseReceivedCommands[0]
					->appendContinuationItemsAction
					->continuationItems[1]
					->continuationItemRenderer
					->continuationEndpoint
					->continuationCommand
					->token
				)
			){
				
				$npt = null;
				
			}else{
				// prepare nextpage for later..
				$npt["post"]["continuation"] =
					$json
					->onResponseReceivedCommands[0]
					->appendContinuationItemsAction
					->continuationItems[1]
					->continuationItemRenderer
					->continuationEndpoint
					->continuationCommand
					->token;
			}
			
			$this->out["npt"] = $npt;
			
		}else{
			
			$search = $get["s"];
			if(strlen($search) === 0){
				
				throw new Exception("Search term is empty!");
			}
			
			$proxy = $this->backend->get_ip();
			$date = $get["date"];
			$type = $get["type"];
			$duration = $get["duration"];
			$feature = $get["feature"];
			$sort = $get["sort"];
			
			// parse ytInitialData
			
			$get = [
				"search_query" => $search
			];
			
			if(
				(
					$filter =
					$this->ytfilter(
						$date,
						$type,
						$duration,
						$feature,
						$sort
					)
				) !== null
			){
				
				$get["sp"] = $filter;
			}
			
			try{
				$json = $this->get(
					$proxy,
					"https://www.youtube.com/results",
					$get
				);
			}catch(Exception $error){
				
				throw new Exception("Could not fetch results page");
			}
			/*
			$handle = fopen("test.html", "r");
			$json = fread($handle, filesize("test.html"));
			fclose($handle);
			*/
			if(
				!preg_match(
					'/ytcfg\.set\(({".*})\); *window\.ytcfg/',
					$json,
					$ytconfig
				)
			){
				
				throw new Exception("Could not get ytcfg");
			}
			
			$ytconfig = json_decode($ytconfig[1]);
			
			if(
				!preg_match(
					'/ytInitialData *= *({.*});<\/script>/',
					$json,
					$json
				)
			){
				
				throw new Exception("Could not get ytInitialData");
			}
			
			$json = json_decode($json[1]);
			
			// generate POST data for nextpage
			
			$ytconfig->INNERTUBE_CONTEXT->client->screenWidthPoints = 1239;
			$ytconfig->INNERTUBE_CONTEXT->client->screenHeightPoints = 999;
			$ytconfig->INNERTUBE_CONTEXT->client->screenPixelDensity = 1;
			$ytconfig->INNERTUBE_CONTEXT->client->screenDensityFloat = 1;
			$ytconfig->INNERTUBE_CONTEXT->client->utcOffsetMinutes = -240;
			$ytconfig->INNERTUBE_CONTEXT->request->internalExperimentFlags = [];
			$ytconfig->INNERTUBE_CONTEXT->request->consistencyTokenJars = [];
			
			$ytconfig->INNERTUBE_CONTEXT->client->mainAppWebInfo = [
				"graftUrl" => $ytconfig->INNERTUBE_CONTEXT->client->originalUrl,
				"webDisplayMode" => "WEB_DISPLAY_MODE_BROWSER",
				"isWebNativeShareAvailable" => false
			];
			
			$ytconfig->INNERTUBE_CONTEXT->adSignalsInfo = [
				"params" => [
					[
						"key" => "dt",
						"value" => (string)$ytconfig->TIME_CREATED_MS
					],
					[
						"key" => "flash",
						"value" => "0"
					],
					[
						"key" => "frm",
						"value" => "0"
					],
					[
						"key" => "u_tz",
						"value" => "-240"
					],
					[
						"key" => "u_his",
						"value" => "3"
					],
					[
						"key" => "u_h",
						"value" => "1080"
					],
					[
						"key" => "u_w",
						"value" => "1920"
					],
					[
						"key" => "u_ah",
						"value" => "1080"
					],
					[
						"key" => "u_cd",
						"value" => "24"
					],
					[
						"key" => "bc",
						"value" => "31"
					],
					[
						"key" => "bih",
						"value" => "999"
					],
					[
						"key" => "biw",
						"value" => "1239"
					],
					[
						"key" => "brdim",
						"value" => "0,0,0,0,1920,0,1920,1061,1239,999"
					],
					[
						"key" => "vis",
						"value" => "1"
					],
					[
						"key" => "wgl",
						"value" => "true"
					],
					[
						"key" => "ca_type",
						"value" => "image"
					]
				]
			];
			
			/*
			echo json_encode($json);
			die();*/
			
			// *inhales*
			foreach(
				$json
				->contents
				->twoColumnSearchResultsRenderer
				->primaryContents
				->sectionListRenderer
				->contents[0]
				->itemSectionRenderer
				->contents
				as $video
			){
				
				$this->parsevideoobject($video);
			}
			
			// get additional data from secondaryContents
			if(
				isset(
					$json
					->contents
					->twoColumnSearchResultsRenderer
					->secondaryContents
					->secondarySearchContainerRenderer
					->contents[0]
					->universalWatchCardRenderer
				)
			){
				
				$video =
					$json
					->contents
					->twoColumnSearchResultsRenderer
					->secondaryContents
					->secondarySearchContainerRenderer
					->contents[0]
					->universalWatchCardRenderer;
				/*
				echo json_encode($video);
				die();*/
				
				$author =
					[
						"name" =>
							$video
							->header
							->watchCardRichHeaderRenderer
							->title
							->simpleText,
						"url" =>
							"https://www.youtube.com/channel/" .
							$video
							->header
							->watchCardRichHeaderRenderer
							->titleNavigationEndpoint
							->browseEndpoint
							->browseId,
						"avatar" => null
					];
				
				if(
					isset(
						$video
						->header
						->watchCardRichHeaderRenderer
						->avatar
						->thumbnails[0]
						->url
					)
				){
					
					$author["avatar"] =
						$video
						->header
						->watchCardRichHeaderRenderer
						->avatar
						->thumbnails[0]
						->url;
				}
				
				// add video in callToAction if present
				if(
					isset(
						$video
						->callToAction
						->watchCardHeroVideoRenderer
						->lengthText
					)
				){
					
					array_push(
						$this->out["video"],
						[
							"title" =>
								$video
								->callToAction
								->watchCardHeroVideoRenderer
								->title
								->simpleText,
							"description" => null,
							"author" => $author,
							"date" =>
								$this->textualdate2unix(
									trim(
										explode(
											"•",
											$video
											->callToAction
											->watchCardHeroVideoRenderer
											->subtitle
											->simpleText
										)[2]
									)
								),
							"duration" =>
								$this->hms2int(
									$video
									->callToAction
									->watchCardHeroVideoRenderer
									->lengthText
									->simpleText
								),
							"views" =>
								$this->truncatedcount2int(
									trim(
										explode(
											"•",
											$video
											->callToAction
											->watchCardHeroVideoRenderer
											->subtitle
											->simpleText,
											2
										)[1]
									)
								),
							"thumb" => [
								"url" =>
									$video
									->callToAction
									->watchCardHeroVideoRenderer
									->heroImage
									->singleHeroImageRenderer
									->thumbnail
									->thumbnails[0]
									->url,
								"ratio" => "16:9"
							],
							"url" =>
								"https://www.youtube.com/watch?v=" .
								$video
								->callToAction
								->watchCardHeroVideoRenderer
								->navigationEndpoint
								->watchEndpoint
								->videoId
						]
					);
				}
				
				// get all playlists, ignore videos
				$out = null;
				
				foreach(
					$video
					->sections
					as $section
				){
					
					if(
						isset(
							$section
							->watchCardSectionSequenceRenderer
							->lists[0]
							->horizontalCardListRenderer
							->cards
						)
					){
						
						$out =
							$section
							->watchCardSectionSequenceRenderer
							->lists[0]
							->horizontalCardListRenderer
							->cards;
						break;
					}
				}
				
				if($out !== null){
					
					foreach(
						$out as $video
					){
						
						if(
							!isset(
								$video
								->searchRefinementCardRenderer
							)
						){
							
							continue;
						}
						
						$video =
							$video
							->searchRefinementCardRenderer;
						
						array_push(
							$this->out["playlist"],
							[
								"title" =>
									$video
									->query
									->runs[0]
									->text,
								"description" => null,
								"author" => $author,
								"date" => null,
								"duration" => null,
								"views" => null,
								"thumb" => [
									"url" =>
										$video
										->thumbnail
										->thumbnails[0]
										->url,
									"ratio" => "1:1"
								],
								"url" =>
									"https://www.youtube.com" .
									$video
									->searchEndpoint
									->commandMetadata
									->webCommandMetadata
									->url
							]
						);
					}
				}
			}
			
			foreach(
				$json
				->contents
				->twoColumnSearchResultsRenderer
				->primaryContents
				->sectionListRenderer
				->contents
				as $cont
			){
				
				if(isset($cont->continuationItemRenderer)){
					
					$this->out["npt"] = [
						"key" =>
							$ytconfig
							->INNERTUBE_API_KEY,
						"post" => [
							"context" =>
								$ytconfig
								->INNERTUBE_CONTEXT,
							"continuation" =>
								$cont
								->continuationItemRenderer
								->continuationEndpoint
								->continuationCommand
								->token
						]
					];
					break;
				}
			}
		}
		
		if($this->out["npt"] !== null){
			
			$this->out["npt"] =
				$this->backend->store(
					json_encode(
						$this->out["npt"]
					),
					"videos",
					$proxy
				);
		}
		
		return $this->out;
	}
	
	private function parsevideoobject($video){
		
		if(isset($video->videoRenderer)){
			
			$video = $video->videoRenderer;
			
			$description = null;
			
			if(isset($video->detailedMetadataSnippets)){
				foreach(
					$video
					->detailedMetadataSnippets[0]
					->snippetText
					->runs
					as $description_part
				){
					
					$description .= $description_part->text;
				}
			}
			
			if(
				isset(
					$video
					->badges[0]
					->metadataBadgeRenderer
					->icon
					->iconType
				) &&
				$video
				->badges[0]
				->metadataBadgeRenderer
				->icon
				->iconType
				== "LIVE"
			){
				
				$type = "livestream";
				$date = null;
				$duration = "_LIVE";
				
				if(isset($video->viewCountText->runs[0]->text)){
					
					$views =
						$this->views2int(
							$video
							->viewCountText
							->runs[0]
							->text
						);
				}else{
					
					$views = null;
				}
			}else{
				
				$type = "video";
				
				if(isset($video->publishedTimeText->simpleText)){
					
					$date = $this->textualdate2unix(
						$video
						->publishedTimeText
						->simpleText
					);
				}else{
					
					$date = null;
				}
				
				if(isset($video->lengthText->simpleText)){
					
					$duration =
						$this->hms2int(
							$video
							->lengthText
							->simpleText
						);
				}else{
					
					$duration = null;
				}
				
				if(isset($video->viewCountText->simpleText)){
					
					$views =
						$this->views2int(
							$video
							->viewCountText
							->simpleText
						);
				}else{
					
					$views = null;
				}
			}
			
			if(
				$video
				->navigationEndpoint
				->commandMetadata
				->webCommandMetadata
				->webPageType
				== "WEB_PAGE_TYPE_SHORTS"
			){
				
				// haha you thought you could get me, youtube
				// jokes on you i dont go outside
				$type = "reel";
			}
			
			array_push(
				$this->out[$type],
				[
					"title" =>
						$video
						->title
						->runs[0]
						->text,
					"description" =>
						$this->titledots($description),
					"author" => [
							"name" =>
								$video
								->longBylineText
								->runs[0]
								->text,
							"url" =>
								"https://www.youtube.com/channel/" .
								$video
								->longBylineText
								->runs[0]
								->navigationEndpoint
								->browseEndpoint
								->browseId,
							"avatar" =>
								$this->checkhttpspresence(
									$video
									->channelThumbnailSupportedRenderers
									->channelThumbnailWithLinkRenderer
									->thumbnail
									->thumbnails[0]
									->url
								)
						],
					"date" => $date,
					"duration" => $duration,
					"views" => $views,
					"thumb" => [
						"url" =>
							$video
							->thumbnail
							->thumbnails[0]
							->url,
						"ratio" => "16:9"
					],
					"url" =>
						"https://www.youtube.com/watch?v=" .
						$video
						->videoId
				]
			);
		}elseif(isset($video->watchCardCompactVideoRenderer)){
			
			$video =
				$video
				->watchCardCompactVideoRenderer;
			
			array_push(
				$this->out["video"],
				[
					"title" =>
						$video
						->title
						->simpleText,
					"description" => null,
					"author" => [
							"name" =>
								$video
								->byline
								->runs[0]
								->text,
							"url" =>
								"https://www.youtube.com/channel/" .
								$video
								->byline
								->runs[0]
								->navigationEndpoint
								->browseEndpoint
								->browseId,
							"avatar" => null
						],
					"date" =>
						$this->textualdate2unix(
							trim(
								explode(
									"•",
									$video
									->subtitle
									->simpleText,
									2
								)[1]
							)
						),
					"duration" =>
						$this->hms2int(
							$video
							->lengthText
							->simpleText
						),
					"views" =>
						$this->truncatedcount2int(
							trim(
								explode(
									"•",
									$video
									->subtitle
									->simpleText,
									2
								)[0]
							)
						),
					"thumb" => [
						"url" =>
							$video
							->thumbnail
							->thumbnails[0]
							->url,
						"ratio" => "16:9"
					],
					"url" =>
						"https://www.youtube.com/watch?v=" .
						$video
						->navigationEndpoint
						->watchEndpoint
						->videoId
				]
			);
			
		}elseif(isset($video->reelShelfRenderer)){
			
			foreach(
				$video
				->reelShelfRenderer
				->items
				as $reel
			){
				
				$reel =
					$reel
					->shortsLockupViewModel;
				
				array_push(
					$this->out["reel"],
					[
						"title" =>
							$reel
							->overlayMetadata
							->primaryText
							->content,
						"description" => null,
						"author" => [
							"name" => null,
							"url" => null,
							"avatar" => null
						],
						"date" => null,
						"duration" => null,
						"views" => null,
						"thumb" => [
							"url" =>
								$reel
								->thumbnail
								->sources[0]
								->url,
							"ratio" => "9:16"
						],
						"url" =>
							"https://www.youtube.com/watch?v=" .
							$reel
							->onTap
							->innertubeCommand
							->reelWatchEndpoint
							->videoId
					]
				);
			}
		}
		
		elseif(isset($video->channelRenderer)){
			
			$video = $video->channelRenderer;
			
			$description = null;
			
			if(isset($video->descriptionSnippet)){
				
				foreach(
					$video
					->descriptionSnippet
					->runs
					as $description_part
				){
					
					$description .= $description_part->text;
				}
			}
			
			array_push(
				$this->out["author"],
				[
					"title" =>
						$video
						->title
						->simpleText,
					"followers" =>
						isset(
							$video
							->videoCountText
							->simpleText
						) ?
						$this->truncatedcount2int(
							$video
							->videoCountText
							->simpleText
						) :
						0,
					"description" => $this->titledots($description),
					"thumb" =>
						[
							"url" =>
								$this->checkhttpspresence(
									$video
									->thumbnail
									->thumbnails[
										count(
											$video
											->thumbnail
											->thumbnails
										) - 1
									]
									->url
								),
							"ratio" => "1:1"
						],
					"url" =>
						"https://www.youtube.com/channel/" .
						$video
						->channelId
				]
			);
		}
		
		elseif(isset($video->shelfRenderer)){
			
			if(
				!is_object(
					$video
					->shelfRenderer
					->content
					->verticalListRenderer
				)
			){
				return;
			}
			
			foreach(
				$video
				->shelfRenderer
				->content
				->verticalListRenderer
				->items
				as $shelfvideo
			){
				
				$this->parsevideoobject($shelfvideo);
			}
			
		}elseif(isset($video->radioRenderer)){
			
			$video = $video->radioRenderer;
			
			$description =
				$video
				->videoCountText
				->runs[0]
				->text
				. ".";
			
			$tmp = [];
			foreach(
				$video->videos
				as $childvideo
			){
				
				$tmp[] =
					$childvideo
					->childVideoRenderer
					->title
					->simpleText;
			}
			
			if(count($tmp) !== 0){
				
				$description .=
					" " . implode(", ", $tmp);
			}
			
			array_push(
				$this->out["playlist"],
				[
					"title" =>
						$video
						->title
						->simpleText,
					"description" => $description,
					"author" => [
						"name" =>
							$video
							->longBylineText
							->simpleText,
						"url" => null,
						"avatar" => null
					],
					"date" => null,
					"duration" => null,
					"views" => null,
					"thumb" => [
						"url" =>
							$video
							->thumbnail
							->thumbnails[
								count(
									$video
									->thumbnail
									->thumbnails
								) - 1
							]
							->url,
						"ratio" => "16:9"
					],
					"url" =>
						"https://www.youtube.com/watch?v=" .
						$video
						->videos[0]
						->childVideoRenderer
						->videoId .
						"&list=" .
						$video
						->playlistId .
						"&start_radio=1"
				]
			);
			
		}elseif(isset($video->playlistRenderer)){
			
			$video = $video->playlistRenderer;
			
			$description = $video->videoCount . " videos.";
			
			$tmp = [];
			foreach(
				$video
				->videos
				as $childvideo
			){
				
				$tmp[] =
					$childvideo
					->childVideoRenderer
					->title
					->simpleText;
			}
			
			if(count($tmp) !== 0){
				
				$description .=
					" " . implode(", ", $tmp);
			}
			
			array_push(
				$this->out["playlist"],
				[
					"title" =>
						$video
						->title
						->simpleText,
					"description" => $description,
					"author" => [
						"name" =>
							$video
							->longBylineText
							->runs[0]
							->text,
						"url" =>
							"https://www.youtube.com/channel/" .
							$video
							->longBylineText
							->runs[0]
							->navigationEndpoint
							->browseEndpoint
							->browseId,
						"picture" => null
					],
					"date" => null,
					"duration" => null,
					"views" => null,
					"thumb" =>
						[
							"url" =>
								$video
								->thumbnails[0]
								->thumbnails[
									count(
										$video
										->thumbnails[0]
										->thumbnails
									) - 1
								]
								->url,
							"ratio" => "16:9"
						],
					"url" =>
						"https://www.youtube.com/watch?v=" .
						$video
						->videos[0]
						->childVideoRenderer
						->videoId .
						"&list=" .
						$video
						->playlistId .
						"&start_radio=1"
				]
			);
			
		}/*else{
			if(!isset($video->searchPyvRenderer)){
			echo json_encode($video);
			die();}
		}*/
	}
	
	private function textualdate2unix($number){
		
		$number =
			explode(
				" ",
				str_replace(
						[
							" ago",
							"seconds",
							"minutes",
							"hours",
							"days",
							"weeks",
							"months",
							"years"
						],
						[
							"",
							"second",
							"minute",
							"hour",
							"day",
							"week",
							"month",
							"year"
						],
						$number
					),
				2
			);
		
		$time = 0;
		switch($number[1]){
			
			case "second":
				$time = (int)$number[0];
				break;
			
			case "minute":
				$time = (int)$number[0] * 60;
				break;
			
			case "hour":
				$time = (int)$number[0] * 3600;
				break;
				
			case "day":
				$time = (int)$number[0] * 86400;
				break;
				
			case "week":
				$time = (int)$number[0] * 604800;
				break;
				
			case "month":
				$time = (int)$number[0] * 2629746;
				break;
				
			case "year":
				$time = (int)$number[0] * 31556952;
				break;
		}
		
		return time() - $time;
	}
	
	private function checkhttpspresence($link){
		
		if(substr($link, 0, 2) == "//"){
			
			return "https:" . $link;
		}
		
		return $link;
	}
	
	private function textualtime2int($number){
		
		$number = explode(" - ", $number);
		
		if(count($number) >= 2){
			
			$number = $number[count($number) - 2];
		}else{
			
			$number = $number[0];
		}
		
		$number =
			str_replace(
				[
					" ",
					"seconds",
					"minutes",
					"hours",
				],
				[
					"",
					"second",
					"minute",
					"hour"
				],
				$number
			);
		
		preg_match_all(
			'/([0-9]+)(second|minute|hour)/',
			$number,
			$number
		);
		
		$time = 0;
		
		for($i=0; $i<count($number[0]); $i++){
			
			switch($number[2][$i]){
				
				case "second":
					$time = $time + (int)$number[1][$i];
					break;
					
				case "minute":
					$time = $time + ((int)$number[1][$i] * 60);
					break;
					
				case "hour":
					$time = $time + ((int)$number[1][$i] * 3600);
					break;
			}
		}
		
		return $time;
	}
	
	private function views2int($views){
		
		return
			(int)str_replace(
				",", "",
				explode(" ", $views, 2)[0]
			);
	}
	
	private function hms2int($time){
		
		$parts = explode(":", $time, 3);
		$time = 0;
		
		if(count($parts) === 3){
			
			// hours
			$time = $time + ((int)$parts[0] * 3600);
			array_shift($parts);
		}
		
		if(count($parts) === 2){
			
			// minutes
			$time = $time + ((int)$parts[0] * 60);
			array_shift($parts);
		}
		
		// seconds
		$time = $time + (int)$parts[0];
		
		return $time;
	}
	
	private function truncatedcount2int($number){
		
		// decimal should always be 1 number long
		$number = explode(" ", $number, 2);
		$number = $number[0];
		
		$unit = strtolower($number[strlen($number) - 1]);
		
		$tmp = explode(".", $number, 2);
		$number = (int)$number;
		
		if(count($tmp) === 2){
			
			$decimal = (int)$tmp[1];
		}else{
			
			$decimal = 0;
		}
		
		switch($unit){
			
			case "k":
				$exponant = 1000;
				break;
			
			case "m":
				$exponant = 1000000;
				break;
			
			case "b";
				$exponant = 1000000000;
				break;
			
			default:
				$exponant = 1;
				break;
		}
		
		return ($number * $exponant) + ($decimal * ($exponant / 10));
	}
	
	private function titledots($title){
		
		$substr = substr($title, -3);
		
		if(
			$substr == "..." ||
			$substr == "…"
		){
			
			return trim(substr($title, 0, -3), " \n\r\t\v\x00\0\x0B\xc2\xa0");
		}
		
		return trim($title, " \n\r\t\v\x00\0\x0B\xc2\xa0");
	}
}
