<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ko">
	<id>https://changpop.wiki/%EB%AA%A8%EB%93%88:%EC%9D%BC%EA%B0%84_%EC%8B%A0%EC%B0%BD%EC%84%AD_%EC%B5%9C%EA%B7%BC_%EB%AA%A9%EB%A1%9D/+/history?feed=atom</id>
	<title>모듈:일간 신창섭 최근 목록 - 편집 역사</title>
	<link rel="self" type="application/atom+xml" href="https://changpop.wiki/%EB%AA%A8%EB%93%88:%EC%9D%BC%EA%B0%84_%EC%8B%A0%EC%B0%BD%EC%84%AD_%EC%B5%9C%EA%B7%BC_%EB%AA%A9%EB%A1%9D/+/history?feed=atom"/>
	<link rel="alternate" type="text/html" href="https://changpop.wiki/%EB%AA%A8%EB%93%88:%EC%9D%BC%EA%B0%84_%EC%8B%A0%EC%B0%BD%EC%84%AD_%EC%B5%9C%EA%B7%BC_%EB%AA%A9%EB%A1%9D/+/history"/>
	<updated>2026-10-26T09:58:52Z</updated>
	<subtitle>이 문서의 편집 역사</subtitle>
	<generator>MediaWiki 1.46.2</generator>
	<entry>
		<id>https://changpop.wiki/index.php?title=%EB%AA%A8%EB%93%88:%EC%9D%BC%EA%B0%84_%EC%8B%A0%EC%B0%BD%EC%84%AD_%EC%B5%9C%EA%B7%BC_%EB%AA%A9%EB%A1%9D&amp;diff=2988&amp;oldid=prev</id>
		<title>창팝위키: 새 문서: local p = {}  local CONFIG = { 	apiuntoSource = &quot;일간신창섭최근목록&quot;, -- LocalSettings.php의 $wgApiuntoSources 키 	templateName  = &quot;유튜브 영상 항목&quot;,  	-- 시트의 열 순서(A~H)를 그대로 숫자 인덱스로 나열. 	-- values[i][col.url] 식으로 참조하기 위한 상수. 	col = { 		url       = 1, -- A: 원본 URL 		videoId   = 2, -- B: 추출된 영상 ID 		title     = 3, -- C: 제목 		channel   = 4, -- D: 채널명 		channelId = 5, -- E: 채널 ID...</title>
		<link rel="alternate" type="text/html" href="https://changpop.wiki/index.php?title=%EB%AA%A8%EB%93%88:%EC%9D%BC%EA%B0%84_%EC%8B%A0%EC%B0%BD%EC%84%AD_%EC%B5%9C%EA%B7%BC_%EB%AA%A9%EB%A1%9D&amp;diff=2988&amp;oldid=prev"/>
		<updated>2026-10-04T15:50:16Z</updated>

		<summary type="html">&lt;p&gt;새 문서: local p = {}  local CONFIG = { 	apiuntoSource = &amp;quot;일간신창섭최근목록&amp;quot;, -- LocalSettings.php의 $wgApiuntoSources 키 	templateName  = &amp;quot;유튜브 영상 항목&amp;quot;,  	-- 시트의 열 순서(A~H)를 그대로 숫자 인덱스로 나열. 	-- values[i][col.url] 식으로 참조하기 위한 상수. 	col = { 		url       = 1, -- A: 원본 URL 		videoId   = 2, -- B: 추출된 영상 ID 		title     = 3, -- C: 제목 		channel   = 4, -- D: 채널명 		channelId = 5, -- E: 채널 ID...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;새 문서&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
local CONFIG = {&lt;br /&gt;
	apiuntoSource = &amp;quot;일간신창섭최근목록&amp;quot;, -- LocalSettings.php의 $wgApiuntoSources 키&lt;br /&gt;
	templateName  = &amp;quot;유튜브 영상 항목&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
	-- 시트의 열 순서(A~H)를 그대로 숫자 인덱스로 나열.&lt;br /&gt;
	-- values[i][col.url] 식으로 참조하기 위한 상수.&lt;br /&gt;
	col = {&lt;br /&gt;
		url       = 1, -- A: 원본 URL&lt;br /&gt;
		videoId   = 2, -- B: 추출된 영상 ID&lt;br /&gt;
		title     = 3, -- C: 제목&lt;br /&gt;
		channel   = 4, -- D: 채널명&lt;br /&gt;
		channelId = 5, -- E: 채널 ID&lt;br /&gt;
		views     = 6, -- F: 조회수&lt;br /&gt;
		published = 7, -- G: 게시일(UTC ISO 8601)&lt;br /&gt;
		duration  = 8, -- H: 길이(mm:ss, 이미 변환되어 있음)&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ── 날짜/시간 유틸리티 (os 라이브러리 미사용) ──────────────────&lt;br /&gt;
&lt;br /&gt;
local DAYS_IN_MONTH = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }&lt;br /&gt;
&lt;br /&gt;
local function isLeapYear(y)&lt;br /&gt;
	return (y % 4 == 0 and y % 100 ~= 0) or (y % 400 == 0)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function daysInMonth(y, m)&lt;br /&gt;
	if m == 2 and isLeapYear(y) then return 29 end&lt;br /&gt;
	return DAYS_IN_MONTH[m]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseIso8601Utc(str)&lt;br /&gt;
	local y, mo, d, h, mi, s = str:match(&lt;br /&gt;
		&amp;quot;^(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)Z$&amp;quot;&lt;br /&gt;
	)&lt;br /&gt;
	if not y then return nil end&lt;br /&gt;
	return {&lt;br /&gt;
		year = tonumber(y), month = tonumber(mo), day = tonumber(d),&lt;br /&gt;
		hour = tonumber(h), min = tonumber(mi), sec = tonumber(s),&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utcToKst(t)&lt;br /&gt;
	local hour = t.hour + 9&lt;br /&gt;
	local day, month, year = t.day, t.month, t.year&lt;br /&gt;
	if hour &amp;gt;= 24 then&lt;br /&gt;
		hour = hour - 24&lt;br /&gt;
		day = day + 1&lt;br /&gt;
		if day &amp;gt; daysInMonth(year, month) then&lt;br /&gt;
			day = 1&lt;br /&gt;
			month = month + 1&lt;br /&gt;
			if month &amp;gt; 12 then&lt;br /&gt;
				month = 1&lt;br /&gt;
				year = year + 1&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return { year = year, month = month, day = day, hour = hour, min = t.min }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function formatKstTime(kst)&lt;br /&gt;
	local period = &amp;quot;오전&amp;quot;&lt;br /&gt;
	local hour12 = kst.hour&lt;br /&gt;
	if kst.hour == 0 then&lt;br /&gt;
		hour12 = 12&lt;br /&gt;
	elseif kst.hour == 12 then&lt;br /&gt;
		period = &amp;quot;오후&amp;quot;&lt;br /&gt;
		hour12 = 12&lt;br /&gt;
	elseif kst.hour &amp;gt; 12 then&lt;br /&gt;
		period = &amp;quot;오후&amp;quot;&lt;br /&gt;
		hour12 = kst.hour - 12&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;quot;%s %d:%02d&amp;quot;, period, hour12, kst.min)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function formatDateHeading(kst)&lt;br /&gt;
	return string.format(&amp;quot;%d월 %d일&amp;quot;, kst.month, kst.day)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function abbreviateViews(n)&lt;br /&gt;
	if not n then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local function trimUnit(value, unit)&lt;br /&gt;
		local truncated = math.floor(value * 10) / 10&lt;br /&gt;
		if truncated == math.floor(truncated) then&lt;br /&gt;
			return string.format(&amp;quot;%d%s&amp;quot;, truncated, unit)&lt;br /&gt;
		end&lt;br /&gt;
		return string.format(&amp;quot;%.1f%s&amp;quot;, truncated, unit)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if n &amp;lt; 1000 then&lt;br /&gt;
		return tostring(n)&lt;br /&gt;
	elseif n &amp;lt; 10000 then&lt;br /&gt;
		return trimUnit(n / 1000, &amp;quot;천&amp;quot;)&lt;br /&gt;
	elseif n &amp;lt; 100000000 then&lt;br /&gt;
		return trimUnit(n / 10000, &amp;quot;만&amp;quot;)&lt;br /&gt;
	else&lt;br /&gt;
		return trimUnit(n / 100000000, &amp;quot;억&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- 메인 진입점: {{#invoke:YoutubeList|main}}&lt;br /&gt;
-- ============================================================&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	local api = mw.ext.Apiunto&lt;br /&gt;
&lt;br /&gt;
	local raw = api.fetch(CONFIG.apiuntoSource, &amp;quot;&amp;quot;)&lt;br /&gt;
	if not raw or raw == &amp;quot;&amp;quot; or raw == &amp;quot;Could not retrieve API Data&amp;quot; then&lt;br /&gt;
		return &amp;quot;Apiunto(&amp;quot; .. CONFIG.apiuntoSource .. &amp;quot;) 응답을 가져오지 못했습니다.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- fetch()는 JSON을 파싱하지 않고 원문 문자열을 그대로 주므로, 여기서 직접 디코드해야 한다.&lt;br /&gt;
	local ok, response = pcall(mw.text.jsonDecode, raw)&lt;br /&gt;
	if not ok or type(response) ~= &amp;quot;table&amp;quot; then&lt;br /&gt;
		return &amp;quot;Apiunto(&amp;quot; .. CONFIG.apiuntoSource .. &amp;quot;) 응답이 유효한 JSON이 아닙니다.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not response.values then&lt;br /&gt;
		return &amp;quot;Apiunto(&amp;quot; .. CONFIG.apiuntoSource .. &amp;quot;) 응답에서 values를 찾지 못했습니다.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local parts = {}&lt;br /&gt;
	local currentDateKey = nil&lt;br /&gt;
	local col = CONFIG.col&lt;br /&gt;
&lt;br /&gt;
	for _, row in ipairs(response.values) do&lt;br /&gt;
		-- row[n]이 빈 셀 이후는 아예 배열에서 생략될 수 있으므로(구글 시트 API 특성),&lt;br /&gt;
		-- 매번 or &amp;quot;&amp;quot;로 안전하게 기본값을 준다.&lt;br /&gt;
		local published = row[col.published] or &amp;quot;&amp;quot;&lt;br /&gt;
		local utc = parseIso8601Utc(published)&lt;br /&gt;
&lt;br /&gt;
		if utc then -- 업로드 시각이 없는(아직 조회 안 된) 행은 건너뜀&lt;br /&gt;
			local kst = utcToKst(utc)&lt;br /&gt;
			local dateKey = string.format(&amp;quot;%04d-%02d-%02d&amp;quot;, kst.year, kst.month, kst.day)&lt;br /&gt;
&lt;br /&gt;
			if dateKey ~= currentDateKey then&lt;br /&gt;
				table.insert(parts, [[&amp;lt;p class=&amp;quot;mw--card-row&amp;quot; style=&amp;quot;&lt;br /&gt;
						font-size: var(--font-size-large, 1.125rem);&lt;br /&gt;
						font-weight: var(--font-weight-medium, 500);&lt;br /&gt;
						margin: 0;&lt;br /&gt;
						&amp;quot;&amp;gt;]] &lt;br /&gt;
					.. formatDateHeading(kst) .. &amp;quot;&amp;lt;/p&amp;gt;&amp;quot;)&lt;br /&gt;
				&lt;br /&gt;
				currentDateKey = dateKey&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local templateArgs = {&lt;br /&gt;
				[&amp;quot;영상Id&amp;quot;]		= row[col.videoId] or &amp;quot;&amp;quot;,&lt;br /&gt;
				[&amp;quot;제목&amp;quot;]		= row[col.title] or &amp;quot;&amp;quot;,&lt;br /&gt;
				[&amp;quot;채널이름&amp;quot;]	= row[col.channel] or &amp;quot;&amp;quot;,&lt;br /&gt;
				[&amp;quot;채널Id&amp;quot;]		= row[col.channelId] or &amp;quot;&amp;quot;,&lt;br /&gt;
				[&amp;quot;조회수&amp;quot;]		= abbreviateViews(tonumber(row[col.views])),&lt;br /&gt;
				[&amp;quot;업로드시각&amp;quot;]	= formatKstTime(kst),&lt;br /&gt;
				[&amp;quot;길이&amp;quot;]		= row[col.duration] or &amp;quot;&amp;quot;,&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			local rendered = frame:expandTemplate{&lt;br /&gt;
				title = CONFIG.templateName,&lt;br /&gt;
				args = templateArgs,&lt;br /&gt;
			}&lt;br /&gt;
			table.insert(parts, rendered)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat(parts, &amp;quot;\n&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>창팝위키</name></author>
	</entry>
</feed>