Module:Song

来自Lyricsinfo 歌词维基

See Also


local specialDelims = {'/', '、', '\\', '/', ' · '}
local yesno = require('Module:Yesno')

-- Trim function
function trim2(s)
    return (s:gsub("^[%z\128-\255%s]+", ""))
end

-- Function to check if a string contains Chinese characters
local function containsChinese(str)
    for i = 1, #str do
        local c = string.byte(str, i)
        if c >= 0x4E00 and c <= 0x9FFF then
            return true
        end
    end
    return false
end

-- Function to check if a string contains English letters
local function containsEnglish(str)
    for i = 1, #str do
        local c = string.byte(str, i)
        if (c >= 0x41 and c <= 0x5A) or (c >= 0x61 and c <= 0x7A) then
            return true
        end
    end
    return false
end

-- Test the functions
--local str1 = "夜夜夜夜 (Demo)"
--local str2 = "Demo (English)"
--print("String 1 contains Chinese: ", containsChinese(str1))  -- Output: true
--print("String 1 contains English: ", containsEnglish(str1))  -- Output: true
--print("String 2 contains Chinese: ", containsChinese(str2))  -- Output: false
--print("String 2 contains English: ", containsEnglish(str2))  -- Output: true

local mArguments
local p = {};     --All Lua modules on Wikipedia must begin by defining a variable 
                    --that will hold their externally accessible functions.
                    --Such variables can have whatever name you want and may 
                    --also contain various data as well as functions.

-- Function to create a mock frame object with arguments
function p.createMockFrame(args)
    local frame = {
        args = args,
        getParent = function() return nil end,
        getTitle = function() return nil end,
        callParserFunction = function() return nil end,
        preprocess = function(_, text) return text end,
        expandTemplate = function(_, tmpl) return tmpl end
    }
    return frame
end

function p.getSongTitlefromPage()
	local songtitle = ''
	local pagename = tostring(mw.title.getCurrentTitle())
	if string.match(pagename, '%(') then
		local pattern = "^(.-)%s*%([^%(%)]*%)$" -- Find the position of the last set of parentheses
		--local pattern = ".*%(([^()]+)%)$" -- This pattern captures the last set of parentheses and its content
		songtitle = string.match(pagename, pattern)
	else
		songtitle = pagename
	end
	return songtitle
end

function p.getArtistfromPage()
	local _ret = ''
	local pagename = tostring(mw.title.getCurrentTitle())
	if string.match(pagename, '%(') then
		local pattern = ".*%(([^()]+)%)$" -- This pattern captures the last set of parentheses and its content
		_ret = string.match(pagename, pattern)
	else
    	_ret = '群星'
	end 
	return _ret 
end

function p.addCat(frame)		-- Add another function
	local cat = frame.args[1] or '' -- To access arguments passed to a module, use `frame.args`
							    -- `frame.args[1]` refers to the first unnamed parameter
							    -- given to the module
    local ret = ''
    
    if cat == '' then
		ret = '[[Category:单曲歌词]] [[Category:Song Lyrics]]'
    end

	if string.match(cat, '国语') then
    	ret = ret .. '[[Category:国语歌词]]'
	end
	if string.match(cat, '粤语') then
    	ret = ret .. '[[Category:粤语歌词]]'
	end
	if string.match(cat, '闽南语') or string.match(cat, '台语') then
    	ret = ret .. '[[Category:闽南语歌词]]'
	end
	if string.match(cat, '英语') then
    	ret = ret .. '[[Category:Lyrics in English]]'
	end	
	if string.match(cat, '日语') then
    	ret = ret .. '[[Category:日本語の歌詞]]'
	end	
	if string.match(cat, '韩语') then
    	ret = ret .. '[[Category:한국어 가사]]'
	end	
	if string.match(cat, '法语') then
    	ret = ret .. '[[Category:Paroles en français]]'
	end	
	if string.match(cat, '音乐') then
    	ret = ret .. '[[Category:Instrumental]]'
	end	

	if ret == '' then
		ret = ret .. '[[Category:另类歌词/非音乐]]'
	end
	
	return ret  -- `..` concatenates strings. This will return a customized
									 -- greeting depending on the name given, such as "Hello, Fred!"
end

