media assets: add file_key, is_public columns.

`file_key` is a random 9-character base-62 string that will be used as
the image filename in the future.

`is_public` is whether the image can be viewed without authentication or not.

Users running downstream boorus must run `bin/rails db:migrate` and
`script/fixes/109_generate_media_asset_file_keys.rb` after this commit.
This commit is contained in:
evazion
2022-05-04 22:03:25 -05:00
parent d511a6b6cf
commit 4ba993319a
5 changed files with 56 additions and 4 deletions

View File

@@ -3,6 +3,7 @@
class MediaAsset < ApplicationRecord
class Error < StandardError; end
FILE_KEY_LENGTH = 9
VARIANTS = %i[preview 180x180 360x360 720x720 sample original]
MAX_VIDEO_DURATION = Danbooru.config.max_video_duration.to_i
MAX_IMAGE_RESOLUTION = Danbooru.config.max_image_resolution
@@ -22,6 +23,9 @@ class MediaAsset < ApplicationRecord
delegate :metadata, to: :media_metadata
delegate :is_non_repeating_animation?, :is_greyscale?, :is_rotated?, to: :metadata
scope :public_only, -> { where(is_public: true) }
scope :private_only, -> { where(is_public: false) }
# Processing: The asset's files are currently being resized and distributed to the backend servers.
# Active: The asset has been successfully uploaded and is ready to use.
# Deleted: The asset's files have been deleted by moving them to a trash folder. They can be undeleted by moving them out of the trash folder.
@@ -39,9 +43,12 @@ class MediaAsset < ApplicationRecord
validates :md5, uniqueness: { conditions: -> { where(status: [:processing, :active]) } }
validates :file_ext, inclusion: { in: %w[jpg png gif mp4 webm swf zip], message: "File is not an image or video" }
validates :file_size, numericality: { less_than_or_equal_to: Danbooru.config.max_file_size, message: ->(asset, _) { "too large (size: #{asset.file_size.to_formatted_s(:human_size)}; max size: #{Danbooru.config.max_file_size.to_formatted_s(:human_size)})" } }
validates :file_key, length: { is: FILE_KEY_LENGTH }, uniqueness: true, if: :file_key_changed?
validates :duration, numericality: { less_than_or_equal_to: MAX_VIDEO_DURATION, message: "must be less than #{MAX_VIDEO_DURATION} seconds", allow_nil: true }, on: :create # XXX should allow admins to bypass
validate :validate_resolution, on: :create
before_create :initialize_file_key
class Variant
extend Memoist
@@ -186,7 +193,7 @@ class MediaAsset < ApplicationRecord
concerning :SearchMethods do
class_methods do
def search(params)
q = search_attributes(params, :id, :created_at, :updated_at, :md5, :file_ext, :file_size, :image_width, :image_height)
q = search_attributes(params, :id, :created_at, :updated_at, :md5, :file_ext, :file_size, :image_width, :image_height, :file_key, :is_public)
if params[:metadata].present?
q = q.joins(:media_metadata).merge(MediaMetadata.search(metadata: params[:metadata]))
@@ -368,4 +375,15 @@ class MediaAsset < ApplicationRecord
end
end
end
def self.generate_file_key
loop do
key = SecureRandom.send(:choose, [*"0".."9", *"A".."Z", *"a".."z"], FILE_KEY_LENGTH) # base62
return key unless MediaAsset.exists?(file_key: key)
end
end
def initialize_file_key
self.file_key = MediaAsset.generate_file_key
end
end

View File

@@ -13,7 +13,7 @@ class MediaAssetPolicy < ApplicationPolicy
if can_see_image?
super
else
super.excluding(:md5)
super.excluding(:md5, :file_key)
end
end
end

View File

@@ -0,0 +1,9 @@
class AddFileKeyToMediaAssets < ActiveRecord::Migration[7.0]
def change
add_column :media_assets, :file_key, :string, null: true
add_column :media_assets, :is_public, :boolean, default: true, null: false
add_index :media_assets, :file_key, unique: true
add_index :media_assets, :is_public, where: "is_public = FALSE"
end
end

View File

@@ -1102,7 +1102,9 @@ CREATE TABLE public.media_assets (
image_width integer NOT NULL,
image_height integer NOT NULL,
duration double precision,
status integer DEFAULT 200 NOT NULL
status integer DEFAULT 200 NOT NULL,
file_key character varying,
is_public boolean DEFAULT true NOT NULL
);
@@ -3805,6 +3807,13 @@ CREATE INDEX index_media_assets_on_duration ON public.media_assets USING btree (
CREATE INDEX index_media_assets_on_file_ext ON public.media_assets USING btree (file_ext);
--
-- Name: index_media_assets_on_file_key; Type: INDEX; Schema: public; Owner: -
--
CREATE UNIQUE INDEX index_media_assets_on_file_key ON public.media_assets USING btree (file_key);
--
-- Name: index_media_assets_on_file_size; Type: INDEX; Schema: public; Owner: -
--
@@ -3826,6 +3835,13 @@ CREATE INDEX index_media_assets_on_image_height ON public.media_assets USING btr
CREATE INDEX index_media_assets_on_image_width ON public.media_assets USING btree (image_width);
--
-- Name: index_media_assets_on_is_public; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_media_assets_on_is_public ON public.media_assets USING btree (is_public) WHERE (is_public = false);
--
-- Name: index_media_assets_on_md5; Type: INDEX; Schema: public; Owner: -
--
@@ -5806,6 +5822,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20220403042706'),
('20220403220558'),
('20220407203236'),
('20220410050628');
('20220410050628'),
('20220504235329');

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env ruby
require_relative "base"
MediaAsset.where(file_key: nil).parallel_each do |asset|
asset.update_columns(file_key: MediaAsset.generate_file_key)
puts "id=#{asset.id} file_key=#{asset.file_key}"
end