Module:Infobox Music

Documentation for this module may be created at Module:Infobox Music/doc

-- <nowiki>
--------------------------
-- Module for [[Template:Infobox Music]]
-- Please test changes to this module at [[Module:Infobox Music/sandbox]] first
------------------------
local p = {}

local onmain = require('Module:Mainonly').on_main
local yesno = require('Module:Yesno')
-- local empty = require('Module:Paramtest').is_empty
local infobox = require('Module:Infobox')
local sortkeys = require('Module:Infobox Music/sortkeys')
local cleanimg = require('Module:Clean image').clean

function p.main(frame)
	local args = frame:getParent().args
	return p._main(args)
end

-- =p._main{name='Citadel XVI'}
function p._main(args)
	local ret = infobox.new(args)

	ret:defineParams{
		{ name = 'number', func = { name = numberarg, params = { 'number' }, flag = { 'p' } } },
		{ name = 'number_smw', func = { name = numbersmwarg, params = { 'number' }, flag = { 'p' } } },
		{ name = 'name', func = { name = namearg, params = { 'name', 'number' }, flag = { 'p', 'd' } } },
		{ name = 'file', func = { name = filearg, params = { 'file' }, flag = { 'p' } } },
		{ name = 'release', func = 'release' },
		{ name = 'update', func = 'has_content' },
		{ name = 'removal', func = 'removal' },
		{ name = 'members', func = { name = membersarg, params = { 'members' }, flag = { 'p' } } },
		{ name = 'location', func = 'has_content' },
		{ name = 'quest', func = 'has_content' },
		{ name = 'platform', func = 'has_content' },
		{ name = 'album', func = 'has_content' },
		{ name = 'hint', func = 'has_content' },
		{ name = 'vocals', func = 'has_content' },
		{ name = 'instruments', func = 'has_content' },
		{ name = 'duration', func = { name = durationarg, params = { 'duration' }, flag = { 'p' } } },
		{ name = 'composer', func = 'has_content' },
		{ name = 'soundcloud', func = { name = soundcloudarg, params = { 'soundcloud' }, flag = { 'p' } } },
		{ name = 'scexclusive', func = { name = scexclusivearg, params = { 'scexclusive' }, flag = { 'p' } } }, 
		{ name = 'downloads', func = { name = downloadsarg, params = { 'downloads' }, flag = { 'p' } } }, 
		{ name = 'map', func = { name = maparg, params = { 'map' }, flag = { 'd' } } },
		{ name = 'intmap', func = { name = intmaparg, params = { 'intmap' }, flag = { 'd' } } },
		{ name = 'mapdisp', func = { name = mapdisp, params = { 'map', 'intmap' }, flag = { 'd', 'd' } }, dupes = true },
	}
	
	ret:useSMW({
		members = 'Is members only',
		release = 'Release date',
		update = 'Release update',
		--removal = 'Removal date',
		removalupdate = 'Removal update'
	})

	ret:useSMWOne({
		number_smw = 'Music number'
	})
	
	ret:create()
	ret:cleanParams()
	
	ret:defineLinks({ links = {{ 'Template:%s/doc', 'Infobox' },
		{ 'Template_talk:%s', 'Talk page' }}, colspan = 2 })
		
	ret:defineName('Infobox Music')
	ret:addClass('infobox-music')
	
	-- Since the name is a combination of the track name and number,
	-- need to duplicate Module:Infobox's name-comparison logic here
	-- in order to set the 'no-parenthesis-style' if appropriate
	if args.name then
		if mw.text.decode(args.name) == mw.title.getCurrentTitle().fullText then
			ret:addClass('no-parenthesis-style')
		end
	end
	
	if onmain() then
		local a2 = ret:categoryData()
	end
	
	-- PARAMETER: name
	ret:addRow{
		{ tag = 'argh', content = 'name', class = 'infobox-header', colspan = '2',
			css = { ['border-bottom'] = '0' } } }
	
	-- PARAMETER: file
	if ret:paramDefined('file') then
		ret:addRow{
			{ tag = 'argh', content = 'file', class = 'infobox-image', colspan = '2',
				css = { ['text-align'] = 'center',
						['padding'] = '3px 0 0.2em 0',
						['border-right'] = '0'
					}
			} }
	end
	
	if ret:paramGrep('scexclusive', 'yes') then
		ret:addRow{ { tag = 'td', content = '[[List of SoundCloud exclusive music|SoundCloud-exclusive music track]]',css = { ['text-align'] = 'center', ['font-weight'] = 'bold' }, colspan = '2' } }
	else
		-- PARAMETER: release
		-- (update included automatically by infobox)
		ret:addRow{ { tag = 'th', content = '[[List of music by release date|Release]]' },
					{ tag = 'argd', content = 'release' } }
		
		-- PARAMETER: removal
		if ret:paramDefined('removal') then
			ret:addRow{ { tag = 'th', content = 'Removal' },
					{ tag = 'argd', content = 'removal' } }
		end
		
		-- PARAMETER: members
		ret:addRow{
			{ tag = 'th', content = '[[Members]]' },
			{ tag = 'argd', content = 'members' } }
		
		-- PARAMETER: location
		ret:addRow{
			{ tag = 'th', content = '[[Locations|Location]]' },
			{ tag = 'argd', content = 'location' } }
		
		-- PARAMETER: quest
		if ret:paramDefined('quest') then
			ret:addRow{
				{ tag = 'th', content = '[[Quest]]' },
				{ tag = 'argd', content = 'quest' } }
		end
	end

	ret:addRow{
		{ tag = 'th', content = 'Details',
			class = 'infobox-subheader', colspan = '2' } }
	
	-- PARAMETER: duration
	ret:addRow{
		{ tag = 'th', content = 'Duration' },
		{ tag = 'argd', content = 'duration' } }
	
	-- PARAMETER: platform
	if ret:paramDefined('platform') then
		ret:addRow{
			{ tag = 'th', content = 'Platforms' },
			{ tag = 'argd', content = 'platform' } }
	end
	
	-- PARAMETER: album
	if ret:paramDefined('album') then
		ret:addRow{
			{ tag = 'th', content = 'Album' },
			{ tag = 'argd', content = 'album' } }
	end
	
	-- PARAMETER: vocals
	if ret:paramDefined('vocals') then
		ret:addRow{
			{ tag = 'th', content = 'Vocals' },
			{ tag = 'argd', content = 'vocals' } }
	end
	
	-- PARAMETER: instruments
	ret:addRow{
		{ tag = 'th', content = 'Instruments' },
		{ tag = 'argd', content = 'instruments' } }
	
	-- PARAMETER: composer
	ret:addRow{
		{ tag = 'th', content = 'Composer' },
		{ tag = 'argd', content = 'composer' } }
		
	-- PARAMETER: soundcloud
	if ret:paramDefined('soundcloud') then
		ret:addRow{
			{ tag = 'th', content = 'SoundCloud' },
			{ tag = 'argd', content = 'soundcloud' } }
	end
	
	-- PARAMETER: downloads
	if ret:paramDefined('downloads') then
		ret:addRow{
			{ tag = 'th', content = 'Downloads' },
			{ tag = 'argd', content = 'downloads' } }
	end
	
	if not ret:paramGrep('scexclusive', 'yes') and not ret:paramGrep('downloads', 'yes') then
		-- ret = ret --noop i guess because i don't know how false checking works in lua
	--else
		ret:addRow{
			{ tag = 'th', content = 'Unlock hint',
				class = 'infobox-subheader', colspan = '2' } }
		
		-- PARAMETER: hint
		if args.number and (string.lower(args.number) ~= 'n/a') or not args.number then
			ret:addRow{
				{ tag = 'argd', content = 'hint',
					css = { ['text-align'] = 'center' }, colspan = '2' } }
		else
			ret:addRow{
				{ tag = 'td', content = 'This track is unlisted and cannot be unlocked.',
					css = { ['text-align'] = 'center' }, colspan = '2' } }
		end
			
		-- PARAMETER: map
		if ret:paramGrep('mapdisp', function(x) return x == true end) then
			local mapcontent = (ret:paramDefined('map') and 'map') or (ret:paramDefined('intmap') and 'intmap')
			ret:addRow{
				{ tag = 'argd', content = mapcontent, class = 'infobox-map', colspan = '2' } }
		end
	end
		
	ret:finish()
	if onmain() then
		-- Add a category sort key for music tracks that need it
		local name = args.name or string.match( ret:param('name', 'd'), '(.-)<span' )
		local sortkey = sortkeys[name]
		local defaultsort = sortkey and mw.getCurrentFrame():callParserFunction{ name = 'DEFAULTSORT', args = { sortkey } } or ''
		local a2 = ret:categoryData()
		ret:wikitext( defaultsort )
		ret:wikitext( addcategories( ret, a2 ) )
	end

	return ret:tostring()
	