function p.addSongTitle(frame)
	local songtitle = frame.args[1] or ''
	if songtitle == '' then
		local pagename = tostring(mw.title.getCurrentTitle())
		if string.match(pagename, '%(') then
			local pattern = "^(.-)%s*%([^%(%)]*%)$" -- Find the position of the last set of parentheses
			--local pattern = ".*%(([^()]+)%)$" -- This pattern captures the last set of parentheses and its content
			songtitle = string.match(pagename, pattern)
		else
    		songtitle = pagename
    	end
	end 
	return songtitle
end

function p.addSongLink(frame)		-- Add Track link (input = frame)
	local ret = '—'
	local songname = frame.args[1] or ret
	
	if (songname == '' or songname == '—') then
		-- do nothing, keep ret as '—'
	elseif (string.match(songname, '%{') or string.match(songname,'%[') or string.match(songname, "\127")) then
		ret = songname -- update ret as songname, don't add links
	else
		local artist = frame.args[2] or p.getArtistfromPage()
		if artist == '' then artist = p.getArtistfromPage() end

		ret = '[[' .. songname ..'_(' .. artist .. ')|' .. songname .. ']]' -- '{{sl|' .. songname .. '}}'
	end
	
	return ret
end	

function p.addTrackNumUp(frame)		-- Add Track Seq number 上一首,曲序号减小
	local ret = ''
	local tracknum = frame.args[1] or '0'
	local currtracknum = frame.args[3] or '0'
	local songname = frame.args[2] or ret

	if (songname == '' or songname == '—') then
		-- do nothing, keep ret as ''
	elseif (tracknum == '0' or tracknum == '') then
		currtracknum = tonumber(currtracknum) or 999
		currtracknum = currtracknum - 1
		ret = '\r(' .. currtracknum .. ')'
		
	--elseif (string.match(tracknum, '%{') or string.match(tracknum,'%[') or string.match(tracknum, "\127")) then
	--	ret = '\r(' .. tracknum .. ')'-- update ret as tracknum, don't add links
	else
		--tracknum = tonumber(tracknum)
		ret = '\r(' .. tracknum .. ')'
	end
	return ret
end	

function p.addTrackNumDown(frame)		-- Add Track Seq number 下一首,曲序号增加
	local ret = ''
	local tracknum = frame.args[1] or '0'
	local currtracknum = frame.args[3] or '0'
	local songname = frame.args[2] or ret

	if (songname == '' or songname == '—') then
		-- do nothing, keep ret as ''
	elseif (tracknum == '0' or tracknum == '') then
		currtracknum = tonumber(currtracknum) or 999
		currtracknum = currtracknum + 1
		ret = '\r(' .. currtracknum .. ')'
		
	--elseif (string.match(tracknum, '%{') or string.match(tracknum,'%[') or string.match(tracknum, "\127")) then
	--	ret = '\r(' .. tracknum .. ')'-- update ret as tracknum, don't add links
	else
		--tracknum = tonumber(tracknum)
		ret = '\r(' .. tracknum .. ')'
	end
	return ret
end	

function p.getTrackNum(tracknum, currtracknum, n)		-- get Last/Next TrackNum (input = current track num)
	local ret = ''
	local tracknum = tracknum or '0'
	n = tonumber(n) or 0
	
	if (tracknum == '0' or tracknum == '') then -- if tracknum is not available
		currtracknum = tonumber(currtracknum) or 999
		tracknum = currtracknum + n
		ret = '(' .. tracknum .. ')'
	else
		ret = '(' .. tracknum .. ')'
	end
	return ret
end	

function p.getSecHeader(title, artist)		-- get Section Header from album/albumartist (input)
	local ret = ''
	if artist == '' then artist = p.getArtistfromPage() end
	
	if title == '' then -- 不是专辑,是单曲
		local args1 = { artist }
		local frame1 = p.createMockFrame(args1)
		artist = p.addDiscogsArtist(frame1)

		ret = ret .. artist .. ' · 单曲发行列表'
	else
		local args2 = { title, artist }
		local frame2 = p.createMockFrame(args2)
		ret = p.addDiscogs(frame2)
		ret = ret .. '  曲目列表'
	end
	
	return ret
end	

