Module:Cast and crew
Appearance
This module depends on the following other modules: |
Supporting module for Module:Film; implements {{cast and crew}}.
require('strict')
local p = {}
local getArgs = require('Module:Arguments').getArgs
local error_message = require('Module:Error')['error']
local function getLink(args)
-- Get the Wikidata item ID from the frame arguments
local wikidataItem = args[1]
if not wikidataItem then
return error_message({'[[Module:Cast and crew]] error: no Wikidata item provided'})
end
-- Query Wikidata for sitelinks
local entity = mw.wikibase.getEntity(wikidataItem)
if not entity then
return error_message({'[[Module:Cast and crew]] error: invalid or nonexistent Wikidata item'})
end
local label = entity:getLabel('en') or wikidataItem
-- Check for an English Wikisource link
local enwikisourceLink = entity:getSitelink('enwikisource')
if enwikisourceLink then
return '[[' .. enwikisourceLink .. '|' .. label .. ']]'
end
-- Check for an English Wikipedia link
local enwikiLink = entity:getSitelink('enwiki')
if enwikiLink then
return '[[w:' .. enwikiLink .. '|' .. label .. ']]'
end
-- Fallback to a direct Wikidata link with label
return '[[d:' .. wikidataItem .. '|' .. label .. ']]'
end
function p._getCastList(args)
local wikidataItem = args[1]
if not wikidataItem then
return error_message({'[[Module:Cast and crew]] error: no Wikidata item provided'})
end
-- Query Wikidata for cast member claims (P161)
local entity = mw.wikibase.getEntity(wikidataItem)
if not entity or not entity.claims then
return ''
end
local castMembers = entity.claims['P161']
if not castMembers then
castMembers = entity.claims['P725'] -- voice actors
end
if not castMembers then
return ''
end
local leadingActors = {}
local otherCastMembers = {}
local uncreditedActors = {}
for _, castMember in ipairs(castMembers) do
local castMemberQID = (castMember.mainsnak.datavalue and castMember.mainsnak.datavalue.value.id) or nil
local roleClaims = (castMember.qualifiers and castMember.qualifiers['P453']) or nil
local characteristicClaims = (castMember.qualifiers and castMember.qualifiers['P1552']) or nil
local role = ''
local uncredited = false
local isLeadingActor = false
-- Get the role if available
if roleClaims and roleClaims[1].datavalue and roleClaims[1].datavalue.value then
local roleQID = roleClaims[1].datavalue.value.id
if roleQID == 'Q18086706' then
role = 'Self'
elseif roleQID then
role = getLink({roleQID})
end
end
-- Check for characteristics (leading actor, uncredited)
if characteristicClaims then
for _, characteristic in ipairs(characteristicClaims) do
local characteristicQID
if characteristic and characteristic.datavalue and characteristic.datavalue.value then
characteristicQID = characteristic.datavalue.value.id
end
if characteristicQID == 'Q1765879' then -- Leading actor
isLeadingActor = true
elseif characteristicQID == 'Q16582801' or characteristicQID == 'Q122392315' then -- Uncredited
uncredited = true
end
end
end
-- Generate link for the cast member
local castMemberLink = getLink({castMemberQID})
if uncredited then
castMemberLink = castMemberLink .. ' (uncredited)'
end
-- generate table cells
local castMemberCells = '<tr><td>' .. role .. '</td><td>' .. castMemberLink .. '</td></tr>'
-- Sort leading actors, other cast members, and uncredited actors
if isLeadingActor then
table.insert(leadingActors, castMemberCells)
elseif uncredited then
table.insert(uncreditedActors, castMemberCells)
else
table.insert(otherCastMembers, castMemberCells)
end
end
-- Combine leading actors, other cast members, and uncredited actors
local castList = table.concat(leadingActors) .. table.concat(otherCastMembers) .. table.concat(uncreditedActors)
-- Return the formatted table if there are cast members
if castList ~= '' then
return '<tr><th colspan="2">Cast</th></tr><tr><th>Role</th><th>Actor</th></tr>' .. castList
else
return ''
end
end
function p.getCastList(frame)
return p._getCastList(getArgs(frame))
end
function p._getCrewList(args)
local properties = {
{"Production company", "P272"},
{"Distributor", "P750"},
{"Director", "P57"},
{"Producer", "P162"},
{"Screenwriter", "P58"},
{"Cinematographer", "P344"},
{"Editor", "P1040"},
{"Composer", "P86"},
{"Animator", "P6942"},
{"Production designer", "P2554"},
{"Costume designer", "P2515"},
{"Storyboard artist", "P3275"},
}
local deathYearNeeded = {
["Director"] = true,
["Producer"] = true,
["Screenwriter"] = true,
["Animator"] = true,
["Cinematographer"] = true,
["Composer"] = true
}
local wikidataItem = args[1]
if not wikidataItem then
return error_message({'[[Module:Cast and crew]] error: no Wikidata item provided'})
end
local entity = mw.wikibase.getEntity(wikidataItem)
if not entity or not entity.claims then
return ''
end
local crewTableRows = {}
local latestDeathYear = 0
local deathYears = {}
local releaseDateClaim = entity.claims['P577'] and entity.claims['P577'][1]
local releaseDate = releaseDateClaim and releaseDateClaim.mainsnak.datavalue.value.time
local releaseYear = releaseDate and releaseDate:match('+([0-9]+)')
local isSilentFilm = false
if releaseYear and tonumber(releaseYear) >= 1926 then
local instanceOfClaims = entity.claims['P31'] or {}
local genreClaims = entity.claims['P136'] or {}
for _, claim in ipairs(instanceOfClaims) do
if not isSilentFilm and claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value.id == 'Q226730' then
isSilentFilm = true
break
end
end
if not isSilentFilm then
for _, claim in ipairs(genreClaims) do
if not isSilentFilm and claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value.id == 'Q226730' then
isSilentFilm = true
break
end
end
end
end
for _, prop in ipairs(properties) do
local role, propertyId = unpack(prop)
local crewMembers = entity.claims[propertyId]
if crewMembers then
local crewMemberLinks = {}
for _, crewMember in ipairs(crewMembers) do
if crewMember and crewMember.mainsnak and crewMember.mainsnak.datavalue and crewMember.mainsnak.datavalue.value then
local crewMemberQID = crewMember.mainsnak.datavalue.value.id
local crewMemberLink = getLink({crewMemberQID})
if deathYearNeeded[role] and not deathYears[crewMemberQID] then
local deathDateClaims = mw.wikibase.getBestStatements(crewMemberQID, 'P570')
if deathDateClaims and deathDateClaims[1] and deathDateClaims[1].mainsnak and deathDateClaims[1].mainsnak.datavalue and deathDateClaims[1].mainsnak.datavalue.value then
local deathDate = deathDateClaims[1].mainsnak.datavalue.value['time']
if deathDate then
local deathYear = deathDate:match('+([0-9]+)')
if deathYear then
crewMemberLink = crewMemberLink .. ' (d. ' .. deathYear .. ')'
deathYears[crewMemberQID] = deathYear
latestDeathYear = math.max(latestDeathYear, tonumber(deathYear))
end
end
end
end
table.insert(crewMemberLinks, crewMemberLink)
end
end
if #crewMemberLinks > 0 then
table.insert(crewTableRows, '<tr><td>' .. role .. '</td><td>' .. table.concat(crewMemberLinks, ', ') .. '</td></tr>')
end
end
end
if #crewTableRows == 0 then
return ''
end
table.insert(crewTableRows, 1, '<tr><th colspan="2">Crew</th></tr>')
if latestDeathYear > 0 then
local currentYear = tonumber(os.date('*t').year)
local pmaYears = currentYear - latestDeathYear - 1
local footnoteText = string.format('Based on available information, the latest crew member that is relevant to international copyright laws died in %d, meaning that this film may be in the public domain in countries and jurisdictions with %d years p.m.a. or less, as well as in the United States.', latestDeathYear, pmaYears)
table.insert(crewTableRows, '<tr><td colspan="2" class="footnoteText">' .. footnoteText .. '</td></tr>')
end
return table.concat(crewTableRows)
end
function p.getCrewList(frame)
return p._getCrewList(getArgs(frame))
end
function p._generateCastAndCrew(args)
-- Try to get the current Wikidata item associated with the page
local currentPageQID = args[1] or mw.wikibase.getEntityIdForCurrentPage()
if not currentPageQID then
return ''
end
local castListContent = p._getCastList({currentPageQID})
local crewListContent = p._getCrewList({currentPageQID})
if crewListContent == '' and castListContent == '' then
return ''
end
return tostring(mw.html.create('table')
:addClass('wikitable mw-collapsible mw-collapsed wst-cast-and-crew')
:wikitext('<caption>Cast and Crew </caption>' .. castListContent .. crewListContent)
)
end
function p.generateCastAndCrew(frame)
return p._generateCastAndCrew(getArgs(frame))
end
return p