fix: YouTube 봇 fetchRecentVideos를 Activities API로 변경
- playlistItems API 대신 Activities API 사용 - playlistItems는 새 영상 반영이 지연되는 문제가 있었음 - Activities API는 새 업로드를 즉시 반영함 - upload 외 다른 활동(좋아요, 플레이리스트 등)도 포함되므로 2배로 조회 후 필터링 - API 할당량 비용은 동일 (1 단위) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f3f99c7428
commit
730da864a4
1 changed files with 31 additions and 13 deletions
|
|
@ -94,15 +94,15 @@ async function getVideoDurations(videoIds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 최근 N개 영상 조회
|
* 최근 N개 영상 조회 (Activities API 사용 - playlistItems보다 빠른 반영)
|
||||||
* @param {string} channelId - 채널 ID
|
* @param {string} channelId - 채널 ID
|
||||||
* @param {number} maxResults - 최대 결과 수
|
* @param {number} maxResults - 최대 결과 수
|
||||||
* @param {string} uploadsPlaylistId - 캐싱된 uploads playlist ID (선택)
|
* @param {string} uploadsPlaylistId - 미사용 (하위 호환성 유지)
|
||||||
*/
|
*/
|
||||||
export async function fetchRecentVideos(channelId, maxResults = 10, uploadsPlaylistId = null) {
|
export async function fetchRecentVideos(channelId, maxResults = 10, uploadsPlaylistId = null) {
|
||||||
const uploadsId = uploadsPlaylistId || await getUploadsPlaylistId(channelId);
|
// Activities API에 type=upload 지정 (다른 활동이 섞일 수 있어 2배 조회)
|
||||||
|
const fetchCount = Math.min(maxResults * 2, 50);
|
||||||
const url = `${API_BASE}/playlistItems?part=snippet&playlistId=${uploadsId}&maxResults=${maxResults}&key=${API_KEY}`;
|
const url = `${API_BASE}/activities?part=snippet,contentDetails&channelId=${channelId}&type=upload&maxResults=${fetchCount}&key=${API_KEY}`;
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
|
|
@ -110,17 +110,35 @@ export async function fetchRecentVideos(channelId, maxResults = 10, uploadsPlayl
|
||||||
throw new Error(data.error.message);
|
throw new Error(data.error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoIds = data.items.map(item => item.snippet.resourceId.videoId);
|
// 'upload' 타입만 필터링 (API가 완벽히 필터링하지 않을 수 있음)
|
||||||
const shortsMap = await getVideoDurations(videoIds);
|
const uploadItems = (data.items || [])
|
||||||
|
.filter(item => item.snippet.type === 'upload')
|
||||||
|
.slice(0, maxResults);
|
||||||
|
|
||||||
return data.items.map(item => {
|
if (uploadItems.length === 0) {
|
||||||
const { snippet } = item;
|
return [];
|
||||||
const videoId = snippet.resourceId.videoId;
|
}
|
||||||
const isShorts = shortsMap[videoId] || false;
|
|
||||||
|
// video ID 목록 추출
|
||||||
|
const videoIds = uploadItems.map(item => item.contentDetails.upload.videoId);
|
||||||
|
|
||||||
|
// videos API로 상세 정보 조회 (duration, description 등)
|
||||||
|
const videosUrl = `${API_BASE}/videos?part=snippet,contentDetails&id=${videoIds.join(',')}&key=${API_KEY}`;
|
||||||
|
const videosRes = await fetch(videosUrl);
|
||||||
|
const videosData = await videosRes.json();
|
||||||
|
|
||||||
|
if (videosData.error) {
|
||||||
|
throw new Error(videosData.error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (videosData.items || []).map(video => {
|
||||||
|
const { snippet, contentDetails } = video;
|
||||||
|
const seconds = parseDuration(contentDetails.duration);
|
||||||
|
const isShorts = seconds > 0 && seconds <= 60;
|
||||||
const publishedAt = new Date(snippet.publishedAt);
|
const publishedAt = new Date(snippet.publishedAt);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
videoId,
|
videoId: video.id,
|
||||||
title: snippet.title,
|
title: snippet.title,
|
||||||
description: snippet.description || '',
|
description: snippet.description || '',
|
||||||
channelId: snippet.channelId,
|
channelId: snippet.channelId,
|
||||||
|
|
@ -129,7 +147,7 @@ export async function fetchRecentVideos(channelId, maxResults = 10, uploadsPlayl
|
||||||
date: formatDate(publishedAt),
|
date: formatDate(publishedAt),
|
||||||
time: formatTime(publishedAt),
|
time: formatTime(publishedAt),
|
||||||
videoType: isShorts ? 'shorts' : 'video',
|
videoType: isShorts ? 'shorts' : 'video',
|
||||||
videoUrl: getVideoUrl(videoId, isShorts),
|
videoUrl: getVideoUrl(video.id, isShorts),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue