diff --git a/backend/src/services/schedule.js b/backend/src/services/schedule.js index 277f6f3..39f1c43 100644 --- a/backend/src/services/schedule.js +++ b/backend/src/services/schedule.js @@ -214,6 +214,7 @@ export async function getScheduleDetail(db, id, getXProfile = null) { sx.username as x_username, sx.content as x_content, sx.image_urls as x_image_urls, + sx.video_thumbnails as x_video_thumbnails, sv.broadcaster as variety_broadcaster, sv.replay_url as variety_replay_url, svi.medium_url as variety_thumbnail_url, @@ -297,6 +298,7 @@ export async function getScheduleDetail(db, id, getXProfile = null) { result.username = username; result.content = s.x_content || null; result.imageUrls = s.x_image_urls ? JSON.parse(s.x_image_urls) : []; + result.videoThumbnails = s.x_video_thumbnails ? JSON.parse(s.x_video_thumbnails) : []; result.postUrl = `https://x.com/${username}/status/${s.x_post_id}`; if (getXProfile) { diff --git a/backend/src/services/x/index.js b/backend/src/services/x/index.js index 43d1f0f..07be8c3 100644 --- a/backend/src/services/x/index.js +++ b/backend/src/services/x/index.js @@ -93,13 +93,14 @@ async function xBotPlugin(fastify, opts) { // schedule_x 테이블에 저장 await connection.query( - 'INSERT INTO schedule_x (schedule_id, post_id, username, content, image_urls) VALUES (?, ?, ?, ?, ?)', + 'INSERT INTO schedule_x (schedule_id, post_id, username, content, image_urls, video_thumbnails) VALUES (?, ?, ?, ?, ?, ?)', [ scheduleId, tweet.id, tweetUsername, tweet.text, tweet.imageUrls.length > 0 ? JSON.stringify(tweet.imageUrls) : null, + tweet.videoThumbnails?.length > 0 ? JSON.stringify(tweet.videoThumbnails) : null, ] ); diff --git a/backend/src/services/x/scraper.js b/backend/src/services/x/scraper.js index 6030720..77d85b3 100644 --- a/backend/src/services/x/scraper.js +++ b/backend/src/services/x/scraper.js @@ -51,6 +51,22 @@ export function extractImageUrls(html) { return [...new Set(urls)]; } +/** + * HTML에서 영상/GIF 썸네일 URL 추출 + * Nitter는 영상 파일을 제공하지 않고 썸네일만 노출 (amplify_video_thumb, + * ext_tw_video_thumb, tweet_video_thumb). 재생은 원본 트윗으로 이동. + */ +export function extractVideoThumbnails(html) { + const urls = []; + const regex = / )} + {/* 영상 썸네일 (재생 버튼 → 원본 트윗으로 이동) */} + {schedule.videoThumbnails?.length > 0 && ( +
+ {schedule.videoThumbnails.map((url, i) => ( + + +
+
+ + + +
+
+ {/* 외부(X) 재생 표시 배지 */} +
+ + + + X에서 재생 +
+
+ ))} +
+ )} + {/* 날짜/시간 */}
{formatXDateTimeWithTime(schedule.date, schedule.time)} diff --git a/frontend/src/pages/pc/public/schedule/sections/XSection.jsx b/frontend/src/pages/pc/public/schedule/sections/XSection.jsx index 35e9d1f..c46ab5c 100644 --- a/frontend/src/pages/pc/public/schedule/sections/XSection.jsx +++ b/frontend/src/pages/pc/public/schedule/sections/XSection.jsx @@ -166,6 +166,37 @@ function XSection({ schedule }) {
)} + {/* 영상 썸네일 (재생 버튼 → 원본 트윗으로 이동) */} + {schedule.videoThumbnails?.length > 0 && ( +
+ {schedule.videoThumbnails.map((url, i) => ( + + +
+
+ + + +
+
+ {/* 외부(X) 재생 표시 배지 */} +
+ + + + X에서 재생 +
+
+ ))} +
+ )} + {/* 날짜/시간 */}
{formatXDateTimeWithTime(schedule.date, schedule.time)}