StorageManager: remove Post-specific code.

Refactor StorageManager to remove all image URL generation code. Instead
the image URL generation code lives in MediaAsset.

Now StorageManager is only concerned with how to read and write files to
remote storage backends like S3 or SFTP, not with how image URLs should
be generated. This way the file storage code isn't tightly coupled to
posts, so it can be used to store any kind of file, not just images
belonging to posts.
This commit is contained in:
evazion
2021-10-26 19:11:37 -05:00
parent afe5095ee6
commit 082544ab03
7 changed files with 33 additions and 172 deletions

View File

@@ -48,102 +48,8 @@ class StorageManager
raise NotImplementedError, "open not implemented"
end
# Store or replace the given file belonging to the given post.
# @param io [IO] the file to store
# @param post [Post] the post the image belongs to
# @param type [Symbol] the image variant to store (:preview, :crop, :large, :original)
def store_file(io, post, type)
store(io, file_path(post.md5, post.file_ext, type))
end
# Delete the file belonging to the given post.
# @param post_id [Integer] the post's id
# @param md5 [String] the post's md5
# @param file_ext [String] the post's file extension
# @param type [Symbol] the image variant to delete (:preview, :crop, :large, :original)
def delete_file(post_id, md5, file_ext, type)
delete(file_path(md5, file_ext, type))
end
# Return a readonly copy of the image belonging to the given post.
# @param post [Post] the post
# @param type [Symbol] the image variant to open (:preview, :crop, :large, :original)
# @return [MediaFile] the image file
def open_file(post, type)
self.open(file_path(post.md5, post.file_ext, type))
end
# Generate the image URL for the given post.
# @param post [Post] the post
# @param type [Symbol] the post's image variant (:preview, :crop, :large, :original)
# @param tagged_filename [Boolean] whether the URL should contain the post's tags
# @return [String] the image URL
def file_url(post, type, tagged_filenames: false)
subdir = subdir_for(post.md5)
file = file_name(post.md5, post.file_ext, type)
seo_tags = seo_tags(post) if tagged_filenames
if type == :preview && !post.has_preview?
"#{root_url}/images/download-preview.png"
elsif type == :preview
"#{base_url}/preview/#{subdir}#{file}"
elsif type == :crop
"#{base_url}/crop/#{subdir}#{file}"
elsif type == :large && post.has_large?
"#{base_url}/sample/#{subdir}#{seo_tags}#{file}"
else
"#{base_url}/original/#{subdir}#{seo_tags}#{post.md5}.#{post.file_ext}"
end
end
def root_url
origin = Addressable::URI.parse(base_url).origin
origin = "" if origin == "null" # base_url was relative
origin
end
def file_path(post_or_md5, file_ext, type)
md5 = post_or_md5.is_a?(String) ? post_or_md5 : post_or_md5.md5
subdir = subdir_for(md5)
file = file_name(md5, file_ext, type)
case type
when :preview
"/preview/#{subdir}#{file}"
when :crop
"/crop/#{subdir}#{file}"
when :large
"/sample/#{subdir}#{file}"
when :original
"/original/#{subdir}#{file}"
end
end
def file_name(md5, file_ext, type)
large_file_ext = (file_ext == "zip") ? "webm" : "jpg"
case type
when :preview
"#{md5}.jpg"
when :crop
"#{md5}.jpg"
when :large
"sample-#{md5}.#{large_file_ext}"
when :original
"#{md5}.#{file_ext}"
end
end
def subdir_for(md5)
"#{md5[0..1]}/#{md5[2..3]}/"
end
# Generate the tags in the image URL.
def seo_tags(post)
return "" if !tagged_filenames
tags = post.presenter.humanized_essential_tag_string.gsub(/[^a-z0-9]+/, "_").gsub(/(?:^_+)|(?:_+$)/, "").gsub(/_{2,}/, "_")
"__#{tags}__"
def file_url(path)
File.join(base_url, path)
end
def full_path(path)

View File

@@ -38,7 +38,9 @@ class MediaAsset < ApplicationRecord
end
def open_file
storage_service.open(file_path)
file = storage_service.open(file_path)
frame_data = media_asset.pixiv_ugoira_frame_data&.data if media_asset.is_ugoira?
MediaFile.open(file, frame_data: frame_data)
end
def convert_file(media_file)

View File

@@ -74,36 +74,47 @@ class Post < ApplicationRecord
module FileMethods
extend ActiveSupport::Concern
def storage_manager
Danbooru.config.storage_manager
def seo_tags
presenter.humanized_essential_tag_string.gsub(/[^a-z0-9]+/, "_").gsub(/(?:^_+)|(?:_+$)/, "").gsub(/_{2,}/, "_")
end
def file(type = :original)
storage_manager.open_file(self, type)
media_asset.variant(type).open_file
end
def tagged_file_url(tagged_filenames: !CurrentUser.user.disable_tagged_filenames?)
storage_manager.file_url(self, :original, tagged_filenames: tagged_filenames)
slug = seo_tags if tagged_filenames
media_asset.variant(:original).file_url(slug)
end
def tagged_large_file_url(tagged_filenames: !CurrentUser.user.disable_tagged_filenames?)
storage_manager.file_url(self, :large, tagged_filenames: tagged_filenames)
slug = seo_tags if tagged_filenames
if media_asset.has_variant?(:sample)
media_asset.variant(:sample).file_url(slug)
else
media_asset.variant(:original).file_url(slug)
end
end
def file_url
storage_manager.file_url(self, :original)
media_asset.variant(:original).file_url
end
def large_file_url
storage_manager.file_url(self, :large)
if media_asset.has_variant?(:sample)
media_asset.variant(:sample).file_url
else
media_asset.variant(:original).file_url
end
end
def preview_file_url
storage_manager.file_url(self, :preview)
media_asset.variant(:preview).file_url
end
def crop_file_url
storage_manager.file_url(self, :crop)
media_asset.variant(:crop).file_url
end
def open_graph_image_url
@@ -1162,7 +1173,7 @@ class Post < ApplicationRecord
ModAction.log("<@#{user.name}> regenerated IQDB for post ##{id}", :post_regenerate_iqdb, user)
else
media_file = MediaFile.open(file, frame_data: pixiv_ugoira_frame_data&.data.to_a)
media_file = media_asset.variant(:original).open_file
media_asset.distribute_files!(media_file)
update!(