不要な成否判定の削除

ref: c0b76e5
This commit is contained in:
eai04191 2019-09-10 07:03:46 +09:00
parent fb6c1a0574
commit 033784bfc8
17 changed files with 257 additions and 335 deletions

View File

@ -25,19 +25,15 @@ class CienResolver extends MetadataResolver
public function resolve(string $url): Metadata public function resolve(string $url): Metadata
{ {
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody());
// 画像URLから有効期限の起点を拾う // 画像URLから有効期限の起点を拾う
parse_str(parse_url($metadata->image, PHP_URL_QUERY), $params); parse_str(parse_url($metadata->image, PHP_URL_QUERY), $params);
if (empty($params['px-time'])) { if (empty($params['px-time'])) {
throw new \RuntimeException('Parameter "px-time" not found. Image=' . $metadata->image . ' Source=' . $url); throw new \RuntimeException('Parameter "px-time" not found. Image=' . $metadata->image . ' Source=' . $url);
}
$metadata->expires_at = Carbon::createFromTimestamp($params['px-time'])->addHour(1);
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
$metadata->expires_at = Carbon::createFromTimestamp($params['px-time'])->addHour(1);
return $metadata;
} }
} }

View File

@ -73,59 +73,55 @@ class DLsiteResolver implements Resolver
} }
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody());
$dom = new \DOMDocument(); $dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8'));
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
// OGPタイトルから[]に囲まれているmakerを取得する // OGPタイトルから[]に囲まれているmakerを取得する
// 複数の作者がいる場合スペース区切りになるためexplodeしている // 複数の作者がいる場合スペース区切りになるためexplodeしている
// スペースを含むmakerの場合名前の一部しか取れないが動作には問題ない // スペースを含むmakerの場合名前の一部しか取れないが動作には問題ない
preg_match('~ \[([^\[\]]*)\] (予告作品 )?\| DLsite(がるまに)?$~', $metadata->title, $match); preg_match('~ \[([^\[\]]*)\] (予告作品 )?\| DLsite(がるまに)?$~', $metadata->title, $match);
$makers = explode(' ', $match[1]); $makers = explode(' ', $match[1]);
//フォローボタン(.btn_follow)はテキストを含んでしまうことがあるので要素を削除しておく //フォローボタン(.btn_follow)はテキストを含んでしまうことがあるので要素を削除しておく
$followButtonNode = $xpath->query('//*[@class="btn_follow"]')->item(0); $followButtonNode = $xpath->query('//*[@class="btn_follow"]')->item(0);
$followButtonNode->parentNode->removeChild($followButtonNode); $followButtonNode->parentNode->removeChild($followButtonNode);
// maker, makerHeadを探す // maker, makerHeadを探す
// makers // makers
// #work_makerから「makerを含むテキスト」を持つ要素を持つtdを探す // #work_makerから「makerを含むテキスト」を持つ要素を持つtdを探す
// 作者名単体の場合もあるし、"作者A / 作者B"のようになることもある // 作者名単体の場合もあるし、"作者A / 作者B"のようになることもある
$makersNode = $xpath->query('//*[@id="work_maker"]//*[contains(text(), "' . $makers[0] . '")]/ancestor::td')->item(0); $makersNode = $xpath->query('//*[@id="work_maker"]//*[contains(text(), "' . $makers[0] . '")]/ancestor::td')->item(0);
// nbspをspaceに置換 // nbspをspaceに置換
$makers = trim(str_replace("\xc2\xa0", ' ', $makersNode->textContent)); $makers = trim(str_replace("\xc2\xa0", ' ', $makersNode->textContent));
// makersHaed // makersHaed
// $makerNode(td)に対するthを探す // $makerNode(td)に対するthを探す
// "著者", "サークル名", "ブランド名"など // "著者", "サークル名", "ブランド名"など
$makersHeadNode = $xpath->query('preceding-sibling::th', $makersNode)->item(0); $makersHeadNode = $xpath->query('preceding-sibling::th', $makersNode)->item(0);
$makersHead = trim($makersHeadNode->textContent); $makersHead = trim($makersHeadNode->textContent);
// 余分な文を消す // 余分な文を消す
// OGPタイトルから作者名とサイト名を消す // OGPタイトルから作者名とサイト名を消す
$metadata->title = trim(preg_replace('~ \[[^\[\]]*\] (予告作品 )?\| DLsite(がるまに)?$~', '', $metadata->title)); $metadata->title = trim(preg_replace('~ \[[^\[\]]*\] (予告作品 )?\| DLsite(がるまに)?$~', '', $metadata->title));
// OGP説明文から定型文を消す // OGP説明文から定型文を消す
if (strpos($url, 'dlsite.com/eng/') || strpos($url, 'dlsite.com/ecchi-eng/')) { if (strpos($url, 'dlsite.com/eng/') || strpos($url, 'dlsite.com/ecchi-eng/')) {
$metadata->description = preg_replace('~DLsite.+ is a download shop for .+With a huge selection of products, we\'re sure you\'ll find whatever tickles your fancy\. DLsite is one of the greatest indie contents download shops in Japan\.$~', '', $metadata->description); $metadata->description = preg_replace('~DLsite.+ is a download shop for .+With a huge selection of products, we\'re sure you\'ll find whatever tickles your fancy\. DLsite is one of the greatest indie contents download shops in Japan\.$~', '', $metadata->description);
} else {
$metadata->description = preg_replace('~「DLsite.+」は.+のダウンロードショップ。お気に入りの作品をすぐダウンロードできてすぐ楽しめる毎日更新しているのであなたが探している作品にきっと出会えます。国内最大級の二次元総合ダウンロードショップ「DLsite」$~', '', $metadata->description);
}
$metadata->description = trim(strip_tags($metadata->description));
// 整形
$metadata->description = $makersHead . ': ' . $makers . PHP_EOL . $metadata->description;
$metadata->image = str_replace('img_sam.jpg', 'img_main.jpg', $metadata->image);
$metadata->tags = $this->extractTags($res->getBody());
return $metadata;
} else { } else {
throw new \RuntimeException("{$res->getStatusCode()}: $url"); $metadata->description = preg_replace('~「DLsite.+」は.+のダウンロードショップ。お気に入りの作品をすぐダウンロードできてすぐ楽しめる毎日更新しているのであなたが探している作品にきっと出会えます。国内最大級の二次元総合ダウンロードショップ「DLsite」$~', '', $metadata->description);
} }
$metadata->description = trim(strip_tags($metadata->description));
// 整形
$metadata->description = $makersHead . ': ' . $makers . PHP_EOL . $metadata->description;
$metadata->image = str_replace('img_sam.jpg', 'img_main.jpg', $metadata->image);
$metadata->tags = $this->extractTags($res->getBody());
return $metadata;
} }
} }

