Module: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  .. label ..  end -- Check for an English Wikipedia link local enwikiLink = entity:getSitelink('enwiki') if enwikiLink then return  .. label ..  end -- Fallback to a direct Wikidata link with label return  .. 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 = ' ' .. role .. ' ' .. castMemberLink .. ' '        -- 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 ' Cast   Role  Actor  ' .. 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, ' ' .. role .. '  ' .. table.concat(crewMemberLinks, ', ') .. '  ') end end end if #crewTableRows == 0 then return '' end table.insert(crewTableRows, 1, ' Crew ') 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, ' ' .. footnoteText .. ' ') 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(' Cast and Crew&#32; ' .. castListContent .. crewListContent)	) end

function p.generateCastAndCrew(frame) return p._generateCastAndCrew(getArgs(frame)) end

return p