打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

Module:VOCALOID Songbox

来自Vocawiki

此模块的文档可以在Module:VOCALOID Songbox/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 Platform { id: string }

---@alias Platforms {
---	nico: Platform?,
---	bili: Platform?,
---	acfun: Platform?,
---	ncm: Platform?,
---	ytb: Platform?,
---	other_info: string?,
---}

---@alias Style {
---	general: string?,
---	[1]: string?,
---	[2]: string?,
---	[3]: string?,
---}

---@param str string | nil
---@return string | nil
local function clean(str)
	if not str then return nil end
	str = trim(str)
	return str ~= '' and str or nil
end

---@param s1 string | nil
---@param s2 string | nil
---@return string | nil
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?,
---	uploaded_at: string?,
---	views: string?,
---	note: string?,
---	singer: string?,
---	platforms: Platforms,
---	colors: Style,
---	text_styles: Style,
---}
function c.SongBox(props)
	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'] / (props.albums and Raw(props.albums) or c.Links(props.platforms)),
	}
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 {uploaded_at: string?,	views: string?, note: string?}
function c.Note(props)
	local content = ''
	if props.uploaded_at then
		content = '于'..props.uploaded_at..'投稿'
	end
	if props.views then
		if #content > 0 then
			content = content..',再生数为'..props.views
		else
			content = '再生数为'..props.views
		end
	end
	if props.note then
		if #content > 0 then
			content = content..' '..props.note
		else
			content = props.note
		end
	end

	return content ~= '' and div['songbox_note'] {
		raw_or_empty(content),
	} or ''
end

---@param props Platforms
function c.Links(props)
	local links = Fragment()
	for platform in get(
		{ key = 'nico', url = 'https://www.nicovideo.jp/watch/%s', name = 'Nicovideo' },
		{ 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' }
	):filter(function (x) return props[x.key] end) do
		local data = props[platform.key]  ---@cast data Platform
		links:insert(
			Raw(('[%s %s]&nbsp; '):format(platform.url:format(data.id), platform.name))
		)
	end
	if props.other_info then
		links:insert(Raw(props.other_info))
	end
	return links
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

--[[
image
圖片信息 + 图片信息
圖片大小 > 图片大小
tabs

歌曲名称
再生 + 投稿时间 + 其他资料
演唱
UP主 > P主
收录专辑 > nnd_id + bb_id + ac_id + wyy_id + yt_id + 链接

颜色
颜色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

	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主'],
		uploaded_at = args['投稿时间'],
		views = args['再生'],
		note = args['其他资料'],
		singer = args['演唱'],
		albums = args['收录专辑'],
		platforms = {
			nico = args.nnd_id and { id = args.nnd_id },
			bili = args.bb_id and { id = args.bb_id },
			acfun = args.ac_id and { id = args.ac_id },
			ncm = args.wyy_id and { id = args.wyy_id },
			ytb = args.yt_id and { id = args.yt_id },
			other_info = 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

return p