function p.addTrackSection(frame)		-- frame args[1] = section序号
	local ret = ''
	mArguments = require('Module:Arguments')
	local args = mArguments.getArgs(frame, {parentOnly = true})
	local k = frame.args[1] or ''
	local lastTrack = args['上一首' .. k] or ''
	local nextTrack = args['下一首' .. k] or ''
	local lastTrackNum = args['上一首曲序' .. k] or '0'
	local nextTrackNum = args['下一首曲序' .. k] or '0'
	local currTrackNum = args['曲序' .. k] or '0'
	
	if (lastTrack == '' or lastTrack == '—') then lastTrackNum = ''
	else lastTrackNum = p.getTrackNum(lastTrackNum, currTrackNum, -1) or '0' end
	if (nextTrack == '' or nextTrack == '—') then nextTrackNum = ''
	else nextTrackNum = p.getTrackNum(nextTrackNum, currTrackNum, 1) or '0' end
	currTrackNum = p.getTrackNum(currTrackNum, currTrackNum, 0) or '0' 

	local artist = args['专辑艺人' .. k] or args['艺人' .. k] or p.getArtistfromPage() or ''
	local albumtitle = args['专辑' .. k] or ''
	
	if lastTrack == '' and nextTrack == '' then
	else
		local secHeader = args['Chronology' .. k] or p.getSecHeader(albumtitle, artist)
		
		local trackArgs = { lastTrack, artist }
		local frame_track = p.createMockFrame(trackArgs)
		local lastTrackTitle = p.addSongLink(frame_track)
		
		local currTrackTitle = args['歌名'] or p.getSongTitlefromPage()
		currTrackTitle = "'''" .. currTrackTitle .. "'''"
		
		trackArgs = { nextTrack, artist }
		frame_track = p.createMockFrame(trackArgs)
		local nextTrackTitle = p.addSongLink(frame_track)

		ret = ret .. '\n|-\n! colspan="3" scope="col" class="infoheaderprime" style="font-size: 9pt" | ' .. secHeader
		ret = ret .. '\n|-\n| colspan="3" |'
		ret = ret .. '\n{| cellspacing="0" cellpadding="0" width="100%" style="background: transparent;"'
		ret = ret .. '\n|- style="text-align: center; font-size: 0.8em; line-height: 1.4em;"\n'
		ret = ret .. '| style="width: 33%; vertical-align: top; padding: 0.2em 0.1em 0.2em 0;" | ' .. lastTrackTitle .. '<br>' .. lastTrackNum .. '\n'
		ret = ret .. '| style="width: 33%; vertical-align: top; padding: 0.2em 0.1em 0.2em 0.1em;" | ' .. currTrackTitle .. '<br>' .. currTrackNum .. '\n'
		ret = ret .. '| style="width: 33%; vertical-align: top; padding: 0.2em 0 0.2em 0.1em;" | ' .. nextTrackTitle .. '<br>' .. nextTrackNum .. '\n'
		ret = ret .. '|}'
	end
	
	return ret	
end

