このコミットが含まれているのは:
2026-07-16 18:16:20 +09:00
コミット f76fbe6711
14個のファイルの変更445行の追加43行の削除
+35 -2
ファイルの表示
@@ -2,6 +2,8 @@ class Post < ApplicationRecord
require 'mini_magick'
require 'stringio'
class RemoteThumbnailFetchFailed < StandardError; end
ORIGINAL_CREATED_INVALID_MESSAGE = 'オリジナルの作成日時の形式が不正です.'.freeze
ORIGINAL_CREATED_MINUTE_PRECISION_MESSAGE =
'オリジナルの作成日時は分単位で入力してください.'.freeze
@@ -12,7 +14,9 @@ class Post < ApplicationRecord
def self.resized_thumbnail_attachment(upload)
upload.rewind
image = MiniMagick::Image.read(upload.read)
image.resize '180x180'
image.resize '180x180^'
image.gravity 'Center'
image.extent '180x180'
image.format 'jpg'
{ io: StringIO.new(image.to_blob),
@@ -125,7 +129,11 @@ class Post < ApplicationRecord
'%d:%02d' % [min, s]
end
remainder_ms.positive? ? "#{ base }.#{ remainder_ms.to_s.rjust(3, '0') }" : base
if remainder_ms.positive?
"#{ base }.#{ remainder_ms.to_s.rjust(3, '0') }"
else
base
end
end
def snapshot_parent_post_ids = parents.order(:id).pluck(:id)
@@ -147,8 +155,33 @@ class Post < ApplicationRecord
thumbnail.attach(self.class.resized_thumbnail_attachment(StringIO.new(thumbnail.download)))
end
def attach_thumbnail_from_url! raw_url
response = Preview::ThumbnailFetcher.fetch_image_response(raw_url)
thumbnail.attach(
io: StringIO.new(response.body),
filename: remote_thumbnail_filename(response.url),
content_type: response.content_type)
resized_thumbnail!
rescue Preview::UrlSafety::UnsafeUrl,
Preview::ThumbnailFetcher::GenerationFailed,
Preview::HttpFetcher::FetchFailed,
Preview::HttpFetcher::FetchTimeout,
Preview::HttpFetcher::ResponseTooLarge,
MiniMagick::Error => e
raise RemoteThumbnailFetchFailed, e.message
end
private
def remote_thumbnail_filename url
filename = File.basename(URI.parse(url).path.to_s)
return filename if filename.present? && filename != '/'
'thumbnail'
rescue URI::InvalidURIError
'thumbnail'
end
def validate_original_created_range
f = parse_original_created_value(:original_created_from)
b = parse_original_created_value(:original_created_before)
+19 -2
ファイルの表示
@@ -1,9 +1,12 @@
class PostCreator
class VideoMsParseError < ArgumentError; end
attr_reader :field_warnings
def initialize actor:, attributes:
@actor = actor
@attributes = attributes.symbolize_keys
@field_warnings = { }
end
def create!
@@ -13,8 +16,7 @@ class PostCreator
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?
attach_thumbnail!(post)
ApplicationRecord.transaction do
post.save!
@@ -34,6 +36,21 @@ class PostCreator
private
def attach_thumbnail! post
thumbnail = @attributes[:thumbnail]
if thumbnail.present?
post.thumbnail.attach(Post.resized_thumbnail_attachment(thumbnail))
return
end
thumbnail_base = post.thumbnail_base
return if thumbnail_base.blank?
post.attach_thumbnail_from_url!(thumbnail_base)
rescue Post::RemoteThumbnailFetchFailed => e
@field_warnings[:thumbnail_base] = [e.message]
end
def tag_names = @attributes[:tags].to_s.split
def parent_post_ids
+7 -2
ファイルの表示
@@ -36,8 +36,13 @@ class PostImportRunner
attributes['tags'] = preview[:attributes]['tags']
attributes['url'] = row['url']
post = PostCreator.new(actor: @actor, attributes:).create!
{ source_row: row['source_row'], status: 'created', post: PostRepr.base(post) }
creator = PostCreator.new(actor: @actor, attributes:)
post = creator.create!
result = { source_row: row['source_row'], status: 'created', post: PostRepr.base(post) }
if creator.field_warnings.present?
result[:field_warnings] = creator.field_warnings
end
result
rescue ActiveRecord::RecordInvalid => e
existing_post = existing_post_for_race(row, e.record)
if existing_post
+29 -20
ファイルの表示
@@ -25,6 +25,22 @@ module Preview
fetch_image!(metadata[:image_url])
end
def self.fetch_image_response(raw_url)
uri, = UrlSafety.validate(raw_url)
response = HttpFetcher.fetch(uri.to_s)
unless allowed_image_content_type?(response.content_type)
raise GenerationFailed, 'サムネール画像が見つかりませんでした.'
end
response
rescue HttpFetcher::FetchTimeout
raise
rescue HttpFetcher::ResponseTooLarge
raise
rescue HttpFetcher::FetchFailed
raise GenerationFailed, 'サムネール画像を取得できませんでした.'
end
def self.title(raw_url)
uri, = UrlSafety.validate(raw_url)
HtmlMetadataExtractor.extract(
@@ -45,18 +61,7 @@ module Preview
end
def self.fetch_image!(url)
response = HttpFetcher.fetch(url)
unless allowed_image_content_type?(response.content_type)
raise GenerationFailed, 'サムネール画像が見つかりませんでした.'
end
response.body
rescue HttpFetcher::FetchTimeout
raise
rescue HttpFetcher::ResponseTooLarge
raise
rescue HttpFetcher::FetchFailed
raise GenerationFailed, 'サムネール画像を取得できませんでした.'
fetch_image_response(url).body
end
def self.niconico_thumbnail_url(uri)
@@ -70,16 +75,20 @@ module Preview
xml.at_xpath('//thumbnail_url')&.text&.strip.presence
rescue HttpFetcher::FetchFailed, HttpFetcher::FetchTimeout => e
Rails.logger.info("preview_niconico_getthumbinfo_fallback #{ { url: uri.to_s,
video_id:,
error: e.class.name,
message: e.message }.to_json }")
payload = {
url: uri.to_s,
video_id:,
error: e.class.name,
message: e.message }
Rails.logger.info("preview_niconico_getthumbinfo_fallback #{ payload.to_json }")
nil
rescue Nokogiri::XML::SyntaxError => e
Rails.logger.info("preview_niconico_getthumbinfo_fallback #{ { url: uri.to_s,
video_id:,
error: e.class.name,
message: e.message }.to_json }")
payload = {
url: uri.to_s,
video_id:,
error: e.class.name,
message: e.message }
Rails.logger.info("preview_niconico_getthumbinfo_fallback #{ payload.to_json }")
nil
end
+6 -7
ファイルの表示
@@ -1,4 +1,3 @@
require 'open-uri'
require 'set'
require 'time'
@@ -127,12 +126,12 @@ module Youtube
return if post.thumbnail.attached?
return if thumbnail_url.blank?
post.thumbnail.attach(
io: URI.open(thumbnail_url),
filename: File.basename(URI.parse(thumbnail_url).path),
content_type: 'image/jpeg')
post.resized_thumbnail!
post.attach_thumbnail_from_url!(thumbnail_url)
rescue Post::RemoteThumbnailFetchFailed => e
Rails.logger.info("youtube_sync_thumbnail_fetch_failed #{ { post_id: post.id,
thumbnail_url:,
error: e.class.name,
message: e.message }.to_json }")
end
def youtube_url_regexp id