diff --git a/app/MetadataResolver/CienResolver.php b/app/MetadataResolver/CienResolver.php index 5af47c3..eb5c1b4 100644 --- a/app/MetadataResolver/CienResolver.php +++ b/app/MetadataResolver/CienResolver.php @@ -25,19 +25,15 @@ class CienResolver extends MetadataResolver public function resolve(string $url): Metadata { $res = $this->client->get($url); - if ($res->getStatusCode() === 200) { - $metadata = $this->ogpResolver->parse($res->getBody()); + $metadata = $this->ogpResolver->parse($res->getBody()); - // 画像URLから有効期限の起点を拾う - parse_str(parse_url($metadata->image, PHP_URL_QUERY), $params); - if (empty($params['px-time'])) { - 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"); + // 画像URLから有効期限の起点を拾う + parse_str(parse_url($metadata->image, PHP_URL_QUERY), $params); + if (empty($params['px-time'])) { + 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; } } diff --git a/app/MetadataResolver/DLsiteResolver.php b/app/MetadataResolver/DLsiteResolver.php index 94821a7..957a5d1 100644 --- a/app/MetadataResolver/DLsiteResolver.php +++ b/app/MetadataResolver/DLsiteResolver.php @@ -73,59 +73,55 @@ class DLsiteResolver implements Resolver } $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->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); - $xpath = new \DOMXPath($dom); + $dom = new \DOMDocument(); + @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); + $xpath = new \DOMXPath($dom); - // OGPタイトルから[]に囲まれているmakerを取得する - // 複数の作者がいる場合スペース区切りになるためexplodeしている - // スペースを含むmakerの場合名前の一部しか取れないが動作には問題ない - preg_match('~ \[([^\[\]]*)\] (予告作品 )?\| DLsite(がるまに)?$~', $metadata->title, $match); - $makers = explode(' ', $match[1]); + // OGPタイトルから[]に囲まれているmakerを取得する + // 複数の作者がいる場合スペース区切りになるためexplodeしている + // スペースを含むmakerの場合名前の一部しか取れないが動作には問題ない + preg_match('~ \[([^\[\]]*)\] (予告作品 )?\| DLsite(がるまに)?$~', $metadata->title, $match); + $makers = explode(' ', $match[1]); - //フォローボタン(.btn_follow)はテキストを含んでしまうことがあるので要素を削除しておく - $followButtonNode = $xpath->query('//*[@class="btn_follow"]')->item(0); - $followButtonNode->parentNode->removeChild($followButtonNode); + //フォローボタン(.btn_follow)はテキストを含んでしまうことがあるので要素を削除しておく + $followButtonNode = $xpath->query('//*[@class="btn_follow"]')->item(0); + $followButtonNode->parentNode->removeChild($followButtonNode); - // maker, makerHeadを探す + // maker, makerHeadを探す - // makers - // #work_makerから「makerを含むテキスト」を持つ要素を持つtdを探す - // 作者名単体の場合もあるし、"作者A / 作者B"のようになることもある - $makersNode = $xpath->query('//*[@id="work_maker"]//*[contains(text(), "' . $makers[0] . '")]/ancestor::td')->item(0); - // nbspをspaceに置換 - $makers = trim(str_replace("\xc2\xa0", ' ', $makersNode->textContent)); + // makers + // #work_makerから「makerを含むテキスト」を持つ要素を持つtdを探す + // 作者名単体の場合もあるし、"作者A / 作者B"のようになることもある + $makersNode = $xpath->query('//*[@id="work_maker"]//*[contains(text(), "' . $makers[0] . '")]/ancestor::td')->item(0); + // nbspをspaceに置換 + $makers = trim(str_replace("\xc2\xa0", ' ', $makersNode->textContent)); - // makersHaed - // $makerNode(td)に対するthを探す - // "著者", "サークル名", "ブランド名"など - $makersHeadNode = $xpath->query('preceding-sibling::th', $makersNode)->item(0); - $makersHead = trim($makersHeadNode->textContent); + // makersHaed + // $makerNode(td)に対するthを探す + // "著者", "サークル名", "ブランド名"など + $makersHeadNode = $xpath->query('preceding-sibling::th', $makersNode)->item(0); + $makersHead = trim($makersHeadNode->textContent); - // 余分な文を消す + // 余分な文を消す - // OGPタイトルから作者名とサイト名を消す - $metadata->title = trim(preg_replace('~ \[[^\[\]]*\] (予告作品 )?\| DLsite(がるまに)?$~', '', $metadata->title)); + // OGPタイトルから作者名とサイト名を消す + $metadata->title = trim(preg_replace('~ \[[^\[\]]*\] (予告作品 )?\| DLsite(がるまに)?$~', '', $metadata->title)); - // OGP説明文から定型文を消す - 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); - } 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; + // OGP説明文から定型文を消す + 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); } 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; } } diff --git a/app/MetadataResolver/FC2ContentsResolver.php b/app/MetadataResolver/FC2ContentsResolver.php index 36a3b8b..79e2e6f 100644 --- a/app/MetadataResolver/FC2ContentsResolver.php +++ b/app/MetadataResolver/FC2ContentsResolver.php @@ -24,21 +24,17 @@ class FC2ContentsResolver implements Resolver public function resolve(string $url): Metadata { $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->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); - $xpath = new \DOMXPath($dom); + $dom = new \DOMDocument(); + @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); + $xpath = new \DOMXPath($dom); - $thumbnailNode = $xpath->query('//*[@class="main_thum_img"]/a')->item(0); - if ($thumbnailNode) { - $metadata->image = preg_replace('~^http:~', 'https:', $thumbnailNode->getAttribute('href')); - } - - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); + $thumbnailNode = $xpath->query('//*[@class="main_thum_img"]/a')->item(0); + if ($thumbnailNode) { + $metadata->image = preg_replace('~^http:~', 'https:', $thumbnailNode->getAttribute('href')); } + + return $metadata; } } diff --git a/app/MetadataResolver/FantiaResolver.php b/app/MetadataResolver/FantiaResolver.php index 4dec4c1..08f38e2 100644 --- a/app/MetadataResolver/FantiaResolver.php +++ b/app/MetadataResolver/FantiaResolver.php @@ -28,29 +28,25 @@ class FantiaResolver implements Resolver $postId = $match[0]; $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->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); - $xpath = new \DOMXPath($dom); + $dom = new \DOMDocument(); + @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); + $xpath = new \DOMXPath($dom); - $node = $xpath->query("//meta[@property='twitter:image']")->item(0); - $ogpUrl = $node->getAttribute('content'); + $node = $xpath->query("//meta[@property='twitter:image']")->item(0); + $ogpUrl = $node->getAttribute('content'); - // 投稿に画像がない場合(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); - $uuid = $match[1]; - $extension = $match[2]; + // 投稿に画像がない場合(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); + $uuid = $match[1]; + $extension = $match[2]; - // 大きい画像に変換 - $metadata->image = "https://c.fantia.jp/uploads/post/file/{$postId}/main_{$uuid}.{$extension}"; - } - - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); + // 大きい画像に変換 + $metadata->image = "https://c.fantia.jp/uploads/post/file/{$postId}/main_{$uuid}.{$extension}"; } + + return $metadata; } } diff --git a/app/MetadataResolver/FanzaResolver.php b/app/MetadataResolver/FanzaResolver.php index b1e1c66..5ef1c29 100644 --- a/app/MetadataResolver/FanzaResolver.php +++ b/app/MetadataResolver/FanzaResolver.php @@ -24,14 +24,10 @@ class FanzaResolver implements Resolver public function resolve(string $url): Metadata { $res = $this->client->get($url); - if ($res->getStatusCode() === 200) { - $metadata = $this->ogpResolver->parse($res->getBody()); - $metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $metadata->image); - $metadata->description = str_replace('<>', '', $metadata->description); + $metadata = $this->ogpResolver->parse($res->getBody()); + $metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $metadata->image); + $metadata->description = str_replace('<>', '', $metadata->description); - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); - } + return $metadata; } } diff --git a/app/MetadataResolver/IwaraResolver.php b/app/MetadataResolver/IwaraResolver.php index 3dbcf9e..ba4846c 100644 --- a/app/MetadataResolver/IwaraResolver.php +++ b/app/MetadataResolver/IwaraResolver.php @@ -20,45 +20,41 @@ class IwaraResolver implements Resolver public function resolve(string $url): Metadata { $res = $this->client->get($url); - if ($res->getStatusCode() === 200) { - $metadata = new Metadata(); - $html = (string) $res->getBody(); - $crawler = new Crawler($html); + $metadata = new Metadata(); + $html = (string) $res->getBody(); + $crawler = new Crawler($html); - $infoElements = $crawler->filter('#video-player + div, .field-name-field-video-url + div, .field-name-field-images + div'); - $title = $infoElements->filter('h1.title')->text(); - $author = $infoElements->filter('.username')->text(); - $description = $infoElements->filter('.field-type-text-with-summary')->text(''); - $tags = $infoElements->filter('a[href^="/videos"], a[href^="/images"]')->extract('_text'); - // 役に立たないタグを削除する - $tags = array_values(array_diff($tags, ['Uncategorized', 'Other'])); - array_push($tags, $author); + $infoElements = $crawler->filter('#video-player + div, .field-name-field-video-url + div, .field-name-field-images + div'); + $title = $infoElements->filter('h1.title')->text(); + $author = $infoElements->filter('.username')->text(); + $description = $infoElements->filter('.field-type-text-with-summary')->text(''); + $tags = $infoElements->filter('a[href^="/videos"], a[href^="/images"]')->extract('_text'); + // 役に立たないタグを削除する + $tags = array_values(array_diff($tags, ['Uncategorized', 'Other'])); + array_push($tags, $author); - $metadata->title = $title; - $metadata->description = '投稿者: ' . $author . PHP_EOL . $description; - $metadata->tags = $tags; + $metadata->title = $title; + $metadata->description = '投稿者: ' . $author . PHP_EOL . $description; + $metadata->tags = $tags; - // iwara video - if ($crawler->filter('#video-player')->count()) { - $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"); + // iwara video + if ($crawler->filter('#video-player')->count()) { + $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; } } diff --git a/app/MetadataResolver/KomifloResolver.php b/app/MetadataResolver/KomifloResolver.php index 7073015..22efa9f 100644 --- a/app/MetadataResolver/KomifloResolver.php +++ b/app/MetadataResolver/KomifloResolver.php @@ -24,33 +24,28 @@ class KomifloResolver implements Resolver $id = $matches[1]; $res = $this->client->get('https://api.komiflo.com/content/id/' . $id); - if ($res->getStatusCode() === 200) { - $json = json_decode($res->getBody()->getContents(), true); - $metadata = new Metadata(); + $json = json_decode($res->getBody()->getContents(), true); + $metadata = new Metadata(); - $metadata->title = $json['content']['data']['title'] ?? ''; - $metadata->description = ($json['content']['attributes']['artists']['children'][0]['data']['name'] ?? '?') . - ' - ' . - ($json['content']['parents'][0]['data']['title'] ?? '?'); - $metadata->image = 'https://t.komiflo.com/564_mobile_large_3x/' . $json['content']['named_imgs']['cover']['filename']; + $metadata->title = $json['content']['data']['title'] ?? ''; + $metadata->description = ($json['content']['attributes']['artists']['children'][0]['data']['name'] ?? '?') . + ' - ' . ($json['content']['parents'][0]['data']['title'] ?? '?'); + $metadata->image = 'https://t.komiflo.com/564_mobile_large_3x/' . $json['content']['named_imgs']['cover']['filename']; - // 作者情報 - if (!empty($json['content']['attributes']['artists']['children'])) { - foreach ($json['content']['attributes']['artists']['children'] as $artist) { - $metadata->tags[] = preg_replace('/\s/', '_', $artist['data']['name']); - } + // 作者情報 + if (!empty($json['content']['attributes']['artists']['children'])) { + foreach ($json['content']['attributes']['artists']['children'] as $artist) { + $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; } } diff --git a/app/MetadataResolver/MelonbooksResolver.php b/app/MetadataResolver/MelonbooksResolver.php index b94b719..d5ebe2b 100644 --- a/app/MetadataResolver/MelonbooksResolver.php +++ b/app/MetadataResolver/MelonbooksResolver.php @@ -27,46 +27,42 @@ class MelonbooksResolver implements Resolver $cookieJar = CookieJar::fromArray(['AUTH_ADULT' => '1'], 'www.melonbooks.co.jp'); $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->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); - $xpath = new \DOMXPath($dom); - $descriptionNodelist = $xpath->query('//div[@id="description"]//p'); - $specialDescriptionNodelist = $xpath->query('//div[@id="special_description"]//p'); + $dom = new \DOMDocument(); + @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); + $xpath = new \DOMXPath($dom); + $descriptionNodelist = $xpath->query('//div[@id="description"]//p'); + $specialDescriptionNodelist = $xpath->query('//div[@id="special_description"]//p'); - // censoredフラグの除去 - if (mb_strpos($metadata->image, '&c=1') !== false) { - $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('
', "\n", $specialDescriptionNodelist->item(0)->nodeValue)) . "\n"; - if ($specialDescriptionNodelist->length === 2) { - $description .= "\n"; - $description .= trim(str_replace('
', "\n", $specialDescriptionNodelist->item(1)->nodeValue)) . "\n"; - } - } - - if ($descriptionNodelist->length !== 0) { - $description .= trim(str_replace('
', "\n", $descriptionNodelist->item(0)->nodeValue)); - } - - $metadata->title = $title; - $metadata->description = trim($description); - - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); + // censoredフラグの除去 + if (mb_strpos($metadata->image, '&c=1') !== false) { + $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('
', "\n", $specialDescriptionNodelist->item(0)->nodeValue)) . "\n"; + if ($specialDescriptionNodelist->length === 2) { + $description .= "\n"; + $description .= trim(str_replace('
', "\n", $specialDescriptionNodelist->item(1)->nodeValue)) . "\n"; + } + } + + if ($descriptionNodelist->length !== 0) { + $description .= trim(str_replace('
', "\n", $descriptionNodelist->item(0)->nodeValue)); + } + + $metadata->title = $title; + $metadata->description = trim($description); + + return $metadata; } } diff --git a/app/MetadataResolver/NarouResolver.php b/app/MetadataResolver/NarouResolver.php index 6600b23..1ee5eb0 100644 --- a/app/MetadataResolver/NarouResolver.php +++ b/app/MetadataResolver/NarouResolver.php @@ -27,34 +27,30 @@ class NarouResolver implements Resolver $cookieJar = CookieJar::fromArray(['over18' => 'yes'], '.syosetu.com'); $res = $this->client->get($url, ['cookies' => $cookieJar]); - if ($res->getStatusCode() === 200) { - $metadata = $this->ogpResolver->parse($res->getBody()); - $metadata->description = ''; + $metadata = $this->ogpResolver->parse($res->getBody()); + $metadata->description = ''; - $dom = new \DOMDocument(); - @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win')); - $xpath = new \DOMXPath($dom); + $dom = new \DOMDocument(); + @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win')); + $xpath = new \DOMXPath($dom); - $description = []; + $description = []; - // 作者名 - $writerNodes = $xpath->query('//*[contains(@class, "novel_writername")]'); - if ($writerNodes->length !== 0 && !empty($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"); + // 作者名 + $writerNodes = $xpath->query('//*[contains(@class, "novel_writername")]'); + if ($writerNodes->length !== 0 && !empty($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; } } diff --git a/app/MetadataResolver/NijieResolver.php b/app/MetadataResolver/NijieResolver.php index 33408ab..8ce9315 100644 --- a/app/MetadataResolver/NijieResolver.php +++ b/app/MetadataResolver/NijieResolver.php @@ -32,32 +32,28 @@ class NijieResolver implements Resolver } $res = $this->client->get($url); - if ($res->getStatusCode() === 200) { - $html = (string) $res->getBody(); - $metadata = $this->ogpResolver->parse($html); - $crawler = new Crawler($html); + $html = (string) $res->getBody(); + $metadata = $this->ogpResolver->parse($html); + $crawler = new Crawler($html); - // DomCrawler内でjson内の日本語がHTMLエンティティに変換されるのでhtml_entity_decode - $json = html_entity_decode($crawler->filter('script[type="application/ld+json"]')->first()->text()); + // DomCrawler内でjson内の日本語がHTMLエンティティに変換されるのでhtml_entity_decode + $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->description = '投稿者: ' . $data['author']['name'] . PHP_EOL . $data['description']; - if ( - isset($data['thumbnailUrl']) && - !ends_with($data['thumbnailUrl'], '.gif') && - !ends_with($data['thumbnailUrl'], '.mp4') - ) { - // サムネイルからメイン画像に - $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->title = $data['name']; + $metadata->description = '投稿者: ' . $data['author']['name'] . PHP_EOL . $data['description']; + if ( + isset($data['thumbnailUrl']) && + !ends_with($data['thumbnailUrl'], '.gif') && + !ends_with($data['thumbnailUrl'], '.mp4') + ) { + // サムネイルからメイン画像に + $metadata->image = str_replace('__rs_l160x160/', '', $data['thumbnailUrl']); } + $metadata->tags = $crawler->filter('#view-tag span.tag_name')->extract('_text'); + + return $metadata; } } diff --git a/app/MetadataResolver/OGPResolver.php b/app/MetadataResolver/OGPResolver.php index f8e185e..2c16997 100644 --- a/app/MetadataResolver/OGPResolver.php +++ b/app/MetadataResolver/OGPResolver.php @@ -19,11 +19,7 @@ class OGPResolver implements Resolver, Parser public function resolve(string $url): Metadata { $res = $this->client->get($url); - if ($res->getStatusCode() === 200) { - return $this->parse($res->getBody()); - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); - } + return $this->parse($res->getBody()); } public function parse(string $html): Metadata diff --git a/app/MetadataResolver/PatreonResolver.php b/app/MetadataResolver/PatreonResolver.php index 7ba3235..0d32fe0 100644 --- a/app/MetadataResolver/PatreonResolver.php +++ b/app/MetadataResolver/PatreonResolver.php @@ -25,18 +25,14 @@ class PatreonResolver implements Resolver public function resolve(string $url): Metadata { $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); - if (isset($query['token-time'])) { - $expires_at_unixtime = $query['token-time']; - $metadata->expires_at = Carbon::createFromTimestamp($expires_at_unixtime); - } - - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); + parse_str(parse_url($metadata->image, PHP_URL_QUERY), $query); + if (isset($query['token-time'])) { + $expires_at_unixtime = $query['token-time']; + $metadata->expires_at = Carbon::createFromTimestamp($expires_at_unixtime); } + + return $metadata; } } diff --git a/app/MetadataResolver/PixivResolver.php b/app/MetadataResolver/PixivResolver.php index ff893a0..327c74c 100644 --- a/app/MetadataResolver/PixivResolver.php +++ b/app/MetadataResolver/PixivResolver.php @@ -38,14 +38,10 @@ class PixivResolver implements Resolver { if (preg_match('~www\.pixiv\.net/user/\d+/series/\d+~', $url, $matches)) { $res = $this->client->get($url); - if ($res->getStatusCode() === 200) { - $metadata = $this->ogpResolver->parse($res->getBody()); - $metadata->image = $this->proxize($metadata->image); + $metadata = $this->ogpResolver->parse($res->getBody()); + $metadata->image = $this->proxize($metadata->image); - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); - } + return $metadata; } 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); - if ($res->getStatusCode() === 200) { - $json = json_decode($res->getBody()->getContents(), true); - $metadata = new Metadata(); + $json = json_decode($res->getBody()->getContents(), true); + $metadata = new Metadata(); - $metadata->title = $json['body']['illustTitle'] ?? ''; - $metadata->description = '投稿者: ' . $json['body']['userName'] . PHP_EOL . strip_tags(str_replace('
', PHP_EOL, $json['body']['illustComment'] ?? '')); - $metadata->image = $this->proxize($json['body']['urls']['regular'] ?? ''); + $metadata->title = $json['body']['illustTitle'] ?? ''; + $metadata->description = '投稿者: ' . $json['body']['userName'] . PHP_EOL . strip_tags(str_replace('
', PHP_EOL, $json['body']['illustComment'] ?? '')); + $metadata->image = $this->proxize($json['body']['urls']['regular'] ?? ''); - // ページ数の指定がある場合は画像URLをそのページにする - if ($page != 0) { - $metadata->image = str_replace('_p0', '_p'.$page, $metadata->image); - } + // ページ数の指定がある場合は画像URLをそのページにする + if ($page != 0) { + $metadata->image = str_replace('_p0', '_p' . $page, $metadata->image); + } - // タグ - if (!empty($json['body']['tags']['tags'])) { - foreach ($json['body']['tags']['tags'] as $tag) { - // 一部の固定キーワードは無視 - if (array_search($tag['tag'], ['R-18', 'イラスト', 'pixiv', 'ピクシブ'], true) === false) { - $metadata->tags[] = preg_replace('/\s/', '_', $tag['tag']); - } + // タグ + if (!empty($json['body']['tags']['tags'])) { + foreach ($json['body']['tags']['tags'] as $tag) { + // 一部の固定キーワードは無視 + if (array_search($tag['tag'], ['R-18', 'イラスト', 'pixiv', 'ピクシブ'], true) === false) { + $metadata->tags[] = preg_replace('/\s/', '_', $tag['tag']); } } - - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); } + + return $metadata; } } diff --git a/app/MetadataResolver/PlurkResolver.php b/app/MetadataResolver/PlurkResolver.php index 7422fef..b381399 100644 --- a/app/MetadataResolver/PlurkResolver.php +++ b/app/MetadataResolver/PlurkResolver.php @@ -24,21 +24,17 @@ class PlurkResolver implements Resolver public function resolve(string $url): Metadata { $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->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); - $xpath = new \DOMXPath($dom); - $imageNode = $xpath->query('//div[@class="text_holder"]/a[1]')->item(0); + $dom = new \DOMDocument(); + @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); + $xpath = new \DOMXPath($dom); + $imageNode = $xpath->query('//div[@class="text_holder"]/a[1]')->item(0); - if ($imageNode) { - $metadata->image = $imageNode->getAttribute('href'); - } - - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); + if ($imageNode) { + $metadata->image = $imageNode->getAttribute('href'); } + + return $metadata; } } diff --git a/app/MetadataResolver/SteamResolver.php b/app/MetadataResolver/SteamResolver.php index 987c97b..010bf37 100644 --- a/app/MetadataResolver/SteamResolver.php +++ b/app/MetadataResolver/SteamResolver.php @@ -24,21 +24,17 @@ class SteamResolver implements Resolver $appid = $matches[1]; $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); - if ($json[$appid]['success'] === false) { - 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('
', PHP_EOL, html_entity_decode($data['short_description'] ?? ''))); - $metadata->image = $data['header_image'] ?? ''; - - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); + $json = json_decode($res->getBody()->getContents(), true); + if ($json[$appid]['success'] === false) { + 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('
', PHP_EOL, html_entity_decode($data['short_description'] ?? ''))); + $metadata->image = $data['header_image'] ?? ''; + + return $metadata; } } diff --git a/app/MetadataResolver/ToranoanaResolver.php b/app/MetadataResolver/ToranoanaResolver.php index 382cac7..7d60780 100644 --- a/app/MetadataResolver/ToranoanaResolver.php +++ b/app/MetadataResolver/ToranoanaResolver.php @@ -25,20 +25,16 @@ class ToranoanaResolver implements Resolver public function resolve(string $url): Metadata { $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->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); - $xpath = new \DOMXPath($dom); - $imgNode = $xpath->query('//*[@id="preview"]//img')->item(0); - if ($imgNode !== null) { - $metadata->image = $imgNode->getAttribute('src'); - } - - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); + $dom = new \DOMDocument(); + @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); + $xpath = new \DOMXPath($dom); + $imgNode = $xpath->query('//*[@id="preview"]//img')->item(0); + if ($imgNode !== null) { + $metadata->image = $imgNode->getAttribute('src'); } + + return $metadata; } } diff --git a/tests/Unit/MetadataResolver/OGPResolverTest.php b/tests/Unit/MetadataResolver/OGPResolverTest.php index d1c6e80..1081f0a 100644 --- a/tests/Unit/MetadataResolver/OGPResolverTest.php +++ b/tests/Unit/MetadataResolver/OGPResolverTest.php @@ -3,21 +3,12 @@ namespace Tests\Unit\MetadataResolver; use App\MetadataResolver\OGPResolver; -use GuzzleHttp\Exception\ClientException; use Tests\TestCase; class OGPResolverTest extends TestCase { use CreateMockedResolver; - public function testMissingUrl() - { - $this->createResolver(OGPResolver::class, '', [], 404); - - $this->expectException(\RuntimeException::class); - $this->resolver->resolve('http://example.com/404'); - } - public function testResolve() { $response = <<< 'HTML'