119 lines
4.5 KiB
PHP
119 lines
4.5 KiB
PHP
<?php
|
|
namespace Uiharu\Lookup;
|
|
|
|
use RuntimeException;
|
|
use Uiharu\Config;
|
|
use Uiharu\Url;
|
|
|
|
final class YouTubeLookup implements \Uiharu\ILookup {
|
|
private const SHORT_DOMAINS = [
|
|
'youtu.be', 'www.youtu.be', // www. doesn't work for this, but may as well cover it
|
|
];
|
|
|
|
private const VALID_TLDS = [
|
|
'ae', 'at', 'az', 'ba', 'be', 'bg', 'bh', 'bo', 'by',
|
|
'ca', 'cat', 'ch', 'cl', 'co', 'co.ae', 'co.at', 'co.cr', 'co.hu',
|
|
'co.id', 'co.il', 'co.in', 'co.jp', 'co.ke', 'co.kr', 'co.ma', 'co.nz',
|
|
'co.th', 'co.tz', 'co.ug', 'co.uk', 'co.ve', 'co.za', 'co.zw',
|
|
'com', 'com.ar', 'com.au', 'com.az', 'com.bd', 'com.bh', 'com.bo',
|
|
'com.br', 'com.by', 'com.co', 'com.do', 'com.ec', 'com.ee', 'com.eg',
|
|
'com.es', 'com.gh', 'com.gr', 'com.gt', 'com.hk', 'com.hn', 'com.hr',
|
|
'com.jm', 'com.jo', 'com.kw', 'com.lb', 'com.lv', 'com.ly', 'com.mk',
|
|
'com.mt', 'com.mx', 'com.my', 'com.ng', 'com.ni', 'com.om', 'com.pa',
|
|
'com.pe', 'com.ph', 'com.pk', 'com.pt', 'com.py', 'com.qa', 'com.ro',
|
|
'com.sa', 'com.sg', 'com.sv', 'com.tn', 'com.tr', 'com.tw', 'com.ua',
|
|
'com.uy', 'com.ve', 'cr', 'cz', 'de', 'dk', 'ee', 'es', 'fi', 'fr',
|
|
'ge', 'gr', 'gt', 'hk', 'hr', 'hu', 'ie', 'in', 'iq', 'is', 'it', 'jo',
|
|
'jp', 'kr', 'kz', 'lk', 'lt', 'lu', 'lv', 'ly', 'ma', 'me', 'mk', 'mx',
|
|
'my', 'net.in', 'ng', 'ni', 'nl', 'no', 'pa', 'pe', 'ph', 'pk', 'pl',
|
|
'pr', 'pt', 'qa', 'ro', 'rs', 'ru', 'sa', 'se', 'sg', 'si', 'sk', 'sn',
|
|
'sv', 'tn', 'ua', 'ug', 'uy', 'vn',
|
|
];
|
|
|
|
public static function isShortDomain(string $host): bool {
|
|
return in_array($host, self::SHORT_DOMAINS);
|
|
}
|
|
|
|
public function match(Url $url): bool {
|
|
if(!$url->isWeb())
|
|
return false;
|
|
|
|
$urlHost = $url->getHost();
|
|
if(self::isShortDomain($urlHost))
|
|
return true;
|
|
|
|
$parts = array_reverse(explode('.', $urlHost));
|
|
$partsCount = count($parts);
|
|
|
|
if($partsCount < 2 || $partsCount > 4)
|
|
return false;
|
|
|
|
if($parts[$partsCount - 1] === 'www')
|
|
array_pop($parts);
|
|
|
|
if($parts[0] === 'com') {
|
|
if($parts[1] !== 'youtube' && $parts[1] !== 'youtube-nocookie')
|
|
return false;
|
|
} else {
|
|
if(array_pop($parts) !== 'youtube')
|
|
return false;
|
|
|
|
$tld = implode('.', array_reverse($parts));
|
|
if(!in_array($tld, self::VALID_TLDS))
|
|
return false;
|
|
}
|
|
|
|
$urlPath = $url->getPath();
|
|
return $urlPath === '/watch'
|
|
|| str_starts_with($urlPath, '/watch/');
|
|
}
|
|
|
|
private function lookupVideo(string $videoId): ?object {
|
|
$curl = curl_init("https://www.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&id={$videoId}&key=" . Config::get('Google', 'apiKey'));
|
|
curl_setopt_array($curl, [
|
|
CURLOPT_AUTOREFERER => false,
|
|
CURLOPT_CERTINFO => false,
|
|
CURLOPT_FAILONERROR => false,
|
|
CURLOPT_FOLLOWLOCATION => false,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TCP_FASTOPEN => true,
|
|
CURLOPT_CONNECTTIMEOUT => 2,
|
|
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
|
|
CURLOPT_TIMEOUT => 5,
|
|
CURLOPT_USERAGENT => 'Uiharu/' . UIH_VERSION,
|
|
CURLOPT_HTTPHEADER => [
|
|
'Accept: application/json',
|
|
],
|
|
]);
|
|
$resp = curl_exec($curl);
|
|
curl_close($curl);
|
|
return json_decode($resp);
|
|
}
|
|
|
|
public function lookup(Url $url): YouTubeLookupResult {
|
|
$urlPath = $url->getPath();
|
|
parse_str($url->getQuery(), $urlQuery);
|
|
|
|
if(self::isShortDomain($url->getHost())) {
|
|
$videoId = substr($urlPath, 1);
|
|
} else {
|
|
if(str_starts_with($urlPath, '/watch/'))
|
|
$videoId = explode('/', trim($urlPath, '/'))[1] ?? '';
|
|
else
|
|
$videoId = $urlQuery['v'] ?? '';
|
|
}
|
|
|
|
if(empty($videoId))
|
|
throw new RuntimeException('YouTube video id missing.');
|
|
|
|
$videoInfo = $this->lookupVideo($videoId);
|
|
if($videoInfo === null)
|
|
throw new RuntimeException('YouTube video with given id could not be found.');
|
|
|
|
unset($urlQuery['v']);
|
|
$url = Url::parse(trim('https://www.youtube.com/watch?v=' . $videoId . '&' . http_build_query($urlQuery), '&'));
|
|
|
|
return new YouTubeLookupResult($url, $videoId, $videoInfo, $urlQuery);
|
|
}
|
|
}
|