Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 37 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@ http {
And here's minimal viable config for 4 locations you need to set up. These locations are described in the sections below:
```
# video location
location ~ ^/(?<luamp_media_type>(video))/(?<luamp_flags>([0-9a-zA-Z_,\.:]+)\/|)(?<luamp_media_id>[0-9a-zA-Z_\-\.]+)\.(?<luamp_media_extension>(mp4))$ {
location ~ ^/(?<luamp_prefix>.*?\/upload\/)(?<luamp_flags>([^\/]+)\/|\/|)(v\d+|)\/(?<luamp_postfix>.*\/|\/|)(?<luamp_media_id>[0-9a-zA-Z_\-\.]+)\.(?<luamp_media_extension>(mp4))$ {
# these two are required to be set regardless
set $luamp_media_type "video";
set $luamp_original_file "";
set $luamp_transcoded_file "";

# these are needed to be set if you did not use them in regex matching location
set $luamp_prefix "";
set $luamp_postfix "";
Expand All @@ -81,7 +82,12 @@ location @luamp_video_process {
}

# image location
location ~ ^/(?<luamp_media_type>(image))/(?<luamp_flags>([0-9a-zA-Z_,\.:]+)\/|)(?<luamp_media_id>[0-9a-zA-Z_\-\.]+)\.(?<luamp_media_extension>(jpe?g|png|gif|bmp|tiff?|svg|pdf|webp))$ {
location ~ ^/(?<luamp_prefix>.*?\/upload\/)(?<luamp_flags>([^\/]+)\/|\/|)(v\d+|)\/(?<luamp_postfix>.*\/|\/|)(?<luamp_media_id>[0-9a-zA-Z_\-\.]+)\.(?<luamp_media_extension>(jpe?g|png|gif|bmp|tiff?|svg|pdf|webp))$ {
# these are needed to be set if you did not use them in regex matching location
set $luamp_media_type "image";
set $luamp_prefix "";
set $luamp_postfix "";

#pass to transcoder location
try_files $uri @luamp_media_processor;
}
Expand All @@ -91,10 +97,6 @@ location @luamp_media_processor {
# these two are required to be set regardless
set $luamp_original_file "";
set $luamp_transcoded_file "";

# these are needed to be set if you did not use them in regex matching location
set $luamp_prefix "";
set $luamp_postfix "";

content_by_lua_file "/absolute/path/to/nginx-lua-mp4/media-processor.lua";
}
Expand Down Expand Up @@ -124,14 +126,13 @@ This location used as an entry point and to set initial variables. This is usual

There are two variables you need to `set`/initialise: `$luamp_original_file` and `$luamp_transcoded_file`.

There are six variables that may be used as a named capture group in location regex: `luamp_prefix`, `luamp_media_type`, `luamp_flags`, `luamp_postfix`, `luamp_media_id`, `luamp_media_extension`.
There are six variables that may be used as a named capture group in location regex: `luamp_prefix`, `luamp_flags`, `luamp_postfix`, `luamp_media_id`, `luamp_media_extension`.

For example:

```
https://example.com/asset/video/width_1980,height_1080,crop_padding/2019/12/new_year_boardgames_party.mp4
luamp_prefix: asset/
luamp_media_type: video
luamp_prefix: asset/video/
luamp_flags: width_1980,height_1080,crop_padding
luamp_postfix: 2019/12/
luamp_media_id: new_year_boardgames_party
Expand All @@ -145,10 +146,6 @@ location @luamp_media_processor {
# these two are required to be set regardless
set $luamp_original_file "";
set $luamp_transcoded_file "";

# these are needed to be set if you did not use them in regex matching location
set $luamp_prefix "";
set $luamp_postfix "";

content_by_lua_file "/absolute/path/to/nginx-lua-mp4/media-processor.lua";
}
Expand All @@ -174,6 +171,10 @@ Process location is pretty simple, it just passes execution to the LUA part of l

```
location @luamp_media_processor {
# these two are required to be set regardless
set $luamp_original_file "";
set $luamp_transcoded_file "";

content_by_lua_file "/absolute/path/to/nginx-lua-mp4/media-processor.lua";
}
```
Expand Down Expand Up @@ -232,7 +233,7 @@ $ nano config.lua

When set to `true`, `luamp` will attempt to download missing original videos from the upstream. Set it to `false` if you have original videos provided by other means to this directory:
```
config.mediaBaseFilepath/<media_type>/original/$prefix/$postfix/$media_id
config.mediaBaseFilepath/$prefix/$postfix/$media_id.$media_extension
```

#### `config.ffmpeg`
Expand All @@ -244,6 +245,15 @@ $ which ffmpeg
/usr/local/bin/ffmpeg
```

#### `config.magick`

Path to the `magick` executable. Can be figured out by using `which` command in the terminal:

```
$ which magick
/usr/local/bin/magick
```

#### `config.ffmpegDevNull`

Where to redirect `ffmpeg` output if `config.logFfmpegOutput` is set to false.
Expand Down Expand Up @@ -337,9 +347,9 @@ Log level, available values: `ngx.STDERR`, `ngx.EMERG`, `ngx.ALERT`, `ngx.CRIT`,

Whether to prepend `ffmpeg` command with `time` utility, if you wish to log time spent in transcoding.

#### `config.maxHeight` and `config.maxWidth`
#### `config.maxVideoHeight`,`config.maxVideoWidth`, `config.maxImageHeight` and `config.maxImageWidth`

Limit the output video's maximum height or width. If the resulting height or width is exceeding the limit (for example, after a high DPR calculation), it will be capped at the `config.maxHeight` and `config.maxWidth`.
Limit the maximum height or width of the output media. If the resulting height or width exceeds the limit (for example, after a high DPR calculation), it will be limited to the specified limits.

#### `config.mediaBaseFilepath`

Expand All @@ -355,6 +365,14 @@ During the transcoding, errors may occur and ffmpeg sometimes leaves corrupt fil

Serve original file when transcode failed. If set to `false`, luamp will respond with 404 in this case

#### `config.stripColorProfile`

Removes a color profile from the original file.

#### `config.colorProfilePath`

Applies a color profile from specified location.

## Update

To update luamp, just do a `git pull`.
Expand Down Expand Up @@ -389,9 +407,9 @@ You definitely want to keep what was customized by you but also to get new confi
```
nginx-lua-mp4 $ sdiff -s config.lua config.lua.example | grep -e '\s*>' | sed -ne "s/^[[:space:]]*>\t//p"
-- top limit for output video height (default 4k UHD)
config.maxHeight = 2160
config.maxVideoHeight = 2160
-- top limit for output video width (default 4k UHD)
config.maxWidth = 3840
config.maxVideoWidth = 3840
```

You can now just copy and paste these lines above the `return config` in `config.lua` and that's it 👌
Expand Down
29 changes: 20 additions & 9 deletions command.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,29 @@ local function getCanvas(config, file, flags)

canvas = file.originalFileIdPath .. ' -size %wx%h gradient:' .. dominantColors .. ' -delete 0 '
elseif background == 'blurred' then
canvas = file.originalFileIdPath .. ' -crop 80%x80% +repage -blur 0x8 '
canvas = file.originalFileIdPath .. ' -crop 80%x80% +repage -scale 10% -blur 0x2.5 -resize 1000% '
else
canvas = file.originalFileIdPath .. ' -size %wx%h xc:' .. (background or '') .. ' -delete 0 '
end

return canvas
end

local function getMask(radius)
local mask =
' -size %[origwidth]x%[origheight]' ..
' xc:black' ..
' -fill white'

if radius then
mask = mask .. ' -draw "roundrectangle 0,0,%[origwidth],%[origheight],' .. radius .. ',' .. radius .. '"'
else
mask = mask .. ' -draw "rectangle 0,0,%[origwidth],%[origheight]"'
end

return mask .. ' -alpha Copy'
end

-- Build image processing command
---@param config table
---@param file table
Expand All @@ -39,6 +54,7 @@ local function buildImageProcessingCommand(config, file, flags)
local height = flags[Flag.IMAGE_HEIGHT_NAME].value
local radius = flags[Flag.IMAGE_RADIUS_NAME].value
local quality = flags[Flag.IMAGE_QUALITY_NAME].value
local minpad = flags[Flag.IMAGE_MINPAD_NAME].value

-- Construct a command
local command = ''
Expand All @@ -47,13 +63,8 @@ local function buildImageProcessingCommand(config, file, flags)
' -quality ' .. quality ..
' -gravity ' .. gravity .. ' '
local canvas = getCanvas(config, file, flags)
local image = file.originalFileIdPath .. ' -modulate 100,120,100 '
local mask =
'-size %[origwidth]x%[origheight]' ..
' xc:black' ..
' -fill white' ..
' -draw "roundrectangle 0,0,%[origwidth],%[origheight],' .. radius .. ',' .. radius .. '"' ..
' -alpha Copy'
local image = file.originalFileIdPath .. ' -modulate 100,120,100'
local mask = getMask(radius)
local dimensions = (width or '') .. 'x' .. (height or '')

if crop == 'fill' and (width or height) then
Expand All @@ -76,7 +87,7 @@ local function buildImageProcessingCommand(config, file, flags)
' -crop ' .. dimensions .. '+0+0 ' ..
' \\( ' ..
image ..
' -resize ' .. dimensions .. '\\>' ..
' -resize ' .. (width - (minpad or 0)) .. 'x' .. (height - (minpad or 0)) .. '\\>' ..
' -set option:origwidth %w' ..
' -set option:origheight %h' ..
' \\( ' .. mask .. ' \\) -compose CopyOpacity -composite' ..
Expand Down
1 change: 1 addition & 0 deletions config.lua.example
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ config.flagImageMap = {
w = Flag.IMAGE_WIDTH_NAME,
r = Flag.IMAGE_RADIUS_NAME,
q = Flag.IMAGE_QUALITY_NAME,
minp = Flag.IMAGE_MINPAD_NAME,
}

-- override URL flag values. Useful when you migrate from another transcoding solution and already have
Expand Down
5 changes: 4 additions & 1 deletion file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ local function buildCacheDirPath(basePath, flags)

-- Add the flag name to the ordered list
for flagName, _ in pairs(flags) do
table.insert(flagNamesOrdered, flagName)
local flag = flags[flagName]
if flag.makeDir then
table.insert(flagNamesOrdered, flagName)
end
end
-- Sort flags so path will be the same for `w_1280,h_960` and `h_960,w_1280`
table.sort(flagNamesOrdered)
Expand Down
34 changes: 24 additions & 10 deletions flag.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,32 @@ Flag.IMAGE_HEIGHT_NAME = 'height'
Flag.IMAGE_WIDTH_NAME = 'width'
Flag.IMAGE_RADIUS_NAME = 'radius'
Flag.IMAGE_QUALITY_NAME = 'quality'
Flag.IMAGE_MINPAD_NAME = 'minpad'

local IMAGE_DEFAULTS = {
[Flag.IMAGE_BACKGROUND_NAME] = 'white',
[Flag.IMAGE_DPR_NAME] = 1,
[Flag.IMAGE_GRAVITY_NAME] = 'center',
[Flag.IMAGE_X_NAME] = 0,
[Flag.IMAGE_Y_NAME] = 0,
[Flag.IMAGE_RADIUS_NAME] = 0.1,
[Flag.IMAGE_QUALITY_NAME] = 80
}

-- Base class method new
function Flag.new(name, value)
function Flag.new(config, name, value)
local self = {}
self.config = config
self.name = name
self.value = value or IMAGE_DEFAULTS[name]
self.isScalable = false
self.makeDir = true

if self.name == Flag.IMAGE_HEIGHT_NAME or self.name == Flag.IMAGE_WIDTH_NAME or self.name == Flag.IMAGE_RADIUS_NAME or self.name == Flag.IMAGE_MINPAD_NAME then
self.isScalable = true
end

if self.name == Flag.IMAGE_DPR_NAME then
self.makeDir = false
end

setmetatable(self, { __index = Flag })
return self
Expand All @@ -41,15 +51,19 @@ function Flag:setValue(value, valueMapper)
end
end

-- Apply limits to a given dimension
-- Scale dimension
---@param dpr number
---@param max number
function Flag:scaleDimension(dpr, max)
if (self.name == Flag.IMAGE_HEIGHT_NAME or self.name == Flag.IMAGE_WIDTH_NAME) and self.value and dpr then
self.value = math.ceil(self.value * dpr)
function Flag:scale(dpr)
if self.value and self.value ~= '' then
self.value = math.ceil(self.value * (dpr or 1))

-- Apply limits
if self.name == Flag.IMAGE_HEIGHT_NAME and self.config.maxImageHeight and self.value > self.config.maxImageHeight then
self.value = self.config.maxImageHeight
end

if self.value > max then
self.value = max
if self.name == Flag.IMAGE_WIDTH_NAME and self.config.maxImageWidth and self.value > self.config.maxImageWidth then
self.value = self.config.maxImageWidth
end
end
end
Expand Down
39 changes: 21 additions & 18 deletions media-processor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,17 @@ local function main()
if mediaType == File.IMAGE_TYPE then
if luampFlags ~= '' then
flags = {
[Flag.IMAGE_BACKGROUND_NAME] = Flag.new(Flag.IMAGE_BACKGROUND_NAME),
[Flag.IMAGE_CROP_NAME] = Flag.new(Flag.IMAGE_CROP_NAME),
[Flag.IMAGE_DPR_NAME] = Flag.new(Flag.IMAGE_DPR_NAME),
[Flag.IMAGE_GRAVITY_NAME] = Flag.new(Flag.IMAGE_GRAVITY_NAME),
[Flag.IMAGE_X_NAME] = Flag.new(Flag.IMAGE_X_NAME),
[Flag.IMAGE_Y_NAME] = Flag.new(Flag.IMAGE_Y_NAME),
[Flag.IMAGE_HEIGHT_NAME] = Flag.new(Flag.IMAGE_HEIGHT_NAME),
[Flag.IMAGE_WIDTH_NAME] = Flag.new(Flag.IMAGE_WIDTH_NAME),
[Flag.IMAGE_RADIUS_NAME] = Flag.new(Flag.IMAGE_RADIUS_NAME),
[Flag.IMAGE_QUALITY_NAME] = Flag.new(Flag.IMAGE_QUALITY_NAME),
[Flag.IMAGE_BACKGROUND_NAME] = Flag.new(config, Flag.IMAGE_BACKGROUND_NAME),
[Flag.IMAGE_CROP_NAME] = Flag.new(config, Flag.IMAGE_CROP_NAME),
[Flag.IMAGE_DPR_NAME] = Flag.new(config, Flag.IMAGE_DPR_NAME),
[Flag.IMAGE_GRAVITY_NAME] = Flag.new(config, Flag.IMAGE_GRAVITY_NAME),
[Flag.IMAGE_X_NAME] = Flag.new(config, Flag.IMAGE_X_NAME),
[Flag.IMAGE_Y_NAME] = Flag.new(config, Flag.IMAGE_Y_NAME),
[Flag.IMAGE_HEIGHT_NAME] = Flag.new(config, Flag.IMAGE_HEIGHT_NAME),
[Flag.IMAGE_WIDTH_NAME] = Flag.new(config, Flag.IMAGE_WIDTH_NAME),
[Flag.IMAGE_RADIUS_NAME] = Flag.new(config, Flag.IMAGE_RADIUS_NAME),
[Flag.IMAGE_QUALITY_NAME] = Flag.new(config, Flag.IMAGE_QUALITY_NAME),
[Flag.IMAGE_MINPAD_NAME] = Flag.new(config, Flag.IMAGE_MINPAD_NAME),
}
flagMapper = config.flagImageMap
valueMapper = config.flagValueMap
Expand All @@ -104,15 +105,16 @@ local function main()
end
end


-- Scale dimensions with respect to limits
if flags[Flag.IMAGE_HEIGHT_NAME] then
local maxHeight = (mediaType == File.IMAGE_TYPE and config.maxImageHeight) or config.maxVideoHeight
flags[Flag.IMAGE_HEIGHT_NAME]:scaleDimension(flags[Flag.IMAGE_DPR_NAME].value, maxHeight)
end
if flags[Flag.IMAGE_WIDTH_NAME] then
local maxWidth = (mediaType == File.IMAGE_TYPE and config.maxImageWidth) or config.maxVideoWidth
flags[Flag.IMAGE_WIDTH_NAME]:scaleDimension(flags[Flag.IMAGE_DPR_NAME].value, maxWidth)
local dpr = flags[Flag.IMAGE_DPR_NAME]
if dpr and dpr.value then
for flagName, _ in pairs(flags) do
local flag = flags[flagName]
if flag.isScalable then
log('Scaling flag: ' .. flagName)
flag:scale(dpr.value)
end
end
end

local file = File.new(config, prefix, postfix, mediaId, mediaExtension, mediaType, flags)
Expand Down Expand Up @@ -140,6 +142,7 @@ local function main()

log('Original is present on local FS. Transcoding to ' .. file.cachedFilePath)
local cmd = Command.new(config, file, flags)
log('Command: ' .. cmd.command)
local executeSuccess = cmd:execute()

if executeSuccess then
Expand Down