Jump to content

Module:YouTubeSubscribers

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by BrokenSegue (talk | contribs) at 02:41, 18 February 2023 (val). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

POINT_IN_TIME_PID = "P585"
YT_CHAN_ID_PID= "P2397"
SUB_COUNT_PID = "P8687"

local p = {} 

-- taken from https://en.wikipedia.org/wiki/Module:Wd
function parseDate(dateStr, precision)
	precision = precision or "d"

	local i, j, index, ptr
	local parts = {nil, nil, nil}

	if dateStr == nil then
		return parts[1], parts[2], parts[3]  -- year, month, day
	end

	-- 'T' for snak values, '/' for outputs with '/Julian' attached
	i, j = dateStr:find("[T/]")

	if i then
		dateStr = dateStr:sub(1, i-1)
	end

	local from = 1

	if dateStr:sub(1,1) == "-" then
		-- this is a negative number, look further ahead
		from = 2
	end

	index = 1
	ptr = 1

	i, j = dateStr:find("-", from)

	if i then
		-- year
		parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10)  -- remove '+' sign (explicitly give base 10 to prevent error)

		if parts[index] == -0 then
			parts[index] = tonumber("0")  -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
		end

		if precision == "y" then
			-- we're done
			return parts[1], parts[2], parts[3]  -- year, month, day
		end

		index = index + 1
		ptr = i + 1

		i, j = dateStr:find("-", ptr)

		if i then
			-- month
			parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)

			if precision == "m" then
				-- we're done
				return parts[1], parts[2], parts[3]  -- year, month, day
			end

			index = index + 1
			ptr = i + 1
		end
	end

	if dateStr:sub(ptr) ~= "" then
		-- day if we have month, month if we have year, or year
		parts[index] = tonumber(dateStr:sub(ptr), 10)
	end

	return parts[1], parts[2], parts[3]  -- year, month, day
end

-- taken from https://en.wikipedia.org/wiki/Module:Wd
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
	if aY == nil or bY == nil then
		return nil
	end
	aM = aM or 1
	aD = aD or 1
	bM = bM or 1
	bD = bD or 1

	if aY < bY then
		return true
	end

	if aY > bY then
		return false
	end

	if aM < bM then
		return true
	end

	if aM > bM then
		return false
	end

	if aD < bD then
		return true
	end

	return false
end

function getClaimDate(claim)
	if claim['qualifiers'] and claim['qualifiers'][POINT_IN_TIME_PID] then 
		local pointsInTime = claim['qualifiers'][POINT_IN_TIME_PID]
		if #pointsInTime ~= 1 then
			-- be conservative in what we accept
			return nil
		end
		local pointInTime = pointsInTime[1]
		if pointInTime and pointInTime['datavalue'] and pointInTime['datavalue']['value'] and pointInTime['datavalue']['value']['time'] then
			return parseDate(pointInTime['datavalue']['value']['time'])
		end
		
	end
	return nil
end

-- for a given list of statements find the newest one with a matching qual
function newestMatchingStatement(statements, qual, targetQualValue)
	local newestStatement = nil
	local newestStatementYr = nil
	local newestStatementMo = nil
	local newestStatementDay = nil
    for k, v in pairs(statements) do
    	if v['rank'] ~= "deprecated" and v['qualifiers'] and v['qualifiers'][qual] then
    		local quals = v['qualifiers'][qual]
    		-- should only have one instance of the qualifier on a statement
    		if #quals == 1 then
    			local qual = quals[1]
    			if qual['datavalue'] and qual['datavalue']['value'] then
    				local qualValue = qual['datavalue']['value']
    				if qualValue == targetQualValue then
	    				local targetYr, targetMo, targetDay = getClaimDate(v)
	    				if targetYr then
	    					local older = datePrecedesDate(targetYr, targetMo, targetDay, newestStatementYr, newestStatementMo, newestStatementDay)
	    					if older == nil or not older then
	    						newestStatementYr, newestStatementMo, newestStatementDay = targetYr, targetMo, targetDay
	    						newestStatement = v
	    					end
	    				end
    				end
    			end
    		end
    	end
	end
	return newestStatement
end

-- for a given property and qualifier pair returns the newest statement that matches
function newestMatching(e, prop, qual, targetQualValue)
	-- first check the best statements
	local statements = e:getBestStatements(prop)
	local newestStatement = newestMatchingStatement(statements, qual, targetQualValue)
	if newestStatement then
		return newestStatement
	end
	-- try again with all statements if nothing so far
	statements = e:getAllStatements(prop)
	newestStatement = newestMatchingStatement(statements, qual, targetQualValue)
	if newestStatement then
		return newestStatement
	end
	return nil
end

function getEntity ( frame )
	local qid = nil
	if frame.args then
		qid = frame.args["qid"]
	end
	if not qid then
		qid = mw.wikibase.getEntityIdForCurrentPage()
	end
	if not qid then
		error("No qid found for page")
	end
	local e = mw.wikibase.getEntity(qid)
	if not e then
		error("No such item found: " .. qid)
	end
	return e
end

-- find the channel ID we are going to be getting the sub counts for
function getBestYtChanId(e) 
	local chanIds = e:getBestStatements(YT_CHAN_ID_PID)
	if #chanIds == 1 then
		local chan = chanIds[1]
		if chan and chan["mainsnak"] and chan["mainsnak"]["datavalue"] and chan["mainsnak"]["datavalue"]["value"] then
			return chan["mainsnak"]["datavalue"]["value"]
		end
	end
	return nil
end

-- the date of the current YT subscriber count
function p.date( frame )
	local e = getEntity(frame)
	local chanId = getBestYtChanId(e)
	local s = newestMatching(e, SUB_COUNT_PID, YT_CHAN_ID_PID, chanId)
	if s then
		local yt_year, yt_month, yt_day = getClaimDate(s)
		if not yt_year then
			return nil
		end
		local dateString = yt_year .. "|"
		-- construct YYYY|mm|dd date string
		if yt_month and yt_month ~= 0 then
			dateString = dateString .. yt_month .. "|"
			if yt_day and yt_day ~= 0 then
				dateString = dateString .. yt_day
			end
		end
		return "{{Format date|" ..dateString .. "}}"
    end
end

-- the most up to date number of subscribers
function p.subCount( frame )
	local e = getEntity(frame)
	local subCount = nil
	local chanId = getBestYtChanId(e)
	if chanId then
		local s = newestMatching(e, SUB_COUNT_PID, YT_CHAN_ID_PID, chanId)
		if s and s["mainsnak"] and s['mainsnak']["datavalue"] and s['mainsnak']["datavalue"]["value"] and s['mainsnak']["datavalue"]['value']['amount'] then
			subCount = s['mainsnak']["datavalue"]['value']['amount']
		end
	end
    if subCount then
    	return "{{val|" .. tonumber(subCount) .. "}}"
    else
    	error("could not find sub count")
    	return "?"
    end
end

return p