Added ニコニコ動画 metadata support.
This commit is contained in:
parent
5fe283a2a7
commit
19b46188ad
6 changed files with 154 additions and 2 deletions
|
@ -12,6 +12,7 @@ if(UIH_DEBUG)
|
||||||
|
|
||||||
$ctx->registerLookup(new \Uiharu\Lookup\TwitterLookup);
|
$ctx->registerLookup(new \Uiharu\Lookup\TwitterLookup);
|
||||||
$ctx->registerLookup(new \Uiharu\Lookup\YouTubeLookup);
|
$ctx->registerLookup(new \Uiharu\Lookup\YouTubeLookup);
|
||||||
|
$ctx->registerLookup(new \Uiharu\Lookup\NicoNicoLookup);
|
||||||
|
|
||||||
// this should always come AFTER other lookups involved http(s)
|
// this should always come AFTER other lookups involved http(s)
|
||||||
$ctx->registerLookup(new \Uiharu\Lookup\WebLookup);
|
$ctx->registerLookup(new \Uiharu\Lookup\WebLookup);
|
||||||
|
|
|
@ -17,6 +17,7 @@ use Uiharu\Lookup\TwitterLookupResult;
|
||||||
use Uiharu\Lookup\TwitterLookupTweetResult;
|
use Uiharu\Lookup\TwitterLookupTweetResult;
|
||||||
use Uiharu\Lookup\TwitterLookupUserResult;
|
use Uiharu\Lookup\TwitterLookupUserResult;
|
||||||
use Uiharu\Lookup\YouTubeLookupResult;
|
use Uiharu\Lookup\YouTubeLookupResult;
|
||||||
|
use Uiharu\Lookup\NicoNicoLookupResult;
|
||||||
use Index\MediaType;
|
use Index\MediaType;
|
||||||
use Index\Data\IDbConnection;
|
use Index\Data\IDbConnection;
|
||||||
use Index\Http\HttpFx;
|
use Index\Http\HttpFx;
|
||||||
|
@ -154,6 +155,14 @@ final class v1_0 implements \Uiharu\IApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($result instanceof NicoNicoLookupResult) {
|
||||||
|
$resp->nicovideo_video_id = $result->getNicoNicoVideoId();
|
||||||
|
|
||||||
|
if(UIH_DEBUG) {
|
||||||
|
$resp->dbg_nicovideo_thumb_info = $result->getNicoNicoThumbInfo()->ownerDocument->saveXML();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if($result instanceof IHasMediaInfo) {
|
if($result instanceof IHasMediaInfo) {
|
||||||
if($result->isMedia()) {
|
if($result->isMedia()) {
|
||||||
$resp->is_image = $result->isImage();
|
$resp->is_image = $result->isImage();
|
||||||
|
|
63
src/Lookup/NicoNicoLookup.php
Normal file
63
src/Lookup/NicoNicoLookup.php
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
namespace Uiharu\Lookup;
|
||||||
|
|
||||||
|
use DOMDocument;
|
||||||
|
use RuntimeException;
|
||||||
|
use Uiharu\Url;
|
||||||
|
|
||||||
|
final class NicoNicoLookup implements \Uiharu\ILookup {
|
||||||
|
public function match(Url $url): bool {
|
||||||
|
if(!$url->isWeb() || ($url->getHost() !== 'www.nicovideo.jp' && $url->getHost() !== 'nicovideo.jp'))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(str_starts_with($url->getPath(), '/watch/sm'))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lookup(Url $url): NicoNicoLookupResult {
|
||||||
|
$videoId = explode('/', trim($url->getPath(), '/'))[1] ?? '';
|
||||||
|
if(empty($videoId))
|
||||||
|
throw new RuntimeException('Nico Nico Douga video id missing.');
|
||||||
|
|
||||||
|
$thumbDoc = self::lookupThumbInfo($videoId);
|
||||||
|
|
||||||
|
$thumbResp = $thumbDoc->getElementsByTagName('nicovideo_thumb_response')[0] ?? null;
|
||||||
|
if(empty($thumbResp) || !$thumbResp->hasAttribute('status') || $thumbResp->getAttribute('status') !== 'ok')
|
||||||
|
throw new RuntimeException('Nico Nico Douga video with the given id could not be found.');
|
||||||
|
|
||||||
|
$thumbInfo = $thumbResp->getElementsByTagName('thumb')[0] ?? null;
|
||||||
|
if(empty($thumbInfo))
|
||||||
|
throw new RuntimeException('Nico Nico Douga thumb info missing from API result????');
|
||||||
|
|
||||||
|
return new NicoNicoLookupResult($url, $videoId, $thumbInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function lookupThumbInfo(string $videoId): DOMDocument {
|
||||||
|
$curl = curl_init("https://ext.nicovideo.jp/api/getthumbinfo/{$videoId}");
|
||||||
|
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,
|
||||||
|
]);
|
||||||
|
$resp = curl_exec($curl);
|
||||||
|
curl_close($curl);
|
||||||
|
|
||||||
|
if(empty($resp))
|
||||||
|
throw new RuntimeException('Nico Nico Douga API request failed.');
|
||||||
|
|
||||||
|
$doc = new DOMDocument;
|
||||||
|
if(!$doc->loadXML($resp))
|
||||||
|
throw new RuntimeException('Failed to parse Nico Nico Douga API response.');
|
||||||
|
|
||||||
|
return $doc;
|
||||||
|
}
|
||||||
|
}
|
80
src/Lookup/NicoNicoLookupResult.php
Normal file
80
src/Lookup/NicoNicoLookupResult.php
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
namespace Uiharu\Lookup;
|
||||||
|
|
||||||
|
use DOMElement;
|
||||||
|
use Index\MediaType;
|
||||||
|
use Uiharu\Url;
|
||||||
|
|
||||||
|
final class NicoNicoLookupResult implements \Uiharu\ILookupResult {
|
||||||
|
private DOMElement|false|null $title = null;
|
||||||
|
private DOMElement|false|null $description = null;
|
||||||
|
private DOMElement|false|null $previewImage = null;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private Url $url,
|
||||||
|
private string $videoId,
|
||||||
|
private DOMElement $thumbInfo,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function getUrl(): Url {
|
||||||
|
return $this->url;
|
||||||
|
}
|
||||||
|
public function getObjectType(): string {
|
||||||
|
return 'niconico:video';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNicoNicoVideoId(): string {
|
||||||
|
return $this->videoId;
|
||||||
|
}
|
||||||
|
public function getNicoNicoThumbInfo(): DOMElement {
|
||||||
|
return $this->thumbInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasMediaType(): bool {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public function getMediaType(): MediaType {
|
||||||
|
throw new RuntimeException('Unsupported');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasColour(): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public function getColour(): int {
|
||||||
|
return 0x252525;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasTitle(): bool {
|
||||||
|
if($this->title === null)
|
||||||
|
$this->title = $this->thumbInfo->getElementsByTagName('title')[0] ?? false;
|
||||||
|
return $this->title !== false;
|
||||||
|
}
|
||||||
|
public function getTitle(): string {
|
||||||
|
return $this->hasTitle() ? trim($this->title->textContent) : 'No Title';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasSiteName(): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public function getSiteName(): string {
|
||||||
|
return 'ニコニコ動画';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasDescription(): bool {
|
||||||
|
if($this->description === null)
|
||||||
|
$this->description = $this->thumbInfo->getElementsByTagName('description')[0] ?? false;
|
||||||
|
return $this->description !== false && !empty($this->description->textContent);
|
||||||
|
}
|
||||||
|
public function getDescription(): string {
|
||||||
|
return $this->hasDescription() ? trim($this->description->textContent) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasPreviewImage(): bool {
|
||||||
|
if($this->previewImage === null)
|
||||||
|
$this->previewImage = $this->thumbInfo->getElementsByTagName('thumbnail_url')[0] ?? false;
|
||||||
|
return $this->previewImage !== false && !empty($this->previewImage->textContent);
|
||||||
|
}
|
||||||
|
public function getPreviewImage(): string {
|
||||||
|
return $this->hasPreviewImage() ? (trim($this->previewImage->textContent) . '.L') : '';
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ namespace Uiharu\Lookup;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Uiharu\Config;
|
use Uiharu\Config;
|
||||||
use Uiharu\Url;
|
use Uiharu\Url;
|
||||||
use Index\MediaType;
|
|
||||||
|
|
||||||
final class YouTubeLookup implements \Uiharu\ILookup {
|
final class YouTubeLookup implements \Uiharu\ILookup {
|
||||||
private const SHORT_DOMAINS = [
|
private const SHORT_DOMAINS = [
|
||||||
|
|
|
@ -12,7 +12,7 @@ define('UIH_DEBUG', is_file(UIH_ROOT . '/.debug'));
|
||||||
define('UIH_PUBLIC', UIH_ROOT . '/public');
|
define('UIH_PUBLIC', UIH_ROOT . '/public');
|
||||||
define('UIH_SOURCE', UIH_ROOT . '/src');
|
define('UIH_SOURCE', UIH_ROOT . '/src');
|
||||||
define('UIH_LIBRARY', UIH_ROOT . '/lib');
|
define('UIH_LIBRARY', UIH_ROOT . '/lib');
|
||||||
define('UIH_VERSION', '20220716');
|
define('UIH_VERSION', '20230125');
|
||||||
|
|
||||||
require_once UIH_LIBRARY . '/index/index.php';
|
require_once UIH_LIBRARY . '/index/index.php';
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue