// ==UserScript==
// @name 숲 멀티뷰 시청자 제거기
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 숲 생방송 시청자 목록을 가져오고 멀티뷰를 제거합니다.
// @author asdi
// @match https://play.sooplive.co.kr/*
// @grant none
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
// 팝업창 생성
const popup = document.createElement('div');
popup.style.position = 'fixed';
popup.style.top = '50%'; // 세로 중앙에 위치하도록
popup.style.left = '0'; // 가로 왼쪽에 위치하도록
popup.style.transform = 'translateY(-50%)'; // 세로 중앙 정렬
popup.style.zIndex = '1000';
popup.style.width = '350px';
popup.style.padding = '15px';
popup.style.backgroundColor = '#f8f9fa';
popup.style.border = '1px solid #ccc';
popup.style.borderRadius = '8px';
popup.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)';
popup.style.fontSize = '14px';
popup.style.fontFamily = 'Arial, sans-serif';
popup.style.color = '#333';
popup.innerHTML = `
닉네임이 여기에 표시됩니다.
결과가 여기에 표시됩니다.
`;
document.body.appendChild(popup);
// 닫기 버튼 핸들러
document.getElementById('closePopup').addEventListener('click', () => {
document.body.removeChild(popup); // 팝업창 삭제
});
// userIdList에서 괄호 부분을 무시하고 중복 없는 아이디 리스트를 구하는 함수
const getUniqueUserIdList = (userIdList) => {
const uniqueUserIds = new Set();
userIdList.forEach(userId => {
const baseUserId = userId.replace(/\(\d+\)$/, '');
uniqueUserIds.add(baseUserId);
});
return [...uniqueUserIds];
};
// 닉네임 가져오기 버튼 핸들러
document.getElementById('getNickName').addEventListener('click', async () => {
const nicknameElement = document.querySelector('a#infoNickName');
const nicknameDisplay = document.getElementById('nicknameDisplay');
const nicknameResults = document.getElementById('nicknameResults');
if (nicknameElement) {
const nickname = nicknameElement.textContent.trim();
nicknameDisplay.textContent = `닉네임: ${nickname}`;
try {
liveView.Chat.chatUserListLayer.reconnect();
liveView.playerController.sendChUser();
const viewerText = document.getElementById('nAllViewer').textContent;
const viewerNumber = parseInt(viewerText.replace(/,/g, ''));
console.log('Viewer Number:', viewerNumber);
// 3초 대기
await new Promise(resolve => setTimeout(resolve, 3000));
const userList = liveView.Chat.chatUserListLayer;
const userIdList = [
...Object.keys(userList.userListSeparatedByGrade.fan),
...Object.keys(userList.userListSeparatedByGrade.manager),
...Object.keys(userList.userListSeparatedByGrade.normal),
...Object.keys(userList.userListSeparatedByGrade.subscription),
...Object.keys(userList.userListSeparatedByGrade.vip)
];
const uniqueUserIdList = getUniqueUserIdList(userIdList);
console.log('Unique User ID List:', uniqueUserIdList);
const loginRatio = (uniqueUserIdList.length / viewerNumber * 100).toFixed(2);
const resultMessage = `${nickname} - 현재 시청자 수 : ${viewerNumber}, 중복 제거한 로그인 시청자 : ${uniqueUserIdList.length} (로그인 비율 : ${loginRatio}%)`;
const nicknameResultsContent = document.getElementById('nicknameResults');
const previousResults = nicknameResultsContent.textContent.trim();
const updatedResults = previousResults ? previousResults + `\n\n${resultMessage}` : resultMessage;
nicknameResultsContent.textContent = updatedResults;
// localStorage에 저장
const savedResults = localStorage.getItem('allResults') || '';
localStorage.setItem('allResults', savedResults + `\n${resultMessage}`);
localStorage.setItem(`uniqueUserIdList_${nickname}`, JSON.stringify(uniqueUserIdList));
} catch (error) {
console.error('Error:', error);
nicknameResults.textContent = `Error: ${error.message}`;
}
} else {
nicknameDisplay.textContent = '닉네임을 찾을 수 없습니다.';
}
});
// 총 시청자 수 구하기 버튼 핸들러
document.getElementById('totalViewerCount').addEventListener('click', () => {
const totalViewerResults = document.getElementById('totalViewerResults');
const nicknameKeys = Object.keys(localStorage).filter(key => key.startsWith('uniqueUserIdList_'));
if (nicknameKeys.length > 0) {
const uniqueViewerLists = [];
const nicknames = [];
let totalViewerNumber = 0;
nicknameKeys.forEach(key => {
const nickname = key.replace('uniqueUserIdList_', '');
const uniqueUserIdList = JSON.parse(localStorage.getItem(key));
uniqueViewerLists.push(new Set(uniqueUserIdList));
nicknames.push(nickname);
totalViewerNumber += uniqueUserIdList.length;
});
const unionSet = new Set();
uniqueViewerLists.forEach(set => set.forEach(viewer => unionSet.add(viewer)));
const multiViewExcludedCount = unionSet.size;
const nicknameList = nicknames.join(', ');
const idCounts = {};
uniqueViewerLists.forEach(set => {
set.forEach(viewer => {
idCounts[viewer] = (idCounts[viewer] || 0) + 1;
});
});
const multiSetUsers = Object.keys(idCounts).filter(viewer => idCounts[viewer] > 1);
const multiSetCount = multiSetUsers.length;
const now = new Date();
const formattedDate = `${now.getFullYear()}년 ${now.getMonth() + 1}월 ${now.getDate()}일 ${['일', '월', '화', '수', '목', '금', '토'][now.getDay()]} ${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;
totalViewerResults.textContent = `집계 시각 : ${formattedDate}
${nicknameList} 의 멀티뷰 제외 로그인 시청자 수 : ${multiViewExcludedCount}명
( 멀티뷰 포함 : ${totalViewerNumber}, 멀티뷰 중인 시청자 수 : ${multiSetCount} )`;
} else {
totalViewerResults.textContent = '저장된 결과가 없습니다.';
}
});
// 리셋 버튼 핸들러
document.getElementById('resetButton').addEventListener('click', () => {
localStorage.removeItem('allResults');
Object.keys(localStorage).forEach(key => {
if (key.startsWith('uniqueUserIdList_')) {
localStorage.removeItem(key);
}
});
document.getElementById('nicknameResults').textContent = '결과가 여기에 표시됩니다.';
document.getElementById('totalViewerResults').textContent = '결과가 여기에 표시됩니다.';
alert('모든 결과가 리셋되었습니다.');
});
// localStorage에서 저장된 모든 결과 가져오기
const savedResults = localStorage.getItem('allResults');
if (savedResults) {
document.getElementById('nicknameResults').textContent = savedResults;
}
})();