このコミットが含まれているのは:
2026-07-10 00:34:45 +09:00
コミット 3a8f7d8d89
4個のファイルの変更109行の追加23行の削除
+54 -6
ファイルの表示
@@ -1,26 +1,47 @@
module Preview
class ThumbnailFetcher
class GenerationFailed < StandardError; end
HTML_MAX_BYTES = 1.megabyte
NICONICO_XML_MAX_BYTES = 256.kilobytes
def self.fetch(raw_url)
uri, = UrlSafety.validate(raw_url)
known_url = KnownSiteExtractor.thumbnail_url(uri)
return fetch_image(known_url) if known_url
image = fetch_image_or_nil(known_url) if known_url
return image if image
page = HttpFetcher.fetch(uri.to_s)
niconico_url = niconico_thumbnail_url(uri)
image = fetch_image_or_nil(niconico_url) if niconico_url
return image if image
page = HttpFetcher.fetch(uri.to_s, max_bytes: HTML_MAX_BYTES)
metadata = HtmlMetadataExtractor.extract(page)
raise GenerationFailed, 'サムネール画像が見つかりませんでした.' if metadata[:image_url].blank?
fetch_image(metadata[:image_url])
fetch_image!(metadata[:image_url])
end
def self.title(raw_url)
uri, = UrlSafety.validate(raw_url)
HtmlMetadataExtractor.extract(HttpFetcher.fetch(uri.to_s))[:title]
HtmlMetadataExtractor.extract(
HttpFetcher.fetch(uri.to_s, max_bytes: HTML_MAX_BYTES))[:title]
end
def self.fetch_image(url)
def self.fetch_image_or_nil(url)
return nil if url.blank?
response = HttpFetcher.fetch(url)
return nil unless response.content_type.downcase.start_with?('image/')
response.body
rescue HttpFetcher::FetchTimeout
raise
rescue HttpFetcher::FetchFailed
nil
end
def self.fetch_image!(url)
response = HttpFetcher.fetch(url)
unless response.content_type.downcase.start_with?('image/')
raise GenerationFailed, 'サムネール画像が見つかりませんでした.'
@@ -29,10 +50,37 @@ module Preview
response.body
rescue HttpFetcher::FetchTimeout
raise
rescue HttpFetcher::ResponseTooLarge
raise
rescue HttpFetcher::FetchFailed
raise GenerationFailed, 'サムネール画像を取得できませんでした.'
end
private_class_method :fetch_image
def self.niconico_thumbnail_url(uri)
video_id = KnownSiteExtractor.niconico_video_id(uri)
return nil if video_id.blank?
response = HttpFetcher.fetch("https://ext.nicovideo.jp/api/getthumbinfo/#{ video_id }",
max_bytes: NICONICO_XML_MAX_BYTES)
xml = Nokogiri::XML(response.body)
return nil unless xml.at_xpath('/nicovideo_thumb_response/@status')&.value == 'ok'
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 }")
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 }")
nil
end
private_class_method :fetch_image_or_nil, :fetch_image!,
:niconico_thumbnail_url
end
end