function p.addLyricsinfoSection(frame)
	local ret = ''
	mArguments = require('Module:Arguments')
	local args = mArguments.getArgs(frame, {parentOnly = true})
	--local k = frame.args[1] or ''
	local catalog = args['整理编号'] or ''
	
	if catalog == '' then
		return ret
	else
		local team = args['分部'] or ''
		local secHeader = '[[' .. team .. '工作室|' .. team .. '歌词整理列表]]'
		local owner = args['整理人'] or '[[Lyricsinfo]]'
		local credits = args['制作信息'] or '否'
		local verify = args['校对'] or '否'

		local lastWork = args['上一张整理'] or ''
		local nextWork = args['下一张整理'] or ''
		local lastCatalog = args['上一整理编号'] or ''
		if (lastCatalog == '' or lastCatalog == '—') then lastCatalog = ''
		else lastCatalog = p.getTrackNum(lastCatalog, lastCatalog, 0) or '' end
		local nextCatalog = args['下一整理编号'] or ''
		if (nextCatalog == '' or nextCatalog == '—') then nextCatalog = ''
		else nextCatalog = p.getTrackNum(nextCatalog, nextCatalog, 0) or '' end
		local currCatalog = p.getTrackNum(catalog, catalog, 0) or '' 
		
		local single = args['歌名'] or p.getSongTitlefromPage()
		single = "'''" .. single .. "'''"
		
		ret = ret .. '\n|-\n! colspan="3" scope="col" style="background: #CECEF6;font-size: 9pt" | ' .. secHeader
		ret = ret .. '\n|-'
		ret = ret .. '\n! scope="row" style="width: 5.2em; text-align: left;font-size: 9pt" | 整理编号'
		ret = ret .. '\n| colspan="2" style="width: 14em;font-size: 9pt" |' .. catalog
		ret = ret .. '\n|-'
		ret = ret .. '\n! scope="row" style="width: 5.2em; text-align: left;font-size: 9pt" | 分部'
		ret = ret .. '\n| colspan="2" style="width: 14em;font-size: 9pt" |' .. team
		ret = ret .. '\n|-'
		ret = ret .. '\n! scope="row" style="width: 5.2em; text-align: left;font-size: 9pt" | 整理人'
		ret = ret .. '\n| colspan="2" style="width: 14em;font-size: 9pt" |' .. owner
		ret = ret .. '\n|-'
		ret = ret .. '\n! scope="row" style="width: 5.2em; text-align: left;font-size: 9pt" | 制作信息'
		ret = ret .. '\n| colspan="2" style="width: 14em;font-size: 9pt" |' .. credits
		ret = ret .. '\n|-'
		ret = ret .. '\n! scope="row" style="width: 5.2em; text-align: left;font-size: 9pt" | 校对'
		ret = ret .. '\n| colspan="2" style="width: 14em;font-size: 9pt" |' .. verify
		ret = ret .. '\n|-'
		
		ret = ret .. '\n| colspan="3" |'
		ret = ret .. '\n{| cellspacing="0" cellpadding="0" width="100%" style="background: transparent;"'
		ret = ret .. '\n|- style="text-align: center; font-size: 0.8em; line-height: 1.4em;"\n'
		ret = ret .. '| style="width: 33%; vertical-align: top; padding: 0.2em 0.1em 0.2em 0;" | ' .. lastWork .. '<br>' .. lastCatalog .. '\n'
		ret = ret .. '| style="width: 33%; vertical-align: top; padding: 0.2em 0.1em 0.2em 0.1em;" | ' .. single .. '<br>' .. currCatalog .. '\n'
		ret = ret .. '| style="width: 33%; vertical-align: top; padding: 0.2em 0 0.2em 0.1em;" | ' .. nextWork .. '<br>' .. nextCatalog .. '\n'
		ret = ret .. '|}'

	end
	
	return ret	
end

function p.getMultiISRCs(frame, delim)
    -- Split the input string by the delimiter " / "
    local segments = {}
    local isrcstr = frame.args[1] or ''

    delim = delim or ''
    isrcstr = string.gsub(isrcstr, delim, "/")
    
    for segment in string.gmatch(isrcstr, '([^/]+)') do
        -- Remove leading and trailing whitespace
        segment = segment:match("^%s*(.-)%s*$")

        table.insert(segments, segment)
    end
    return segments

end

function p.addCatAlbum(frame)		-- 自动增加专辑标签
	local nocat = yesno(frame.args[3], false)
	
	if nocat then
		return ''
	end
	
	local album = frame.args[1] 
	local tracknum = frame.args[2] or '0'
	
	tracknum = tonumber(tracknum) 

	local ret = '[[Category:'
	if string.match(album, "`UNIQ--") then
		album = string.match(album, "(.-)\127") -- remove string after DEL 
	end
	if string.match(album, '《') or string.match(album, ':w:c:zh.discogs:') then
		local pattern = "%[%[.-|(.-)%]%]" -- This pattern looks for [[ followed by any number of characters until |, then captures everything until ]]
		if (string.match(album, pattern)) then
			ret = ret .. string.match(album, pattern) .. ' (专辑)'	
		else
			ret = ret .. album .. ' (专辑)'
		end
	else
		ret = ret .. album .. ' (专辑)'
	end
	
	if tracknum then
		tracknum = p.numberToUC(tracknum) or '0'
		ret = ret .. '|' .. tracknum .. ']]'
	else
		ret = ret .. ']]'
	end
	
	return ret