View File

@ -24,21 +24,17 @@ class FC2ContentsResolver implements Resolver
public function resolve(string $url): Metadata public function resolve(string $url): Metadata
{ {
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody());
$dom = new \DOMDocument(); $dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8'));
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
$thumbnailNode = $xpath->query('//*[@class="main_thum_img"]/a')->item(0); $thumbnailNode = $xpath->query('//*[@class="main_thum_img"]/a')->item(0);
if ($thumbnailNode) { if ($thumbnailNode) {
$metadata->image = preg_replace('~^http:~', 'https:', $thumbnailNode->getAttribute('href')); $metadata->image = preg_replace('~^http:~', 'https:', $thumbnailNode->getAttribute('href'));
}
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
return $metadata;
} }
} }

View File

@ -28,29 +28,25 @@ class FantiaResolver implements Resolver
$postId = $match[0]; $postId = $match[0];
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody());
$dom = new \DOMDocument(); $dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8'));
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
$node = $xpath->query("//meta[@property='twitter:image']")->item(0); $node = $xpath->query("//meta[@property='twitter:image']")->item(0);
$ogpUrl = $node->getAttribute('content'); $ogpUrl = $node->getAttribute('content');
// 投稿に画像がない場合ogp.jpgでない場合のみ大きい画像に変換する // 投稿に画像がない場合ogp.jpgでない場合のみ大きい画像に変換する
if ($ogpUrl != 'http://fantia.jp/images/ogp.jpg') { if ($ogpUrl != 'http://fantia.jp/images/ogp.jpg') {
preg_match("~https://fantia\.s3\.amazonaws\.com/uploads/post/file/{$postId}/ogp_(.*?)\.(jpg|png)~", $ogpUrl, $match); preg_match("~https://fantia\.s3\.amazonaws\.com/uploads/post/file/{$postId}/ogp_(.*?)\.(jpg|png)~", $ogpUrl, $match);
$uuid = $match[1]; $uuid = $match[1];
$extension = $match[2]; $extension = $match[2];
// 大きい画像に変換 // 大きい画像に変換
$metadata->image = "https://c.fantia.jp/uploads/post/file/{$postId}/main_{$uuid}.{$extension}"; $metadata->image = "https://c.fantia.jp/uploads/post/file/{$postId}/main_{$uuid}.{$extension}";
}
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
return $metadata;
} }
} }