end

function namearg(name, number)
	if not infobox.isDefined(name) then
		name = mw.title.getCurrentTitle().text
	end
	return name .. number
end

function numberarg(arg)
	if infobox.isDefined(arg) then
		if tonumber(arg) then
			return '<span style="font-size: 80%;"> (#' .. arg .. ')</span>'
		end
		if string.lower(arg) == 'n/a' or string.lower(arg) == 'no' then
			return ''
		end
	else
		return ''
	end
end

function numbersmwarg(arg)
	if infobox.isDefined(arg) and tonumber(arg) then
		return arg
	end
end

function filearg(arg)
	if string.match(arg, "(\.ogg)$") or string.match(arg, "(\.mp3)$") then
		frame = mw.getCurrentFrame()
		return frame:expandTemplate{ title = 'Listen inline', args = { filename = arg } }
	end
end

function membersarg(arg)
	return (string.lower(arg) == 'yes' and 'Yes') or (string.lower(arg) == 'no' and 'No') or nil
end

function durationarg(arg)
	if string.match(arg, "^(%d?%d:%d%d)$") then
		return arg
	end
end

function soundcloudarg(arg)
	_arg = string.gsub(arg, 'https?://www%.', 'https://')
	if string.match(_arg, "^https://soundcloud%.com/runescapesoundtrack/[%l%d%-]*$") then
		return string.format('[%s Link]', arg)
	end