end

function p.numberToUC(num)
    local utf8Bytes = {}
    
    if num >= 1 and num <= 20 then
        -- 使用带圆圈的数字字符,Unicode起始码点为0x2460
        local unicodeStart = 0x2460 + num - 1
        -- 构建UTF-8字节序列
        if unicodeStart >= 0x2460 and unicodeStart <= 0x2469 then
            utf8Bytes = {0xE2, 0x91, 0xA0 + (unicodeStart - 0x2460)}
        elseif unicodeStart >= 0x246A and unicodeStart <= 0x246F then
            utf8Bytes = {0xE2, 0x91, 0xAA + (unicodeStart - 0x246A)}
        elseif unicodeStart >= 0x2470 and unicodeStart <= 0x2479 then
            utf8Bytes = {0xE2, 0x91, 0xB0 + (unicodeStart - 0x2470)}
        end
        
    elseif num >= 21 and num <= 35 then
        local unicodeStart = 0x3251 + num - 21
        -- 构建UTF-8字节序列
        if unicodeStart >= 0x3251 and unicodeStart <= 0x3259 then
            utf8Bytes = {0xE3, 0x89, 0x91 + (unicodeStart - 0x3251)}
        elseif unicodeStart >= 0x325A and unicodeStart <= 0x325F then
            utf8Bytes = {0xE3, 0x89, 0x9A + (unicodeStart - 0x325A)}
        end
    
    elseif num >= 36 and num <= 50 then
        local unicodeStart = 0x32B1 + num - 36
        -- 构建UTF-8字节序列
        if unicodeStart >= 0x32B1 and unicodeStart <= 0x32B9 then
            utf8Bytes = {0xE3, 0x8A, 0xB1 + (unicodeStart - 0x32B1)}
        elseif unicodeStart >= 0x32BA and unicodeStart <= 0x32BF then
            utf8Bytes = {0xE3, 0x8A, 0xBA + (unicodeStart - 0x32BA)}
        end 
    
    elseif num > 50 and num <=90 then
		-- 使用带圆圈的数字字符,Unicode起始码点为0x2470
        local unicodeStart = 0x2474 + num - 51
         
        if unicodeStart >= 0x2470 and unicodeStart <= 0x2479 then
            utf8Bytes = {0xE2, 0x91, 0xB0 + (unicodeStart - 0x2470)}
        elseif unicodeStart >= 0x247A and unicodeStart <= 0x247F then
            utf8Bytes = {0xE2, 0x91, 0xBA + (unicodeStart - 0x247A)}
        elseif unicodeStart >= 0x2480 and unicodeStart <= 0x2489 then
            utf8Bytes = {0xE2, 0x92, 0x80 + (unicodeStart - 0x2480)}
        elseif unicodeStart >= 0x248A and unicodeStart <= 0x248F then
            utf8Bytes = {0xE2, 0x92, 0x8A + (unicodeStart - 0x248A)}
        elseif unicodeStart >= 0x2490 and unicodeStart <= 0x2499 then
            utf8Bytes = {0xE2, 0x92, 0x90 + (unicodeStart - 0x2490)}
        elseif unicodeStart >= 0x249A and unicodeStart <= 0x249F then
            utf8Bytes = {0xE2, 0x92, 0x9A + (unicodeStart - 0x249A)}
        end
        
    elseif num > 90 and num <=99 then
		-- 使用带圆圈的数字字符,Unicode起始码点为0x2776
        local unicodeStart = 0x2776 + num - 91  
        if unicodeStart >= 0x2776 and unicodeStart <= 0x2779 then
           utf8Bytes = {0xE2, 0x9D, 0xB6 + (unicodeStart - 0x2776)}
        elseif unicodeStart >= 0x277A and unicodeStart <= 0x277F then
           utf8Bytes = {0xE2, 0x9D, 0xBA + (unicodeStart - 0x277A)}
        end
        
    else
        return '0'
    end
    
    -- 将字节序列转换为字符
    local char = ""
    for _, byte in ipairs(utf8Bytes) do
        char = char .. string.char(byte)
    end

    return char
   
end

