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:
@@ -3,6 +3,7 @@
|
|||||||
class MediaAsset < ApplicationRecord
|
class MediaAsset < ApplicationRecord
|
||||||
class Error < StandardError; end
|
class Error < StandardError; end
|
||||||
|
|
||||||
|
FILE_KEY_LENGTH = 9
|
||||||
VARIANTS = %i[preview 180x180 360x360 720x720 sample original]
|
VARIANTS = %i[preview 180x180 360x360 720x720 sample original]
|
||||||
MAX_VIDEO_DURATION = Danbooru.config.max_video_duration.to_i
|
MAX_VIDEO_DURATION = Danbooru.config.max_video_duration.to_i
|
||||||
MAX_IMAGE_RESOLUTION = Danbooru.config.max_image_resolution
|
MAX_IMAGE_RESOLUTION = Danbooru.config.max_image_resolution
|
||||||
@@ -22,6 +23,9 @@ class MediaAsset < ApplicationRecord
|
|||||||
delegate :metadata, to: :media_metadata
|
delegate :metadata, to: :media_metadata
|
||||||
delegate :is_non_repeating_animation?, :is_greyscale?, :is_rotated?, to: :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.
|
# 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.
|
# 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.
|
# 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 :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_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_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
|
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
|
validate :validate_resolution, on: :create
|
||||||
|
|
||||||
|
before_create :initialize_file_key
|
||||||
|
|
||||||
class Variant
|
class Variant
|
||||||
extend Memoist
|
extend Memoist
|
||||||
|
|
||||||
@@ -186,7 +193,7 @@ class MediaAsset < ApplicationRecord
|
|||||||
concerning :SearchMethods do
|
concerning :SearchMethods do
|
||||||
class_methods do
|
class_methods do
|
||||||
def search(params)
|
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?
|
if params[:metadata].present?
|
||||||
q = q.joins(:media_metadata).merge(MediaMetadata.search(metadata: params[:metadata]))
|
q = q.joins(:media_metadata).merge(MediaMetadata.search(metadata: params[:metadata]))
|
||||||
@@ -368,4 +375,15 @@ class MediaAsset < ApplicationRecord
|
|||||||
end
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class MediaAssetPolicy < ApplicationPolicy
|
|||||||
if can_see_image?
|
if can_see_image?
|
||||||
super
|
super
|
||||||
else
|
else
|
||||||
super.excluding(:md5)
|
super.excluding(:md5, :file_key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -1102,7 +1102,9 @@ CREATE TABLE public.media_assets (
|
|||||||
image_width integer NOT NULL,
|
image_width integer NOT NULL,
|
||||||
image_height integer NOT NULL,
|
image_height integer NOT NULL,
|
||||||
duration double precision,
|
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);
|
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: -
|
-- 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);
|
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: -
|
-- Name: index_media_assets_on_md5; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@@ -5806,6 +5822,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||||||
('20220403042706'),
|
('20220403042706'),
|
||||||
('20220403220558'),
|
('20220403220558'),
|
||||||
('20220407203236'),
|
('20220407203236'),
|
||||||
('20220410050628');
|
('20220410050628'),
|
||||||
|
('20220504235329');
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
8
script/fixes/109_generate_media_asset_file_keys.rb
Executable file
8
script/fixes/109_generate_media_asset_file_keys.rb
Executable 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
|
||||||
Reference in New Issue
Block a user