Module:SocialMediaStats
- point in time (P585) (see uses )
- YouTube channel ID (P2397) (see uses )
- YouTube handle (P11245) (see uses )
- social media followers (P8687) (see uses )
- number of viewers/listeners (P5436) (see uses )
This module fetches social media stats count from a page's Wikidata entity. That data should be automatically updated on Wikidata's side.
Usage
[edit ]Currently there are three methods YTsubscribers, YTviews and YTdate. They can be used with a YouTube channel ID like below:
{{#invoke:SocialMediaStats|YTsubscribers|youtube_id=YouTube channel ID}}
{{#invoke:SocialMediaStats|YTviews|youtube_id=YouTube channel ID}}
{{#invoke:SocialMediaStats|YTdate|youtube_id=YouTube channel ID}}
Or used with a YouTube handle like below:
{{#invoke:SocialMediaStats|YTsubscribers|youtube_handle=YouTube handle}}
{{#invoke:SocialMediaStats|YTviews|youtube_handle=YouTube handle}}
{{#invoke:SocialMediaStats|YTdate|youtube_handle=YouTube handle}}
You can use up to three of these and you can mix and match them as in:
{{#invoke:SocialMediaStats|YTsubscribers|youtube_handle=YouTube handle|youtube_id2=YouTube channel ID}}
They can be also used with a QID like below to specify where to load the data from but you generally won't need this.
{{#invoke:SocialMediaStats|YTsubscribers|qid=Wikidata entity ID|youtube_handle=YouTube handle }}
Error tracking category
[edit ]Editors can experiment in this module's sandbox (create | mirror) and testcases (edit | run) pages.
Subpages of this module.
-- scribunto module to get YouTube channel statistics from Wikidata for social media personality infoboxes require('strict') localautoDate=require("Module:Auto date formatter") localPOINT_IN_TIME_PID="P585" localYT_CHAN_ID_PID="P2397" localYT_HANDLE_PID="P11245" localSUB_COUNT_PID="P8687" localVIEW_COUNT_PID="P5436" localp={} -- taken from https://en.wikipedia.org/wiki/Module:Wd localfunctionparseDate(dateStr,precision) precision=precisionor"d" locali,j,index,ptr localparts={nil,nil,nil} ifdateStr==nilthen returnparts[1],parts[2],parts[3]-- year, month, day end -- 'T' for snak values, '/' for outputs with '/Julian' attached i,j=dateStr:find("[T/]") ifithen dateStr=dateStr:sub(1,i-1) end localfrom=1 ifdateStr:sub(1,1)=="-"then -- this is a negative number, look further ahead from=2 end index=1 ptr=1 i,j=dateStr:find("-",from) ifithen -- year parts[index]=tonumber(mw.ustring.gsub(dateStr:sub(ptr,i-1),"^%+(.+)$","%1"),10)-- remove '+' sign (explicitly give base 10 to prevent error) ifparts[index]==-0then parts[index]=tonumber("0")-- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead end ifprecision=="y"then -- we're done returnparts[1],parts[2],parts[3]-- year, month, day end index=index+1 ptr=i+1 i,j=dateStr:find("-",ptr) ifithen -- month parts[index]=tonumber(dateStr:sub(ptr,i-1),10) ifprecision=="m"then -- we're done returnparts[1],parts[2],parts[3]-- year, month, day end index=index+1 ptr=i+1 end end ifdateStr:sub(ptr)~=""then -- day if we have month, month if we have year, or year parts[index]=tonumber(dateStr:sub(ptr),10) end returnparts[1],parts[2],parts[3]-- year, month, day end -- taken from https://en.wikipedia.org/wiki/Module:Wd localfunctiondatePrecedesDate(aY,aM,aD,bY,bM,bD) ifaY==nilorbY==nilthen returnnil end aM=aMor1 aD=aDor1 bM=bMor1 bD=bDor1 ifaY<bYthen returntrue elseifaY>bYthen returnfalse elseifaM<bMthen returntrue elseifaM>bMthen returnfalse elseifaD<bDthen returntrue end returnfalse end localfunctiongetClaimDate(claim) ifclaim['qualifiers']andclaim['qualifiers'][POINT_IN_TIME_PID]then localpointsInTime=claim['qualifiers'][POINT_IN_TIME_PID] if#pointsInTime~=1then -- be conservative in what we accept error("Encountered a statement with zero or multiple point in time (P85) qualifiers. Please add or remove point in time information so each statement has exactly one") end localpointInTime=pointsInTime[1] ifpointInTimeand pointInTime['datavalue']and pointInTime['datavalue']['value']and pointInTime['datavalue']['value']['time'] then returnparseDate(pointInTime['datavalue']['value']['time']) end end returnnil end -- for a given list of statements find the newest one with a matching qualifier localfunctionnewestMatchingStatement(statements,qual,targetQualValue) localnewestStatement=nil localnewestStatementYr=nil localnewestStatementMo=nil localnewestStatementDay=nil fork,vinpairs(statements)do ifv['rank']~="deprecated"andv['qualifiers']andv['qualifiers'][qual]then localquals=v['qualifiers'][qual] -- should only have one instance of the qualifier on a statement if#quals==1then localqual=quals[1] ifqual['datavalue']andqual['datavalue']['value']then localqualValue=qual['datavalue']['value'] ifqualValue==targetQualValuethen localtargetYr,targetMo,targetDay=getClaimDate(v) iftargetYrthen localolder=datePrecedesDate(targetYr,targetMo,targetDay,newestStatementYr,newestStatementMo,newestStatementDay) ifolder==nilornotolderthen newestStatementYr,newestStatementMo,newestStatementDay=targetYr,targetMo,targetDay newestStatement=v end end end end end end end returnnewestStatement end -- for a given property and qualifier pair returns the newest statement that matches localfunctionnewestMatching(e,prop,qual,targetQualValue) -- first check the best statements localstatements=e:getBestStatements(prop) localnewestStatement=newestMatchingStatement(statements,qual,targetQualValue) ifnewestStatementthen returnnewestStatement end -- try again with all statements if nothing so far statements=e:getAllStatements(prop) newestStatement=newestMatchingStatement(statements,qual,targetQualValue) ifnewestStatementthen returnnewestStatement end returnnil end localfunctiongetValidStatements(e,prop) -- call getAllStatements and filter out deprecated ones localallStatements=e:getAllStatements(prop) localvalidStatements={} for_,statementinpairs(allStatements)do ifstatement['rank']~="deprecated"then table.insert(validStatements,statement) end end returnvalidStatements end localfunctiongetEntity(frame) localqid=nil ifframe.argsthen qid=frame.args["qid"] end ifnotqidormw.text.trim(qid)==""then qid=mw.wikibase.getEntityIdForCurrentPage() end ifnotqidthen locale=nil returne end locale=mw.wikibase.getEntity(qid) assert(e,"No such item found: "..qid) returne end -- Convert YouTube handle to channel ID if needed localfunctionnormalizeChannelId(channelParam) ifnotchannelParamthen returnnil end ifchannelParam:sub(1,1)=="@"then returnchannelParam:sub(2) else returnchannelParam end end -- Get all YouTube channel IDs from the entity localfunctiongetAllYtChannelIds(e) localchannelIds={} localchanIdStatements=getValidStatements(e,YT_CHAN_ID_PID) for_,statementinpairs(chanIdStatements)do ifstatementand statement["mainsnak"]and statement["mainsnak"]["datavalue"]and statement["mainsnak"]["datavalue"]["value"] then table.insert(channelIds,statement["mainsnak"]["datavalue"]["value"]) end end returnchannelIds end localfunctiongetHandlesToChannelIds(e) -- get a mapping of handles to channel IDs and vice versa localmapping={} mapping["handles"]={} mapping["channelIds"]={} localchanIdStatements=getValidStatements(e,YT_CHAN_ID_PID) -- Iterate over each channel ID statement and find associated handles as qualifiers for_,chanStatementinpairs(chanIdStatements)do localchannelId=nil ifchanStatementand chanStatement["mainsnak"]and chanStatement["mainsnak"]["datavalue"]and chanStatement["mainsnak"]["datavalue"]["value"] then channelId=chanStatement["mainsnak"]["datavalue"]["value"] end -- Now look for handle qualifiers on this statement ifchanStatement['qualifiers']then localhandleQuals=chanStatement['qualifiers'][YT_HANDLE_PID] ifhandleQualsthen for_,handleQualinpairs(handleQuals)do ifhandleQual['datavalue']andhandleQual['datavalue']['value']then localhandleValue=handleQual['datavalue']['value'] locallowerHandle=handleValue:lower() mapping["handles"][lowerHandle]=channelId mapping["channelIds"][channelId]=handleValue end end end end end localhandleStatements=getValidStatements(e,YT_HANDLE_PID) -- Iterate over each handle statement and find associated channel IDs as qualifiers for_,handleStatementinpairs(handleStatements)do localhandleValue=nil ifhandleStatementand handleStatement["mainsnak"]and handleStatement["mainsnak"]["datavalue"]and handleStatement["mainsnak"]["datavalue"]["value"] then handleValue=handleStatement["mainsnak"]["datavalue"]["value"] end -- Now look for channel ID qualifiers on this statement ifhandleStatement['qualifiers']then localchanIdQuals=handleStatement['qualifiers'][YT_CHAN_ID_PID] ifchanIdQualsthen for_,chanIdQualinpairs(chanIdQuals)do ifchanIdQual['datavalue']andchanIdQual['datavalue']['value']then localchannelId=chanIdQual['datavalue']['value'] locallowerHandle=handleValue:lower() mapping["handles"][lowerHandle]=channelId mapping["channelIds"][channelId]=handleValue end end end end end returnmapping end -- Find the best matching channel ID for a given parameter localfunctionfindMatchingChannelId(e,channelParam) ifnotchannelParamthen returnnil end localnormalizedParam=normalizeChannelId(channelParam) localallChannelIds=getAllYtChannelIds(e) -- First try exact match for_,channelIdinpairs(allChannelIds)do ifchannelId==normalizedParamorchannelId==channelParamthen returnchannelId end end -- If no exact match then we assume it's a handle and look for it -- first check if it starts with UC localhandleToChannelId=getHandlesToChannelIds(e) ifhandleToChannelId["handles"][normalizedParam:lower()]then returnhandleToChannelId["handles"][normalizedParam:lower()] end returnnil end localfunctionreturnError(frame,eMessage) returnframe:expandTemplate{title='error',args={eMessage}}.."[[Category:Pages with SocialMediaStats module errors]]" end -- Get the statistic value from a statement localfunctiongetStatisticValue(statement) ifstatementand statement["mainsnak"]and statement['mainsnak']["datavalue"]and statement['mainsnak']["datavalue"]["value"]and statement['mainsnak']["datavalue"]['value']['amount'] then returntonumber(statement['mainsnak']["datavalue"]['value']['amount']) end returnnil end -- Get formatted date from a statement localfunctiongetFormattedDate(frame,statement) ifstatementthen localyt_year,yt_month,yt_day=getClaimDate(statement) ifyt_yearthen returnautoDate._access_archive_format(frame:expandTemplate{title="Format date",args={yt_year,yt_month,yt_day}}) end end returnnil end -- Get subscriber count for a channel localfunctiongetSubscriberCount(e,channelId) localstatement=newestMatching(e,SUB_COUNT_PID,YT_CHAN_ID_PID,channelId) returngetStatisticValue(statement) end -- Get view count for a channel localfunctiongetViewCount(e,channelId) localstatement=newestMatching(e,VIEW_COUNT_PID,YT_CHAN_ID_PID,channelId) returngetStatisticValue(statement) end -- Get the date for statistics (assumes subscriber and view counts have same date) localfunctiongetStatsDate(frame,e,channelId) localstatement=newestMatching(e,SUB_COUNT_PID,YT_CHAN_ID_PID,channelId) returngetFormattedDate(frame,statement) end localfunctionpassedArgs(frame) -- iterate over frame.args and check if any non-qid and non-number args are present fork,vinpairs(frame.args)do ifk~="qid"andtype(k)~="number"andtonumber(k)==nilthen returntrue end end returnfalse end -- Main function to get subscriber counts for up to 3 channels functionp.YTsubscribersInt(frame) ifnotpassedArgs(frame)then return"" end locale=getEntity(frame) ifnotethen return"" end localresults={} localsingleResult=nil localhasData=false localhandleMapping=getHandlesToChannelIds(e) -- Check each of the 3 possible channels fori=1,3do localhandleParam="youtube_handle"..(i==1and""ortostring(i)) localidParam="youtube_id"..(i==1and""ortostring(i)) localchannelParam=frame.args[handleParam] ifchannelParam==nilorchannelParam==""then channelParam=frame.args[idParam] end ifchannelParamthen localchannelId=findMatchingChannelId(e,channelParam) ifchannelIdthen localsubCount=getSubscriberCount(e,channelId) ifsubCountandsubCount>0then localformattedCount=frame:expandTemplate{title="Format price",args={subCount}} localchannelName=channelParam:gsub("^@","") ifhandleMapping["channelIds"][channelName]then channelName=handleMapping["channelIds"][channelName] end table.insert(results,formattedCount.." ("..channelName..")") singleResult=formattedCount hasData=true end end end end ifnothasDatathen localparams="" fork,vinpairs(frame.args)do params=params..k.."="..v.."; " end returnreturnError(frame,"No subscriber data found for "..e:getId().." with the provided parameters: "..params) end -- If only one result, return it directly if#results==1andsingleResult~=nilthen returnsingleResult end -- Multiple results, use {{ubl}} returnframe:expandTemplate{title="ubl",args=results} end -- Main function to get view counts for up to 3 channels functionp.YTviewsInt(frame) ifnotpassedArgs(frame)then return"" end locale=getEntity(frame) ifnotethen return"" end localresults={} localsingleResult=nil localhasData=false localhandleMapping=getHandlesToChannelIds(e) -- Check each of the 3 possible channels fori=1,3do localhandleParam="youtube_handle"..(i==1and""ortostring(i)) localidParam="youtube_id"..(i==1and""ortostring(i)) localchannelParam=frame.args[handleParam] ifchannelParam==nilorchannelParam==""then channelParam=frame.args[idParam] end ifchannelParamthen localchannelId=findMatchingChannelId(e,channelParam) ifchannelIdthen localviewCount=getViewCount(e,channelId) ifviewCountandviewCount>0then localformattedCount=frame:expandTemplate{title="Format price",args={viewCount}} localchannelName=channelParam:gsub("^@","")-- Remove @ if present for display ifhandleMapping["channelIds"][channelName]then channelName=handleMapping["channelIds"][channelName] end table.insert(results,formattedCount.." ("..channelName..")") singleResult=formattedCount hasData=true end end end end ifnothasDatathen return"" end -- If only one result, return it directly if#results==1andsingleResult~=nilthen returnsingleResult end -- Multiple results, use {{ubl}} returnframe:expandTemplate{title="ubl",args=results} end -- Function to get the date of statistics functionp.YTdateInt(frame) locale=getEntity(frame) ifnotethen return"" end -- Try to get date from any available channel fori=1,3do localhandleParam="youtube_handle"..(i==1and""ortostring(i)) localidParam="youtube_id"..(i==1and""ortostring(i)) localchannelParam=frame.args[handleParam] ifchannelParam==nilorchannelParam==""then channelParam=frame.args[idParam] end ifchannelParamthen localchannelId=findMatchingChannelId(e,channelParam) ifchannelIdthen localdate=getStatsDate(frame,e,channelId) ifdatethen returndate end end end end return"" end -- Safe wrapper functions functionp.YTsubscribers(frame) localstatus,obj=pcall(p.YTsubscribersInt,frame) ifstatusthen returnobj else returnreturnError(frame,obj) end end functionp.YTviews(frame) localstatus,obj=pcall(p.YTviewsInt,frame) ifstatusthen returnobj else returnreturnError(frame,obj) end end functionp.YTdate(frame) localstatus,obj=pcall(p.YTdateInt,frame) ifstatusthen returnobj else returnreturnError(frame,obj) end end returnp --[[ -- useful for debugger testing local f = mw.getCurrentFrame() local args = {} args['qid'] = 'Q111862397' args['youtube_handle'] = 'LinusTechTips' f['args'] = args p.YTsubscribersInt(f) p.YTviewsInt(f) local e = mw.wikibase.getEntity('Q57618112') print(mw.dumpObject(getHandlesToChannelIds(e))) --]]