113 行
3.8 KiB
Ruby
113 行
3.8 KiB
Ruby
class PostCreator
|
|
class VideoMsParseError < ArgumentError; end
|
|
|
|
def initialize actor:, attributes:
|
|
@actor = actor
|
|
@attributes = attributes.symbolize_keys
|
|
end
|
|
|
|
def create!
|
|
post = Post.new(title: @attributes[:title].presence,
|
|
url: @attributes[:url],
|
|
thumbnail_base: @attributes[:thumbnail_base].presence,
|
|
uploaded_user: @actor,
|
|
original_created_from: @attributes[:original_created_from].presence,
|
|
original_created_before: @attributes[:original_created_before].presence)
|
|
thumbnail = @attributes[:thumbnail]
|
|
post.thumbnail.attach(Post.resized_thumbnail_attachment(thumbnail)) if thumbnail.present?
|
|
|
|
ApplicationRecord.transaction do
|
|
post.save!
|
|
Tag.normalise_tags!(tag_names, deny_deprecated: true, with_sections: true) =>
|
|
{ tags:, sections: }
|
|
TagVersioning.record_tag_snapshots!(tags, created_by_user: @actor)
|
|
tags = Tag.expand_parent_tags(tags).reject(&:deprecated?)
|
|
post.video_ms = normalise_video_ms(tags)
|
|
validate_video_sections!(post.video_ms, sections)
|
|
post.save!
|
|
sync_post_tags!(post, tags, sections)
|
|
sync_parent_posts!(post, parent_post_ids)
|
|
PostVersionRecorder.record!(post:, event_type: :create, created_by_user: @actor)
|
|
end
|
|
post
|
|
end
|
|
|
|
private
|
|
|
|
def tag_names = @attributes[:tags].to_s.split
|
|
|
|
def parent_post_ids
|
|
Array(@attributes[:parent_post_ids]).flat_map { _1.to_s.split }.map { |token|
|
|
id = Integer(token, exception: false)
|
|
raise ArgumentError, "親投稿 Id. が不正です: #{ token }" if id.nil? || id <= 0
|
|
|
|
id
|
|
}.uniq
|
|
end
|
|
|
|
def normalise_video_ms tags
|
|
return nil unless tags.any? { _1.id == Tag.video.id }
|
|
|
|
video_ms = @attributes[:video_ms]
|
|
if video_ms.present?
|
|
value = Integer(video_ms, exception: false)
|
|
raise VideoMsParseError unless value&.positive?
|
|
|
|
return value
|
|
end
|
|
duration = @attributes[:duration]
|
|
return nil if duration.blank?
|
|
|
|
return duration.to_i if duration.is_a?(Numeric) && duration.to_i.positive?
|
|
|
|
value = Tag.time_to_ms!(duration.to_s, tag_name: '動画時間')
|
|
raise VideoMsParseError unless value.positive?
|
|
|
|
value
|
|
rescue Tag::SectionLiteralParseError
|
|
raise VideoMsParseError
|
|
end
|
|
|
|
def validate_video_sections! video_ms, sections
|
|
return unless video_ms
|
|
|
|
sections.each_value do |ranges|
|
|
ranges.each do |begin_ms, end_ms|
|
|
next if begin_ms < video_ms && (!end_ms || end_ms <= video_ms)
|
|
|
|
post = Post.new
|
|
post.errors.add :video_ms, 'タグ区間が動画時間の範囲外です.'
|
|
raise ActiveRecord::RecordInvalid, post
|
|
end
|
|
end
|
|
end
|
|
|
|
def sync_post_tags! post, desired_tags, sections
|
|
desired_ids = desired_tags.map(&:id).to_set
|
|
current_ids = post.tags.pluck(:id).to_set
|
|
Tag.where(id: desired_ids - current_ids).find_each do |tag|
|
|
PostTag.create_or_find_by!(post:, tag:, created_user: @actor)
|
|
end
|
|
PostTagSection.where(post_id: post.id).destroy_all
|
|
sections.each do |tag_id, ranges|
|
|
ranges.each { |begin_ms, end_ms| PostTagSection.create!(post_id: post.id, tag_id:, begin_ms:, end_ms:) }
|
|
end
|
|
PostTag.where(post_id: post.id, tag_id: (current_ids - desired_ids).to_a).kept.find_each do |post_tag|
|
|
post_tag.discard_by!(@actor)
|
|
end
|
|
end
|
|
|
|
def sync_parent_posts! post, ids
|
|
if ids.include?(post.id)
|
|
post.errors.add :parent_post_ids, '自分自身を親投稿にはできません.'
|
|
raise ActiveRecord::RecordInvalid, post
|
|
end
|
|
missing = ids - Post.where(id: ids).pluck(:id)
|
|
if missing.present?
|
|
post.errors.add :parent_post_ids, "存在しない親投稿 Id. があります: #{ missing.join(' ') }"
|
|
raise ActiveRecord::RecordInvalid, post
|
|
end
|
|
ids.each { |parent_post_id| PostImplication.create_or_find_by!(post:, parent_post_id:) }
|
|
end
|
|
end
|