Added Index library to Uiharu.

This commit is contained in:
flash 2022-07-14 20:42:37 +00:00
parent 1ae7ec9f19
commit 0a668992d9
12 changed files with 217 additions and 548 deletions
public

View file

@ -1,10 +1,100 @@
<?php
define('UIH_VERSION', '20201009');
define('UIH_DEBUG', isset($_GET['_debug']));
define('UIH_CACHE', !UIH_DEBUG && !isset($_GET['_skip']));
namespace Uiharu;
use stdClass;
use Index\MediaType;
use Index\Performance\Stopwatch;
require_once __DIR__ . '/../uiharu.php';
define('UIH_CACHE', !UIH_DEBUG || isset($_GET['_cache']));
define('UIH_INCLUDE_RAW', UIH_DEBUG || isset($_GET['include_raw']));
define('UIH_SEM_NAME', 'U');
define('UIH_SEM_PATH', sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'uiharu');
function uih_media_type_json(MediaType $mediaType): array {
$parts = [
'string' => (string)$mediaType,
'type' => $mediaType->getCategory(),
'subtype' => $mediaType->getKind(),
];
if(!empty($suffix = $mediaType->getSuffix()))
$parts['suffix'] = $suffix;
if(!empty($params = $mediaType->getParams()))
$parts['params'] = $params;
return $parts;
}
function uih_parse_url(string $url): array|false {
$parts = parse_url($url);
if($parts === false)
return false;
// v1 compat
$parts['uri'] = uih_build_url($parts);
if(isset($parts['pass']))
$parts['password'] = $parts['pass'];
return $parts;
}
function uih_build_url(array $parts): string {
$string = '';
if(!empty($parts['scheme']))
$string .= $parts['scheme'] . ':';
$authority = '';
if(isset($parts['user']) || isset($parts['pass'])) {
if(isset($parts['user']))
$authority .= $parts['user'];
if(isset($parts['pass']))
$authority .= ':' . $parts['pass'];
$authority .= '@';
}
if(isset($parts['host'])) {
$authority .= $parts['host'];
if(isset($parts['port']))
$authority .= ':' . $parts['port'];
}
$hasAuthority = !empty($authority);
if($hasAuthority)
$string .= '//' . $authority;
$path = $parts['path'] ?? '';
$hasPath = !empty($path);
if($hasAuthority && (!$hasPath || $path[0] !== '/'))
$string .= '/';
elseif(!$hasAuthority && $path[1] === '/')
$path = '/' . trim($path, '/');
$string .= $path;
if(!empty($parts['query'])) {
$string .= '?';
$queryParts = explode('&', $parts['query']);
foreach($queryParts as $queryPart) {
$kvp = explode('=', $queryPart, 2);
$string .= rawurlencode($kvp[0]);
if(isset($kvp[1]))
$string .= '=' . rawurlencode($kvp[1]);
$string .= '&';
}
$string = substr($string, 0, -1);
}
if(!empty($parts['fragment']))
$string .= '#' . rawurlencode($parts['fragment']);
return $string;
}
function uih_eeprom_lookup(stdClass $resp, string $eepromFileId, string $domain = 'flashii'): void {
$resp->type = 'eeprom:file';
@ -39,22 +129,12 @@ function uih_eeprom_lookup(stdClass $resp, string $eepromFileId, string $domain
if(!is_dir(UIH_SEM_PATH))
mkdir(UIH_SEM_PATH, 0777, true);
require_once __DIR__ . '/../config.php';
header('X-Powered-By: Uiharu');
ini_set('display_errors', UIH_DEBUG ? 'on' : 'off');
error_reporting(UIH_DEBUG ? -1 : 0);
$db->execute('DELETE FROM `uih_metadata_cache` WHERE `metadata_created` < NOW() - INTERVAL 7 DAY');
set_include_path(realpath(__DIR__ . '/../lib/') . PATH_SEPARATOR . get_include_path());
spl_autoload_extensions('.php');
spl_autoload_register();
DB::init(UIH_PDO_DSN, UIH_PDO_USER, UIH_PDO_PASS, DB::ATTRS);
DB::exec('DELETE FROM `uih_metadata_cache` WHERE `metadata_created` < NOW() - INTERVAL 7 DAY');
$reqMethod = filter_input(INPUT_SERVER, 'REQUEST_METHOD', FILTER_SANITIZE_STRING);
$reqPath = '/' . trim(parse_url(filter_input(INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_STRING), PHP_URL_PATH), '/');
$reqMethod = filter_input(INPUT_SERVER, 'REQUEST_METHOD');
$reqPath = '/' . trim(parse_url(filter_input(INPUT_SERVER, 'REQUEST_URI'), PHP_URL_PATH), '/');
$reqHead = false;
if($reqMethod == 'HEAD') {
@ -98,15 +178,15 @@ if($reqPath === '/metadata') {
if($reqHead)
return;
Stopwatch::start();
$sw = Stopwatch::startNew();
$resp = new stdClass;
if($_SERVER['HTTP_HOST'] === 'mii.flashii.net') {
$resp->type = 'object';
$resp->content_type = new stdClass;
$resp->content_type->string = 'application/x-update-your-script-to-use-uiharu.flashii.net-instead-of-mii.flashii.net';
$resp->content_type->type = 'text';
$resp->content_type->subtype = 'deprecation';
$resp->content_type = [];
$resp->content_type['string'] = 'application/x-update-your-script-to-use-uiharu.flashii.net-instead-of-mii.flashii.net';
$resp->content_type['type'] = 'text';
$resp->content_type['subtype'] = 'deprecation';
$resp->title = 'Update your URLs: mii.flashii.net -> uiharu.flashii.net';
$resp->description = 'Update your URLs: mii.flashii.net -> uiharu.flashii.net';
$resp->site_name = 'Deprecation notice';
@ -121,20 +201,23 @@ if($reqPath === '/metadata') {
$targetUrl = (string)filter_input(INPUT_GET, 'url');
}
try {
$resp->uri = $parsedUrl = new Uri($targetUrl);
} catch(InvalidArgumentException $ex) {
$parsedUrl = uih_parse_url($targetUrl);
if($parsedUrl === false) {
http_response_code(400);
$resp->error = 'metadata:uri';
echo json_encode($resp);
return;
}
// if no scheme is specified, try https
if($parsedUrl->getScheme() === '')
$parsedUrl = new Uri('https://' . (string)$parsedUrl);
$resp->uri = $parsedUrl;
$urlHash = $parsedUrl->getHash();
// if no scheme is specified, try https
if(empty($parsedUrl['scheme'])) {
$parsedUrl['scheme'] = 'https';
$parsedUrl = uih_parse_url(uih_build_url($parsedUrl));
}
$urlHash = hash('sha256', uih_build_url($parsedUrl));
try {
$semPath = UIH_SEM_PATH . DIRECTORY_SEPARATOR . $urlHash;
@ -145,30 +228,31 @@ if($reqPath === '/metadata') {
while(!sem_acquire($semaphore)) usleep(100);
if(UIH_CACHE) {
$loadCache = DB::prepare('SELECT `metadata_resp` AS `resp` FROM `uih_metadata_cache` WHERE `metadata_url` = UNHEX(:hash) AND `metadata_created` > NOW() - INTERVAL 10 MINUTE')
->bind('hash', $urlHash)
->fetchObject();
if(isset($loadCache->resp)) {
$cacheResp = json_decode($loadCache->resp);
$cacheFetch = $db->prepare('SELECT `metadata_resp` FROM `uih_metadata_cache` WHERE `metadata_url` = UNHEX(?) AND `metadata_created` > NOW() - INTERVAL 10 MINUTE');
$cacheFetch->addParameter(1, $urlHash);
$cacheFetch->execute();
$cacheResult = $cacheFetch->getResult();
if($cacheResult->next()) {
$cacheResp = json_decode($cacheResult->getString(0));
if($cacheResp !== null)
$resp = $cacheResp;
}
}
if(empty($resp->type)) {
$urlScheme = strtolower($parsedUrl->getScheme());
$urlHost = strtolower($parsedUrl->getHost());
$urlPath = '/' . trim($parsedUrl->getPath(), '/');
$urlScheme = strtolower($parsedUrl['scheme']);
$urlHost = strtolower($parsedUrl['host']);
$urlPath = '/' . trim($parsedUrl['path'], '/');
if($urlScheme === 'eeprom') {
if(preg_match('#^([A-Za-z0-9-_]+)$#', $parsedUrl->getPath(), $matches)) {
$resp->uri = $parsedUrl = new Uri('https://i.fii.moe/' . $matches[1]);
if(preg_match('#^([A-Za-z0-9-_]+)$#', $parsedUrl['path'], $matches)) {
$resp->uri = $parsedUrl = uih_parse_url('https://i.fii.moe/' . $matches[1]);
$continueRaw = true;
uih_eeprom_lookup($resp, $matches[1]);
}
} elseif($urlScheme === 'devrom') {
if(preg_match('#^([A-Za-z0-9-_]+)$#', $parsedUrl->getPath(), $matches)) {
$resp->uri = $parsedUrl = new Uri('https://i.edgii.net/' . $matches[1]);
if(preg_match('#^([A-Za-z0-9-_]+)$#', $parsedUrl['path'], $matches)) {
$resp->uri = $parsedUrl = uih_parse_url('https://i.edgii.net/' . $matches[1]);
$continueRaw = true;
uih_eeprom_lookup($resp, $matches[1], 'edgii');
}
@ -219,7 +303,7 @@ if($reqPath === '/metadata') {
CURLOPT_TIMEOUT => 5,
CURLOPT_USERAGENT => 'Uiharu/' . UIH_VERSION,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . TWITTER_API_TOKEN,
'Authorization: Bearer ' . Config::get('Twitter', 'apiToken'),
'Accept: application/json',
],
]);
@ -253,7 +337,7 @@ if($reqPath === '/metadata') {
CURLOPT_TIMEOUT => 5,
CURLOPT_USERAGENT => 'Uiharu/' . UIH_VERSION,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . TWITTER_API_TOKEN,
'Authorization: Bearer ' . Config::get('Twitter', 'apiToken'),
'Accept: application/json',
],
]);
@ -275,7 +359,7 @@ if($reqPath === '/metadata') {
$youtubeVideoId = substr($urlPath, 1);
case 'youtube.com': case 'www.youtube.com':
case 'youtube-nocookie.com': case 'www.youtube-nocookie.com':
parse_str($parsedUrl->getQuery(), $queryString);
parse_str($parsedUrl['query'], $queryString);
if(!isset($youtubeVideoId) && $urlPath === '/watch')
$youtubeVideoId = $queryString['v'] ?? null;
@ -292,7 +376,7 @@ if($reqPath === '/metadata') {
if(isset($queryString['index']))
$resp->youtube_playlist_index = $queryString['index'];
$curl = curl_init("https://www.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&id={$resp->youtube_video_id}&key=" . GOOGLE_API_KEY);
$curl = curl_init("https://www.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&id={$resp->youtube_video_id}&key=" . Config::get('Google', 'apiKey'));
curl_setopt_array($curl, [
CURLOPT_AUTOREFERER => false,
CURLOPT_CERTINFO => false,
@ -326,8 +410,8 @@ if($reqPath === '/metadata') {
$resp->error = 'metadata:scheme';
}
if((empty($resp->type) || isset($continueRaw)) && in_array($parsedUrl->getScheme(), ['http', 'https'])) {
$curl = curl_init((string)$parsedUrl);
if((empty($resp->type) || isset($continueRaw)) && in_array($parsedUrl['scheme'], ['http', 'https'])) {
$curl = curl_init(uih_build_url($parsedUrl));
curl_setopt_array($curl, [
CURLOPT_AUTOREFERER => true,
CURLOPT_CERTINFO => false,
@ -377,15 +461,15 @@ if($reqPath === '/metadata') {
}
try {
$contentType = new MediaType($headers['content-type'] ?? '');
$contentType = MediaType::parse($headers['content-type'] ?? '');
} catch(InvalidArgumentException $ex) {
$contentType = new MediaType('application/octet-stream');
$contentType = MediaType::parse('application/octet-stream');
}
$resp->content_type = $contentType;
$resp->content_type = uih_media_type_json($contentType);
$isHTML = $contentType->match('text/html');
$isXHTML = $contentType->match('application/xhtml+xml');
$isHTML = $contentType->equals('text/html');
$isXHTML = $contentType->equals('application/xhtml+xml');
if($isHTML || $isXHTML) {
curl_setopt_array($curl, [
@ -469,14 +553,14 @@ if($reqPath === '/metadata') {
}
}
} else {
$resp->is_image = $isImage = $contentType->match('image/*');
$resp->is_audio = $isAudio = $contentType->match('audio/*');
$resp->is_video = $isVideo = $contentType->match('video/*');
$resp->is_image = $isImage = $contentType->matchCategory('image');
$resp->is_audio = $isAudio = $contentType->matchCategory('audio');
$resp->is_video = $isVideo = $contentType->matchCategory('video');
if($isImage || $isAudio || $isVideo) {
curl_close($curl);
$resp->media = new stdClass;
$ffmpeg = json_decode(shell_exec(sprintf('ffprobe -show_streams -show_format -print_format json -v quiet -i %s', escapeshellarg((string)$parsedUrl))));
$ffmpeg = json_decode(shell_exec(sprintf('ffprobe -show_streams -show_format -print_format json -v quiet -i %s', escapeshellarg(uih_build_url($parsedUrl)))));
if(!empty($ffmpeg)) {
if(!empty($ffmpeg->format)) {
@ -562,13 +646,13 @@ if($reqPath === '/metadata') {
}
}
Stopwatch::stop();
$resp->took = Stopwatch::elapsed();
$sw->stop();
$resp->took = $sw->getElapsedTime() / 1000;
$respJson = json_encode($resp);
DB::prepare('REPLACE INTO `uih_metadata_cache` (`metadata_url`, `metadata_resp`) VALUES (UNHEX(:hash), :resp)')
->bind('hash', $urlHash)
->bind('resp', $respJson)
->execute();
$replaceCache = $db->prepare('REPLACE INTO `uih_metadata_cache` (`metadata_url`, `metadata_resp`) VALUES (UNHEX(?), ?)');
$replaceCache->addParameter(1, $urlHash);
$replaceCache->addParameter(2, $respJson);
$replaceCache->execute();
}
} finally {
if(!empty($semaphore))