function p.addCatISRC(isrc)
	local ret = '[[Category:'
	if string.match(isrc, "`UNIQ--") then
		isrc = string.match(isrc, "(.-)\127") -- remove string after DEL 
	end
	local isrc_clean = isrc:gsub("-", "")
    
    -- 使用模式匹配来拆分处理过的字符串
    local isrc_m, isrc_t = isrc_clean:match("^(%a+%d+%d+%d+)(%d%d)$")
    if not isrc_m or not isrc_t then
        ret = ret .. "Invalid ISRC]]"

    else
    	isrc_m = isrc_m .. '00'
    	ret = ret .. 'ISRC ' .. isrc_m
    	
    	isrc_t = tonumber(isrc_t) or '0'
    	isrc_t = p.numberToUC(isrc_t)
    	
    	ret = ret .. '|' .. isrc_t .. ']]'
    end
    
	return ret
end

function p.addISRC(frame)		-- Add another function
	local cat = frame.args[1]  
	local nocat = yesno(frame.args[2], false)
	
	local ret = cat
	if string.match(cat, '{{ISRC') then

	elseif string.match(cat, '/') then
		ret = '<span class="ISRC">' .. cat .. '</span>'
		
		if nocat then 
		else
			local isrcs = {}
			local match = '/'
			isrcs = p.getMultiISRCs(frame, match)
			for i, c in ipairs(isrcs) do
				isrcs[i] = p.addCatISRC(c)
			end
			ret = ret .. table.concat(isrcs, " ")
		end

	else
		if nocat then
		else
			ret = ret .. p.addCatISRC(cat)
		end
	end
	
	return ret
end


function p.addCatMultiArtists(frame)		-- 自动增加多艺人标签
	local multiartiststr = frame.args[1] or ''

	local ret = multiartiststr	
	if (multiartiststr == '') then
		return ret
	else

		local match = ''
		for i, d in ipairs(specialDelims) do
			match = string.match(multiartiststr, specialDelims[i]) or match --or string.match(artist, "/") or ''
		end
		
		if match == '' then
			ret = '[[Category:' .. multiartiststr .. ']]'
		else -- 多艺人
			local artists = {}
			artists = p.getMultiArtists(frame, match)
			for i, a in ipairs(artists) do
				if string.match(a, "`UNIQ--") then
					a = string.match(a, "(.-)\127") -- remove string after DEL 
				end
				artists[i] = '[[Category:' .. a	.. ']]'
			end
			ret = table.concat(artists, " ")
		end
	end

	return ret
end

function p.addCreatePageDefault(frame)		-- Add another function
	local pagename = frame.args[1]  
    local ret = ''
	if string.match(pagename, '%(') then
		local pattern = "%([^()]+%)$" -- This pattern captures the last set of parentheses and its content
		ret = '曲名 ' .. string.match(pagename, pattern)
	else
    	ret = '曲名 (艺术家)'
	end
	return ret  
end

function p.getArtist(frame)		-- 从参数或歌词页面标题获取艺术家名
	local ret = frame.args[1] or ''
	if string.match(ret, "`UNIQ--") then
		ret = string.match(ret, "(.-)\127") -- remove string after DEL 
	end
	
	if ret == '' then
		ret = p.getArtistfromPage()
	end
	return ret  
end

function p.getMultiArtists(frame, delim)
    -- Split the input string by the delimiter " / "
    local segments = {}
    local artiststr = frame.args[1] or ''

    delim = delim or ''
    artiststr = string.gsub(artiststr, delim, "/")
    
    for segment in string.gmatch(artiststr, '([^/]+)') do
        -- Remove leading and trailing whitespace
        segment = segment:match("^%s*(.-)%s*$")
        -- Format each segment
        if string.match(segment, ':w:c:zh.discogs:') then
			local pattern2 = "%[%[.-|(.-)%]%]" -- This pattern looks for [[ followed by any number of characters until |, then captures everything until ]]
			if (string.match(segment, pattern2)) then
				segment = string.match(segment, pattern2)	
			end
		end
        --local formattedSegment = string.format("[[:w:c:zh.discogs:%s|%s]]", segment, segment)
        table.insert(segments, segment)
    end
    return segments
    -- Join the formatted segments with " / "
    --local result = table.concat(segments, " / ")
    --return result
end

