Module:Media
Jump to navigation
Jump to search
Native-file and archive media wrappers: Template:Image, Template:Audio and Template:Video.
-- Shared author-facing media interface. File rendering and archive identity
-- verification remain with their MediaWiki handlers.
local p = {}
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local function arguments(frame)
return getArgs(frame, { trim = true, removeBlanks = true })
end
local function failure(message)
return tostring(mw.html.create('strong'):addClass('error'):wikitext(message))
end
local function dimension(value)
if value == nil then return nil end
local n = tonumber(value)
if not n or n % 1 ~= 0 or n < 1 or n > 16384 then return false end
return tostring(n)
end
local function fileTitle(value)
if not value then return nil end
local title = mw.title.new(value, 6)
if not title or title.namespace ~= 6 or title.fragment ~= '' or title.isExternal then return nil end
return title.prefixedText
end
local function archive(frame, kind, a)
local values
if kind == 'image' then
values = { a.site or '', a.sha256 or '', a.alt or '', a.width or '',
a.height or '', yesno(a.decorative) and 'yes' or 'no', a.filename or '',
a.frame or 'none', a.align or '', a.caption or '' }
elseif kind == 'audio' then
values = { a.site or '', a.label or '', a.transcript or '' }
for i = 1, 4 do
values[#values + 1] = a['source' .. i .. '_sha256'] or ''
values[#values + 1] = a['source' .. i .. '_type'] or ''
end
for i = 1, 4 do values[#values + 1] = a['source' .. i .. '_filename'] or '' end
else
values = { a.site or '', a.label or '', a.width or '', a.height or '',
yesno(a.loop) and '1' or '0', yesno(a.muted) and '1' or '0',
a.poster_sha256 or '', a.poster_filename or '' }
for i = 1, 4 do
for _, suffix in ipairs({ '_sha256', '_type', '_filename' }) do
values[#values + 1] = a['source' .. i .. suffix] or ''
end
end
end
return frame:callParserFunction('#preservation' .. kind, values)
end
local function render(frame, kind)
local a = arguments(frame)
local file = a.file or a[1]
local hasArchive = a.site or a.sha256 or a.source1_sha256
if hasArchive then
if file then return failure('Choose a wiki file or archive sources, not both.') end
if a.start or a['end'] or a.thumbtime then return failure('Clip times require a wiki file.') end
return archive(frame, kind, a)
end
local title = fileTitle(file)
if not title then return failure('A valid wiki file is required.') end
local width, height = dimension(a.width), dimension(a.height)
if width == false or height == false then
return failure('Media dimensions must be whole pixels from 1 to 16384.')
end
local options = { title }
if kind == 'image' then
local formats = { none = true, thumb = true, frame = true, frameless = true }
local aligns = { left = true, right = true, center = true, none = true }
if a.frame and not formats[a.frame] then return failure('Invalid image frame.') end
if a.align and not aligns[a.align] then return failure('Invalid image alignment.') end
if a.frame and a.frame ~= 'none' then options[#options + 1] = a.frame end
if a.align then options[#options + 1] = a.align end
if yesno(a.decorative) and a.alt then return failure('A decorative image must have empty alternative text.') end
if a.alt or yesno(a.decorative) then options[#options + 1] = 'alt=' .. (a.alt or '') end
if yesno(a.decorative) then options[#options + 1] = 'link=' end
end
if width or height then
options[#options + 1] = (width or '') .. (height and 'x' .. height or '') .. 'px'
end
if kind == 'image' and a.caption then options[#options + 1] = a.caption end
if kind ~= 'image' then
if yesno(a.loop) then options[#options + 1] = 'loop' end
if yesno(a.muted) then options[#options + 1] = 'muted' end
for _, key in ipairs({'start', 'end', 'thumbtime'}) do
if a[key] then options[#options + 1] = key .. '=' .. a[key] end
end
end
local rendered = '[[' .. table.concat(options, '|') .. ']]'
if kind == 'image' then
local tag = (a.frame == 'thumb' or a.frame == 'frame' or a.align) and 'div' or 'span'
return frame:extensionTag('templatestyles', '', { src = 'Module:Media/styles.css' }) ..
tostring(mw.html.create(tag):addClass('remilia-image'):wikitext(rendered))
end
if not a.label then return failure('An accessible media label is required.') end
-- Playback remains user initiated. Native player capabilities and available
-- source formats come from TimedMediaHandler and the file repository.
if a.poster_sha256 or a.poster_filename then
return failure('Source-poster options require archive sources.')
end
local node = mw.html.create('div'):addClass('remilia-media'):addClass('remilia-media-' .. kind)
:attr('role', 'group'):attr('aria-label', a.label):wikitext(rendered)
node:tag('div'):addClass('remilia-media-caption'):wikitext(a.label)
if a.transcript then node:tag('div'):addClass('remilia-media-transcript'):wikitext(a.transcript) end
return frame:extensionTag('templatestyles', '', { src = 'Module:Media/styles.css' }) .. tostring(node)
end
function p.image(frame) return render(frame, 'image') end
function p.audio(frame) return render(frame, 'audio') end
function p.video(frame) return render(frame, 'video') end
return p