Module:VOCALOID Songbox/new
来自Vocawiki
更多操作
此模块的文档可以在Module:VOCALOID Songbox/new/doc创建
local strings = require('Module:Strings')
local get = require('Module:Get')
local acandy = require('Module:ACandy')
local a, Fragment, Raw = acandy.a, acandy.Fragment, acandy.Raw
local div, span = a.div, a.span
local trim = mw.text.trim
---@alias Image {
--- file: string,
--- caption: string?,
--- size: string?,
---}
---@alias Date {
--- year: number,
--- month: number,
--- day: number,
---}
---@alias Platform { id: string, date: Date }
---@alias Platforms {
--- nico: Platform?,
--- bili: Platform?,
--- acfun: Platform?,
--- ncm: Platform?,
--- ytb: Platform?,
---}
---@alias Style {
--- general: string?,
--- [1]: string?,
--- [2]: string?,
--- [3]: string?,
---}
---@param str string?
---@return string?
local function clean(str)
if not str then return nil end
str = trim(str)
return str ~= '' and str or nil
end
---@param s1 string?
---@param s2 string?
---@return string?
local function merge(s1, s2)
local merged = trim((s1 or '')..(s2 or ''))
return merged ~= '' and merged or nil
end
local function raw_or_empty(v)
return v ~= nil and Raw(v) or ''
end
local c = {} -- components
---@param props {
--- image: Image?,
--- tabs: string?,
--- title: string?,
--- uploader: string?,
--- producer: string?,
--- note: string?,
--- singer: string?,
--- albums: string?,
--- platforms: Platforms,
--- submissions: string?,
--- colors: Style,
--- text_styles: Style,
---}
function c.SongBox(props, frame)
local colors = props.colors
local text_styles = props.text_styles
local function THeader(nth, content)
local th_style = colors[nth] or colors.general
if th_style then
th_style = 'background:'..th_style
else
th_style = 'background: #66CDAA; color: #FFF;'
end
return div['songbox_th'] {
style = th_style,
span {
style = 'display: flex; align-items: center;'..(text_styles[nth] or text_styles.general or ''),
content,
},
}
end
return div['songbox'] {
props.image and c.Image(props.image) or '',
raw_or_empty(props.tabs),
c.Title(props.title),
c.Note(props),
THeader(1, '演唱'),
div['songbox_td'] / raw_or_empty(props.singer),
THeader(2, props.uploader and 'UP主' or 'P主'),
div['songbox_td'] / raw_or_empty(props.uploader or props.producer),
THeader(3, props.albums and '收录专辑' or '投稿'),
div['songbox_td'] / div['songbox_submission_container']
/ (props.albums
and Raw(props.albums)
or { c.Submissions(props.platforms), raw_or_empty(props.submissions) }
),
}
end
---@param props Image
function c.Image(props)
local frame = mw.getCurrentFrame()
return Raw(frame:expandTemplate { title = '氛围图片', args = props })
end
---@param title string?
function c.Title(title)
if not title then
return div['songbox_titles unknown']
end
local titles = strings.split(title, '%s*<br%s*/?>%s*')
local main_title = table.remove(titles, 1)
return div['songbox_titles'] {
div['songbox_main-title'] / Raw(main_title),
Raw(table.concat(titles, '<br>')),
}
end
---@param props {note: string?}
function c.Note(props)
return props.note and div['songbox_note'] {
raw_or_empty(props.note),
} or ''
end
local platform_data = get(
{ key = 'nico', url = 'https://www.nicovideo.jp/watch/%s', name = 'niconico' },
{ key = 'bili', url = 'https://www.bilibili.com/video/%s', name = 'bilibili' },
{ key = 'acfun', url = 'http://www.acfun.cn/v/%s', name = 'AcFun' },
{ key = 'ncm', url = 'https://music.163.com/#/song?id=%s', name = '网易云' },
{ key = 'ytb', url = 'https://www.youtube.com/watch?v=%s', name = 'YouTube' })
---@param props Platforms
function c.Submissions(props)
local submissions = Fragment()
for platform in platform_data:filter(function (x) return props[x.key] end) do
---@cast platform { key: string, url: string, name: string }
local data = props[platform.key] ---@cast data Platform
submissions:insert(c.Card(platform, data))
end
return submissions
end
---@param platform { key: string, url: string, name: string }
---@param data Platform
function c.Card(platform, data, overrides)
overrides = overrides or {}
local frame = mw.getCurrentFrame()
local card = div[('songbox_submission %s %s'):format(platform.key, overrides.class or '')] {
div['submission_link']
/ (data.id and Raw(('[%s -{}-]'):format(platform.url:format(data.id)))),
div['submission_platform'] / Raw(overrides.title or platform.name),
div['submission_info'] {
div['submission_date']
/ Raw(('%s-%s-%s'):format(data.date.year, data.date.month, data.date.day)),
div['submission_playcount'] /
(overrides.playcount
and span['override'] / Raw(overrides.playcount)
or Raw(frame:expandTemplate {
title = platform.name .. 'Count',
args = { id = data.id },
}))
},
mw.title.getCurrentTitle()--[[@cast -?]]:inNamespace(0)
and Raw(
('[[Category:%s年投稿至%s的歌曲]]'):format(data.date.year, platform.name)
.. ('[[Category:%s月%s日投稿至%s的歌曲]]'):format(data.date.month, data.date.day, platform.name)
) or nil,
}
if mw.title.getCurrentTitle()--[[@cast -?]]:inNamespace(0) then
card['data-json'] = mw.text.jsonEncode({
id = data.id,
date = data.date,
platform = { name = platform.name }
})
end
return card;
end
local p = {
components = c,
}
local function get_by_aliases(t, ...)
local n = select('#', ...)
for i = 1, n do
local key = select(i, ...)
local item = t[key]
if item ~= nil then
return item
end
end
return nil
end
---@param date_str string?
---@return Date?
local function parse_date(date_str)
if not date_str then return nil end
local parts = strings.split(date_str, '%s*[-/年月日]%s*', true)
local year = tonumber(parts[1])
local month = tonumber(parts[2])
local day = tonumber(parts[3])
if year and month and day then
return { year = year, month = month, day = day }
else
return nil
end
end
--[[
image
圖片信息 + 图片信息
圖片大小 > 图片大小
tabs
歌曲名称
投稿时间 + 其他资料
演唱
UP主 > P主
收录专辑 > 各平台id&date + 链接
颜色
颜色1
颜色2
颜色3
文字样式
文字样式1
文字样式2
文字样式3
]]
function p.from_args(uncleaned_args)
local args = {} ---@type table<string, string | nil>
for k, v in pairs(uncleaned_args) do
args[k] = clean(v)
end
function platform_from_arg(prefix)
local id = args[prefix..'_id']
local date = parse_date(args[prefix..'_date']
or get_by_aliases(args, '投稿时间', '投稿時間'))
if not id or not date then return nil end -- TODO: 报错
return {
id = id,
date = date,
}
end
local props = {
image = args.image and {
file = args.image,
caption = merge(args['圖片信息'], args['图片信息']),
size = get_by_aliases(args, '圖片大小', '图片大小'),
},
tabs = args.tabs,
title = args['歌曲名称'],
uploader = args['UP主'],
producer = args['P主'],
note = args['其他资料'],
singer = args['演唱'],
albums = args['收录专辑'],
platforms = {
nico = platform_from_arg('nnd'),
bili = platform_from_arg('bb'),
acfun = platform_from_arg('ac'),
ncm = platform_from_arg('wyy'),
ytb = platform_from_arg('yt'),
},
submissions = args['投稿'],
colors = {
general = args['颜色'],
args['颜色1'],
args['颜色2'],
args['颜色3'],
},
text_styles = {
general = args['文字样式'],
args['文字样式1'],
args['文字样式2'],
args['文字样式3'],
}
}
return c.SongBox(props)
end
function p.from_frame(frame)
return p.from_args(frame.args)
end
function p.from_parent(frame)
local parent = frame:getParent()
return p.from_frame(parent)
end
function p.card_from_args(uncleaned_args)
local args = {} ---@type table<string, string | nil>
for k, v in pairs(uncleaned_args) do
args[k] = clean(v)
end
local conversion = {
nnd = "nico",
bb = "bili",
yt = "ytb",
wyy = "ncm",
ac = "acfun"
}
local key = conversion[args[1]] or args[1]
local data = {
id = args[2],
date = parse_date(args[3])
}
local overrides = {
title = args[4],
playcount = get_by_aliases(args, '再生', 'count'),
class = args['class']
}
for platform in platform_data:filter(function (x) return x.key == key end) do
return c.Card(platform, data, overrides)
end
end
function p.card_from_frame(frame)
return p.card_from_args(frame.args)
end
function p.card_from_parent(frame)
local parent = frame:getParent()
return p.card_from_frame(parent)
end
return p