// ==UserScript==
// @name SOOP 다시보기 라이브 당시 시간 표시
// @namespace http://tampermonkey.net/
// @version 4.3.2
// @description SOOP 다시보기에서 생방송 당시 시간을 표시하고 특정 시간으로 이동합니다.
// @author WakViewer
// @match https://vod.sooplive.co.kr/player/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=www.sooplive.co.kr
// @license MIT
// @grant none
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
const startTimeSelector = "#player_area > div.wrapping.player_bottom > div.broadcast_information > div:nth-child(2) > div.cnt_info > ul > li:nth-child(2) > span.broad_time";
const currentTimeSelector = "span.time-current";
const durationSelector = "#player > div.player_ctrlBox > div.ctrlBox > div.ctrl > div.time_display > span.time-duration";
let startTime = null;
let endTime = null;
let currentLiveTime = '';
const waitForElement = (selector, callback) => {
const observer = new MutationObserver(() => {
const element = document.querySelector(selector);
if (element) {
observer.disconnect();
callback(element);
}
});
observer.observe(document.body, { childList: true, subtree: true });
};
const init = () => {
waitForElement(startTimeSelector, (startTimeElement) => {
const tipContent = startTimeElement.getAttribute('tip');
const timeMatch = tipContent.match(/방송시간 : (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ~ (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/);
if (!timeMatch) {
return;
}
startTime = new Date(timeMatch[1]);
endTime = new Date(timeMatch[2]);
setInterval(() => {
updateLiveTime(startTime);
}, 500);
waitForElement(durationSelector, checkEditNotice);
addDisplayAndButton();
});
};
const checkEditNotice = (durationElement) => {
const durationParts = durationElement.textContent.split(':').map(Number);
const totalDuration = (durationParts[0] * 3600) + (durationParts[1] * 60) + durationParts[2];
const expectedDuration = (endTime - startTime) / 1000;
if (totalDuration < expectedDuration - 60) { // 편집된 경우
const liveTimeDisplay = document.getElementById('live-time-display');
if (!liveTimeDisplay) return;
let editNotice = document.getElementById('edit-notice');
if (!editNotice) {
editNotice = document.createElement('span');
editNotice.id = 'edit-notice';
editNotice.textContent = '[같이보기 진행 또는 편집된 영상입니다.]';
editNotice.style.fontSize = '14px';
editNotice.style.lineHeight = '28px';
editNotice.style.color = 'rgb(145, 150, 161)';
editNotice.style.marginRight = '10px';
liveTimeDisplay.parentNode.insertBefore(editNotice, liveTimeDisplay);
}
}
};
const addDisplayAndButton = () => {
const upCntElement = document.querySelector("#player_area > div.wrapping.player_bottom > div.broadcast_information > div:nth-child(2) > div.cnt_info > ul");
if (!upCntElement) return;
// 가로 길이 조정
upCntElement.style.width = '180px';
// 라이브 당시 시간 표시
let liveTimeDisplay = document.getElementById('live-time-display');
if (!liveTimeDisplay) {
liveTimeDisplay = document.createElement('span');
liveTimeDisplay.id = 'live-time-display';
liveTimeDisplay.style.fontSize = '14px';
liveTimeDisplay.style.lineHeight = '28px';
liveTimeDisplay.style.color = '#9196a1';
liveTimeDisplay.title = '라이브 당시 시간 복사';
liveTimeDisplay.addEventListener('click', () => {
navigator.clipboard.writeText(currentLiveTime).then(() => {
showToastMessage(`복사 완료: ${currentLiveTime}`);
});
});
upCntElement.parentNode.insertBefore(liveTimeDisplay, upCntElement);
}
// 점프 버튼 추가
let jumpButton = document.getElementById('jump-button');
if (!jumpButton) {
jumpButton = document.createElement('button');
jumpButton.id = 'jump-button';
jumpButton.innerHTML = '⇋';
jumpButton.style.marginLeft = '10px';
jumpButton.style.color = '#d5d7dc';
jumpButton.style.background = 'transparent';
jumpButton.style.border = 'none';
jumpButton.style.cursor = 'pointer';
jumpButton.title = '특정 시간으로 이동';
jumpButton.addEventListener('click', () => {
const userInput = prompt(`이동할 라이브 당시 시간을 입력하세요.\n(예: YYYY-MM-DD, HH:mm:ss)\n\n[방송정보]\n시작: ${formatDate(startTime)}\n종료: ${formatDate(endTime)}`);
const targetTime = new Date(userInput);
if (isNaN(targetTime)) {
showToastMessage('올바른 형식이 아닙니다!', true);
return;
}
if (targetTime < startTime || targetTime > endTime) {
showToastMessage('해당 방송이 진행된 시간이 아닙니다!', true);
return;
}
const diffInSeconds = Math.floor((targetTime - startTime) / 1000);
const newUrl = `${window.location.origin}${window.location.pathname}?change_second=${diffInSeconds}`;
window.location.href = newUrl;
});
liveTimeDisplay.insertAdjacentElement('afterend', jumpButton);
}
};
const updateLiveTime = (startTime) => {
const currentTimeElement = document.querySelector(currentTimeSelector);
if (!currentTimeElement) return;
const [hours, minutes, seconds] = currentTimeElement.textContent.trim().split(':').map(Number);
const liveTime = new Date(startTime);
liveTime.setSeconds(liveTime.getSeconds() + hours * 3600 + minutes * 60 + seconds);
currentLiveTime = formatDate(liveTime);
document.getElementById('live-time-display').innerHTML = `Live 당시 시간⠀ ${currentLiveTime}`;
};
function formatDate(date) {
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}, ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`;
}
function showToastMessage(message, isError = false) {
const toastContainer = document.querySelector('#toastMessage');
if (toastContainer) {
const toastWrapper = document.createElement('div');
const toastContent = document.createElement('p');
toastContent.textContent = message;
toastWrapper.appendChild(toastContent);
toastContainer.appendChild(toastWrapper);
setTimeout(() => {
toastContainer.removeChild(toastWrapper);
}, 2000);
} else {
alert(message);
}
}
window.addEventListener('load', init);
})();