跳转到内容

Module:Soundtrack

来自Undertale Wiki

此模組用來生成原聲帶列表。


--- Handles track title, number, order and motif inference.
--  @module             soundtrack
--  @alias              p
--  @require            Module:User error
--  @author             [[User:en:KockaAdmiralac|KockaAdmiralac]]
--  <nowiki>
-- <nowiki>
local p = {}

local data = mw.loadData('Module:Soundtrack/data')
local title = mw.title.getCurrentTitle()

--  Private logic

--- 將曲目名稱轉換為對應於該曲目的 Bandcamp 網址段。
--  @function           bandcamp
--  @param              {string} track 曲目名稱
--  @return             {string} 對應於該曲目的 Bandcamp 網址段

local function bandcamp(track)
    local _
    track, _ = mw.ustring.gsub(track, ' ', '-')
    track, _ = mw.ustring.gsub(track, '[^a-zA-Z0-9-]', '')
    return mw.ustring.lower(track)
end


--- 根據某專輯中曲目的索引與總曲目數量,回傳該曲目的分類排序鍵值。
--  @function           sortkey
--  @param              {string} album 專輯名稱
--  @param              {number} trackNum 曲目編號
--  @return             {string} 用於排序的補零曲目編號
local function sortkey(album, trackNum)
    local numTracks = data.albums[album].length
    if numTracks > 100 then
        return string.format('%03d', trackNum)
    elseif numTracks > 10 then
        return string.format('%02d', trackNum)
    else
        return trackNum
    end
end

--- 以以下方式標準化曲目名稱:
--  1. 將曲目名稱轉為小寫(以便正確匹配 "sans.")
--  2. 移除底線(以便正確匹配 "Soulmate_Locatedalbums")
--  3. 去除首尾空白(以便正確匹配 "END OF THE LINE_")
--  這用於將曲目名稱與其對應的條目名稱進行匹配。
--  @function           normalizeTrackName
--  @param              {string} name 要標準化的曲目名稱
--  @return             {string} 標準化後的曲目名稱
local function normalizeTrackName(name)
    local lowerName = mw.ustring.lower(name)
    local noUnderscores, _ = mw.ustring.gsub(lowerName, '_', ' ')
    local trimmed = mw.text.trim(noUnderscores)
    return trimmed
end

---原文名稱 → 翻譯條目。
--  @function           En2Zh
--  @param              {string} 英文
--  @return             {string} 中文
local function En2Zh(En)
    return data.zhtitle[En] or En
end

--- 翻譯條目 → 原文名稱。
--  @function           Zh2En
--  @param              {string} 中文名稱
--  @return             {string} 英文名稱
local function Zh2En(Zh)
    return data.reverseMapping[Zh] or Zh
end

--- 翻譯條目 → 原文名稱 (回傳列表用)。
function p.Zh2En(frame)
    return Zh2En(frame.args[1] or title.fullText)
end

--- 回傳一首曲目出現在幾張專輯中。
--  @function           albumCount
--  @param              {string} track 曲目名稱
--  @return             {number} 該曲目出現的專輯數量
local function albumCount(track)
    local count = 0
    for _, __ in pairs(data.tracks[track]) do
        count = count + 1
    end
    return count
end

--  Package items.

--- 回傳指定頁面與專輯下的曲目編號。
--  @function           p.trackNum
--  @param              {string} page 條目名稱
--  @return             {string} 指定頁面與專輯下的曲目編號
function p.trackNum(frame)
    local pageName = frame.args[1]
    local albumName = frame.args[2]
    return data.tracks[Zh2En(pageName)][Zh2En(albumName)]
end

--- 回傳指定頁面之曲目在所有專輯中的曲目編號清單。
--  @function           p.trackNumList
--  @param              {table} frame Scribunto 框架物件
--  @return             {string} 所有專輯中曲目編號的 Wikitext 清單
function p.trackNumList(frame)
    local pageName = frame.args[1] or title.fullText
    local trackName = Zh2En(pageName)
    local trackNumList = {}
    local hasMultipleAlbums = albumCount(trackName) > 1
    for _, albumName in pairs(data.order) do
        local trackNum = data.tracks[trackName][albumName]
        if trackNum ~= nil then
            local trackRow = {'* ', trackNum}
            if hasMultipleAlbums then
                table.insert(trackRow, ' ([[')
                table.insert(trackRow, En2Zh(albumName))
                table.insert(trackRow, ']])')
            end
            if title.namespace == 0 then
                table.insert(trackRow, '[[Category:')
                table.insert(trackRow, En2Zh(albumName))
                table.insert(trackRow, '|')
                table.insert(trackRow, sortkey(albumName, trackNum))
                table.insert(trackRow, ']]')
            end
            table.insert(trackNumList, table.concat(trackRow))
        end
    end
    return table.concat(trackNumList, '\n')
end

--- 回傳指定頁面對應的曲目名稱。
--  @function           p.trackTitle
--  @param              {table} frame Scribunto 框架物件
--  @return             {string} 指定頁面對應的曲目名稱
function p.trackTitle(frame)
    return Zh2En(frame.args[1] or title.fullText)
end

