このコミットが含まれているのは:
2026-07-16 18:33:45 +09:00
コミット ef95b20a7e
9個のファイルの変更145行の追加24行の削除
+2 -13
ファイルの表示
@@ -158,10 +158,8 @@ class Post < ApplicationRecord
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!
self.class.resized_thumbnail_attachment(
StringIO.new(response.body)))
rescue Preview::UrlSafety::UnsafeUrl,
Preview::ThumbnailFetcher::GenerationFailed,
Preview::HttpFetcher::FetchFailed,
@@ -173,15 +171,6 @@ class Post < ApplicationRecord
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)
+20 -2
ファイルの表示
@@ -81,12 +81,22 @@ class PostMetadataFetcher
day = match[3].to_i
hour = match[4].to_i
minute = match[5]&.to_i || 0
second = match[6]&.to_i || 0
fraction = match[7]
offset = match[8]
nanoseconds = parse_nanoseconds(fraction)
timestamp =
if offset.present?
Time.new(year, month, day, hour, minute, 0, parse_offset(offset)).in_time_zone
Time.new(
year,
month,
day,
hour,
minute,
second + Rational(nanoseconds, 1_000_000_000),
parse_offset(offset)).in_time_zone
else
Time.zone.local(year, month, day, hour, minute)
Time.zone.local(year, month, day, hour, minute, second).change(nsec: nanoseconds)
end
from = timestamp.change(sec: 0, nsec: 0)
@@ -94,6 +104,13 @@ class PostMetadataFetcher
[from, before]
end
def self.parse_nanoseconds value
return 0 if value.blank?
digits = value[0, 9].ljust(9, '0')
Integer(digits, 10)
end
def self.parse_offset value
return '+00:00' if value == 'Z'
@@ -109,6 +126,7 @@ class PostMetadataFetcher
private_class_method :platform_tags,
:original_created_range,
:parse_timestamp_range,
:parse_nanoseconds,
:parse_offset,
:serialise_time
end
+47
ファイルの表示
@@ -187,12 +187,59 @@ RSpec.describe Post, type: :model do
post = described_class.create!(title: 'title', url: 'https://example.com/post')
expect(post.thumbnail).to receive(:attach).once.and_call_original
post.attach_thumbnail_from_url!('https://example.com/thumb.png')
expect(post.thumbnail).to be_attached
image = read_image(post.thumbnail)
expect(image.dimensions).to eq([180, 180])
end
it 'does not attach anything when thumbnail conversion fails' do
response = Preview::HttpFetcher::Response.new(
'not-an-image',
'image/png',
'https://example.com/thumb.png')
allow(Preview::ThumbnailFetcher).to receive(:fetch_image_response)
.with('https://example.com/thumb.png')
.and_return(response)
allow(described_class).to receive(:resized_thumbnail_attachment)
.and_raise(MiniMagick::Error, 'convert failed')
post = described_class.create!(title: 'title', url: 'https://example.com/post')
expect {
post.attach_thumbnail_from_url!('https://example.com/thumb.png')
}.to raise_error(Post::RemoteThumbnailFetchFailed, 'convert failed')
expect(post.thumbnail).not_to be_attached
end
it 'keeps an existing thumbnail when remote conversion fails' do
existing = described_class.create!(title: 'title', url: 'https://example.com/post')
existing.thumbnail.attach(
io: StringIO.new('existing'),
filename: 'existing.jpg',
content_type: 'image/jpeg')
blob_id = existing.thumbnail.blob.id
response = Preview::HttpFetcher::Response.new(
'not-an-image',
'image/png',
'https://example.com/thumb.png')
allow(Preview::ThumbnailFetcher).to receive(:fetch_image_response)
.with('https://example.com/thumb.png')
.and_return(response)
allow(described_class).to receive(:resized_thumbnail_attachment)
.and_raise(MiniMagick::Error, 'convert failed')
expect {
existing.attach_thumbnail_from_url!('https://example.com/thumb.png')
}.to raise_error(Post::RemoteThumbnailFetchFailed, 'convert failed')
existing.reload
expect(existing.thumbnail).to be_attached
expect(existing.thumbnail.blob.id).to eq(blob_id)
end
end
end
end
+7
ファイルの表示
@@ -61,4 +61,11 @@ RSpec.describe PostMetadataFetcher do
expect(result.fetch(:original_created_from)).to be_nil
expect(result.fetch(:original_created_before)).to be_nil
end
it 'returns nil dates for an invalid timestamp second value' do
result = fetch_with_published_time('2024-02-03T12:34:99Z')
expect(result.fetch(:original_created_from)).to be_nil
expect(result.fetch(:original_created_before)).to be_nil
end
end
+17 -1
ファイルの表示
@@ -23,6 +23,22 @@ RSpec.describe Youtube::Sync do
post,
'https://example.com/thumb.jpg')
end
it 'retries on a later sync when the previous remote attach failed' do
post = create(:post, thumbnail_base: nil)
allow(sync).to receive(:attach_thumbnail_if_needed!).and_call_original
expect(post).to receive(:attach_thumbnail_from_url!)
.with('https://example.com/thumb.jpg')
.twice
.and_raise(Post::RemoteThumbnailFetchFailed, 'failed')
2.times do
sync.send(
:attach_thumbnail_if_needed!,
post,
'https://example.com/thumb.jpg')
end
end
end
describe '#sync!' do
@@ -91,7 +107,7 @@ RSpec.describe Youtube::Sync do
sync.sync!
end
it 'creates a YouTube post with default tags and no_deerjikist when no deerjikist mapping exists' do
it 'creates a YouTube post with default tags when no deerjikist mapping exists' do
Tag.tagme
Tag.bot
Tag.youtube