Remove the `hierarchical` file storage option. This means that image files are always stored in MD5-based subdirectories, like this: https://danbooru.donmai.us/data/original/f3/a7/f3a70a89c350b5ed4db22dbb25b934bb.jpg https://danbooru.donmai.us/data/sample/f3/a7/sample-f3a70a89c350b5ed4db22dbb25b934bb.jpg https://danbooru.donmai.us/data/preview/f3/a7/f3a70a89c350b5ed4db22dbb25b934bb.jpg instead of in a single flat directory, like this: https://danbooru.donmai.us/data/original/f3a70a89c350b5ed4db22dbb25b934bb.jpg This option is removed because storing files in a single directory is a bad idea for large installations, and migrating from a single directory to subdirectories later is a pain. Downstream boorus who still have files in the old layout can migrate by running this script: `./script/fixes/077_symlink_subdirectories.rb` This will create symlinks that redirect the 00-ff subdirectories back to the current directory, so that you can still store files in a single directory, but use URLs containing subdirectories. You should also make sure to remove the `hierarchical` option from `storage_manager` in `config/danbooru_local_config.rb` if you set it there.
110 lines
3.0 KiB
Ruby
110 lines
3.0 KiB
Ruby
class StorageManager
|
|
class Error < StandardError; end
|
|
|
|
attr_reader :base_url, :base_dir, :tagged_filenames
|
|
|
|
def initialize(base_url:, base_dir:, tagged_filenames: Danbooru.config.enable_seo_post_urls)
|
|
@base_url = base_url.chomp("/")
|
|
@base_dir = base_dir
|
|
@tagged_filenames = tagged_filenames
|
|
end
|
|
|
|
# Store the given file at the given path. If a file already exists at that
|
|
# location it should be overwritten atomically. Either the file is fully
|
|
# written, or an error is raised and the original file is left unchanged. The
|
|
# file should never be in a partially written state.
|
|
def store(io, path)
|
|
raise NotImplementedError, "store not implemented"
|
|
end
|
|
|
|
# Delete the file at the given path. If the file doesn't exist, no error
|
|
# should be raised.
|
|
def delete(path)
|
|
raise NotImplementedError, "delete not implemented"
|
|
end
|
|
|
|
# Return a readonly copy of the file located at the given path.
|
|
def open(path)
|
|
raise NotImplementedError, "open not implemented"
|
|
end
|
|
|
|
def store_file(io, post, type)
|
|
store(io, file_path(post.md5, post.file_ext, type))
|
|
end
|
|
|
|
def delete_file(post_id, md5, file_ext, type)
|
|
delete(file_path(md5, file_ext, type))
|
|
end
|
|
|
|
def open_file(post, type)
|
|
self.open(file_path(post.md5, post.file_ext, type))
|
|
end
|
|
|
|
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
|
|
"#{base_dir}/preview/#{subdir}#{file}"
|
|
when :crop
|
|
"#{base_dir}/crop/#{subdir}#{file}"
|
|
when :large
|
|
"#{base_dir}/sample/#{subdir}#{file}"
|
|
when :original
|
|
"#{base_dir}/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
|
|
|
|
def seo_tags(post)
|
|
return "" if !tagged_filenames
|
|
|
|
tags = post.presenter.humanized_essential_tag_string.gsub(/[^a-z0-9]+/, "_").gsub(/(?:^_+)|(?:_+$)/, "").gsub(/_{2,}/, "_")
|
|
"__#{tags}__"
|
|
end
|
|
end
|