--- 回傳所有專輯中,距離指定頁面曲目指定偏移量的曲目名稱清單,用於上一首和下一首選擇。
--  @function           p.trackNav
--  @param              {table} frame Scribunto 框架物件
--  @return             {string} 所有專輯中偏移曲目的 Wikitext 清單
function p.trackNav(frame)
    local offset = tonumber(frame.args[1])
    local pageName = frame.args[2] or title.fullText
    local trackName = Zh2En(pageName)
    local trackList = {}
    local hasMultipleAlbums = albumCount(trackName) > 1
    for _, albumName in pairs(data.order) do
        local trackNum = data.tracks[trackName][albumName]
        if trackNum ~= nil then
            local offsetTrackName = data.albums[albumName][trackNum + offset]
            if offsetTrackName ~= nil then
                if not hasMultipleAlbums then
                    return table.concat({
                        '[[',
                        En2Zh(offsetTrackName),
                        '|',
                        En2Zh(offsetTrackName):gsub('%s*%(%s*原聲帶%s*%)', ''),
                        ']]'
                    })
                end
                table.insert(trackList, table.concat({
                    '* [[',
                    En2Zh(offsetTrackName),
                    '|',
                    En2Zh(offsetTrackName):gsub('%s*%(%s*原聲帶%s*%)', ''),
                    ']] ([[',
                    En2Zh(albumName),
                    ']])'
                }))
            end
        end
    end
    return table.concat(trackList, '\n')
end
--- 回傳指定曲目中出現的主導動機清單。
--  @function           p.motifs
--  @param              {table} frame Scribunto 框架物件
--  @return             {string} 當前曲目的主導動機
function p.motifs(frame)
    local motifs = mw.loadData('Module:Soundtrack/motifs')
    local formattedMotifs = {}
    local currentTrack = frame.args[1] or title.fullText
    local normCurrentTrack = normalizeTrackName(currentTrack)
    for motif, data in pairs(motifs) do
        for _, track in ipairs(data) do
            if normCurrentTrack == normalizeTrackName(track) then
                local fmt = data.major and '' or '\'\''
                table.insert(formattedMotifs, table.concat({
                    fmt, '[[主導動機#', motif, '|', motif, ']]', fmt
                }))
            end
        end
    end
    return table.concat(formattedMotifs, '<br />')
end

--- 回傳指定曲目頁面的 Bandcamp/ Spotify 小工具。
--  @function           p.bandcamp
--  @param              {table} frame Scribunto 框架物件
--  @return             {string} 指定曲目頁面的 Bandcamp 網址段
function p.bandcamp(frame)
    local trackId = tonumber(frame.args[1])
    local spotifyId = frame.args[3]
    if trackId == nil and spotifyId == nil then
        -- No track ID was passed, do not generate any widget.
        return ''
    end
    if trackId == nil and spotifyId ~= nil then
        return tostring(mw.html.create('div'):attr({
            ['class']         = 'bandcamp-widget',
            ['data-track']    = spotifyId,
            ['data-platform'] = 'spotify'
        }):wikitext(table.concat({
        	'[https://open.spotify.com/track/',
        	spotifyId,
        	' Link]'
        })))
    end
    local pageName = frame.args[2] or title.fullText
    local trackName = Zh2En(pageName)
    for albumName, _ in pairs(data.tracks[trackName]) do
        local albumId = data.albums[albumName].id
        return tostring(mw.html.create('div'):attr({
            ['class']      = 'bandcamp-widget',
            ['data-album'] = albumId,
            ['data-track'] = trackId
        }):wikitext(table.concat({
            '[https://tobyfox.bandcamp.com/track/',
            bandcamp(trackName),
            ' Link]'
        })))
    end
end

--- 為其他平台新增連結
--  @function           p.distribution
--  @param              {table} frame Scribunto frame object
--  @return             {string} Div element with relevant track icon links

function p.distribution(frame)
    local disttype = frame.args[1]
    local args = frame:getParent().args
    local links = {'<div class="soundtrack-links">'}
    local hasAny = false
    for platform, pdata in pairs(data.platforms) do
        local id = args[platform]
        if id ~= nil then
        	hasAny = true
            table.insert(links, '[[File:')
            table.insert(links, pdata.icon)
            table.insert(links, '|32px|')
            table.insert(links, pdata.title)
            table.insert(links, '|link=')
            table.insert(links, pdata[disttype])
            table.insert(links, id)
            table.insert(links, ']]')
        end
    end
    if not hasAny then
        return ''
    end
    table.insert(links, '</div>')
    return table.concat(links)
end

--- 自動為提供的作者名稱清單加上連結。
--  @function           p.authors
--  @param              {table} frame Scribunto 框架物件
--  @return             {string} 加上連結的作者名稱清單
function p.authors(frame)
	local authors = frame.args[1]
	local processedAuthors = {}
	for author in mw.text.gsplit(authors, ', ', true) do
		if data.authors[author] then
			table.insert(processedAuthors, table.concat({
				'[[', data.authors[author], '|', author, ']]'
			}))
		else
			table.insert(processedAuthors, author)
		end
	end
	return table.concat(processedAuthors, '<br />')
end

return p