diff --git a/backend/app/models/tag.rb b/backend/app/models/tag.rb index e772e9c..c4b36e6 100644 --- a/backend/app/models/tag.rb +++ b/backend/app/models/tag.rb @@ -67,9 +67,17 @@ class Tag < ApplicationRecord @bot ||= find_or_create_by_tag_name!('bot操作', category: :meta) end + def self.video + @video ||= find_or_create_by_tag_name!('動画', category: :meta) + end + + def self.niconico + @niconico ||= find_or_create_by_tag_name!('ニコニコ', category: :meta) + end + def self.normalise_tags tag_names, with_tagme: true, deny_nico: true if deny_nico && tag_names.any? { |n| n.start_with?('nico:') } - raise NicoTagNormalisationError + raise NicoTagNormalisationError end tags = tag_names.map do |name| diff --git a/backend/lib/tasks/sync_nico.rake b/backend/lib/tasks/sync_nico.rake index 86bd941..b3596cb 100644 --- a/backend/lib/tasks/sync_nico.rake +++ b/backend/lib/tasks/sync_nico.rake @@ -1,10 +1,12 @@ namespace :nico do desc 'ニコニコ DB 同期' task sync: :environment do + require 'json' require 'open3' require 'open-uri' require 'nokogiri' require 'set' + require 'time' fetch_thumbnail = -> url do html = URI.open(url, read_timeout: 60, 'User-Agent' => 'Mozilla/5.0').read @@ -45,13 +47,42 @@ namespace :nico do data = JSON.parse(stdout) data.each do |datum| code = datum['code'] - post = Post.where('url REGEXP ?', "nicovideo\\.jp/watch/#{ Regexp.escape(code) }([^0-9]|$)") - .first - unless post - title = datum['title'] + + post = + Post + .where('url REGEXP ?', "nicovideo\\.jp/watch/#{ Regexp.escape(code) }([^0-9]|$)") + .first + + title = datum['title'] + original_created_at = datum['uploaded_at'] && + Time.strptime(datum['uploaded_at'], '%Y-%m-%d %H:%M:%S') + original_created_from = original_created_at&.change(sec: 0) + original_created_before = original_created_from&.+(1.minute) + + if post + attrs = { title:, original_created_from:, original_created_before: } + + unless post.thumbnail.attached? + thumbnail_base = fetch_thumbnail.(post.url) rescue nil + if thumbnail_base.present? + post.thumbnail.attach( + io: URI.open(thumbnail_base), + filename: File.basename(URI.parse(thumbnail_base).path), + content_type: 'image/jpeg') + attrs[:thumbnail_base] = thumbnail_base + end + end + + post.assign_attributes(attrs) + if post.changed? + post.save! + post.resized_thumbnail! if post.thumbnail.attached? + end + else url = "https://www.nicovideo.jp/watch/#{ code }" thumbnail_base = fetch_thumbnail.(url) rescue nil - post = Post.new(title:, url:, thumbnail_base:, uploaded_user: nil) + post = Post.new(title:, url:, thumbnail_base:, uploaded_user: nil, + original_created_from:, original_created_before:) if thumbnail_base.present? post.thumbnail.attach( io: URI.open(thumbnail_base), @@ -60,7 +91,7 @@ namespace :nico do end post.save! post.resized_thumbnail! - sync_post_tags!(post, [Tag.tagme.id]) + sync_post_tags!(post, [Tag.tagme.id, Tag.bot.id, Tag.niconico.id, Tag.video.id]) end kept_ids = PostTag.kept.where(post_id: post.id).pluck(:tag_id).to_set diff --git a/backend/spec/tasks/nico_sync_spec.rb b/backend/spec/tasks/nico_sync_spec.rb index e4cef17..daa243e 100644 --- a/backend/spec/tasks/nico_sync_spec.rb +++ b/backend/spec/tasks/nico_sync_spec.rb @@ -13,7 +13,6 @@ RSpec.describe "nico:sync" do end def link_nico_to_tag!(nico_tag, tag) - # NicoTagRelation がある前提(君の model からそう見える) NicoTagRelation.create!(nico_tag_id: nico_tag.id, tag_id: tag.id) end @@ -35,7 +34,12 @@ RSpec.describe "nico:sync" do Tag.tagme # pythonの出力(AAA が付く) - stub_python([{ "code" => "sm9", "title" => "t", "tags" => ["AAA"] }]) + stub_python([{ + 'code' => 'sm9', + 'title' => 't', + 'tags' => ['AAA'], + 'uploaded_at' => '2026-01-01 12:34:56', + 'deleted_at' => '2026-01-31 00:00:00' }]) # 外部HTTPは今回「既存 post なので呼ばれない」はずだが、念のため塞ぐ allow(URI).to receive(:open).and_return(StringIO.new("")) @@ -49,6 +53,9 @@ RSpec.describe "nico:sync" do expect(active_tag_names).to include("nico:AAA") expect(active_tag_names).to include("spec_linked") + expect(post.original_created_from).to eq(Time.iso8601('2026-01-01T03:34:00Z')) + expect(post.original_created_before).to eq(Time.iso8601('2026-01-01T03:35:00Z')) + # 差分が出るので bot が付く(kept_non_nico_ids != desired_non_nico_ids) expect(active_tag_names).to include("bot操作") end