View File

@ -24,14 +24,10 @@ class FanzaResolver implements Resolver
public function resolve(string $url): Metadata public function resolve(string $url): Metadata
{ {
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody()); $metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $metadata->image);
$metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $metadata->image); $metadata->description = str_replace('<>', '', $metadata->description);
$metadata->description = str_replace('<>', '', $metadata->description);
return $metadata; return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
}
} }
} }

View File

@ -20,45 +20,41 @@ class IwaraResolver implements Resolver
public function resolve(string $url): Metadata public function resolve(string $url): Metadata
{ {
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $metadata = new Metadata();
$metadata = new Metadata(); $html = (string) $res->getBody();
$html = (string) $res->getBody(); $crawler = new Crawler($html);
$crawler = new Crawler($html);
$infoElements = $crawler->filter('#video-player + div, .field-name-field-video-url + div, .field-name-field-images + div'); $infoElements = $crawler->filter('#video-player + div, .field-name-field-video-url + div, .field-name-field-images + div');
$title = $infoElements->filter('h1.title')->text(); $title = $infoElements->filter('h1.title')->text();
$author = $infoElements->filter('.username')->text(); $author = $infoElements->filter('.username')->text();
$description = $infoElements->filter('.field-type-text-with-summary')->text(''); $description = $infoElements->filter('.field-type-text-with-summary')->text('');
$tags = $infoElements->filter('a[href^="/videos"], a[href^="/images"]')->extract('_text'); $tags = $infoElements->filter('a[href^="/videos"], a[href^="/images"]')->extract('_text');
// 役に立たないタグを削除する // 役に立たないタグを削除する
$tags = array_values(array_diff($tags, ['Uncategorized', 'Other'])); $tags = array_values(array_diff($tags, ['Uncategorized', 'Other']));
array_push($tags, $author); array_push($tags, $author);
$metadata->title = $title; $metadata->title = $title;
$metadata->description = '投稿者: ' . $author . PHP_EOL . $description; $metadata->description = '投稿者: ' . $author . PHP_EOL . $description;
$metadata->tags = $tags; $metadata->tags = $tags;
// iwara video // iwara video
if ($crawler->filter('#video-player')->count()) { if ($crawler->filter('#video-player')->count()) {
$metadata->image = 'https:' . $crawler->filter('#video-player')->attr('poster'); $metadata->image = 'https:' . $crawler->filter('#video-player')->attr('poster');
}
// youtube
if ($crawler->filter('iframe[src^="//www.youtube.com"]')->count()) {
if (preg_match('~youtube\.com/embed/(\S+)\?~', $crawler->filter('iframe[src^="//www.youtube.com"]')->attr('src'), $matches) === 1) {
$youtubeId = $matches[1];
$metadata->image = 'https://img.youtube.com/vi/' . $youtubeId . '/maxresdefault.jpg';
}
}
// images
if ($crawler->filter('.field-name-field-images')->count()) {
$metadata->image = 'https:' . $crawler->filter('.field-name-field-images a')->first()->attr('href');
}
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
// youtube
if ($crawler->filter('iframe[src^="//www.youtube.com"]')->count()) {
if (preg_match('~youtube\.com/embed/(\S+)\?~', $crawler->filter('iframe[src^="//www.youtube.com"]')->attr('src'), $matches) === 1) {
$youtubeId = $matches[1];
$metadata->image = 'https://img.youtube.com/vi/' . $youtubeId . '/maxresdefault.jpg';
}
}
// images
if ($crawler->filter('.field-name-field-images')->count()) {
$metadata->image = 'https:' . $crawler->filter('.field-name-field-images a')->first()->attr('href');
}
return $metadata;
} }
} }

