Packages
phoenix_kit
1.6.5
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/phoenix_kit/storage/dimension.ex
defmodule PhoenixKit.Storage.Dimension do
@moduledoc """
Schema for dimension presets for automatic file variant generation.
Dimensions define target sizes and quality settings for automatically
generating different versions of uploaded images and videos.
## Fields
- `name` - Unique name for this dimension (e.g., "thumbnail", "medium")
- `width` - Target width in pixels (nullable = maintain aspect ratio)
- `height` - Target height in pixels (nullable = maintain aspect ratio)
- `quality` - Quality setting (1-100 for images, CRF 0-51 for videos)
- `format` - Output format override (nullable = preserve original)
- `applies_to` - What this dimension applies to: "image", "video", or "both"
- `enabled` - Whether this dimension is active
- `order` - Display order in admin interface
## Quality Settings
### Images (JPEG, WebP, PNG)
- Range: 1-100
- 85 = Good quality (default)
- 95 = High quality
- 70 = Medium quality
### Videos (MP4, WebM)
- Range: 0-51 (CRF - Constant Rate Factor)
- Lower = Higher quality
- 23 = Good quality (default)
- 18 = High quality
- 28 = Medium quality
## Examples
# Image thumbnail
%Dimension{
name: "thumbnail",
width: 150,
height: 150,
quality: 85,
format: "jpg",
applies_to: "image",
enabled: true,
order: 1
}
# Video 720p variant
%Dimension{
name: "720p",
width: 1280,
height: 720,
quality: 23,
format: "mp4",
applies_to: "video",
enabled: true,
order: 2
}
# Responsive image (maintain aspect ratio)
%Dimension{
name: "large",
width: 1920,
height: nil, # Maintain aspect ratio
quality: 85,
format: nil, # Preserve original
applies_to: "image",
enabled: true,
order: 3
}
"""
use Ecto.Schema
import Ecto.Changeset
@primary_key {:id, UUIDv7, autogenerate: true}
@foreign_key_type UUIDv7
schema "phoenix_kit_storage_dimensions" do
field :name, :string
field :width, :integer
field :height, :integer
field :quality, :integer
field :format, :string
field :applies_to, :string
field :enabled, :boolean, default: true
field :maintain_aspect_ratio, :boolean, default: true
field :order, :integer, default: 0
timestamps(type: :naive_datetime)
end
@doc """
Changeset for creating or updating a dimension.
## Required Fields
- `name`
- `applies_to`
- `width` - Required when `maintain_aspect_ratio` is true or false
## Optional Fields
- `height` - Required when `maintain_aspect_ratio` is false (fixed dimensions)
- `maintain_aspect_ratio` - Whether to preserve aspect ratio (default: true)
## Validation Rules
- Name must be unique
- Width must be specified (always required)
- Height must be specified when `maintain_aspect_ratio` is false
- Width and height must be positive integers
- Quality must be between 1-100 for images or 0-51 for videos
- Applies to must be one of: "image", "video", "both"
- Format must be valid if specified
- Order must be >= 0
"""
def changeset(dimension, attrs) do
dimension
|> cast(attrs, [
:name,
:width,
:height,
:quality,
:format,
:applies_to,
:enabled,
:maintain_aspect_ratio,
:order
])
|> validate_required([:name, :applies_to])
|> validate_format(:name, ~r/^[a-z0-9_]+$/,
message: "must contain only lowercase letters, numbers, and underscores"
)
|> validate_length(:name, min: 1, max: 50)
|> validate_inclusion(:applies_to, ["image", "video", "both"])
|> validate_number(:width, greater_than: 0)
|> validate_number(:height, greater_than: 0)
|> validate_number(:order, greater_than_or_equal_to: 0)
|> validate_dimension_size()
|> validate_quality()
|> validate_format()
|> unique_constraint(:name, name: :phoenix_kit_storage_dimensions_name_index)
end
# Validate dimensions based on maintain_aspect_ratio setting
defp validate_dimension_size(changeset) do
width = get_field(changeset, :width)
height = get_field(changeset, :height)
maintain_aspect = get_field(changeset, :maintain_aspect_ratio)
cond do
is_nil(width) ->
add_error(changeset, :width, "width is required")
maintain_aspect == false && is_nil(height) ->
add_error(changeset, :height, "height is required when not maintaining aspect ratio")
true ->
changeset
end
end
# Validate quality based on file type
defp validate_quality(changeset) do
quality = get_field(changeset, :quality)
applies_to = get_field(changeset, :applies_to)
name = get_field(changeset, :name)
format = get_field(changeset, :format)
cond do
is_nil(quality) ->
changeset
# video_thumbnail outputs images, so it uses image quality scale (1-100)
name == "video_thumbnail" and format in ["jpg", "jpeg", "png", "webp", "gif"] ->
validate_number(changeset, :quality,
greater_than_or_equal_to: 1,
less_than_or_equal_to: 100
)
applies_to in ["image", "both"] ->
validate_number(changeset, :quality,
greater_than_or_equal_to: 1,
less_than_or_equal_to: 100
)
applies_to == "video" ->
validate_number(changeset, :quality,
greater_than_or_equal_to: 0,
less_than_or_equal_to: 51
)
true ->
changeset
end
end
# Validate format is supported
defp validate_format(changeset) do
format = get_field(changeset, :format)
applies_to = get_field(changeset, :applies_to)
if is_nil(format) do
changeset
else
valid_formats = get_valid_formats(applies_to)
if format in valid_formats do
changeset
else
add_error(changeset, :format, "must be one of: #{Enum.join(valid_formats, ", ")}")
end
end
end
defp get_valid_formats("image"), do: ["jpg", "jpeg", "png", "webp", "gif"]
# Videos can output video formats, but video_thumbnail outputs JPG images
defp get_valid_formats("video"), do: ["mp4", "webm", "avi", "mov", "jpg", "jpeg", "png", "webp"]
defp get_valid_formats("both"),
do: ["jpg", "jpeg", "png", "webp", "gif", "mp4", "webm", "avi", "mov"]
@doc """
Returns whether this dimension applies to images.
"""
def applies_to_images?(%__MODULE__{applies_to: applies_to})
when applies_to in ["image", "both"],
do: true
def applies_to_images?(_), do: false
@doc """
Returns whether this dimension applies to videos.
"""
def applies_to_videos?(%__MODULE__{applies_to: applies_to})
when applies_to in ["video", "both"],
do: true
def applies_to_videos?(_), do: false
@doc """
Returns whether this dimension preserves aspect ratio.
"""
def preserve_aspect_ratio?(%__MODULE__{maintain_aspect_ratio: maintain_aspect}) do
maintain_aspect == true
end
@doc """
Returns a human-readable description of this dimension.
"""
def description(%__MODULE__{
name: name,
width: width,
height: height,
format: format,
maintain_aspect_ratio: maintain_aspect
}) do
size_desc =
cond do
maintain_aspect && width -> "#{width}px wide (aspect ratio maintained)"
width && height -> "#{width}×#{height}"
width -> "#{width}px wide"
height -> "#{height}px tall"
true -> "auto"
end
format_desc = if format, do: " (#{format})", else: ""
"#{name}: #{size_desc}#{format_desc}"
end
end