diff --git a/app/MetadataResolver/IwaraResolver.php b/app/MetadataResolver/IwaraResolver.php index 361501d..f19efc9 100644 --- a/app/MetadataResolver/IwaraResolver.php +++ b/app/MetadataResolver/IwaraResolver.php @@ -3,6 +3,7 @@ namespace App\MetadataResolver; use GuzzleHttp\Client; +use Symfony\Component\DomCrawler\Crawler; class IwaraResolver implements Resolver { @@ -19,46 +20,37 @@ class IwaraResolver implements Resolver public function resolve(string $url): Metadata { $res = $this->client->get($url); - if ($res->getStatusCode() === 200) { - $dom = new \DOMDocument(); - @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8')); - $xpath = new \DOMXPath($dom); - $metadata = new Metadata(); + $html = (string) $res->getBody(); + $crawler = new Crawler($html); - // find title - foreach ($xpath->query('//title') as $node) { - $content = $node->textContent; - if (!empty($content)) { - $metadata->title = $content; - break; + $descriptionElement = $crawler->filter('#video-player + div, .field-name-field-video-url + div, .field-name-field-images + div'); + $title = $descriptionElement->filter('h1.title')->text(); + $author = $descriptionElement->filter('.username')->text(); + $description = $descriptionElement->children('div')->eq(1)->text(); + $tags = $descriptionElement->filter('a[href^="/video-categories"], a[href^="/images"]')->extract('_text'); + + $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'; } } - // find thumbnail - foreach ($xpath->query('//*[@id="video-player"]') as $node) { - $poster = $node->getAttribute('poster'); - if (!empty($poster)) { - if (strpos($poster, '//') === 0) { - $poster = 'https:' . $poster; - } - $metadata->image = $poster; - break; - } - } - if (empty($metadata->image)) { - // YouTube embedded? - foreach ($xpath->query('//div[@class="embedded-video"]//iframe') as $node) { - $src = $node->getAttribute('src'); - if (preg_match('~youtube\.com/embed/(\S+)\?~', $src, $matches) !== -1) { - $youtubeId = $matches[1]; - $iwaraThumbUrl = 'https://i.iwara.tv/sites/default/files/styles/thumbnail/public/video_embed_field_thumbnails/youtube/' . $youtubeId . '.jpg'; - - $metadata->image = $iwaraThumbUrl; - break; - } - } + // 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/MetadataResolver.php b/app/MetadataResolver/MetadataResolver.php index e53f7d6..363b6f3 100644 --- a/app/MetadataResolver/MetadataResolver.php +++ b/app/MetadataResolver/MetadataResolver.php @@ -14,7 +14,7 @@ class MetadataResolver implements Resolver '~komiflo\.com(/#!)?/comics/(\\d+)~' => KomifloResolver::class, '~www\.melonbooks\.co\.jp/detail/detail\.php~' => MelonbooksResolver::class, '~ec\.toranoana\.(jp|shop)/(tora|joshi)(_[rd]+)?/(ec|digi)/item/~' => ToranoanaResolver::class, - '~iwara\.tv/videos/.*~' => IwaraResolver::class, + '~iwara\.tv/(videos|images)/.*~' => IwaraResolver::class, '~www\.dlsite\.com/.*/(work|announce)/=/product_id/..\d+(\.html)?~' => DLsiteResolver::class, '~dlsite\.jp/...tw/..\d+~' => DLsiteResolver::class, '~www\.pixiv\.net/member_illust\.php\?illust_id=\d+~' => PixivResolver::class, diff --git a/tests/Unit/MetadataResolver/IwaraResolverTest.php b/tests/Unit/MetadataResolver/IwaraResolverTest.php new file mode 100644 index 0000000..d8865b2 --- /dev/null +++ b/tests/Unit/MetadataResolver/IwaraResolverTest.php @@ -0,0 +1,71 @@ +shouldUseMock()) { + sleep(1); + } + } + + public function testVideo() + { + $responseText = file_get_contents(__DIR__ . '/../../fixture/Iwara/video.html'); + + $this->createResolver(IwaraResolver::class, $responseText); + + $url = 'https://ecchi.iwara.tv/videos/wqlwatgmvhqg40kg'; + $metadata = $this->resolver->resolve($url); + $this->assertEquals('Cakeface【鈴谷、プリンツ】', $metadata->title); + $this->assertEquals('投稿者: kuro@vov' . PHP_EOL . 'Thank you for watching!いつもありがとうございます' . PHP_EOL . 'こっそり微修正…' . PHP_EOL . 'Model:鈴谷&プリンツ つみだんご様 罪袋:BCD様' . PHP_EOL . '(いずれも改変)クレジット漏れゴメンナサイ。。。' . PHP_EOL, $metadata->description); + $this->assertEquals(['Uncategorized', 'KanColle'], $metadata->tags); + $this->assertEquals('https://i.iwara.tv/sites/default/files/videos/thumbnails/238591/thumbnail-238591_0004.jpg', $metadata->image); + if ($this->shouldUseMock()) { + $this->assertSame($url, (string) $this->handler->getLastRequest()->getUri()); + } + } + + public function testYouTube() + { + $responseText = file_get_contents(__DIR__ . '/../../fixture/Iwara/youtube.html'); + + $this->createResolver(IwaraResolver::class, $responseText); + + $url = 'https://iwara.tv/videos/z4dn6fag4iko08o0'; + $metadata = $this->resolver->resolve($url); + $this->assertEquals('むちむち天龍ちゃんで君色に染まる', $metadata->title); + $this->assertEquals('投稿者: kochira' . PHP_EOL . 'Ray-cast test. Still trying to figure out how Ray-cast works so I\'m sorry if anything looks off.' . PHP_EOL . 'Unauthorized reproduction prohibited (無断転載は禁止です/未經授權禁止複製)' . PHP_EOL, $metadata->description); + $this->assertEquals(['Uncategorized', 'KanColle'], $metadata->tags); + $this->assertEquals('https://img.youtube.com/vi/pvA5Db082yo/maxresdefault.jpg', $metadata->image); + if ($this->shouldUseMock()) { + $this->assertSame($url, (string) $this->handler->getLastRequest()->getUri()); + } + } + + public function testImages() + { + $responseText = file_get_contents(__DIR__ . '/../../fixture/Iwara/images.html'); + + $this->createResolver(IwaraResolver::class, $responseText); + + $url = 'https://iwara.tv/images/%E9%8F%A1%E9%9F%B3%E3%82%8A%E3%82%9318%E6%AD%B3'; + $metadata = $this->resolver->resolve($url); + $this->assertEquals('鏡音りん18歳', $metadata->title); + $this->assertEquals('投稿者: Tonjiru Lion' . PHP_EOL . '今回はあんまエロくないです。' . PHP_EOL, $metadata->description); + $this->assertEquals(['Vocaloid'], $metadata->tags); + $this->assertEquals('https://i.iwara.tv/sites/default/files/photos/jing_yin_rin18sui_a.png', $metadata->image); + if ($this->shouldUseMock()) { + $this->assertSame($url, (string) $this->handler->getLastRequest()->getUri()); + } + } +} diff --git a/tests/fixture/Iwara/images.html b/tests/fixture/Iwara/images.html new file mode 100644 index 0000000..eb48004 --- /dev/null +++ b/tests/fixture/Iwara/images.html @@ -0,0 +1,644 @@ + + +
+今回はあんまエロくないです。
+Thank you for watching!いつもありがとうございます
+こっそり微修正…
+Model:鈴谷&プリンツ つみだんご様 罪袋:BCD様
+(いずれも改変)クレジット漏れゴメンナサイ。。。
哇 又有 新的MMD了 先留言 感谢 UP
+我是挂的SS 用IDM下载的。。 首先你的网络要支持 然后要用下载软件。
+Holy shit, your videos are seriously so damn good.
+ちょっとこのクオリティはやばくないですかねぇ・・・・・・
+あっ、そこの人ティッシュ取って
ついから急いで来ました。
+す、素晴しい!!
+ぶらーーーぼぉーーー!
+ぶーーーらぁーーーぼぉーーー!
This is High End entertainment
+this is the best cakeface mmd i've ever seen
+素晴らしすぎます
+相変わらず、素晴らしいの一言です、今夜は色々忙しくなりそうです
+Amazing artwork!Such a masterpiece !
+この二人に搾り取られたいw
+MMDにおけるCakefaceをさらにエロく進化させる方が現れるとは…。
+ダンケ百唱モノです!
作品真的很用心,也非常好用,期待您更多的佳作。
+Ray-cast test. Still trying to figure out how Ray-cast works so I'm sorry if anything looks off.
+Unauthorized reproduction prohibited (無断転載は禁止です/未經授權禁止複製)
+Wow, the Ray-cast looks great! I hope you do all of your videos like that. I have to add though I would prefer a Tenryuu with a skinnier waist and a smaller butt though. But beggars can't be choosers. T_T
+Edit: And with her tits out!
+良い表情してるね
+すごく楽しそう
Yay welcome back Kochira nice video I love it lol.
+oh god!!! Uncensored version please!!! <3 this is so good GJ!!!
+I hope to see a Uncensored version.
+
+ コメント数 6
+ + +良いカスタムモデルですね
++ぶっちゃけ裸よりエロいです(*´ω`)
可愛いと思います!
+これくらいの微エロも捗りますね
+これはけしからん18歳
+That is Nice!
+Lovely! I wanna make her videos!! plz
+