View File

@ -24,33 +24,28 @@ class KomifloResolver implements Resolver
$id = $matches[1]; $id = $matches[1];
$res = $this->client->get('https://api.komiflo.com/content/id/' . $id); $res = $this->client->get('https://api.komiflo.com/content/id/' . $id);
if ($res->getStatusCode() === 200) { $json = json_decode($res->getBody()->getContents(), true);
$json = json_decode($res->getBody()->getContents(), true); $metadata = new Metadata();
$metadata = new Metadata();
$metadata->title = $json['content']['data']['title'] ?? ''; $metadata->title = $json['content']['data']['title'] ?? '';
$metadata->description = ($json['content']['attributes']['artists']['children'][0]['data']['name'] ?? '?') . $metadata->description = ($json['content']['attributes']['artists']['children'][0]['data']['name'] ?? '?') .
' - ' . ' - ' . ($json['content']['parents'][0]['data']['title'] ?? '?');
($json['content']['parents'][0]['data']['title'] ?? '?'); $metadata->image = 'https://t.komiflo.com/564_mobile_large_3x/' . $json['content']['named_imgs']['cover']['filename'];
$metadata->image = 'https://t.komiflo.com/564_mobile_large_3x/' . $json['content']['named_imgs']['cover']['filename'];
// 作者情報 // 作者情報
if (!empty($json['content']['attributes']['artists']['children'])) { if (!empty($json['content']['attributes']['artists']['children'])) {
foreach ($json['content']['attributes']['artists']['children'] as $artist) { foreach ($json['content']['attributes']['artists']['children'] as $artist) {
$metadata->tags[] = preg_replace('/\s/', '_', $artist['data']['name']); $metadata->tags[] = preg_replace('/\s/', '_', $artist['data']['name']);
}
} }
// タグ
if (!empty($json['content']['attributes']['tags']['children'])) {
foreach ($json['content']['attributes']['tags']['children'] as $tag) {
$metadata->tags[] = preg_replace('/\s/', '_', $tag['data']['name']);
}
}
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
// タグ
if (!empty($json['content']['attributes']['tags']['children'])) {
foreach ($json['content']['attributes']['tags']['children'] as $tag) {
$metadata->tags[] = preg_replace('/\s/', '_', $tag['data']['name']);
}
}
return $metadata;
} }
} }

View File

@ -27,46 +27,42 @@ class MelonbooksResolver implements Resolver
$cookieJar = CookieJar::fromArray(['AUTH_ADULT' => '1'], 'www.melonbooks.co.jp'); $cookieJar = CookieJar::fromArray(['AUTH_ADULT' => '1'], 'www.melonbooks.co.jp');
$res = $this->client->get($url, ['cookies' => $cookieJar]); $res = $this->client->get($url, ['cookies' => $cookieJar]);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody());
$dom = new \DOMDocument(); $dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8'));
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
$descriptionNodelist = $xpath->query('//div[@id="description"]//p'); $descriptionNodelist = $xpath->query('//div[@id="description"]//p');
$specialDescriptionNodelist = $xpath->query('//div[@id="special_description"]//p'); $specialDescriptionNodelist = $xpath->query('//div[@id="special_description"]//p');
// censoredフラグの除去 // censoredフラグの除去
if (mb_strpos($metadata->image, '&c=1') !== false) { if (mb_strpos($metadata->image, '&c=1') !== false) {
$metadata->image = preg_replace('/&c=1/u', '', $metadata->image); $metadata->image = preg_replace('/&c=1/u', '', $metadata->image);
}
// 抽出
preg_match('~^(.+)(.+))の通販・購入はメロンブックス$~', $metadata->title, $match);
$title = $match[1];
$maker = $match[2];
// 整形
$description = 'サークル: ' . $maker . "\n";
if ($specialDescriptionNodelist->length !== 0) {
$description .= trim(str_replace('<br>', "\n", $specialDescriptionNodelist->item(0)->nodeValue)) . "\n";
if ($specialDescriptionNodelist->length === 2) {
$description .= "\n";
$description .= trim(str_replace('<br>', "\n", $specialDescriptionNodelist->item(1)->nodeValue)) . "\n";
}
}
if ($descriptionNodelist->length !== 0) {
$description .= trim(str_replace('<br>', "\n", $descriptionNodelist->item(0)->nodeValue));
}
$metadata->title = $title;
$metadata->description = trim($description);
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
// 抽出
preg_match('~^(.+)(.+))の通販・購入はメロンブックス$~', $metadata->title, $match);
$title = $match[1];
$maker = $match[2];
// 整形
$description = 'サークル: ' . $maker . "\n";
if ($specialDescriptionNodelist->length !== 0) {
$description .= trim(str_replace('<br>', "\n", $specialDescriptionNodelist->item(0)->nodeValue)) . "\n";
if ($specialDescriptionNodelist->length === 2) {
$description .= "\n";
$description .= trim(str_replace('<br>', "\n", $specialDescriptionNodelist->item(1)->nodeValue)) . "\n";
}
}
if ($descriptionNodelist->length !== 0) {
$description .= trim(str_replace('<br>', "\n", $descriptionNodelist->item(0)->nodeValue));
}
$metadata->title = $title;
$metadata->description = trim($description);
return $metadata;
} }
} }

