Merge pull request #185 from shikorism/feature/metadata_tag

MetadataResolverからタグ情報を保存できるようにする
This commit is contained in:
shibafu 2019-05-03 11:00:01 +09:00 committed by GitHub
commit debc350b12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 2 deletions

View File

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
use App\Metadata;
use App\MetadataResolver\MetadataResolver;
use App\Tag;
use App\Utilities\Formatter;
use Illuminate\Http\Request;
@ -41,6 +42,13 @@ class CardController
'image' => $resolved->image,
'expires_at' => $resolved->expires_at
]);
$tagIds = [];
foreach ($resolved->tags as $tagName) {
$tag = Tag::firstOrCreate(['name' => $tagName]);
$tagIds[] = $tag->id;
}
$metadata->tags()->sync($tagIds);
}
$response = response($metadata);

View File

@ -5,6 +5,7 @@ namespace App\Listeners;
use App\Events\LinkDiscovered;
use App\Metadata;
use App\MetadataResolver\MetadataResolver;
use App\Tag;
use App\Utilities\Formatter;
use GuzzleHttp\Exception\TransferException;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -47,12 +48,19 @@ class LinkCollector
if ($metadata == null || ($metadata->expires_at !== null && $metadata->expires_at < now())) {
try {
$resolved = $this->metadataResolver->resolve($url);
Metadata::updateOrCreate(['url' => $url], [
$metadata = Metadata::updateOrCreate(['url' => $url], [
'title' => $resolved->title,
'description' => $resolved->description,
'image' => $resolved->image,
'expires_at' => $resolved->expires_at
]);
$tagIds = [];
foreach ($resolved->tags as $tagName) {
$tag = Tag::firstOrCreate(['name' => $tagName]);
$tagIds[] = $tag->id;
}
$metadata->tags()->sync($tagIds);
} catch (TransferException $e) {
// 何らかの通信エラーによってメタデータの取得に失敗した時、とりあえずエラーログにURLを残す
Log::error(self::class . ': メタデータの取得に失敗 URL=' . $url);

View File

@ -14,4 +14,9 @@ class Metadata extends Model
protected $visible = ['url', 'title', 'description', 'image', 'expires_at'];
protected $dates = ['created_at', 'updated_at', 'expires_at'];
public function tags()
{
return $this->belongsToMany(Tag::class)->withTimestamps();
}
}

View File

@ -34,6 +34,20 @@ class KomifloResolver implements Resolver
($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']['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");

View File

@ -6,9 +6,21 @@ use Carbon\Carbon;
class Metadata
{
/** @var string タイトル */
public $title = '';
/** @var string 概要 */
public $description = '';
/** @var string サムネイルのURL */
public $image = '';
/** @var Carbon|null */
/** @var Carbon|null メタデータの有効期限 */
public $expires_at = null;
/**
* @var string[] タグ
* チェックインタグと同様に保存されるため、スペースや改行文字を含めてはいけません。
*/
public $tags = [];
}

View File

@ -49,6 +49,43 @@ class PixivResolver implements Resolver
return str_replace('i.pximg.net', 'i.pixiv.cat', $pixivUrl);
}
/**
* HTMLからタグとして利用可能な情報を抽出する
* @param string $html ページ HTML
* @return string[] タグ
*/
public function extractTags(string $html): array
{
$dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$xpath = new \DOMXPath($dom);
$nodes = $xpath->query("//meta[@name='keywords']");
if ($nodes->length === 0) {
return [];
}
$keywords = $nodes->item(0)->getAttribute('content');
$tags = [];
foreach (mb_split(',', $keywords) as $keyword) {
$keyword = trim($keyword);
if (empty($keyword)) {
continue;
}
// 一部の固定キーワードは無視
if (array_search($keyword, ['R-18', 'イラスト', 'pixiv', 'ピクシブ'], true)) {
continue;
}
$tags[] = preg_replace('/\s/', '_', $keyword);
}
return $tags;
}
public function resolve(string $url): Metadata
{
parse_str(parse_url($url, PHP_URL_QUERY), $params);
@ -78,6 +115,8 @@ class PixivResolver implements Resolver
$metadata->image = $this->proxize($illustUrl);
$metadata->tags = $this->extractTags($res->getBody());
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMetadataTagTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('metadata_tag', function (Blueprint $table) {
$table->increments('id');
$table->text('metadata_url')->index();
$table->integer('tag_id')->index();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('metadata_tag');
}
}