201 行
6.5 KiB
Ruby
201 行
6.5 KiB
Ruby
class PostBulkCreator
|
|
def initialize actor:, posts:, thumbnails:
|
|
@actor_id = actor.id
|
|
@posts = posts
|
|
@thumbnails = thumbnails
|
|
end
|
|
|
|
def run
|
|
results = Array.new(@posts.length)
|
|
mutex = Mutex.new
|
|
next_index = 0
|
|
|
|
workers = Array.new(2) do
|
|
Thread.new do
|
|
Rails.application.executor.wrap do
|
|
ActiveRecord::Base.connection_pool.with_connection do
|
|
actor = User.find(@actor_id)
|
|
loop do
|
|
index = nil
|
|
begin
|
|
index = mutex.synchronize do
|
|
current = next_index
|
|
next_index += 1
|
|
current
|
|
end
|
|
break if index >= @posts.length
|
|
|
|
attributes = @posts[index]
|
|
results[index] = create_row(actor, attributes, index)
|
|
rescue StandardError => e
|
|
Rails.logger.error(
|
|
"post_bulk_creator_worker_failure #{ { error: e.class.name,
|
|
message: e.message,
|
|
index: }.to_json }")
|
|
results[index] = {
|
|
status: 'failed',
|
|
recoverable: false,
|
|
errors: { base: ['登録中にエラーが発生しました.'] },
|
|
base_errors: [] }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
workers.each(&:join)
|
|
|
|
results.each_index do |index|
|
|
next if results[index].present?
|
|
|
|
results[index] = {
|
|
status: 'failed',
|
|
recoverable: false,
|
|
errors: { base: ['登録中にエラーが発生しました.'] },
|
|
base_errors: [] }
|
|
end
|
|
|
|
{ results: }
|
|
end
|
|
|
|
private
|
|
|
|
def create_row actor, attributes, index
|
|
preflight =
|
|
PostCreatePreflight.new(
|
|
attributes: attributes,
|
|
thumbnail: thumbnail_for(index, attributes)).run
|
|
if preflight[:existing_post_id].present?
|
|
return {
|
|
status: 'skipped',
|
|
existing_post_id: preflight[:existing_post_id],
|
|
existing_post: preflight[:existing_post] }
|
|
end
|
|
|
|
post = PostCreator.new(
|
|
actor: actor,
|
|
attributes: normalised_attributes(attributes, preflight, index)).create!
|
|
result = {
|
|
status: 'created',
|
|
post: { id: post.id } }
|
|
result[:field_warnings] = preflight[:field_warnings] if preflight[:field_warnings].present?
|
|
result[:base_warnings] = preflight[:base_warnings] if preflight[:base_warnings].present?
|
|
result
|
|
rescue PostCreatePreflight::ValidationFailed => e
|
|
{
|
|
status: 'failed',
|
|
recoverable: true,
|
|
errors: e.fields,
|
|
base_errors: e.base_errors }
|
|
rescue ActiveRecord::RecordInvalid => e
|
|
existing_post = existing_post_for_race(attributes, e.record)
|
|
if existing_post.present?
|
|
return {
|
|
status: 'skipped',
|
|
existing_post_id: existing_post[:id],
|
|
existing_post: existing_post }
|
|
end
|
|
|
|
{
|
|
status: 'failed',
|
|
recoverable: true,
|
|
errors: e.record.errors.to_hash,
|
|
base_errors: e.record.errors[:base] }
|
|
rescue ActiveRecord::RecordNotUnique => e
|
|
if e.message.include?('index_posts_on_url')
|
|
existing_post = existing_post_for_race(attributes)
|
|
return {
|
|
status: 'skipped',
|
|
existing_post_id: existing_post[:id],
|
|
existing_post: existing_post } if existing_post.present?
|
|
end
|
|
|
|
Rails.logger.error(
|
|
"post_bulk_creator_record_not_unique #{ { error: e.class.name,
|
|
message: e.message }.to_json }")
|
|
{
|
|
status: 'failed',
|
|
recoverable: false,
|
|
errors: { base: ['登録中にエラーが発生しました.'] },
|
|
base_errors: [] }
|
|
rescue Tag::NicoTagNormalisationError
|
|
{
|
|
status: 'failed',
|
|
recoverable: true,
|
|
errors: { tags: ['ニコニコ・タグは直接指定できません.'] },
|
|
base_errors: [] }
|
|
rescue Tag::DeprecatedTagNormalisationError
|
|
{
|
|
status: 'failed',
|
|
recoverable: true,
|
|
errors: { tags: ['廃止済みタグは付与できません.'] },
|
|
base_errors: [] }
|
|
rescue PostCreator::VideoMsParseError
|
|
{
|
|
status: 'failed',
|
|
recoverable: true,
|
|
errors: { video_ms: ['動画時間の記法が不正です.'] },
|
|
base_errors: [] }
|
|
rescue Post::RemoteThumbnailFetchFailed
|
|
{
|
|
status: 'failed',
|
|
recoverable: true,
|
|
errors: { thumbnail_base: ['サムネイル画像の取得に失敗しました.'] },
|
|
base_errors: [] }
|
|
rescue ArgumentError => e
|
|
{
|
|
status: 'failed',
|
|
recoverable: true,
|
|
errors: { base: [e.message] },
|
|
base_errors: [] }
|
|
rescue StandardError => e
|
|
Rails.logger.error(
|
|
"post_bulk_creator_failure #{ { error: e.class.name,
|
|
message: e.message }.to_json }")
|
|
{
|
|
status: 'failed',
|
|
recoverable: false,
|
|
errors: { base: ['登録中にエラーが発生しました.'] },
|
|
base_errors: [] }
|
|
end
|
|
|
|
def normalised_attributes attributes, preflight, index
|
|
{
|
|
url: preflight[:url],
|
|
title: preflight[:title],
|
|
thumbnail_base: preflight[:thumbnail_base],
|
|
thumbnail: thumbnail_for(index, attributes),
|
|
tags: preflight[:tags],
|
|
parent_post_ids: preflight[:parent_post_ids],
|
|
original_created_from: preflight[:original_created_from],
|
|
original_created_before: preflight[:original_created_before],
|
|
duration: preflight[:duration],
|
|
video_ms: preflight[:video_ms],
|
|
direct_tag_specs: preflight[:direct_tag_specs],
|
|
default_tag_specs: preflight[:default_tag_specs],
|
|
snapshot_tag_specs: preflight[:snapshot_tag_specs],
|
|
post_tag_specs: preflight[:post_tag_specs],
|
|
tag_sections: preflight[:tag_sections],
|
|
normalised_parent_post_ids: preflight[:normalised_parent_post_ids] }
|
|
end
|
|
|
|
def thumbnail_for index, attributes
|
|
return nil if attributes['thumbnail_base'].present? || attributes[:thumbnail_base].present?
|
|
|
|
@thumbnails[index]
|
|
end
|
|
|
|
def existing_post_for_race attributes, record = nil
|
|
return nil if record.present? && !(record.errors.of_kind?(:url, :taken))
|
|
|
|
normal_url = PostUrlNormaliser.normalise(attributes['url'] || attributes[:url])
|
|
return nil if normal_url.blank?
|
|
|
|
compact_existing_post(Post.with_attached_thumbnail.find_by(url: normal_url))
|
|
end
|
|
|
|
def compact_existing_post post
|
|
PostCompactRepr.base(post)
|
|
end
|
|
end
|