View File

@ -27,34 +27,30 @@ class NarouResolver implements Resolver
$cookieJar = CookieJar::fromArray(['over18' => 'yes'], '.syosetu.com'); $cookieJar = CookieJar::fromArray(['over18' => 'yes'], '.syosetu.com');
$res = $this->client->get($url, ['cookies' => $cookieJar]); $res = $this->client->get($url, ['cookies' => $cookieJar]);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody()); $metadata->description = '';
$metadata->description = '';
$dom = new \DOMDocument(); $dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win')); @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win'));
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
$description = []; $description = [];
// 作者名 // 作者名
$writerNodes = $xpath->query('//*[contains(@class, "novel_writername")]'); $writerNodes = $xpath->query('//*[contains(@class, "novel_writername")]');
if ($writerNodes->length !== 0 && !empty($writerNodes->item(0)->textContent)) { if ($writerNodes->length !== 0 && !empty($writerNodes->item(0)->textContent)) {
$description[] = trim($writerNodes->item(0)->textContent); $description[] = trim($writerNodes->item(0)->textContent);
}
// あらすじ
$exNodes = $xpath->query('//*[@id="novel_ex"]');
if ($exNodes->length !== 0 && !empty($exNodes->item(0)->textContent)) {
$summary = trim($exNodes->item(0)->textContent);
$description[] = mb_strimwidth($summary, 0, 101, '…'); // 100 + '…'(1)
}
$metadata->description = implode(' / ', $description);
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
// あらすじ
$exNodes = $xpath->query('//*[@id="novel_ex"]');
if ($exNodes->length !== 0 && !empty($exNodes->item(0)->textContent)) {
$summary = trim($exNodes->item(0)->textContent);
$description[] = mb_strimwidth($summary, 0, 101, '…'); // 100 + '…'(1)
}
$metadata->description = implode(' / ', $description);
return $metadata;
} }
} }

View File

@ -32,32 +32,28 @@ class NijieResolver implements Resolver
} }
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $html = (string) $res->getBody();
$html = (string) $res->getBody(); $metadata = $this->ogpResolver->parse($html);
$metadata = $this->ogpResolver->parse($html); $crawler = new Crawler($html);
$crawler = new Crawler($html);
// DomCrawler内でjson内の日本語がHTMLエンティティに変換されるのでhtml_entity_decode // DomCrawler内でjson内の日本語がHTMLエンティティに変換されるのでhtml_entity_decode
$json = html_entity_decode($crawler->filter('script[type="application/ld+json"]')->first()->text()); $json = html_entity_decode($crawler->filter('script[type="application/ld+json"]')->first()->text());
// 改行がそのまま入っていることがあるのでデコード前にエスケープが必要 // 改行がそのまま入っていることがあるのでデコード前にエスケープが必要
$data = json_decode(preg_replace('/\r?\n/', '\n', $json), true); $data = json_decode(preg_replace('/\r?\n/', '\n', $json), true);
$metadata->title = $data['name']; $metadata->title = $data['name'];
$metadata->description = '投稿者: ' . $data['author']['name'] . PHP_EOL . $data['description']; $metadata->description = '投稿者: ' . $data['author']['name'] . PHP_EOL . $data['description'];
if ( if (
isset($data['thumbnailUrl']) && isset($data['thumbnailUrl']) &&
!ends_with($data['thumbnailUrl'], '.gif') && !ends_with($data['thumbnailUrl'], '.gif') &&
!ends_with($data['thumbnailUrl'], '.mp4') !ends_with($data['thumbnailUrl'], '.mp4')
) { ) {
// サムネイルからメイン画像に // サムネイルからメイン画像に
$metadata->image = str_replace('__rs_l160x160/', '', $data['thumbnailUrl']); $metadata->image = str_replace('__rs_l160x160/', '', $data['thumbnailUrl']);
}
$metadata->tags = $crawler->filter('#view-tag span.tag_name')->extract('_text');
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
$metadata->tags = $crawler->filter('#view-tag span.tag_name')->extract('_text');
return $metadata;
} }
} }