end

function downloadsarg(arg)
	if yesno(arg) then
		return 'Yes'
	end
	return 'No'
end

function scexclusivearg(arg)
	if yesno(arg) then
		return 'yes'
	end
	return 'no'
end

function maparg(arg)
	if infobox.isDefined(arg) then
		local low = string.lower(arg)
		if yesno(low) == false then
			return 'None'
		elseif low == 'name' then
			return string.format('[[File:%s location.png]]', mw.title.getCurrentTitle().text)
		elseif string.find(low, '.png') then
			return cleanimg{ file = arg, width = 250 }
		else
			return arg
		end
	else
		return nil
	end
end

function intmaparg(arg)
	if infobox.isDefined(arg) then
		if yesno(arg) == false then
			return 'None'
		else
			return mw.getCurrentFrame():preprocess( arg )
		end
	else
		return nil
	end
end

function mapdisp(map, intmap)
	if infobox.isDefined(intmap) or infobox.isDefined(map) then
		if ( intmap == 'None' ) and ( map == 'None' ) then
			return false
		end
		return true
	end
	return nil
end

local composers = {
	['mod ian'] = 'Music composed by Mod Ian',
	['mod grace'] = 'Music composed by Mod Grace',
	['mod bond'] = 'Music composed by Mod Bond',
	['mod lord'] = 'Music composed by Mod Lord',
	['andrew l'] = 'Music composed by Andrew L',
	['mod adam r'] = 'Music composed by Mod Adam R',
	['michael m'] = 'Music composed by Michael M',
	['iain h'] = 'Music composed by Iain H',
	['chris j'] = 'Music composed by Chris J',
	['mod dan a'] = 'Music composed by Mod Dan A',
	['james h'] = 'Music composed by James H',
	['sam j'] = 'Music composed by Sam J',
	['mod ash'] = 'Music composed by Mod Ash',
	['mod abbie'] = 'Music composed by Mod Abbie',
	['abbie d'] = 'Music composed by Mod Abbie',
	['mod slippers'] = 'Music composed by Mod Slippers',
	['helen r'] = 'Music composed by Helen R',
	['linda h'] = 'Music composed by Linda H',
	['mod surma'] = 'Music composed by Mod Surma',
	['mod jodgers'] = 'Music composed by Mod Jodgers',
}

function addcategories( ibox, catargs )
	local args = ibox.args
	local ret = { 'Music tracks' }
	
	local cat_map = {
		-- Added if the parameter has content
		defined = {
			-- param = 'category',
		},
		-- Added if the parameter has no content
		notdefined = {
			number = 'Missing track number',
			file = 'Needs audio added',
			instruments = 'Needs instruments',
			duration = 'Needs track duration added',
			composer = 'Needs composer added',
		},
		notdefined_notscex = {
			members = 'Needs members status',
			location = 'Missing track location',
			mapdisp = 'Needs map',
			release = 'Needs release date'
		}
	}
	
	if args.number then
		if tonumber( args.number ) and not args.removal then
			table.insert( ret, 'Music Player tracks' )
			if not args.hint or args.hint == '' then
				table.insert( ret, 'Missing unlock hint' )
			end
		end
		if string.lower( args.number ) == 'n/a' then
			table.insert( ret, 'Unlisted music tracks' )
		end
	end
	if yesno( args.vocals ) then
		table.insert( ret, 'Music tracks with vocals' )
	end
	if yesno( args.members ) then
		table.insert( ret, 'Members-only music tracks' )
	end
	if yesno( args.members ) == false then
		table.insert( ret, 'Free-to-play music tracks' )
	end
	if yesno( args.scexclusive ) then
		table.insert( ret, 'SoundCloud-exclusive music' )
	end
	if yesno( args.downloads ) then
		table.insert( ret, 'Download-only music' )
	end
	if args.composer then
		local complist = string.lower( args.composer )
		local compcount = 0
		for name, cat in pairs( composers ) do
			if complist:find( name ) then
				table.insert( ret, cat )
				compcount = compcount + 1
			end
		end
		if compcount > 1 then
			table.insert( ret, 'Tracks with multiple composers' )
		end
	end

	-- Run and add mapped categories
	for n, v in pairs( cat_map.defined ) do
		if catargs[n] and catargs[n].one_defined then
			table.insert( ret, v )
		end
	end
	for n, v in pairs( cat_map.notdefined ) do
		if catargs[n] and catargs[n].all_defined == false then
			table.insert( ret, v )
		end
	end
	if ibox:paramGrep('scexclusive', 'no') then
		for n, v in pairs( cat_map.notdefined_notscex ) do
			if catargs[n] and catargs[n].all_defined == false then
				table.insert( ret, v )
			end
		end
	end
	
	-- combine table and format category wikicode
	for i, v in ipairs( ret ) do
		if ( v ~= '' ) then
			ret[i] = string.format( '[[Category:%s]]', v )
		end
	end
	
	return table.concat( ret, '' )
end

return p