diff --git a/app/Ejaculation.php b/app/Ejaculation.php index 24f7e46..0f3522c 100644 --- a/app/Ejaculation.php +++ b/app/Ejaculation.php @@ -15,7 +15,7 @@ class Ejaculation extends Model protected $fillable = [ 'user_id', 'ejaculated_date', 'note', 'geo_latitude', 'geo_longitude', 'link', - 'is_private' + 'is_private', 'is_too_sensitive' ]; protected $dates = [ @@ -56,6 +56,7 @@ class Ejaculation extends Model }, 'likes.user' => function ($query) { $query->where('is_protected', false) + ->where('private_likes', false) ->orWhere('id', Auth::id()); } ]) @@ -72,11 +73,25 @@ class Ejaculation extends Model $query->latest()->take(10); }, 'likes.user' => function ($query) { - $query->where('is_protected', false); + $query->where('is_protected', false) + ->where('private_likes', false); } ]) ->withCount('likes') ->addSelect(DB::raw('0 as is_liked')); } } + + /** + * このチェックインと同じ情報を流用してチェックインするためのURLを生成 + * @return string + */ + public function makeCheckinURL(): string + { + return route('checkin', [ + 'link' => $this->link, + 'tags' => $this->textTags(), + 'is_too_sensitive' => $this->is_too_sensitive, + ]); + } } diff --git a/app/Http/Controllers/EjaculationController.php b/app/Http/Controllers/EjaculationController.php index 13ef8c4..cf11294 100644 --- a/app/Http/Controllers/EjaculationController.php +++ b/app/Http/Controllers/EjaculationController.php @@ -21,7 +21,8 @@ class EjaculationController extends Controller 'link' => $request->input('link', ''), 'tags' => $request->input('tags', ''), 'note' => $request->input('note', ''), - 'is_private' => $request->input('is_private', 0) == 1 + 'is_private' => $request->input('is_private', 0) == 1, + 'is_too_sensitive' => $request->input('is_too_sensitive', 0) == 1 ]; return view('ejaculation.checkin')->with('defaults', $defaults); @@ -56,7 +57,8 @@ class EjaculationController extends Controller 'ejaculated_date' => Carbon::createFromFormat('Y/m/d H:i', $inputs['date'] . ' ' . $inputs['time']), 'note' => $inputs['note'] ?? '', 'link' => $inputs['link'] ?? '', - 'is_private' => $request->has('is_private') ?? false + 'is_private' => $request->has('is_private') ?? false, + 'is_too_sensitive' => $request->has('is_too_sensitive') ?? false ]); $tagIds = []; @@ -137,7 +139,8 @@ class EjaculationController extends Controller 'ejaculated_date' => Carbon::createFromFormat('Y/m/d H:i', $inputs['date'] . ' ' . $inputs['time']), 'note' => $inputs['note'] ?? '', 'link' => $inputs['link'] ?? '', - 'is_private' => $request->has('is_private') ?? false + 'is_private' => $request->has('is_private') ?? false, + 'is_too_sensitive' => $request->has('is_too_sensitive') ?? false ])->save(); $tagIds = []; diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 86e8571..99c4b98 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -30,6 +30,7 @@ id, ejaculated_date, note, is_private, +is_too_sensitive, link, to_char(lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC), 'YYYY/MM/DD HH24:MI') AS before_date, to_char(ejaculated_date - (lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC)), 'FMDDD日 FMHH24時間 FMMI分') AS ejaculated_span @@ -151,6 +152,7 @@ id, ejaculated_date, note, is_private, +is_too_sensitive, link, to_char(lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC), 'YYYY/MM/DD HH24:MI') AS before_date, to_char(ejaculated_date - (lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC)), 'FMDDD日 FMHH24時間 FMMI分') AS ejaculated_span diff --git a/app/MetadataResolver/CienResolver.php b/app/MetadataResolver/CienResolver.php index 5af47c3..a7d9d67 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((string) $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/DeviantArtResolver.php b/app/MetadataResolver/DeviantArtResolver.php index 1233223..b658436 100644 --- a/app/MetadataResolver/DeviantArtResolver.php +++ b/app/MetadataResolver/DeviantArtResolver.php @@ -24,30 +24,16 @@ class DeviantArtResolver implements Resolver public function resolve(string $url): Metadata { $res = $this->client->get('https://backend.deviantart.com/oembed?url=' . $url); - if ($res->getStatusCode() === 200) { - $data = json_decode($res->getBody()->getContents(), true); - $metadata = new Metadata(); + $data = json_decode($res->getBody()->getContents(), true); + $metadata = new Metadata(); - if (preg_match('~\.wixmp\.com$~', parse_url($data['url'])['host'])) { - // アスペクト比を保ったまま、縦か横が最大1024pxになる画像を取得する。 - // Ref: https://support.wixmp.com/en/article/image-service-3835799 - // 作成されていない画像が参照されると403を返すようなので、サイト内で使用されている1024pxにした。 - if (strpos($data['url'], '/v1/fill/')) { - $metadata->image = preg_replace('~/v1/fill/w_\d+,h_\d+(?:,q_\d+),strp/.+\.(jpg|png|webp|gif)~', '/v1/fit/w_1024,h_1024,strp/image.jpg', $data['url']); - } else { - $queryStartPos = strpos($data['url'], '?'); - $metadata->image = substr_replace($data['url'], '/v1/fit/w_1024,h_1024,strp/image.jpg', $queryStartPos, 0); - } - } else { - $metadata->image = $data['url']; - } - - $metadata->title = $data['title'] ?? ''; - $metadata->description = 'By ' . $data['author_name']; - - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); + $metadata->title = $data['title'] ?? ''; + $metadata->description = 'By ' . $data['author_name']; + $metadata->image = $data['url']; + if (isset($data['tags'])) { + $metadata->tags = explode(', ', $data['tags']); } + + 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..d31fd6d 100644 --- a/app/MetadataResolver/FantiaResolver.php +++ b/app/MetadataResolver/FantiaResolver.php @@ -3,7 +3,6 @@ namespace App\MetadataResolver; use GuzzleHttp\Client; -use Illuminate\Support\Facades\Log; class FantiaResolver implements Resolver { @@ -11,46 +10,31 @@ class FantiaResolver implements Resolver * @var Client */ private $client; - /** - * @var OGPResolver - */ - private $ogpResolver; - public function __construct(Client $client, OGPResolver $ogpResolver) + public function __construct(Client $client) { $this->client = $client; - $this->ogpResolver = $ogpResolver; } public function resolve(string $url): Metadata { - preg_match("~\d+~", $url, $match); - $postId = $match[0]; + preg_match("~posts/(\d+)~", $url, $match); + $postId = $match[1]; - $res = $this->client->get($url); - if ($res->getStatusCode() === 200) { - $metadata = $this->ogpResolver->parse($res->getBody()); + $res = $this->client->get("https://fantia.jp/api/v1/posts/{$postId}"); + $data = json_decode(str_replace('\r\n', '\n', (string) $res->getBody()), true); + $post = $data['post']; - $dom = new \DOMDocument(); - @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); - $xpath = new \DOMXPath($dom); + $tags = array_map(function ($tag) { + return $tag['name']; + }, $post['tags']); - $node = $xpath->query("//meta[@property='twitter:image']")->item(0); - $ogpUrl = $node->getAttribute('content'); + $metadata = new Metadata(); + $metadata->title = $post['title'] ?? ''; + $metadata->description = 'サークル: ' . $post['fanclub']['fanclub_name_with_creator_name'] . PHP_EOL . $post['comment']; + $metadata->image = str_replace('micro', 'main', $post['thumb_micro']) ?? ''; + $metadata->tags = array_merge($tags, [$post['fanclub']['creator_name']]); - // 投稿に画像がない場合(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"); - } + return $metadata; } } diff --git a/app/MetadataResolver/FanzaResolver.php b/app/MetadataResolver/FanzaResolver.php index b1e1c66..84ead71 100644 --- a/app/MetadataResolver/FanzaResolver.php +++ b/app/MetadataResolver/FanzaResolver.php @@ -3,6 +3,7 @@ namespace App\MetadataResolver; use GuzzleHttp\Client; +use Symfony\Component\DomCrawler\Crawler; class FanzaResolver implements Resolver { @@ -21,17 +22,84 @@ class FanzaResolver implements Resolver $this->ogpResolver = $ogpResolver; } + /** + * arrayの各要素をtrim・スペースの_置換をした後、重複した値を削除してキーを詰め直す + * + * @param array $array + * + * @return array 処理されたarray + */ + public function array_finish(array $array): array + { + $array = array_map('trim', $array); + $array = array_map((function ($value) { + return str_replace(' ', '_', $value); + }), $array); + $array = array_unique($array); + $array = array_values($array); + + return $array; + } + 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); + $html = (string) $res->getBody(); + $crawler = new Crawler($html); + + // 動画 + if (preg_match('~www\.dmm\.co\.jp/digital/(videoa|videoc|anime)/-/detail~', $url)) { + $metadata = new Metadata(); + $metadata->title = trim($crawler->filter('#title')->text('')); + $metadata->description = trim($crawler->filter('.box-rank+table+div+div')->text('')); + $metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $crawler->filter('meta[property="og:image"]')->attr('content')); + $metadata->tags = $this->array_finish($crawler->filter('.box-rank+table a:not([href="#review"])')->extract(['_text'])); return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); } + + // 同人 + if (mb_strpos($url, 'www.dmm.co.jp/dc/doujin/-/detail/') !== false) { + $genre = $this->array_finish($crawler->filter('.m-productInformation a:not([href="#update-top"])')->extract(['_text'])); + $genre = array_filter($genre, (function ($text) { + return !preg_match('~%OFF対象$~', $text); + })); + + $metadata = new Metadata(); + $metadata->title = $crawler->filter('meta[property="og:title"]')->attr('content'); + $metadata->description = trim($crawler->filter('.summary__txt')->text('')); + $metadata->image = $crawler->filter('meta[property="og:image"]')->attr('content'); + $metadata->tags = array_merge($genre, [$crawler->filter('.circleName__txt')->text('')]); + + return $metadata; + } + + // 電子書籍 + if (mb_strpos($url, 'book.dmm.co.jp/detail/') !== false) { + $metadata = new Metadata(); + $metadata->title = trim($crawler->filter('#title')->text('')); + $metadata->description = trim($crawler->filter('.m-boxDetailProduct__info__story')->text('')); + $metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $crawler->filter('meta[property="og:image"]')->attr('content')); + $metadata->tags = $this->array_finish($crawler->filter('.m-boxDetailProductInfoMainList__description__list__item, .m-boxDetailProductInfo__list__description__item a')->extract(['_text'])); + + return $metadata; + } + + // PCゲーム + if (mb_strpos($url, 'dlsoft.dmm.co.jp/detail/') !== false) { + $metadata = new Metadata(); + $metadata->title = trim($crawler->filter('#title')->text('')); + $metadata->description = trim($crawler->filter('.area-detail-read .text-overflow')->text('')); + $metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $crawler->filter('meta[property="og:image"]')->attr('content')); + $metadata->tags = $this->array_finish($crawler->filter('.area-bskt table a:not([href="#review"])')->extract(['_text'])); + + return $metadata; + } + + // 上で特に対応しなかったURL 画像の置換くらいはしておく + $metadata = $this->ogpResolver->parse($html); + $metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $metadata->image); + + 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/MetadataResolver.php b/app/MetadataResolver/MetadataResolver.php index 88981e2..9e19fe9 100644 --- a/app/MetadataResolver/MetadataResolver.php +++ b/app/MetadataResolver/MetadataResolver.php @@ -20,6 +20,7 @@ class MetadataResolver implements Resolver '~www\.dlsite\.com/.*/dlaf/=/aid/.+/url/.+~' => DLsiteResolver::class, '~dlsite\.jp/...tw/..\d+~' => DLsiteResolver::class, '~www\.pixiv\.net/member_illust\.php\?illust_id=\d+~' => PixivResolver::class, + '~www\.pixiv\.net/artworks/\d+~' => PixivResolver::class, '~www\.pixiv\.net/user/\d+/series/\d+~' => PixivResolver::class, '~fantia\.jp/posts/\d+~' => FantiaResolver::class, '~dmm\.co\.jp/~' => FanzaResolver::class, 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/NicoSeigaResolver.php b/app/MetadataResolver/NicoSeigaResolver.php index 3f2e000..de1a8ab 100644 --- a/app/MetadataResolver/NicoSeigaResolver.php +++ b/app/MetadataResolver/NicoSeigaResolver.php @@ -3,6 +3,7 @@ namespace App\MetadataResolver; use GuzzleHttp\Client; +use Symfony\Component\DomCrawler\Crawler; class NicoSeigaResolver implements Resolver { @@ -24,16 +25,18 @@ class NicoSeigaResolver implements Resolver public function resolve(string $url): Metadata { $res = $this->client->get($url); - if ($res->getStatusCode() === 200) { - $metadata = $this->ogpResolver->parse($res->getBody()); + $html = (string)$res->getBody(); + $metadata = $this->ogpResolver->parse($html); + $crawler = new Crawler($html); - // ページURLからサムネイルURLに変換 - preg_match('~http://(?:(?:sp\\.)?seiga\\.nicovideo\\.jp/seiga(?:/#!)?|nico\\.ms)/im(\\d+)~', $url, $matches); - $metadata->image = "http://lohas.nicoseiga.jp/thumb/${matches[1]}l?"; + // タグ + $excludeTags = ['R-15']; + $metadata->tags = array_values(array_diff($crawler->filter('.tag')->extract(['_text']), $excludeTags)); - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); - } + // ページURLからサムネイルURLに変換 + preg_match('~https?://(?:(?:sp\\.)?seiga\\.nicovideo\\.jp/seiga(?:/#!)?|nico\\.ms)/im(\\d+)~', $url, $matches); + $metadata->image = "https://lohas.nicoseiga.jp/thumb/${matches[1]}l?"; + + 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..a1cf603 100644 --- a/app/MetadataResolver/OGPResolver.php +++ b/app/MetadataResolver/OGPResolver.php @@ -18,12 +18,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($this->client->get($url)->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..e6690ac 100644 --- a/app/MetadataResolver/PixivResolver.php +++ b/app/MetadataResolver/PixivResolver.php @@ -38,52 +38,48 @@ 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); - $illustId = $params['illust_id']; $page = 0; + if (preg_match('~www\.pixiv\.net/artworks/(\d+)~', $url, $matches)) { + $illustId = $matches[1]; + } else { + parse_str(parse_url($url, PHP_URL_QUERY), $params); + $illustId = $params['illust_id']; - // 漫画ページ(ページ数はmanga_bigならあるかも) - if ($params['mode'] === 'manga_big' || $params['mode'] === 'manga') { - $page = $params['page'] ?? 0; + // 漫画ページ(ページ数はmanga_bigならあるかも) + if ($params['mode'] === 'manga_big' || $params['mode'] === 'manga') { + $page = $params['page'] ?? 0; + } } $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/app/MetadataResolver/XtubeResolver.php b/app/MetadataResolver/XtubeResolver.php index d56cce0..d2b3272 100644 --- a/app/MetadataResolver/XtubeResolver.php +++ b/app/MetadataResolver/XtubeResolver.php @@ -3,6 +3,7 @@ namespace App\MetadataResolver; use GuzzleHttp\Client; +use Symfony\Component\DomCrawler\Crawler; class XtubeResolver implements Resolver { @@ -18,24 +19,24 @@ class XtubeResolver implements Resolver public function resolve(string $url): Metadata { - if (preg_match('~www\.xtube\.com/video-watch/.*-(\d+)$~', $url, $matches) !== 1) { + if (preg_match('~www\.xtube\.com/video-watch/.*-(\d+)$~', $url) !== 1) { throw new \RuntimeException("Unmatched URL Pattern: $url"); } - $videoid = $matches[1]; - $res = $this->client->get('https://www.xtube.com/webmaster/api/getvideobyid?video_id=' . $videoid); - if ($res->getStatusCode() === 200) { - $data = json_decode($res->getBody()->getContents(), true); - $metadata = new Metadata(); + $res = $this->client->get($url); + $html = (string) $res->getBody(); + $metadata = new Metadata(); + $crawler = new Crawler($html); - $metadata->title = $data['title'] ?? ''; - $metadata->description = strip_tags(str_replace('\n', PHP_EOL, html_entity_decode($data['description'] ?? ''))); - $metadata->image = str_replace('eSuQ8f', 'eSK08f', $data['thumb'] ?? ''); // 300x169 to 300x210 - $metadata->tags = array_values(array_unique($data['tags'])); + // poster URL抽出 + $playerConfig = explode("\n", trim($crawler->filter('#playerWrapper script')->last()->text())); + preg_match('~https:\\\/\\\/cdn\d+-s-hw-e5\.xtube\.com\\\/m=(?P.{8})\\\/videos\\\/\d{6}\\\/\d{2}\\\/.{5}-.{4}-\\\/original\\\/\d+\.jpg~', $playerConfig[0], $matches); + $metadata->image = str_replace('\/', '/', $matches[0]); - return $metadata; - } else { - throw new \RuntimeException("{$res->getStatusCode()}: $url"); - } + $metadata->title = trim($crawler->filter('.underPlayerRateForm h1')->text('')); + $metadata->description = trim($crawler->filter('.fullDescription ')->text('')); + $metadata->tags = $crawler->filter('.tagsCategories a')->extract('_text'); + + return $metadata; } } diff --git a/database/migrations/2019_09_07_164200_add_is_too_sensitive_to_ejaculations.php b/database/migrations/2019_09_07_164200_add_is_too_sensitive_to_ejaculations.php new file mode 100644 index 0000000..d897a39 --- /dev/null +++ b/database/migrations/2019_09_07_164200_add_is_too_sensitive_to_ejaculations.php @@ -0,0 +1,32 @@ +boolean('is_too_sensitive')->default(false); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('ejaculations', function (Blueprint $table) { + $table->dropColumn('is_too_sensitive'); + }); + } +} diff --git a/resources/assets/js/app.js b/resources/assets/js/app.js index bf79d54..629d2dc 100644 --- a/resources/assets/js/app.js +++ b/resources/assets/js/app.js @@ -92,4 +92,10 @@ $(() => { }); } }); + + $(document).on('click', '.card-spoiler-overlay', function (event) { + const $this = $(this); + $this.siblings(".card-link").removeClass("card-spoiler"); + $this.remove(); + }); }); \ No newline at end of file diff --git a/resources/assets/sass/components/_link-card.scss b/resources/assets/sass/components/_link-card.scss index a154454..f5f7268 100644 --- a/resources/assets/sass/components/_link-card.scss +++ b/resources/assets/sass/components/_link-card.scss @@ -30,4 +30,27 @@ .card-text { white-space: pre-line; } + + .card-spoiler-overlay { + position: absolute; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + cursor: pointer; + + .warning-text { + padding: 10px; + user-select: none; + background-color: rgba(240, 240, 240, 0.8); + border-radius: 5px; + } + } + + .card-spoiler { + z-index: 1; + filter: blur(15px) grayscale(100%); + } } diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php index 0b8764d..031c035 100644 --- a/resources/views/auth/register.blade.php +++ b/resources/views/auth/register.blade.php @@ -24,6 +24,7 @@
+ 半角英数字と一部記号が使用できます。一度決めたら変更できません。 @if ($errors->has('name'))
{{ $errors->first('name') }}
@@ -81,4 +82,4 @@
-@endsection \ No newline at end of file +@endsection diff --git a/resources/views/components/ejaculation.blade.php b/resources/views/components/ejaculation.blade.php index cda134b..9635273 100644 --- a/resources/views/components/ejaculation.blade.php +++ b/resources/views/components/ejaculation.blade.php @@ -19,7 +19,7 @@ @if (!empty($ejaculation->link))
- @component('components.link-card', ['link' => $ejaculation->link]) + @component('components.link-card', ['link' => $ejaculation->link, 'is_too_sensitive' => $ejaculation->is_too_sensitive]) @endcomponent

{{ $ejaculation->link }} @@ -49,8 +49,8 @@

+ title="同じオカズでチェックイン" data-href="{{ $ejaculation->makeCheckinURL() }}"> -
\ No newline at end of file +
diff --git a/resources/views/components/link-card.blade.php b/resources/views/components/link-card.blade.php index f0d7de3..57eda2d 100644 --- a/resources/views/components/link-card.blade.php +++ b/resources/views/components/link-card.blade.php @@ -1,5 +1,10 @@ diff --git a/resources/views/ejaculation/edit.blade.php b/resources/views/ejaculation/edit.blade.php index 2e5984e..ffdbd45 100644 --- a/resources/views/ejaculation/edit.blade.php +++ b/resources/views/ejaculation/edit.blade.php @@ -86,6 +86,12 @@ このチェックインを非公開にする +
+ is_too_sensitive) ? 'checked' : '' }}> + +
diff --git a/resources/views/ejaculation/show.blade.php b/resources/views/ejaculation/show.blade.php index e5dc595..8d870e0 100644 --- a/resources/views/ejaculation/show.blade.php +++ b/resources/views/ejaculation/show.blade.php @@ -47,7 +47,7 @@ @if (!empty($ejaculation->link))
- @component('components.link-card', ['link' => $ejaculation->link]) + @component('components.link-card', ['link' => $ejaculation->link, 'is_too_sensitive' => $ejaculation->is_too_sensitive]) @endcomponent

{{ $ejaculation->link }} @@ -75,7 +75,7 @@ @endif

- + @if ($user->isMe()) diff --git a/resources/views/setting/profile.blade.php b/resources/views/setting/profile.blade.php index 706b1df..deff717 100644 --- a/resources/views/setting/profile.blade.php +++ b/resources/views/setting/profile.blade.php @@ -29,7 +29,7 @@
- 現在は変更できません。 + 変更することはできません。
diff --git a/resources/views/user/profile.blade.php b/resources/views/user/profile.blade.php index 7276548..056a53e 100644 --- a/resources/views/user/profile.blade.php +++ b/resources/views/user/profile.blade.php @@ -53,7 +53,7 @@ @if (!empty($ejaculation->link))
- @component('components.link-card', ['link' => $ejaculation->link]) + @component('components.link-card', ['link' => $ejaculation->link, 'is_too_sensitive' => $ejaculation->is_too_sensitive]) @endcomponent

{{ $ejaculation->link }} @@ -81,7 +81,7 @@ @endif

- + @if ($user->isMe()) diff --git a/tests/MyAsserts.php b/tests/MyAsserts.php new file mode 100644 index 0000000..c8019d8 --- /dev/null +++ b/tests/MyAsserts.php @@ -0,0 +1,17 @@ +assertSame($expected, array_intersect($actual, $expected), $message); + } +} diff --git a/tests/Unit/MetadataResolver/CienResolverTest.php b/tests/Unit/MetadataResolver/CienResolverTest.php new file mode 100644 index 0000000..ca6f8d1 --- /dev/null +++ b/tests/Unit/MetadataResolver/CienResolverTest.php @@ -0,0 +1,48 @@ +shouldUseMock()) { + sleep(1); + } + } + + public function test() + { + $responseText = file_get_contents(__DIR__ . '/../../fixture/Cien/test.html'); + + $this->createResolver(CienResolver::class, $responseText); + + $metadata = $this->resolver->resolve('https://ci-en.dlsite.com/creator/2462/article/87502'); + $this->assertSame('進捗とボツ立ち絵', $metadata->title); + $this->assertSame('ドット製2D ACTを製作しています。' . PHP_EOL . '恐ろしい存在に襲われる絶望感や、被虐的な官能がテーマです。', $metadata->description); + $this->assertStringStartsWith('https://media.ci-en.jp/private/attachment/creator/00002462/a7afd3b02a6d1caa6afe6a3bf5550fb6a42aefba686f17a0a2f63c97fd6867ab/image-800.jpg?px-time=', $metadata->image); + if ($this->shouldUseMock()) { + $this->assertSame('https://media.ci-en.jp/private/attachment/creator/00002462/a7afd3b02a6d1caa6afe6a3bf5550fb6a42aefba686f17a0a2f63c97fd6867ab/image-800.jpg?px-time=1568231879&px-hash=70c57e9a73d5afb4ac5363d1f37a851af8e0cb1f', $metadata->image); + $this->assertSame(1568235479, $metadata->expires_at->timestamp); + $this->assertSame('https://ci-en.dlsite.com/creator/2462/article/87502', (string) $this->handler->getLastRequest()->getUri()); + } + } + + public function testWithNoTimestamp() + { + $responseText = file_get_contents(__DIR__ . '/../../fixture/Cien/testWithNoTimestamp.html'); + $this->createResolver(CienResolver::class, $responseText); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Parameter "px-time" not found. Image=https://ci-en.dlsite.com/assets/img/common/logo_Ci-en_R18.svg Source=https://ci-en.dlsite.com/'); + + $this->resolver->resolve('https://ci-en.dlsite.com/'); + } +} diff --git a/tests/Unit/MetadataResolver/CreateMockedResolver.php b/tests/Unit/MetadataResolver/CreateMockedResolver.php index 528728c..e7e0bc6 100644 --- a/tests/Unit/MetadataResolver/CreateMockedResolver.php +++ b/tests/Unit/MetadataResolver/CreateMockedResolver.php @@ -5,6 +5,7 @@ namespace Tests\Unit\MetadataResolver; use App\MetadataResolver\Resolver; use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; +use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7\Response; use Monolog\Handler\AbstractHandler; @@ -41,7 +42,7 @@ trait CreateMockedResolver $mockResponse = new Response($status, $headers, $responseText); $this->handler = new MockHandler([$mockResponse]); - $client = new Client(['handler' => $this->handler]); + $client = new Client(['handler' => HandlerStack::create($this->handler)]); $this->resolver = app()->make($resolverClass, ['client' => $client]); return $this->resolver; diff --git a/tests/Unit/MetadataResolver/DLsiteResolverTest.php b/tests/Unit/MetadataResolver/DLsiteResolverTest.php index 2f163cb..ff164ba 100644 --- a/tests/Unit/MetadataResolver/DLsiteResolverTest.php +++ b/tests/Unit/MetadataResolver/DLsiteResolverTest.php @@ -44,7 +44,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('ことのはアムリラート', $metadata->title); $this->assertEquals('メーカー名: SukeraSparo' . PHP_EOL . '異世界へと迷い込んだ凜に救いの手を差し伸べるルカ――。これは、ふたりが手探りの意思疎通(ことのは)で織りなす、もどかしくも純粋な……女の子同士の物語。', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/professional/VJ012000/VJ011276_img_main.jpg', $metadata->image); - $this->assertEquals(['少女', '日常/生活', '純愛', '百合'], $metadata->tags); + $this->assertEquals(['日常/生活', '純愛', '百合', '少女'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/soft/work/=/product_id/VJ011276.html', (string) $this->handler->getLastRequest()->getUri()); } @@ -60,7 +60,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('快楽ヒストリエ', $metadata->title); $this->assertEquals('著者: 火鳥' . PHP_EOL . '天地創造と原初の人類を描いた「創世編」をはじめ、英雄たちの偉業を大真面目に考証した正真正銘の学術コミック全15編。', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/books/BJ139000/BJ138581_img_main.jpg', $metadata->image); - $this->assertEquals(['おっぱい', 'ロリ', 'ショタ', '妹', '男性/おやじ', '女王様/お姫様', '王子様/王子系', '戦士', 'セーラー服', '着物/和服', '青年コミック', 'ギャグ', 'コメディ', '歴史/時代物', '褐色/日焼け', '爺'], $metadata->tags); + $this->assertEquals(['おっぱい', '青年コミック', 'ギャグ', 'コメディ', '歴史/時代物', 'ロリ', 'ショタ', '妹', '男性/おやじ', '女王様/お姫様', '王子様/王子系', '戦士', 'セーラー服', '着物/和服', '褐色/日焼け', '爺'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/comic/work/=/product_id/BJ138581.html', (string) $this->handler->getLastRequest()->getUri()); } @@ -76,7 +76,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('催眠術で新婚人妻マナカさんとエッチしよう', $metadata->title); $this->assertEquals('サークル名: デルタブレード' . PHP_EOL . '催眠術で新婚人妻マナカさんの愛する夫にすり替わって子作りラブラブエッチをするCG集です。', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/doujin/RJ206000/RJ205445_img_main.jpg', $metadata->image); - $this->assertEquals(['断面図', '人妻', '中出し', '妊娠/孕ませ', '催眠', '口内射精'], $metadata->tags); + $this->assertEquals(['断面図', '中出し', '妊娠/孕ませ', '催眠', '口内射精', '人妻'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/maniax/work/=/product_id/RJ205445.html', (string) $this->handler->getLastRequest()->getUri()); } @@ -92,7 +92,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('euphoria (HDリマスター) Best Price版', $metadata->title); $this->assertEquals('ブランド名: CLOCK UP' . PHP_EOL . 'インモラルハードコアADV「euphoria」が高解像度(1024×768)版、「euphoria HDリマスター」となって登場!', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/professional/VJ009000/VJ008455_img_main.jpg', $metadata->image); - $this->assertEquals(['アブノーマル', '幼なじみ', '女教師', '退廃/背徳/インモラル', '拘束', '強制/無理矢理', 'スカトロ', 'アヘ顔', '拷問', '血液/流血', '狂気'], $metadata->tags); + $this->assertEquals(['アブノーマル', 'アヘ顔', '退廃/背徳/インモラル', '拘束', '強制/無理矢理', 'スカトロ', '幼なじみ', '女教師', '拷問', '血液/流血', '狂気'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/pro/work/=/product_id/VJ008455.html', (string) $this->handler->getLastRequest()->getUri()); } @@ -108,7 +108,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('永遠娘 vol.6', $metadata->title); $this->assertEquals('著者: あまがえる / 玉之けだま / びんせん / 甘露アメ / 源五郎 / すみやお / 宇宙烏賊 / 毒茸人 / あやね / ガロウド / ハードボイルドよし子 / 夜歌 / 黒青郎君' . PHP_EOL . '君の命はどんな味なのだろうな?', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/books/BJ192000/BJ191317_img_main.jpg', $metadata->image); - $this->assertEquals(['ツンデレ', 'ロリ', '妖怪', '人外娘/モンスター娘', 'セーラー服', 'メイド', 'ストッキング', 'ファンタジー', 'ぶっかけ', '中出し', '近親相姦', 'アヘ顔', '口内射精'], $metadata->tags); + $this->assertEquals(['アヘ顔', 'ファンタジー', 'ぶっかけ', '中出し', '近親相姦', '口内射精', 'ツンデレ', 'ロリ', '妖怪', '人外娘/モンスター娘', 'セーラー服', 'メイド', 'ストッキング'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/books/work/=/product_id/BJ191317.html', (string) $this->handler->getLastRequest()->getUri()); } @@ -124,7 +124,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('体イク教師', $metadata->title); $this->assertEquals('サークル名: Dusk' . PHP_EOL . '思い込みの激しい体育教師に執着されるお話', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/doujin/RJ218000/RJ217995_img_main.jpg', $metadata->image); - $this->assertEquals(['教師', '中出し', '陵辱', '変態', '強制/無理矢理', 'レイプ'], $metadata->tags); + $this->assertEquals(['中出し', '陵辱', '変態', '強制/無理矢理', 'レイプ', '教師'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/girls/work/=/product_id/RJ217995.html', (string) $this->handler->getLastRequest()->getUri()); } @@ -140,7 +140,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('×××レクチャー', $metadata->title); $this->assertEquals('著者: 江口尋' . PHP_EOL . '昔、告白してくれた地味な同級生・瀬尾は超人気セクシー男優になっていて!?', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/books/BJ171000/BJ170641_img_main.jpg', $metadata->image); - $this->assertEquals(['メガネ', '芸能人/アイドル/モデル', '俺様', 'ラブコメ', 'ラブラブ/あまあま', 'ティーンズラブ', '調教', '褐色/日焼け'], $metadata->tags); + $this->assertEquals(['ラブコメ', 'ラブラブ/あまあま', 'ティーンズラブ', '調教', 'メガネ', '芸能人/アイドル/モデル', '俺様', '褐色/日焼け'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/girls-pro/work/=/product_id/BJ170641.html', (string) $this->handler->getLastRequest()->getUri()); } @@ -156,7 +156,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('秘密に堕つ', $metadata->title); $this->assertEquals('サークル名: ナゲットぶん投げ屋さん' . PHP_EOL . 'とある村に越してきた新婚夫婦。村の集会所で行われた歓迎会で犯される花婿。村の男達に犯され続けた花婿にある変化が…?', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/doujin/RJ245000/RJ244977_img_main.jpg', $metadata->image); - $this->assertEquals(['既婚者', '中出し', '強制/無理矢理', 'レイプ', 'モブ姦'], $metadata->tags); + $this->assertEquals(['中出し', '強制/無理矢理', 'レイプ', 'モブ姦', '既婚者'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/bl/work/=/product_id/RJ244977.html', (string) $this->handler->getLastRequest()->getUri()); } @@ -172,7 +172,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('With Your First Girlfriend, at a Ghostly Night [Ear Cleaning] [Sleep Sharing]', $metadata->title); $this->assertEquals('Circle: Triangle!' . PHP_EOL . 'You go with a girl of your first love and enjoy going to haunted places and her massage, ear cleaning, sleep sharing etc. (CV: Yui Asami)', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/doujin/RJ229000/RJ228866_img_main.jpg', $metadata->image); - $this->assertEquals(['Healing', 'Binaural', 'ASMR', 'Childhood Friend', 'Ear Cleaning', 'Romance'], $metadata->tags); + $this->assertEquals(['Healing', 'Binaural', 'ASMR', 'Ear Cleaning', 'Lovey Dovey/Sweet Love', 'Childhood Friend'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/eng/work/=/product_id/RE228866.html', (string) $this->handler->getLastRequest()->getUri()); } @@ -188,7 +188,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('NEKOPARA vol.1', $metadata->title); $this->assertEquals('Circle: NEKO WORKs' . PHP_EOL . 'Chocolat and Vanilla star in a rich adult eroge series with E-mote system and animated H scenes', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/doujin/RJ145000/RJ144678_img_main.jpg', $metadata->image); - $this->assertEquals(['Moe', 'Master and Servant', 'Funny Love Story', 'Nekomimi (Cat Ears)'], $metadata->tags); + $this->assertEquals(['Moe', 'Love Comedy/Romcom', 'Master and Servant', 'Nekomimi (Cat Ears)'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/ecchi-eng/work/=/product_id/RE144678.html', (string) $this->handler->getLastRequest()->getUri()); } @@ -279,7 +279,7 @@ class DLsiteResolverTest extends TestCase $this->assertEquals('獣○彼女カタログ', $metadata->title); $this->assertEquals('著者: チキコ / MUJIN編集部' . PHP_EOL . '【DLsite.com独占販売】 エロ漫画界騒然、1冊まるごと獣○オンリー単行本! 人間チ×ポは出てきませんっ!!', $metadata->description); $this->assertEquals('https://img.dlsite.jp/modpub/images2/work/books/BJ124000/BJ123822_img_main.jpg', $metadata->image); - $this->assertEquals(['断面図', '制服', '水着', 'メイド', '巫女', '軍服', '中出し', 'フェラチオ', '複数プレイ/乱交', '異種姦', '巨乳/爆乳', '処女', '褐色/日焼け'], $metadata->tags); + $this->assertEquals(['断面図', '中出し', 'フェラチオ', '複数プレイ/乱交', '異種姦', '制服', '水着', 'メイド', '巫女', '軍服', '巨乳/爆乳', '処女', '褐色/日焼け'], $metadata->tags); if ($this->shouldUseMock()) { $this->assertSame('https://www.dlsite.com/books/work/=/product_id/BJ123822.html', (string) $this->handler->getLastRequest()->getUri()); } diff --git a/tests/Unit/MetadataResolver/DeviantArtResolverTest.php b/tests/Unit/MetadataResolver/DeviantArtResolverTest.php index 88bae98..8e3f500 100644 --- a/tests/Unit/MetadataResolver/DeviantArtResolverTest.php +++ b/tests/Unit/MetadataResolver/DeviantArtResolverTest.php @@ -18,48 +18,19 @@ class DeviantArtResolverTest extends TestCase } } - public function testWixmp() - { - $responseText = file_get_contents(__DIR__ . '/../../fixture/DeviantArt/wixmp.json'); - - $this->createResolver(DeviantArtResolver::class, $responseText); - - $metadata = $this->resolver->resolve('https://www.deviantart.com/bonchilo/art/Sally-Nox-743562408'); - $this->assertSame('Sally Nox', $metadata->title); - $this->assertSame('By Bonchilo', $metadata->description); - $this->assertStringStartsWith('https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/f6b84a8f-053e-4ab6-bd6c-71276a4a9282/dcap4fc-6fd6359c-770b-4515-9e29-e99311d58d57.png/v1/fit/w_1024,h_1024,strp/image.jpg?token=', $metadata->image); - if ($this->shouldUseMock()) { - $this->assertSame('https://backend.deviantart.com/oembed?url=https://www.deviantart.com/bonchilo/art/Sally-Nox-743562408', (string) $this->handler->getLastRequest()->getUri()); - } - } - - public function testWixmpNoImageOptions() - { - $responseText = file_get_contents(__DIR__ . '/../../fixture/DeviantArt/wixmpNoImageOptions.json'); - - $this->createResolver(DeviantArtResolver::class, $responseText); - - $metadata = $this->resolver->resolve('https://www.deviantart.com/messenger-lame/art/rem-639676105'); - $this->assertSame('rem', $metadata->title); - $this->assertSame('By messenger-lame', $metadata->description); - $this->assertStringStartsWith('https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/9afa7937-381f-47f0-a8bc-40b9db1faad1/dakuh8p-aea3fc1c-c06e-466b-88ba-d27be8e164e9.png/v1/fit/w_1024,h_1024,strp/image.jpg?token=', $metadata->image); - if ($this->shouldUseMock()) { - $this->assertSame('https://backend.deviantart.com/oembed?url=https://www.deviantart.com/messenger-lame/art/rem-639676105', (string) $this->handler->getLastRequest()->getUri()); - } - } - public function testMature() { $responseText = file_get_contents(__DIR__ . '/../../fixture/DeviantArt/mature.json'); $this->createResolver(DeviantArtResolver::class, $responseText); - $metadata = $this->resolver->resolve('https://www.deviantart.com/rasbii/art/backstage-620617246'); - $this->assertSame('backstage', $metadata->title); - $this->assertSame('By Rasbii', $metadata->description); - $this->assertSame('https://orig00.deviantart.net/eb50/f/2016/191/a/b/preview_by_rasbii-da9hzby.png', $metadata->image); + $metadata = $this->resolver->resolve('https://www.deviantart.com/gatanii69/art/R-15-mabel-and-will-update-686016962'); + $this->assertSame('R-15 mabel and will update', $metadata->title); + $this->assertSame('By gatanii69', $metadata->description); + $this->assertStringStartsWith('https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/6854f36d-8010-4cd0-9d62-0cf9b7829764/dbcfq2q-d78c9f6e-dced-4e5c-a345-2a1bfd5d7620.jpg', $metadata->image); + $this->assertSame(['nsfw', 'reversefalls', 'gravityfalls', 'gravityfallsfanart', 'mabelpines', 'billcipher', 'reversemabel', 'willcipher', 'reversebill', 'reversebillcipher', 'mawill'], $metadata->tags); if ($this->shouldUseMock()) { - $this->assertSame('https://backend.deviantart.com/oembed?url=https://www.deviantart.com/rasbii/art/backstage-620617246', (string) $this->handler->getLastRequest()->getUri()); + $this->assertSame('https://backend.deviantart.com/oembed?url=https://www.deviantart.com/gatanii69/art/R-15-mabel-and-will-update-686016962', (string) $this->handler->getLastRequest()->getUri()); } } } diff --git a/tests/Unit/MetadataResolver/FantiaResolverTest.php b/tests/Unit/MetadataResolver/FantiaResolverTest.php new file mode 100644 index 0000000..ae976e9 --- /dev/null +++ b/tests/Unit/MetadataResolver/FantiaResolverTest.php @@ -0,0 +1,36 @@ +shouldUseMock()) { + sleep(1); + } + } + + public function test() + { + $responseText = file_get_contents(__DIR__ . '/../../fixture/Fantia/test.json'); + + $this->createResolver(FantiaResolver::class, $responseText); + + $metadata = $this->resolver->resolve('https://fantia.jp/posts/206561'); + $this->assertSame('召喚士アルドラ', $metadata->title); + $this->assertSame('サークル: サークルぬるま湯 (ナナナナ)' . PHP_EOL . 'コミッション' . PHP_EOL . 'クイーンズブレイドリベリオンの召喚士アルドラです。', $metadata->description); + $this->assertSame('https://c.fantia.jp/uploads/post/file/206561/main_dbcc59e5-4090-4650-b969-8855a721c6a5.jpg', $metadata->image); + $this->assertSame(['ふたなり', '超乳', '超根', 'クイーンズブレイド', 'ナナナナ'], $metadata->tags); + if ($this->shouldUseMock()) { + $this->assertSame('https://fantia.jp/api/v1/posts/206561', (string) $this->handler->getLastRequest()->getUri()); + } + } +} diff --git a/tests/Unit/MetadataResolver/FanzaResolverTest.php b/tests/Unit/MetadataResolver/FanzaResolverTest.php new file mode 100644 index 0000000..919884f --- /dev/null +++ b/tests/Unit/MetadataResolver/FanzaResolverTest.php @@ -0,0 +1,101 @@ +shouldUseMock()) { + sleep(1); + } + } + + /** + * @dataProvider provider + */ + public function test($filename, $url, $title, $description, $image, $tags) + { + $responseText = file_get_contents(__DIR__ . "/../../fixture/Fanza/{$filename}"); + + $this->createResolver(FanzaResolver::class, $responseText); + + $metadata = $this->resolver->resolve($url); + $this->assertSame($title, $metadata->title); + $this->assertSame($description, $metadata->description); + $this->assertSame($image, $metadata->image); + $this->assertSame($tags, $metadata->tags); + if ($this->shouldUseMock()) { + $this->assertSame($url, (string) $this->handler->getLastRequest()->getUri()); + } + } + + public function provider() + { + return [ + '動画 digital/videoa' => [ + 'digital_videoa.html', + 'https://www.dmm.co.jp/digital/videoa/-/detail/=/cid=ssni00558/', + '巨乳姉妹2人とただひたすらセックスに明け暮れた両親不在の3日間', + '「お姉ちゃんもヤりなよ。すごい気持ちいいよ、セックス」ボクには父親が再婚してできた義理の妹たちがいる。名前はみはるとしおん。ある週末、父と母が外出して家を空けると、僕と妹たちの関係が大きく変わった。姉のみはるの前で妹のしおんと肉体関係を持つとそのままみはるともSEX。そして僕たちは両親がいない3日間、ただただSEXを楽しんだんだ。※ 配信方法によって収録内容が異なる場合があります。', + 'https://pics.dmm.co.jp/digital/video/ssni00558/ssni00558pl.jpg', + ['夕美しおん', '羽咲みはる', '朝霧浄', 'エスワン_ナンバーワンスタイル', 'S1_NO.1_STYLE', 'ハイビジョン', '独占配信', '制服', 'ドラマ', '巨乳', '美少女', 'ギリモザ', '姉・妹'] + ], + '素人動画 digital/videoc' => [ + 'digital_videoc.html', + 'https://www.dmm.co.jp/digital/videoc/-/detail/=/cid=sweet015/', + 'ねる', + '鉄板オナ素材的ハイシコリティ!もうサンプルは見ていただけましたか!?そうなんです!非の打ち所まるで無し!恋するキラッキラの瞳!愛嬌抜群の純真笑顔!Gカップ巨乳にむっちむちの恵体!モザイク越しにも伝わってしまう雑誌グラビア級の美少女ルックス!このスペックなのに自分に自信が持てない系のウブっ子!触れただけで濡れだす敏感ボディ!ねっとりDキスから嬉しそうに大量唾液をゴク飲みする程度には恋愛洗脳済み!溢れ出るガマン汁を丁寧に舐めとるラブいフェラ!ビックビク痙攣しながら困り顔で何度も何度も連続イキ絶頂!※ 配信方法によって収録内容が異なる場合があります。' . PHP_EOL . '特集:' . PHP_EOL . PHP_EOL . 'FANZAオリジナル『素人ホイホイZ/素人ホイホイsweet!』', + 'https://pics.dmm.co.jp/digital/amateur/sweet015/sweet015jp.jpg', + ['素人ホイホイsweet!', '独占配信', '巨乳', '制服', '清楚', '美少女', '女子校生', 'ハイビジョン'] + ], + 'アニメ digital/anime' => [ + 'digital_anime.html', + 'https://www.dmm.co.jp/digital/anime/-/detail/=/cid=h_1379jdxa57513/', + '性活週間 THE ANIMATION 第1巻', + 'めちゃシコ美少女マスター・みちきんぐの初単行本が' . PHP_EOL . '『ヌーディストビーチに修学旅行で?』『リアルエロゲシチュエーション』など' . PHP_EOL . '大ヒットシリーズを手掛けたアダルトアニメ界の新進気鋭クリエイター' . PHP_EOL . '「小原和大」によって待望のOVA化!' . PHP_EOL . '私と姉体験してみない?' . PHP_EOL . '(c)2019 みちきんぐ/GOT/ピンクパイナップル※ 配信方法によって収録内容が異なる場合があります。', + 'https://pics.dmm.co.jp/digital/video/h_1379jdxa57513/h_1379jdxa57513pl.jpg', + ['性活週間_THE_ANIMATION', 'ピンクパイナップル', 'Pink_Pineapple', 'ハイビジョン', '中出し', 'フェラ', '巨乳', '姉・妹'] + ], + '同人' => [ + 'doujin.html', + 'https://www.dmm.co.jp/dc/doujin/-/detail/=/cid=d_115139/', + '美少女拉致って性教育', + 'ハ○エースでおさげ髪美少女を拉致って、凌辱する内容です。' . PHP_EOL . '汚っさん×美少女モノ。' . PHP_EOL . '表紙込み総ページ数28p(内本文27p)' . PHP_EOL . '表紙大きさ1200×1719' . PHP_EOL . '本文大きさ1200×1694', + 'https://doujin-assets.dmm.co.jp/digital/comic/d_115139/d_115139pr.jpg', + ['美少女拉致って性教育', 'オリジナル', '制服', '男性向け', 'ミニ系', '少女', '屋外', '中出し', '成人向け', 'みくろぺえじ'], + ], + '電子書籍' => [ + 'book.html', + 'https://book.dmm.co.jp/detail/b104atint00313/', + '少女×少女×少女', + '少女達が乱舞する…!' . PHP_EOL . '天上家。俺が捨てたあの家…祭子から「母が亡くなった」と電話を受けて、俺は妹達を救うために帰って行くが…。そこで待っていたのは、運命に逆らえず妹達との果てしなき乱交の宴だった…。' . PHP_EOL . '透明感溢れる魅力的なキャラクター、緻密に描きこまれた世界、そしてそのスタイルからは想像できないハードかつ長大なエロ描写!赤月みゅうとのセカンド単行本。', + 'https://ebook-assets.dmm.co.jp/digital/e-book/b104atint00313/b104atint00313pl.jpg', + ['赤月みゅうと', 'MUJIN編集部', '少女×少女×少女', 'MUJIN_COMICS', 'ティーアイネット', 'アダルトコミック単行本', '単行本', '美少女', '中出し', '3P・4P', 'ハーレム'] + ], + 'PCゲーム' => [ + 'dlsoft.html', + 'https://dlsoft.dmm.co.jp/detail/views_0630/', + '姫と穢欲のサクリファイス', + 'ソリデ国――国家間戦争に勝利し発展した大国は、一人の男によって襲撃される。国王に強い恨みを抱き、復讐のために行動を起こした主人公・カルドは使役している‘‘悪魔’’の力を借りて城を掌握。国政や国民には興味を示さず、国王への復讐として悪魔達の能力を使って王女・フィアナへの調教を開始する。', + 'https://pics.dmm.co.jp/digital/pcgame/views_0630/views_0630pl.jpg', + ['B-銀河', '遊丸', '瑠奈璃亜', 'はっとりまさき', '蒼瀬', '木下じゃっく', '御導はるか', '薄迷', '犬童飛沫', '星天誠', '紅ぴえろ', 'エスクード', 'お姫様', '辱め', 'デモ・体験版あり', 'ファンタジー'] + ], + '未対応' => [ + 'nosupport.html', + 'http://www.dmm.co.jp/ppm/video/-/detail/=/cid=h_275tdsu00032/', + '素人のお姉さん!!「チ○ポを洗う」お仕事してみませんか? 2', + 'パーツモデルの募集と思い面接に訪れた素人娘達に、初めての『チ●ポ』を洗うお仕事してもらいました!『エッチとかじゃなくて…洗うだけなら…』自らに言い聞かせる様に出演承諾した彼女...', + 'http://pics.dmm.co.jp/digital/video/h_275tdsu00032/h_275tdsu00032pl.jpg', + [] + ] + ]; + } +} diff --git a/tests/Unit/MetadataResolver/NicoSeigaResolverTest.php b/tests/Unit/MetadataResolver/NicoSeigaResolverTest.php new file mode 100644 index 0000000..a96a35f --- /dev/null +++ b/tests/Unit/MetadataResolver/NicoSeigaResolverTest.php @@ -0,0 +1,53 @@ +shouldUseMock()) { + sleep(1); + } + } + + public function testSeiga() + { + $responseText = file_get_contents(__DIR__ . '/../../fixture/NicoSeiga/seiga.html'); + + $this->createResolver(NicoSeigaResolver::class, $responseText); + + $metadata = $this->resolver->resolve('https://seiga.nicovideo.jp/seiga/im9623750'); + $this->assertSame('シャミ子 / まとけち さんのイラスト', $metadata->title); + $this->assertSame('シャミ子が悪いんだよ・・・', $metadata->description); + $this->assertSame('https://lohas.nicoseiga.jp/thumb/9623750l?', $metadata->image); + $this->assertArrayContains(['アニメ', 'まちカドまぞく', 'シャミ子', 'シャドウミストレス優子', '吉田優子', '危機管理フォーム'], $metadata->tags); + if ($this->shouldUseMock()) { + $this->assertSame('https://seiga.nicovideo.jp/seiga/im9623750', (string) $this->handler->getLastRequest()->getUri()); + } + } + + public function testShunga() + { + $responseText = file_get_contents(__DIR__ . '/../../fixture/NicoSeiga/shunga.html'); + + $this->createResolver(NicoSeigaResolver::class, $responseText); + + $metadata = $this->resolver->resolve('https://seiga.nicovideo.jp/seiga/im9232798'); + $this->assertSame('ベッドのゆかりさん / せゆーら/Se-U-Ra さんのイラスト', $metadata->title); + $this->assertSame('待つ側の方がつよいってスマブラが伝えてきたので', $metadata->description); + $this->assertSame('https://lohas.nicoseiga.jp/thumb/9232798l?', $metadata->image); + $this->assertArrayContains(['結月ゆかり', 'VOICEROID'], $metadata->tags); + if ($this->shouldUseMock()) { + $this->assertSame('https://seiga.nicovideo.jp/seiga/im9232798', (string) $this->handler->getLastRequest()->getUri()); + } + } +} diff --git a/tests/Unit/MetadataResolver/OGPResolverTest.php b/tests/Unit/MetadataResolver/OGPResolverTest.php index d1c6e80..c03a0fe 100644 --- a/tests/Unit/MetadataResolver/OGPResolverTest.php +++ b/tests/Unit/MetadataResolver/OGPResolverTest.php @@ -3,7 +3,7 @@ namespace Tests\Unit\MetadataResolver; use App\MetadataResolver\OGPResolver; -use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Exception\BadResponseException; use Tests\TestCase; class OGPResolverTest extends TestCase @@ -14,7 +14,7 @@ class OGPResolverTest extends TestCase { $this->createResolver(OGPResolver::class, '', [], 404); - $this->expectException(\RuntimeException::class); + $this->expectException(BadResponseException::class); $this->resolver->resolve('http://example.com/404'); } diff --git a/tests/Unit/MetadataResolver/PixivResolverTest.php b/tests/Unit/MetadataResolver/PixivResolverTest.php index 41fc7cb..df5e83a 100644 --- a/tests/Unit/MetadataResolver/PixivResolverTest.php +++ b/tests/Unit/MetadataResolver/PixivResolverTest.php @@ -65,4 +65,20 @@ class PixivResolverTest extends TestCase $this->assertSame('https://www.pixiv.net/ajax/illust/46713544', (string) $this->handler->getLastRequest()->getUri()); } } + + public function testArtworkUrl() + { + $responseText = file_get_contents(__DIR__ . '/../../fixture/Pixiv/illust.json'); + + $this->createResolver(PixivResolver::class, $responseText); + + $metadata = $this->resolver->resolve('https://www.pixiv.net/artworks/68188073'); + $this->assertEquals('coffee break', $metadata->title); + $this->assertEquals('投稿者: 裕' . PHP_EOL, $metadata->description); + $this->assertEquals('https://i.pixiv.cat/img-master/img/2018/04/12/00/01/28/68188073_p0_master1200.jpg', $metadata->image); + $this->assertEquals(['オリジナル', 'カフェ', '眼鏡', 'イヤホン', 'ぱっつん', '艶ぼくろ', '眼鏡っ娘', 'オリジナル5000users入り'], $metadata->tags); + if ($this->shouldUseMock()) { + $this->assertSame('https://www.pixiv.net/ajax/illust/68188073', (string) $this->handler->getLastRequest()->getUri()); + } + } } diff --git a/tests/Unit/MetadataResolver/ToranoanaResolverTest.php b/tests/Unit/MetadataResolver/ToranoanaResolverTest.php index efa23bf..7c208d4 100644 --- a/tests/Unit/MetadataResolver/ToranoanaResolverTest.php +++ b/tests/Unit/MetadataResolver/ToranoanaResolverTest.php @@ -26,7 +26,7 @@ class ToranoanaResolverTest extends TestCase $metadata = $this->resolver->resolve('https://ec.toranoana.shop/tora/ec/item/040030720152'); $this->assertEquals('新・古明地喫茶~そしてまた扉は開く~', $metadata->title); - $this->assertEquals('サークル【ツキギのとこ】(槻木こうすけ)発行の「新・古明地喫茶~そしてまた扉は開く~」を買うなら、とらのあな全年齢向け通信販売!', $metadata->description); + $this->assertEquals('サークル【ツキギのとこ】(槻木こうすけ)発行の「新・古明地喫茶~そしてまた扉は開く~」を買うなら、とらのあな全年齢向け通販!', $metadata->description); $this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image); if ($this->shouldUseMock()) { $this->assertSame('https://ec.toranoana.shop/tora/ec/item/040030720152', (string) $this->handler->getLastRequest()->getUri()); @@ -41,7 +41,7 @@ class ToranoanaResolverTest extends TestCase $metadata = $this->resolver->resolve('https://ec.toranoana.jp/tora_r/ec/item/040030720174'); $this->assertEquals('お姉ちゃんが妹のぱんつでひとりえっちしてました。', $metadata->title); - $this->assertEquals('サークル【没後】(RYO)発行の「お姉ちゃんが妹のぱんつでひとりえっちしてました。」を買うなら、とらのあな成年向け通信販売!', $metadata->description); + $this->assertEquals('サークル【没後】(RYO)発行の「お姉ちゃんが妹のぱんつでひとりえっちしてました。」を買うなら、とらのあな成年向け通販!', $metadata->description); $this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image); if ($this->shouldUseMock()) { $this->assertSame('https://ec.toranoana.jp/tora_r/ec/item/040030720174', (string) $this->handler->getLastRequest()->getUri()); @@ -56,7 +56,7 @@ class ToranoanaResolverTest extends TestCase $metadata = $this->resolver->resolve('https://ec.toranoana.shop/tora_d/digi/item/042000013358'); $this->assertEquals('虎の穴ラボの薄い本。vol 1.5', $metadata->title); - $this->assertEquals('サークル【虎の穴ラボ】(虎の穴ラボエンジニアチーム)発行の「虎の穴ラボの薄い本。vol 1.5」を買うなら、とらのあな全年齢向け電子書籍!', $metadata->description); + $this->assertEquals('サークル【虎の穴ラボ】(虎の穴ラボエンジニアチーム)発行の「虎の穴ラボの薄い本。vol 1.5」を買うなら、とらのあな全年齢向け電子書籍通販!', $metadata->description); $this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image); if ($this->shouldUseMock()) { $this->assertSame('https://ec.toranoana.shop/tora_d/digi/item/042000013358', (string) $this->handler->getLastRequest()->getUri()); @@ -71,7 +71,7 @@ class ToranoanaResolverTest extends TestCase $metadata = $this->resolver->resolve('https://ec.toranoana.jp/tora_rd/digi/item/042000013181'); $this->assertEquals('放課後のお花摘み', $metadata->title); - $this->assertEquals('サークル【給食泥棒】(村雲)発行の「放課後のお花摘み」を買うなら、とらのあな成年向け電子書籍!', $metadata->description); + $this->assertEquals('サークル【給食泥棒】(村雲)発行の「放課後のお花摘み」を買うなら、とらのあな成年向け電子書籍通販!', $metadata->description); $this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image); if ($this->shouldUseMock()) { $this->assertSame('https://ec.toranoana.jp/tora_rd/digi/item/042000013181', (string) $this->handler->getLastRequest()->getUri()); @@ -86,7 +86,7 @@ class ToranoanaResolverTest extends TestCase $metadata = $this->resolver->resolve('https://ec.toranoana.shop/joshi/ec/item/040030702729'); $this->assertEquals('円卓のクソ漫画', $metadata->title); - $this->assertEquals('サークル【地獄のすなぎもカーニバル】(槌田)発行の「円卓のクソ漫画」を買うなら、とらのあなJOSHIBU全年齢向け通信販売!', $metadata->description); + $this->assertEquals('サークル【地獄のすなぎもカーニバル】(槌田)発行の「円卓のクソ漫画」を買うなら、とらのあな女子部全年齢向け通販!', $metadata->description); $this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image); if ($this->shouldUseMock()) { $this->assertSame('https://ec.toranoana.shop/joshi/ec/item/040030702729', (string) $this->handler->getLastRequest()->getUri()); @@ -101,7 +101,7 @@ class ToranoanaResolverTest extends TestCase $metadata = $this->resolver->resolve('https://ec.toranoana.jp/joshi_r/ec/item/040030730126'); $this->assertEquals('リバースナイトリバース', $metadata->title); - $this->assertEquals('サークル【雨傘サイクル】(チャリリズム)発行の「リバースナイトリバース」を買うなら、とらのあなJOSHIBU成年向け通信販売!', $metadata->description); + $this->assertEquals('サークル【雨傘サイクル】(チャリリズム)発行の「リバースナイトリバース」を買うなら、とらのあな女子部成年向け通販!', $metadata->description); $this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image); if ($this->shouldUseMock()) { $this->assertSame('https://ec.toranoana.jp/joshi_r/ec/item/040030730126', (string) $this->handler->getLastRequest()->getUri()); @@ -116,7 +116,7 @@ class ToranoanaResolverTest extends TestCase $metadata = $this->resolver->resolve('https://ec.toranoana.shop/joshi_d/digi/item/042000012192'); $this->assertEquals('超幸運ガール審神者GOLDEN', $metadata->title); - $this->assertEquals('サークル【Day Of The Dead】(ほんちゅ)発行の「超幸運ガール審神者GOLDEN」を買うなら、とらのあなJOSHIBU全年齢向け電子書籍!', $metadata->description); + $this->assertEquals('サークル【Day Of The Dead】(ほんちゅ)発行の「超幸運ガール審神者GOLDEN」を買うなら、とらのあな女子部全年齢向け電子書籍通販!', $metadata->description); $this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image); if ($this->shouldUseMock()) { $this->assertSame('https://ec.toranoana.shop/joshi_d/digi/item/042000012192', (string) $this->handler->getLastRequest()->getUri()); @@ -131,7 +131,7 @@ class ToranoanaResolverTest extends TestCase $metadata = $this->resolver->resolve('https://ec.toranoana.jp/joshi_rd/digi/item/042000013472'); $this->assertEquals('UBWの裏側で非公式に遠坂凛をナデナデする本', $metadata->title); - $this->assertEquals('サークル【阿仁谷組】(阿仁谷ユイジ)発行の「UBWの裏側で非公式に遠坂凛をナデナデする本」を買うなら、とらのあなJOSHIBU成年向け電子書籍!', $metadata->description); + $this->assertEquals('サークル【阿仁谷組】(阿仁谷ユイジ)発行の「UBWの裏側で非公式に遠坂凛をナデナデする本」を買うなら、とらのあな女子部成年向け電子書籍通販!', $metadata->description); $this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image); if ($this->shouldUseMock()) { $this->assertSame('https://ec.toranoana.jp/joshi_rd/digi/item/042000013472', (string) $this->handler->getLastRequest()->getUri()); diff --git a/tests/Unit/MetadataResolver/XtubeResolverTest.php b/tests/Unit/MetadataResolver/XtubeResolverTest.php index 828ec3f..24b55ac 100644 --- a/tests/Unit/MetadataResolver/XtubeResolverTest.php +++ b/tests/Unit/MetadataResolver/XtubeResolverTest.php @@ -20,15 +20,18 @@ class XtubeResolverTest extends TestCase public function test() { - $responseText = file_get_contents(__DIR__ . '/../../fixture/Xtube/test.json'); + $responseText = file_get_contents(__DIR__ . '/../../fixture/Xtube/video.html'); $this->createResolver(XtubeResolver::class, $responseText); $metadata = $this->resolver->resolve('https://www.xtube.com/video-watch/homegrown-big-tits-18634762'); $this->assertEquals('Homegrown Big Tits', $metadata->title); $this->assertEquals('Dedicated to the fans of the beautiful amateur women with big natural tits. All user submitted - you can see big boob amateur hotties fucking and sucking as their tits bounce and sway.', $metadata->description); - $this->assertRegExp('~https://cdn\d-s-hw-e5\.xtube\.com/m=eSK08f/videos/201302/07/RF4Nk-S774-/240X180/1\.jpg~', $metadata->image); - $this->assertEquals(['bigtits', 'homeg'], $metadata->tags); + $this->assertRegExp('~https://cdn\d+-s-hw-e5\.xtube\.com/m=eaAaaEFb/videos/201302/07/RF4Nk-S774-/original/1\.jpg~', $metadata->image); + $this->assertEquals(['Amateur', 'Blowjob', 'Big Boobs', 'bigtits', 'homeg'], $metadata->tags); + if ($this->shouldUseMock()) { + $this->assertSame('https://www.xtube.com/video-watch/homegrown-big-tits-18634762', (string) $this->handler->getLastRequest()->getUri()); + } } public function testNotMatch() @@ -39,13 +42,4 @@ class XtubeResolverTest extends TestCase $this->createResolver(XtubeResolver::class, ''); $this->resolver->resolve('https://www.xtube.com/gallery/black-celebs-free-7686657'); } - - public function testNotOK() - { - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('404: https://www.xtube.com/video-watch/notfound-404'); - - $this->createResolver(XtubeResolver::class, '', [], 404); - $this->resolver->resolve('https://www.xtube.com/video-watch/notfound-404'); - } } diff --git a/tests/fixture/Cien/test.html b/tests/fixture/Cien/test.html new file mode 100644 index 0000000..04f73fa --- /dev/null +++ b/tests/fixture/Cien/test.html @@ -0,0 +1,678 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 進捗とボツ立ち絵 - ねんない5 - Ci-en + + + + + + + + + + + + + + +
+ +
+
+
+ + + + + + + + + + + + +
+ + +
+
+
+
+
+

ねんない5さんをフォローして、最新情報をチェックしよう!

+ マイページへ +

Ci-enはクリエイターに対して、金銭的な支援を送ることができるサービスです。

+
+
+
+ + +
+
+
+ + + +
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+ + + + + +
+ + ねんない5 + + + +
+
+ +
+

進捗とボツ立ち絵

+
+

+
+

今日のサムネイルはストアページに掲載する予定のキャラクター紹介画像です。

+

ドットでない解像度の高いイラストは時間も体力も精神力もかかるので、こういうのを行うタスクを開発終盤に残さないでよかったと本気で思っています。

+




【作業進捗】

+

現在は引き続き、音入れ・敵の調整・やられ演出の設定を行っています。
+バグもよくみつかります。

+

みつかるバグはいいバグです。

+


◆完了したステージ
+・森
+・大樹
+・砦
+・廃村

+

◆まだ
+・下水←着手中
+・聖堂

+

下水は残すところボスのみですが、ボスの動きにやや大きめの調整が必要で、そこで少し時間を使っています。

+

森~廃村ステージまではずっと1日1ステージ単位でやってきましたが、さすがにちょっとペースが早すぎて消耗してきたので、少し速度を落としているというのもあります。

+





◆ボツの立ち絵

+

最近は開発も終盤なので、お見せできる絵的な進捗が少なかったと思います。
+おそらくボツになる立ち絵が出てきましたのでそれを掲載させて頂きます。

+

ネタバレではないと思いますが、一応エロなので下に置きます。

+

お題箱の下記に掲載します。

+ +
+
フォロワー以上限定無料
+
+ + +

開発に関する記事の観覧を行えます。

+
+
無料
+ +
+
+
+
+
+ +
+
\いいね・ツイートで応援!/
+ +
+ + + +
+ + + + + +
+ + + +
+
+ + + + + + diff --git a/tests/fixture/Cien/testWithNoTimestamp.html b/tests/fixture/Cien/testWithNoTimestamp.html new file mode 100644 index 0000000..6d7b804 --- /dev/null +++ b/tests/fixture/Cien/testWithNoTimestamp.html @@ -0,0 +1,602 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 好きの気持ちは、カタチで伝えよう。 - Ci-en + + + + + + + + + + + + + + +
+ +
+
+
+ + + + + + + + + + + + +
+ + +
+
+
+
+
+
+

+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+

+
+

新しいものを作るのは、簡単なことではありません。思いを形にするには時間と手間、そして資金が必要です。

+

Ci-enで好きなクリエイターを支援すれば、その収益を創作活動に活かすことができるようになります。

+

クリエイターも支援者も、誰もが創作を楽しめる世界に参加してみませんか?

+
+ + +
+ + + +
+
+
+ +
+ + +
+
+
+ +
+
+
+
+ +
+
+
ONEONE1
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
同人サークルGyu!
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
クリメニア
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
Hypnotic Yanh
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
シロクマの嫁(伊ヶ崎綾香)
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
鉱油/73号坑道
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
ぽいずん
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
あいすシチュー
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
みこにそみ
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
D-LIS-ディーリス
+
+ +
+
+ +
+
+
+ + + +
+
+ +
+ +
+ +
+
+
+

DLsiteアカウントをお持ちの方はログインできます。

+ +

株式会社エイシスが運営しているサービスをDLsiteアカウント一つでご利用いただけます。

+
+
+
+ +
+
+
+ + + + + + diff --git a/tests/fixture/DLsite/testBL.html b/tests/fixture/DLsite/testBL.html index 68a35e7..4fa78bf 100644 --- a/tests/fixture/DLsite/testBL.html +++ b/tests/fixture/DLsite/testBL.html @@ -1,8 +1,8 @@ - + - + @@ -43,20 +43,20 @@ - + - - - - + + + + - + - + - + @@ -94,7 +94,7 @@ $.extend({ if ($.useAdultcheck) return; $.useAdultcheck = true; var s = document.createElement('script'); - s.src = '/js/adultcheck.js?1553837147'; + s.src = '/js/adultcheck.js?1566881566'; s.defer = true; document.querySelector('head').appendChild(s); } @@ -121,19 +121,22 @@ if ((dlsite.isAdult() && dlsite.getId() !== 'circle' && !(dlsite.isFemale() && d 女性向け - +
    @@ -146,11 +149,10 @@ if ((dlsite.isAdult() && dlsite.getId() !== 'circle' && !(dlsite.isFemale() && d
    - {{ login_id.substr(0, 30) }}{{ login_id.length > 30 ? '...' : '' }}でログイン中 -
    + {{ login_id.substr(0, 30) }}{{ login_id.length > 30 ? '...' : '' }}でログイン中
    • - アカウント管理 + アカウント管理
    • ログアウト @@ -162,12 +164,18 @@ if ((dlsite.isAdult() && dlsite.getId() !== 'circle' && !(dlsite.isFemale() && d
      -

      ログイン中のアカウントが変更されました

      +

      {{ login_id.substr(0, 30) }}{{ login_id.length > 30 ? '...' : '' }}でログインしています

      +

      ログアウトしました

    + +
    + +
    +
@@ -204,10 +212,10 @@ if ((dlsite.isAdult() && dlsite.getId() !== 'circle' && !(dlsite.isFemale() && d @@ -340,7 +373,21 @@ if (loginchecked & 1) {
-
+
    @@ -397,7 +444,7 @@ if (loginchecked & 1) {

    - +
@@ -418,24 +465,23 @@ if (loginchecked & 1) {
-
- - - - - - + + + + + + +
- - - - - - - - -
     
-
- +
+ + + + + - - - -
+ + + + + + + + +
     
+
+
({{ product.rate_count }})
@@ -508,19 +554,29 @@ if (loginchecked & 1) {
-
({{ product.review_count }})
DL : {{ product.dl_count }}
+
+
+
+
+
+
+
+
- +
- +
@@ -574,9 +630,10 @@ if (loginchecked & 1) {
- HTML版で表示 + HTML版で表示 - {{ __('totalImages', {count: items.length}) }} + +
@@ -586,7 +643,7 @@ if (loginchecked & 1) {