View File

@ -19,11 +19,7 @@ class OGPResolver implements Resolver, Parser
public function resolve(string $url): Metadata public function resolve(string $url): Metadata
{ {
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { return $this->parse($res->getBody());
return $this->parse($res->getBody());
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
}
} }
public function parse(string $html): Metadata public function parse(string $html): Metadata

View File

@ -25,18 +25,14 @@ class PatreonResolver implements Resolver
public function resolve(string $url): Metadata public function resolve(string $url): Metadata
{ {
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody());
parse_str(parse_url($metadata->image, PHP_URL_QUERY), $query); parse_str(parse_url($metadata->image, PHP_URL_QUERY), $query);
if (isset($query['token-time'])) { if (isset($query['token-time'])) {
$expires_at_unixtime = $query['token-time']; $expires_at_unixtime = $query['token-time'];
$metadata->expires_at = Carbon::createFromTimestamp($expires_at_unixtime); $metadata->expires_at = Carbon::createFromTimestamp($expires_at_unixtime);
}
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
return $metadata;
} }
} }

View File

@ -38,14 +38,10 @@ class PixivResolver implements Resolver
{ {
if (preg_match('~www\.pixiv\.net/user/\d+/series/\d+~', $url, $matches)) { if (preg_match('~www\.pixiv\.net/user/\d+/series/\d+~', $url, $matches)) {
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody()); $metadata->image = $this->proxize($metadata->image);
$metadata->image = $this->proxize($metadata->image);
return $metadata; return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
}
} }
parse_str(parse_url($url, PHP_URL_QUERY), $params); parse_str(parse_url($url, PHP_URL_QUERY), $params);
@ -58,32 +54,28 @@ class PixivResolver implements Resolver
} }
$res = $this->client->get('https://www.pixiv.net/ajax/illust/' . $illustId); $res = $this->client->get('https://www.pixiv.net/ajax/illust/' . $illustId);
if ($res->getStatusCode() === 200) { $json = json_decode($res->getBody()->getContents(), true);
$json = json_decode($res->getBody()->getContents(), true); $metadata = new Metadata();
$metadata = new Metadata();
$metadata->title = $json['body']['illustTitle'] ?? ''; $metadata->title = $json['body']['illustTitle'] ?? '';
$metadata->description = '投稿者: ' . $json['body']['userName'] . PHP_EOL . strip_tags(str_replace('<br />', PHP_EOL, $json['body']['illustComment'] ?? '')); $metadata->description = '投稿者: ' . $json['body']['userName'] . PHP_EOL . strip_tags(str_replace('<br />', PHP_EOL, $json['body']['illustComment'] ?? ''));
$metadata->image = $this->proxize($json['body']['urls']['regular'] ?? ''); $metadata->image = $this->proxize($json['body']['urls']['regular'] ?? '');
// ページ数の指定がある場合は画像URLをそのページにする // ページ数の指定がある場合は画像URLをそのページにする
if ($page != 0) { if ($page != 0) {
$metadata->image = str_replace('_p0', '_p'.$page, $metadata->image); $metadata->image = str_replace('_p0', '_p' . $page, $metadata->image);
} }
// タグ // タグ
if (!empty($json['body']['tags']['tags'])) { if (!empty($json['body']['tags']['tags'])) {
foreach ($json['body']['tags']['tags'] as $tag) { foreach ($json['body']['tags']['tags'] as $tag) {
// 一部の固定キーワードは無視 // 一部の固定キーワードは無視
if (array_search($tag['tag'], ['R-18', 'イラスト', 'pixiv', 'ピクシブ'], true) === false) { if (array_search($tag['tag'], ['R-18', 'イラスト', 'pixiv', 'ピクシブ'], true) === false) {
$metadata->tags[] = preg_replace('/\s/', '_', $tag['tag']); $metadata->tags[] = preg_replace('/\s/', '_', $tag['tag']);
}
} }
} }
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
return $metadata;
} }
} }