function p.addDiscogs(frame)		-- Add discogs album link
	local album = frame.args[1]  
	local artist = frame.args[2] or ''
	if artist == '' then
		artist = p.getArtistfromPage()
	end
	local albumtype = frame.args[3] or '专辑'
	if albumtype == '' then albumtype = '专辑' end
	
	local ret = album
	local extra = ''
	if string.match(album, "\127")  then
		--extra = string.match(album, "\127(.-)$") -- '"`UNIQUNIQ--ref-00000003-QINU`"'
		--album = string.match(album, "(.-)\127") 
		return ret
	end

	if string.match(album, '《') then
	elseif string.match(album, '%{') then
	elseif string.match(album, '%[') then
	elseif string.match(album, ':w:c:zh.discogs:') then

	elseif artist == '' or artist == '-' or artist == '-' 
		or artist == '群星' or artist == '合辑' or artist == 'Various Artists' then
		ret = '[[:w:c:zh.discogs:' .. album .. '|' .. album .. ']]'
		
	else
		ret = '[[:w:c:zh.discogs:' .. album .. '_(' .. artist .. albumtype .. ')|' .. album .. ']]'
	end
	
	return ret
end

function p.addDiscogsArtist(frame)		-- Add discogs link to artist 
	local ret = ''
	local artist = frame.args[1] 

	--local pattern = "[" .. table.concat(specialDelims) .. "]"
	local match = ''
	for i, d in ipairs(specialDelims) do
		match = string.match(artist, specialDelims[i]) or match --or string.match(artist, "/") or ''
	end
	
	if match == '' then
		artist = p.getArtist(frame)
		ret = ret .. '[[:w:c:zh.discogs:' .. artist	.. '|' .. artist .. ']]'
	else -- 多艺人
		local artists = {}
		artists = p.getMultiArtists(frame, match)
		for i, a in ipairs(artists) do
			if string.match(a, "\127")  then
				local a0 = string.match(a, "(.-)\127") -- remove string after DEL 
				a = string.gsub(a, "\127'", "]]\127'")
				artists[i] = '[[:w:c:zh.discogs:' .. a0 .. '|' .. a
			else
				artists[i] = '[[:w:c:zh.discogs:' .. a	.. '|' .. a .. ']]'
			end
		end
		ret = table.concat(artists, " / ")
	end
	
	return ret
end

---------------- TEST ---------

function p.numberToUCtest(frame)
    local num = tonumber(frame.args[1])
    local utf8Bytes = {}
    
    if num >= 21 and num <= 35 then
        -- 使用带圆圈的数字字符,Unicode起始码点为0x2460
        local unicodeStart = 0x3251 + num - 21
        -- 构建UTF-8字节序列
        if unicodeStart >= 0x3251 and unicodeStart <= 0x3259 then
            utf8Bytes = {0xE3, 0x89, 0x91 + (unicodeStart - 0x3251)}
        elseif unicodeStart >= 0x325A and unicodeStart <= 0x325F then
            utf8Bytes = {0xE3, 0x89, 0x9A + (unicodeStart - 0x325A)}
        end
    
    elseif num >= 36 and num <= 50 then
        local unicodeStart = 0x32B1 + num - 36
        -- 构建UTF-8字节序列
        if unicodeStart >= 0x32B1 and unicodeStart <= 0x32B9 then
            utf8Bytes = {0xE3, 0x8A, 0xB1 + (unicodeStart - 0x32B1)}
        elseif unicodeStart >= 0x32BA and unicodeStart <= 0x32BF then
            utf8Bytes = {0xE3, 0x8A, 0xBA + (unicodeStart - 0x32BA)}
        end 

    else
    	return '0'
    end
    
    -- 将字节序列转换为字符
    local char = ""
    for _, byte in ipairs(utf8Bytes) do
        char = char .. string.char(byte)
    end

    return char
end

----------- TEST ENDS ----

function p.addLRCSection(frame)
	local ret = ''
	
	local disclaimer = '关于LRC动态歌词的更多信息,请参考[[Lyricsinfo:LRC]]。Lyricsinfo维基站的LRC动态歌词经[[Lyricsinfo:Foobar2000|Foobar2000]]校对,如需获得最佳体验,请参考和采用[[Lyricsinfo:LRC#推荐配置|推荐配置]]。'
	
	ret = '<small>' .. disclaimer .. '</small>'
	return ret
