// ==UserScript== // @name 아프리카TV - 사이드바 UI 변경 // @name:ko 아프리카TV - 사이드바 UI 변경 // @namespace https://www.afreecatv.com/ // @version 2024-01-71 // @description 아프리카TV의 사이드바 UI를 변경합니다. // @description:ko 아프리카TV의 사이드바 UI를 변경합니다. // @author You // @match https://www.afreecatv.com/ // @match https://www.afreecatv.com/?hash=* // @match https://www.afreecatv.com/?NaPm=* // @match https://play.afreecatv.com/*/* // @icon https://www.google.com/s2/favicons?sz=64&domain=afreecatv.com // @grant GM_addStyle // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @run-at document-end // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; const currentUrl = window.location.href; let installMessage = GM_getValue("installMessage", 1); let coloring_live = GM_getValue("coloring_live", 1); let display_follow = GM_getValue("display_follow", 6); let display_myplus = GM_getValue("display_myplus", 6); let display_top = GM_getValue("display_top", 6); let myplus_position = GM_getValue("myplus_position", 1); let myplus_order = GM_getValue("myplus_order", 1); let clickDisplayCount = GM_getValue("clickDisplayCount", 10); let blockedUsers = GM_getValue('blockedUsers', []); let blockedCategories = GM_getValue('blockedCategories', []); let open_newtab = GM_getValue("open_newtab", 0); let playerSidebar = GM_getValue("playerSidebar", 1); let playerSmode = GM_getValue("playerSmode", 0); let preplayerSmode = playerSmode; let sidebarMinimized = GM_getValue("sidebarMinimized", 0); let smodeSidebar = GM_getValue("smodeSidebar", 1); let menuIds = {}; let categoryMenuIds = {}; const isDarkMode = document.body.classList.contains('thema_dark'); let savedCategory = GM_getValue("szBroadCategory",0); let delayCheckEnabled = true; let webplayer_scroll_left = 240; if(sidebarMinimized){ webplayer_scroll_left = 52; } // 데이터를 로드하고 처리하는 함수 function loadData() { // 현재 시간 기록 var currentTime = new Date().getTime(); // 이전 실행 시간 불러오기 var lastExecutionTime = GM_getValue("lastExecutionTime", 0); // 마지막 실행 시간으로부터 1시간 이상 경과했는지 확인 if (currentTime - lastExecutionTime >= 3600000) { // 1시간은 3600000 밀리초 GM_xmlhttpRequest({ method: "GET", url: "https://live.afreecatv.com/script/locale/ko_KR/broad_category.js", onload: function(response) { if (response.status === 200) { // 성공적으로 데이터를 받았을 때 처리할 코드 작성 var szBroadCategory = response.responseText; // 이후 처리할 작업 추가 szBroadCategory = JSON.parse(szBroadCategory.split('var szBroadCategory = ')[1].slice(0, -1)); if (szBroadCategory.CHANNEL.RESULT === "1") { // 데이터 저장 GM_setValue("szBroadCategory", szBroadCategory); // 현재 시간을 마지막 실행 시간으로 업데이트 GM_setValue("lastExecutionTime", currentTime); } } else { console.error("Failed to load data:", response.statusText); } }, onerror: function(error) { console.error("Error occurred while loading data:", error); } }); } else { console.log("1 hour not elapsed since last execution. Skipping data load."); } } // 페이지가 로드되면 데이터를 로드하고 처리 window.addEventListener('load', function() { console.log(GM_getValue("lastExecutionTime")); loadData(); }); const css_Darkmode = ` @media screen and (max-width: 1136px) { .left_navbar a:first-child { display: none; } } #sidebar.min { width: 52px; } #sidebar.min .users-section a.user span { display: none; } #sidebar.min .users-section button { font-size:11px; padding: 1px; } #sidebar.max .button-fold-sidebar { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none slice' viewBox='0 0 7 11'%3e%3cpath fill='%23f9f9f9' d='M5.87 11.01L.01 5.51 5.87.01l1.08 1.01-4.74 4.45L7 9.96 5.87 11z'/%3e%3c/svg%3e"); background-size: 7px 11px; background-repeat: no-repeat; width: 26px; height: 26px; background-position: center; /* background-color: #000000; */ position: absolute; top: 13px; left: 206px; } #sidebar.max .button-unfold-sidebar { display:none; } #sidebar.min .button-fold-sidebar { display:none; } #sidebar.min .button-unfold-sidebar { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none slice' viewBox='0 0 7 11'%3e%3cpath fill='%23f9f9f9' d='M1.13 11.01l5.86-5.5L1.13.01.05 1.02l4.74 4.45L0 9.96 1.13 11z'/%3e%3c/svg%3e"); background-size: 7px 11px; background-repeat: no-repeat; width: 26px; height: 26px; background-position: center; /* background-color: #000000; */ position: relative; top: 8px; left: 12px; padding-bottom:10px; } #sidebar.min .top-section span.max{ display:none; } #sidebar.max .top-section span.min{ display:none; } .game_post_area { width: 94%; left: 30px; } .game_post_area .scroll_area ul li{ background-color:#0E0E10; } #list-container { overflow-x: hidden; background-color:#0E0E10; } #listMain #wrap { min-width: 960px; } #listMain #wrap #serviceHeader { min-width: 960px; } #listMain #wrap #list-container #list-section { padding: 12px 22px 0 38px; } button.block-icon-svg-white { width: 40px; height: 50px; } button.block-icon-svg-white span { background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0 0 64 64" style="fill:%23B2B2B2;"%3E%3Cpath d="M32 6C17.641 6 6 17.641 6 32C6 46.359 17.641 58 32 58C46.359 58 58 46.359 58 32C58 17.641 46.359 6 32 6zM32 10C37.331151 10 42.225311 11.905908 46.037109 15.072266L14.505859 45.318359C11.682276 41.618415 10 37.00303 10 32C10 19.869 19.869 10 32 10zM48.927734 17.962891C52.094092 21.774689 54 26.668849 54 32C54 44.131 44.131 54 32 54C26.99697 54 22.381585 52.317724 18.681641 49.494141L48.927734 17.962891z"%3E%3C/path%3E%3C/svg%3E'); background-size: 100% 100%; width: 20px; height: 20px; } button.block-icon-svg-white:hover span { background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0 0 64 64" style="fill:%235285FF;"%3E%3Cpath d="M32 6C17.641 6 6 17.641 6 32C6 46.359 17.641 58 32 58C46.359 58 58 46.359 58 32C58 17.641 46.359 6 32 6zM32 10C37.331151 10 42.225311 11.905908 46.037109 15.072266L14.505859 45.318359C11.682276 41.618415 10 37.00303 10 32C10 19.869 19.869 10 32 10zM48.927734 17.962891C52.094092 21.774689 54 26.668849 54 32C54 44.131 44.131 54 32 54C26.99697 54 22.381585 52.317724 18.681641 49.494141L48.927734 17.962891z"%3E%3C/path%3E%3C/svg%3E'); } html { overflow: hidden; } .users-section.myplus > .user.show-more, .users-section.follow > .user.show-more, .users-section.top > .user.show-more { display: none; } #toggleButton, #toggleButton2, #toggleButton3 { padding:12px; color:#A1A1A1; width: 100%; text-align: center; } .left_navbar { display: flex; align-items: center; justify-content: flex-end; position: absolute; flex-direction: row-reverse; top: 0px; left: 160px; } .left_nav_button { position: relative; width: 70px; height: 70px; padding: 0; border: 0; border-radius: 50%; cursor: pointer; z-index: 3001; transition: all .2s; color: #e5e5e5; font-size: 15px; font-weight: 600; } .left_nav_button.active { color: #019BFE; } #sidebar { width: 240px; grid-area: sidebar; background-color: #1F1F23; color:white; margin-right:10px; padding-bottom:260px; } #sidebar .top-section { display: flex; align-items: center; justify-content: space-around; margin: 12px 0px 6px 0px; } #sidebar .top-section>span { text-transform: uppercase; font-weight: 550; font-size: 14px; margin-top: 6px; margin-bottom: 2px; color:#DEDEE3; } #sidebar .top-section>span>a { color:#DEDEE3; } #sidebar .twitch-message-section { margin: 0px 10px; margin-top: 10px; padding: 0 25px; background-color: #1F1F23; } #sidebar .twitch-message-section .title { margin: 0px; font-size: 1.5rem; font-weight: 500; } #sidebar .twitch-message-section .title>span { color: var(--primary-color); } #sidebar .twitch-message-section .description { margin: 8px 0px; line-height: 1.3rem; font-size: 0.9rem; color: #A1A1A1; } #sidebar .twitch-message-section .description>span { display: block; text-align: center; } .users-section .user { display: grid; grid-template-areas: "profile-picture username watchers" "profile-picture description blank"; grid-template-columns: 40px auto auto; padding: 6px 10px; } .users-section .user:hover { background-color: #26262c; cursor: pointer; } .users-section .user .profile-picture { grid-area: profile-picture; width: 32px; height: 32px; border-radius: 50%; } .users-section .user .username { grid-area: username; font-size: 14px; font-weight: 600; color:#DEDEE3; letter-spacing: 0.6px; margin-left:1px; } .users-section .user .description { grid-area: description; font-size: 13px; color: #a1a1a1; font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-left:1px; } .users-section .user .watchers { grid-area: watchers; display: flex; align-items: center; justify-content: flex-end; font-weight: 400; font-size: 14px; color: #c0c0c0; margin-right: 2px; } .users-section .user .watchers .dot { font-size: 7px; margin-right: 5px; } #listMain #wrap #serviceHeader #afLogo { left: 30px; height: 72px; } .btn_flexible { display: none; } #innerLnb { display: none; } #list-container { height: 100vh; overflow-y: auto; } #sidebar { height: 100vh; overflow-y: auto; position: fixed; } #sidebar::-webkit-scrollbar { display: none; /* Chrome, Safari, Edge */ } .tooltip-container { z-index: 999; width: 320px; height: auto; position: fixed; background-color: #26262C; display: flex; } .tooltip-container img { position: relative; z-index: 999; width: auto; height: auto; max-width:320px; max-height:240px flex: 0; } .tooltiptext { flex: 0; position: relative; z-index: 999; width: 320px; max-width: 100%; /* 넘치는 경우 최대 너비 */ height: auto; background-color: #26262C; color: #fff; text-align: center; align-items: center; /* 세로 가운데 정렬 */ justify-content: center; /* 가로 가운데 정렬 */ box-sizing: border-box; /* 패딩을 포함한 전체 너비 유지 */ padding: 8px 20px 14px 20px; } `; const css_Whitemode = ` @media screen and (max-width: 1136px) { .left_navbar a:first-child { display: none; } } #sidebar.max { width: 240px; } #sidebar.min { width: 52px; } #sidebar.min .users-section a.user span { display: none; } #sidebar.min .users-section button { font-size:11px; padding: 1px; } #sidebar.max .button-fold-sidebar { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none slice' viewBox='0 0 7 11'%3e%3cpath fill='%23888' d='M5.87 11.01L.01 5.51 5.87.01l1.08 1.01-4.74 4.45L7 9.96 5.87 11z'/%3e%3c/svg%3e"); background-size: 7px 11px; background-repeat: no-repeat; width: 26px; height: 26px; background-position: center; /* background-color: #000000; */ position: absolute; top: 13px; left: 206px; } #sidebar.max .button-unfold-sidebar { display:none; } #sidebar.min .button-fold-sidebar { display:none; } #sidebar.min .button-unfold-sidebar { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none slice' viewBox='0 0 7 11'%3e%3cpath fill='%23888' d='M1.13 11.01l5.86-5.5L1.13.01.05 1.02l4.74 4.45L0 9.96 1.13 11z'/%3e%3c/svg%3e"); background-size: 7px 11px; background-repeat: no-repeat; width: 26px; height: 26px; background-position: center; /* background-color: #000000; */ position: relative; top: 8px; left: 12px; padding-bottom:10px; } #sidebar.min .top-section span.max{ display:none; } #sidebar.max .top-section span.min{ display:none; } .game_post_area { width: 94%; left: 30px; } .game_post_area .scroll_area ul li{ background-color:#F7F7F8; } #list-container { overflow-x: hidden; background-color:#F7F7F8; } #listMain #wrap { min-width: 960px; } #listMain #wrap #serviceHeader { min-width: 960px; } #listMain #wrap #list-container #list-section { padding: 12px 22px 0 38px; } button.block-icon-svg { width: 40px; height: 50px; } button.block-icon-svg span { background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0 0 64 64" style="fill:%237C7D7D;"%3E%3Cpath d="M32 6C17.641 6 6 17.641 6 32C6 46.359 17.641 58 32 58C46.359 58 58 46.359 58 32C58 17.641 46.359 6 32 6zM32 10C37.331151 10 42.225311 11.905908 46.037109 15.072266L14.505859 45.318359C11.682276 41.618415 10 37.00303 10 32C10 19.869 19.869 10 32 10zM48.927734 17.962891C52.094092 21.774689 54 26.668849 54 32C54 44.131 44.131 54 32 54C26.99697 54 22.381585 52.317724 18.681641 49.494141L48.927734 17.962891z"%3E%3C/path%3E%3C/svg%3E'); background-size: 100% 100%; width: 20px; height: 20px; } button.block-icon-svg:hover span { background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0 0 64 64" style="fill:%235285FF;"%3E%3Cpath d="M32 6C17.641 6 6 17.641 6 32C6 46.359 17.641 58 32 58C46.359 58 58 46.359 58 32C58 17.641 46.359 6 32 6zM32 10C37.331151 10 42.225311 11.905908 46.037109 15.072266L14.505859 45.318359C11.682276 41.618415 10 37.00303 10 32C10 19.869 19.869 10 32 10zM48.927734 17.962891C52.094092 21.774689 54 26.668849 54 32C54 44.131 44.131 54 32 54C26.99697 54 22.381585 52.317724 18.681641 49.494141L48.927734 17.962891z"%3E%3C/path%3E%3C/svg%3E'); } html { overflow: hidden; } .users-section.myplus > .user.show-more, .users-section.follow > .user.show-more, .users-section.top > .user.show-more { display: none; } #toggleButton, #toggleButton2, #toggleButton3 { padding:12px; color:#53535F; width: 100%; text-align: center; } .left_navbar { display: flex; align-items: center; justify-content: flex-end; position: absolute; flex-direction: row-reverse; top: 0px; left: 160px; } .left_nav_button { position: relative; width: 70px; height: 70px; padding: 0; border: 0; border-radius: 50%; cursor: pointer; z-index: 3001; transition: all .2s; color: 1F1F23; font-size: 15px; font-weight: 600; } .left_nav_button.active { color: #0545B1; } #sidebar { width: 240px; grid-area: sidebar; background-color: #EFEFF1; color:black; padding-bottom:260px; } #sidebar .top-section { display: flex; align-items: center; justify-content: space-around; margin: 12px 0px 6px 0px; } #sidebar .top-section>span { text-transform: uppercase; font-weight: 600; font-size: 14px; margin-top: 6px; margin-bottom: 2px; color:#0E0E10; } #sidebar .top-section>span>a { color:#0E0E10; } #sidebar .twitch-message-section { margin: 0px 10px; margin-top: 10px; padding: 0 25px; background-color: #EFEFF1; } #sidebar .twitch-message-section .title { margin: 0px; font-size: 1.5rem; font-weight: 500; } #sidebar .twitch-message-section .title>span { color: var(--primary-color); } #sidebar .twitch-message-section .description { margin: 8px 0px; line-height: 1.3rem; font-size: 0.9rem; color: #53535F; } #sidebar .twitch-message-section .description>span { display: block; text-align: center; } .users-section .user { display: grid; grid-template-areas: "profile-picture username watchers" "profile-picture description blank"; grid-template-columns: 40px auto auto; padding: 6px 10px; } .users-section .user:hover { background-color: #E6E6EA; cursor: pointer; } .users-section .user .profile-picture { grid-area: profile-picture; width: 32px; height: 32px; border-radius: 50%; } .users-section .user .username { grid-area: username; font-size: 14px; font-weight: 600; color:#1F1F23; letter-spacing: 0.6px; margin-left:1px; } .users-section .user .description { grid-area: description; font-size: 13px; font-weight: 400; color: #53535F; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-left:1px; } .users-section .user .watchers { grid-area: watchers; display: flex; align-items: center; justify-content: flex-end; font-size: 14px; font-weight: 400; color: black; margin-right: 2px; } .users-section .user .watchers .dot { font-size: 7px; margin-right: 5px; } #listMain #wrap #serviceHeader #afLogo { left: 30px; height: 72px; } .btn_flexible { display: none; } #innerLnb { display: none; } #list-container { height: 100vh; overflow-y: auto; } #sidebar { height: 100vh; overflow-y: auto; position: fixed; } #sidebar::-webkit-scrollbar { display: none; /* Chrome, Safari, Edge */ } .tooltip-container { z-index: 999; width: 320px; height: auto; position: fixed; background-color: #E6E6EA; display: flex; } .tooltip-container img { position: relative; z-index: 999; width: auto; height: auto; max-width:320px; max-height:240px flex: 0; } .tooltiptext { flex: 0; position: relative; z-index: 999; width: 320px; max-width: 100%; /* 넘치는 경우 최대 너비 */ height: auto; background-color: #E6E6EA; color: black; text-align: center; align-items: center; /* 세로 가운데 정렬 */ justify-content: center; /* 가로 가운데 정렬 */ box-sizing: border-box; /* 패딩을 포함한 전체 너비 유지 */ padding: 8px 20px 14px 20px; } `; const css_Darkmode_player = ` @media (max-width: 1320px) { .layout_v2#webplayer.chat_open #webplayer_contents, .layout_v2#webplayer.chat_open.list_open #webplayer_contents, .layout_v2#webplayer.chat_open.list_bookmark_open #webplayer_contents { min-width: 600px; } } @media screen and (max-width: 1120px) { .left_nav_button { display: none; } } #sidebar.max { width: 240px; } #sidebar.min { width: 52px; } #sidebar.min .users-section a.user span { display: none; } #sidebar.min .users-section button { font-size:11px; padding: 1px; } #sidebar.max .button-fold-sidebar { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none slice' viewBox='0 0 7 11'%3e%3cpath fill='%23f9f9f9' d='M5.87 11.01L.01 5.51 5.87.01l1.08 1.01-4.74 4.45L7 9.96 5.87 11z'/%3e%3c/svg%3e"); background-size: 7px 11px; background-repeat: no-repeat; width: 26px; height: 26px; background-position: center; /* background-color: #000000; */ position: absolute; top: 13px; left: 206px; } #sidebar.max .button-unfold-sidebar { display:none; } #sidebar.min .button-fold-sidebar { display:none; } #sidebar.min .button-unfold-sidebar { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none slice' viewBox='0 0 7 11'%3e%3cpath fill='%23f9f9f9' d='M1.13 11.01l5.86-5.5L1.13.01.05 1.02l4.74 4.45L0 9.96 1.13 11z'/%3e%3c/svg%3e"); background-size: 7px 11px; background-repeat: no-repeat; width: 26px; height: 26px; background-position: center; /* background-color: #000000; */ position: relative; top: 10px; left: 12px; padding-bottom:10px; } #sidebar.min .top-section span.max{ display:none; } #sidebar.max .top-section span.min{ display:none; } #afreecatv_player { width: 100%; } .smode #webplayer_scroll { top: 0; left:0; } #webplayer_scroll{ left: ${webplayer_scroll_left}px; } #sidebar { height:100vh; grid-area: sidebar; background-color: #1F1F23; color:white; overflow-y: auto; position: fixed; top: 56px; } #sidebar > :last-child { padding-bottom: 240px; /* 마지막 자식에만 padding 추가 */ } #sidebar::-webkit-scrollbar { display: none; /* Chrome, Safari, Edge */ } #sidebar .top-section { display: flex; align-items: center; justify-content: space-around; margin: 12px 0px 6px 0px; } #sidebar .top-section>span { text-transform: uppercase; font-weight: 550; font-size: 14px; margin-top: 6px; margin-bottom: 2px; color:#DEDEE3; } #sidebar .top-section>span>a { color:#DEDEE3; } #sidebar .twitch-message-section { margin: 0px 10px; margin-top: 10px; padding: 0 25px; background-color: #1F1F23; } #sidebar .twitch-message-section .title { margin: 0px; font-size: 1.5rem; font-weight: 500; } #sidebar .twitch-message-section .title>span { color: var(--primary-color); } #sidebar .twitch-message-section .description { margin: 8px 0px; line-height: 1.3rem; font-size: 0.9rem; color: #A1A1A1; } #sidebar .twitch-message-section .description>span { display: block; text-align: center; } .users-section .user { display: grid; grid-template-areas: "profile-picture username watchers" "profile-picture description blank"; grid-template-columns: 40px auto auto; padding: 6px 10px; } .users-section .user:hover { background-color: #26262c; cursor: pointer; } .users-section .user .profile-picture { grid-area: profile-picture; width: 32px; height: 32px; border-radius: 50%; } .users-section .user .username { grid-area: username; font-size: 14px; font-weight: 600; color:#DEDEE3; letter-spacing: 0.6px; margin-left:1px; } .users-section .user .description { grid-area: description; font-size: 13px; color: #a1a1a1; font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-left:1px; } .users-section .user .watchers { grid-area: watchers; display: flex; align-items: center; justify-content: flex-end; font-weight: 400; font-size: 14px; color: #c0c0c0; margin-right: 2px; } .users-section .user .watchers .dot { font-size: 7px; margin-right: 5px; } #webplayer_top .logo { top: 18px; left: 18px; } .left_navbar { display: flex; align-items: center; justify-content: flex-end; position: absolute; flex-direction: row-reverse; top: 0px; left: 140px; } .left_nav_button { position: relative; width: 70px; height: 56px; padding: 0; border: 0; border-radius: 50%; cursor: pointer; z-index: 3001; transition: all .2s; color: #e5e5e5; font-size: 15px; font-weight: 600; } .tooltip-container { z-index: 999; width: 320px; height: auto; position: fixed; background-color: #26262C; display: flex; } .tooltip-container img { position: relative; z-index: 999; width: auto; height: auto; max-width:320px; max-height:240px flex: 0; } .tooltiptext { flex: 0; position: relative; z-index: 999; width: 320px; max-width: 100%; /* 넘치는 경우 최대 너비 */ height: auto; background-color: #26262C; color: #fff; text-align: center; align-items: center; /* 세로 가운데 정렬 */ justify-content: center; /* 가로 가운데 정렬 */ box-sizing: border-box; /* 패딩을 포함한 전체 너비 유지 */ padding: 8px 20px 14px 20px; } .users-section.myplus > .user.show-more, .users-section.follow > .user.show-more, .users-section.top > .user.show-more { display: none; } #toggleButton, #toggleButton2, #toggleButton3 { padding:12px; color:#A1A1A1; width: 100%; text-align: center; } `; const css_Whitemode_player = ` @media (max-width: 1320px) { .layout_v2#webplayer.chat_open #webplayer_contents, .layout_v2#webplayer.chat_open.list_open #webplayer_contents, .layout_v2#webplayer.chat_open.list_bookmark_open #webplayer_contents { min-width: 600px; } } @media screen and (max-width: 1120px) { .left_nav_button { display: none; } } #afSearcharea .search_window .searchInputWrap { border: 1px solid #E5E5E5 !important; } #webplayer_top { border-bottom: 1px solid #E5E5E5 !important; } #sidebar.max { width: 240px; } #sidebar.min { width: 52px; } #sidebar.min .users-section a.user span { display: none; } #sidebar.min .users-section button { font-size:11px; padding: 1px; } #sidebar.max .button-fold-sidebar { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none slice' viewBox='0 0 7 11'%3e%3cpath fill='%23888' d='M5.87 11.01L.01 5.51 5.87.01l1.08 1.01-4.74 4.45L7 9.96 5.87 11z'/%3e%3c/svg%3e"); background-size: 7px 11px; background-repeat: no-repeat; width: 26px; height: 26px; background-position: center; /* background-color: #000000; */ position: absolute; top: 13px; left: 206px; } #sidebar.max .button-unfold-sidebar { display:none; } #sidebar.min .button-fold-sidebar { display:none; } #sidebar.min .button-unfold-sidebar { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none slice' viewBox='0 0 7 11'%3e%3cpath fill='%23888' d='M1.13 11.01l5.86-5.5L1.13.01.05 1.02l4.74 4.45L0 9.96 1.13 11z'/%3e%3c/svg%3e"); background-size: 7px 11px; background-repeat: no-repeat; width: 26px; height: 26px; background-position: center; /* background-color: #000000; */ position: relative; top: 8px; left: 12px; padding-bottom:10px; } #sidebar.min .top-section span.max{ display:none; } #sidebar.max .top-section span.min{ display:none; } #afreecatv_player { width: 100%; } .smode #webplayer_scroll { top: 0; left:0; } #webplayer_scroll{ left: ${webplayer_scroll_left}px; } #sidebar { width: 240px; height:100vh; grid-area: sidebar; background-color: #EFEFF1; color:white; overflow-y: auto; position: fixed; top: 56px; } #sidebar > :last-child { padding-bottom: 240px; /* 마지막 자식에만 padding 추가 */ } #sidebar::-webkit-scrollbar { display: none; /* Chrome, Safari, Edge */ } #sidebar .top-section { display: flex; align-items: center; justify-content: space-around; margin: 12px 0px 6px 0px; } #sidebar .top-section>span { text-transform: uppercase; font-weight: 600; font-size: 14px; margin-top: 6px; margin-bottom: 2px; color:#0E0E10; } #sidebar .top-section>span>a { color:#0E0E10; } #sidebar .twitch-message-section { margin: 0px 10px; margin-top: 10px; padding: 0 25px; background-color: #EFEFF1; } #sidebar .twitch-message-section .title { margin: 0px; font-size: 1.5rem; font-weight: 500; } #sidebar .twitch-message-section .title>span { color: var(--primary-color); } #sidebar .twitch-message-section .description { margin: 8px 0px; line-height: 1.3rem; font-size: 0.9rem; color: #53535F; } #sidebar .twitch-message-section .description>span { display: block; text-align: center; } .users-section .user { display: grid; grid-template-areas: "profile-picture username watchers" "profile-picture description blank"; grid-template-columns: 40px auto auto; padding: 6px 10px; } .users-section .user:hover { background-color: #E6E6EA; cursor: pointer; } .users-section .user .profile-picture { grid-area: profile-picture; width: 32px; height: 32px; border-radius: 50%; } .users-section .user .username { grid-area: username; font-size: 14px; font-weight: 600; color:#1F1F23; letter-spacing: 0.6px; margin-left:1px; } .users-section .user .description { grid-area: description; font-size: 13px; font-weight: 400; color: #53535F; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-left:1px; } .users-section .user .watchers { grid-area: watchers; display: flex; align-items: center; justify-content: flex-end; font-size: 14px; font-weight: 400; color: black; margin-right: 2px; } .users-section .user .watchers .dot { font-size: 7px; margin-right: 5px; } .tooltip-container { z-index: 999; width: 320px; height: auto; position: fixed; background-color: #E6E6EA; display: flex; } .tooltip-container img { position: relative; z-index: 999; width: auto; height: auto; max-width:320px; max-height:240px flex: 0; } .tooltiptext { flex: 0; position: relative; z-index: 999; width: 320px; max-width: 100%; /* 넘치는 경우 최대 너비 */ height: auto; background-color: #E6E6EA; color: black; text-align: center; align-items: center; /* 세로 가운데 정렬 */ justify-content: center; /* 가로 가운데 정렬 */ box-sizing: border-box; /* 패딩을 포함한 전체 너비 유지 */ padding: 8px 20px 14px 20px; } #webplayer_top .logo { top: 18px; left: 18px; } .left_navbar { display: flex; align-items: center; justify-content: flex-end; position: absolute; flex-direction: row-reverse; top: 0px; left: 140px; } .left_nav_button { position: relative; width: 70px; height: 56px; padding: 0; border: 0; border-radius: 50%; cursor: pointer; z-index: 3001; transition: all .2s; color: #1F1F23; font-size: 15px; font-weight: 600; } .users-section.myplus > .user.show-more, .users-section.follow > .user.show-more, .users-section.top > .user.show-more { display: none; } #toggleButton, #toggleButton2, #toggleButton3 { padding:12px; color:#53535F; width: 100%; text-align: center; } `; //======================================공용 함수======================================// function refreshPageOnDarkModeToggle() { var modecheck1 = document.getElementById("modecheck"); var modecheck2 = document.getElementById("modecheck2"); if (modecheck1 !== null) { modecheck1.addEventListener("change", function () { location.reload(); }); } if (modecheck2 !== null) { modecheck2.addEventListener("change", function () { location.reload(); }); } } function addNumberSeparator(number) { // toLocaleString 메서드를 사용하여 숫자에 구분자 추가 number = Number(number); return number.toLocaleString(); } /* function getCategoryName(cate_no){ if(savedCategory){ const categoryList = savedCategory.category_list; const filteredList = categoryList.filter(word => !["전체", "제한"].some(keyword => word.menu_name.includes(keyword))); const targetActionContent = cate_no; const regexPattern = new RegExp(targetActionContent.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i"); const matchedItem = filteredList.find(item => regexPattern.test(item.action_content)); // 일치하는 항목이 있다면 해당 항목의 menu_name 리턴, 없으면 null 리턴 let result = matchedItem ? matchedItem.menu_name : findCateName(cate_no); return result; } else { return findCateName(cate_no); } }*/ function getCategoryName(targetCateNo) { function searchCategory(categories, targetCateNo) { // 카테고리 배열을 순회합니다. for (let category of categories) { // 현재 카테고리의 cate_no가 목표 cate_no와 일치하는지 확인합니다. if (category.cate_no === targetCateNo) { // 일치하는 경우 cate_name을 반환합니다. return category.cate_name; } else { // 현재 카테고리에 child가 있는지 확인합니다. if (category.child && category.child.length > 0) { // 재귀적으로 child 카테고리를 검색합니다. let result = searchCategory(category.child, targetCateNo); // 재귀 호출 결과가 null이 아니라면 해당 cate_name을 반환합니다. if (result !== null) { return result; } } } } // cate_no에 해당하는 카테고리가 없는 경우 null을 반환합니다. return null; } // 함수 호출 시 CHANNEL.BROAD_CATEGORY에서 시작합니다. return searchCategory(savedCategory.CHANNEL.BROAD_CATEGORY, targetCateNo); } // 차단 목록을 저장합니다. function saveBlockedUsers() { GM_setValue('blockedUsers', blockedUsers); } // 사용자를 차단 목록에 추가합니다. function blockUser(userName, userId) { // 이미 차단된 사용자인지 확인 if (!isUserBlocked(userId)) { blockedUsers.push({ userName, userId }); saveBlockedUsers(); alert(`사용자 ${userName}(${userId})를 차단했습니다.`); registerUnblockMenu({ userName, userId }); } else { alert(`사용자 ${userName}(${userId})는 이미 차단되어 있습니다.`); } } // 함수: 사용자 차단 해제 function unblockUser(userId) { // 차단된 사용자 목록에서 해당 사용자 찾기 let unblockedUser = blockedUsers.find(user => user.userId === userId); // 사용자를 찾았을 때만 차단 해제 및 메뉴 삭제 수행 if (unblockedUser) { // 차단된 사용자 목록에서 해당 사용자 제거 blockedUsers = blockedUsers.filter(user => user.userId !== userId); // 변경된 목록을 저장 GM_setValue('blockedUsers', blockedUsers); alert(`사용자 ${userId}의 차단이 해제되었습니다.`); unregisterUnblockMenu(unblockedUser.userName); } } // 사용자가 이미 차단되어 있는지 확인합니다. function isUserBlocked(userId) { return blockedUsers.some(user => user.userId === userId); } // 함수: 동적으로 메뉴 등록 function registerUnblockMenu(user) { // GM_registerMenuCommand로 메뉴를 등록하고 메뉴 ID를 기록 let menuId = GM_registerMenuCommand(`💔 차단 해제 - ${user.userName}`, function() { unblockUser(user.userId); }); // 메뉴 ID를 기록 menuIds[user.userName] = menuId; } // 함수: 동적으로 메뉴 삭제 function unregisterUnblockMenu(userName) { // userName을 기반으로 저장된 메뉴 ID를 가져와서 삭제 let menuId = menuIds[userName]; if (menuId) { GM_unregisterMenuCommand(menuId); delete menuIds[userName]; // 삭제된 메뉴 ID를 객체에서도 제거 } } // 카테고리 목록을 저장합니다. function saveBlockedCategories() { GM_setValue('blockedCategories', blockedCategories); } // 카테고리를 차단 목록에 추가합니다. function blockCategory(categoryName, categoryId) { // 이미 차단된 카테고리인지 확인 if (!isCategoryBlocked(categoryId)) { blockedCategories.push({ categoryName, categoryId }); saveBlockedCategories(); alert(`카테고리 ${categoryName}(${categoryId})를 차단했습니다.`); registerCategoryUnblockMenu({ categoryName, categoryId }); } else { alert(`카테고리 ${categoryName}(${categoryId})는 이미 차단되어 있습니다.`); } } // 함수: 카테고리 차단 해제 function unblockCategory(categoryId) { // 차단된 카테고리 목록에서 해당 카테고리 찾기 let unblockedCategory = blockedCategories.find(category => category.categoryId === categoryId); // 카테고리를 찾았을 때만 차단 해제 및 메뉴 삭제 수행 if (unblockedCategory) { // 차단된 카테고리 목록에서 해당 카테고리 제거 blockedCategories = blockedCategories.filter(category => category.categoryId !== categoryId); // 변경된 목록을 저장 GM_setValue('blockedCategories', blockedCategories); alert(`카테고리 ${categoryId}의 차단이 해제되었습니다.`); unregisterCategoryUnblockMenu(unblockedCategory.categoryName); } } // 카테고리가 이미 차단되어 있는지 확인합니다. function isCategoryBlocked(categoryId) { return blockedCategories.some(category => category.categoryId === categoryId); } // 함수: 동적으로 카테고리 메뉴 등록 function registerCategoryUnblockMenu(category) { // GM_registerMenuCommand로 카테고리 메뉴를 등록하고 메뉴 ID를 기록 let menuId = GM_registerMenuCommand(`💔 카테고리 차단 해제 - ${category.categoryName}`, function() { unblockCategory(category.categoryId); }); // 메뉴 ID를 기록 categoryMenuIds[category.categoryName] = menuId; } // 함수: 동적으로 카테고리 메뉴 삭제 function unregisterCategoryUnblockMenu(categoryName) { // categoryName을 기반으로 저장된 메뉴 ID를 가져와서 삭제 let menuId = categoryMenuIds[categoryName]; if (menuId) { GM_unregisterMenuCommand(menuId); delete categoryMenuIds[categoryName]; // 삭제된 메뉴 ID를 객체에서도 제거 } } function waitForElement(elementSelector, callBack, attempts = 0, maxAttempts = 100) { const element = document.querySelector(elementSelector); if (element) { callBack(elementSelector, element); } else { if (attempts < maxAttempts) { setTimeout(function () { waitForElement(elementSelector, callBack, attempts + 1, maxAttempts); }, 200); } else { console.error('Reached maximum attempts. Element not found.'); } } } function desc_order(selector){ // Get the container element const container = document.querySelector(selector); // Get all user elements const userElements = document.querySelectorAll(`${selector} >.user`); // Convert NodeList to Array for easier manipulation const userArray = Array.from(userElements); // Sort userArray based on the data-watchers attribute userArray.sort((a, b) => { const watchersA = parseInt(a.getAttribute('data-watchers') || '0'); const watchersB = parseInt(b.getAttribute('data-watchers') || '0'); return watchersB - watchersA; }); // Clear container and append sorted elements container.innerHTML = ''; userArray.forEach(user => { container.appendChild(user); }); } function makeTopNavbarAndSidebar(page){ // .left_navbar를 찾거나 생성 var leftNavbar = document.querySelector('.left_navbar'); if (!leftNavbar) { leftNavbar = document.createElement('div'); leftNavbar.className = 'left_navbar'; // 페이지의 적절한 위치에 추가 var targetElement = document.body; // 원하는 위치에 따라 수정 targetElement.insertBefore(leftNavbar, targetElement.firstChild); } var buttonData = [ { href: 'https://www.afreecatv.com/?hash=all', text: '전체' }, { href: 'https://www.afreecatv.com/?hash=game', text: '게임' }, { href: 'https://www.afreecatv.com/?hash=bora', text: '보.라' }, { href: 'https://www.afreecatv.com/?hash=sports', text: '스포츠' } ]; buttonData.reverse().forEach(function (data) { var newButton = document.createElement('a'); newButton.href = data.href; newButton.innerHTML = ``; leftNavbar.appendChild(newButton); }); var tooltipContainer = document.createElement('div'); tooltipContainer.classList.add('tooltip-container'); let sidebarClass = "max"; if(sidebarMinimized){ sidebarClass = "min"; } if(page==="main"){ const newHtml = `
`; const serviceLnbElement = document.getElementById('serviceLnb'); if (serviceLnbElement) { serviceLnbElement.insertAdjacentHTML('beforeend', newHtml); } var listsection = document.querySelector('#list-section'); listsection.appendChild(tooltipContainer); } if(page==="player"){ const sidebarHtml = ` `; const webplayerElement = document.getElementById('webplayer'); if (webplayerElement) { webplayerElement.insertAdjacentHTML('beforeend', sidebarHtml); } webplayerElement.appendChild(tooltipContainer); } } function updateElementWithContent(targetElement, newContent) { // DocumentFragment 생성 function createFragment(content) { var fragment = document.createDocumentFragment(); var tempDiv = document.createElement('div'); tempDiv.innerHTML = content; while (tempDiv.firstChild) { fragment.appendChild(tempDiv.firstChild); } return fragment; } // 기존 내용을 지우고 DocumentFragment를 적용 function applyFragment(fragment) { targetElement.innerHTML = ''; // 기존 내용을 모두 지움 targetElement.appendChild(fragment); // 새로운 내용 추가 } // 호출 시점에 전달된 newContent를 사용하여 DocumentFragment 생성 후 적용 applyFragment(createFragment(newContent)); } // 사용자 요소를 생성하는 함수 function createUserElement(channel) { const userId = channel.user_id; const broadNo = channel.broad_no; const totalViewCnt = channel.total_view_cnt; const broadTitle = channel.broad_title; const userNick = channel.user_nick; const playerLink = "https://play.afreecatv.com/"+userId+"/"+broadNo; const broad_thumnail = `https://liveimg.afreecatv.com/m/${broadNo}`; const userElement = document.createElement('a'); userElement.classList.add('user'); if(!open_newtab){ //userElement.setAttribute('onclick',`window.location.href='${playerLink}'`); userElement.setAttribute('href',`${playerLink}`); } else { //userElement.setAttribute('onclick',`window.open('${playerLink}', '_blank')`); userElement.setAttribute('href',`${playerLink}`); userElement.setAttribute('target','_blank'); } userElement.setAttribute('data-watchers',`${totalViewCnt}`); userElement.setAttribute('broad_thumnail',`${broad_thumnail}`); userElement.setAttribute('tooltip',`${broadTitle}`); userElement.setAttribute('user_id',`${userId}`); //userElement.setAttribute('broad_no',`${broadNo}`); const profilePicture = document.createElement('img'); const pp_webp="https://stimg.afreecatv.com/LOGO/"+userId.slice(0, 2)+"/"+userId+"/m/"+userId+".webp"; const pp_jpg="https://profile.img.afreecatv.com/LOGO/"+userId.slice(0, 2)+"/"+userId+"/m/"+userId+".jpg"; profilePicture.src = pp_webp; // 프로필사진 profilePicture.setAttribute('onerror', `this.onerror=null; this.src='${pp_jpg}'`); profilePicture.setAttribute('alt', `${userId}'`); profilePicture.setAttribute('onclick', `event.preventDefault(); event.stopPropagation(); (document.getElementById('sidebar').offsetWidth === 52) ? (location.href = '${playerLink}') : window.open('https://bj.afreecatv.com/${userId}', '_blank');`); //profilePicture.onerror=`this.onerror=null; this.src='${pp_jpg}'`; profilePicture.classList.add('profile-picture'); const username = document.createElement('span'); username.classList.add('username'); username.textContent = userNick; //스트리머명 const cat_no = channel.broad_cate_no; const description = document.createElement('span'); description.classList.add('description'); description.textContent = getCategoryName(cat_no); //카테고리 userElement.setAttribute('broad_cate_no',`${cat_no}`); const watchers = document.createElement('span'); watchers.classList.add('watchers'); if(coloring_live === 1){ if(channel.auto_hashtags[0]==="웰컴"){ watchers.innerHTML = `🟣${addNumberSeparator(totalViewCnt)}`; } else if(channel.broad_resolution==="2560x1440"){ watchers.innerHTML = `🟠${addNumberSeparator(totalViewCnt)}`; } else { watchers.innerHTML = `🔴${addNumberSeparator(totalViewCnt)}`; } } else { watchers.innerHTML = `🔴${addNumberSeparator(totalViewCnt)}`; } userElement.appendChild(profilePicture); userElement.appendChild(username); userElement.appendChild(description); userElement.appendChild(watchers); return userElement; } function isUserInFollowSection(userid) { const followUsers = document.querySelectorAll('.users-section.follow .user'); // 유저가 포함되어 있는지 확인 for (const user of followUsers) { if (user.getAttribute('user_id') === userid) { return true; // 유저가 포함되어 있으면 true를 리턴 } } return false; // 유저가 포함되어 있지 않으면 false를 리턴 } function insertFoldButton() { const foldButton = ` `; const newHtml = `${foldButton}`; const webplayer_scroll = document.getElementById('webplayer_scroll') || document.getElementById('list-container'); const serviceLnbElement = document.getElementById('sidebar'); if (serviceLnbElement) { serviceLnbElement.insertAdjacentHTML('beforeend', newHtml); // 버튼 요소 가져오기 const buttons = serviceLnbElement.querySelectorAll('.button-fold-sidebar, .button-unfold-sidebar'); buttons.forEach(function(button) { // 버튼에 클릭 이벤트 리스너 추가 button.addEventListener('click', function () { // sidebar 상태 변경 sidebarMinimized = !sidebarMinimized; // max 클래스가 있으면 제거하고 min 클래스 추가 if (serviceLnbElement.classList.contains('max')) { serviceLnbElement.classList.remove('max'); serviceLnbElement.classList.add('min'); webplayer_scroll.style.left = '52px'; } // min 클래스가 있으면 제거하고 min 클래스 추가 else if (serviceLnbElement.classList.contains('min')) { serviceLnbElement.classList.remove('min'); serviceLnbElement.classList.add('max'); webplayer_scroll.style.left = '240px'; } // sidebarMinimized 값을 저장 GM_setValue("sidebarMinimized", sidebarMinimized ? 1 : 0); }); }); } } function insertTopChannels(update){ let topIcon = `