View File

@ -24,21 +24,17 @@ class PlurkResolver implements Resolver
public function resolve(string $url): Metadata public function resolve(string $url): Metadata
{ {
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody());
$dom = new \DOMDocument(); $dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8'));
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
$imageNode = $xpath->query('//div[@class="text_holder"]/a[1]')->item(0); $imageNode = $xpath->query('//div[@class="text_holder"]/a[1]')->item(0);
if ($imageNode) { if ($imageNode) {
$metadata->image = $imageNode->getAttribute('href'); $metadata->image = $imageNode->getAttribute('href');
}
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
return $metadata;
} }
} }

View File

@ -24,21 +24,17 @@ class SteamResolver implements Resolver
$appid = $matches[1]; $appid = $matches[1];
$res = $this->client->get('https://store.steampowered.com/api/appdetails/?l=japanese&appids=' . $appid); $res = $this->client->get('https://store.steampowered.com/api/appdetails/?l=japanese&appids=' . $appid);
if ($res->getStatusCode() === 200) { $json = json_decode($res->getBody()->getContents(), true);
$json = json_decode($res->getBody()->getContents(), true); if ($json[$appid]['success'] === false) {
if ($json[$appid]['success'] === false) { throw new \RuntimeException("API response [$appid][success] is false: $url");
throw new \RuntimeException("API response [$appid][success] is false: $url");
}
$data = $json[$appid]['data'];
$metadata = new Metadata();
$metadata->title = $data['name'] ?? '';
$metadata->description = strip_tags(str_replace('<br />', PHP_EOL, html_entity_decode($data['short_description'] ?? '')));
$metadata->image = $data['header_image'] ?? '';
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
$data = $json[$appid]['data'];
$metadata = new Metadata();
$metadata->title = $data['name'] ?? '';
$metadata->description = strip_tags(str_replace('<br />', PHP_EOL, html_entity_decode($data['short_description'] ?? '')));
$metadata->image = $data['header_image'] ?? '';
return $metadata;
} }
} }

View File

@ -25,20 +25,16 @@ class ToranoanaResolver implements Resolver
public function resolve(string $url): Metadata public function resolve(string $url): Metadata
{ {
$res = $this->client->get($url); $res = $this->client->get($url);
if ($res->getStatusCode() === 200) { $metadata = $this->ogpResolver->parse($res->getBody());
$metadata = $this->ogpResolver->parse($res->getBody());
$dom = new \DOMDocument(); $dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8'));
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
$imgNode = $xpath->query('//*[@id="preview"]//img')->item(0); $imgNode = $xpath->query('//*[@id="preview"]//img')->item(0);
if ($imgNode !== null) { if ($imgNode !== null) {
$metadata->image = $imgNode->getAttribute('src'); $metadata->image = $imgNode->getAttribute('src');
}
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
return $metadata;
} }
} }

View File

@ -3,21 +3,12 @@
namespace Tests\Unit\MetadataResolver; namespace Tests\Unit\MetadataResolver;
use App\MetadataResolver\OGPResolver; use App\MetadataResolver\OGPResolver;
use GuzzleHttp\Exception\ClientException;
use Tests\TestCase; use Tests\TestCase;
class OGPResolverTest extends TestCase class OGPResolverTest extends TestCase
{ {
use CreateMockedResolver; use CreateMockedResolver;
public function testMissingUrl()
{
$this->createResolver(OGPResolver::class, '', [], 404);
$this->expectException(\RuntimeException::class);
$this->resolver->resolve('http://example.com/404');
}
public function testResolve() public function testResolve()
{ {
$response = <<< 'HTML' $response = <<< 'HTML'