end

function p.addLRCVideo(frame)
	
	local vid = frame.args[1] or ''
	local songtitle = frame.args[2] or ''
	local artist = frame.args[3] or ''
	local detail = frame.args[4] or ''
	local pagename = tostring(mw.title.getCurrentTitle())
	local pos = frame.args.pos or 'right'
	
	if songtitle == '' then
		songtitle = p.getSongTitlefromPage()
	end
	if artist == '' then
		artist = p.getArtistfromPage()
	end
	if detail == '' then
		detail = 'CD版+动态滚动歌词'
	end
	
	local ret = ''

	if vid == '' then
	elseif string.match(vid, 'BV') then
		local header = frame.args.header 
		if header == '' then header = songtitle .. '|Lyric Video' end
		
		ret = '<div style="float:' .. pos .. '; clear:' .. pos .. '; width:max-content;"><tabber>' .. header .. '={{BilibiliVideo|' .. vid .. '|title=' .. header .. '}}</tabber></div>'

	else
		ret = '[[File:' .. vid .. ' ' .. artist .. '|' .. songtitle .. '|Lyric Video|' .. detail .. '|right|300px]]\n'
	end

	ret = ret .. '\n详见:-{[[' .. pagename .. '/lrc]]}-\n'
	ret = frame:preprocess(ret)
    
    return ret
end

function p.addLRCCode(frame)
	local ret = ''
	local logtext = frame.args[1] or ''
	
	if (logtext == '') then
		ret = p.addLRCSection(frame)
	else
		ret = '<poem class="lrc">' .. logtext .. '</poem>'
		ret = frame:preprocess(ret)
		ret = ret .. p.addLRCSection(frame)
	end
	
	return ret
end

-------------------------------------------------------------------


local function lucky(a, b) -- One can define custom functions for use. Here we define a function 'lucky' that has two inputs a and b. The names are of your choice.
	if b == 'yeah' then -- Condition: if b is the string 'yeah'. Strings require quotes. Remember to include 'then'.
		return a .. ' is my lucky number.' -- Outputs 'a is my lucky number.' if the above condition is met. The string concatenation operator is denoted by 2 dots.
	else -- If no conditions are met, i.e. if b is anything else, output specified on the next line.  'else' should not have 'then'.
		return a -- Simply output a.
	end -- The 'if' section should end with 'end'.
end -- As should 'function'.

function p.Name2(frame)
	-- The next five lines are mostly for convenience only and can be used as is for your module. The output conditions start on line 50.
	local pf = frame:getParent().args -- This line allows template parameters to be used in this code easily. The equal sign is used to define variables. 'pf' can be replaced with a word of your choice.
	local f = frame.args -- This line allows parameters from {{#invoke:}} to be used easily. 'f' can be replaced with a word of your choice.
	local M = f[1] or pf[1] -- f[1] and pf[1], which we just defined, refer to the first parameter. This line shortens them as 'M' for convenience. You could use the original variable names.
	local m = f[2] or pf[2] -- Second shortened as 'm'.
	local l = f.lucky or pf.lucky -- A named parameter 'lucky' is shortend as l. Note that the syntax is different from unnamed parameters.
	if m == nil then -- If the second parameter is not used.
		return 'Lonely' -- Outputs the string 'Lonely' if the first condition is met.
	elseif M > m then -- If the first condition is not met, this line tests a second condition: if M is greater than m.
		return lucky(M - m, l) -- If the condition is met, the difference is calculated and passed to the self defined function along with l. The output depends on whether l is set to 'yeah'.
	else
		return 'Be positive!'
	end
end


return p    --All modules end by returning the variable containing their functions to Wikipedia.
-- Now we can use this module by calling {{#invoke: Example | hello }},
-- {{#invoke: Example | hello_to | foo }}, or {{#invoke:Example|count_fruit|bananas=5|apples=6}}
-- Note that the first part of the invoke is the name of the Module's wikipage,
-- and the second part is the name of one of the functions attached to the 
-- variable that you returned.

-- The "print" function is not allowed in Wikipedia.  All output is accomplished
-- via strings "returned" to Wikipedia.