ctx = $ctx; $this->db = $ctx->getDatabase(); } public function match(string $url): string { return !str_starts_with($url, '/v'); } public function register(HttpFx $router): void { $router->get('/metadata', [$this, 'handleGET']); $router->post('/metadata', [$this, 'handlePOST']); } public function handleGET($response, $request) { if($request->getMethod() === 'HEAD') { $response->setTypeJson(); return; } return $this->handler( $response, $request, (string)$request->getParam('url') ); } public function handlePOST($response, $request) { if(!$request->isStreamContent()) return 400; return $this->handler( $response, $request, $request->getContent()->getStream()->read(1000) ); } private function handler($response, $request, string $targetUrl) { $sw = Stopwatch::startNew(); $resp = new stdClass; $response->setTypeJson(); if(empty($targetUrl)) { $response->setStatusCode(400); return $resp; } try { $parsedUrl = Url::parse($targetUrl); } catch(InvalidArgumentException $ex) { $response->setStatusCode(400); $resp->error = 'metadata:uri'; return $resp; } // if no scheme is specified, try https if(!$parsedUrl->hasScheme()) $parsedUrl->setScheme('https'); $resp->uri = $parsedUrl->toV1(); $urlHash = $parsedUrl->calculateHash(false); $enableCache = !UIH_DEBUG || $request->hasParam('_cache'); $includeRawResult = UIH_DEBUG || $request->hasParam('include_raw'); if($enableCache) { $cacheFetch = $this->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)) { $lookup = $this->ctx->matchLookup($parsedUrl); if($lookup !== null) try { $result = $lookup->lookup($parsedUrl); $resp->uri = $result->getUrl()->toV1(); $resp->type = $result->getObjectType(); if($result->hasMediaType()) $resp->content_type = MediaTypeExts::toV1($result->getMediaType()); if($result->hasColour()) $resp->color = Colour::toHexString($result->getColour()); if($result->hasTitle()) $resp->title = $result->getTitle(); if($result->hasSiteName()) $resp->site_name = $result->getSiteName(); if($result->hasDescription()) $resp->description = $result->getDescription(); if($result->hasPreviewImage()) $resp->image = $result->getPreviewImage(); if($result instanceof TwitterLookupResult) { if($result instanceof TwitterLookupTweetResult) $resp->tweet_id = $result->getTwitterTweetId(); if($result instanceof TwitterLookupUserResult) $resp->twitter_user_name = $result->getTwitterUserName(); if(UIH_DEBUG) $resp->dbg_twitter_info = $result->getTwitterResult(); } if($result instanceof YouTubeLookupResult) { $resp->youtube_video_id = $result->getYouTubeVideoId(); if($result->hasYouTubeVideoStartTime()) $resp->youtube_start_time = $result->getYouTubeVideoStartTime(); if($result->hasYouTubePlayListId()) $resp->youtube_playlist = $result->getYouTubePlayListId(); if($result->hasYouTubePlayListIndex()) $resp->youtube_playlist_index = $result->getYouTubePlayListIndex(); if(UIH_DEBUG) { $resp->dbg_youtube_info = $result->getYouTubeVideoInfo(); $resp->dbg_youtube_query = $result->getYouTubeUrlQuery(); } } if($result instanceof IHasMediaInfo) { if($result->isMedia()) { $resp->is_image = $result->isImage(); $resp->is_audio = $result->isAudio(); $resp->is_video = $result->isVideo(); if($result->hasDimensions()) { $resp->width = $result->getWidth(); $resp->height = $result->getHeight(); } $resp->media = new stdClass; $resp->media->confidence = $result->getConfidence(); if($result->hasAspectRatio()) $resp->media->aspect_ratio = $result->getAspectRatio(); if($result->hasDuration()) $resp->media->duration = $result->getDuration(); if($result->hasSize()) $resp->media->size = $result->getSize(); if($result->hasBitRate()) $resp->media->bitrate = $result->getBitRate(); if($result->hasAudioTags()) { $audioTags = $result->getAudioTags(); $resp->media->tags = new stdClass; if($audioTags->hasTitle()) $resp->media->tags->title = $audioTags->getTitle(); if($audioTags->hasArtist()) $resp->media->tags->artist = $audioTags->getArtist(); if($audioTags->hasAlbum()) $resp->media->tags->album = $audioTags->getAlbum(); if($audioTags->hasDate()) $resp->media->tags->date = $audioTags->getDate(); if($audioTags->hasComment()) $resp->media->tags->comment = $audioTags->getComment(); if($audioTags->hasGenre()) $resp->media->tags->genre = $audioTags->getGenre(); } } if($result instanceof EEPROMLookupResult) { $resp->eeprom_file_id = $result->getEEPROMId(); $resp->eeprom_file_info = $result->getEEPROMInfo(); } if(UIH_DEBUG && $result->hasMediaInfo()) $resp->dbg_media_info = $result->getMediaInfo(); } } catch(Exception $ex) { $resp->error = 'metadata:lookup'; if(UIH_DEBUG) { $resp->dbg_msg = $ex->getMessage(); $resp->dbg_ex = (string)$ex; } $response->setStatusCode(500); return $resp; } $sw->stop(); $resp->took = $sw->getElapsedTime() / 1000; $respJson = json_encode($resp); $replaceCache = $this->db->prepare('REPLACE INTO `uih_metadata_cache` (`metadata_url`, `metadata_resp`) VALUES (UNHEX(?), ?)'); $replaceCache->addParameter(1, $urlHash); $replaceCache->addParameter(2, $respJson); $replaceCache->execute(); } if(!empty($respJson)) $response->setContent($respJson); else return $resp; } }